#include "config.h"
#if SVG_SUPPORT
#include "RenderPath.h"
#include "GraphicsContext.h"
#include "RenderSVGContainer.h"
#include "KRenderingDevice.h"
#include "KRenderingFillPainter.h"
#include "KRenderingStrokePainter.h"
#include "SVGStyledElement.h"
#include <wtf/OwnPtr.h>
namespace WebCore {
class RenderPath::Private {
public:
RefPtr<KCanvasPath> path;
FloatRect fillBBox;
FloatRect strokeBbox;
AffineTransform matrix;
IntRect absoluteBounds;
};
RenderPath::RenderPath(RenderStyle* style, SVGStyledElement* node)
: RenderObject(node), d(new Private)
{
ASSERT(style != 0);
}
RenderPath::~RenderPath()
{
delete d;
}
AffineTransform RenderPath::localTransform() const
{
return d->matrix;
}
void RenderPath::setLocalTransform(const AffineTransform &matrix)
{
d->matrix = matrix;
}
FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint& point) const
{
double localX;
double localY;
absoluteTransform().invert().map(point.x(), point.y(), &localX, &localY);
return FloatPoint(localX, localY);
}
bool RenderPath::fillContains(const FloatPoint& point) const
{
if (!d->path)
return false;
if (!KSVGPainterFactory::fillPaintServer(style(), this))
return false;
return path()->containsPoint(mapAbsolutePointToLocal(point),
KSVGPainterFactory::fillPainter(style(), this).fillRule());
}
bool RenderPath::strokeContains(const FloatPoint& point) const
{
if (!d->path)
return false;
if (!KSVGPainterFactory::strokePaintServer(style(), this))
return false;
return path()->strokeContainsPoint(mapAbsolutePointToLocal(point));
}
FloatRect RenderPath::strokeBBox() const
{
if (KSVGPainterFactory::isStroked(style())) {
KRenderingStrokePainter strokePainter = KSVGPainterFactory::strokePainter(style(), this);
return path()->strokeBoundingBox(strokePainter);
}
return path()->boundingBox();
}
FloatRect RenderPath::relativeBBox(bool includeStroke) const
{
if (!d->path)
return FloatRect();
if (includeStroke) {
if (d->strokeBbox.isEmpty())
d->strokeBbox = strokeBBox();
return d->strokeBbox;
}
if (d->fillBBox.isEmpty())
d->fillBBox = path()->boundingBox();
return d->fillBBox;
}
void RenderPath::setPath(KCanvasPath* newPath)
{
d->path = newPath;
d->strokeBbox = FloatRect();
d->fillBBox = FloatRect();
}
KCanvasPath* RenderPath::path() const
{
return d->path.get();
}
void RenderPath::layout()
{
IntRect oldBounds;
bool checkForRepaint = checkForRepaintDuringLayout();
if (selfNeedsLayout() && checkForRepaint)
oldBounds = d->absoluteBounds;
static_cast<SVGStyledElement*>(element())->notifyAttributeChange();
d->absoluteBounds = getAbsoluteRepaintRect();
setWidth(d->absoluteBounds.width());
setHeight(d->absoluteBounds.height());
if (selfNeedsLayout() && checkForRepaint)
repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
setNeedsLayout(false);
}
IntRect RenderPath::getAbsoluteRepaintRect()
{
FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));
KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
if (filter)
repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
if (!repaintRect.isEmpty())
repaintRect.inflate(1); return enclosingIntRect(repaintRect);
}
bool RenderPath::requiresLayer()
{
return false;
}
short RenderPath::lineHeight(bool b, bool isRootLineBox) const
{
return static_cast<short>(relativeBBox(true).height());
}
short RenderPath::baselinePosition(bool b, bool isRootLineBox) const
{
return static_cast<short>(relativeBBox(true).height());
}
void RenderPath::paint(PaintInfo &paintInfo, int parentX, int parentY)
{
if (paintInfo.p->paintingDisabled() || (paintInfo.phase != PaintPhaseForeground) || style()->visibility() == HIDDEN)
return;
KRenderingDevice* device = renderingDevice();
KRenderingDeviceContext *context = device->currentContext();
bool shouldPopContext = false;
if (context)
paintInfo.p->save();
else {
context = paintInfo.p->createRenderingDeviceContext();
device->pushContext(context);
shouldPopContext = true;
}
context->concatCTM(localTransform());
KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
if (filter) {
filter->prepareFilter(relativeBBox(true));
context = device->currentContext();
}
if (KCanvasClipper *clipper = getClipperById(document(), style()->svgStyle()->clipPath().mid(1)))
clipper->applyClip(relativeBBox(true));
if (KCanvasMasker *masker = getMaskerById(document(), style()->svgStyle()->maskElement().mid(1)))
masker->applyMask(relativeBBox(true));
context->clearPath();
KRenderingPaintServer *fillPaintServer = KSVGPainterFactory::fillPaintServer(style(), this);
if (fillPaintServer) {
context->addPath(path());
fillPaintServer->setActiveClient(this);
fillPaintServer->draw(context, this, APPLY_TO_FILL);
}
KRenderingPaintServer *strokePaintServer = KSVGPainterFactory::strokePaintServer(style(), this);
if (strokePaintServer) {
context->addPath(path()); strokePaintServer->setActiveClient(this);
strokePaintServer->draw(context, this, APPLY_TO_STROKE);
}
drawMarkersIfNeeded(paintInfo.p, paintInfo.r, path());
if (filter)
filter->applyFilter(relativeBBox(true));
if (!shouldPopContext)
paintInfo.p->restore();
else {
device->popContext();
delete context;
}
}
void RenderPath::absoluteRects(DeprecatedValueList<IntRect>& rects, int _tx, int _ty)
{
rects.append(getAbsoluteRepaintRect());
}
bool RenderPath::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
{
if (hitTestAction != HitTestForeground)
return false;
if (strokeContains(FloatPoint(_x, _y)) || fillContains(FloatPoint(_x, _y))) {
setInnerNode(info);
return true;
}
return false;
}
}
#endif // SVG_SUPPORT