9148dbbdf46b8ec62282c2c1958dd3963e73c6c1
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGShape.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  * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
10  * Copyright (C) 2011 University of Szeged
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  */
27
28 #include "config.h"
29
30 #if ENABLE(SVG)
31 #include "RenderSVGShape.h"
32
33 #include "FloatPoint.h"
34 #include "FloatQuad.h"
35 #include "GraphicsContext.h"
36 #include "HitTestRequest.h"
37 #include "LayoutRepainter.h"
38 #include "PointerEventsHitRules.h"
39 #include "RenderSVGContainer.h"
40 #include "RenderSVGResourceMarker.h"
41 #include "RenderSVGResourceSolidColor.h"
42 #include "SVGGraphicsElement.h"
43 #include "SVGPathData.h"
44 #include "SVGRenderingContext.h"
45 #include "SVGResources.h"
46 #include "SVGResourcesCache.h"
47 #include "SVGTransformList.h"
48 #include "SVGURIReference.h"
49 #include "StrokeStyleApplier.h"
50 #include <wtf/MathExtras.h>
51 #include <wtf/StackStats.h>
52
53 namespace WebCore {
54
55 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
56     : RenderSVGModelObject(node)
57     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
58     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
59     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
60 {
61 }
62
63 RenderSVGShape::~RenderSVGShape()
64 {
65 }
66
67 void RenderSVGShape::updateShapeFromElement()
68 {
69     m_path.clear();
70     m_path = adoptPtr(new Path);
71     ASSERT(RenderSVGShape::isEmpty());
72
73     updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path());
74     processMarkerPositions();
75
76     m_fillBoundingBox = calculateObjectBoundingBox();
77     m_strokeBoundingBox = calculateStrokeBoundingBox();
78 }
79
80 bool RenderSVGShape::isEmpty() const
81 {
82     return path().isEmpty();
83 }
84
85 void RenderSVGShape::fillShape(GraphicsContext* context) const
86 {
87     context->fillPath(path());
88 }
89
90 void RenderSVGShape::strokeShape(GraphicsContext* context) const
91 {
92     ASSERT(m_path);
93     Path* usePath = m_path.get();
94
95     if (hasNonScalingStroke())
96         usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
97
98     context->strokePath(*usePath);
99 }
100
101 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
102 {
103     ASSERT(m_path);
104     BoundingRectStrokeStyleApplier applier(this, style());
105
106     if (hasNonScalingStroke()) {
107         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
108         Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
109
110         return usePath->strokeContains(&applier, nonScalingTransform.mapPoint(point));
111     }
112
113     return m_path->strokeContains(&applier, point);
114 }
115
116 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
117 {
118     return path().contains(point, fillRule);
119 }
120
121 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
122 {
123     if (!m_fillBoundingBox.contains(point))
124         return false;
125
126     Color fallbackColor;
127     if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
128         return false;
129
130     return shapeDependentFillContains(point, fillRule);
131 }
132
133 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
134 {
135     if (!strokeBoundingBox().contains(point))
136         return false;
137
138     Color fallbackColor;
139     if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
140         return false;
141
142     return shapeDependentStrokeContains(point);
143 }
144
145 void RenderSVGShape::layout()
146 {
147     StackStats::LayoutCheckPoint layoutCheckPoint;
148     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
149
150     bool updateCachedBoundariesInParents = false;
151
152     if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
153         updateShapeFromElement();
154         m_needsShapeUpdate = false;
155         updateRepaintBoundingBox();
156         m_needsBoundariesUpdate = false;
157         updateCachedBoundariesInParents = true;
158     }
159
160     if (m_needsTransformUpdate) {
161         m_localTransform = toSVGGraphicsElement(element())->animatedLocalTransform();
162         m_needsTransformUpdate = false;
163         updateCachedBoundariesInParents = true;
164     }
165
166     // Invalidate all resources of this client if our layout changed.
167     if (everHadLayout() && selfNeedsLayout())
168         SVGResourcesCache::clientLayoutChanged(this);
169
170     // If our bounds changed, notify the parents.
171     if (updateCachedBoundariesInParents)
172         RenderSVGModelObject::setNeedsBoundariesUpdate();
173
174     repainter.repaintAfterLayout();
175     setNeedsLayout(false);
176 }
177
178 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
179 {
180     DEFINE_STATIC_LOCAL(Path, tempPath, ());
181
182     tempPath = *path;
183     tempPath.transform(strokeTransform);
184
185     return &tempPath;
186 }
187
188 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
189 {
190     if (!strokeTransform.isInvertible())
191         return false;
192
193     stateSaver.save();
194     stateSaver.context()->concatCTM(strokeTransform.inverse());
195     return true;
196 }
197
198 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
199 {
200     return toSVGGraphicsElement(element())->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
201 }
202
203 bool RenderSVGShape::shouldGenerateMarkerPositions() const
204 {
205     if (!style()->svgStyle()->hasMarkers())
206         return false;
207
208     if (!toSVGGraphicsElement(element())->supportsMarkers())
209         return false;
210
211     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
212     if (!resources)
213         return false;
214
215     return resources->markerStart() || resources->markerMid() || resources->markerEnd();
216 }
217
218 void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
219 {
220     Color fallbackColor;
221     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
222         if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
223             fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
224         else if (fallbackColor.isValid()) {
225             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
226             fallbackResource->setColor(fallbackColor);
227             if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
228                 fallbackResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
229         }
230     }
231 }
232
233 void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
234 {
235     Color fallbackColor;
236     if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor)) {
237         if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
238             strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
239         else if (fallbackColor.isValid()) {
240             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
241             fallbackResource->setColor(fallbackColor);
242             if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
243                 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
244         }
245     }
246 }
247
248 void RenderSVGShape::fillAndStrokeShape(GraphicsContext* context)
249 {
250     RenderStyle* style = this->style();
251
252     fillShape(style, context);
253
254     if (!style->svgStyle()->hasVisibleStroke())
255         return;
256
257     GraphicsContextStateSaver stateSaver(*context, false);
258
259     if (hasNonScalingStroke()) {
260         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
261         if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
262             return;
263     }
264
265     strokeShape(style, context);
266 }
267
268 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
269 {
270     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || isEmpty())
271         return;
272     FloatRect boundingBox = repaintRectInLocalCoordinates();
273     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
274         return;
275
276     PaintInfo childPaintInfo(paintInfo);
277     bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
278     if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
279         GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
280         childPaintInfo.applyTransform(m_localTransform);
281
282         if (childPaintInfo.phase == PaintPhaseForeground) {
283             SVGRenderingContext renderingContext(this, childPaintInfo);
284
285             if (renderingContext.isRenderingPrepared()) {
286                 const SVGRenderStyle* svgStyle = style()->svgStyle();
287                 if (svgStyle->shapeRendering() == SR_CRISPEDGES)
288                     childPaintInfo.context->setShouldAntialias(false);
289
290                 fillAndStrokeShape(childPaintInfo.context);
291                 if (!m_markerPositions.isEmpty())
292                     drawMarkers(childPaintInfo);
293             }
294         }
295
296         if (drawsOutline)
297             paintOutline(childPaintInfo, IntRect(boundingBox));
298     }
299 }
300
301 // This method is called from inside paintOutline() since we call paintOutline()
302 // while transformed to our coord system, return local coords
303 void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
304 {
305     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
306     if (!rect.isEmpty())
307         rects.append(rect);
308 }
309
310 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
311 {
312     // We only draw in the forground phase, so we only hit-test then.
313     if (hitTestAction != HitTestForeground)
314         return false;
315
316     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
317
318     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
319         return false;
320
321     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
322     bool isVisible = (style()->visibility() == VISIBLE);
323     if (isVisible || !hitRules.requireVisible) {
324         const SVGRenderStyle* svgStyle = style()->svgStyle();
325         WindRule fillRule = svgStyle->fillRule();
326         if (request.svgClipContent())
327             fillRule = svgStyle->clipRule();
328         if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
329             || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
330             updateHitTestResult(result, roundedLayoutPoint(localPoint));
331             return true;
332         }
333     }
334     return false;
335 }
336
337 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
338 {
339     switch (type) {
340     case StartMarker:
341         return markerStart;
342     case MidMarker:
343         return markerMid;
344     case EndMarker:
345         return markerEnd;
346     }
347
348     ASSERT_NOT_REACHED();
349     return 0;
350 }
351
352 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
353 {
354     ASSERT(!m_markerPositions.isEmpty());
355
356     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
357     ASSERT(resources);
358
359     RenderSVGResourceMarker* markerStart = resources->markerStart();
360     RenderSVGResourceMarker* markerMid = resources->markerMid();
361     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
362     ASSERT(markerStart || markerMid || markerEnd);
363
364     FloatRect boundaries;
365     unsigned size = m_markerPositions.size();
366     for (unsigned i = 0; i < size; ++i) {
367         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
368             boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
369     }
370     return boundaries;
371 }
372
373 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
374 {
375     return path().fastBoundingRect();
376 }
377
378 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
379 {
380     ASSERT(m_path);
381     FloatRect strokeBoundingBox = m_fillBoundingBox;
382
383     const SVGRenderStyle* svgStyle = style()->svgStyle();
384     if (svgStyle->hasStroke()) {
385         BoundingRectStrokeStyleApplier strokeStyle(this, style());
386         if (hasNonScalingStroke()) {
387             AffineTransform nonScalingTransform = nonScalingStrokeTransform();
388             if (nonScalingTransform.isInvertible()) {
389                 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
390                 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle);
391                 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
392                 strokeBoundingBox.unite(strokeBoundingRect);
393             }
394         } else
395             strokeBoundingBox.unite(path().strokeBoundingRect(&strokeStyle));
396     }
397
398     if (!m_markerPositions.isEmpty())
399         strokeBoundingBox.unite(markerRect(strokeWidth()));
400
401     return strokeBoundingBox;
402 }
403
404 void RenderSVGShape::updateRepaintBoundingBox()
405 {
406     m_repaintBoundingBoxExcludingShadow = strokeBoundingBox();
407     SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBoxExcludingShadow);
408
409     m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
410     SVGRenderSupport::intersectRepaintRectWithShadows(this, m_repaintBoundingBox);
411 }
412
413 float RenderSVGShape::strokeWidth() const
414 {
415     SVGLengthContext lengthContext(element());
416     return style()->svgStyle()->strokeWidth().value(lengthContext);
417 }
418
419 bool RenderSVGShape::hasSmoothStroke() const
420 {
421     const SVGRenderStyle* svgStyle = style()->svgStyle();
422     return svgStyle->strokeDashArray().isEmpty()
423         && svgStyle->strokeMiterLimit() == svgStyle->initialStrokeMiterLimit()
424         && svgStyle->joinStyle() == svgStyle->initialJoinStyle()
425         && svgStyle->capStyle() == svgStyle->initialCapStyle();
426 }
427
428 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
429 {
430     ASSERT(!m_markerPositions.isEmpty());
431
432     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
433     if (!resources)
434         return;
435
436     RenderSVGResourceMarker* markerStart = resources->markerStart();
437     RenderSVGResourceMarker* markerMid = resources->markerMid();
438     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
439     if (!markerStart && !markerMid && !markerEnd)
440         return;
441
442     float strokeWidth = this->strokeWidth();
443     unsigned size = m_markerPositions.size();
444     for (unsigned i = 0; i < size; ++i) {
445         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
446             marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
447     }
448 }
449
450 void RenderSVGShape::processMarkerPositions()
451 {
452     m_markerPositions.clear();
453
454     if (!shouldGenerateMarkerPositions())
455         return;
456
457     ASSERT(m_path);
458
459     SVGMarkerData markerData(m_markerPositions);
460     m_path->apply(&markerData, SVGMarkerData::updateFromPathElement);
461     markerData.pathIsDone();
462 }
463
464 }
465
466 #endif // ENABLE(SVG)