From 4575d23b1e905b140d8097937292fbe9e03a81cc Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Wed, 15 Apr 2026 23:35:11 +0100 Subject: [PATCH] LibGfx: Preserve out-of-range components in HSL to sRGB conversion --- Libraries/LibGfx/Color.h | 2 +- Libraries/LibGfx/ColorConversion.h | 7 +++- .../parsing/color-mix-out-of-gamut.txt | 23 +++++++++++ .../parsing/color-mix-out-of-gamut.html | 41 +++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.html diff --git a/Libraries/LibGfx/Color.h b/Libraries/LibGfx/Color.h index a3b08221b4..c35c7443e2 100644 --- a/Libraries/LibGfx/Color.h +++ b/Libraries/LibGfx/Color.h @@ -171,7 +171,7 @@ public: static constexpr Color from_hsl(float h_degrees, float s, float l) { return from_hsla(h_degrees, s, l, 1.0); } static constexpr Color from_hsla(float h_degrees, float s, float l, float a) { - auto srgb = hsl_to_srgb({ h_degrees, s, l, a }); + auto srgb = hsl_to_srgb({ h_degrees, clamp(s, 0.0f, 1.0f), clamp(l, 0.0f, 1.0f), a }); u8 r_u8 = clamp(lroundf(srgb[0] * 255.0f), 0, 255); u8 g_u8 = clamp(lroundf(srgb[1] * 255.0f), 0, 255); u8 b_u8 = clamp(lroundf(srgb[2] * 255.0f), 0, 255); diff --git a/Libraries/LibGfx/ColorConversion.h b/Libraries/LibGfx/ColorConversion.h index 374031cb92..68f5dc9ca5 100644 --- a/Libraries/LibGfx/ColorConversion.h +++ b/Libraries/LibGfx/ColorConversion.h @@ -43,8 +43,11 @@ constexpr ColorComponents hsl_to_srgb(ColorComponents const& hsl) if (h < 0.0f) h += 360.0f; - float s = clamp(hsl[1], 0.0f, 1.0f); - float l = clamp(hsl[2], 0.0f, 1.0f); + // NB: Saturation and lightness are intentionally left unclamped. Color interpolation in HSL can produce + // out-of-range s/l when representing colors outside the sRGB gamut, and those values should be able to + // round-trip back to sRGB correctly. + float s = hsl[1]; + float l = hsl[2]; float a = clamp(hsl.alpha(), 0.0f, 1.0f); // Algorithm from https://drafts.csswg.org/css-color-3/#hsl-color diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.txt new file mode 100644 index 0000000000..a626c20999 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.txt @@ -0,0 +1,23 @@ +Harness status: OK + +Found 18 tests + +18 Pass +Pass Property color value 'color-mix(in hsl, color(display-p3 0 1 0) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, lab(100 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, lab(0 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, lch(100 116 334) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, lch(0 116 334) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, oklab(1 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, oklab(0 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, oklch(1 0.399 336.3) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hsl, oklch(0 0.399 336.3) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, color(display-p3 0 1 0) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, lab(100 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, lab(0 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, lch(100 116 334) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, lch(0 116 334) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, oklab(1 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, oklab(0 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, oklch(1 0.399 336.3) 100%, rgb(0, 0, 0) 0%)' +Pass Property color value 'color-mix(in hwb, oklch(0 0.399 336.3) 100%, rgb(0, 0, 0) 0%)' \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.html b/Tests/LibWeb/Text/input/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.html new file mode 100644 index 0000000000..1920ee8668 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-color/parsing/color-mix-out-of-gamut.html @@ -0,0 +1,41 @@ + + + + +CSS Color Level 4: Computation of colors using color-mix() function syntax that result in out-of-gamut sRGB colors + + + + + + + + + + + + +
+ + +