mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWeb: Route lch()/oklch() through unified ColorFunctionStyleValue
This commit is contained in:
committed by
Sam Atkins
parent
43ba21a45b
commit
8d4f8a2d7f
Notes:
github-actions[bot]
2026-04-22 10:54:12 +00:00
Author: https://github.com/tcl3 Commit: https://github.com/LadybirdBrowser/ladybird/commit/8d4f8a2d7ff Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8993 Reviewed-by: https://github.com/AtkinsSJ ✅
@@ -268,7 +268,6 @@ set(SOURCES
|
||||
CSS/StyleValues/ImageStyleValue.cpp
|
||||
CSS/StyleValues/IntegerStyleValue.cpp
|
||||
CSS/StyleValues/KeywordStyleValue.cpp
|
||||
CSS/StyleValues/LCHLikeColorStyleValue.cpp
|
||||
CSS/StyleValues/LengthStyleValue.cpp
|
||||
CSS/StyleValues/LightDarkStyleValue.cpp
|
||||
CSS/StyleValues/LinearGradientStyleValue.cpp
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <LibWeb/CSS/Interpolation.h>
|
||||
#include <LibWeb/CSS/StyleValues/ColorFunctionStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/KeywordStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/LCHLikeColorStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
@@ -138,12 +137,12 @@ static MissingComponents extract_missing_components(StyleValue const& style_valu
|
||||
return { is_component_none(oklab.channel(0)), is_component_none(oklab.channel(1)), is_component_none(oklab.channel(2)), is_component_none(oklab.alpha()) };
|
||||
}
|
||||
case ColorStyleValue::ColorType::LCH: {
|
||||
auto const& lch = as<LCHColorStyleValue>(color);
|
||||
return { is_component_none(lch.l()), is_component_none(lch.c()), is_component_none(lch.h()), is_component_none(lch.alpha()) };
|
||||
auto const& lch = as<ColorFunctionStyleValue>(color);
|
||||
return { is_component_none(lch.channel(0)), is_component_none(lch.channel(1)), is_component_none(lch.channel(2)), is_component_none(lch.alpha()) };
|
||||
}
|
||||
case ColorStyleValue::ColorType::OKLCH: {
|
||||
auto const& oklch = as<OKLCHColorStyleValue>(color);
|
||||
return { is_component_none(oklch.l()), is_component_none(oklch.c()), is_component_none(oklch.h()), is_component_none(oklch.alpha()) };
|
||||
auto const& oklch = as<ColorFunctionStyleValue>(color);
|
||||
return { is_component_none(oklch.channel(0)), is_component_none(oklch.channel(1)), is_component_none(oklch.channel(2)), is_component_none(oklch.alpha()) };
|
||||
}
|
||||
case ColorStyleValue::ColorType::RGB: {
|
||||
auto const& rgb = as<ColorFunctionStyleValue>(color);
|
||||
@@ -363,13 +362,13 @@ static ValueComparingNonnullRefPtr<StyleValue const> style_value_from_polar_colo
|
||||
alpha);
|
||||
}
|
||||
case PolarColorSpace::Lch:
|
||||
return LCHLikeColorStyleValue::create<LCHColorStyleValue>(
|
||||
return ColorFunctionStyleValue::create(ColorStyleValue::ColorType::LCH,
|
||||
number_or_none(components[0], missing.component(0)),
|
||||
number_or_none(components[1], missing.component(1)),
|
||||
number_or_none(components[2], missing.component(2)),
|
||||
alpha);
|
||||
case PolarColorSpace::Oklch:
|
||||
return LCHLikeColorStyleValue::create<OKLCHColorStyleValue>(
|
||||
return ColorFunctionStyleValue::create(ColorStyleValue::ColorType::OKLCH,
|
||||
number_or_none(components[0], missing.component(0)),
|
||||
number_or_none(components[1], missing.component(1)),
|
||||
number_or_none(components[2], missing.component(2)),
|
||||
@@ -437,20 +436,20 @@ static Optional<Gfx::ColorComponents> style_value_to_color_components(StyleValue
|
||||
return Gfx::ColorComponents { static_cast<float>(l.value()), static_cast<float>(a_comp.value()), static_cast<float>(b_comp.value()), a.value() };
|
||||
}
|
||||
case ColorStyleValue::ColorType::LCH: {
|
||||
auto const& lch = as<LCHColorStyleValue>(color);
|
||||
auto l = ColorStyleValue::resolve_with_reference_value(lch.l(), 100.0f, context);
|
||||
auto c = ColorStyleValue::resolve_with_reference_value(lch.c(), 150.0f, context);
|
||||
auto h = ColorStyleValue::resolve_hue(lch.h(), context);
|
||||
auto const& lch = as<ColorFunctionStyleValue>(color);
|
||||
auto l = ColorStyleValue::resolve_with_reference_value(lch.channel(0), 100.0f, context);
|
||||
auto c = ColorStyleValue::resolve_with_reference_value(lch.channel(1), 150.0f, context);
|
||||
auto h = ColorStyleValue::resolve_hue(lch.channel(2), context);
|
||||
auto a = resolve_alpha(lch.alpha());
|
||||
if (!l.has_value() || !c.has_value() || !h.has_value() || !a.has_value())
|
||||
return {};
|
||||
return Gfx::ColorComponents { static_cast<float>(l.value()), static_cast<float>(c.value()), static_cast<float>(h.value()), a.value() };
|
||||
}
|
||||
case ColorStyleValue::ColorType::OKLCH: {
|
||||
auto const& oklch = as<OKLCHColorStyleValue>(color);
|
||||
auto l = ColorStyleValue::resolve_with_reference_value(oklch.l(), 1.0f, context);
|
||||
auto c = ColorStyleValue::resolve_with_reference_value(oklch.c(), 0.4f, context);
|
||||
auto h = ColorStyleValue::resolve_hue(oklch.h(), context);
|
||||
auto const& oklch = as<ColorFunctionStyleValue>(color);
|
||||
auto l = ColorStyleValue::resolve_with_reference_value(oklch.channel(0), 1.0f, context);
|
||||
auto c = ColorStyleValue::resolve_with_reference_value(oklch.channel(1), 0.4f, context);
|
||||
auto h = ColorStyleValue::resolve_hue(oklch.channel(2), context);
|
||||
auto a = resolve_alpha(oklch.alpha());
|
||||
if (!l.has_value() || !c.has_value() || !h.has_value() || !a.has_value())
|
||||
return {};
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/KeywordStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/LCHLikeColorStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/LightDarkStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/LinearGradientStyleValue.h>
|
||||
@@ -1893,7 +1892,7 @@ RefPtr<StyleValue const> Parser::parse_lch_color_value(TokenStream<ComponentValu
|
||||
|
||||
auto& color_values = *maybe_color_values;
|
||||
|
||||
return LCHLikeColorStyleValue::create<LCHColorStyleValue>(color_values[0].release_nonnull(),
|
||||
return ColorFunctionStyleValue::create(ColorStyleValue::ColorType::LCH, color_values[0].release_nonnull(),
|
||||
color_values[1].release_nonnull(),
|
||||
color_values[2].release_nonnull(),
|
||||
color_values[3].release_nonnull());
|
||||
@@ -1913,7 +1912,7 @@ RefPtr<StyleValue const> Parser::parse_oklch_color_value(TokenStream<ComponentVa
|
||||
|
||||
auto& color_values = *maybe_color_values;
|
||||
|
||||
return LCHLikeColorStyleValue::create<OKLCHColorStyleValue>(color_values[0].release_nonnull(),
|
||||
return ColorFunctionStyleValue::create(ColorStyleValue::ColorType::OKLCH, color_values[0].release_nonnull(),
|
||||
color_values[1].release_nonnull(),
|
||||
color_values[2].release_nonnull(),
|
||||
color_values[3].release_nonnull());
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026, Sam Atkins <sam@ladybird.org>
|
||||
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "LCHLikeColorStyleValue.h"
|
||||
#include <AK/Math.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibWeb/CSS/Serialize.h>
|
||||
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/KeywordStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
bool LCHLikeColorStyleValue::equals(StyleValue const& other) const
|
||||
{
|
||||
if (type() != other.type())
|
||||
return false;
|
||||
auto const& other_color = other.as_color();
|
||||
if (color_type() != other_color.color_type())
|
||||
return false;
|
||||
auto const& other_oklch_like = as<LCHLikeColorStyleValue>(other_color);
|
||||
return m_properties == other_oklch_like.m_properties;
|
||||
}
|
||||
|
||||
Optional<Color> LCHColorStyleValue::to_color(ColorResolutionContext color_resolution_context) const
|
||||
{
|
||||
auto raw_l_val = resolve_with_reference_value(m_properties.l, 100, color_resolution_context.calculation_resolution_context);
|
||||
auto c_val = resolve_with_reference_value(m_properties.c, 150, color_resolution_context.calculation_resolution_context);
|
||||
auto raw_h_val = resolve_hue(m_properties.h, color_resolution_context.calculation_resolution_context);
|
||||
auto alpha_val = resolve_alpha(m_properties.alpha, color_resolution_context.calculation_resolution_context);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
ValueComparingNonnullRefPtr<StyleValue const> LCHColorStyleValue::absolutized(ComputationContext const& context) const
|
||||
{
|
||||
auto l = m_properties.l->absolutized(context);
|
||||
auto c = m_properties.c->absolutized(context);
|
||||
auto h = m_properties.h->absolutized(context);
|
||||
auto alpha = m_properties.alpha->absolutized(context);
|
||||
if (l == m_properties.l && c == m_properties.c && h == m_properties.h && alpha == m_properties.alpha)
|
||||
return *this;
|
||||
return LCHLikeColorStyleValue::create<LCHColorStyleValue>(move(l), move(c), move(h), move(alpha));
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#serializing-lab-lch
|
||||
void LCHColorStyleValue::serialize(StringBuilder& builder, SerializationMode mode) const
|
||||
{
|
||||
builder.append("lch("sv);
|
||||
serialize_color_component(builder, mode, m_properties.l, 100, 0, 100);
|
||||
builder.append(' ');
|
||||
serialize_color_component(builder, mode, m_properties.c, 150, 0);
|
||||
builder.append(' ');
|
||||
serialize_hue_component(builder, mode, m_properties.h);
|
||||
if ((!m_properties.alpha->is_number() || m_properties.alpha->as_number().number() < 1)
|
||||
&& (!m_properties.alpha->is_percentage() || m_properties.alpha->as_percentage().percentage().as_fraction() < 1)) {
|
||||
builder.append(" / "sv);
|
||||
serialize_alpha_component(builder, mode, m_properties.alpha);
|
||||
}
|
||||
|
||||
builder.append(')');
|
||||
}
|
||||
|
||||
Optional<Color> OKLCHColorStyleValue::to_color(ColorResolutionContext color_resolution_context) const
|
||||
{
|
||||
auto raw_l_val = resolve_with_reference_value(m_properties.l, 1.0, color_resolution_context.calculation_resolution_context);
|
||||
auto raw_c_val = resolve_with_reference_value(m_properties.c, 0.4, color_resolution_context.calculation_resolution_context);
|
||||
auto raw_h_val = resolve_hue(m_properties.h, color_resolution_context.calculation_resolution_context);
|
||||
auto alpha_val = resolve_alpha(m_properties.alpha, color_resolution_context.calculation_resolution_context);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
ValueComparingNonnullRefPtr<StyleValue const> OKLCHColorStyleValue::absolutized(ComputationContext const& context) const
|
||||
{
|
||||
auto l = m_properties.l->absolutized(context);
|
||||
auto c = m_properties.c->absolutized(context);
|
||||
auto h = m_properties.h->absolutized(context);
|
||||
auto alpha = m_properties.alpha->absolutized(context);
|
||||
if (l == m_properties.l && c == m_properties.c && h == m_properties.h && alpha == m_properties.alpha)
|
||||
return *this;
|
||||
return LCHLikeColorStyleValue::create<OKLCHColorStyleValue>(l, c, h, alpha);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#serializing-oklab-oklch
|
||||
void OKLCHColorStyleValue::serialize(StringBuilder& builder, SerializationMode mode) const
|
||||
{
|
||||
builder.append("oklch("sv);
|
||||
serialize_color_component(builder, mode, m_properties.l, 1.0f, 0, 1);
|
||||
builder.append(' ');
|
||||
serialize_color_component(builder, mode, m_properties.c, 0.4f, 0);
|
||||
builder.append(' ');
|
||||
serialize_hue_component(builder, mode, m_properties.h);
|
||||
if ((!m_properties.alpha->is_number() || m_properties.alpha->as_number().number() < 1)
|
||||
&& (!m_properties.alpha->is_percentage() || m_properties.alpha->as_percentage().percentage().as_fraction() < 1)) {
|
||||
builder.append(" / "sv);
|
||||
serialize_alpha_component(builder, mode, m_properties.alpha);
|
||||
}
|
||||
|
||||
builder.append(')');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ComputationContext.h>
|
||||
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
class LCHLikeColorStyleValue : public ColorStyleValue {
|
||||
public:
|
||||
template<DerivedFrom<LCHLikeColorStyleValue> T>
|
||||
static ValueComparingNonnullRefPtr<T const> create(ValueComparingNonnullRefPtr<StyleValue const> l, ValueComparingNonnullRefPtr<StyleValue const> c, ValueComparingNonnullRefPtr<StyleValue const> h, ValueComparingRefPtr<StyleValue const> alpha = {})
|
||||
{
|
||||
// alpha defaults to 1
|
||||
if (!alpha)
|
||||
alpha = NumberStyleValue::create(1);
|
||||
|
||||
return adopt_ref(*new (nothrow) T({}, move(l), move(c), move(h), alpha.release_nonnull()));
|
||||
}
|
||||
virtual ~LCHLikeColorStyleValue() override = default;
|
||||
|
||||
StyleValue const& l() const { return *m_properties.l; }
|
||||
StyleValue const& c() const { return *m_properties.c; }
|
||||
StyleValue const& h() const { return *m_properties.h; }
|
||||
StyleValue const& alpha() const { return *m_properties.alpha; }
|
||||
|
||||
virtual bool equals(StyleValue const& other) const override;
|
||||
|
||||
virtual bool is_computationally_independent() const override
|
||||
{
|
||||
return m_properties.l->is_computationally_independent()
|
||||
&& m_properties.c->is_computationally_independent()
|
||||
&& m_properties.h->is_computationally_independent()
|
||||
&& m_properties.alpha->is_computationally_independent();
|
||||
}
|
||||
|
||||
protected:
|
||||
LCHLikeColorStyleValue(ColorType color_type, ValueComparingNonnullRefPtr<StyleValue const> l, ValueComparingNonnullRefPtr<StyleValue const> c, ValueComparingNonnullRefPtr<StyleValue const> h, ValueComparingNonnullRefPtr<StyleValue const> alpha)
|
||||
: ColorStyleValue(color_type, ColorSyntax::Modern)
|
||||
, m_properties { .l = move(l), .c = move(c), .h = move(h), .alpha = move(alpha) }
|
||||
{
|
||||
}
|
||||
|
||||
struct Properties {
|
||||
ValueComparingNonnullRefPtr<StyleValue const> l;
|
||||
ValueComparingNonnullRefPtr<StyleValue const> c;
|
||||
ValueComparingNonnullRefPtr<StyleValue const> h;
|
||||
ValueComparingNonnullRefPtr<StyleValue const> alpha;
|
||||
bool operator==(Properties const&) const = default;
|
||||
} m_properties;
|
||||
};
|
||||
|
||||
class LCHColorStyleValue final : public LCHLikeColorStyleValue {
|
||||
public:
|
||||
LCHColorStyleValue(Badge<LCHLikeColorStyleValue>, ValueComparingNonnullRefPtr<StyleValue const> l, ValueComparingNonnullRefPtr<StyleValue const> c, ValueComparingNonnullRefPtr<StyleValue const> h, ValueComparingNonnullRefPtr<StyleValue const> alpha)
|
||||
: LCHLikeColorStyleValue(ColorType::LCH, move(l), move(c), move(h), move(alpha))
|
||||
{
|
||||
}
|
||||
virtual ~LCHColorStyleValue() override = default;
|
||||
|
||||
virtual Optional<Color> to_color(ColorResolutionContext) const override;
|
||||
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
|
||||
|
||||
virtual void serialize(StringBuilder&, SerializationMode) const override;
|
||||
};
|
||||
|
||||
class OKLCHColorStyleValue final : public LCHLikeColorStyleValue {
|
||||
public:
|
||||
OKLCHColorStyleValue(Badge<LCHLikeColorStyleValue>, ValueComparingNonnullRefPtr<StyleValue const> l, ValueComparingNonnullRefPtr<StyleValue const> c, ValueComparingNonnullRefPtr<StyleValue const> h, ValueComparingNonnullRefPtr<StyleValue const> alpha)
|
||||
: LCHLikeColorStyleValue(ColorType::OKLCH, move(l), move(c), move(h), move(alpha))
|
||||
{
|
||||
}
|
||||
virtual ~OKLCHColorStyleValue() override = default;
|
||||
|
||||
virtual Optional<Color> to_color(ColorResolutionContext) const override;
|
||||
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
|
||||
|
||||
virtual void serialize(StringBuilder&, SerializationMode) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -362,7 +362,6 @@ class MediaQueryListEvent;
|
||||
class Number;
|
||||
class NumberStyleValue;
|
||||
class NumericType;
|
||||
class OKLCHColorStyleValue;
|
||||
class OpenTypeTaggedStyleValue;
|
||||
class ParsedFontFace;
|
||||
class PendingSubstitutionStyleValue;
|
||||
|
||||
Reference in New Issue
Block a user