/src/serenity/Userland/Libraries/LibWeb/SVG/SVGRectElement.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibWeb/Bindings/Intrinsics.h> |
8 | | #include <LibWeb/Bindings/SVGRectElementPrototype.h> |
9 | | #include <LibWeb/SVG/AttributeNames.h> |
10 | | #include <LibWeb/SVG/AttributeParser.h> |
11 | | #include <LibWeb/SVG/SVGAnimatedLength.h> |
12 | | #include <LibWeb/SVG/SVGLength.h> |
13 | | #include <LibWeb/SVG/SVGRectElement.h> |
14 | | |
15 | | namespace Web::SVG { |
16 | | |
17 | | JS_DEFINE_ALLOCATOR(SVGRectElement); |
18 | | |
19 | | SVGRectElement::SVGRectElement(DOM::Document& document, DOM::QualifiedName qualified_name) |
20 | 0 | : SVGGeometryElement(document, qualified_name) |
21 | 0 | { |
22 | 0 | } |
23 | | |
24 | | void SVGRectElement::initialize(JS::Realm& realm) |
25 | 0 | { |
26 | 0 | Base::initialize(realm); |
27 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGRectElement); |
28 | 0 | } |
29 | | |
30 | | void SVGRectElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value) |
31 | 0 | { |
32 | 0 | SVGGeometryElement::attribute_changed(name, old_value, value); |
33 | |
|
34 | 0 | if (name == SVG::AttributeNames::x) { |
35 | 0 | m_x = AttributeParser::parse_coordinate(value.value_or(String {})); |
36 | 0 | } else if (name == SVG::AttributeNames::y) { |
37 | 0 | m_y = AttributeParser::parse_coordinate(value.value_or(String {})); |
38 | 0 | } else if (name == SVG::AttributeNames::width) { |
39 | 0 | m_width = AttributeParser::parse_positive_length(value.value_or(String {})); |
40 | 0 | } else if (name == SVG::AttributeNames::height) { |
41 | 0 | m_height = AttributeParser::parse_positive_length(value.value_or(String {})); |
42 | 0 | } else if (name == SVG::AttributeNames::rx) { |
43 | 0 | m_radius_x = AttributeParser::parse_length(value.value_or(String {})); |
44 | 0 | } else if (name == SVG::AttributeNames::ry) { |
45 | 0 | m_radius_y = AttributeParser::parse_length(value.value_or(String {})); |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | Gfx::Path SVGRectElement::get_path(CSSPixelSize) |
50 | 0 | { |
51 | 0 | float width = m_width.value_or(0); |
52 | 0 | float height = m_height.value_or(0); |
53 | 0 | float x = m_x.value_or(0); |
54 | 0 | float y = m_y.value_or(0); |
55 | |
|
56 | 0 | Gfx::Path path; |
57 | | // If width or height is zero, rendering is disabled. |
58 | 0 | if (width == 0 || height == 0) |
59 | 0 | return path; |
60 | | |
61 | 0 | auto corner_radii = calculate_used_corner_radius_values(); |
62 | 0 | float rx = corner_radii.width(); |
63 | 0 | float ry = corner_radii.height(); |
64 | | |
65 | | // 1. perform an absolute moveto operation to location (x+rx,y); |
66 | 0 | path.move_to({ x + rx, y }); |
67 | | |
68 | | // 2, perform an absolute horizontal lineto with parameter x+width-rx; |
69 | 0 | path.horizontal_line_to(x + width - rx); |
70 | | |
71 | | // 3. if both rx and ry are greater than zero, |
72 | | // perform an absolute elliptical arc operation to coordinate (x+width,y+ry), |
73 | | // where rx and ry are used as the equivalent parameters to the elliptical arc command, |
74 | | // the x-axis-rotation and large-arc-flag are set to zero, |
75 | | // the sweep-flag is set to one; |
76 | 0 | double x_axis_rotation = 0; |
77 | 0 | bool large_arc_flag = false; |
78 | 0 | bool sweep_flag = true; |
79 | 0 | if (rx > 0 && ry > 0) |
80 | 0 | path.elliptical_arc_to({ x + width, y + ry }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); |
81 | | |
82 | | // 4. perform an absolute vertical lineto parameter y+height-ry; |
83 | 0 | path.vertical_line_to(y + height - ry); |
84 | | |
85 | | // 5. if both rx and ry are greater than zero, |
86 | | // perform an absolute elliptical arc operation to coordinate (x+width-rx,y+height), |
87 | | // using the same parameters as previously; |
88 | 0 | if (rx > 0 && ry > 0) |
89 | 0 | path.elliptical_arc_to({ x + width - rx, y + height }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); |
90 | | |
91 | | // 6. perform an absolute horizontal lineto parameter x+rx; |
92 | 0 | path.horizontal_line_to(x + rx); |
93 | | |
94 | | // 7. if both rx and ry are greater than zero, |
95 | | // perform an absolute elliptical arc operation to coordinate (x,y+height-ry), |
96 | | // using the same parameters as previously; |
97 | 0 | if (rx > 0 && ry > 0) |
98 | 0 | path.elliptical_arc_to({ x, y + height - ry }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); |
99 | | |
100 | | // 8. perform an absolute vertical lineto parameter y+ry |
101 | 0 | path.vertical_line_to(y + ry); |
102 | | |
103 | | // 9. if both rx and ry are greater than zero, |
104 | | // perform an absolute elliptical arc operation with a segment-completing close path operation, |
105 | | // using the same parameters as previously. |
106 | 0 | if (rx > 0 && ry > 0) |
107 | 0 | path.elliptical_arc_to({ x + rx, y }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); |
108 | | |
109 | | // Spec bug: the path needs to be closed independent of if rx and ry are greater than zero, |
110 | | // https://github.com/w3c/svgwg/issues/753#issuecomment-567199686 |
111 | 0 | path.close(); |
112 | |
|
113 | 0 | return path; |
114 | 0 | } |
115 | | |
116 | | Gfx::FloatSize SVGRectElement::calculate_used_corner_radius_values() const |
117 | 0 | { |
118 | | // 1. Let rx and ry be length values. |
119 | 0 | float rx = 0; |
120 | 0 | float ry = 0; |
121 | | |
122 | | // 2. If neither ‘rx’ nor ‘ry’ are properly specified, then set both rx and ry to 0. (This will result in square corners.) |
123 | 0 | if (!m_radius_x.has_value() && !m_radius_y.has_value()) { |
124 | 0 | rx = 0; |
125 | 0 | ry = 0; |
126 | 0 | } |
127 | | // 3. Otherwise, if a properly specified value is provided for ‘rx’, but not for ‘ry’, then set both rx and ry to the value of ‘rx’. |
128 | 0 | else if (m_radius_x.has_value() && !m_radius_y.has_value()) { |
129 | 0 | rx = m_radius_x.value(); |
130 | 0 | ry = m_radius_x.value(); |
131 | 0 | } |
132 | | // 4. Otherwise, if a properly specified value is provided for ‘ry’, but not for ‘rx’, then set both rx and ry to the value of ‘ry’. |
133 | 0 | else if (m_radius_y.has_value() && !m_radius_x.has_value()) { |
134 | 0 | rx = m_radius_y.value(); |
135 | 0 | ry = m_radius_y.value(); |
136 | 0 | } |
137 | | // 5. Otherwise, both ‘rx’ and ‘ry’ were specified properly. Set rx to the value of ‘rx’ and ry to the value of ‘ry’. |
138 | 0 | else { |
139 | 0 | rx = m_radius_x.value(); |
140 | 0 | ry = m_radius_y.value(); |
141 | 0 | } |
142 | | |
143 | | // 6. If rx is greater than half of ‘width’, then set rx to half of ‘width’. |
144 | 0 | auto half_width = m_width.value_or(0) / 2; |
145 | 0 | if (rx > half_width) |
146 | 0 | rx = half_width; |
147 | | |
148 | | // 7. If ry is greater than half of ‘height’, then set ry to half of ‘height’. |
149 | 0 | auto half_height = m_height.value_or(0) / 2; |
150 | 0 | if (ry > half_height) |
151 | 0 | ry = half_height; |
152 | | |
153 | | // 8. The effective values of ‘rx’ and ‘ry’ are rx and ry, respectively. |
154 | 0 | return { rx, ry }; |
155 | 0 | } |
156 | | |
157 | | // https://www.w3.org/TR/SVG11/shapes.html#RectElementXAttribute |
158 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::x() const |
159 | 0 | { |
160 | | // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). |
161 | | // FIXME: Create a proper animated value when animations are supported. |
162 | 0 | auto base_length = SVGLength::create(realm(), 0, m_x.value_or(0)); |
163 | 0 | auto anim_length = SVGLength::create(realm(), 0, m_x.value_or(0)); |
164 | 0 | return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); |
165 | 0 | } |
166 | | |
167 | | // https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute |
168 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::y() const |
169 | 0 | { |
170 | | // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). |
171 | | // FIXME: Create a proper animated value when animations are supported. |
172 | 0 | auto base_length = SVGLength::create(realm(), 0, m_y.value_or(0)); |
173 | 0 | auto anim_length = SVGLength::create(realm(), 0, m_y.value_or(0)); |
174 | 0 | return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); |
175 | 0 | } |
176 | | |
177 | | // https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute |
178 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::width() const |
179 | 0 | { |
180 | | // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). |
181 | | // FIXME: Create a proper animated value when animations are supported. |
182 | 0 | auto base_length = SVGLength::create(realm(), 0, m_width.value_or(0)); |
183 | 0 | auto anim_length = SVGLength::create(realm(), 0, m_width.value_or(0)); |
184 | 0 | return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); |
185 | 0 | } |
186 | | |
187 | | // https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute |
188 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::height() const |
189 | 0 | { |
190 | | // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). |
191 | | // FIXME: Create a proper animated value when animations are supported. |
192 | 0 | auto base_length = SVGLength::create(realm(), 0, m_height.value_or(0)); |
193 | 0 | auto anim_length = SVGLength::create(realm(), 0, m_height.value_or(0)); |
194 | 0 | return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); |
195 | 0 | } |
196 | | |
197 | | // https://www.w3.org/TR/SVG11/shapes.html#RectElementRXAttribute |
198 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::rx() const |
199 | 0 | { |
200 | | // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). |
201 | | // FIXME: Create a proper animated value when animations are supported. |
202 | 0 | auto base_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)); |
203 | 0 | auto anim_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)); |
204 | 0 | return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); |
205 | 0 | } |
206 | | |
207 | | // https://www.w3.org/TR/SVG11/shapes.html#RectElementRYAttribute |
208 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::ry() const |
209 | 0 | { |
210 | | // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). |
211 | | // FIXME: Create a proper animated value when animations are supported. |
212 | 0 | auto base_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)); |
213 | 0 | auto anim_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)); |
214 | 0 | return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)); |
215 | 0 | } |
216 | | |
217 | | } |