LibWeb: Allow passing a resolution context to CSSStyleValue::to_color

This will be used for resolving any calculated style values within the
various `CSSColorValue` sub-classes.

No functionality changes.
This commit is contained in:
Callum Law 2025-06-29 12:56:28 +12:00 committed by Sam Atkins
commit 62d138ebf7
Notes: github-actions[bot] 2025-07-04 12:20:15 +00:00
33 changed files with 105 additions and 105 deletions

View file

@ -393,7 +393,7 @@ public:
virtual ValueComparingNonnullRefPtr<CSSStyleValue const> absolutized(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const; virtual ValueComparingNonnullRefPtr<CSSStyleValue const> absolutized(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const { return {}; } virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const { return {}; }
Keyword to_keyword() const; Keyword to_keyword() const;
virtual String to_string(SerializationMode) const = 0; virtual String to_string(SerializationMode) const = 0;

View file

@ -226,7 +226,7 @@ Color ComputedProperties::color_or_fallback(PropertyID id, Layout::NodeWithStyle
auto const& value = property(id); auto const& value = property(id);
if (!value.has_color()) if (!value.has_color())
return fallback; return fallback;
return value.to_color(node); return value.to_color(node, {});
} }
// https://drafts.csswg.org/css-color-adjust-1/#determine-the-used-color-scheme // https://drafts.csswg.org/css-color-adjust-1/#determine-the-used-color-scheme
@ -670,7 +670,7 @@ Optional<Color> ComputedProperties::accent_color(Layout::NodeWithStyle const& no
{ {
auto const& value = property(PropertyID::AccentColor); auto const& value = property(PropertyID::AccentColor);
if (value.has_color()) if (value.has_color())
return value.to_color(node); return value.to_color(node, {});
return {}; return {};
} }
@ -924,7 +924,7 @@ Color ComputedProperties::caret_color(Layout::NodeWithStyle const& node) const
return node.computed_values().color(); return node.computed_values().color();
if (value.has_color()) if (value.has_color())
return value.to_color(node); return value.to_color(node, {});
return InitialValues::caret_color(); return InitialValues::caret_color();
} }
@ -1187,7 +1187,7 @@ Vector<ShadowData> ComputedProperties::shadow(PropertyID property_id, Layout::No
maybe_offset_y.release_value(), maybe_offset_y.release_value(),
maybe_blur_radius.release_value(), maybe_blur_radius.release_value(),
maybe_spread_distance.release_value(), maybe_spread_distance.release_value(),
value.color()->to_color(as<Layout::NodeWithStyle>(layout_node)), value.color()->to_color(as<Layout::NodeWithStyle>(layout_node), {}),
value.placement() value.placement()
}; };
}; };
@ -1809,7 +1809,7 @@ Color ComputedProperties::stop_color() const
if (value->has_color()) { if (value->has_color()) {
// FIXME: This is used by the SVGStopElement, which does not participate in layout, // FIXME: This is used by the SVGStopElement, which does not participate in layout,
// so can't pass a layout node (so can't resolve some colors, e.g. palette ones) // so can't pass a layout node (so can't resolve some colors, e.g. palette ones)
return value->to_color({}); return value->to_color({}, {});
} }
return Color::Black; return Color::Black;
} }
@ -1893,8 +1893,8 @@ ScrollbarColorData ComputedProperties::scrollbar_color(Layout::NodeWithStyle con
if (value.is_scrollbar_color()) { if (value.is_scrollbar_color()) {
auto& scrollbar_color_value = value.as_scrollbar_color(); auto& scrollbar_color_value = value.as_scrollbar_color();
auto thumb_color = scrollbar_color_value.thumb_color()->to_color(layout_node); auto thumb_color = scrollbar_color_value.thumb_color()->to_color(layout_node, {});
auto track_color = scrollbar_color_value.track_color()->to_color(layout_node); auto track_color = scrollbar_color_value.track_color()->to_color(layout_node, {});
return { thumb_color, track_color }; return { thumb_color, track_color };
} }

View file

@ -669,7 +669,7 @@ RefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element& element, Calcul
if (!interpolated_offset_x || !interpolated_offset_y || !interpolated_blur_radius || !interpolated_spread_distance) if (!interpolated_offset_x || !interpolated_offset_y || !interpolated_blur_radius || !interpolated_spread_distance)
return {}; return {};
auto result_shadow = ShadowStyleValue::create( auto result_shadow = ShadowStyleValue::create(
CSSColorValue::create_from_color(interpolate_color(from_shadow.color()->to_color({}), to_shadow.color()->to_color({}), delta), ColorSyntax::Modern), CSSColorValue::create_from_color(interpolate_color(from_shadow.color()->to_color({}, {}), to_shadow.color()->to_color({}, {}), delta), ColorSyntax::Modern),
*interpolated_offset_x, *interpolated_offset_x,
*interpolated_offset_y, *interpolated_offset_y,
*interpolated_blur_radius, *interpolated_blur_radius,
@ -783,7 +783,7 @@ static RefPtr<CSSStyleValue const> interpolate_value_impl(DOM::Element& element,
Optional<Layout::NodeWithStyle const&> layout_node; Optional<Layout::NodeWithStyle const&> layout_node;
if (auto node = element.layout_node()) if (auto node = element.layout_node())
layout_node = *node; layout_node = *node;
return CSSColorValue::create_from_color(interpolate_color(from.to_color(layout_node), to.to_color(layout_node), delta), ColorSyntax::Modern); return CSSColorValue::create_from_color(interpolate_color(from.to_color(layout_node, {}), to.to_color(layout_node, {}), delta), ColorSyntax::Modern);
} }
case CSSStyleValue::Type::Edge: { case CSSStyleValue::Type::Edge: {
auto resolved_from = from.as_edge().resolved_value(calculation_context); auto resolved_from = from.as_edge().resolved_value(calculation_context);

View file

@ -5113,7 +5113,7 @@ RefPtr<CSSStyleValue const> Parser::parse_filter_value_list_value(TokenStream<Co
} }
Optional<Color> color = {}; Optional<Color> color = {};
if (maybe_color) if (maybe_color)
color = maybe_color->to_color({}); color = maybe_color->to_color({}, {});
return if_no_more_tokens_return(FilterOperation::DropShadow { x_offset.value(), y_offset.value(), maybe_radius, color }); return if_no_more_tokens_return(FilterOperation::DropShadow { x_offset.value(), y_offset.value(), maybe_radius, color });
} else if (filter_token == FilterToken::HueRotate) { } else if (filter_token == FilterToken::HueRotate) {

View file

@ -30,7 +30,7 @@ ValueComparingNonnullRefPtr<CSSColorValue const> CSSColorValue::create_from_colo
name); name);
} }
Optional<double> CSSColorValue::resolve_hue(CSSStyleValue const& style_value) Optional<double> CSSColorValue::resolve_hue(CSSStyleValue const& style_value, CalculationResolutionContext const& resolution_context)
{ {
// <number> | <angle> | none // <number> | <angle> | none
auto normalized = [](double number) { auto normalized = [](double number) {
@ -44,7 +44,7 @@ Optional<double> CSSColorValue::resolve_hue(CSSStyleValue const& style_value)
return normalized(style_value.as_angle().angle().to_degrees()); return normalized(style_value.as_angle().angle().to_degrees());
if (style_value.is_calculated() && style_value.as_calculated().resolves_to_angle()) if (style_value.is_calculated() && style_value.as_calculated().resolves_to_angle())
return normalized(style_value.as_calculated().resolve_angle({}).value().to_degrees()); return normalized(style_value.as_calculated().resolve_angle(resolution_context).value().to_degrees());
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
return 0; return 0;
@ -52,7 +52,7 @@ Optional<double> CSSColorValue::resolve_hue(CSSStyleValue const& style_value)
return {}; return {};
} }
Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const& style_value, float one_hundred_percent_value) Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const& style_value, float one_hundred_percent_value, CalculationResolutionContext const& resolution_context)
{ {
// <percentage> | <number> | none // <percentage> | <number> | none
auto normalize_percentage = [one_hundred_percent_value](Percentage const& percentage) { auto normalize_percentage = [one_hundred_percent_value](Percentage const& percentage) {
@ -67,11 +67,10 @@ Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const
if (style_value.is_calculated()) { if (style_value.is_calculated()) {
auto const& calculated = style_value.as_calculated(); auto const& calculated = style_value.as_calculated();
CalculationResolutionContext context {};
if (calculated.resolves_to_number()) if (calculated.resolves_to_number())
return calculated.resolve_number(context).value(); return calculated.resolve_number(resolution_context).value();
if (calculated.resolves_to_percentage()) if (calculated.resolves_to_percentage())
return normalize_percentage(calculated.resolve_percentage(context).value()); return normalize_percentage(calculated.resolve_percentage(resolution_context).value());
} }
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
@ -80,7 +79,7 @@ Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const
return {}; return {};
} }
Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value) Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value, CalculationResolutionContext const& resolution_context)
{ {
// <number> | <percentage> | none // <number> | <percentage> | none
auto normalized = [](double number) { auto normalized = [](double number) {
@ -97,11 +96,10 @@ Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value)
if (style_value.is_calculated()) { if (style_value.is_calculated()) {
auto const& calculated = style_value.as_calculated(); auto const& calculated = style_value.as_calculated();
CalculationResolutionContext context {};
if (calculated.resolves_to_number()) if (calculated.resolves_to_number())
return normalized(calculated.resolve_number(context).value()); return normalized(calculated.resolve_number(resolution_context).value());
if (calculated.resolves_to_percentage()) if (calculated.resolves_to_percentage())
return normalized(calculated.resolve_percentage(context).value().as_fraction()); return normalized(calculated.resolve_percentage(resolution_context).value().as_fraction());
} }
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
@ -120,7 +118,7 @@ void CSSColorValue::serialize_color_component(StringBuilder& builder, Serializat
builder.append(component.to_string(mode)); builder.append(component.to_string(mode));
return; return;
} }
auto resolved_value = resolve_with_reference_value(component, one_hundred_percent_value).value_or(0); auto resolved_value = resolve_with_reference_value(component, one_hundred_percent_value, {}).value_or(0);
if (clamp_min.has_value() && resolved_value < *clamp_min) if (clamp_min.has_value() && resolved_value < *clamp_min)
resolved_value = *clamp_min; resolved_value = *clamp_min;
if (clamp_max.has_value() && resolved_value > *clamp_max) if (clamp_max.has_value() && resolved_value > *clamp_max)
@ -143,7 +141,7 @@ void CSSColorValue::serialize_alpha_component(StringBuilder& builder, Serializat
builder.append(component.to_string(mode)); builder.append(component.to_string(mode));
return; return;
} }
auto resolved_value = resolve_alpha(component).value_or(0); auto resolved_value = resolve_alpha(component, {}).value_or(0);
builder.appendff("{}", resolved_value); builder.appendff("{}", resolved_value);
} }
@ -157,7 +155,7 @@ void CSSColorValue::serialize_hue_component(StringBuilder& builder, Serializatio
builder.append(component.to_string(mode)); builder.append(component.to_string(mode));
return; return;
} }
builder.appendff("{:.4}", resolve_hue(component).value_or(0)); builder.appendff("{:.4}", resolve_hue(component, {}).value_or(0));
} }
} }

