LibWeb: Store GradientStyleValue color-stop positions as StyleValues

A few things fall out of this:
- We no longer need to templatize our color-stop list types.
- A bit more code is required to resolve gradient data.

This results in a slightly different rendering for a couple of the test
gradients, with a larger difference between macOS and Linux. I've
expanded the fuzziness factor to cover for it.
This commit is contained in:
Sam Atkins 2025-11-28 17:14:31 +00:00
parent 71397c876c
commit 73fbaaba77
Notes: github-actions[bot] 2025-12-01 11:10:16 +00:00
10 changed files with 103 additions and 85 deletions

View file

@ -16,4 +16,23 @@ GC::Ref<CSSStyleValue> AbstractImageStyleValue::reify(JS::Realm& realm, FlyStrin
return CSSImageValue::create(realm, *this);
}
void serialize_color_stop_list(StringBuilder& builder, Vector<ColorStopListElement> const& color_stop_list, SerializationMode mode)
{
bool first = true;
for (auto const& element : color_stop_list) {
if (!first)
builder.append(", "sv);
if (element.transition_hint.has_value())
builder.appendff("{}, "sv, element.transition_hint->value->to_string(mode));
builder.append(element.color_stop.color->to_string(mode));
if (element.color_stop.position)
builder.appendff(" {}"sv, element.color_stop.position->to_string(mode));
if (element.color_stop.second_position)
builder.appendff(" {}"sv, element.color_stop.second_position->to_string(mode));
first = false;
}
}
}

View file

@ -157,45 +157,22 @@ struct InterpolationMethod {
bool operator==(InterpolationMethod const&) const = default;
};
template<typename TPosition>
struct ColorStopListElement {
using PositionType = TPosition;
struct ColorHint {
TPosition value;
inline bool operator==(ColorHint const&) const = default;
NonnullRefPtr<StyleValue const> value;
bool operator==(ColorHint const&) const = default;
};
Optional<ColorHint> transition_hint;
struct ColorStop {
RefPtr<StyleValue const> color;
Optional<TPosition> position;
Optional<TPosition> second_position = {};
inline bool operator==(ColorStop const&) const = default;
RefPtr<StyleValue const> position;
RefPtr<StyleValue const> second_position {};
bool operator==(ColorStop const&) const = default;
} color_stop;
inline bool operator==(ColorStopListElement const&) const = default;
bool operator==(ColorStopListElement const&) const = default;
};
using LinearColorStopListElement = ColorStopListElement<LengthPercentage>;
using AngularColorStopListElement = ColorStopListElement<AnglePercentage>;
static void serialize_color_stop_list(StringBuilder& builder, auto const& color_stop_list, SerializationMode mode)
{
bool first = true;
for (auto const& element : color_stop_list) {
if (!first)
builder.append(", "sv);
if (element.transition_hint.has_value())
builder.appendff("{}, "sv, element.transition_hint->value.to_string(mode));
builder.append(element.color_stop.color->to_string(mode));
for (auto position : Array { &element.color_stop.position, &element.color_stop.second_position }) {
if (position->has_value())
builder.appendff(" {}"sv, (*position)->to_string(mode));
}
first = false;
}
}
void serialize_color_stop_list(StringBuilder&, Vector<ColorStopListElement> const&, SerializationMode);
}

View file

