65620288b2dcb2b0ec2b67f9ae07a4bfe2997fbc
[WebKit-https.git] / WebCore / kcanvas / RenderPath.cpp
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005 Rob Buis <buis@kde.org>
4                   2005 Eric Seidel <eric.seidel@kdemail.net>
5
6     This file is part of the KDE project
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     aint with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21     Boston, MA 02111-1307, USA.
22 */
23
24 #include "config.h"
25 #ifdef SVG_SUPPORT
26 #include "RenderPath.h"
27
28 #include "GraphicsContext.h"
29 #include "RenderSVGContainer.h"
30 #include "KRenderingDevice.h"
31 #include "KRenderingFillPainter.h"
32 #include "KRenderingStrokePainter.h"
33 #include "SVGStyledElement.h"
34 #include <wtf/OwnPtr.h>
35
36 namespace WebCore {
37
38 class RenderPath::Private {
39 public:
40     RefPtr<KCanvasPath> path;
41
42     FloatRect fillBBox;
43     FloatRect strokeBbox;
44     AffineTransform matrix;
45     IntRect absoluteBounds;
46 };        
47
48 // RenderPath
49 RenderPath::RenderPath(RenderStyle* style, SVGStyledElement* node)
50     : RenderObject(node), d(new Private)
51 {
52     ASSERT(style != 0);
53 }
54
55 RenderPath::~RenderPath()
56 {
57     delete d;
58 }
59
60 AffineTransform RenderPath::localTransform() const
61 {
62     return d->matrix;
63 }
64
65 void RenderPath::setLocalTransform(const AffineTransform &matrix)
66 {
67     d->matrix = matrix;
68 }
69
70
71 FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint& point) const
72 {
73     // FIXME: does it make sense to map incoming points with the inverse of the
74     // absolute transform? 
75     double localX;
76     double localY;
77     absoluteTransform().invert().map(point.x(), point.y(), &localX, &localY);
78     return FloatPoint(localX, localY);
79 }
80
81 bool RenderPath::fillContains(const FloatPoint& point) const
82 {
83     if (!d->path)
84         return false;
85
86     if (!KSVGPainterFactory::fillPaintServer(style(), this))
87         return false;
88
89     return path()->containsPoint(mapAbsolutePointToLocal(point),
90                                  KSVGPainterFactory::fillPainter(style(), this).fillRule());
91 }
92
93 bool RenderPath::strokeContains(const FloatPoint& point) const
94 {
95     if (!d->path)
96         return false;
97
98     if (!KSVGPainterFactory::strokePaintServer(style(), this))
99         return false;
100
101     return path()->strokeContainsPoint(mapAbsolutePointToLocal(point));
102 }
103
104 FloatRect RenderPath::strokeBBox() const
105 {
106     if (KSVGPainterFactory::isStroked(style())) {
107         KRenderingStrokePainter strokePainter = KSVGPainterFactory::strokePainter(style(), this);
108         return path()->strokeBoundingBox(strokePainter);
109     }
110
111     return path()->boundingBox();
112 }
113
114 FloatRect RenderPath::relativeBBox(bool includeStroke) const
115 {
116     if (!d->path)
117         return FloatRect();
118
119     if (includeStroke) {
120         if (d->strokeBbox.isEmpty())
121             d->strokeBbox = strokeBBox();
122         return d->strokeBbox;
123     }
124
125     if (d->fillBBox.isEmpty())
126         d->fillBBox = path()->boundingBox();
127     return d->fillBBox;
128 }
129
130 void RenderPath::setPath(KCanvasPath* newPath)
131 {
132     d->path = newPath;
133     d->strokeBbox = FloatRect();
134     d->fillBBox = FloatRect();
135 }
136
137 KCanvasPath* RenderPath::path() const
138 {
139     return d->path.get();
140 }
141
142 void RenderPath::layout()
143 {
144     // FIXME: Currently the DOM does all of the % length calculations, so we
145     // pretend that one of the attributes of the element has changed on the DOM
146     // to force the DOM object to update this render object with new aboslute position values.
147
148     IntRect oldBounds;
149     bool checkForRepaint = checkForRepaintDuringLayout();
150     if (selfNeedsLayout() && checkForRepaint)
151         oldBounds = d->absoluteBounds;
152
153     static_cast<SVGStyledElement*>(element())->notifyAttributeChange();
154
155     d->absoluteBounds = getAbsoluteRepaintRect();
156
157     setWidth(d->absoluteBounds.width());
158     setHeight(d->absoluteBounds.height());
159
160     if (selfNeedsLayout() && checkForRepaint)
161         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
162         
163     setNeedsLayout(false);
164 }
165
166 IntRect RenderPath::getAbsoluteRepaintRect()
167 {
168     FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));
169     
170     // Filters can expand the bounding box
171     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
172     if (filter)
173         repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
174     
175     if (!repaintRect.isEmpty())
176         repaintRect.inflate(1); // inflate 1 pixel for antialiasing
177     return enclosingIntRect(repaintRect);
178 }
179
180 bool RenderPath::requiresLayer()
181 {
182     return false;
183 }
184
185 short RenderPath::lineHeight(bool b, bool isRootLineBox) const
186 {
187     return static_cast<short>(relativeBBox(true).height());
188 }
189
190 short RenderPath::baselinePosition(bool b, bool isRootLineBox) const
191 {
192     return static_cast<short>(relativeBBox(true).height());
193 }
194
195 void RenderPath::paint(PaintInfo &paintInfo, int parentX, int parentY)
196 {
197     // No one should be transforming us via these.
198     //ASSERT(parentX == 0);
199     //ASSERT(parentY == 0);
200
201     if (paintInfo.p->paintingDisabled() || (paintInfo.phase != PaintPhaseForeground) || style()->visibility() == HIDDEN)
202         return;
203     
204     KRenderingDevice* device = renderingDevice();
205     KRenderingDeviceContext *context = device->currentContext();
206     bool shouldPopContext = false;
207     if (context)
208         paintInfo.p->save();
209     else {
210         // Need to set up KCanvas rendering if it hasn't already been done.
211         context = paintInfo.p->createRenderingDeviceContext();
212         device->pushContext(context);
213         shouldPopContext = true;
214     }
215
216     context->concatCTM(localTransform());
217
218     // setup to apply filters
219     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
220     if (filter) {
221         filter->prepareFilter(relativeBBox(true));
222         context = device->currentContext();
223     }
224
225     if (KCanvasClipper *clipper = getClipperById(document(), style()->svgStyle()->clipPath().mid(1)))
226         clipper->applyClip(relativeBBox(true));
227
228     if (KCanvasMasker *masker = getMaskerById(document(), style()->svgStyle()->maskElement().mid(1)))
229         masker->applyMask(relativeBBox(true));
230
231     context->clearPath();
232     
233     KRenderingPaintServer *fillPaintServer = KSVGPainterFactory::fillPaintServer(style(), this);
234     if (fillPaintServer) {
235         context->addPath(path());
236         fillPaintServer->setActiveClient(this);
237         fillPaintServer->draw(context, this, APPLY_TO_FILL);
238     }
239     KRenderingPaintServer *strokePaintServer = KSVGPainterFactory::strokePaintServer(style(), this);
240     if (strokePaintServer) {
241         context->addPath(path()); // path is cleared when filled.
242         strokePaintServer->setActiveClient(this);
243         strokePaintServer->draw(context, this, APPLY_TO_STROKE);
244     }
245
246     drawMarkersIfNeeded(paintInfo.p, paintInfo.r, path());
247
248     // actually apply the filter
249     if (filter)
250         filter->applyFilter(relativeBBox(true));
251
252     // restore drawing state
253     if (!shouldPopContext)
254         paintInfo.p->restore();
255     else {
256         device->popContext();
257         delete context;
258     }
259 }
260
261 void RenderPath::absoluteRects(DeprecatedValueList<IntRect>& rects, int _tx, int _ty)
262 {
263     rects.append(getAbsoluteRepaintRect());
264 }
265
266 bool RenderPath::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
267 {
268     // We only draw in the forground phase, so we only hit-test then.
269     if (hitTestAction != HitTestForeground)
270         return false;
271
272     if (strokeContains(FloatPoint(_x, _y)) || fillContains(FloatPoint(_x, _y))) {
273         setInnerNode(info);
274         return true;
275     }
276     return false;
277 }
278
279 }
280
281 // vim:ts=4:noet
282 #endif // SVG_SUPPORT
283