View file

@ -58,9 +58,9 @@ protected:
{ {
} }
static Optional<double> resolve_hue(CSSStyleValue const&); static Optional<double> resolve_hue(CSSStyleValue const&, CalculationResolutionContext const&);
static Optional<double> resolve_with_reference_value(CSSStyleValue const&, float one_hundred_percent_value); static Optional<double> resolve_with_reference_value(CSSStyleValue const&, float one_hundred_percent_value, CalculationResolutionContext const&);
static Optional<double> resolve_alpha(CSSStyleValue const&); static Optional<double> resolve_alpha(CSSStyleValue const&, CalculationResolutionContext const&);
void serialize_color_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component, float one_hundred_percent_value, Optional<double> clamp_min = {}, Optional<double> clamp_max = {}) const; void serialize_color_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component, float one_hundred_percent_value, Optional<double> clamp_min = {}, Optional<double> clamp_max = {}) const;
void serialize_alpha_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component) const; void serialize_alpha_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component) const;

View file

@ -7,15 +7,16 @@
#include "CSSHSL.h" #include "CSSHSL.h"
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <LibWeb/CSS/Serialize.h> #include <LibWeb/CSS/Serialize.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
namespace Web::CSS { namespace Web::CSS {
Color CSSHSL::to_color(Optional<Layout::NodeWithStyle const&>) const Color CSSHSL::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto const h_val = resolve_hue(m_properties.h).value_or(0); auto const h_val = resolve_hue(m_properties.h, resolution_context).value_or(0);
auto const s_val = resolve_with_reference_value(m_properties.s, 100.0).value_or(0); auto const s_val = resolve_with_reference_value(m_properties.s, 100.0, resolution_context).value_or(0);
auto const l_val = resolve_with_reference_value(m_properties.l, 100.0).value_or(0); auto const l_val = resolve_with_reference_value(m_properties.l, 100.0, resolution_context).value_or(0);
auto const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
return Color::from_hsla(h_val, s_val / 100.0f, l_val / 100.0f, alpha_val); return Color::from_hsla(h_val, s_val / 100.0f, l_val / 100.0f, alpha_val);
} }
@ -35,7 +36,7 @@ bool CSSHSL::equals(CSSStyleValue const& other) const
String CSSHSL::to_string(SerializationMode) const String CSSHSL::to_string(SerializationMode) const
{ {
// FIXME: Do this properly, taking unresolved calculated values into account. // FIXME: Do this properly, taking unresolved calculated values into account.
return serialize_a_srgb_value(to_color({})); return serialize_a_srgb_value(to_color({}, {}));
} }
} }

