Skip to content

Commit f81bb1b

Browse files
committed
LibWeb/CSS: Absolutize GradientStyleValues
This lets us not care about non-absolute Length units when resolving gradient data, as they'll already have been converted to px. We can also use Angle::from_style_value() safely on absolutized angles, which reduces some boilerplate code.
1 parent fbe0567 commit f81bb1b

File tree

10 files changed

+205
-161
lines changed

10 files changed

+205
-161
lines changed

Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ GC::Ref<CSSStyleValue> AbstractImageStyleValue::reify(JS::Realm& realm, FlyStrin
1616
return CSSImageValue::create(realm, *this);
1717
}
1818

19+
ColorStopListElement ColorStopListElement::absolutized(ComputationContext const& context) const
20+
{
21+
auto absolutize_if_nonnull = [&context](RefPtr<StyleValue const> const& input) -> RefPtr<StyleValue const> {
22+
if (!input)
23+
return {};
24+
return input->absolutized(context);
25+
};
26+
27+
return {
28+
.transition_hint = transition_hint,
29+
.color_stop = {
30+
.color = absolutize_if_nonnull(color_stop.color),
31+
.position = absolutize_if_nonnull(color_stop.position),
32+
.second_position = absolutize_if_nonnull(color_stop.second_position),
33+
},
34+
};
35+
}
36+
1937
void serialize_color_stop_list(StringBuilder& builder, Vector<ColorStopListElement> const& color_stop_list, SerializationMode mode)
2038
{
2139
bool first = true;

Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ struct ColorStopListElement {
172172
} color_stop;
173173

174174
bool operator==(ColorStopListElement const&) const = default;
175+
ColorStopListElement absolutized(ComputationContext const& context) const;
175176
};
176177
void serialize_color_stop_list(StringBuilder&, Vector<ColorStopListElement> const&, SerializationMode);
177178

Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ void ConicGradientStyleValue::paint(DisplayListRecordingContext& context, Device
6565
context.display_list_recorder().fill_rect_with_conic_gradient(destination_rect, m_resolved->data, position);
6666
}
6767

68+
ValueComparingNonnullRefPtr<StyleValue const> ConicGradientStyleValue::absolutized(ComputationContext const& context) const
69+
{
70+
Vector<ColorStopListElement> absolutized_color_stops;
71+
absolutized_color_stops.ensure_capacity(m_properties.color_stop_list.size());
72+
for (auto const& color_stop : m_properties.color_stop_list) {
73+
absolutized_color_stops.unchecked_append(color_stop.absolutized(context));
74+
}
75+
RefPtr<StyleValue const> absolutized_from_angle;
76+
if (m_properties.from_angle)
77+
absolutized_from_angle = m_properties.from_angle->absolutized(context);
78+
ValueComparingNonnullRefPtr<PositionStyleValue const> absolutized_position = m_properties.position->absolutized(context)->as_position();
79+
return create(move(absolutized_from_angle), move(absolutized_position), move(absolutized_color_stops), m_properties.repeating, m_properties.interpolation_method);
80+
}
81+
6882
bool ConicGradientStyleValue::equals(StyleValue const& other) const
6983
{
7084
if (type() != other.type())
@@ -73,18 +87,11 @@ bool ConicGradientStyleValue::equals(StyleValue const& other) const
7387
return m_properties == other_gradient.m_properties;
7488
}
7589

76-
float ConicGradientStyleValue::angle_degrees(CalculationResolutionContext const& context) const
90+
float ConicGradientStyleValue::angle_degrees() const
7791
{
7892
if (!m_properties.from_angle)
7993
return 0;
80-
if (m_properties.from_angle->is_angle())
81-
return m_properties.from_angle->as_angle().angle().to_degrees();
82-
if (m_properties.from_angle->is_calculated()) {
83-
if (auto maybe_angle = m_properties.from_angle->as_calculated().resolve_angle(context); maybe_angle.has_value())
84-
return maybe_angle->to_degrees();
85-
return 0;
86-
}
87-
VERIFY_NOT_REACHED();
94+
return Angle::from_style_value(*m_properties.from_angle, {}).to_degrees();
8895
}
8996