@ -9,8 +9,6 @@
#pragma once
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/CalculatedOr.h>
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
#include <LibWeb/Painting/GradientPainting.h>
@ -18,7 +16,7 @@ namespace Web::CSS {
class ConicGradientStyleValue final : public AbstractImageStyleValue {
public:
static ValueComparingNonnullRefPtr<ConicGradientStyleValue const> create(ValueComparingRefPtr<StyleValue const> from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
static ValueComparingNonnullRefPtr<ConicGradientStyleValue const> create(ValueComparingRefPtr<StyleValue const> from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<ColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
{
VERIFY(!color_stop_list.is_empty());
bool any_non_legacy = color_stop_list.find_first_index_if([](auto const& stop) { return !stop.color_stop.color->is_keyword() && stop.color_stop.color->as_color().color_syntax() == ColorSyntax::Modern; }).has_value();
@ -31,7 +29,7 @@ public:
virtual bool equals(StyleValue const& other) const override;
Vector<AngularColorStopListElement> const& color_stop_list() const
Vector<ColorStopListElement> const& color_stop_list() const
{
return m_properties.color_stop_list;
}
@ -55,7 +53,7 @@ public:
bool is_repeating() const { return m_properties.repeating == GradientRepeating::Yes; }
private:
ConicGradientStyleValue(ValueComparingRefPtr<StyleValue const> from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
ConicGradientStyleValue(ValueComparingRefPtr<StyleValue const> from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<ColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
: AbstractImageStyleValue(Type::ConicGradient)
, m_properties { .from_angle = move(from_angle), .position = move(position), .color_stop_list = move(color_stop_list), .repeating = repeating, .interpolation_method = interpolation_method, .color_syntax = color_syntax }
{
@ -64,7 +62,7 @@ private:
struct Properties {
ValueComparingRefPtr<StyleValue const> from_angle;
ValueComparingNonnullRefPtr<PositionStyleValue const> position;
Vector<AngularColorStopListElement> color_stop_list;
Vector<ColorStopListElement> color_stop_list;
GradientRepeating repeating;
Optional<InterpolationMethod> interpolation_method;
ColorSyntax color_syntax;

View file

@ -39,7 +39,7 @@ public:
WebKit
};
static ValueComparingNonnullRefPtr<LinearGradientStyleValue const> create(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
static ValueComparingNonnullRefPtr<LinearGradientStyleValue const> create(GradientDirection direction, Vector<ColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
{
VERIFY(!color_stop_list.is_empty());
bool any_non_legacy = color_stop_list.find_first_index_if([](auto const& stop) { return !stop.color_stop.color->is_keyword() && stop.color_stop.color->as_color().color_syntax() == ColorSyntax::Modern; }).has_value();
@ -50,7 +50,7 @@ public:
virtual ~LinearGradientStyleValue() override = default;
virtual bool equals(StyleValue const& other) const override;
Vector<LinearColorStopListElement> const& color_stop_list() const
Vector<ColorStopListElement> const& color_stop_list() const
{
return m_properties.color_stop_list;
}
@ -76,7 +76,7 @@ public:
void paint(DisplayListRecordingContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override;
private:
LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
LinearGradientStyleValue(GradientDirection direction, Vector<ColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
: AbstractImageStyleValue(Type::LinearGradient)
, m_properties { .direction = direction, .color_stop_list = move(color_stop_list), .gradient_type = type, .repeating = repeating, .interpolation_method = interpolation_method, .color_syntax = color_syntax }
{
@ -84,7 +84,7 @@ private:
struct Properties {
GradientDirection direction;
Vector<LinearColorStopListElement> color_stop_list;
Vector<ColorStopListElement> color_stop_list;
GradientType gradient_type;
GradientRepeating repeating;
Optional<InterpolationMethod> interpolation_method;

View file

@ -43,7 +43,7 @@ public:
using Size = Variant<Extent, CircleSize, EllipseSize>;
static ValueComparingNonnullRefPtr<RadialGradientStyleValue const> create(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<LinearColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
static ValueComparingNonnullRefPtr<RadialGradientStyleValue const> create(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<ColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
{
VERIFY(!color_stop_list.is_empty());
bool any_non_legacy = color_stop_list.find_first_index_if([](auto const& stop) { return !stop.color_stop.color->is_keyword() && stop.color_stop.color->as_color().color_syntax() == ColorSyntax::Modern; }).has_value();
@ -56,7 +56,7 @@ public:
virtual bool equals(StyleValue const& other) const override;
Vector<LinearColorStopListElement> const& color_stop_list() const
Vector<ColorStopListElement> const& color_stop_list() const
{
return m_properties.color_stop_list;
}
@ -80,7 +80,7 @@ public:
virtual ~RadialGradientStyleValue() override = default;
private:
RadialGradientStyleValue(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<LinearColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
RadialGradientStyleValue(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<ColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
: AbstractImageStyleValue(Type::RadialGradient)
, m_properties { .ending_shape = ending_shape, .size = size, .position = move(position), .color_stop_list = move(color_stop_list), .repeating = repeating, .interpolation_method = interpolation_method, .color_syntax = color_syntax }
{
@ -90,7 +90,7 @@ private:
EndingShape ending_shape;
Size size;
ValueComparingNonnullRefPtr<PositionStyleValue const> position;
Vector<LinearColorStopListElement> color_stop_list;
Vector<ColorStopListElement> color_stop_list;
GradientRepeating repeating;
Optional<InterpolationMethod> interpolation_method;
ColorSyntax color_syntax;