View file

@ -29,7 +29,7 @@ public:
CSSStyleValue const& l() const { return *m_properties.l; } CSSStyleValue const& l() const { return *m_properties.l; }
CSSStyleValue const& alpha() const { return *m_properties.alpha; } CSSStyleValue const& alpha() const { return *m_properties.alpha; }
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;

View file

@ -7,15 +7,16 @@
#include "CSSHWB.h" #include "CSSHWB.h"
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <LibWeb/CSS/Serialize.h> #include <LibWeb/CSS/Serialize.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
namespace Web::CSS { namespace Web::CSS {
Color CSSHWB::to_color(Optional<Layout::NodeWithStyle const&>) const Color CSSHWB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto const h_val = resolve_hue(m_properties.h).value_or(0); auto const h_val = resolve_hue(m_properties.h, resolution_context).value_or(0);
auto const w_val = clamp(resolve_with_reference_value(m_properties.w, 100.0).value_or(0), 0, 100) / 100.0f; auto const w_val = clamp(resolve_with_reference_value(m_properties.w, 100.0, resolution_context).value_or(0), 0, 100) / 100.0f;
auto const b_val = clamp(resolve_with_reference_value(m_properties.b, 100.0).value_or(0), 0, 100) / 100.0f; auto const b_val = clamp(resolve_with_reference_value(m_properties.b, 100.0, resolution_context).value_or(0), 0, 100) / 100.0f;
auto const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
if (w_val + b_val >= 1.0f) { if (w_val + b_val >= 1.0f) {
auto to_byte = [](float value) { auto to_byte = [](float value) {
@ -45,7 +46,7 @@ bool CSSHWB::equals(CSSStyleValue const& other) const
String CSSHWB::to_string(SerializationMode) const String CSSHWB::to_string(SerializationMode) const
{ {
// FIXME: Do this properly, taking unresolved calculated values into account. // FIXME: Do this properly, taking unresolved calculated values into account.
return serialize_a_srgb_value(to_color({})); return serialize_a_srgb_value(to_color({}, {}));
} }
} }

View file

@ -29,7 +29,7 @@ public:
CSSStyleValue const& b() const { return *m_properties.b; } CSSStyleValue const& b() const { return *m_properties.b; }
CSSStyleValue const& alpha() const { return *m_properties.alpha; } CSSStyleValue const& alpha() const { return *m_properties.alpha; }
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;

View file

@ -135,7 +135,7 @@ bool CSSKeywordValue::has_color() const
return is_color(keyword()); return is_color(keyword());
} }
Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node) const Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const
{ {
if (keyword() == Keyword::Currentcolor) { if (keyword() == Keyword::Currentcolor) {
if (!node.has_value() || !node->has_style()) if (!node.has_value() || !node->has_style())

View file

@ -51,7 +51,7 @@ public:
static bool is_color(Keyword); static bool is_color(Keyword);
virtual bool has_color() const override; virtual bool has_color() const override;
virtual Color to_color(Optional<Layout::NodeWithStyle const&> node) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
bool properties_equal(CSSKeywordValue const& other) const { return m_keyword == other.m_keyword; } bool properties_equal(CSSKeywordValue const& other) const { return m_keyword == other.m_keyword; }

View file

@ -27,12 +27,12 @@ bool CSSLCHLike::equals(CSSStyleValue const& other) const
return m_properties == other_oklch_like.m_properties; return m_properties == other_oklch_like.m_properties;
} }
Color CSSLCH::to_color(Optional<Layout::NodeWithStyle const&>) const Color CSSLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 100).value_or(0), 0, 100); auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 100, resolution_context).value_or(0), 0, 100);
auto const c_val = resolve_with_reference_value(m_properties.c, 150).value_or(0); auto const c_val = resolve_with_reference_value(m_properties.c, 150, resolution_context).value_or(0);
auto const h_val = AK::to_radians(resolve_hue(m_properties.h).value_or(0)); auto const h_val = AK::to_radians(resolve_hue(m_properties.h, resolution_context).value_or(0));
auto const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
return Color::from_lab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val); return Color::from_lab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val);
} }
@ -57,12 +57,12 @@ String CSSLCH::to_string(SerializationMode mode) const
return MUST(builder.to_string()); return MUST(builder.to_string());
} }
Color CSSOKLCH::to_color(Optional<Layout::NodeWithStyle const&>) const Color CSSOKLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 1.0).value_or(0), 0, 1); auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 1.0, resolution_context).value_or(0), 0, 1);
auto const c_val = max(resolve_with_reference_value(m_properties.c, 0.4).value_or(0), 0); auto const c_val = max(resolve_with_reference_value(m_properties.c, 0.4, resolution_context).value_or(0), 0);
auto const h_val = AK::to_radians(resolve_hue(m_properties.h).value_or(0)); auto const h_val = AK::to_radians(resolve_hue(m_properties.h, resolution_context).value_or(0));
auto const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
return Color::from_oklab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val); return Color::from_oklab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val);
} }

