mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-11 02:29:21 +00:00
LibWeb: Don't resolve colors with unresolved components
`CSSColorValue`s which have unresolved `calc` components should be able to be resolved. Previously we would always resolve them but with incorrect values. This is useful as we will now be able to now whether we should serialize colors in their normalized form or not. Slight regression in that we now serialize (RGB, HSL and HWB) colors with components that rely on compute-time information as an empty string, but that will be fixed in the next commit.
This commit is contained in:
parent
e66332c07a
commit
6a9c8d7767
Notes:
github-actions[bot]
2025-07-16 12:06:47 +00:00
Author: https://github.com/Calme1709
Commit: 6a9c8d7767
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5305
Reviewed-by: https://github.com/AtkinsSJ ✅
30 changed files with 278 additions and 129 deletions
|
@ -52,16 +52,26 @@ Optional<double> CSSColorValue::resolve_hue(CSSStyleValue const& style_value, Ca
|
|||
return normalized(style_value.as_angle().angle().to_degrees());
|
||||
|
||||
if (style_value.is_calculated()) {
|
||||
if (style_value.as_calculated().resolves_to_number())
|
||||
return normalized(style_value.as_calculated().resolve_number_deprecated(resolution_context).value());
|
||||
if (style_value.as_calculated().resolves_to_number()) {
|
||||
auto maybe_number = style_value.as_calculated().resolve_number(resolution_context);
|
||||
|
||||
if (style_value.as_calculated().resolves_to_angle())
|
||||
return normalized(style_value.as_calculated().resolve_angle_deprecated(resolution_context).value().to_degrees());
|
||||
if (!maybe_number.has_value())
|
||||
return {};
|
||||
|
||||
return normalized(maybe_number.value());
|
||||
}
|
||||
|
||||
if (style_value.as_calculated().resolves_to_angle()) {
|
||||
auto maybe_angle = style_value.as_calculated().resolve_angle(resolution_context);
|
||||
|
||||
if (!maybe_angle.has_value())
|
||||
return {};
|
||||
|
||||
return normalized(maybe_angle.value().to_degrees());
|
||||
}
|
||||
}
|
||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
||||
return 0;
|
||||
|
||||
return {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const& style_value, float one_hundred_percent_value, CalculationResolutionContext const& resolution_context)
|
||||
|
@ -79,16 +89,26 @@ Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const
|
|||
|
||||
if (style_value.is_calculated()) {
|
||||
auto const& calculated = style_value.as_calculated();
|
||||
if (calculated.resolves_to_number())
|
||||
return calculated.resolve_number_deprecated(resolution_context).value();
|
||||
if (calculated.resolves_to_percentage())
|
||||
return normalize_percentage(calculated.resolve_percentage_deprecated(resolution_context).value());
|
||||
if (calculated.resolves_to_number()) {
|
||||
auto maybe_number = calculated.resolve_number(resolution_context);
|
||||
|
||||
if (!maybe_number.has_value())
|
||||
return {};
|
||||
|
||||
return maybe_number.value();
|
||||
}
|
||||
|
||||
if (calculated.resolves_to_percentage()) {
|
||||
auto percentage = calculated.resolve_percentage(resolution_context);
|
||||
|
||||
if (!percentage.has_value())
|
||||
return {};
|
||||
|
||||
return normalize_percentage(percentage.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
||||
return 0;
|
||||
|
||||
return {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value, CalculationResolutionContext const& resolution_context)
|
||||
|
@ -108,16 +128,29 @@ Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value,
|
|||
|
||||
if (style_value.is_calculated()) {
|
||||
auto const& calculated = style_value.as_calculated();
|
||||
if (calculated.resolves_to_number())
|
||||
return normalized(calculated.resolve_number_deprecated(resolution_context).value());
|
||||
if (calculated.resolves_to_percentage())
|
||||
return normalized(calculated.resolve_percentage_deprecated(resolution_context).value().as_fraction());
|
||||
if (calculated.resolves_to_number()) {
|
||||
auto maybe_number = calculated.resolve_number(resolution_context);
|
||||
|
||||
if (!maybe_number.has_value())
|
||||
return {};
|
||||
|
||||
return normalized(maybe_number.value());
|
||||
}
|
||||
|
||||
if (calculated.resolves_to_percentage()) {
|
||||
auto percentage = calculated.resolve_percentage(resolution_context);
|
||||
|
||||
if (!percentage.has_value())
|
||||
return {};
|
||||
|
||||
return normalized(percentage.value().as_fraction());
|
||||
}
|
||||
}
|
||||
|
||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
||||
return 0;
|
||||
|
||||
return {};
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSSColorValue::serialize_color_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component, float one_hundred_percent_value, Optional<double> clamp_min, Optional<double> clamp_max) const
|
||||
|
@ -130,7 +163,16 @@ void CSSColorValue::serialize_color_component(StringBuilder& builder, Serializat
|
|||
builder.append(component.to_string(mode));
|
||||
return;
|
||||
}
|
||||
auto resolved_value = resolve_with_reference_value(component, one_hundred_percent_value, {}).value_or(0);
|
||||
|
||||
auto maybe_resolved_value = resolve_with_reference_value(component, one_hundred_percent_value, {});
|
||||
|
||||
if (!maybe_resolved_value.has_value()) {
|
||||
builder.append(component.to_string(mode));
|
||||
return;
|
||||
}
|
||||
|
||||
auto resolved_value = maybe_resolved_value.value();
|
||||
|
||||
if (clamp_min.has_value() && resolved_value < *clamp_min)
|
||||
resolved_value = *clamp_min;
|
||||
if (clamp_max.has_value() && resolved_value > *clamp_max)
|
||||
|
@ -153,8 +195,15 @@ void CSSColorValue::serialize_alpha_component(StringBuilder& builder, Serializat
|
|||
builder.append(component.to_string(mode));
|
||||
return;
|
||||
}
|
||||
auto resolved_value = resolve_alpha(component, {}).value_or(0);
|
||||
builder.appendff("{}", resolved_value);
|
||||
|
||||
auto maybe_resolved_value = resolve_alpha(component, {});
|
||||
|
||||
if (!maybe_resolved_value.has_value()) {
|
||||
builder.append(component.to_string(mode));
|
||||
return;
|
||||
}
|
||||
|
||||
builder.appendff("{}", maybe_resolved_value.value());
|
||||
}
|
||||
|
||||
void CSSColorValue::serialize_hue_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component) const
|
||||
|
@ -167,7 +216,15 @@ void CSSColorValue::serialize_hue_component(StringBuilder& builder, Serializatio
|
|||
builder.append(component.to_string(mode));
|
||||
return;
|
||||
}
|
||||
builder.appendff("{:.4}", resolve_hue(component, {}).value_or(0));
|
||||
|
||||
auto maybe_resolved_value = resolve_hue(component, {});
|
||||
|
||||
if (!maybe_resolved_value.has_value()) {
|
||||
builder.append(component.to_string(mode));
|
||||
return;
|
||||
}
|
||||
|
||||
builder.appendff("{:.4}", maybe_resolved_value.value());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,14 +11,17 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
Color CSSHSL::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<Color> CSSHSL::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
{
|
||||
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, resolution_context).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, resolution_context).value_or(1);
|
||||
auto h_val = resolve_hue(m_properties.h, resolution_context);
|
||||
auto s_val = resolve_with_reference_value(m_properties.s, 100.0, resolution_context);
|
||||
auto l_val = resolve_with_reference_value(m_properties.l, 100.0, resolution_context);
|
||||
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||
|
||||
return Color::from_hsla(h_val, s_val / 100.0f, l_val / 100.0f, alpha_val);
|
||||
if (!h_val.has_value() || !s_val.has_value() || !l_val.has_value() || !alpha_val.has_value())
|
||||
return {};
|
||||
|
||||
return Color::from_hsla(h_val.value(), s_val.value() / 100.0f, l_val.value() / 100.0f, alpha_val.value());
|
||||
}
|
||||
|
||||
bool CSSHSL::equals(CSSStyleValue const& other) const
|
||||
|
@ -35,8 +38,11 @@ bool CSSHSL::equals(CSSStyleValue const& other) const
|
|||
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
||||
String CSSHSL::to_string(SerializationMode) const
|
||||
{
|
||||
if (auto color = to_color({}, {}); color.has_value())
|
||||
return serialize_a_srgb_value(color.value());
|
||||
|
||||
// FIXME: Do this properly, taking unresolved calculated values into account.
|
||||
return serialize_a_srgb_value(to_color({}, {}));
|
||||
return ""_string;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
CSSStyleValue const& l() const { return *m_properties.l; }
|
||||
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
||||
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
|
|
|
@ -11,24 +11,30 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
Color CSSHWB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<Color> CSSHWB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
{
|
||||
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, resolution_context).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, resolution_context).value_or(1);
|
||||
auto h_val = resolve_hue(m_properties.h, resolution_context);
|
||||
auto raw_w_value = resolve_with_reference_value(m_properties.w, 100.0, resolution_context);
|
||||
auto raw_b_value = resolve_with_reference_value(m_properties.b, 100.0, resolution_context);
|
||||
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||
|
||||
if (!h_val.has_value() || !raw_w_value.has_value() || !raw_b_value.has_value() || !alpha_val.has_value())
|
||||
return {};
|
||||
|
||||
auto w_val = clamp(raw_w_value.value(), 0, 100) / 100.0f;
|
||||
auto b_val = clamp(raw_b_value.value(), 0, 100) / 100.0f;
|
||||
|
||||
if (w_val + b_val >= 1.0f) {
|
||||
auto to_byte = [](float value) {
|
||||
return round_to<u8>(clamp(value * 255.0f, 0.0f, 255.0f));
|
||||
};
|
||||
u8 gray = to_byte(w_val / (w_val + b_val));
|
||||
return Color(gray, gray, gray, to_byte(alpha_val));
|
||||
return Color(gray, gray, gray, to_byte(alpha_val.value()));
|
||||
}
|
||||
|
||||
auto value = 1 - b_val;
|
||||
auto saturation = 1 - (w_val / value);
|
||||
return Color::from_hsv(h_val, saturation, value).with_opacity(alpha_val);
|
||||
return Color::from_hsv(h_val.value(), saturation, value).with_opacity(alpha_val.value());
|
||||
}
|
||||
|
||||
bool CSSHWB::equals(CSSStyleValue const& other) const
|
||||
|
@ -45,8 +51,11 @@ bool CSSHWB::equals(CSSStyleValue const& other) const
|
|||
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
||||
String CSSHWB::to_string(SerializationMode) const
|
||||
{
|
||||
if (auto color = to_color({}, {}); color.has_value())
|
||||
return serialize_a_srgb_value(color.value());
|
||||
|
||||
// FIXME: Do this properly, taking unresolved calculated values into account.
|
||||
return serialize_a_srgb_value(to_color({}, {}));
|
||||
return ""_string;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
CSSStyleValue const& b() const { return *m_properties.b; }
|
||||
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
||||
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ bool CSSKeywordValue::has_color() const
|
|||
return is_color(keyword());
|
||||
}
|
||||
|
||||
Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const
|
||||
Optional<Color> CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const
|
||||
{
|
||||
if (keyword() == Keyword::Currentcolor) {
|
||||
if (!node.has_value() || !node->has_style())
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
|
||||
static bool is_color(Keyword);
|
||||
virtual bool has_color() const override;
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const override;
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
bool properties_equal(CSSKeywordValue const& other) const { return m_keyword == other.m_keyword; }
|
||||
|
|
|
@ -27,14 +27,20 @@ bool CSSLCHLike::equals(CSSStyleValue const& other) const
|
|||
return m_properties == other_oklch_like.m_properties;
|
||||
}
|
||||
|
||||
Color CSSLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<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, resolution_context).value_or(0), 0, 100);
|
||||
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, resolution_context).value_or(0));
|
||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
||||
auto raw_l_val = resolve_with_reference_value(m_properties.l, 100, resolution_context);
|
||||
auto c_val = resolve_with_reference_value(m_properties.c, 150, resolution_context);
|
||||
auto raw_h_val = resolve_hue(m_properties.h, resolution_context);
|
||||
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||
|
||||
return Color::from_lab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val);
|
||||
if (!raw_l_val.has_value() || !c_val.has_value() || !raw_h_val.has_value() || !alpha_val.has_value())
|
||||
return {};
|
||||
|
||||
auto l_val = clamp(raw_l_val.value(), 0, 100);
|
||||
auto h_val = AK::to_radians(raw_h_val.value());
|
||||
|
||||
return Color::from_lab(l_val, c_val.value() * cos(h_val), c_val.value() * sin(h_val), alpha_val.value());
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#serializing-lab-lch
|
||||
|
@ -57,14 +63,21 @@ String CSSLCH::to_string(SerializationMode mode) const
|
|||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
Color CSSOKLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<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, resolution_context).value_or(0), 0, 1);
|
||||
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, resolution_context).value_or(0));
|
||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
||||
auto raw_l_val = resolve_with_reference_value(m_properties.l, 1.0, resolution_context);
|
||||
auto raw_c_val = resolve_with_reference_value(m_properties.c, 0.4, resolution_context);
|
||||
auto raw_h_val = resolve_hue(m_properties.h, resolution_context);
|
||||
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||
|
||||
return Color::from_oklab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val);
|
||||
if (!raw_l_val.has_value() || !raw_c_val.has_value() || !raw_h_val.has_value() || !alpha_val.has_value())
|
||||
return {};
|
||||
|
||||
auto l_val = clamp(raw_l_val.value(), 0, 1);
|
||||
auto c_val = max(raw_c_val.value(), 0);
|
||||
auto h_val = AK::to_radians(raw_h_val.value());
|
||||
|
||||
return Color::from_oklab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val.value());
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#serializing-oklab-oklch
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
}
|
||||
virtual ~CSSLCH() override = default;
|
||||
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
};
|
||||
|
@ -70,7 +70,7 @@ public:
|
|||
}
|
||||
virtual ~CSSOKLCH() override = default;
|
||||
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
};
|
||||
|
|
|
@ -26,14 +26,17 @@ bool CSSLabLike::equals(CSSStyleValue const& other) const
|
|||
return m_properties == other_lab_like.m_properties;
|
||||
}
|
||||
|
||||
Color CSSOKLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<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, resolution_context).value_or(0), 0, 1);
|
||||
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, resolution_context).value_or(0);
|
||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
||||
auto const l_val = resolve_with_reference_value(m_properties.l, 1.0, resolution_context);
|
||||
auto const a_val = resolve_with_reference_value(m_properties.a, 0.4, resolution_context);
|
||||
auto const b_val = resolve_with_reference_value(m_properties.b, 0.4, resolution_context);
|
||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||
|
||||
return Color::from_oklab(l_val, a_val, b_val, alpha_val);
|
||||
if (!l_val.has_value() || !a_val.has_value() || !b_val.has_value() || !alpha_val.has_value())
|
||||
return {};
|
||||
|
||||
return Color::from_oklab(clamp(l_val.value(), 0, 1), a_val.value(), b_val.value(), alpha_val.value());
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#serializing-oklab-oklch
|
||||
|
@ -56,14 +59,17 @@ String CSSOKLab::to_string(SerializationMode mode) const
|
|||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
Color CSSLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<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, resolution_context).value_or(0), 0, 100);
|
||||
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, resolution_context).value_or(0);
|
||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
||||
auto l_val = resolve_with_reference_value(m_properties.l, 100, resolution_context);
|
||||
auto a_val = resolve_with_reference_value(m_properties.a, 125, resolution_context);
|
||||
auto b_val = resolve_with_reference_value(m_properties.b, 125, resolution_context);
|
||||
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||
|
||||
return Color::from_lab(l_val, a_val, b_val, alpha_val);
|
||||
if (!l_val.has_value() || !a_val.has_value() || !b_val.has_value() || !alpha_val.has_value())
|
||||
return {};
|
||||
|
||||
return Color::from_lab(clamp(l_val.value(), 0, 100), a_val.value(), b_val.value(), alpha_val.value());
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#serializing-lab-lch
|
||||
|
|
|
@ -51,7 +51,7 @@ protected:
|
|||
// https://drafts.css-houdini.org/css-typed-om-1/#cssoklab
|
||||
class CSSOKLab final : public CSSLabLike {
|
||||
public:
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) 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)
|
||||
|
@ -63,7 +63,7 @@ public:
|
|||
// https://drafts.css-houdini.org/css-typed-om-1/#csslab
|
||||
class CSSLab final : public CSSLabLike {
|
||||
public:
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) 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)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
Color CSSLightDark::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<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)
|
||||
return m_properties.dark->to_color(node, resolution_context);
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
}
|
||||
|
||||
virtual bool equals(CSSStyleValue const&) const override;
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<Color> CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
{
|
||||
auto resolve_rgb_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
|
||||
// <number> | <percentage> | none
|
||||
|
@ -32,16 +32,26 @@ Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolu
|
|||
|
||||
if (style_value.is_calculated()) {
|
||||
auto const& calculated = style_value.as_calculated();
|
||||
if (calculated.resolves_to_number())
|
||||
return normalized(calculated.resolve_number_deprecated(resolution_context).value());
|
||||
if (calculated.resolves_to_percentage())
|
||||
return normalized(calculated.resolve_percentage_deprecated(resolution_context).value().value() * 255 / 100);
|
||||
if (calculated.resolves_to_number()) {
|
||||
auto maybe_number = calculated.resolve_number(resolution_context);
|
||||
|
||||
if (!maybe_number.has_value())
|
||||
return {};
|
||||
|
||||
return normalized(maybe_number.value());
|
||||
}
|
||||
|
||||
if (calculated.resolves_to_percentage()) {
|
||||
auto maybe_percentage = calculated.resolve_percentage(resolution_context);
|
||||
|
||||
if (!maybe_percentage.has_value())
|
||||
return {};
|
||||
|
||||
return normalized(maybe_percentage.value().value() * 255 / 100);
|
||||
}
|
||||
}
|
||||
|
||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
||||
return 0;
|
||||
|
||||
return {};
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto resolve_alpha_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
|
||||
|
@ -51,12 +61,15 @@ Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolu
|
|||
return {};
|
||||
};
|
||||
|
||||
u8 const r_val = resolve_rgb_to_u8(m_properties.r).value_or(0);
|
||||
u8 const g_val = resolve_rgb_to_u8(m_properties.g).value_or(0);
|
||||
u8 const b_val = resolve_rgb_to_u8(m_properties.b).value_or(0);
|
||||
u8 const alpha_val = resolve_alpha_to_u8(m_properties.alpha).value_or(255);
|
||||
auto r_val = resolve_rgb_to_u8(m_properties.r);
|
||||
auto g_val = resolve_rgb_to_u8(m_properties.g);
|
||||
auto b_val = resolve_rgb_to_u8(m_properties.b);
|
||||
auto alpha_val = resolve_alpha_to_u8(m_properties.alpha);
|
||||
|
||||
return Color(r_val, g_val, b_val, alpha_val);
|
||||
if (!r_val.has_value() || !g_val.has_value() || !b_val.has_value() || !alpha_val.has_value())
|
||||
return {};
|
||||
|
||||
return Color(r_val.value(), g_val.value(), b_val.value(), alpha_val.value());
|
||||
}
|
||||
|
||||
bool CSSRGB::equals(CSSStyleValue const& other) const
|
||||
|
@ -73,10 +86,14 @@ bool CSSRGB::equals(CSSStyleValue const& other) const
|
|||
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
||||
String CSSRGB::to_string(SerializationMode mode) const
|
||||
{
|
||||
// FIXME: Do this properly, taking unresolved calculated values into account.
|
||||
if (mode != SerializationMode::ResolvedValue && m_properties.name.has_value())
|
||||
return m_properties.name.value().to_string().to_ascii_lowercase();
|
||||
return serialize_a_srgb_value(to_color({}, {}));
|
||||
|
||||
if (auto color = to_color({}, {}); color.has_value())
|
||||
return serialize_a_srgb_value(color.value());
|
||||
|
||||
// FIXME: Do this properly, taking unresolved calculated values into account.
|
||||
return ""_string;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
CSSStyleValue const& b() const { return *m_properties.b; }
|
||||
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
||||
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
|
|
|
@ -82,13 +82,22 @@ bool ColorFunctionStyleValue::equals(CSSStyleValue const& other) const
|
|||
return m_properties == other_lab_like.m_properties;
|
||||
}
|
||||
|
||||
ColorFunctionStyleValue::Resolved ColorFunctionStyleValue::resolve_properties(CalculationResolutionContext const& resolution_context) const
|
||||
Optional<ColorFunctionStyleValue::Resolved> ColorFunctionStyleValue::resolve_properties(CalculationResolutionContext const& resolution_context) const
|
||||
{
|
||||
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, resolution_context).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, resolution_context).value_or(1);
|
||||
return { .channels = { c1, c2, c3 }, .alpha = alpha_val };
|
||||
auto c1 = resolve_with_reference_value(m_properties.channels[0], 1, resolution_context);
|
||||
auto c2 = resolve_with_reference_value(m_properties.channels[1], 1, resolution_context);
|
||||
auto c3 = resolve_with_reference_value(m_properties.channels[2], 1, resolution_context);
|
||||
auto alpha = resolve_alpha(m_properties.alpha, resolution_context);
|
||||
|
||||
if (!c1.has_value() || !c2.has_value() || !c3.has_value() || !alpha.has_value())
|
||||
return {};
|
||||
|
||||
float const c1_value = c1.value();
|
||||
float const c2_value = c2.value();
|
||||
float const c3_value = c3.value();
|
||||
float const alpha_value = alpha.value();
|
||||
|
||||
return ColorFunctionStyleValue::Resolved { .channels = { c1_value, c2_value, c3_value }, .alpha = alpha_value };
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#serializing-color-function-values
|
||||
|
@ -102,14 +111,14 @@ String ColorFunctionStyleValue::to_string(SerializationMode mode) const
|
|||
CalculationResolutionContext context {};
|
||||
auto const& calculated = value->as_calculated();
|
||||
if (calculated.resolves_to_percentage()) {
|
||||
if (auto resolved_percentage = calculated.resolve_percentage_deprecated(context); resolved_percentage.has_value()) {
|
||||
if (auto resolved_percentage = calculated.resolve_percentage(context); resolved_percentage.has_value()) {
|
||||
auto resolved_number = resolved_percentage->value() / 100;
|
||||
if (!isfinite(resolved_number))
|
||||
resolved_number = 0;
|
||||
return NumberStyleValue::create(resolved_number);
|
||||
}
|
||||
} else if (calculated.resolves_to_number()) {
|
||||
if (auto resolved_number = calculated.resolve_number_deprecated(context); resolved_number.has_value())
|
||||
if (auto resolved_number = calculated.resolve_number(context); resolved_number.has_value())
|
||||
return NumberStyleValue::create(*resolved_number);
|
||||
}
|
||||
}
|
||||
|
@ -144,9 +153,14 @@ String ColorFunctionStyleValue::to_string(SerializationMode mode) const
|
|||
convert_percentage(m_properties.channels[2])->to_string(mode)));
|
||||
}
|
||||
|
||||
Color ColorFunctionStyleValue::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<Color> ColorFunctionStyleValue::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||
{
|
||||
auto [channels, alpha_val] = resolve_properties(resolution_context);
|
||||
auto properties = resolve_properties(resolution_context);
|
||||
|
||||
if (!properties.has_value())
|
||||
return {};
|
||||
|
||||
auto [channels, alpha_val] = properties.value();
|
||||
auto c1 = channels[0];
|
||||
auto c2 = channels[1];
|
||||
auto c3 = channels[2];
|
||||
|
|
|
@ -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 = {});
|
||||
|
||||
virtual bool equals(CSSStyleValue const&) const override;
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const override;
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
virtual bool is_color_function() const override { return true; }
|
||||
|
@ -43,7 +43,7 @@ private:
|
|||
float alpha {};
|
||||
};
|
||||
|
||||
Resolved resolve_properties(CalculationResolutionContext const& resolution_context) const;
|
||||
Optional<Resolved> resolve_properties(CalculationResolutionContext const& resolution_context) const;
|
||||
|
||||
Properties m_properties;
|
||||
};
|
||||
|
|
|
@ -175,15 +175,19 @@ ColorMixStyleValue::PercentageNormalizationResult ColorMixStyleValue::normalize_
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-color-5/#color-mix-result
|
||||
Color ColorMixStyleValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
|
||||
Optional<Color> ColorMixStyleValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
|
||||
{
|
||||
// FIXME: Take the color space and hue interpolation method into account.
|
||||
// The current implementation only uses oklab interpolation.
|
||||
auto normalized_percentages = normalize_percentages();
|
||||
auto from_color = m_properties.first_component.color;
|
||||
auto to_color = m_properties.second_component.color;
|
||||
auto from_color = m_properties.first_component.color->to_color(node, resolution_context);
|
||||
auto to_color = m_properties.second_component.color->to_color(node, resolution_context);
|
||||
auto delta = normalized_percentages.p2.value() / 100;
|
||||
return interpolate_color(from_color->to_color(node, resolution_context), to_color->to_color(node, resolution_context), delta, ColorSyntax::Modern);
|
||||
|
||||
if (!from_color.has_value() || !to_color.has_value())
|
||||
return {};
|
||||
|
||||
return interpolate_color(from_color.value(), to_color.value(), delta, ColorSyntax::Modern);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
static ValueComparingNonnullRefPtr<ColorMixStyleValue const> create(ColorInterpolationMethod, ColorMixComponent first_component, ColorMixComponent second_component);
|
||||
|
||||
virtual bool equals(CSSStyleValue const&) const override;
|
||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
private:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue