LibGfx: Add Oklab support to Gfx::Color

Interpolation of color on the web is done via the oklab colorspace
This commit is contained in:
Matthew Olsson 2024-02-11 18:07:38 -07:00 committed by Andreas Kling
parent 71a784741f
commit 70ded2ef42
Notes: sideshowbarker 2024-07-17 16:23:55 +09:00
2 changed files with 64 additions and 6 deletions

View File

@ -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; }

View File

@ -771,13 +771,13 @@ static ErrorOr<NonnullRefPtr<StyleValue>> 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);