View file

@ -56,7 +56,7 @@ public:
} }
virtual ~CSSLCH() override = default; virtual ~CSSLCH() override = default;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
}; };
@ -70,7 +70,7 @@ public:
} }
virtual ~CSSOKLCH() override = default; virtual ~CSSOKLCH() override = default;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
}; };

View file

@ -26,12 +26,12 @@ bool CSSLabLike::equals(CSSStyleValue const& other) const
return m_properties == other_lab_like.m_properties; return m_properties == other_lab_like.m_properties;
} }
Color CSSOKLab::to_color(Optional<Layout::NodeWithStyle const&>) const Color CSSOKLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 1.0).value_or(0), 0, 1); auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 1.0, resolution_context).value_or(0), 0, 1);
auto const a_val = resolve_with_reference_value(m_properties.a, 0.4).value_or(0); auto const a_val = resolve_with_reference_value(m_properties.a, 0.4, resolution_context).value_or(0);
auto const b_val = resolve_with_reference_value(m_properties.b, 0.4).value_or(0); auto const b_val = resolve_with_reference_value(m_properties.b, 0.4, resolution_context).value_or(0);
auto const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
return Color::from_oklab(l_val, a_val, b_val, alpha_val); return Color::from_oklab(l_val, a_val, b_val, alpha_val);
} }
@ -56,12 +56,12 @@ String CSSOKLab::to_string(SerializationMode mode) const
return MUST(builder.to_string()); return MUST(builder.to_string());
} }
Color CSSLab::to_color(Optional<Layout::NodeWithStyle const&>) const Color CSSLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 100).value_or(0), 0, 100); auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 100, resolution_context).value_or(0), 0, 100);
auto const a_val = resolve_with_reference_value(m_properties.a, 125).value_or(0); auto const a_val = resolve_with_reference_value(m_properties.a, 125, resolution_context).value_or(0);
auto const b_val = resolve_with_reference_value(m_properties.b, 125).value_or(0); auto const b_val = resolve_with_reference_value(m_properties.b, 125, resolution_context).value_or(0);
auto const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
return Color::from_lab(l_val, a_val, b_val, alpha_val); return Color::from_lab(l_val, a_val, b_val, alpha_val);
} }