9097
}

Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class ConicGradientStyleValue final : public AbstractImageStyleValue {
2727

2828
void paint(DisplayListRecordingContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override;
2929

30+
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
3031
virtual bool equals(StyleValue const& other) const override;
3132

3233
Vector<ColorStopListElement> const& color_stop_list() const
@@ -42,7 +43,7 @@ class ConicGradientStyleValue final : public AbstractImageStyleValue {
4243
return InterpolationMethod { .color_space = InterpolationMethod::default_color_space(m_properties.color_syntax) };
4344
}
4445

45-
float angle_degrees(CalculationResolutionContext const&) const;
46+
float angle_degrees() const;
4647

4748
bool is_paintable() const override { return true; }
4849

Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@ String LinearGradientStyleValue::to_string(SerializationMode mode) const
7474
return MUST(builder.to_string());
7575
}
7676

77+
ValueComparingNonnullRefPtr<StyleValue const> LinearGradientStyleValue::absolutized(ComputationContext const& context) const
78+
{
79+
Vector<ColorStopListElement> absolutized_color_stops;
80+
absolutized_color_stops.ensure_capacity(m_properties.color_stop_list.size());
81+
for (auto const& color_stop : m_properties.color_stop_list) {
82+
absolutized_color_stops.unchecked_append(color_stop.absolutized(context));
83+
}
84+
return create(m_properties.direction, move(absolutized_color_stops), m_properties.gradient_type, m_properties.repeating, m_properties.interpolation_method);
85+
}
86+
7787
bool LinearGradientStyleValue::equals(StyleValue const& other_) const
7888
{
7989
if (type() != other_.type())
@@ -117,13 +127,7 @@ float LinearGradientStyleValue::angle_degrees(CSSPixelSize gradient_size) const
117127
return angle;
118128
},
119129
[&](NonnullRefPtr<StyleValue const> const& style_value) {
120-
if (style_value->is_angle())
121-
return style_value->as_angle().angle().to_degrees();
122-
if (style_value->is_calculated()) {
123-
if (auto maybe_angle = style_value->as_calculated().resolve_angle({}); maybe_angle.has_value())
124-
return maybe_angle->to_degrees();
125-
}
126-
return 0.0;
130+
return Angle::from_style_value(style_value, {}).to_degrees();
127131
});
128132
}
129133

Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class LinearGradientStyleValue final : public AbstractImageStyleValue {
4646

4747
virtual String to_string(SerializationMode) const override;
4848
virtual ~LinearGradientStyleValue() override = default;
49+
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
4950
virtual bool equals(StyleValue const& other) const override;
5051

5152
Vector<ColorStopListElement> const& color_stop_list() const

Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ String RadialGradientStyleValue::to_string(SerializationMode mode) const
7272
return MUST(builder.to_string());
7373
}
7474

