822858371c35b61c15d51e236910ec4c7cbcd5ca
[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 #include "RenderSVGShape.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 "RenderSVGResourceMarker.h"
38 #include "RenderSVGResourceSolidColor.h"
39 #include "SVGPathData.h"
40 #include "SVGRenderingContext.h"
41 #include "SVGResources.h"
42 #include "SVGResourcesCache.h"
43 #include "SVGTransformList.h"
44 #include "SVGURIReference.h"
45 #include "StrokeStyleApplier.h"
46 #include <wtf/StackStats.h>
47
48 namespace WebCore {
49
50 class BoundingRectStrokeStyleApplier final : public StrokeStyleApplier {
51 public:
52     BoundingRectStrokeStyleApplier(const RenderSVGShape& renderer)
53         : m_renderer(renderer)
54     {
55     }
56
57     virtual void strokeStyle(GraphicsContext* context) override
58     {
59         SVGRenderSupport::applyStrokeStyleToContext(context, m_renderer.style(), m_renderer);
60     }
61
62 private:
63     const RenderSVGShape& m_renderer;
64 };
65
66 RenderSVGShape::RenderSVGShape(SVGGraphicsElement& element, Ref<RenderStyle>&& style)
67     : RenderSVGModelObject(element, WTFMove(style))
68     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
69     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
70     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
71 {
72 }
73
74 RenderSVGShape::~RenderSVGShape()
75 {
76 }
77
78 void RenderSVGShape::updateShapeFromElement()
79 {
80     m_path = std::make_unique<Path>();
81     ASSERT(RenderSVGShape::isEmpty());
82
83     updatePathFromGraphicsElement(&graphicsElement(), path());
84     processMarkerPositions();
85
86     m_fillBoundingBox = calculateObjectBoundingBox();
87     m_strokeBoundingBox = calculateStrokeBoundingBox();
88 }
89
90 bool RenderSVGShape::isEmpty() const
91 {
92     return path().isEmpty();
93 }
94
95 void RenderSVGShape::fillShape(GraphicsContext& context) const
96 {
97     context.fillPath(path());
98 }
99
100 void RenderSVGShape::strokeShape(GraphicsContext& context) const
101 {
102     ASSERT(m_path);
103     Path* usePath = m_path.get();
104
105     if (hasNonScalingStroke())
106         usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
107
108     context.strokePath(*usePath);
109 }
110
111 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
112 {
113     ASSERT(m_path);
114     BoundingRectStrokeStyleApplier applier(*this);
115
116     if (hasNonScalingStroke()) {
117         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
118         Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
119
120         return usePath->strokeContains(&applier, nonScalingTransform.mapPoint(point));
121     }
122
123     return m_path->strokeContains(&applier, point);
124 }
125
126 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
127 {
128     return path().contains(point, fillRule);
129 }
130
131 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
132 {
133     if (!m_fillBoundingBox.contains(point))
134         return false;
135
136     Color fallbackColor;
137     if (requiresFill && !RenderSVGResource::fillPaintingResource(*this, style(), fallbackColor))
138         return false;
139
140     return shapeDependentFillContains(point, fillRule);
141 }
142
143 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
144 {
145     if (!strokeBoundingBox().contains(point))
146         return false;
147
148     Color fallbackColor;
149     if (requiresStroke && !RenderSVGResource::strokePaintingResource(*this, style(), fallbackColor))
150         return false;
151
152     return shapeDependentStrokeContains(point);
153 }
154
155 void RenderSVGShape::layout()
156 {
157     StackStats::LayoutCheckPoint layoutCheckPoint;
158     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) && selfNeedsLayout());
159
160     bool updateCachedBoundariesInParents = false;
161
162     if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
163         updateShapeFromElement();
164         m_needsShapeUpdate = false;
165         updateRepaintBoundingBox();
166         m_needsBoundariesUpdate = false;
167         updateCachedBoundariesInParents = true;
168     }
169
170     if (m_needsTransformUpdate) {
171         m_localTransform = graphicsElement().animatedLocalTransform();
172         m_needsTransformUpdate = false;
173         updateCachedBoundariesInParents = true;
174     }
175
176     // Invalidate all resources of this client if our layout changed.
177     if (everHadLayout() && selfNeedsLayout())
178         SVGResourcesCache::clientLayoutChanged(*this);
179
180     // If our bounds changed, notify the parents.
181     if (updateCachedBoundariesInParents)
182         RenderSVGModelObject::setNeedsBoundariesUpdate();
183
184     repainter.repaintAfterLayout();
185     clearNeedsLayout();
186 }
187
188 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
189 {
190     DEPRECATED_DEFINE_STATIC_LOCAL(Path, tempPath, ());
191
192     tempPath = *path;
193     tempPath.transform(strokeTransform);
194
195     return &tempPath;
196 }
197
198 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
199 {
200     Optional<AffineTransform> inverse = strokeTransform.inverse();
201     if (!inverse)
202         return false;
203
204     stateSaver.save();
205     stateSaver.context()->concatCTM(inverse.value());
206     return true;
207 }
208
209 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
210 {
211     return graphicsElement().getScreenCTM(SVGLocatable::DisallowStyleUpdate);
212 }
213
214 bool RenderSVGShape::shouldGenerateMarkerPositions() const
215 {
216     if (!style().svgStyle().hasMarkers())
217         return false;
218
219     if (!graphicsElement().supportsMarkers())
220         return false;
221
222     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
223     if (!resources)
224         return false;
225
226     return resources->markerStart() || resources->markerMid() || resources->markerEnd();
227 }
228
229 void RenderSVGShape::fillShape(const RenderStyle& style, GraphicsContext& originalContext)
230 {
231     GraphicsContext* context = &originalContext;
232     Color fallbackColor;
233     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(*this, style, fallbackColor)) {
234         if (fillPaintingResource->applyResource(*this, style, context, ApplyToFillMode))
235             fillPaintingResource->postApplyResource(*this, context, ApplyToFillMode, 0, this);
236         else if (fallbackColor.isValid()) {
237             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
238             fallbackResource->setColor(fallbackColor);
239             if (fallbackResource->applyResource(*this, style, context, ApplyToFillMode))
240                 fallbackResource->postApplyResource(*this, context, ApplyToFillMode, 0, this);
241         }
242     }
243 }
244
245 void RenderSVGShape::strokeShape(const RenderStyle& style, GraphicsContext& originalContext)
246 {
247     GraphicsContext* context = &originalContext;
248     Color fallbackColor;
249     if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(*this, style, fallbackColor)) {
250         if (strokePaintingResource->applyResource(*this, style, context, ApplyToStrokeMode))
251             strokePaintingResource->postApplyResource(*this, context, ApplyToStrokeMode, 0, this);
252         else if (fallbackColor.isValid()) {
253             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
254             fallbackResource->setColor(fallbackColor);
255             if (fallbackResource->applyResource(*this, style, context, ApplyToStrokeMode))
256                 fallbackResource->postApplyResource(*this, context, ApplyToStrokeMode, 0, this);
257         }
258     }
259 }
260
261 void RenderSVGShape::strokeShape(GraphicsContext& context)
262 {
263     if (!style().svgStyle().hasVisibleStroke())
264         return;
265
266     GraphicsContextStateSaver stateSaver(context, false);
267     if (hasNonScalingStroke()) {
268         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
269         if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
270             return;
271     }
272     strokeShape(style(), context);
273 }
274
275 void RenderSVGShape::fillStrokeMarkers(PaintInfo& childPaintInfo)
276 {
277     auto paintOrder = style().svgStyle().paintTypesForPaintOrder();
278     for (unsigned i = 0; i < paintOrder.size(); ++i) {
279         switch (paintOrder.at(i)) {
280         case PaintTypeFill:
281             fillShape(style(), childPaintInfo.context());
282             break;
283         case PaintTypeStroke:
284             strokeShape(childPaintInfo.context());
285             break;
286         case PaintTypeMarkers:
287             if (!m_markerPositions.isEmpty())
288                 drawMarkers(childPaintInfo);
289             break;
290         }
291     }
292 }
293
294 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
295 {
296     if (paintInfo.context().paintingDisabled() || paintInfo.phase != PaintPhaseForeground
297         || style().visibility() == HIDDEN || isEmpty())
298         return;
299     FloatRect boundingBox = repaintRectInLocalCoordinates();
300     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
301         return;
302
303     PaintInfo childPaintInfo(paintInfo);
304     GraphicsContextStateSaver stateSaver(childPaintInfo.context());
305     childPaintInfo.applyTransform(m_localTransform);
306
307     if (childPaintInfo.phase == PaintPhaseForeground) {
308         SVGRenderingContext renderingContext(*this, childPaintInfo);
309
310         if (renderingContext.isRenderingPrepared()) {
311             const SVGRenderStyle& svgStyle = style().svgStyle();
312             if (svgStyle.shapeRendering() == SR_CRISPEDGES)
313                 childPaintInfo.context().setShouldAntialias(false);
314
315             fillStrokeMarkers(childPaintInfo);
316         }
317     }
318
319     if (style().outlineWidth())
320         paintOutline(childPaintInfo, IntRect(boundingBox));
321 }
322
323 // This method is called from inside paintOutline() since we call paintOutline()
324 // while transformed to our coord system, return local coords
325 void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
326 {
327     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
328     if (!rect.isEmpty())
329         rects.append(rect);
330 }
331
332 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
333 {
334     // We only draw in the forground phase, so we only hit-test then.
335     if (hitTestAction != HitTestForeground)
336         return false;
337
338     FloatPoint localPoint = m_localTransform.inverse().valueOr(AffineTransform()).mapPoint(pointInParent);
339
340     if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
341         return false;
342
343     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style().pointerEvents());
344     bool isVisible = (style().visibility() == VISIBLE);
345     if (isVisible || !hitRules.requireVisible) {
346         const SVGRenderStyle& svgStyle = style().svgStyle();
347         WindRule fillRule = svgStyle.fillRule();
348         if (request.svgClipContent())
349             fillRule = svgStyle.clipRule();
350         if ((hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
351             || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
352             updateHitTestResult(result, LayoutPoint(localPoint));
353             return true;
354         }
355     }
356     return false;
357 }
358
359 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
360 {
361     switch (type) {
362     case StartMarker:
363         return markerStart;
364     case MidMarker:
365         return markerMid;
366     case EndMarker:
367         return markerEnd;
368     }
369
370     ASSERT_NOT_REACHED();
371     return 0;
372 }
373
374 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
375 {
376     ASSERT(!m_markerPositions.isEmpty());
377
378     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
379     ASSERT(resources);
380
381     RenderSVGResourceMarker* markerStart = resources->markerStart();
382     RenderSVGResourceMarker* markerMid = resources->markerMid();
383     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
384     ASSERT(markerStart || markerMid || markerEnd);
385
386     FloatRect boundaries;
387     unsigned size = m_markerPositions.size();
388     for (unsigned i = 0; i < size; ++i) {
389         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
390             boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
391     }
392     return boundaries;
393 }
394
395 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
396 {
397     return path().fastBoundingRect();
398 }
399
400 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
401 {
402     ASSERT(m_path);
403     FloatRect strokeBoundingBox = m_fillBoundingBox;
404
405     const SVGRenderStyle& svgStyle = style().svgStyle();
406     if (svgStyle.hasStroke()) {
407         BoundingRectStrokeStyleApplier strokeStyle(*this);
408         if (hasNonScalingStroke()) {
409             AffineTransform nonScalingTransform = nonScalingStrokeTransform();
410             if (Optional<AffineTransform> inverse = nonScalingTransform.inverse()) {
411                 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
412                 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle);
413                 strokeBoundingRect = inverse.value().mapRect(strokeBoundingRect);
414                 strokeBoundingBox.unite(strokeBoundingRect);
415             }
416         } else
417             strokeBoundingBox.unite(path().strokeBoundingRect(&strokeStyle));
418     }
419
420     if (!m_markerPositions.isEmpty())
421         strokeBoundingBox.unite(markerRect(strokeWidth()));
422
423     return strokeBoundingBox;
424 }
425
426 void RenderSVGShape::updateRepaintBoundingBox()
427 {
428     m_repaintBoundingBoxExcludingShadow = strokeBoundingBox();
429     SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow);
430
431     m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
432     SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox);
433 }
434
435 float RenderSVGShape::strokeWidth() const
436 {
437     SVGLengthContext lengthContext(&graphicsElement());
438     return lengthContext.valueForLength(style().svgStyle().strokeWidth());
439 }
440
441 bool RenderSVGShape::hasSmoothStroke() const
442 {
443     const SVGRenderStyle& svgStyle = style().svgStyle();
444     return svgStyle.strokeDashArray().isEmpty()
445         && svgStyle.strokeMiterLimit() == svgStyle.initialStrokeMiterLimit()
446         && svgStyle.joinStyle() == svgStyle.initialJoinStyle()
447         && svgStyle.capStyle() == svgStyle.initialCapStyle();
448 }
449
450 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
451 {
452     ASSERT(!m_markerPositions.isEmpty());
453
454     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
455     if (!resources)
456         return;
457
458     RenderSVGResourceMarker* markerStart = resources->markerStart();
459     RenderSVGResourceMarker* markerMid = resources->markerMid();
460     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
461     if (!markerStart && !markerMid && !markerEnd)
462         return;
463
464     float strokeWidth = this->strokeWidth();
465     unsigned size = m_markerPositions.size();
466     for (unsigned i = 0; i < size; ++i) {
467         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
468             marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
469     }
470 }
471
472 void RenderSVGShape::processMarkerPositions()
473 {
474     m_markerPositions.clear();
475
476     if (!shouldGenerateMarkerPositions())
477         return;
478
479     ASSERT(m_path);
480
481     SVGMarkerData markerData(m_markerPositions);
482     m_path->apply([&markerData](const PathElement& pathElement) {
483         SVGMarkerData::updateFromPathElement(markerData, pathElement);
484     });
485     markerData.pathIsDone();
486 }
487
488 }