Use enum classes and OptionSets for PaintPhase and PaintBehavior
[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  * Copyright (C) 2018 Adobe Systems Incorporated. All rights reserved.
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Library General Public License for more details.
22  *
23  * You should have received a copy of the GNU Library General Public License
24  * along with this library; see the file COPYING.LIB.  If not, write to
25  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26  * Boston, MA 02110-1301, USA.
27  */
28
29 #include "config.h"
30 #include "RenderSVGShape.h"
31
32 #include "FloatPoint.h"
33 #include "FloatQuad.h"
34 #include "GraphicsContext.h"
35 #include "HitTestRequest.h"
36 #include "HitTestResult.h"
37 #include "LayoutRepainter.h"
38 #include "PointerEventsHitRules.h"
39 #include "RenderSVGResourceMarker.h"
40 #include "RenderSVGResourceSolidColor.h"
41 #include "SVGPathData.h"
42 #include "SVGRenderingContext.h"
43 #include "SVGResources.h"
44 #include "SVGResourcesCache.h"
45 #include "SVGURIReference.h"
46 #include "StrokeStyleApplier.h"
47 #include <wtf/IsoMallocInlines.h>
48 #include <wtf/StackStats.h>
49
50 namespace WebCore {
51
52 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGShape);
53
54 class BoundingRectStrokeStyleApplier final : public StrokeStyleApplier {
55 public:
56     BoundingRectStrokeStyleApplier(const RenderSVGShape& renderer)
57         : m_renderer(renderer)
58     {
59     }
60
61     void strokeStyle(GraphicsContext* context) override
62     {
63         SVGRenderSupport::applyStrokeStyleToContext(context, m_renderer.style(), m_renderer);
64     }
65
66 private:
67     const RenderSVGShape& m_renderer;
68 };
69
70 RenderSVGShape::RenderSVGShape(SVGGraphicsElement& element, RenderStyle&& style)
71     : RenderSVGModelObject(element, WTFMove(style))
72     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
73     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
74     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
75 {
76 }
77
78 RenderSVGShape::~RenderSVGShape() = default;
79
80 void RenderSVGShape::updateShapeFromElement()
81 {
82     m_path = std::make_unique<Path>(pathFromGraphicsElement(&graphicsElement()));
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, PointCoordinateSpace pointCoordinateSpace)
115 {
116     ASSERT(m_path);
117     BoundingRectStrokeStyleApplier applier(*this);
118
119     if (hasNonScalingStroke() && pointCoordinateSpace != LocalCoordinateSpace) {
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, RenderSVGResourceMode::ApplyToFill))
238             fillPaintingResource->postApplyResource(*this, context, RenderSVGResourceMode::ApplyToFill, nullptr, this);
239         else if (fallbackColor.isValid()) {
240             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
241             fallbackResource->setColor(fallbackColor);
242             if (fallbackResource->applyResource(*this, style, context, RenderSVGResourceMode::ApplyToFill))
243                 fallbackResource->postApplyResource(*this, context, RenderSVGResourceMode::ApplyToFill, nullptr, 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, RenderSVGResourceMode::ApplyToStroke))
254             strokePaintingResource->postApplyResource(*this, context, RenderSVGResourceMode::ApplyToStroke, nullptr, this);
255         else if (fallbackColor.isValid()) {
256             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
257             fallbackResource->setColor(fallbackColor);
258             if (fallbackResource->applyResource(*this, style, context, RenderSVGResourceMode::ApplyToStroke))
259                 fallbackResource->postApplyResource(*this, context, RenderSVGResourceMode::ApplyToStroke, nullptr, 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 != PaintPhase::Foreground
300         || style().visibility() == 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 == PaintPhase::Foreground) {
311         SVGRenderingContext renderingContext(*this, childPaintInfo);
312
313         if (renderingContext.isRenderingPrepared()) {
314             const SVGRenderStyle& svgStyle = style().svgStyle();
315             if (svgStyle.shapeRendering() == ShapeRendering::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::isPointInFill(const FloatPoint& point)
336 {
337     return shapeDependentFillContains(point, style().svgStyle().fillRule());
338 }
339
340 bool RenderSVGShape::isPointInStroke(const FloatPoint& point)
341 {
342     if (!style().svgStyle().hasStroke())
343         return false;
344
345     return shapeDependentStrokeContains(point, LocalCoordinateSpace);
346 }
347
348 float RenderSVGShape::getTotalLength() const
349 {
350     if (m_path)
351         return m_path->length();
352
353     return 0;
354 }
355
356 void RenderSVGShape::getPointAtLength(FloatPoint& point, float distance) const
357 {
358     if (!m_path)
359         return;
360
361     bool isValid;
362     point = m_path->pointAtLength(distance, isValid);
363 }
364
365 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
366 {
367     // We only draw in the forground phase, so we only hit-test then.
368     if (hitTestAction != HitTestForeground)
369         return false;
370
371     FloatPoint localPoint = m_localTransform.inverse().value_or(AffineTransform()).mapPoint(pointInParent);
372
373     if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
374         return false;
375
376     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style().pointerEvents());
377     bool isVisible = (style().visibility() == Visibility::Visible);
378     if (isVisible || !hitRules.requireVisible) {
379         const SVGRenderStyle& svgStyle = style().svgStyle();
380         WindRule fillRule = svgStyle.fillRule();
381         if (request.svgClipContent())
382             fillRule = svgStyle.clipRule();
383         if ((hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
384             || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
385             updateHitTestResult(result, LayoutPoint(localPoint));
386             if (result.addNodeToListBasedTestResult(&graphicsElement(), request, localPoint) == HitTestProgress::Stop)
387                 return true;
388         }
389     }
390     return false;
391 }
392
393 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
394 {
395     switch (type) {
396     case StartMarker:
397         return markerStart;
398     case MidMarker:
399         return markerMid;
400     case EndMarker:
401         return markerEnd;
402     }
403
404     ASSERT_NOT_REACHED();
405     return 0;
406 }
407
408 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
409 {
410     ASSERT(!m_markerPositions.isEmpty());
411
412     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
413     ASSERT(resources);
414
415     RenderSVGResourceMarker* markerStart = resources->markerStart();
416     RenderSVGResourceMarker* markerMid = resources->markerMid();
417     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
418     ASSERT(markerStart || markerMid || markerEnd);
419
420     FloatRect boundaries;
421     unsigned size = m_markerPositions.size();
422     for (unsigned i = 0; i < size; ++i) {
423         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
424             boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
425     }
426     return boundaries;
427 }
428
429 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
430 {
431     return path().boundingRect();
432 }
433
434 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
435 {
436     ASSERT(m_path);
437     FloatRect strokeBoundingBox = m_fillBoundingBox;
438
439     const SVGRenderStyle& svgStyle = style().svgStyle();
440     if (svgStyle.hasStroke()) {
441         BoundingRectStrokeStyleApplier strokeStyle(*this);
442         if (hasNonScalingStroke()) {
443             AffineTransform nonScalingTransform = nonScalingStrokeTransform();
444             if (std::optional<AffineTransform> inverse = nonScalingTransform.inverse()) {
445                 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
446                 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle);
447                 strokeBoundingRect = inverse.value().mapRect(strokeBoundingRect);
448                 strokeBoundingBox.unite(strokeBoundingRect);
449             }
450         } else
451             strokeBoundingBox.unite(path().strokeBoundingRect(&strokeStyle));
452     }
453
454     if (!m_markerPositions.isEmpty())
455         strokeBoundingBox.unite(markerRect(strokeWidth()));
456
457     return strokeBoundingBox;
458 }
459
460 void RenderSVGShape::updateRepaintBoundingBox()
461 {
462     m_repaintBoundingBoxExcludingShadow = strokeBoundingBox();
463     SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow);
464
465     m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
466     SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox);
467 }
468
469 float RenderSVGShape::strokeWidth() const
470 {
471     SVGLengthContext lengthContext(&graphicsElement());
472     return lengthContext.valueForLength(style().strokeWidth());
473 }
474
475 bool RenderSVGShape::hasSmoothStroke() const
476 {
477     const SVGRenderStyle& svgStyle = style().svgStyle();
478     return svgStyle.strokeDashArray().isEmpty()
479         && style().strokeMiterLimit() == style().initialStrokeMiterLimit()
480         && style().joinStyle() == style().initialJoinStyle()
481         && style().capStyle() == style().initialCapStyle();
482 }
483
484 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
485 {
486     ASSERT(!m_markerPositions.isEmpty());
487
488     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
489     if (!resources)
490         return;
491
492     RenderSVGResourceMarker* markerStart = resources->markerStart();
493     RenderSVGResourceMarker* markerMid = resources->markerMid();
494     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
495     if (!markerStart && !markerMid && !markerEnd)
496         return;
497
498     float strokeWidth = this->strokeWidth();
499     unsigned size = m_markerPositions.size();
500     for (unsigned i = 0; i < size; ++i) {
501         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
502             marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
503     }
504 }
505
506 void RenderSVGShape::processMarkerPositions()
507 {
508     m_markerPositions.clear();
509
510     if (!shouldGenerateMarkerPositions())
511         return;
512
513     ASSERT(m_path);
514
515     SVGMarkerData markerData(m_markerPositions, SVGResourcesCache::cachedResourcesForRenderer(*this)->markerReverseStart());
516     m_path->apply([&markerData](const PathElement& pathElement) {
517         SVGMarkerData::updateFromPathElement(markerData, pathElement);
518     });
519     markerData.pathIsDone();
520 }
521
522 }