1/*
2 * Copyright (C) 2012 Google, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "RenderSVGEllipse.h"
29
30#include "SVGCircleElement.h"
31#include "SVGEllipseElement.h"
32#include <wtf/IsoMallocInlines.h>
33
34namespace WebCore {
35
36WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGEllipse);
37
38RenderSVGEllipse::RenderSVGEllipse(SVGGraphicsElement& element, RenderStyle&& style)
39 : RenderSVGShape(element, WTFMove(style))
40 , m_usePathFallback(false)
41{
42}
43
44RenderSVGEllipse::~RenderSVGEllipse() = default;
45
46void RenderSVGEllipse::updateShapeFromElement()
47{
48 // Before creating a new object we need to clear the cached bounding box
49 // to avoid using garbage.
50 m_fillBoundingBox = FloatRect();
51 m_strokeBoundingBox = FloatRect();
52 m_center = FloatPoint();
53 m_radii = FloatSize();
54
55 calculateRadiiAndCenter();
56
57 // Element is invalid if either dimension is negative.
58 if (m_radii.width() < 0 || m_radii.height() < 0)
59 return;
60
61 // Spec: "A value of zero disables rendering of the element."
62 if (!m_radii.isEmpty()) {
63 if (hasNonScalingStroke()) {
64 // Fallback to RenderSVGShape if shape has a non-scaling stroke.
65 RenderSVGShape::updateShapeFromElement();
66 m_usePathFallback = true;
67 return;
68 }
69 m_usePathFallback = false;
70 }
71
72 m_fillBoundingBox = FloatRect(m_center.x() - m_radii.width(), m_center.y() - m_radii.height(), 2 * m_radii.width(), 2 * m_radii.height());
73 m_strokeBoundingBox = m_fillBoundingBox;
74 if (style().svgStyle().hasStroke())
75 m_strokeBoundingBox.inflate(strokeWidth() / 2);
76}
77
78void RenderSVGEllipse::calculateRadiiAndCenter()
79{
80 SVGLengthContext lengthContext(&graphicsElement());
81 m_center = FloatPoint(
82 lengthContext.valueForLength(style().svgStyle().cx(), LengthModeWidth),
83 lengthContext.valueForLength(style().svgStyle().cy(), LengthModeHeight));
84 if (is<SVGCircleElement>(graphicsElement())) {
85 float radius = lengthContext.valueForLength(style().svgStyle().r());
86 m_radii = FloatSize(radius, radius);
87 return;
88 }
89
90 ASSERT(is<SVGEllipseElement>(graphicsElement()));
91 m_radii = FloatSize(
92 lengthContext.valueForLength(style().svgStyle().rx(), LengthModeWidth),
93 lengthContext.valueForLength(style().svgStyle().ry(), LengthModeHeight));
94}
95
96void RenderSVGEllipse::fillShape(GraphicsContext& context) const
97{
98 if (m_usePathFallback) {
99 RenderSVGShape::fillShape(context);
100 return;
101 }
102 context.fillEllipse(m_fillBoundingBox);
103}
104
105void RenderSVGEllipse::strokeShape(GraphicsContext& context) const
106{
107 if (!style().hasVisibleStroke())
108 return;
109 if (m_usePathFallback) {
110 RenderSVGShape::strokeShape(context);
111 return;
112 }
113 context.strokeEllipse(m_fillBoundingBox);
114}
115
116bool RenderSVGEllipse::shapeDependentStrokeContains(const FloatPoint& point, PointCoordinateSpace pointCoordinateSpace)
117{
118 // The optimized contains code below does not support non-smooth strokes so we need
119 // to fall back to RenderSVGShape::shapeDependentStrokeContains in these cases.
120 if (m_usePathFallback || !hasSmoothStroke()) {
121 if (!hasPath())
122 RenderSVGShape::updateShapeFromElement();
123 return RenderSVGShape::shapeDependentStrokeContains(point, pointCoordinateSpace);
124 }
125
126 float halfStrokeWidth = strokeWidth() / 2;
127 FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y());
128
129 // This works by checking if the point satisfies the ellipse equation,
130 // (x/rX)^2 + (y/rY)^2 <= 1, for the outer but not the inner stroke.
131 float xrXOuter = center.x() / (m_radii.width() + halfStrokeWidth);
132 float yrYOuter = center.y() / (m_radii.height() + halfStrokeWidth);
133 if (xrXOuter * xrXOuter + yrYOuter * yrYOuter > 1.0)
134 return false;
135
136 float xrXInner = center.x() / (m_radii.width() - halfStrokeWidth);
137 float yrYInner = center.y() / (m_radii.height() - halfStrokeWidth);
138 return xrXInner * xrXInner + yrYInner * yrYInner >= 1.0;
139}
140
141bool RenderSVGEllipse::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
142{
143 if (m_usePathFallback)
144 return RenderSVGShape::shapeDependentFillContains(point, fillRule);
145
146 FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y());
147
148 // This works by checking if the point satisfies the ellipse equation.
149 // (x/rX)^2 + (y/rY)^2 <= 1
150 float xrX = center.x() / m_radii.width();
151 float yrY = center.y() / m_radii.height();
152 return xrX * xrX + yrY * yrY <= 1.0;
153}
154
155bool RenderSVGEllipse::isRenderingDisabled() const
156{
157 // A radius of zero disables rendering of the element, and results in an empty bounding box.
158 return m_fillBoundingBox.isEmpty();
159}
160
161}
162