Implement SVGGeometryElement's isPointInFill and isPointInStroke
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGRect.cpp
1 /*
2  * Copyright (C) 2011 University of Szeged
3  * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "RenderSVGRect.h"
30
31 #include <wtf/IsoMallocInlines.h>
32
33 namespace WebCore {
34
35 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGRect);
36
37 RenderSVGRect::RenderSVGRect(SVGRectElement& element, RenderStyle&& style)
38     : RenderSVGShape(element, WTFMove(style))
39     , m_usePathFallback(false)
40 {
41 }
42
43 RenderSVGRect::~RenderSVGRect() = default;
44
45 SVGRectElement& RenderSVGRect::rectElement() const
46 {
47     return downcast<SVGRectElement>(RenderSVGShape::graphicsElement());
48 }
49
50 void RenderSVGRect::updateShapeFromElement()
51 {
52     // Before creating a new object we need to clear the cached bounding box
53     // to avoid using garbage.
54     m_fillBoundingBox = FloatRect();
55     m_innerStrokeRect = FloatRect();
56     m_outerStrokeRect = FloatRect();
57     clearPath();
58
59     SVGLengthContext lengthContext(&rectElement());
60     FloatSize boundingBoxSize(lengthContext.valueForLength(style().width(), LengthModeWidth), lengthContext.valueForLength(style().height(), LengthModeHeight));
61
62     // Element is invalid if either dimension is negative.
63     if (boundingBoxSize.width() < 0 || boundingBoxSize.height() < 0)
64         return;
65
66     // Rendering enabled? Spec: "A value of zero disables rendering of the element."
67     if (!boundingBoxSize.isEmpty()) {
68         if (rectElement().rx().value(lengthContext) > 0 || rectElement().ry().value(lengthContext) > 0 || hasNonScalingStroke()) {
69             // Fall back to RenderSVGShape
70             RenderSVGShape::updateShapeFromElement();
71             m_usePathFallback = true;
72             return;
73         }
74         m_usePathFallback = false;
75     }
76
77     m_fillBoundingBox = FloatRect(FloatPoint(lengthContext.valueForLength(style().svgStyle().x(), LengthModeWidth),
78         lengthContext.valueForLength(style().svgStyle().y(), LengthModeHeight)),
79         boundingBoxSize);
80
81     // To decide if the stroke contains a point we create two rects which represent the inner and
82     // the outer stroke borders. A stroke contains the point, if the point is between them.
83     m_innerStrokeRect = m_fillBoundingBox;
84     m_outerStrokeRect = m_fillBoundingBox;
85
86     if (style().svgStyle().hasStroke()) {
87         float strokeWidth = this->strokeWidth();
88         m_innerStrokeRect.inflate(-strokeWidth / 2);
89         m_outerStrokeRect.inflate(strokeWidth / 2);
90     }
91
92     m_strokeBoundingBox = m_outerStrokeRect;
93
94 #if USE(CG)
95     // CoreGraphics can inflate the stroke by 1px when drawing a rectangle with antialiasing disabled at non-integer coordinates, we need to compensate.
96     if (style().svgStyle().shapeRendering() == SR_CRISPEDGES)
97         m_strokeBoundingBox.inflate(1);
98 #endif
99 }
100
101 void RenderSVGRect::fillShape(GraphicsContext& context) const
102 {
103     if (m_usePathFallback) {
104         RenderSVGShape::fillShape(context);
105         return;
106     }
107
108 #if USE(CG)
109     // FIXME: CG implementation of GraphicsContextCG::fillRect has an own
110     // shadow drawing method, which draws an extra shadow.
111     // This is a workaround for switching off the extra shadow.
112     // https://bugs.webkit.org/show_bug.cgi?id=68899
113     if (context.hasShadow()) {
114         GraphicsContextStateSaver stateSaver(context);
115         context.clearShadow();
116         context.fillRect(m_fillBoundingBox);
117         return;
118     }
119 #endif
120
121     context.fillRect(m_fillBoundingBox);
122 }
123
124 void RenderSVGRect::strokeShape(GraphicsContext& context) const
125 {
126     if (!style().hasVisibleStroke())
127         return;
128
129     if (m_usePathFallback) {
130         RenderSVGShape::strokeShape(context);
131         return;
132     }
133
134     context.strokeRect(m_fillBoundingBox, strokeWidth());
135 }
136
137 bool RenderSVGRect::shapeDependentStrokeContains(const FloatPoint& point, PointCoordinateSpace pointCoordinateSpace)
138 {
139     // The optimized contains code below does not support non-smooth strokes so we need
140     // to fall back to RenderSVGShape::shapeDependentStrokeContains in these cases.
141     if (m_usePathFallback || !hasSmoothStroke()) {
142         if (!hasPath())
143             RenderSVGShape::updateShapeFromElement();
144         return RenderSVGShape::shapeDependentStrokeContains(point, pointCoordinateSpace);
145     }
146
147     return m_outerStrokeRect.contains(point, FloatRect::InsideOrOnStroke) && !m_innerStrokeRect.contains(point, FloatRect::InsideButNotOnStroke);
148 }
149
150 bool RenderSVGRect::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
151 {
152     if (m_usePathFallback)
153         return RenderSVGShape::shapeDependentFillContains(point, fillRule);
154     return m_fillBoundingBox.contains(point.x(), point.y());
155 }
156
157 bool RenderSVGRect::isRenderingDisabled() const
158 {
159     // A width or height of zero disables rendering for the element, and results in an empty bounding box.
160     return m_fillBoundingBox.isEmpty();
161 }
162
163 }