Reviewed by Tim H.
[WebKit-https.git] / WebCore / rendering / 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 <math.h>
29
30 #include "GraphicsContext.h"
31 #include "RenderSVGContainer.h"
32 #include "KCanvasClipper.h"
33 #include "KCanvasMasker.h"
34 #include "KCanvasMarker.h"
35 #include "KCanvasRenderingStyle.h"
36 #include "KRenderingDevice.h"
37 #include "SVGStyledElement.h"
38 #include <wtf/OwnPtr.h>
39
40 namespace WebCore {
41
42 // RenderPath
43 RenderPath::RenderPath(RenderStyle* style, SVGStyledElement* node)
44     : RenderObject(node)
45 {
46     ASSERT(style != 0);
47 }
48
49 RenderPath::~RenderPath()
50 {
51 }
52
53 AffineTransform RenderPath::localTransform() const
54 {
55     return m_matrix;
56 }
57
58 void RenderPath::setLocalTransform(const AffineTransform &matrix)
59 {
60     m_matrix = matrix;
61 }
62
63
64 FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint& point) const
65 {
66     // FIXME: does it make sense to map incoming points with the inverse of the
67     // absolute transform? 
68     double localX;
69     double localY;
70     absoluteTransform().invert().map(point.x(), point.y(), &localX, &localY);
71     return FloatPoint(localX, localY);
72 }
73
74 bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill) const
75 {
76     if (m_path.isEmpty())
77         return false;
78
79     if (requiresFill && !KSVGPainterFactory::fillPaintServer(style(), this))
80         return false;
81
82     return path().contains(mapAbsolutePointToLocal(point), style()->svgStyle()->fillRule());
83 }
84
85 FloatRect RenderPath::relativeBBox(bool includeStroke) const
86 {
87     if (m_path.isEmpty())
88         return FloatRect();
89
90     if (includeStroke) {
91         if (m_strokeBbox.isEmpty())
92             m_strokeBbox = strokeBBox();
93         return m_strokeBbox;
94     }
95
96     if (m_fillBBox.isEmpty())
97         m_fillBBox = path().boundingRect();
98     return m_fillBBox;
99 }
100
101 void RenderPath::setPath(const Path& newPath)
102 {
103     m_path = newPath;
104     m_strokeBbox = FloatRect();
105     m_fillBBox = FloatRect();
106 }
107
108 const Path& RenderPath::path() const
109 {
110     return m_path;
111 }
112
113 void RenderPath::layout()
114 {
115     // FIXME: Currently the DOM does all of the % length calculations, so we
116     // pretend that one of the attributes of the element has changed on the DOM
117     // to force the DOM object to update this render object with new aboslute position values.
118
119     IntRect oldBounds;
120     bool checkForRepaint = checkForRepaintDuringLayout();
121     if (selfNeedsLayout() && checkForRepaint)
122         oldBounds = m_absoluteBounds;
123
124     static_cast<SVGStyledElement*>(element())->notifyAttributeChange();
125
126     m_absoluteBounds = getAbsoluteRepaintRect();
127
128     setWidth(m_absoluteBounds.width());
129     setHeight(m_absoluteBounds.height());
130
131     if (selfNeedsLayout() && checkForRepaint)
132         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
133         
134     setNeedsLayout(false);
135 }
136
137 IntRect RenderPath::getAbsoluteRepaintRect()
138 {
139     FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));
140     
141     // Filters can expand the bounding box
142     KCanvasFilter* filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
143     if (filter)
144         repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
145     
146     if (!repaintRect.isEmpty())
147         repaintRect.inflate(1); // inflate 1 pixel for antialiasing
148     return enclosingIntRect(repaintRect);
149 }
150
151 bool RenderPath::requiresLayer()
152 {
153     return false;
154 }
155
156 short RenderPath::lineHeight(bool b, bool isRootLineBox) const
157 {
158     return static_cast<short>(relativeBBox(true).height());
159 }
160
161 short RenderPath::baselinePosition(bool b, bool isRootLineBox) const
162 {
163     return static_cast<short>(relativeBBox(true).height());
164 }
165
166 void RenderPath::paint(PaintInfo &paintInfo, int parentX, int parentY)
167 {
168     // No one should be transforming us via these.
169     //ASSERT(parentX == 0);
170     //ASSERT(parentY == 0);
171
172     if (paintInfo.p->paintingDisabled() || (paintInfo.phase != PaintPhaseForeground) || style()->visibility() == HIDDEN || path().isEmpty())
173         return;
174     
175     KRenderingDevice* device = renderingDevice();
176     KRenderingDeviceContext* context = device->currentContext();
177     bool shouldPopContext = false;
178     if (context)
179         paintInfo.p->save();
180     else {
181         // Need to set up KCanvas rendering if it hasn't already been done.
182         context = paintInfo.p->createRenderingDeviceContext();
183         device->pushContext(context);
184         shouldPopContext = true;
185     }
186
187     context->concatCTM(localTransform());
188
189     // setup to apply filters
190     KCanvasFilter* filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
191     if (filter) {
192         filter->prepareFilter(relativeBBox(true));
193         context = device->currentContext();
194     }
195
196     if (KCanvasClipper* clipper = getClipperById(document(), style()->svgStyle()->clipPath().substring(1)))
197         clipper->applyClip(relativeBBox(true));
198
199     if (KCanvasMasker* masker = getMaskerById(document(), style()->svgStyle()->maskElement().substring(1)))
200         masker->applyMask(relativeBBox(true));
201
202     context->clearPath();
203     
204     KRenderingPaintServer *fillPaintServer = KSVGPainterFactory::fillPaintServer(style(), this);
205     if (fillPaintServer) {
206         context->addPath(path());
207         fillPaintServer->setActiveClient(this);
208         fillPaintServer->draw(context, this, APPLY_TO_FILL);
209     }
210     KRenderingPaintServer *strokePaintServer = KSVGPainterFactory::strokePaintServer(style(), this);
211     if (strokePaintServer) {
212         context->addPath(path()); // path is cleared when filled.
213         strokePaintServer->setActiveClient(this);
214         strokePaintServer->draw(context, this, APPLY_TO_STROKE);
215     }
216
217     drawMarkersIfNeeded(paintInfo.p, paintInfo.r, path());
218
219     // actually apply the filter
220     if (filter)
221         filter->applyFilter(relativeBBox(true));
222
223     // restore drawing state
224     if (!shouldPopContext)
225         paintInfo.p->restore();
226     else {
227         device->popContext();
228         delete context;
229     }
230 }
231
232 void RenderPath::absoluteRects(Vector<IntRect>& rects, int _tx, int _ty)
233 {
234     rects.append(getAbsoluteRepaintRect());
235 }
236
237 RenderPath::PointerEventsHitRules RenderPath::pointerEventsHitRules()
238 {
239     PointerEventsHitRules hitRules;
240     
241     switch (style()->svgStyle()->pointerEvents())
242     {
243         case PE_VISIBLE_PAINTED:
244             hitRules.requireVisible = true;
245             hitRules.requireFill = true;
246             hitRules.requireStroke = true;
247             hitRules.canHitFill = true;
248             hitRules.canHitStroke = true;
249             break;
250         case PE_VISIBLE_FILL:
251             hitRules.requireVisible = true;
252             hitRules.canHitFill = true;
253             break;
254         case PE_VISIBLE_STROKE:
255             hitRules.requireVisible = true;
256             hitRules.canHitStroke = true;
257             break;
258         case PE_VISIBLE:
259             hitRules.requireVisible = true;
260             hitRules.canHitFill = true;
261             hitRules.canHitStroke = true;
262             break;
263         case PE_PAINTED:
264             hitRules.requireFill = true;
265             hitRules.requireStroke = true;
266             hitRules.canHitFill = true;
267             hitRules.canHitStroke = true;
268             break;
269         case PE_FILL:
270             hitRules.canHitFill = true;
271             break;
272         case PE_STROKE:
273             hitRules.canHitStroke = true;
274             break;
275         case PE_ALL:
276             hitRules.canHitFill = true;
277             hitRules.canHitStroke = true;
278             break;
279         case PE_NONE:
280             // nothing to do here, defaults are all false.
281             break;
282     }
283     return hitRules;
284 }
285
286 bool RenderPath::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
287 {
288     // We only draw in the forground phase, so we only hit-test then.
289     if (hitTestAction != HitTestForeground)
290         return false;
291     PointerEventsHitRules hitRules = pointerEventsHitRules();
292     
293     bool isVisible = (style()->visibility() == VISIBLE);
294     if (isVisible || !hitRules.requireVisible) {
295         FloatPoint hitPoint(_x,_y);
296         if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(hitPoint, hitRules.requireStroke))
297             || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(hitPoint, hitRules.requireFill))) {
298             setInnerNode(info);
299             return true;
300         }
301     }
302     return false;
303 }
304
305 enum MarkerType {
306     Start,
307     Mid,
308     End
309 };
310
311 struct MarkerData {
312     FloatPoint origin;
313     FloatPoint subpathStart;
314     double strokeWidth;
315     FloatPoint inslopePoints[2];
316     FloatPoint outslopePoints[2];
317     MarkerType type;
318     KCanvasMarker *marker;
319 };
320
321 struct DrawMarkersData {
322     DrawMarkersData(GraphicsContext*, KCanvasMarker* startMarker, KCanvasMarker* midMarker, double strokeWidth);
323     GraphicsContext* context;
324     int elementIndex;
325     MarkerData previousMarkerData;
326     KCanvasMarker* midMarker;
327 };
328
329 DrawMarkersData::DrawMarkersData(GraphicsContext* c, KCanvasMarker *start, KCanvasMarker *mid, double strokeWidth)
330     : context(c)
331     , elementIndex(0)
332     , midMarker(mid)
333 {
334     previousMarkerData.origin = FloatPoint();
335     previousMarkerData.subpathStart = FloatPoint();
336     previousMarkerData.strokeWidth = strokeWidth;
337     previousMarkerData.marker = start;
338     previousMarkerData.type = Start;
339 }
340
341 static void drawMarkerWithData(GraphicsContext* context, MarkerData &data)
342 {
343     if (!data.marker)
344         return;
345     
346     FloatPoint inslopeChange = data.inslopePoints[1] - FloatSize(data.inslopePoints[0].x(), data.inslopePoints[0].y());
347     FloatPoint outslopeChange = data.outslopePoints[1] - FloatSize(data.outslopePoints[0].x(), data.outslopePoints[0].y());
348     
349     static const double deg2rad = M_PI/180.0;
350     double inslope = atan2(inslopeChange.y(), inslopeChange.x()) / deg2rad;
351     double outslope = atan2(outslopeChange.y(), outslopeChange.x()) / deg2rad;
352     
353     double angle = 0.0;
354     switch (data.type) {
355         case Start:
356             angle = outslope;
357             break;
358         case Mid:
359             angle = (inslope + outslope) / 2;
360             break;
361         case End:
362             angle = inslope;
363     }
364     
365     data.marker->draw(context, FloatRect(), data.origin.x(), data.origin.y(), data.strokeWidth, angle);
366 }
367
368 static inline void updateMarkerDataForElement(MarkerData &previousMarkerData, const PathElement *element)
369 {
370     FloatPoint *points = element->points;
371     
372     switch (element->type) {
373     case PathElementAddQuadCurveToPoint:
374         // TODO
375         previousMarkerData.origin = points[1];
376         break;
377     case PathElementAddCurveToPoint:
378         previousMarkerData.inslopePoints[0] = points[1];
379         previousMarkerData.inslopePoints[1] = points[2];
380         previousMarkerData.origin = points[2];
381         break;
382     case PathElementMoveToPoint:
383         previousMarkerData.subpathStart = points[0];
384     case PathElementAddLineToPoint:
385         previousMarkerData.inslopePoints[0] = previousMarkerData.origin;
386         previousMarkerData.inslopePoints[1] = points[0];
387         previousMarkerData.origin = points[0];
388         break;
389     case PathElementCloseSubpath:
390         previousMarkerData.inslopePoints[0] = previousMarkerData.origin;
391         previousMarkerData.inslopePoints[1] = points[0];
392         previousMarkerData.origin = previousMarkerData.subpathStart;
393         previousMarkerData.subpathStart = FloatPoint();
394     }
395 }
396
397 static void drawStartAndMidMarkers(void *info, const PathElement *element)
398 {
399     DrawMarkersData& data = *reinterpret_cast<DrawMarkersData*>(info);
400
401     int elementIndex = data.elementIndex;
402     MarkerData &previousMarkerData = data.previousMarkerData;
403
404     FloatPoint *points = element->points;
405
406     // First update the outslope for the previous element
407     previousMarkerData.outslopePoints[0] = previousMarkerData.origin;
408     previousMarkerData.outslopePoints[1] = points[0];
409
410     // Draw the marker for the previous element
411     if (elementIndex != 0)
412         drawMarkerWithData(data.context, previousMarkerData);
413
414     // Update our marker data for this element
415     updateMarkerDataForElement(previousMarkerData, element);
416
417     if (elementIndex == 1) {
418         // After drawing the start marker, switch to drawing mid markers
419         previousMarkerData.marker = data.midMarker;
420         previousMarkerData.type = Mid;
421     }
422
423     data.elementIndex++;
424 }
425
426 void RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatRect& rect, const Path& path) const
427 {
428     Document *doc = document();
429     const SVGRenderStyle* svgStyle = style()->svgStyle();
430
431     KCanvasMarker* startMarker = getMarkerById(doc, svgStyle->startMarker().substring(1));
432     KCanvasMarker* midMarker = getMarkerById(doc, svgStyle->midMarker().substring(1));
433     KCanvasMarker* endMarker = getMarkerById(doc, svgStyle->endMarker().substring(1));
434     
435     if (!startMarker && !midMarker && !endMarker)
436         return;
437
438     double strokeWidth = KSVGPainterFactory::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 1.0);
439
440     DrawMarkersData data(context, startMarker, midMarker, strokeWidth);
441
442     path.apply(&data, drawStartAndMidMarkers);
443
444     data.previousMarkerData.marker = endMarker;
445     data.previousMarkerData.type = End;
446     drawMarkerWithData(context, data.previousMarkerData);
447 }
448
449 bool RenderPath::hasPercentageValues() const
450 {
451     return static_cast<SVGStyledElement*>(element())->hasPercentageValues();
452 }
453
454 }
455
456 #endif // SVG_SUPPORT