3e40f3ec696cf5844033d548e0af3554cc4f7fa7
[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 #if SVG_SUPPORT
26 #include "RenderPath.h"
27
28 #include "GraphicsContext.h"
29 #include "KCanvasContainer.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, strokeBbox;
43     QMatrix matrix;
44 };        
45
46 // RenderPath
47 RenderPath::RenderPath(RenderStyle* style, SVGStyledElement* node)
48     : RenderObject(node), d(new Private)
49 {
50     ASSERT(style != 0);
51 }
52
53 RenderPath::~RenderPath()
54 {
55     delete d;
56 }
57
58 QMatrix RenderPath::localTransform() const
59 {
60     return d->matrix;
61 }
62
63 void RenderPath::setLocalTransform(const QMatrix &matrix)
64 {
65     d->matrix = matrix;
66 }
67
68
69 FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint& point) const
70 {
71     // FIXME: does it make sense to map incoming points with the inverse of the
72     // absolute transform? 
73     double localX;
74     double localY;
75     absoluteTransform().invert().map(point.x(), point.y(), &localX, &localY);
76     return FloatPoint(localX, localY);
77 }
78
79 bool RenderPath::fillContains(const FloatPoint& point) const
80 {
81     if (!d->path)
82         return false;
83
84     if (!KSVGPainterFactory::fillPaintServer(style(), this))
85         return false;
86
87     return path()->containsPoint(mapAbsolutePointToLocal(point),
88                                  KSVGPainterFactory::fillPainter(style(), this).fillRule());
89 }
90
91 bool RenderPath::strokeContains(const FloatPoint& point) const
92 {
93     if (!d->path)
94         return false;
95
96     if (!KSVGPainterFactory::strokePaintServer(style(), this))
97         return false;
98
99     return path()->strokeContainsPoint(mapAbsolutePointToLocal(point));
100 }
101
102 FloatRect RenderPath::strokeBBox() const
103 {
104     if (KSVGPainterFactory::isStroked(style())) {
105         KRenderingStrokePainter strokePainter = KSVGPainterFactory::strokePainter(style(), this);
106         return path()->strokeBoundingBox(strokePainter);
107     }
108
109     return path()->boundingBox();
110 }
111
112 FloatRect RenderPath::relativeBBox(bool includeStroke) const
113 {
114     if (!d->path)
115         return FloatRect();
116
117     if (includeStroke) {
118         if (d->strokeBbox.isEmpty())
119             d->strokeBbox = strokeBBox();
120         return d->strokeBbox;
121     }
122
123     if (d->fillBBox.isEmpty())
124         d->fillBBox = path()->boundingBox();
125     return d->fillBBox;
126 }
127
128 void RenderPath::setPath(KCanvasPath* newPath)
129 {
130     d->path = newPath;
131     d->strokeBbox = FloatRect();
132     d->fillBBox = FloatRect();
133 }
134
135 KCanvasPath* RenderPath::path() const
136 {
137     return d->path.get();
138 }
139
140 void RenderPath::layout()
141 {
142     // FIXME: Currently the DOM does all of the % length calculations, so we
143     // pretend that one of the attributes of the element has changed on the DOM
144     // to force the DOM object to update this render object with new aboslute position values.
145
146     static_cast<SVGStyledElement*>(element())->notifyAttributeChange();
147     IntRect layoutRect = getAbsoluteRepaintRect();
148     setWidth(layoutRect.width());
149     setHeight(layoutRect.height());
150     setNeedsLayout(false);
151 }
152
153 IntRect RenderPath::getAbsoluteRepaintRect()
154 {
155     FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));
156     
157     // Filters can expand the bounding box
158     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
159     if (filter)
160         repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
161     
162     if (!repaintRect.isEmpty())
163         repaintRect.inflate(1); // inflate 1 pixel for antialiasing
164     return enclosingIntRect(repaintRect);
165 }
166
167 bool RenderPath::requiresLayer()
168 {
169     return false;
170 }
171
172 short RenderPath::lineHeight(bool b, bool isRootLineBox) const
173 {
174     return static_cast<short>(relativeBBox(true).height());
175 }
176
177 short RenderPath::baselinePosition(bool b, bool isRootLineBox) const
178 {
179     return static_cast<short>(relativeBBox(true).height());
180 }
181
182 void RenderPath::paint(PaintInfo &paintInfo, int parentX, int parentY)
183 {
184     // No one should be transforming us via these.
185     //ASSERT(parentX == 0);
186     //ASSERT(parentY == 0);
187
188     if (paintInfo.p->paintingDisabled() || (paintInfo.phase != PaintPhaseForeground) || style()->visibility() == HIDDEN)
189         return;
190     
191     KRenderingDevice* device = renderingDevice();
192     KRenderingDeviceContext *context = device->currentContext();
193     bool shouldPopContext = false;
194     if (context)
195         paintInfo.p->save();
196     else {
197         // Need to set up KCanvas rendering if it hasn't already been done.
198         context = paintInfo.p->createRenderingDeviceContext();
199         device->pushContext(context);
200         shouldPopContext = true;
201     }
202
203     context->concatCTM(localTransform());
204
205     // setup to apply filters
206     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
207     if (filter) {
208         filter->prepareFilter(relativeBBox(true));
209         context = device->currentContext();
210     }
211
212     if (KCanvasClipper *clipper = getClipperById(document(), style()->svgStyle()->clipPath().mid(1)))
213         clipper->applyClip(relativeBBox(true));
214
215     if (KCanvasMasker *masker = getMaskerById(document(), style()->svgStyle()->maskElement().mid(1)))
216         masker->applyMask(relativeBBox(true));
217
218     context->clearPath();
219     
220     KRenderingPaintServer *fillPaintServer = KSVGPainterFactory::fillPaintServer(style(), this);
221     if (fillPaintServer) {
222         context->addPath(path());
223         fillPaintServer->setActiveClient(this);
224         fillPaintServer->draw(context, this, APPLY_TO_FILL);
225     }
226     KRenderingPaintServer *strokePaintServer = KSVGPainterFactory::strokePaintServer(style(), this);
227     if (strokePaintServer) {
228         context->addPath(path()); // path is cleared when filled.
229         strokePaintServer->setActiveClient(this);
230         strokePaintServer->draw(context, this, APPLY_TO_STROKE);
231     }
232
233     OwnPtr<GraphicsContext> c(context->createGraphicsContext());
234     drawMarkersIfNeeded(c.get(), paintInfo.r, path());
235
236     // actually apply the filter
237     if (filter)
238         filter->applyFilter(relativeBBox(true));
239
240     // restore drawing state
241     if (!shouldPopContext)
242         paintInfo.p->restore();
243     else {
244         device->popContext();
245         delete context;
246     }
247 }
248
249 void RenderPath::absoluteRects(DeprecatedValueList<IntRect>& rects, int _tx, int _ty)
250 {
251     rects.append(getAbsoluteRepaintRect());
252 }
253
254 bool RenderPath::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
255 {
256     // We only draw in the forground phase, so we only hit-test then.
257     if (hitTestAction != HitTestForeground)
258         return false;
259
260     if (strokeContains(FloatPoint(_x, _y)) || fillContains(FloatPoint(_x, _y))) {
261         setInnerNode(info);
262         return true;
263     }
264     return false;
265 }
266
267 }
268
269 // vim:ts=4:noet
270 #endif // SVG_SUPPORT
271