75-
CSSPixelSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, CSSPixelPoint center, CSSPixelRect const& size) const
75+
CSSPixelSize RadialGradientStyleValue::resolve_size(CSSPixelPoint center, CSSPixelRect const& size) const
7676
{
7777
auto const side_shape = [&](auto distance_function) {
7878
auto const distance_from = [&](CSSPixels v, CSSPixels a, CSSPixels b, auto distance_function) {
@@ -180,31 +180,23 @@ CSSPixelSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, CS
180180
},
181181
[&](CircleSize const& circle_size) {
182182
if (circle_size.radius->is_length()) {
183-
auto radius = circle_size.radius->as_length().length().to_px(node);
183+
auto radius = circle_size.radius->as_length().length().absolute_length_to_px();
184184
return CSSPixelSize { radius, radius };
185185
}
186186
if (circle_size.radius->is_calculated()) {
187-
CalculationResolutionContext context {
188-
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
189-
};
190-
auto radius = circle_size.radius->as_calculated().resolve_length(context)->to_px(node);
187+
auto radius = circle_size.radius->as_calculated().resolve_length({})->absolute_length_to_px();
191188
return CSSPixelSize { radius, radius };
192189
}
193190
VERIFY_NOT_REACHED();
194191
},
195192
[&](EllipseSize const& ellipse_size) {
196193
auto resolve = [&](StyleValue const& radius_value, auto percentage_basis_pixels) {
197-
auto percentage_basis = Length::make_px(percentage_basis_pixels);
198-
CalculationResolutionContext context {
199-
.percentage_basis = percentage_basis,
200-
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
201-
};
202194
if (radius_value.is_length())
203-
return radius_value.as_length().length().to_px(node);
195+
return radius_value.as_length().length().absolute_length_to_px();
204196
if (radius_value.is_percentage())
205-
return percentage_basis.percentage_of(radius_value.as_percentage().percentage()).to_px(node);
197+
return CSSPixels { radius_value.as_percentage().percentage().as_fraction() * percentage_basis_pixels };
206198
if (radius_value.is_calculated())
207-
return radius_value.as_calculated().resolve_length(context)->to_px(node);
199+
return radius_value.as_calculated().resolve_length({})->absolute_length_to_px();
208200
VERIFY_NOT_REACHED();
209201
};
210202
auto radius_a = resolve(*ellipse_size.radius_a, size.width());
@@ -246,7 +238,7 @@ void RadialGradientStyleValue::resolve_for_size(Layout::NodeWithStyle const& nod
246238
{
247239
CSSPixelRect gradient_box { { 0, 0 }, paint_size };
248240
auto center = m_properties.position->resolved(node, gradient_box);
249-
auto gradient_size = resolve_size(node, center, gradient_box);
241+
auto gradient_size = resolve_size(center, gradient_box);
250242

251243
ResolvedDataCacheKey cache_key {
252244
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
@@ -262,6 +254,35 @@ void RadialGradientStyleValue::resolve_for_size(Layout::NodeWithStyle const& nod
262254
}
263255
}
264256

257+
ValueComparingNonnullRefPtr<StyleValue const> RadialGradientStyleValue::absolutized(ComputationContext const& context) const
258+
{
259+
Vector<ColorStopListElement> absolutized_color_stops;
260+
absolutized_color_stops.ensure_capacity(m_properties.color_stop_list.size());
261+
for (auto const& color_stop : m_properties.color_stop_list) {
262+
absolutized_color_stops.unchecked_append(color_stop.absolutized(context));
263+
}
264+
265+
auto absolutized_size = m_properties.size.visit(
266+
[&](Extent extent) -> Size {
267+
return extent;
268+
},
269+
[&](CircleSize const& circle_size) -> Size {
270+
return CircleSize {
271+
.radius = circle_size.radius->absolutized(context),
272+
};
273+
},
274+
[&](EllipseSize const& ellipse_size) -> Size {
275+
return EllipseSize {
276+
.radius_a = ellipse_size.radius_a->absolutized(context),
277+
.radius_b = ellipse_size.radius_b->absolutized(context),
278+
};
279+
});
280+
281+
NonnullRefPtr absolutized_position = m_properties.position->absolutized(context)->as_position();
282+
283+
return create(m_properties.ending_shape, move(absolutized_size), move(absolutized_position), move(absolutized_color_stops), m_properties.repeating, m_properties.interpolation_method);
284+
}
285+
265286
bool RadialGradientStyleValue::equals(StyleValue const& other) const
266287
{
267288
if (type() != other.type())

Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class RadialGradientStyleValue final : public AbstractImageStyleValue {
5454

5555
void paint(DisplayListRecordingContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override;
5656

57+
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
5758
virtual bool equals(StyleValue const& other) const override;
5859

5960
Vector<ColorStopListElement> const& color_stop_list() const
@@ -73,7 +74,7 @@ class RadialGradientStyleValue final : public AbstractImageStyleValue {
7374

7475
void resolve_for_size(Layout::NodeWithStyle const&, CSSPixelSize) const override;
7576

76-
CSSPixelSize resolve_size(Layout::Node const&, CSSPixelPoint, CSSPixelRect const&) const;
77+
CSSPixelSize resolve_size(CSSPixelPoint, CSSPixelRect const&) const;
7778

7879
bool is_repeating() const { return m_properties.repeating == GradientRepeating::Yes; }
7980

Libraries/LibWeb/Painting/GradientPainting.cpp

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,14 @@ LinearGradientData resolve_linear_gradient_data(Layout::NodeWithStyle const& nod
122122

123123
CSS::CalculationResolutionContext context {
124124
.percentage_basis = CSS::Length::make_px(gradient_length_px),
125-
.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(node),
126125
};
127126
auto resolved_color_stops = resolve_color_stop_positions(
128127
node, linear_gradient.color_stop_list(), [&](auto const& position) -> float {
129128
if (position.is_length())
130-
return position.as_length().length().to_px_without_rounding(*context.length_resolution_context) / gradient_length_px;
129+
return position.as_length().length().absolute_length_to_px_without_rounding() / gradient_length_px;
131130
if (position.is_percentage())
132131
return position.as_percentage().percentage().as_fraction();
133-
return position.as_calculated().resolve_length(context)->to_px_without_rounding(*context.length_resolution_context) / gradient_length_px;
132+
return position.as_calculated().resolve_length(context)->absolute_length_to_px_without_rounding() / gradient_length_px;
134133
},
135134
linear_gradient.is_repeating());
136135

@@ -140,37 +139,28 @@ LinearGradientData resolve_linear_gradient_data(Layout::NodeWithStyle const& nod
140139
ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyle const& node, CSS::ConicGradientStyleValue const& conic_gradient)
141140
{
142141
CSS::Angle const one_turn { 360.0f, CSS::AngleUnit::Deg };
143-
CSS::CalculationResolutionContext context {
144-
.percentage_basis = one_turn,
145-
.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(node),
146-
};
147142
auto resolved_color_stops = resolve_color_stop_positions(
148143
node, conic_gradient.color_stop_list(), [&](auto const& position) -> float {
149-
if (position.is_angle())
150-
return position.as_angle().angle().to_degrees() / one_turn.to_degrees();
151-
if (position.is_percentage())
152-
return position.as_percentage().percentage().as_fraction();
153-
return position.as_calculated().resolve_angle(context)->to_degrees() / one_turn.to_degrees();
144+
return CSS::Angle::from_style_value(position, one_turn).to_degrees() / one_turn.to_degrees();
154145
},
155146
conic_gradient.is_repeating());
156-
return { conic_gradient.angle_degrees(context), resolved_color_stops, conic_gradient.interpolation_method() };
147+
return { conic_gradient.angle_degrees(), resolved_color_stops, conic_gradient.interpolation_method() };
157148
}
158149

159150
RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyle const& node, CSSPixelSize gradient_size, CSS::RadialGradientStyleValue const& radial_gradient)
160151
{
161152
CSS::CalculationResolutionContext context {
162153
.percentage_basis = CSS::Length::make_px(gradient_size.width()),
163-
.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(node),
164154
};
165155

166156
// Start center, goes right to ending point, where the gradient line intersects the ending shape
167157
auto resolved_color_stops = resolve_color_stop_positions(
168158
node, radial_gradient.color_stop_list(), [&](auto const& position) -> float {
169159
if (position.is_length())
170-
return position.as_length().length().to_px_without_rounding(*context.length_resolution_context) / gradient_size.width().to_float();
160+
return position.as_length().length().absolute_length_to_px_without_rounding() / gradient_size.width().to_float();
171161
if (position.is_percentage())
172162
return position.as_percentage().percentage().as_fraction();
173-
return position.as_calculated().resolve_length(context)->to_px_without_rounding(*context.length_resolution_context) / gradient_size.width().to_float();
163+
return position.as_calculated().resolve_length(context)->absolute_length_to_px_without_rounding() / gradient_size.width().to_float();
174164
},
175165
radial_gradient.is_repeating());
176166
return { resolved_color_stops, radial_gradient.interpolation_method() };

0 commit comments

Comments
 (0)