View file

@ -51,7 +51,7 @@ protected:
// https://drafts.css-houdini.org/css-typed-om-1/#cssoklab // https://drafts.css-houdini.org/css-typed-om-1/#cssoklab
class CSSOKLab final : public CSSLabLike { class CSSOKLab final : public CSSLabLike {
public: public:
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
CSSOKLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha) CSSOKLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha)
@ -63,7 +63,7 @@ public:
// https://drafts.css-houdini.org/css-typed-om-1/#csslab // https://drafts.css-houdini.org/css-typed-om-1/#csslab
class CSSLab final : public CSSLabLike { class CSSLab final : public CSSLabLike {
public: public:
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
CSSLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha) CSSLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha)

View file

@ -9,12 +9,12 @@
namespace Web::CSS { namespace Web::CSS {
Color CSSLightDark::to_color(Optional<Layout::NodeWithStyle const&> node) const Color CSSLightDark::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
{ {
if (node.has_value() && node.value().computed_values().color_scheme() == PreferredColorScheme::Dark) if (node.has_value() && node.value().computed_values().color_scheme() == PreferredColorScheme::Dark)
return m_properties.dark->to_color(node); return m_properties.dark->to_color(node, resolution_context);
return m_properties.light->to_color(node); return m_properties.light->to_color(node, resolution_context);
} }
bool CSSLightDark::equals(CSSStyleValue const& other) const bool CSSLightDark::equals(CSSStyleValue const& other) const

View file

@ -21,7 +21,7 @@ public:
} }
virtual bool equals(CSSStyleValue const&) const override; virtual bool equals(CSSStyleValue const&) const override;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
private: private:

View file

@ -14,9 +14,9 @@
namespace Web::CSS { namespace Web::CSS {
Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>) const Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto resolve_rgb_to_u8 = [](CSSStyleValue const& style_value) -> Optional<u8> { auto resolve_rgb_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
// <number> | <percentage> | none // <number> | <percentage> | none
auto normalized = [](double number) { auto normalized = [](double number) {
if (isnan(number)) if (isnan(number))
@ -32,11 +32,10 @@ Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>) const
if (style_value.is_calculated()) { if (style_value.is_calculated()) {
auto const& calculated = style_value.as_calculated(); auto const& calculated = style_value.as_calculated();
CalculationResolutionContext context {};
if (calculated.resolves_to_number()) if (calculated.resolves_to_number())
return normalized(calculated.resolve_number(context).value()); return normalized(calculated.resolve_number(resolution_context).value());
if (calculated.resolves_to_percentage()) if (calculated.resolves_to_percentage())
return normalized(calculated.resolve_percentage(context).value().value() * 255 / 100); return normalized(calculated.resolve_percentage(resolution_context).value().value() * 255 / 100);
} }
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None) if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
@ -45,8 +44,8 @@ Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>) const
return {}; return {};
}; };
auto resolve_alpha_to_u8 = [](CSSStyleValue const& style_value) -> Optional<u8> { auto resolve_alpha_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
auto alpha_0_1 = resolve_alpha(style_value); auto alpha_0_1 = resolve_alpha(style_value, resolution_context);
if (alpha_0_1.has_value()) if (alpha_0_1.has_value())
return llround(clamp(alpha_0_1.value() * 255.0f, 0.0f, 255.0f)); return llround(clamp(alpha_0_1.value() * 255.0f, 0.0f, 255.0f));
return {}; return {};
@ -77,7 +76,7 @@ String CSSRGB::to_string(SerializationMode mode) const
// FIXME: Do this properly, taking unresolved calculated values into account. // FIXME: Do this properly, taking unresolved calculated values into account.
if (mode != SerializationMode::ResolvedValue && m_properties.name.has_value()) if (mode != SerializationMode::ResolvedValue && m_properties.name.has_value())
return m_properties.name.value().to_string().to_ascii_lowercase(); return m_properties.name.value().to_string().to_ascii_lowercase();
return serialize_a_srgb_value(to_color({})); return serialize_a_srgb_value(to_color({}, {}));
} }
} }

