mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
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.
This commit is contained in:
parent
fbe0567f90
commit
f81bb1bd8c
Notes:
github-actions[bot]
2025-12-01 11:09:51 +00:00
Author: https://github.com/AtkinsSJ
Commit: f81bb1bd8c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6894
Reviewed-by: https://github.com/Calme1709
Reviewed-by: https://github.com/gmta
10 changed files with 205 additions and 161 deletions
|
|
@ -16,6 +16,24 @@ GC::Ref<CSSStyleValue> AbstractImageStyleValue::reify(JS::Realm& realm, FlyStrin
|
|||
return CSSImageValue::create(realm, *this);
|
||||
}
|
||||
|
||||
ColorStopListElement ColorStopListElement::absolutized(ComputationContext const& context) const
|
||||
{
|
||||
auto absolutize_if_nonnull = [&context](RefPtr<StyleValue const> const& input) -> RefPtr<StyleValue const> {
|
||||
if (!input)
|
||||
return {};
|
||||
return input->absolutized(context);
|
||||
};
|
||||
|
||||
return {
|
||||
.transition_hint = transition_hint,
|
||||
.color_stop = {
|
||||
.color = absolutize_if_nonnull(color_stop.color),
|
||||
.position = absolutize_if_nonnull(color_stop.position),
|
||||
.second_position = absolutize_if_nonnull(color_stop.second_position),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
void serialize_color_stop_list(StringBuilder& builder, Vector<ColorStopListElement> const& color_stop_list, SerializationMode mode)
|
||||
{
|
||||
bool first = true;
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ struct ColorStopListElement {
|
|||
} color_stop;
|
||||
|
||||
bool operator==(ColorStopListElement const&) const = default;
|
||||
ColorStopListElement absolutized(ComputationContext const& context) const;
|
||||
};
|
||||
void serialize_color_stop_list(StringBuilder&, Vector<ColorStopListElement> const&, SerializationMode);
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,20 @@ void ConicGradientStyleValue::paint(DisplayListRecordingContext& context, Device
|
|||
context.display_list_recorder().fill_rect_with_conic_gradient(destination_rect, m_resolved->data, position);
|
||||
}
|
||||
|
||||
ValueComparingNonnullRefPtr<StyleValue const> ConicGradientStyleValue::absolutized(ComputationContext const& context) const
|
||||
{
|
||||
Vector<ColorStopListElement> absolutized_color_stops;
|
||||
absolutized_color_stops.ensure_capacity(m_properties.color_stop_list.size());
|
||||
for (auto const& color_stop : m_properties.color_stop_list) {
|
||||
absolutized_color_stops.unchecked_append(color_stop.absolutized(context));
|
||||
}
|
||||
RefPtr<StyleValue const> absolutized_from_angle;
|
||||
if (m_properties.from_angle)
|
||||
absolutized_from_angle = m_properties.from_angle->absolutized(context);
|
||||
ValueComparingNonnullRefPtr<PositionStyleValue const> absolutized_position = m_properties.position->absolutized(context)->as_position();
|
||||
return create(move(absolutized_from_angle), move(absolutized_position), move(absolutized_color_stops), m_properties.repeating, m_properties.interpolation_method);
|
||||
}
|
||||
|
||||
bool ConicGradientStyleValue::equals(StyleValue const& other) const
|
||||
{
|
||||
if (type() != other.type())
|
||||
|
|
@ -73,18 +87,11 @@ bool ConicGradientStyleValue::equals(StyleValue const& other) const
|
|||
return m_properties == other_gradient.m_properties;
|
||||
}
|
||||
|
||||
float ConicGradientStyleValue::angle_degrees(CalculationResolutionContext const& context) const
|
||||
float ConicGradientStyleValue::angle_degrees() const
|
||||
{
|
||||
if (!m_properties.from_angle)
|
||||
return 0;
|
||||
if (m_properties.from_angle->is_angle())
|
||||
return m_properties.from_angle->as_angle().angle().to_degrees();
|
||||
if (m_properties.from_angle->is_calculated()) {
|
||||
if (auto maybe_angle = m_properties.from_angle->as_calculated().resolve_angle(context); maybe_angle.has_value())
|
||||
return maybe_angle->to_degrees();
|
||||
return 0;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
return Angle::from_style_value(*m_properties.from_angle, {}).to_degrees();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public:
|
|||
|
||||
void paint(DisplayListRecordingContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override;
|
||||
|
||||
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
|
||||
virtual bool equals(StyleValue const& other) const override;
|
||||
|
||||
Vector<ColorStopListElement> const& color_stop_list() const
|
||||
|
|
@ -42,7 +43,7 @@ public:
|
|||
return InterpolationMethod { .color_space = InterpolationMethod::default_color_space(m_properties.color_syntax) };
|
||||
}
|
||||
|
||||
float angle_degrees(CalculationResolutionContext const&) const;
|
||||
float angle_degrees() const;
|
||||
|
||||
bool is_paintable() const override { return true; }
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,16 @@ String LinearGradientStyleValue::to_string(SerializationMode mode) const
|
|||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
ValueComparingNonnullRefPtr<StyleValue const> LinearGradientStyleValue::absolutized(ComputationContext const& context) const
|
||||
{
|
||||
Vector<ColorStopListElement> absolutized_color_stops;
|
||||
absolutized_color_stops.ensure_capacity(m_properties.color_stop_list.size());
|
||||
for (auto const& color_stop : m_properties.color_stop_list) {
|
||||
absolutized_color_stops.unchecked_append(color_stop.absolutized(context));
|
||||
}
|
||||
return create(m_properties.direction, move(absolutized_color_stops), m_properties.gradient_type, m_properties.repeating, m_properties.interpolation_method);
|
||||
}
|
||||
|
||||
bool LinearGradientStyleValue::equals(StyleValue const& other_) const
|
||||
{
|
||||
if (type() != other_.type())
|
||||
|
|
@ -117,13 +127,7 @@ float LinearGradientStyleValue::angle_degrees(CSSPixelSize gradient_size) const
|
|||
return angle;
|
||||
},
|
||||
[&](NonnullRefPtr<StyleValue const> const& style_value) {
|
||||
if (style_value->is_angle())
|
||||
return style_value->as_angle().angle().to_degrees();
|
||||
if (style_value->is_calculated()) {
|
||||
if (auto maybe_angle = style_value->as_calculated().resolve_angle({}); maybe_angle.has_value())
|
||||
return maybe_angle->to_degrees();
|
||||
}
|
||||
return 0.0;
|
||||
return Angle::from_style_value(style_value, {}).to_degrees();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public:
|
|||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
virtual ~LinearGradientStyleValue() override = default;
|
||||
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
|
||||
virtual bool equals(StyleValue const& other) const override;
|
||||
|
||||
Vector<ColorStopListElement> const& color_stop_list() const
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ String RadialGradientStyleValue::to_string(SerializationMode mode) const
|
|||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
CSSPixelSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, CSSPixelPoint center, CSSPixelRect const& size) const
|
||||
CSSPixelSize RadialGradientStyleValue::resolve_size(CSSPixelPoint center, CSSPixelRect const& size) const
|
||||
{
|
||||
auto const side_shape = [&](auto distance_function) {
|
||||
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
|
|||
},
|
||||
[&](CircleSize const& circle_size) {
|
||||
if (circle_size.radius->is_length()) {
|
||||
auto radius = circle_size.radius->as_length().length().to_px(node);
|
||||
auto radius = circle_size.radius->as_length().length().absolute_length_to_px();
|
||||
return CSSPixelSize { radius, radius };
|
||||
}
|
||||
if (circle_size.radius->is_calculated()) {
|
||||
CalculationResolutionContext context {
|
||||
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
|
||||
};
|
||||
auto radius = circle_size.radius->as_calculated().resolve_length(context)->to_px(node);
|
||||
auto radius = circle_size.radius->as_calculated().resolve_length({})->absolute_length_to_px();
|
||||
return CSSPixelSize { radius, radius };
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
},
|
||||
[&](EllipseSize const& ellipse_size) {
|
||||
auto resolve = [&](StyleValue const& radius_value, auto percentage_basis_pixels) {
|
||||
auto percentage_basis = Length::make_px(percentage_basis_pixels);
|
||||
CalculationResolutionContext context {
|
||||
.percentage_basis = percentage_basis,
|
||||
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
|
||||
};
|
||||
if (radius_value.is_length())
|
||||
return radius_value.as_length().length().to_px(node);
|
||||
return radius_value.as_length().length().absolute_length_to_px();
|
||||
if (radius_value.is_percentage())
|
||||
return percentage_basis.percentage_of(radius_value.as_percentage().percentage()).to_px(node);
|
||||
return CSSPixels { radius_value.as_percentage().percentage().as_fraction() * percentage_basis_pixels };
|
||||
if (radius_value.is_calculated())
|
||||
return radius_value.as_calculated().resolve_length(context)->to_px(node);
|
||||
return radius_value.as_calculated().resolve_length({})->absolute_length_to_px();
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
auto radius_a = resolve(*ellipse_size.radius_a, size.width());
|
||||
|
|
@ -246,7 +238,7 @@ void RadialGradientStyleValue::resolve_for_size(Layout::NodeWithStyle const& nod
|
|||
{
|
||||
CSSPixelRect gradient_box { { 0, 0 }, paint_size };
|
||||
auto center = m_properties.position->resolved(node, gradient_box);
|
||||
auto gradient_size = resolve_size(node, center, gradient_box);
|
||||
auto gradient_size = resolve_size(center, gradient_box);
|
||||
|
||||
ResolvedDataCacheKey cache_key {
|
||||
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
|
||||
|
|
@ -262,6 +254,35 @@ void RadialGradientStyleValue::resolve_for_size(Layout::NodeWithStyle const& nod
|
|||
}
|
||||
}
|
||||
|
||||
ValueComparingNonnullRefPtr<StyleValue const> RadialGradientStyleValue::absolutized(ComputationContext const& context) const
|
||||
{
|
||||
Vector<ColorStopListElement> absolutized_color_stops;
|
||||
absolutized_color_stops.ensure_capacity(m_properties.color_stop_list.size());
|
||||
for (auto const& color_stop : m_properties.color_stop_list) {
|
||||
absolutized_color_stops.unchecked_append(color_stop.absolutized(context));
|
||||
}
|
||||
|
||||
auto absolutized_size = m_properties.size.visit(
|
||||
[&](Extent extent) -> Size {
|
||||
return extent;
|
||||
},
|
||||
[&](CircleSize const& circle_size) -> Size {
|
||||
return CircleSize {
|
||||
.radius = circle_size.radius->absolutized(context),
|
||||
};
|
||||
},
|
||||
[&](EllipseSize const& ellipse_size) -> Size {
|
||||
return EllipseSize {
|
||||
.radius_a = ellipse_size.radius_a->absolutized(context),
|
||||
.radius_b = ellipse_size.radius_b->absolutized(context),
|
||||
};
|
||||
});
|
||||
|
||||
NonnullRefPtr absolutized_position = m_properties.position->absolutized(context)->as_position();
|
||||
|
||||
return create(m_properties.ending_shape, move(absolutized_size), move(absolutized_position), move(absolutized_color_stops), m_properties.repeating, m_properties.interpolation_method);
|
||||
}
|
||||
|
||||
bool RadialGradientStyleValue::equals(StyleValue const& other) const
|
||||
{
|
||||
if (type() != other.type())
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ public:
|
|||
|
||||
void paint(DisplayListRecordingContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override;
|
||||
|
||||
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
|
||||
virtual bool equals(StyleValue const& other) const override;
|
||||
|
||||
Vector<ColorStopListElement> const& color_stop_list() const
|
||||
|
|
@ -73,7 +74,7 @@ public:
|
|||
|
||||
void resolve_for_size(Layout::NodeWithStyle const&, CSSPixelSize) const override;
|
||||
|
||||
CSSPixelSize resolve_size(Layout::Node const&, CSSPixelPoint, CSSPixelRect const&) const;
|
||||
CSSPixelSize resolve_size(CSSPixelPoint, CSSPixelRect const&) const;
|
||||
|
||||
bool is_repeating() const { return m_properties.repeating == GradientRepeating::Yes; }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue