Selaa lähdekoodia

LibGfx: Reimplement from_hsla

- Hue now wraps properly when negative or larger than 360
- The hsl to rgb conversion now closely mirrors the code example from
  the spec.

This fixes a number of WPT tests in
/css/css-color/parsing/color-computed-hsl.html
Borna Lang 8 kuukautta sitten
vanhempi
commit
201648485f
2 muutettua tiedostoa jossa 28 lisäystä ja 37 poistoa
  1. 13 37
      Libraries/LibGfx/Color.h
  2. 15 0
      Tests/LibGfx/TestColor.cpp

+ 13 - 37
Libraries/LibGfx/Color.h

@@ -131,49 +131,25 @@ public:
     static constexpr Color from_hsla(float h_degrees, float s, float l, float a)
     {
         // Algorithm from https://www.w3.org/TR/css-color-3/#hsl-color
-        float h = clamp(h_degrees / 360.0f, 0.0f, 1.0f);
+
+        float h = fmodf(h_degrees, 360.0f);
+        if (h < 0.0)
+            h += 360.0f;
+
         s = clamp(s, 0.0f, 1.0f);
         l = clamp(l, 0.0f, 1.0f);
         a = clamp(a, 0.0f, 1.0f);
 
-        // HOW TO RETURN hue.to.rgb(m1, m2, h):
-        auto hue_to_rgb = [](float m1, float m2, float h) -> float {
-            // IF h<0: PUT h+1 IN h
-            if (h < 0.0f)
-                h = h + 1.0f;
-            // IF h>1: PUT h-1 IN h
-            if (h > 1.0f)
-                h = h - 1.0f;
-            // IF h*6<1: RETURN m1+(m2-m1)*h*6
-            if (h * 6.0f < 1.0f)
-                return m1 + (m2 - m1) * h * 6.0f;
-            // IF h*2<1: RETURN m2
-            if (h * 2.0f < 1.0f)
-                return m2;
-            // IF h*3<2: RETURN m1+(m2-m1)*(2/3-h)*6
-            if (h * 3.0f < 2.0f)
-                return m1 + (m2 - m1) * (2.0f / 3.0f - h) * 6.0f;
-            // RETURN m1
-            return m1;
+        auto to_rgb = [](float h, float s, float l, float offset) {
+            float k = fmodf(offset + h / 30.0f, 12.0f);
+            float a = s * min(l, 1.0f - l);
+            return l - a * max(-1.0f, min(min(k - 3.0f, 9.0f - k), 1.0f));
         };
 
-        // SELECT:
-        // l<=0.5: PUT l*(s+1) IN m2
-        float m2;
-        if (l <= 0.5f)
-            m2 = l * (s + 1.0f);
-        // ELSE: PUT l+s-l*s IN m2
-        else
-            m2 = l + s - l * s;
-        // PUT l*2-m2 IN m1
-        float m1 = l * 2.0f - m2;
-        // PUT hue.to.rgb(m1, m2, h+1/3) IN r
-        float r = hue_to_rgb(m1, m2, h + 1.0f / 3.0f);
-        // PUT hue.to.rgb(m1, m2, h    ) IN g
-        float g = hue_to_rgb(m1, m2, h);
-        // PUT hue.to.rgb(m1, m2, h-1/3) IN b
-        float b = hue_to_rgb(m1, m2, h - 1.0f / 3.0f);
-        // RETURN (r, g, b)
+        float r = to_rgb(h, s, l, 0.0f);
+        float g = to_rgb(h, s, l, 8.0f);
+        float b = to_rgb(h, s, l, 4.0f);
+
         u8 r_u8 = clamp(lroundf(r * 255.0f), 0, 255);
         u8 g_u8 = clamp(lroundf(g * 255.0f), 0, 255);
         u8 b_u8 = clamp(lroundf(b * 255.0f), 0, 255);

+ 15 - 0
Tests/LibGfx/TestColor.cpp

@@ -28,3 +28,18 @@ TEST_CASE(hsv)
     EXPECT_EQ(Color(51, 179, 51, 128), Color::from_hsv(120, 0.714285714, .7).with_opacity(0.5));
     EXPECT_EQ(Color(87, 128, 77), Color::from_hsv(108, 0.4, .5));
 }
+
+TEST_CASE(hsl)
+{
+    EXPECT_EQ(Color(191, 191, 0), Color::from_hsl(-300, 1.0, 0.375));
+    EXPECT_EQ(Color(159, 138, 96), Color::from_hsl(400, 0.25, 0.5));
+    EXPECT_EQ(Color(159, 96, 128), Color::from_hsl(330, 0.25, 0.5));
+    EXPECT_EQ(Color(128, 0, 128), Color::from_hsl(300, 1.0, 0.25));
+    EXPECT_EQ(Color(0, 128, 128), Color::from_hsl(180, 1.0, 0.25));
+    EXPECT_EQ(Color(128, 239, 16), Color::from_hsl(90, 0.875, 0.5));
+    EXPECT_EQ(Color(128, 223, 32), Color::from_hsl(90, 0.75, 0.5));
+    EXPECT_EQ(Color(128, 207, 48), Color::from_hsl(90, 0.625, 0.5));
+    EXPECT_EQ(Color(128, 191, 64), Color::from_hsl(90, 0.5, 0.5));
+    EXPECT_EQ(Color(128, 175, 80), Color::from_hsl(90, 0.375, 0.5));
+    EXPECT_EQ(Color(128, 159, 96), Color::from_hsl(90, 0.25, 0.5));
+}