View file

@ -29,7 +29,7 @@ public:
CSSStyleValue const& b() const { return *m_properties.b; } CSSStyleValue const& b() const { return *m_properties.b; }
CSSStyleValue const& alpha() const { return *m_properties.alpha; } CSSStyleValue const& alpha() const { return *m_properties.alpha; }
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;

View file

@ -82,12 +82,12 @@ bool ColorFunctionStyleValue::equals(CSSStyleValue const& other) const
return m_properties == other_lab_like.m_properties; return m_properties == other_lab_like.m_properties;
} }
ColorFunctionStyleValue::Resolved ColorFunctionStyleValue::resolve_properties() const ColorFunctionStyleValue::Resolved ColorFunctionStyleValue::resolve_properties(CalculationResolutionContext const& resolution_context) const
{ {
float const c1 = resolve_with_reference_value(m_properties.channels[0], 1).value_or(0); float const c1 = resolve_with_reference_value(m_properties.channels[0], 1, resolution_context).value_or(0);
float const c2 = resolve_with_reference_value(m_properties.channels[1], 1).value_or(0); float const c2 = resolve_with_reference_value(m_properties.channels[1], 1, resolution_context).value_or(0);
float const c3 = resolve_with_reference_value(m_properties.channels[2], 1).value_or(0); float const c3 = resolve_with_reference_value(m_properties.channels[2], 1, resolution_context).value_or(0);
float const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); float const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
return { .channels = { c1, c2, c3 }, .alpha = alpha_val }; return { .channels = { c1, c2, c3 }, .alpha = alpha_val };
} }
@ -144,9 +144,9 @@ String ColorFunctionStyleValue::to_string(SerializationMode mode) const
convert_percentage(m_properties.channels[2])->to_string(mode))); convert_percentage(m_properties.channels[2])->to_string(mode)));
} }
Color ColorFunctionStyleValue::to_color(Optional<Layout::NodeWithStyle const&>) const Color ColorFunctionStyleValue::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
{ {
auto [channels, alpha_val] = resolve_properties(); auto [channels, alpha_val] = resolve_properties(resolution_context);
auto c1 = channels[0]; auto c1 = channels[0];
auto c2 = channels[1]; auto c2 = channels[1];
auto c3 = channels[2]; auto c3 = channels[2];

View file

@ -18,7 +18,7 @@ public:
static ValueComparingNonnullRefPtr<ColorFunctionStyleValue const> create(StringView color_space, ValueComparingNonnullRefPtr<CSSStyleValue const> c1, ValueComparingNonnullRefPtr<CSSStyleValue const> c2, ValueComparingNonnullRefPtr<CSSStyleValue const> c3, ValueComparingRefPtr<CSSStyleValue const> alpha = {}); static ValueComparingNonnullRefPtr<ColorFunctionStyleValue const> create(StringView color_space, ValueComparingNonnullRefPtr<CSSStyleValue const> c1, ValueComparingNonnullRefPtr<CSSStyleValue const> c2, ValueComparingNonnullRefPtr<CSSStyleValue const> c3, ValueComparingRefPtr<CSSStyleValue const> alpha = {});
virtual bool equals(CSSStyleValue const&) const override; virtual bool equals(CSSStyleValue const&) const override;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
virtual bool is_color_function() const override { return true; } virtual bool is_color_function() const override { return true; }
@ -43,7 +43,7 @@ private:
float alpha {}; float alpha {};
}; };
Resolved resolve_properties() const; Resolved resolve_properties(CalculationResolutionContext const& resolution_context) const;
Properties m_properties; Properties m_properties;
}; };

View file

@ -175,13 +175,13 @@ ColorMixStyleValue::PercentageNormalizationResult ColorMixStyleValue::normalize_
} }
// https://drafts.csswg.org/css-color-5/#color-mix-result // https://drafts.csswg.org/css-color-5/#color-mix-result
Color ColorMixStyleValue::to_color(Optional<Layout::NodeWithStyle const&> node) const Color ColorMixStyleValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
{ {
// FIXME: Do this in a spec-compliant way. // FIXME: Do this in a spec-compliant way.
// Our color interpolation doesn't currently take the color space or hue interpolation method into account. // Our color interpolation doesn't currently take the color space or hue interpolation method into account.
auto normalized_percentages = normalize_percentages(); auto normalized_percentages = normalize_percentages();
auto from_color = m_properties.first_component.color->to_color(node); auto from_color = m_properties.first_component.color->to_color(node, resolution_context);
auto to_color = m_properties.second_component.color->to_color(node); auto to_color = m_properties.second_component.color->to_color(node, resolution_context);
auto delta = normalized_percentages.p2.value() / 100; auto delta = normalized_percentages.p2.value() / 100;
return interpolate_color(from_color, to_color, delta); return interpolate_color(from_color, to_color, delta);
} }

