LibWeb: Implement css gradient-interpolation-method

This commit is contained in:
Gingeh 2025-02-19 21:02:12 +11:00 committed by Sam Atkins
commit 31853c13d3
Notes: github-actions[bot] 2025-03-06 11:34:14 +00:00
35 changed files with 499 additions and 101 deletions

View file

@ -13,6 +13,7 @@
#include <LibWeb/CSS/Enums.h>
#include <LibWeb/CSS/PercentageOr.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/CSS/StyleValues/CSSColorValue.h>
namespace Web::CSS {
@ -48,6 +49,112 @@ enum class GradientRepeating {
No
};
enum class GradientSpace : u8 {
sRGB,
sRGBLinear,
DisplayP3,
A98RGB,
ProPhotoRGB,
Rec2020,
Lab,
OKLab,
XYZD50,
XYZD65,
HSL,
HWB,
LCH,
OKLCH,
};
enum class HueMethod : u8 {
Shorter,
Longer,
Increasing,
Decreasing,
};
struct InterpolationMethod {
GradientSpace color_space;
HueMethod hue_method = HueMethod::Shorter;
String to_string() const
{
StringBuilder builder;
switch (color_space) {
case GradientSpace::OKLab:
builder.append("in oklab"sv);
break;
case GradientSpace::sRGB:
builder.append("in srgb"sv);
break;
case GradientSpace::sRGBLinear:
builder.append("in srgb-linear"sv);
break;
case GradientSpace::DisplayP3:
builder.append("in display-p3"sv);
break;
case GradientSpace::A98RGB:
builder.append("in a98-rgb"sv);
break;
case GradientSpace::ProPhotoRGB:
builder.append("in prophoto-rgb"sv);
break;
case GradientSpace::Rec2020:
builder.append("in rec2020"sv);
break;
case GradientSpace::Lab:
builder.append("in lab"sv);
break;
case GradientSpace::XYZD50:
builder.append("in xyz-d50"sv);
break;
case GradientSpace::XYZD65:
builder.append("in xyz-d65"sv);
break;
case GradientSpace::HSL:
builder.append("in hsl"sv);
break;
case GradientSpace::HWB:
builder.append("in hwb"sv);
break;
case GradientSpace::LCH:
builder.append("in lch"sv);
break;
case GradientSpace::OKLCH:
builder.append("in oklch"sv);
break;
}
switch (hue_method) {
case HueMethod::Shorter:
// "shorter" is the default value and isn't serialized
break;
case HueMethod::Longer:
builder.append(" longer hue"sv);
break;
case HueMethod::Increasing:
builder.append(" increasing hue"sv);
break;
case HueMethod::Decreasing:
builder.append(" decreasing hue"sv);
break;
}
return MUST(builder.to_string());
}
static GradientSpace default_color_space(ColorSyntax color_syntax)
{
if (color_syntax == ColorSyntax::Legacy)
return GradientSpace::sRGB;
return GradientSpace::OKLab;
}
bool operator==(InterpolationMethod const&) const = default;
};
template<typename TPosition>
struct ColorStopListElement {
using PositionType = TPosition;

View file

@ -25,7 +25,7 @@ public:
private:
CSSColor(ColorType color_type, ValueComparingNonnullRefPtr<CSSStyleValue> c1, ValueComparingNonnullRefPtr<CSSStyleValue> c2, ValueComparingNonnullRefPtr<CSSStyleValue> c3, ValueComparingNonnullRefPtr<CSSStyleValue> alpha)
: CSSColorValue(color_type)
: CSSColorValue(color_type, ColorSyntax::Modern)
, m_properties { .channels = { move(c1), move(c2), move(c3) }, .alpha = move(alpha) }
{
}

View file

@ -18,13 +18,14 @@
namespace Web::CSS {
ValueComparingNonnullRefPtr<CSSColorValue> CSSColorValue::create_from_color(Color color, Optional<FlyString> name)
ValueComparingNonnullRefPtr<CSSColorValue> CSSColorValue::create_from_color(Color color, ColorSyntax color_syntax, Optional<FlyString> name)
{
return CSSRGB::create(
NumberStyleValue::create(color.red()),
NumberStyleValue::create(color.green()),
NumberStyleValue::create(color.blue()),
NumberStyleValue::create(color.alpha() / 255.0),
color_syntax,
name);
}

View file

@ -15,10 +15,15 @@
namespace Web::CSS {
enum class ColorSyntax : u8 {
Legacy,
Modern,
};
// https://drafts.css-houdini.org/css-typed-om-1/#csscolorvalue
class CSSColorValue : public CSSStyleValue {
public:
static ValueComparingNonnullRefPtr<CSSColorValue> create_from_color(Color color, Optional<FlyString> name = {});
static ValueComparingNonnullRefPtr<CSSColorValue> create_from_color(Color color, ColorSyntax color_syntax, Optional<FlyString> name = {});
virtual ~CSSColorValue() override = default;
virtual bool has_color() const override { return true; }
@ -42,11 +47,13 @@ public:
LightDark, // This is used by CSSLightDark for light-dark(..., ...).
};
ColorType color_type() const { return m_color_type; }
ColorSyntax color_syntax() const { return m_color_syntax; }
protected:
explicit CSSColorValue(ColorType color_type)
explicit CSSColorValue(ColorType color_type, ColorSyntax color_syntax)
: CSSStyleValue(Type::Color)
, m_color_type(color_type)
, m_color_syntax(color_syntax)
{
}
@ -55,6 +62,7 @@ protected:
static Optional<double> resolve_alpha(CSSStyleValue const&);
ColorType m_color_type;
ColorSyntax m_color_syntax;
};
}

View file

@ -14,13 +14,13 @@ namespace Web::CSS {
// https://drafts.css-houdini.org/css-typed-om-1/#csshsl
class CSSHSL final : public CSSColorValue {
public:
static ValueComparingNonnullRefPtr<CSSHSL> create(ValueComparingNonnullRefPtr<CSSStyleValue> h, ValueComparingNonnullRefPtr<CSSStyleValue> s, ValueComparingNonnullRefPtr<CSSStyleValue> l, ValueComparingRefPtr<CSSStyleValue> alpha = {})
static ValueComparingNonnullRefPtr<CSSHSL> create(ValueComparingNonnullRefPtr<CSSStyleValue> h, ValueComparingNonnullRefPtr<CSSStyleValue> s, ValueComparingNonnullRefPtr<CSSStyleValue> l, ValueComparingRefPtr<CSSStyleValue> alpha, ColorSyntax color_syntax)
{
// alpha defaults to 1
if (!alpha)
return adopt_ref(*new (nothrow) CSSHSL(move(h), move(s), move(l), NumberStyleValue::create(1)));
return adopt_ref(*new (nothrow) CSSHSL(move(h), move(s), move(l), NumberStyleValue::create(1), color_syntax));
return adopt_ref(*new (nothrow) CSSHSL(move(h), move(s), move(l), alpha.release_nonnull()));
return adopt_ref(*new (nothrow) CSSHSL(move(h), move(s), move(l), alpha.release_nonnull(), color_syntax));
}
virtual ~CSSHSL() override = default;
@ -36,8 +36,8 @@ public:
virtual bool equals(CSSStyleValue const& other) const override;
private:
CSSHSL(ValueComparingNonnullRefPtr<CSSStyleValue> h, ValueComparingNonnullRefPtr<CSSStyleValue> s, ValueComparingNonnullRefPtr<CSSStyleValue> l, ValueComparingNonnullRefPtr<CSSStyleValue> alpha)
: CSSColorValue(ColorType::HSL)
CSSHSL(ValueComparingNonnullRefPtr<CSSStyleValue> h, ValueComparingNonnullRefPtr<CSSStyleValue> s, ValueComparingNonnullRefPtr<CSSStyleValue> l, ValueComparingNonnullRefPtr<CSSStyleValue> alpha, ColorSyntax color_syntax)
: CSSColorValue(ColorType::HSL, color_syntax)
, m_properties { .h = move(h), .s = move(s), .l = move(l), .alpha = move(alpha) }
{
}

View file

@ -37,7 +37,7 @@ public:
private:
CSSHWB(ValueComparingNonnullRefPtr<CSSStyleValue> h, ValueComparingNonnullRefPtr<CSSStyleValue> w, ValueComparingNonnullRefPtr<CSSStyleValue> b, ValueComparingNonnullRefPtr<CSSStyleValue> alpha)
: CSSColorValue(ColorType::HWB)
: CSSColorValue(ColorType::HWB, ColorSyntax::Modern)
, m_properties { .h = move(h), .w = move(w), .b = move(b), .alpha = move(alpha) }
{
}

View file

@ -33,7 +33,7 @@ public:
protected:
CSSLCHLike(ColorType color_type, ValueComparingNonnullRefPtr<CSSStyleValue> l, ValueComparingNonnullRefPtr<CSSStyleValue> c, ValueComparingNonnullRefPtr<CSSStyleValue> h, ValueComparingNonnullRefPtr<CSSStyleValue> alpha)
: CSSColorValue(color_type)
: CSSColorValue(color_type, ColorSyntax::Modern)
, m_properties { .l = move(l), .c = move(c), .h = move(h), .alpha = move(alpha) }
{
}

View file

@ -34,7 +34,7 @@ public:
protected:
CSSLabLike(ColorType color_type, ValueComparingNonnullRefPtr<CSSStyleValue> l, ValueComparingNonnullRefPtr<CSSStyleValue> a, ValueComparingNonnullRefPtr<CSSStyleValue> b, ValueComparingNonnullRefPtr<CSSStyleValue> alpha)
: CSSColorValue(color_type)
: CSSColorValue(color_type, ColorSyntax::Modern)
, m_properties { .l = move(l), .a = move(a), .b = move(b), .alpha = move(alpha) }
{
}

View file

@ -26,7 +26,7 @@ public:
private:
CSSLightDark(ValueComparingNonnullRefPtr<CSSStyleValue> light, ValueComparingNonnullRefPtr<CSSStyleValue> dark)
: CSSColorValue(CSSColorValue::ColorType::LightDark)
: CSSColorValue(CSSColorValue::ColorType::LightDark, ColorSyntax::Modern)
, m_properties { .light = move(light), .dark = move(dark) }
{
}

View file

@ -14,13 +14,13 @@ namespace Web::CSS {
// https://drafts.css-houdini.org/css-typed-om-1/#cssrgb
class CSSRGB final : public CSSColorValue {
public:
static ValueComparingNonnullRefPtr<CSSRGB> create(ValueComparingNonnullRefPtr<CSSStyleValue> r, ValueComparingNonnullRefPtr<CSSStyleValue> g, ValueComparingNonnullRefPtr<CSSStyleValue> b, ValueComparingRefPtr<CSSStyleValue> alpha = {}, Optional<FlyString> name = {})
static ValueComparingNonnullRefPtr<CSSRGB> create(ValueComparingNonnullRefPtr<CSSStyleValue> r, ValueComparingNonnullRefPtr<CSSStyleValue> g, ValueComparingNonnullRefPtr<CSSStyleValue> b, ValueComparingRefPtr<CSSStyleValue> alpha, ColorSyntax color_syntax, Optional<FlyString> name = {})
{
// alpha defaults to 1
if (!alpha)
return adopt_ref(*new (nothrow) CSSRGB(move(r), move(g), move(b), NumberStyleValue::create(1), name));
return adopt_ref(*new (nothrow) CSSRGB(move(r), move(g), move(b), NumberStyleValue::create(1), color_syntax, name));
return adopt_ref(*new (nothrow) CSSRGB(move(r), move(g), move(b), alpha.release_nonnull(), name));
return adopt_ref(*new (nothrow) CSSRGB(move(r), move(g), move(b), alpha.release_nonnull(), color_syntax, name));
}
virtual ~CSSRGB() override = default;
@ -36,8 +36,8 @@ public:
virtual bool equals(CSSStyleValue const& other) const override;
private:
CSSRGB(ValueComparingNonnullRefPtr<CSSStyleValue> r, ValueComparingNonnullRefPtr<CSSStyleValue> g, ValueComparingNonnullRefPtr<CSSStyleValue> b, ValueComparingNonnullRefPtr<CSSStyleValue> alpha, Optional<FlyString> name = {})
: CSSColorValue(ColorType::RGB)
CSSRGB(ValueComparingNonnullRefPtr<CSSStyleValue> r, ValueComparingNonnullRefPtr<CSSStyleValue> g, ValueComparingNonnullRefPtr<CSSStyleValue> b, ValueComparingNonnullRefPtr<CSSStyleValue> alpha, ColorSyntax color_syntax, Optional<FlyString> name = {})
: CSSColorValue(ColorType::RGB, color_syntax)
, m_properties { .r = move(r), .g = move(g), .b = move(b), .alpha = move(alpha), .name = name }
{
}

View file

@ -22,6 +22,8 @@ String ConicGradientStyleValue::to_string(SerializationMode mode) const
builder.append("conic-gradient("sv);
bool has_from_angle = m_properties.from_angle.to_degrees() != 0;
bool has_at_position = !m_properties.position->is_center();
bool has_color_space = m_properties.interpolation_method.has_value() && m_properties.interpolation_method.value().color_space != InterpolationMethod::default_color_space(m_properties.color_syntax);
if (has_from_angle)
builder.appendff("from {}", m_properties.from_angle.to_string());
if (has_at_position) {
@ -29,7 +31,12 @@ String ConicGradientStyleValue::to_string(SerializationMode mode) const
builder.append(' ');
builder.appendff("at {}"sv, m_properties.position->to_string(mode));
}
if (has_from_angle || has_at_position)
if (has_color_space) {
if (has_from_angle || has_at_position)
builder.append(' ');
builder.append(m_properties.interpolation_method.value().to_string());
}
if (has_from_angle || has_at_position || has_color_space)
builder.append(", "sv);
serialize_color_stop_list(builder, m_properties.color_stop_list, mode);
builder.append(')');

View file

@ -17,10 +17,11 @@ namespace Web::CSS {
class ConicGradientStyleValue final : public AbstractImageStyleValue {
public:
static ValueComparingNonnullRefPtr<ConicGradientStyleValue> create(Angle from_angle, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating)
static ValueComparingNonnullRefPtr<ConicGradientStyleValue> create(Angle from_angle, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
{
VERIFY(!color_stop_list.is_empty());
return adopt_ref(*new (nothrow) ConicGradientStyleValue(from_angle, move(position), move(color_stop_list), repeating));
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();
return adopt_ref(*new (nothrow) ConicGradientStyleValue(from_angle, move(position), move(color_stop_list), repeating, interpolation_method, any_non_legacy ? ColorSyntax::Modern : ColorSyntax::Legacy));
}
virtual String to_string(SerializationMode) const override;
@ -34,6 +35,14 @@ public:
return m_properties.color_stop_list;
}
InterpolationMethod interpolation_method() const
{
if (m_properties.interpolation_method.has_value())
return m_properties.interpolation_method.value();
return InterpolationMethod { .color_space = InterpolationMethod::default_color_space(m_properties.color_syntax) };
}
float angle_degrees() const;
bool is_paintable() const override { return true; }
@ -45,18 +54,19 @@ public:
bool is_repeating() const { return m_properties.repeating == GradientRepeating::Yes; }
private:
ConicGradientStyleValue(Angle from_angle, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating)
ConicGradientStyleValue(Angle from_angle, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
: AbstractImageStyleValue(Type::ConicGradient)
, m_properties { .from_angle = from_angle, .position = move(position), .color_stop_list = move(color_stop_list), .repeating = repeating }
, m_properties { .from_angle = from_angle, .position = move(position), .color_stop_list = move(color_stop_list), .repeating = repeating, .interpolation_method = interpolation_method, .color_syntax = color_syntax }
{
}
struct Properties {
// FIXME: Support <color-interpolation-method>
Angle from_angle;
ValueComparingNonnullRefPtr<PositionStyleValue> position;
Vector<AngularColorStopListElement> color_stop_list;
GradientRepeating repeating;
Optional<InterpolationMethod> interpolation_method;
ColorSyntax color_syntax;
bool operator==(Properties const&) const = default;
} m_properties;

View file

@ -27,30 +27,45 @@ String LinearGradientStyleValue::to_string(SerializationMode mode) const
case SideOrCorner::Right:
return "right"sv;
case SideOrCorner::TopLeft:
return "top left"sv;
return "left top"sv;
case SideOrCorner::TopRight:
return "top right"sv;
return "right top"sv;
case SideOrCorner::BottomLeft:
return "bottom left"sv;
return "left bottom"sv;
case SideOrCorner::BottomRight:
return "bottom right"sv;
return "right bottom"sv;
default:
VERIFY_NOT_REACHED();
}
};
auto default_direction = m_properties.gradient_type == GradientType::WebKit ? SideOrCorner::Top : SideOrCorner::Bottom;
bool has_direction = m_properties.direction != default_direction;
bool has_color_space = m_properties.interpolation_method.has_value() && m_properties.interpolation_method.value().color_space != InterpolationMethod::default_color_space(m_properties.color_syntax);
if (m_properties.gradient_type == GradientType::WebKit)
builder.append("-webkit-"sv);
if (is_repeating())
builder.append("repeating-"sv);
builder.append("linear-gradient("sv);
m_properties.direction.visit(
[&](SideOrCorner side_or_corner) {
return builder.appendff("{}{}, "sv, m_properties.gradient_type == GradientType::Standard ? "to "sv : ""sv, side_or_corner_to_string(side_or_corner));
},
[&](Angle const& angle) {
return builder.appendff("{}, "sv, angle.to_string());
});
if (has_direction) {
m_properties.direction.visit(
[&](SideOrCorner side_or_corner) {
builder.appendff("{}{}"sv, m_properties.gradient_type == GradientType::Standard ? "to "sv : ""sv, side_or_corner_to_string(side_or_corner));
},
[&](Angle const& angle) {
builder.append(angle.to_string());
});
if (has_color_space)
builder.append(' ');
}
if (has_color_space)
builder.append(m_properties.interpolation_method.value().to_string());
if (has_direction || has_color_space)
builder.append(", "sv);
serialize_color_stop_list(builder, m_properties.color_stop_list, mode);
builder.append(")"sv);

View file

@ -13,6 +13,7 @@
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/Percentage.h>
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
#include <LibWeb/CSS/StyleValues/CSSColorValue.h>
#include <LibWeb/Painting/GradientPainting.h>
namespace Web::CSS {
@ -38,10 +39,11 @@ public:
WebKit
};
static ValueComparingNonnullRefPtr<LinearGradientStyleValue> create(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating)
static ValueComparingNonnullRefPtr<LinearGradientStyleValue> create(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
{
VERIFY(!color_stop_list.is_empty());
return adopt_ref(*new (nothrow) LinearGradientStyleValue(direction, move(color_stop_list), type, repeating));
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();
return adopt_ref(*new (nothrow) LinearGradientStyleValue(direction, move(color_stop_list), type, repeating, interpolation_method, any_non_legacy ? ColorSyntax::Modern : ColorSyntax::Legacy));
}
virtual String to_string(SerializationMode) const override;
@ -53,6 +55,17 @@ public:
return m_properties.color_stop_list;
}
// FIXME: This (and the any_non_legacy code in the constructor) is duplicated in the separate gradient classes,
// should this logic be pulled into some kind of GradientStyleValue superclass?
// It could also contain the "gradient related things" currently in AbstractImageStyleValue.h
InterpolationMethod interpolation_method() const
{
if (m_properties.interpolation_method.has_value())
return m_properties.interpolation_method.value();
return InterpolationMethod { .color_space = InterpolationMethod::default_color_space(m_properties.color_syntax) };
}
bool is_repeating() const { return m_properties.repeating == GradientRepeating::Yes; }
float angle_degrees(CSSPixelSize gradient_size) const;
@ -63,9 +76,9 @@ public:
void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override;
private:
LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating)
LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> 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 }
, m_properties { .direction = direction, .color_stop_list = move(color_stop_list), .gradient_type = type, .repeating = repeating, .interpolation_method = interpolation_method, .color_syntax = color_syntax }
{
}
@ -74,6 +87,8 @@ private:
Vector<LinearColorStopListElement> color_stop_list;
GradientType gradient_type;
GradientRepeating repeating;
Optional<InterpolationMethod> interpolation_method;
ColorSyntax color_syntax;
bool operator==(Properties const&) const = default;
} m_properties;

View file

@ -19,8 +19,11 @@ String RadialGradientStyleValue::to_string(SerializationMode mode) const
StringBuilder builder;
if (is_repeating())
builder.append("repeating-"sv);
builder.appendff("radial-gradient({} "sv,
m_properties.ending_shape == EndingShape::Circle ? "circle"sv : "ellipse"sv);
builder.appendff("radial-gradient("sv);
bool has_size = !m_properties.size.has<Extent>() || m_properties.size.get<Extent>() != Extent::FarthestCorner;
bool has_position = !m_properties.position->is_center();
bool has_color_space = m_properties.interpolation_method.has_value() && m_properties.interpolation_method.value().color_space != InterpolationMethod::default_color_space(m_properties.color_syntax);
m_properties.size.visit(
[&](Extent extent) {
@ -31,7 +34,8 @@ String RadialGradientStyleValue::to_string(SerializationMode mode) const
case Extent::ClosestSide:
return "closest-side"sv;
case Extent::FarthestCorner:
return "farthest-corner"sv;
// "farthest-corner" is the default value and isn't serialized
return ""sv;
case Extent::FarthestSide:
return "farthest-side"sv;
default:
@ -46,10 +50,23 @@ String RadialGradientStyleValue::to_string(SerializationMode mode) const
builder.appendff("{} {}", ellipse_size.radius_a.to_string(), ellipse_size.radius_b.to_string());
});
if (!m_properties.position->is_center())
builder.appendff(" at {}"sv, m_properties.position->to_string(mode));
if (has_position) {
if (has_size)
builder.append(' ');
builder.appendff("at {}"sv, m_properties.position->to_string(mode));
}
if (has_color_space) {
if (has_size || has_position)
builder.append(' ');
builder.append(m_properties.interpolation_method.value().to_string());
}
if (has_size || has_position || has_color_space)
builder.append(", "sv);
builder.append(", "sv);
serialize_color_stop_list(builder, m_properties.color_stop_list, mode);
builder.append(')');
return MUST(builder.to_string());

View file

@ -43,10 +43,11 @@ public:
using Size = Variant<Extent, CircleSize, EllipseSize>;
static ValueComparingNonnullRefPtr<RadialGradientStyleValue> create(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<LinearColorStopListElement> color_stop_list, GradientRepeating repeating)
static ValueComparingNonnullRefPtr<RadialGradientStyleValue> create(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<LinearColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
{
VERIFY(!color_stop_list.is_empty());
return adopt_ref(*new (nothrow) RadialGradientStyleValue(ending_shape, size, move(position), move(color_stop_list), repeating));
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();
return adopt_ref(*new (nothrow) RadialGradientStyleValue(ending_shape, size, move(position), move(color_stop_list), repeating, interpolation_method, any_non_legacy ? ColorSyntax::Modern : ColorSyntax::Legacy));
}
virtual String to_string(SerializationMode) const override;
@ -60,6 +61,14 @@ public:
return m_properties.color_stop_list;
}
InterpolationMethod interpolation_method() const
{
if (m_properties.interpolation_method.has_value())
return m_properties.interpolation_method.value();
return InterpolationMethod { .color_space = InterpolationMethod::default_color_space(m_properties.color_syntax) };
}
bool is_paintable() const override { return true; }
void resolve_for_size(Layout::NodeWithStyle const&, CSSPixelSize) const override;
@ -71,9 +80,9 @@ public:
virtual ~RadialGradientStyleValue() override = default;
private:
RadialGradientStyleValue(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<LinearColorStopListElement> color_stop_list, GradientRepeating repeating)
RadialGradientStyleValue(EndingShape ending_shape, Size size, ValueComparingNonnullRefPtr<PositionStyleValue> position, Vector<LinearColorStopListElement> 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 }
, 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 }
{
}
@ -83,6 +92,8 @@ private:
ValueComparingNonnullRefPtr<PositionStyleValue> position;
Vector<LinearColorStopListElement> color_stop_list;
GradientRepeating repeating;
Optional<InterpolationMethod> interpolation_method;
ColorSyntax color_syntax;
bool operator==(Properties const&) const = default;
} m_properties;