/src/serenity/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, MacDue <macdue@dueutil.tech> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibWeb/Bindings/Intrinsics.h> |
8 | | #include <LibWeb/Bindings/SVGLinearGradientElementPrototype.h> |
9 | | #include <LibWeb/DOM/Document.h> |
10 | | #include <LibWeb/Painting/PaintStyle.h> |
11 | | #include <LibWeb/SVG/AttributeNames.h> |
12 | | #include <LibWeb/SVG/AttributeParser.h> |
13 | | #include <LibWeb/SVG/SVGLinearGradientElement.h> |
14 | | #include <LibWeb/SVG/SVGStopElement.h> |
15 | | |
16 | | namespace Web::SVG { |
17 | | |
18 | | JS_DEFINE_ALLOCATOR(SVGLinearGradientElement); |
19 | | |
20 | | SVGLinearGradientElement::SVGLinearGradientElement(DOM::Document& document, DOM::QualifiedName qualified_name) |
21 | 0 | : SVGGradientElement(document, qualified_name) |
22 | 0 | { |
23 | 0 | } |
24 | | |
25 | | void SVGLinearGradientElement::initialize(JS::Realm& realm) |
26 | 0 | { |
27 | 0 | Base::initialize(realm); |
28 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGLinearGradientElement); |
29 | 0 | } |
30 | | |
31 | | void SVGLinearGradientElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value) |
32 | 0 | { |
33 | 0 | SVGGradientElement::attribute_changed(name, old_value, value); |
34 | | |
35 | | // FIXME: Should allow for `<number-percentage> | <length>` for x1, x2, y1, y2 |
36 | 0 | if (name == SVG::AttributeNames::x1) { |
37 | 0 | m_x1 = AttributeParser::parse_number_percentage(value.value_or(String {})); |
38 | 0 | } else if (name == SVG::AttributeNames::y1) { |
39 | 0 | m_y1 = AttributeParser::parse_number_percentage(value.value_or(String {})); |
40 | 0 | } else if (name == SVG::AttributeNames::x2) { |
41 | 0 | m_x2 = AttributeParser::parse_number_percentage(value.value_or(String {})); |
42 | 0 | } else if (name == SVG::AttributeNames::y2) { |
43 | 0 | m_y2 = AttributeParser::parse_number_percentage(value.value_or(String {})); |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX1Attribute |
48 | | NumberPercentage SVGLinearGradientElement::start_x() const |
49 | 0 | { |
50 | 0 | HashTable<SVGGradientElement const*> seen_gradients; |
51 | 0 | return start_x_impl(seen_gradients); |
52 | 0 | } |
53 | | |
54 | | NumberPercentage SVGLinearGradientElement::start_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const |
55 | 0 | { |
56 | 0 | if (m_x1.has_value()) |
57 | 0 | return *m_x1; |
58 | 0 | if (auto gradient = linked_linear_gradient(seen_gradients)) |
59 | 0 | return gradient->start_x_impl(seen_gradients); |
60 | | // If the attribute is not specified, the effect is as if a value of '0%' were specified. |
61 | 0 | return NumberPercentage::create_percentage(0); |
62 | 0 | } |
63 | | |
64 | | // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY1Attribute |
65 | | NumberPercentage SVGLinearGradientElement::start_y() const |
66 | 0 | { |
67 | 0 | HashTable<SVGGradientElement const*> seen_gradients; |
68 | 0 | return start_y_impl(seen_gradients); |
69 | 0 | } |
70 | | |
71 | | NumberPercentage SVGLinearGradientElement::start_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const |
72 | 0 | { |
73 | 0 | if (m_y1.has_value()) |
74 | 0 | return *m_y1; |
75 | 0 | if (auto gradient = linked_linear_gradient(seen_gradients)) |
76 | 0 | return gradient->start_y_impl(seen_gradients); |
77 | | // If the attribute is not specified, the effect is as if a value of '0%' were specified. |
78 | 0 | return NumberPercentage::create_percentage(0); |
79 | 0 | } |
80 | | |
81 | | // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX2Attribute |
82 | | NumberPercentage SVGLinearGradientElement::end_x() const |
83 | 0 | { |
84 | 0 | HashTable<SVGGradientElement const*> seen_gradients; |
85 | 0 | return end_x_impl(seen_gradients); |
86 | 0 | } |
87 | | |
88 | | NumberPercentage SVGLinearGradientElement::end_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const |
89 | 0 | { |
90 | 0 | if (m_x2.has_value()) |
91 | 0 | return *m_x2; |
92 | 0 | if (auto gradient = linked_linear_gradient(seen_gradients)) |
93 | 0 | return gradient->end_x_impl(seen_gradients); |
94 | | // If the attribute is not specified, the effect is as if a value of '100%' were specified. |
95 | 0 | return NumberPercentage::create_percentage(100); |
96 | 0 | } |
97 | | |
98 | | // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY2Attribute |
99 | | NumberPercentage SVGLinearGradientElement::end_y() const |
100 | 0 | { |
101 | 0 | HashTable<SVGGradientElement const*> seen_gradients; |
102 | 0 | return end_y_impl(seen_gradients); |
103 | 0 | } |
104 | | |
105 | | NumberPercentage SVGLinearGradientElement::end_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const |
106 | 0 | { |
107 | 0 | if (m_y2.has_value()) |
108 | 0 | return *m_y2; |
109 | 0 | if (auto gradient = linked_linear_gradient(seen_gradients)) |
110 | 0 | return gradient->end_y_impl(seen_gradients); |
111 | | // If the attribute is not specified, the effect is as if a value of '0%' were specified. |
112 | 0 | return NumberPercentage::create_percentage(0); |
113 | 0 | } |
114 | | |
115 | | Optional<Painting::PaintStyle> SVGLinearGradientElement::to_gfx_paint_style(SVGPaintContext const& paint_context) const |
116 | 0 | { |
117 | 0 | auto units = gradient_units(); |
118 | | // FIXME: Resolve percentages properly |
119 | 0 | Gfx::FloatPoint start_point {}; |
120 | 0 | Gfx::FloatPoint end_point {}; |
121 | | // https://svgwg.org/svg2-draft/pservers.html#LinearGradientElementGradientUnitsAttribute |
122 | 0 | if (units == GradientUnits::ObjectBoundingBox) { |
123 | | // If gradientUnits="objectBoundingBox", the user coordinate system for attributes ‘x1’, ‘y1’, ‘x2’ and ‘y2’ |
124 | | // is established using the bounding box of the element to which the gradient is applied (see Object bounding |
125 | | // box units) and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent |
126 | | // values relative to the bounding box for the object. |
127 | | // Note: For gradientUnits="objectBoundingBox" both "100%" and "1" are treated the same. |
128 | 0 | start_point = { start_x().value(), start_y().value() }; |
129 | 0 | end_point = { end_x().value(), end_y().value() }; |
130 | 0 | } else { |
131 | | // GradientUnits::UserSpaceOnUse |
132 | | // If gradientUnits="userSpaceOnUse", ‘x1’, ‘y1’, ‘x2’, and ‘y2’ represent values in the coordinate system |
133 | | // that results from taking the current user coordinate system in place at the time when the gradient element |
134 | | // is referenced (i.e., the user coordinate system for the element referencing the gradient element via a |
135 | | // fill or stroke property) and then applying the transform specified by attribute ‘gradientTransform’. |
136 | | // Percentages represent values relative to the current SVG viewport. |
137 | 0 | start_point = Gfx::FloatPoint { |
138 | 0 | start_x().resolve_relative_to(paint_context.viewport.width()), |
139 | 0 | start_y().resolve_relative_to(paint_context.viewport.height()), |
140 | 0 | }; |
141 | 0 | end_point = Gfx::FloatPoint { |
142 | 0 | end_x().resolve_relative_to(paint_context.viewport.width()), |
143 | 0 | end_y().resolve_relative_to(paint_context.viewport.height()), |
144 | 0 | }; |
145 | 0 | } |
146 | |
|
147 | 0 | if (!m_paint_style) { |
148 | 0 | m_paint_style = Painting::SVGLinearGradientPaintStyle::create(start_point, end_point); |
149 | | // FIXME: Update stops in DOM changes: |
150 | 0 | add_color_stops(*m_paint_style); |
151 | 0 | } else { |
152 | 0 | m_paint_style->set_start_point(start_point); |
153 | 0 | m_paint_style->set_end_point(end_point); |
154 | 0 | } |
155 | |
|
156 | 0 | m_paint_style->set_gradient_transform(gradient_paint_transform(paint_context)); |
157 | 0 | m_paint_style->set_spread_method(to_painting_spread_method(spread_method())); |
158 | 0 | return *m_paint_style; |
159 | 0 | } |
160 | | |
161 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x1() const |
162 | 0 | { |
163 | | // FIXME: Implement this properly. |
164 | 0 | return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0)); |
165 | 0 | } |
166 | | |
167 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y1() const |
168 | 0 | { |
169 | | // FIXME: Implement this properly. |
170 | 0 | return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0)); |
171 | 0 | } |
172 | | |
173 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x2() const |
174 | 0 | { |
175 | | // FIXME: Implement this properly. |
176 | 0 | return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0)); |
177 | 0 | } |
178 | | |
179 | | JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y2() const |
180 | 0 | { |
181 | | // FIXME: Implement this properly. |
182 | 0 | return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0)); |
183 | 0 | } |
184 | | |
185 | | } |