View file

@ -30,7 +30,7 @@ public:
static ValueComparingNonnullRefPtr<ColorMixStyleValue const> create(ColorInterpolationMethod, ColorMixComponent first_component, ColorMixComponent second_component); static ValueComparingNonnullRefPtr<ColorMixStyleValue const> create(ColorInterpolationMethod, ColorMixComponent first_component, ColorMixComponent second_component);
virtual bool equals(CSSStyleValue const&) const override; virtual bool equals(CSSStyleValue const&) const override;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override; virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
private: private:

View file

@ -1690,7 +1690,7 @@ void Document::obtain_theme_color()
if (html_element() && html_element()->layout_node()) if (html_element() && html_element()->layout_node())
root_node = *html_element()->layout_node(); root_node = *html_element()->layout_node();
theme_color = css_value->to_color(root_node); theme_color = css_value->to_color(root_node, {});
return TraversalDecision::Break; return TraversalDecision::Break;
} }
} }

View file

@ -1174,7 +1174,7 @@ Optional<String> effective_command_value(GC::Ptr<DOM::Node> node, FlyString cons
if (!background_color.has_value()) if (!background_color.has_value())
return NumericLimits<u8>::max(); return NumericLimits<u8>::max();
VERIFY(is<Layout::NodeWithStyle>(node->layout_node())); VERIFY(is<Layout::NodeWithStyle>(node->layout_node()));
return background_color.value()->to_color(*static_cast<Layout::NodeWithStyle*>(node->layout_node())).alpha(); return background_color.value()->to_color(*static_cast<Layout::NodeWithStyle*>(node->layout_node()), {}).alpha();
}; };
while (resolved_background_alpha() == 0 && node->parent() && is<DOM::Element>(*node->parent())) while (resolved_background_alpha() == 0 && node->parent() && is<DOM::Element>(*node->parent()))
node = node->parent(); node = node->parent();

View file

@ -318,6 +318,7 @@ enum class MediaFeatureID : u8;
enum class PropertyID : u16; enum class PropertyID : u16;
struct BackgroundLayerData; struct BackgroundLayerData;
struct CalculationResolutionContext;
struct CSSStyleSheetInit; struct CSSStyleSheetInit;
struct GridRepeatParams; struct GridRepeatParams;
struct StyleSheetIdentifier; struct StyleSheetIdentifier;

View file

