Remove unnecessary #include SVGResourcesCache.h in SVGDocumentExtensions.h; use forwa...
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGPath.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2008 Rob Buis <buis@kde.org>
4  * Copyright (C) 2005, 2007 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Google, Inc.
6  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
8  * Copyright (C) 2009 Jeff Schiller <codedread@gmail.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(SVG)
29 #include "RenderSVGPath.h"
30
31 #include "FloatPoint.h"
32 #include "FloatQuad.h"
33 #include "GraphicsContext.h"
34 #include "HitTestRequest.h"
35 #include "LayoutRepainter.h"
36 #include "PointerEventsHitRules.h"
37 #include "RenderSVGContainer.h"
38 #include "RenderSVGResourceMarker.h"
39 #include "RenderSVGResourceSolidColor.h"
40 #include "SVGPathData.h"
41 #include "SVGRenderSupport.h"
42 #include "SVGResources.h"
43 #include "SVGResourcesCache.h"
44 #include "SVGStyledTransformableElement.h"
45 #include "SVGTransformList.h"
46 #include "SVGURIReference.h"
47 #include "StrokeStyleApplier.h"
48 #include <wtf/MathExtras.h>
49
50 namespace WebCore {
51
52 class BoundingRectStrokeStyleApplier : public StrokeStyleApplier {
53 public:
54     BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style)
55         : m_object(object)
56         , m_style(style)
57     {
58         ASSERT(style);
59         ASSERT(object);
60     }
61
62     void strokeStyle(GraphicsContext* gc)
63     {
64         SVGRenderSupport::applyStrokeStyleToContext(gc, m_style, m_object);
65     }
66
67 private:
68     const RenderObject* m_object;
69     RenderStyle* m_style;
70 };
71
72 RenderSVGPath::RenderSVGPath(SVGStyledTransformableElement* node)
73     : RenderSVGModelObject(node)
74     , m_needsBoundariesUpdate(false) // default is false, the cached rects are empty from the beginning
75     , m_needsPathUpdate(true) // default is true, so we grab a Path object once from SVGStyledTransformableElement
76     , m_needsTransformUpdate(true) // default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement
77 {
78 }
79
80 RenderSVGPath::~RenderSVGPath()
81 {
82 }
83
84 bool RenderSVGPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule)
85 {
86     if (!m_fillBoundingBox.contains(point))
87         return false;
88
89     Color fallbackColor;
90     if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
91         return false;
92
93     return m_path.contains(point, fillRule);
94 }
95
96 bool RenderSVGPath::strokeContains(const FloatPoint& point, bool requiresStroke)
97 {
98     if (!m_strokeAndMarkerBoundingBox.contains(point))
99         return false;
100
101     Color fallbackColor;
102     if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
103         return false;
104
105     if (shouldStrokeZeroLengthSubpath())
106         return zeroLengthSubpathRect().contains(point);
107
108     BoundingRectStrokeStyleApplier strokeStyle(this, style());
109     return m_path.strokeContains(&strokeStyle, point);
110 }
111
112 void RenderSVGPath::layout()
113 {
114     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
115     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
116
117     bool updateCachedBoundariesInParents = false;
118
119     bool needsPathUpdate = m_needsPathUpdate;
120     if (needsPathUpdate) {
121         m_path.clear();
122         updatePathFromGraphicsElement(element, m_path);
123         m_needsPathUpdate = false;
124         updateCachedBoundariesInParents = true;
125     }
126
127     if (m_needsTransformUpdate) {
128         m_localTransform = element->animatedLocalTransform();
129         m_needsTransformUpdate = false;
130         updateCachedBoundariesInParents = true;
131     }
132
133     if (m_needsBoundariesUpdate)
134         updateCachedBoundariesInParents = true;
135
136     // Invalidate all resources of this client if our layout changed.
137     if (m_everHadLayout && selfNeedsLayout()) {
138         SVGResourcesCache::clientLayoutChanged(this);
139         m_markerLayoutInfo.clear();
140     }
141
142     // At this point LayoutRepainter already grabbed the old bounds,
143     // recalculate them now so repaintAfterLayout() uses the new bounds.
144     if (needsPathUpdate || m_needsBoundariesUpdate) {
145         updateCachedBoundaries();
146         m_needsBoundariesUpdate = false;
147     }
148
149     // If our bounds changed, notify the parents.
150     if (updateCachedBoundariesInParents)
151         RenderSVGModelObject::setNeedsBoundariesUpdate();
152
153     repainter.repaintAfterLayout();
154     setNeedsLayout(false);
155 }
156
157 bool RenderSVGPath::shouldStrokeZeroLengthSubpath() const
158 {
159     // Spec(11.4): Any zero length subpath shall not be stroked if the ‘stroke-linecap’ property has a value of butt
160     // but shall be stroked if the ‘stroke-linecap’ property has a value of round or square
161     return style()->svgStyle()->hasStroke() && style()->svgStyle()->capStyle() != ButtCap && !m_fillBoundingBox.width() && !m_fillBoundingBox.height();
162 }
163
164 FloatRect RenderSVGPath::zeroLengthSubpathRect() const
165 {
166     SVGElement* svgElement = static_cast<SVGElement*>(node());
167     SVGLengthContext lengthContext(svgElement);
168     float strokeWidth = style()->svgStyle()->strokeWidth().value(lengthContext);
169     return FloatRect(m_fillBoundingBox.x() - strokeWidth / 2, m_fillBoundingBox.y() - strokeWidth / 2, strokeWidth, strokeWidth);
170 }
171
172 void RenderSVGPath::setupSquareCapPath(Path*& usePath, int& applyMode)
173 {
174     // Spec(11.4): Any zero length subpath shall not be stroked if the ‘stroke-linecap’ property has a value of butt
175     // but shall be stroked if the ‘stroke-linecap’ property has a value of round or square
176     DEFINE_STATIC_LOCAL(Path, tempPath, ());
177
178     applyMode = ApplyToFillMode;
179     usePath = &tempPath;
180     usePath->clear();
181     if (style()->svgStyle()->capStyle() == SquareCap)
182         usePath->addRect(zeroLengthSubpathRect());
183     else
184         usePath->addEllipse(zeroLengthSubpathRect());
185 }
186
187 bool RenderSVGPath::setupNonScalingStrokePath(Path*& usePath, GraphicsContextStateSaver& stateSaver)
188 {
189     DEFINE_STATIC_LOCAL(Path, tempPath, ());
190
191     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
192     AffineTransform nonScalingStrokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
193     if (!nonScalingStrokeTransform.isInvertible())
194         return false;
195
196     tempPath = m_path;
197     usePath = &tempPath;
198     tempPath.transform(nonScalingStrokeTransform);
199
200     stateSaver.save();
201     stateSaver.context()->concatCTM(nonScalingStrokeTransform.inverse());
202     return true;
203 }
204
205 void RenderSVGPath::fillAndStrokePath(GraphicsContext* context)
206 {
207     RenderStyle* style = this->style();
208
209     Color fallbackColor;
210     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
211         if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
212             fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
213         else if (fallbackColor.isValid()) {
214             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
215             fallbackResource->setColor(fallbackColor);
216             if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
217                 fallbackResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
218         }
219     }
220
221     fallbackColor = Color();
222     RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor);
223     if (!strokePaintingResource)
224         return;
225
226     Path* usePath = &m_path;
227     int applyMode = ApplyToStrokeMode;
228
229     bool nonScalingStroke = style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE;
230
231     GraphicsContextStateSaver stateSaver(*context, false);
232
233     // Spec(11.4): Any zero length subpath shall not be stroked if the ‘stroke-linecap’ property has a value of butt
234     // but shall be stroked if the ‘stroke-linecap’ property has a value of round or square
235     // FIXME: this does not work for zero-length subpaths, only when total path is zero-length
236     if (shouldStrokeZeroLengthSubpath())
237         setupSquareCapPath(usePath, applyMode);
238     else if (nonScalingStroke) {
239        if (!setupNonScalingStrokePath(usePath, stateSaver))
240            return;
241     }
242
243     if (strokePaintingResource->applyResource(this, style, context, applyMode))
244         strokePaintingResource->postApplyResource(this, context, applyMode, usePath);
245     else if (fallbackColor.isValid()) {
246         RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
247         fallbackResource->setColor(fallbackColor);
248         if (fallbackResource->applyResource(this, style, context, applyMode))
249             fallbackResource->postApplyResource(this, context, applyMode, usePath);
250     }
251 }
252
253 void RenderSVGPath::paint(PaintInfo& paintInfo, const LayoutPoint&)
254 {
255     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
256         return;
257
258     FloatRect boundingBox = repaintRectInLocalCoordinates();
259     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
260         return;
261
262     PaintInfo childPaintInfo(paintInfo);
263     bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
264     if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
265         GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
266         childPaintInfo.applyTransform(m_localTransform);
267
268         if (childPaintInfo.phase == PaintPhaseForeground) {
269             PaintInfo savedInfo(childPaintInfo);
270
271             if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) {
272                 const SVGRenderStyle* svgStyle = style()->svgStyle();
273                 if (svgStyle->shapeRendering() == SR_CRISPEDGES)
274                     childPaintInfo.context->setShouldAntialias(false);
275
276                 fillAndStrokePath(childPaintInfo.context);
277
278                 if (svgStyle->hasMarkers())
279                     m_markerLayoutInfo.drawMarkers(childPaintInfo);
280             }
281
282             SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context);
283         }
284
285         if (drawsOutline)
286             paintOutline(childPaintInfo.context, IntRect(boundingBox));
287     }
288 }
289
290 // This method is called from inside paintOutline() since we call paintOutline()
291 // while transformed to our coord system, return local coords
292 void RenderSVGPath::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint&) 
293 {
294     LayoutRect rect = enclosingLayoutRect(repaintRectInLocalCoordinates());
295     if (!rect.isEmpty())
296         rects.append(rect);
297 }
298
299 bool RenderSVGPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
300 {
301     // We only draw in the forground phase, so we only hit-test then.
302     if (hitTestAction != HitTestForeground)
303         return false;
304
305     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
306
307     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
308         return false;
309
310     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
311     bool isVisible = (style()->visibility() == VISIBLE);
312     if (isVisible || !hitRules.requireVisible) {
313         const SVGRenderStyle* svgStyle = style()->svgStyle();
314         WindRule fillRule = svgStyle->fillRule();
315         if (request.svgClipContent())
316             fillRule = svgStyle->clipRule();
317         if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
318             || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
319             updateHitTestResult(result, roundedLayoutPoint(localPoint));
320             return true;
321         }
322     }
323     return false;
324 }
325
326 FloatRect RenderSVGPath::calculateMarkerBoundsIfNeeded()
327 {
328     SVGElement* svgElement = static_cast<SVGElement*>(node());
329     ASSERT(svgElement && svgElement->document());
330     if (!svgElement->isStyled())
331         return FloatRect();
332
333     SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
334     if (!styledElement->supportsMarkers())
335         return FloatRect();
336
337     const SVGRenderStyle* svgStyle = style()->svgStyle();
338     ASSERT(svgStyle->hasMarkers());
339
340     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
341     if (!resources)
342         return FloatRect();
343
344     RenderSVGResourceMarker* markerStart = resources->markerStart();
345     RenderSVGResourceMarker* markerMid = resources->markerMid();
346     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
347     if (!markerStart && !markerMid && !markerEnd)
348         return FloatRect();
349
350     SVGLengthContext lengthContext(svgElement);
351     return m_markerLayoutInfo.calculateBoundaries(markerStart, markerMid, markerEnd, svgStyle->strokeWidth().value(lengthContext), m_path);
352 }
353
354 void RenderSVGPath::updateCachedBoundaries()
355 {
356     if (m_path.isEmpty()) {
357         m_fillBoundingBox = FloatRect();
358         m_strokeAndMarkerBoundingBox = FloatRect();
359         m_repaintBoundingBox = FloatRect();
360         return;
361     }
362
363     // Cache _unclipped_ fill bounding box, used for calculations in resources
364     m_fillBoundingBox = m_path.fastBoundingRect();
365
366     // Spec(11.4): Any zero length subpath shall not be stroked if the ‘stroke-linecap’ property has a value of butt
367     // but shall be stroked if the ‘stroke-linecap’ property has a value of round or square
368     if (shouldStrokeZeroLengthSubpath()) {
369         m_strokeAndMarkerBoundingBox = zeroLengthSubpathRect();
370         // Cache smallest possible repaint rectangle
371         m_repaintBoundingBox = m_strokeAndMarkerBoundingBox;
372         SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
373         return;
374     }
375
376     // Cache _unclipped_ stroke bounding box, used for calculations in resources (includes marker boundaries)
377     m_strokeAndMarkerBoundingBox = m_fillBoundingBox;
378
379     const SVGRenderStyle* svgStyle = style()->svgStyle();
380     if (svgStyle->hasStroke()) {
381         BoundingRectStrokeStyleApplier strokeStyle(this, style());
382         m_strokeAndMarkerBoundingBox.unite(m_path.strokeBoundingRect(&strokeStyle));
383     }
384
385     if (svgStyle->hasMarkers()) {
386         FloatRect markerBounds = calculateMarkerBoundsIfNeeded();
387         if (!markerBounds.isEmpty())
388             m_strokeAndMarkerBoundingBox.unite(markerBounds);
389     }
390
391     // Cache smallest possible repaint rectangle
392     m_repaintBoundingBox = m_strokeAndMarkerBoundingBox;
393     SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
394 }
395
396 }
397
398 #endif // ENABLE(SVG)