mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
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:
parent
71397c876c
commit
73fbaaba77
Notes:
github-actions[bot]
2025-12-01 11:10:16 +00:00
Author: https://github.com/AtkinsSJ
Commit: 73fbaaba77
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 103 additions and 85 deletions
|
|
@ -18,8 +18,7 @@
|
|||
|
||||
namespace Web::CSS::Parser {
|
||||
|
||||
template<typename TElement>
|
||||
Optional<Vector<TElement>> Parser::parse_color_stop_list(TokenStream<ComponentValue>& tokens, auto parse_position)
|
||||
Optional<Vector<ColorStopListElement>> Parser::parse_color_stop_list(TokenStream<ComponentValue>& tokens, auto parse_position)
|
||||
{
|
||||
enum class ElementType {
|
||||
Garbage,
|
||||
|
|
@ -27,20 +26,20 @@ Optional<Vector<TElement>> Parser::parse_color_stop_list(TokenStream<ComponentVa
|
|||
ColorHint
|
||||
};
|
||||
|
||||
auto parse_color_stop_list_element = [&](TElement& element) -> ElementType {
|
||||
auto parse_color_stop_list_element = [&](auto& element) -> ElementType {
|
||||
tokens.discard_whitespace();
|
||||
if (!tokens.has_next_token())
|
||||
return ElementType::Garbage;
|
||||
|
||||
RefPtr<StyleValue const> color;
|
||||
Optional<typename TElement::PositionType> position;
|
||||
Optional<typename TElement::PositionType> second_position;
|
||||
if (position = parse_position(tokens); position.has_value()) {
|
||||
RefPtr<StyleValue const> position;
|
||||
RefPtr<StyleValue const> second_position;
|
||||
if (position = parse_position(tokens); position) {
|
||||
// [<T-percentage> <color>] or [<T-percentage>]
|
||||
tokens.discard_whitespace();
|
||||
// <T-percentage>
|
||||
if (!tokens.has_next_token() || tokens.next_token().is(Token::Type::Comma)) {
|
||||
element.transition_hint = typename TElement::ColorHint { *position };
|
||||
element.transition_hint = ColorStopListElement::ColorHint { *position };
|
||||
return ElementType::ColorHint;
|
||||
}
|
||||
// <T-percentage> <color>
|
||||
|
|
@ -60,24 +59,24 @@ Optional<Vector<TElement>> Parser::parse_color_stop_list(TokenStream<ComponentVa
|
|||
for (auto stop_position : Array { &position, &second_position }) {
|
||||
if (tokens.has_next_token() && !tokens.next_token().is(Token::Type::Comma)) {
|
||||
*stop_position = parse_position(tokens);
|
||||
if (!stop_position->has_value())
|
||||
if (!stop_position)
|
||||
return ElementType::Garbage;
|
||||
tokens.discard_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
element.color_stop = typename TElement::ColorStop { color, position, second_position };
|
||||
element.color_stop = ColorStopListElement::ColorStop { color, position, second_position };
|
||||
return ElementType::ColorStop;
|
||||
};
|
||||
|
||||
TElement first_element {};
|
||||
ColorStopListElement first_element {};
|
||||
if (parse_color_stop_list_element(first_element) != ElementType::ColorStop)
|
||||
return {};
|
||||
|
||||
Vector<TElement> color_stops { first_element };
|
||||
Vector<ColorStopListElement> color_stops { first_element };
|
||||
while (tokens.has_next_token()) {
|
||||
TElement list_element {};
|
||||
ColorStopListElement list_element {};
|
||||
tokens.discard_whitespace();
|
||||
if (!tokens.consume_a_token().is(Token::Type::Comma))
|
||||
return {};
|
||||
|
|
@ -110,34 +109,34 @@ static StringView consume_if_starts_with(StringView str, StringView start, auto
|
|||
return str;
|
||||
}
|
||||
|
||||
Optional<Vector<LinearColorStopListElement>> Parser::parse_linear_color_stop_list(TokenStream<ComponentValue>& tokens)
|
||||
Optional<Vector<ColorStopListElement>> Parser::parse_linear_color_stop_list(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
// <color-stop-list> =
|
||||
// <linear-color-stop> , [ <linear-color-hint>? , <linear-color-stop> ]#
|
||||
return parse_color_stop_list<LinearColorStopListElement>(
|
||||
return parse_color_stop_list(
|
||||
tokens,
|
||||
[&](auto& it) { return parse_length_percentage(it); });
|
||||
[&](auto& it) { return parse_length_percentage_value(it); });
|
||||
}
|
||||
|
||||
Optional<Vector<AngularColorStopListElement>> Parser::parse_angular_color_stop_list(TokenStream<ComponentValue>& tokens)
|
||||
Optional<Vector<ColorStopListElement>> Parser::parse_angular_color_stop_list(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
auto context_guard = push_temporary_value_parsing_context(SpecialContext::AngularColorStopList);
|
||||
|
||||
// <angular-color-stop-list> =
|
||||
// <angular-color-stop> , [ <angular-color-hint>? , <angular-color-stop> ]#
|
||||
return parse_color_stop_list<AngularColorStopListElement>(
|
||||
return parse_color_stop_list(
|
||||
tokens,
|
||||
[&](TokenStream<ComponentValue>& it) -> Optional<AnglePercentage> {
|
||||
[&](TokenStream<ComponentValue>& it) -> RefPtr<StyleValue const> {
|
||||
if (tokens.next_token().is(Token::Type::Number)) {
|
||||
auto transaction = tokens.begin_transaction();
|
||||
auto numeric_value = tokens.consume_a_token().token().number_value();
|
||||
if (numeric_value == 0) {
|
||||
transaction.commit();
|
||||
return Angle::make_degrees(0);
|
||||
return AngleStyleValue::create(Angle::make_degrees(0));
|
||||
}
|
||||
}
|
||||
|
||||
return parse_angle_percentage(it);
|
||||
return parse_angle_percentage_value(it);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -356,10 +356,9 @@ private:
|
|||
|
||||
RefPtr<FitContentStyleValue const> parse_fit_content_value(TokenStream<ComponentValue>&);
|
||||
|
||||
template<typename TElement>
|
||||
Optional<Vector<TElement>> parse_color_stop_list(TokenStream<ComponentValue>& tokens, auto parse_position);
|
||||
Optional<Vector<LinearColorStopListElement>> parse_linear_color_stop_list(TokenStream<ComponentValue>&);
|
||||
Optional<Vector<AngularColorStopListElement>> parse_angular_color_stop_list(TokenStream<ComponentValue>&);
|
||||
Optional<Vector<ColorStopListElement>> parse_color_stop_list(TokenStream<ComponentValue>& tokens, auto parse_position);
|
||||
Optional<Vector<ColorStopListElement>> parse_linear_color_stop_list(TokenStream<ComponentValue>&);
|
||||
Optional<Vector<ColorStopListElement>> parse_angular_color_stop_list(TokenStream<ComponentValue>&);
|
||||
Optional<InterpolationMethod> parse_interpolation_method(TokenStream<ComponentValue>&);
|
||||
|
||||
RefPtr<LinearGradientStyleValue const> parse_linear_gradient_function(TokenStream<ComponentValue>&);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue