From 70ded2ef421e0e4ab64a4986a729251844766b94 Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Sun, 11 Feb 2024 18:07:38 -0700 Subject: [PATCH] LibGfx: Add Oklab support to Gfx::Color Interpolation of color on the web is done via the oklab colorspace --- Userland/Libraries/LibGfx/Color.h | 58 +++++++++++++++++++ .../Libraries/LibWeb/CSS/StyleComputer.cpp | 12 ++-- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibGfx/Color.h b/Userland/Libraries/LibGfx/Color.h index b53fe8facd..daed6e3f55 100644 --- a/Userland/Libraries/LibGfx/Color.h +++ b/Userland/Libraries/LibGfx/Color.h @@ -32,6 +32,12 @@ struct YUV { float v { 0 }; }; +struct Oklab { + float L { 0 }; + float a { 0 }; + float b { 0 }; +}; + class Color { public: enum NamedColor { @@ -159,6 +165,58 @@ public: return Color(r_u8, g_u8, b_u8, a_u8); } + // https://bottosson.github.io/posts/oklab/ + static constexpr Color from_oklab(float L, float a, float b, float alpha = 1.0f) + { + auto linear_to_srgb = [](float c) { + return c >= 0.0031308f ? 1.055f * pow(c, 0.4166666f) - 0.055f : 12.92f * c; + }; + + float l = L + 0.3963377774f * a + 0.2158037573f * b; + float m = L - 0.1055613458f * a - 0.0638541728f * b; + float s = L - 0.0894841775f * a - 1.2914855480f * b; + + l = l * l * l; + m = m * m * m; + s = s * s * s; + + float red = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s; + float green = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s; + float blue = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s; + + red = linear_to_srgb(red) * 255.f; + green = linear_to_srgb(green) * 255.f; + blue = linear_to_srgb(blue) * 255.f; + + return Color( + clamp(lroundf(red), 0, 255), + clamp(lroundf(green), 0, 255), + clamp(lroundf(blue), 0, 255), + clamp(lroundf(alpha * 255.f), 0, 255)); + } + + // https://bottosson.github.io/posts/oklab/ + constexpr Oklab to_oklab() + { + auto srgb_to_linear = [](float c) { + return c >= 0.04045f ? pow((c + 0.055f) / 1.055f, 2.4f) : c / 12.92f; + }; + + float r = srgb_to_linear(red() / 255.f); + float g = srgb_to_linear(green() / 255.f); + float b = srgb_to_linear(blue() / 255.f); + + float l = cbrtf(0.4122214708f * r + 0.5363325363f * g + 0.0514459929f * b); + float m = cbrtf(0.2119034982f * r + 0.6806995451f * g + 0.1073969566f * b); + float s = cbrtf(0.0883024619f * r + 0.2817188376f * g + 0.6299787005f * b); + + return { + 0.2104542553f * l + 0.7936177850f * m - 0.0040720468f * s, + 1.9779984951f * l - 2.4285922050f * m + 0.4505937099f * s, + 0.0259040371f * l + 0.7827717662f * m - 0.8086757660f * s, + }; + } + constexpr u8 red() const { return (m_value >> 16) & 0xff; } constexpr u8 green() const { return (m_value >> 8) & 0xff; } constexpr u8 blue() const { return m_value & 0xff; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 54fea1edef..7670653cde 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -771,13 +771,13 @@ static ErrorOr> interpolate_property(StyleValue const& case StyleValue::Type::Color: { auto from_color = from.as_color().color(); auto to_color = to.as_color().color(); - auto from_hsv = from_color.to_hsv(); - auto to_hsv = to_color.to_hsv(); + auto from_oklab = from_color.to_oklab(); + auto to_oklab = to_color.to_oklab(); - auto color = Color::from_hsv( - interpolate_raw(from_hsv.hue, to_hsv.hue), - interpolate_raw(from_hsv.saturation, to_hsv.saturation), - interpolate_raw(from_hsv.value, to_hsv.value)); + auto color = Color::from_oklab( + interpolate_raw(from_oklab.L, to_oklab.L), + interpolate_raw(from_oklab.a, to_oklab.a), + interpolate_raw(from_oklab.b, to_oklab.b)); color.set_alpha(interpolate_raw(from_color.alpha(), to_color.alpha())); return ColorStyleValue::create(color);