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 "third_party/blink/renderer/core/layout/svg/layout_svg_rect.h"
29
30 #include "third_party/blink/renderer/core/svg/svg_length_context.h"
31 #include "third_party/blink/renderer/core/svg/svg_rect_element.h"
32 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
33
34 namespace blink {
35
LayoutSVGRect(SVGRectElement * node)36 LayoutSVGRect::LayoutSVGRect(SVGRectElement* node)
37 : LayoutSVGShape(node, kSimple), use_path_fallback_(false) {}
38
39 LayoutSVGRect::~LayoutSVGRect() = default;
40
UpdateShapeFromElement()41 void LayoutSVGRect::UpdateShapeFromElement() {
42 NOT_DESTROYED();
43 // Before creating a new object we need to clear the cached bounding box
44 // to avoid using garbage.
45 fill_bounding_box_ = FloatRect();
46 stroke_bounding_box_ = FloatRect();
47 use_path_fallback_ = false;
48
49 SVGLengthContext length_context(GetElement());
50 const ComputedStyle& style = StyleRef();
51 FloatSize bounding_box_size(ToFloatSize(
52 length_context.ResolveLengthPair(style.Width(), style.Height(), style)));
53
54 // Spec: "A negative value is an error."
55 if (bounding_box_size.Width() < 0 || bounding_box_size.Height() < 0)
56 return;
57
58 const SVGComputedStyle& svg_style = style.SvgStyle();
59 // Spec: "A value of zero disables rendering of the element."
60 if (!bounding_box_size.IsEmpty()) {
61 // Fallback to LayoutSVGShape and path-based hit detection if the rect
62 // has rounded corners or a non-scaling or non-simple stroke.
63 // However, only use LayoutSVGShape bounding-box calculations for the
64 // non-scaling stroke case, since the computation below should be accurate
65 // for the other cases.
66 if (HasNonScalingStroke()) {
67 LayoutSVGShape::UpdateShapeFromElement();
68 use_path_fallback_ = true;
69 return;
70 }
71 FloatPoint radii(length_context.ResolveLengthPair(svg_style.Rx(),
72 svg_style.Ry(), style));
73 if (radii.X() > 0 || radii.Y() > 0 || !DefinitelyHasSimpleStroke()) {
74 CreatePath();
75 use_path_fallback_ = true;
76 }
77 }
78
79 if (!use_path_fallback_)
80 ClearPath();
81
82 fill_bounding_box_ = FloatRect(
83 length_context.ResolveLengthPair(svg_style.X(), svg_style.Y(), style),
84 bounding_box_size);
85 stroke_bounding_box_ = CalculateStrokeBoundingBox();
86 }
87
ShapeDependentStrokeContains(const HitTestLocation & location)88 bool LayoutSVGRect::ShapeDependentStrokeContains(
89 const HitTestLocation& location) {
90 NOT_DESTROYED();
91 // The optimized code below does not support the cases that we set
92 // use_path_fallback_ in UpdateShapeFromElement().
93 if (use_path_fallback_)
94 return LayoutSVGShape::ShapeDependentStrokeContains(location);
95
96 const FloatPoint& point = location.TransformedPoint();
97 const float half_stroke_width = StrokeWidth() / 2;
98 const float half_width = fill_bounding_box_.Width() / 2;
99 const float half_height = fill_bounding_box_.Height() / 2;
100
101 const FloatPoint fill_bounding_box_center =
102 FloatPoint(fill_bounding_box_.X() + half_width,
103 fill_bounding_box_.Y() + half_height);
104 const float abs_delta_x = std::abs(point.X() - fill_bounding_box_center.X());
105 const float abs_delta_y = std::abs(point.Y() - fill_bounding_box_center.Y());
106
107 if (!(abs_delta_x <= half_width + half_stroke_width &&
108 abs_delta_y <= half_height + half_stroke_width))
109 return false;
110
111 return (half_width - half_stroke_width <= abs_delta_x) ||
112 (half_height - half_stroke_width <= abs_delta_y);
113 }
114
ShapeDependentFillContains(const HitTestLocation & location,const WindRule fill_rule) const115 bool LayoutSVGRect::ShapeDependentFillContains(const HitTestLocation& location,
116 const WindRule fill_rule) const {
117 NOT_DESTROYED();
118 if (use_path_fallback_)
119 return LayoutSVGShape::ShapeDependentFillContains(location, fill_rule);
120 const FloatPoint& point = location.TransformedPoint();
121 return fill_bounding_box_.Contains(point.X(), point.Y());
122 }
123
124 // Returns true if the stroke is continuous and definitely uses miter joins.
DefinitelyHasSimpleStroke() const125 bool LayoutSVGRect::DefinitelyHasSimpleStroke() const {
126 NOT_DESTROYED();
127 const SVGComputedStyle& svg_style = StyleRef().SvgStyle();
128
129 // The four angles of a rect are 90 degrees. Using the formula at:
130 // http://www.w3.org/TR/SVG/painting.html#StrokeMiterlimitProperty
131 // when the join style of the rect is "miter", the ratio of the miterLength
132 // to the stroke-width is found to be
133 // miterLength / stroke-width = 1 / sin(45 degrees)
134 // = 1 / (1 / sqrt(2))
135 // = sqrt(2)
136 // = 1.414213562373095...
137 // When sqrt(2) exceeds the miterlimit, then the join style switches to
138 // "bevel". When the miterlimit is greater than or equal to sqrt(2) then
139 // the join style remains "miter".
140 //
141 // An approximation of sqrt(2) is used here because at certain precise
142 // miterlimits, the join style used might not be correct (e.g. a miterlimit
143 // of 1.4142135 should result in bevel joins, but may be drawn using miter
144 // joins).
145 return svg_style.StrokeDashArray()->data.IsEmpty() &&
146 svg_style.JoinStyle() == kMiterJoin &&
147 svg_style.StrokeMiterLimit() >= 1.5;
148 }
149
150 } // namespace blink
151