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
33namespace WebCore {
34
35WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGRect);
36
37RenderSVGRect::RenderSVGRect(SVGRectElement& element, RenderStyle&& style)
38 : RenderSVGShape(element, WTFMove(style))
39 , m_usePathFallback(false)
40{
41}
42
43RenderSVGRect::~RenderSVGRect() = default;
44
45SVGRectElement& RenderSVGRect::rectElement() const
46{
47 return downcast<SVGRectElement>(RenderSVGShape::graphicsElement());
48}
49
50void 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 m_usePathFallback = false;
59
60 SVGLengthContext lengthContext(&rectElement());
61 FloatSize boundingBoxSize(lengthContext.valueForLength(style().width(), LengthModeWidth), lengthContext.valueForLength(style().height(), LengthModeHeight));
62
63 // Element is invalid if either dimension is negative.
64 if (boundingBoxSize.width() < 0 || boundingBoxSize.height() < 0)
65 return;
66
67 // Rendering enabled? Spec: "A value of zero disables rendering of the element."
68 if (!boundingBoxSize.isEmpty()) {
69 if (rectElement().rx().value(lengthContext) > 0 || rectElement().ry().value(lengthContext) > 0 || hasNonScalingStroke()) {
70 // Fall back to RenderSVGShape
71 RenderSVGShape::updateShapeFromElement();
72 m_usePathFallback = true;
73 return;
74 }
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() == ShapeRendering::CrispEdges)
97 m_strokeBoundingBox.inflate(1);
98#endif
99}
100
101void 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
124void 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
137bool 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
150bool 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
157bool 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}
164