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