@ -37,7 +37,7 @@ public:
// https://drafts.csswg.org/css-color/#parse-a-css-color-value // https://drafts.csswg.org/css-color/#parse-a-css-color-value
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), string, CSS::PropertyID::Color); auto style_value = parse_css_value(CSS::Parser::ParsingParams(), string, CSS::PropertyID::Color);
if (style_value && style_value->has_color()) { if (style_value && style_value->has_color()) {
auto parsedValue = style_value->to_color(OptionalNone()); auto parsedValue = style_value->to_color(OptionalNone(), {});
// 4. Set this's fill style to parsedValue. // 4. Set this's fill style to parsedValue.
my_drawing_state().fill_style = parsedValue; my_drawing_state().fill_style = parsedValue;
@ -76,7 +76,7 @@ public:
// https://drafts.csswg.org/css-color/#parse-a-css-color-value // https://drafts.csswg.org/css-color/#parse-a-css-color-value
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), string, CSS::PropertyID::Color); auto style_value = parse_css_value(CSS::Parser::ParsingParams(), string, CSS::PropertyID::Color);
if (style_value && style_value->has_color()) { if (style_value && style_value->has_color()) {
auto parsedValue = style_value->to_color(OptionalNone()); auto parsedValue = style_value->to_color(OptionalNone(), {});
// 4. Set this's stroke style to parsedValue. // 4. Set this's stroke style to parsedValue.
my_drawing_state().stroke_style = parsedValue; my_drawing_state().stroke_style = parsedValue;

View file

@ -941,7 +941,7 @@ void CanvasRenderingContext2D::set_shadow_color(String color)
// 2. Let parsedValue be the result of parsing the given value with context if non-null. // 2. Let parsedValue be the result of parsing the given value with context if non-null.
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color); auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color);
if (style_value && style_value->has_color()) { if (style_value && style_value->has_color()) {
auto parsedValue = style_value->to_color(OptionalNone()); auto parsedValue = style_value->to_color(OptionalNone(), {});
// 4. Set this's shadow color to parsedValue. // 4. Set this's shadow color to parsedValue.
drawing_state().shadow_color = parsedValue; drawing_state().shadow_color = parsedValue;

View file

@ -288,7 +288,7 @@ void OffscreenCanvasRenderingContext2D::set_shadow_color(String color)
// 2. Let parsedValue be the result of parsing the given value with context if non-null. // 2. Let parsedValue be the result of parsing the given value with context if non-null.
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color); auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color);
if (style_value && style_value->has_color()) { if (style_value && style_value->has_color()) {
auto parsedValue = style_value->to_color(OptionalNone()); auto parsedValue = style_value->to_color({}, {});
// 4. Set this's shadow color to parsedValue. // 4. Set this's shadow color to parsedValue.
drawing_state().shadow_color = parsedValue; drawing_state().shadow_color = parsedValue;

View file

@ -796,7 +796,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
do_border_style(computed_values.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle); do_border_style(computed_values.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle);
if (auto const& outline_color = computed_style.property(CSS::PropertyID::OutlineColor); outline_color.has_color()) if (auto const& outline_color = computed_style.property(CSS::PropertyID::OutlineColor); outline_color.has_color())
computed_values.set_outline_color(outline_color.to_color(*this)); computed_values.set_outline_color(outline_color.to_color(*this, {}));
if (auto const& outline_offset = computed_style.property(CSS::PropertyID::OutlineOffset); outline_offset.is_length()) if (auto const& outline_offset = computed_style.property(CSS::PropertyID::OutlineOffset); outline_offset.is_length())
computed_values.set_outline_offset(outline_offset.as_length().length()); computed_values.set_outline_offset(outline_offset.as_length().length());
computed_values.set_outline_style(computed_style.outline_style()); computed_values.set_outline_style(computed_style.outline_style());
@ -836,16 +836,16 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
auto const& fill = computed_style.property(CSS::PropertyID::Fill); auto const& fill = computed_style.property(CSS::PropertyID::Fill);
if (fill.has_color()) if (fill.has_color())
computed_values.set_fill(fill.to_color(*this)); computed_values.set_fill(fill.to_color(*this, {}));
else if (fill.is_url()) else if (fill.is_url())
computed_values.set_fill(fill.as_url().url()); computed_values.set_fill(fill.as_url().url());
auto const& stroke = computed_style.property(CSS::PropertyID::Stroke); auto const& stroke = computed_style.property(CSS::PropertyID::Stroke);
if (stroke.has_color()) if (stroke.has_color())
computed_values.set_stroke(stroke.to_color(*this)); computed_values.set_stroke(stroke.to_color(*this, {}));
else if (stroke.is_url()) else if (stroke.is_url())
computed_values.set_stroke(stroke.as_url().url()); computed_values.set_stroke(stroke.as_url().url());
if (auto const& stop_color = computed_style.property(CSS::PropertyID::StopColor); stop_color.has_color()) if (auto const& stop_color = computed_style.property(CSS::PropertyID::StopColor); stop_color.has_color())
computed_values.set_stop_color(stop_color.to_color(*this)); computed_values.set_stop_color(stop_color.to_color(*this, {}));
auto const& stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth); auto const& stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth);
// FIXME: Converting to pixels isn't really correct - values should be in "user units" // FIXME: Converting to pixels isn't really correct - values should be in "user units"
// https://svgwg.org/svg2-draft/coords.html#TermUserUnits // https://svgwg.org/svg2-draft/coords.html#TermUserUnits

View file

@ -586,7 +586,7 @@ Optional<BordersData> borders_data_for_outline(Layout::Node const& layout_node,
if (outline_style == CSS::OutlineStyle::Auto) { if (outline_style == CSS::OutlineStyle::Auto) {
// `auto` lets us do whatever we want for the outline. 2px of the accent colour seems reasonable. // `auto` lets us do whatever we want for the outline. 2px of the accent colour seems reasonable.
line_style = CSS::LineStyle::Solid; line_style = CSS::LineStyle::Solid;
outline_color = CSS::CSSKeywordValue::create(CSS::Keyword::Accentcolor)->to_color(*static_cast<Layout::NodeWithStyle const*>(&layout_node)); outline_color = CSS::CSSKeywordValue::create(CSS::Keyword::Accentcolor)->to_color(*static_cast<Layout::NodeWithStyle const*>(&layout_node), {});
outline_width = 2; outline_width = 2;
} else { } else {
line_style = CSS::keyword_to_line_style(CSS::to_keyword(outline_style)).value_or(CSS::LineStyle::None); line_style = CSS::keyword_to_line_style(CSS::to_keyword(outline_style)).value_or(CSS::LineStyle::None);

View file

@ -30,7 +30,7 @@ static ColorStopData resolve_color_stop_positions(Layout::NodeWithStyle const& n
resolved_color_stops.ensure_capacity(expanded_size); resolved_color_stops.ensure_capacity(expanded_size);
for (auto& stop : color_stop_list) { for (auto& stop : color_stop_list) {
auto resolved_stop = Gfx::ColorStop { .color = stop.color_stop.color->to_color(node) }; auto resolved_stop = Gfx::ColorStop { .color = stop.color_stop.color->to_color(node, {}) };
for (int i = 0; i < color_stop_length(stop); i++) for (int i = 0; i < color_stop_length(stop); i++)
resolved_color_stops.append(resolved_stop); resolved_color_stops.append(resolved_stop);
} }