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