Replace Color::getHSL() with sRGBToHSL to ensure it at least gives somewhat sensible...
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 May 2020 22:54:11 +0000 (22:54 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 May 2020 22:54:11 +0000 (22:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=212143

Reviewed by Simon Fraser.

Source/WebCore:

- Updated API tests to test sRGBToHSL() rather than Color::getHSL() and extended the tests
  to include lightness tests and round tripping tests.
- Update editing/pasteboard/paste-dark-mode-color-filtered.html with extended color test cases.

Replaces Color::getHSL() with sRGBToHSL(color.toSRGBAComponentsLossy()) and adds
an optimized variant, lightness(...) that just extracts the lightness component for
callers that only needed that.

It is now required to explicitly use color.toSRGBAComponentsLossy() to indicate that
for non-SRGB colors, this will be a lossy transformation and give us an easy way to
find all these sites in the future, if we want to make a better conversion for other
color spaces.

* editing/ReplaceSelectionCommand.cpp:
(WebCore::fragmentNeedsColorTransformed):
Switch to using lightness(). This was previously broken for extended colors but now works (though
in a lossy way through sRGB). Update editing/pasteboard/paste-dark-mode-color-filtered.html with extended colors.

* editing/cocoa/DataDetection.mm:
(WebCore::DataDetection::detectContentInRange):
Switch to using sRGBToHSL(color.toSRGBAComponentsLossy()).

* page/FrameView.cpp:
(WebCore::FrameView::recalculateScrollbarOverlayStyle):
Switch to using lightness().

* platform/graphics/Color.cpp:
(WebCore::Color::getHSL const): Deleted.
* platform/graphics/Color.h:
Remove Color::getHSL().

* platform/graphics/ColorUtilities.cpp:
(WebCore::lightness):
Added optimized subset of sRGBToHSL which just computes the lightness component for callers
that only need that.

(WebCore::sRGBToHSL):
Simplify/cleanup code a little using initialize-list based std::max/std::min and structured bindings.

* platform/graphics/ColorUtilities.h:
Export functions to allow testing them directly.

Source/WebKit:

* UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView _updateScrollViewBackground]):
Switch to using Color::lightness().

Tools:

* TestWebKitAPI/Tests/WebCore/ColorTests.cpp:
(TestWebKitAPI::TEST):
Update tests to use sRGBToHSL instead of Color::getHSL() and add testing of WebCore::lightness() and
round tripping. Update results for hue to be 0->1 rather than 0->6 which allows the round tripping to succeed.

LayoutTests:

* editing/pasteboard/paste-dark-mode-color-filtered-expected.txt:
* editing/pasteboard/paste-dark-mode-color-filtered.html:
Update test to include a test case that uses extended colors.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@261967 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/pasteboard/paste-dark-mode-color-filtered-expected.txt
LayoutTests/editing/pasteboard/paste-dark-mode-color-filtered.html
Source/WebCore/ChangeLog
Source/WebCore/editing/ReplaceSelectionCommand.cpp
Source/WebCore/editing/cocoa/DataDetection.mm
Source/WebCore/page/FrameView.cpp
Source/WebCore/platform/graphics/Color.cpp
Source/WebCore/platform/graphics/Color.h
Source/WebCore/platform/graphics/ColorUtilities.cpp
Source/WebCore/platform/graphics/ColorUtilities.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebCore/ColorTests.cpp

index 2510480..81ce361 100644 (file)
@@ -1,3 +1,14 @@
+2020-05-20  Sam Weinig  <weinig@apple.com>
+
+        Replace Color::getHSL() with sRGBToHSL to ensure it at least gives somewhat sensible results for ExtendedColors and reduce code duplication
+        https://bugs.webkit.org/show_bug.cgi?id=212143
+
+        Reviewed by Simon Fraser.
+
+        * editing/pasteboard/paste-dark-mode-color-filtered-expected.txt:
+        * editing/pasteboard/paste-dark-mode-color-filtered.html:
+        Update test to include a test case that uses extended colors.
+
 2020-05-20  Kate Cheney  <katherine_cheney@apple.com>
 
         Support operating dates in ResourceLoadStatisticsDatabaseStore
index d6c73ae..efa827c 100644 (file)
@@ -7,6 +7,7 @@ PASS pastedMarkup is '<span style="color: rgb(106, 106, 106);">Hello</span><span
 PASS pastedMarkup is '<span style="caret-color: rgb(127, 128, 127); color: rgb(127, 128, 127);">Hello</span>'
 PASS pastedMarkup is '<span style="caret-color: rgb(170, 170, 170); color: rgb(170, 170, 170);">Hello</span>'
 PASS pastedMarkup is '<span style="caret-color: rgb(191, 191, 191); color: rgb(191, 191, 191);">Hello</span>'
+PASS pastedMarkup is '<span style="caret-color: rgb(22, 22, 22); color: rgb(22, 22, 22); background-color: rgb(255, 255, 255);">Hello</span>'
 PASS pastedMarkup is '<li>Item 1</li><li><span style="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255);">Hello</span></li><li>Item 2</li>'
 PASS pastedMarkup is '<li>Item 1</li><li style="color: rgb(21, 21, 21); background-color: rgb(213, 213, 213);">Hello 1</li><li>Hello 2</li><li>Item 2</li>'
 PASS pastedMarkup is '<li>Item 1</li><li>Hello 1</li><li style="color: rgb(127, 128, 127);">Hello 2</li><li>Item 2</li>'
index 2faa8c7..0c7b5ac 100644 (file)
@@ -66,6 +66,7 @@
     testCopyPaste("<span style=\"color: rgb(153, 153, 153)\">Hello</span>", "<span style=\"caret-color: rgb(127, 128, 127); color: rgb(127, 128, 127);\">Hello</span>");
     testCopyPaste("<span style=\"color: rgb(119, 119, 119)\">Hello</span>", "<span style=\"caret-color: rgb(170, 170, 170); color: rgb(170, 170, 170);\">Hello</span>");
     testCopyPaste("<span style=\"color: rgb(102, 102, 102)\">Hello</span>", "<span style=\"caret-color: rgb(191, 191, 191); color: rgb(191, 191, 191);\">Hello</span>");
+    testCopyPaste("<span style=\"color: color(display-p3 0.93 0.93 0.93); background-color: color(display-p3 0.2 0.2 0.2)\">Hello</span>", "<span style=\"caret-color: rgb(22, 22, 22); color: rgb(22, 22, 22); background-color: rgb(255, 255, 255);\">Hello</span>");
 
     // Transformed list items on paste
     testCopyPaste("<li><span style=\"color: white; background-color: black\">Hello</span></li>", "<li>Item 1</li><li><span style=\"color: rgb(0, 0, 0); background-color: rgb(255, 255, 255);\">Hello</span></li><li>Item 2</li>", true);
index 5ddc981..174de58 100644 (file)
@@ -1,3 +1,52 @@
+2020-05-20  Sam Weinig  <weinig@apple.com>
+
+        Replace Color::getHSL() with sRGBToHSL to ensure it at least gives somewhat sensible results for ExtendedColors and reduce code duplication
+        https://bugs.webkit.org/show_bug.cgi?id=212143
+
+        Reviewed by Simon Fraser.
+
+        - Updated API tests to test sRGBToHSL() rather than Color::getHSL() and extended the tests
+          to include lightness tests and round tripping tests.
+        - Update editing/pasteboard/paste-dark-mode-color-filtered.html with extended color test cases.
+
+        Replaces Color::getHSL() with sRGBToHSL(color.toSRGBAComponentsLossy()) and adds
+        an optimized variant, lightness(...) that just extracts the lightness component for 
+        callers that only needed that.
+        
+        It is now required to explicitly use color.toSRGBAComponentsLossy() to indicate that
+        for non-SRGB colors, this will be a lossy transformation and give us an easy way to
+        find all these sites in the future, if we want to make a better conversion for other
+        color spaces. 
+
+        * editing/ReplaceSelectionCommand.cpp:
+        (WebCore::fragmentNeedsColorTransformed):
+        Switch to using lightness(). This was previously broken for extended colors but now works (though 
+        in a lossy way through sRGB). Update editing/pasteboard/paste-dark-mode-color-filtered.html with extended colors.
+
+        * editing/cocoa/DataDetection.mm:
+        (WebCore::DataDetection::detectContentInRange):
+        Switch to using sRGBToHSL(color.toSRGBAComponentsLossy()).
+        
+        * page/FrameView.cpp:
+        (WebCore::FrameView::recalculateScrollbarOverlayStyle):
+        Switch to using lightness().
+        
+        * platform/graphics/Color.cpp:
+        (WebCore::Color::getHSL const): Deleted.
+        * platform/graphics/Color.h:
+        Remove Color::getHSL().
+        
+        * platform/graphics/ColorUtilities.cpp:
+        (WebCore::lightness):
+        Added optimized subset of sRGBToHSL which just computes the lightness component for callers
+        that only need that.
+
+        (WebCore::sRGBToHSL):
+        Simplify/cleanup code a little using initialize-list based std::max/std::min and structured bindings.
+
+        * platform/graphics/ColorUtilities.h:
+        Export functions to allow testing them directly.
+
 2020-05-20  Megan Gardner  <megan_gardner@apple.com>
 
         Hide password echo when screen is being captured.
index 73863be..f82f395 100644 (file)
@@ -547,9 +547,7 @@ static bool fragmentNeedsColorTransformed(ReplacementFragment& fragment, const P
         if (!color || !color.value().isVisible() || color.value().isSemantic())
             return { };
 
-        double hue, saturation, lightness;
-        color.value().getHSL(hue, saturation, lightness);
-        return lightness;
+        return color.value().lightness();
     };
 
     const double lightnessDarkEnoughForText = 0.4;
index dce04e9..34db958 100644 (file)
@@ -619,14 +619,13 @@ NSArray *DataDetection::detectContentInRange(const SimpleRange& contextRange, Da
                 if (renderStyle) {
                     auto textColor = renderStyle->visitedDependentColor(CSSPropertyColor);
                     if (textColor.isValid()) {
-                        double hue, saturation, lightness;
-                        textColor.getHSL(hue, saturation, lightness);
+                        auto [hue, saturation, lightness, alpha] = sRGBToHSL(textColor.toSRGBAComponentsLossy());
 
                         // Force the lightness of the underline color to the middle, and multiply the alpha by 38%,
                         // so the color will appear on light and dark backgrounds, since only one color can be specified.
                         double overrideLightness = 0.5;
                         double overrideAlphaMultiplier = 0.38;
-                        auto underlineColor = Color(makeRGBAFromHSLA(hue, saturation, overrideLightness, overrideAlphaMultiplier * textColor.alphaAsFloat()));
+                        auto underlineColor = Color(makeRGBAFromHSLA(hue, saturation, overrideLightness, overrideAlphaMultiplier * alpha));
 
                         anchorElement->setInlineStyleProperty(CSSPropertyColor, CSSValueCurrentcolor);
                         anchorElement->setInlineStyleProperty(CSSPropertyTextDecorationColor, underlineColor.cssText());
index 86ffc9a..8936605 100644 (file)
@@ -365,9 +365,7 @@ void FrameView::recalculateScrollbarOverlayStyle()
         // Reduce the background color from RGB to a lightness value
         // and determine which scrollbar style to use based on a lightness
         // heuristic.
-        double hue, saturation, lightness;
-        backgroundColor.getHSL(hue, saturation, lightness);
-        if (lightness <= .5 && backgroundColor.isVisible())
+        if (backgroundColor.lightness() <= .5f && backgroundColor.isVisible())
             computedOverlayStyle = ScrollbarOverlayStyleLight;
         else if (!backgroundColor.isVisible() && useDarkAppearance())
             computedOverlayStyle = ScrollbarOverlayStyleLight;
index 324edf0..a9b1dfb 100644 (file)
@@ -394,6 +394,12 @@ bool Color::isDark() const
     return a > 0.5 && largestNonAlphaChannel < 0.5;
 }
 
+float Color::lightness() const
+{
+    // FIXME: This can probably avoid conversion to sRGB by having per-colorspace algorithms for HSL.
+    return WebCore::lightness(toSRGBAComponentsLossy());
+}
+
 static int blendComponent(int c, int a)
 {
     // We use white.
@@ -467,43 +473,6 @@ Color Color::colorWithAlpha(float alpha) const
     return result;
 }
 
-// FIXME: Use sRGBToHSL().
-void Color::getHSL(double& hue, double& saturation, double& lightness) const
-{
-    // http://en.wikipedia.org/wiki/HSL_color_space. This is a direct copy of
-    // the algorithm therein, although it's 360^o based and we end up wanting
-    // [0...6) based. It's clearer if we stick to 360^o until the end.
-    double r = static_cast<double>(red()) / 255.0;
-    double g = static_cast<double>(green()) / 255.0;
-    double b = static_cast<double>(blue()) / 255.0;
-    double max = std::max(std::max(r, g), b);
-    double min = std::min(std::min(r, g), b);
-    double chroma = max - min;
-
-    if (!chroma)
-        hue = 0.0;
-    else if (max == r)
-        hue = (60.0 * ((g - b) / chroma)) + 360.0;
-    else if (max == g)
-        hue = (60.0 * ((b - r) / chroma)) + 120.0;
-    else
-        hue = (60.0 * ((r - g) / chroma)) + 240.0;
-
-    if (hue >= 360.0)
-        hue -= 360.0;
-
-    // makeRGBAFromHSLA assumes that hue is in [0...6).
-    hue /= 60.0;
-
-    lightness = 0.5 * (max + min);
-    if (!chroma)
-        saturation = 0.0;
-    else if (lightness <= 0.5)
-        saturation = (chroma / (max + min));
-    else
-        saturation = (chroma / (2.0 - (max + min)));
-}
-
 std::pair<ColorSpace, FloatComponents> Color::colorSpaceAndComponents() const
 {
     if (isExtended()) {
index 37f42f4..43168d1 100644 (file)
@@ -96,7 +96,7 @@ WEBCORE_EXPORT RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha);
 RGBA32 colorWithOverrideAlpha(RGBA32 color, Optional<float> overrideAlpha);
 
 WEBCORE_EXPORT RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a);
-RGBA32 makeRGBAFromHSLA(float h, float s, float l, float a);
+WEBCORE_EXPORT RGBA32 makeRGBAFromHSLA(float h, float s, float l, float a);
 RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a);
 
 uint8_t roundAndClampColorChannel(int);
@@ -207,11 +207,6 @@ public:
 
     unsigned hash() const;
 
-    // FIXME: ExtendedColor - these should be renamed (to be clear about their parameter types, or
-    // replaced with alternative accessors.
-
-    WEBCORE_EXPORT void getHSL(double& h, double& s, double& l) const;
-
     WEBCORE_EXPORT std::pair<ColorSpace, FloatComponents> colorSpaceAndComponents() const;
 
     // This will convert non-sRGB colorspace colors into sRGB.
@@ -221,6 +216,8 @@ public:
     Color dark() const;
 
     bool isDark() const;
+    
+    WEBCORE_EXPORT float lightness() const;
 
     // This is an implementation of Porter-Duff's "source-over" equation
     Color blend(const Color&) const;
index abdd021..97c41ce 100644 (file)
@@ -153,6 +153,16 @@ FloatComponents sRGBToP3(const FloatComponents& sRGB)
     return linearToRGBComponents(linearP3);
 }
 
+float lightness(const FloatComponents& sRGBCompontents)
+{
+    auto [r, g, b, a] = sRGBCompontents;
+
+    float max = std::max({ r, g, b });
+    float min = std::min({ r, g, b });
+
+    return 0.5f * (max + min);
+}
+
 // This is similar to sRGBToLinearColorComponent but for some reason
 // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
 // doesn't use the standard sRGB -> linearRGB threshold of 0.04045.
@@ -185,15 +195,13 @@ float contrastRatio(const FloatComponents& componentsA, const FloatComponents& c
     return (lighterLuminance + 0.05) / (darkerLuminance + 0.05);
 }
 
-FloatComponents sRGBToHSL(const FloatComponents& sRGBColor)
+FloatComponents sRGBToHSL(const FloatComponents& sRGBCompontents)
 {
     // http://en.wikipedia.org/wiki/HSL_color_space.
-    float r = sRGBColor.components[0];
-    float g = sRGBColor.components[1];
-    float b = sRGBColor.components[2];
+    auto [r, g, b, alpha] = sRGBCompontents;
 
-    float max = std::max(std::max(r, g), b);
-    float min = std::min(std::min(r, g), b);
+    float max = std::max({ r, g, b });
+    float min = std::min({ r, g, b });
     float chroma = max - min;
 
     float hue;
@@ -224,7 +232,7 @@ FloatComponents sRGBToHSL(const FloatComponents& sRGBColor)
         hue,
         saturation,
         lightness,
-        sRGBColor.components[3]
+        alpha
     };
 }
 
index fa5a745..a49b755 100644 (file)
@@ -184,9 +184,10 @@ FloatComponents linearToRGBComponents(const FloatComponents&);
 FloatComponents p3ToSRGB(const FloatComponents&);
 FloatComponents sRGBToP3(const FloatComponents&);
 
-FloatComponents sRGBToHSL(const FloatComponents&);
-FloatComponents hslToSRGB(const FloatComponents&);
+WEBCORE_EXPORT FloatComponents sRGBToHSL(const FloatComponents&);
+WEBCORE_EXPORT FloatComponents hslToSRGB(const FloatComponents&);
 
+float lightness(const FloatComponents& sRGBCompontents);
 float luminance(const FloatComponents& sRGBCompontents);
 float contrastRatio(const FloatComponents&, const FloatComponents&);
 
index cee20cc..69b6d0f 100644 (file)
@@ -1,3 +1,14 @@
+2020-05-20  Sam Weinig  <weinig@apple.com>
+
+        Replace Color::getHSL() with sRGBToHSL to ensure it at least gives somewhat sensible results for ExtendedColors and reduce code duplication
+        https://bugs.webkit.org/show_bug.cgi?id=212143
+
+        Reviewed by Simon Fraser.
+
+        * UIProcess/API/ios/WKWebViewIOS.mm:
+        (-[WKWebView _updateScrollViewBackground]):
+        Switch to using Color::lightness().
+
 2020-05-20  Megan Gardner  <megan_gardner@apple.com>
 
         Hide password echo when screen is being captured.
index d165353..f820b20 100644 (file)
@@ -532,9 +532,7 @@ static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
     [_scrollView setBackgroundColor:uiBackgroundColor.get()];
 
     // Update the indicator style based on the lightness/darkness of the background color.
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
-    if (lightness <= .5 && color.isVisible())
+    if (color.lightness() <= .5f && color.isVisible())
         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
     else
         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleBlack];
index 5016e55..659d92f 100644 (file)
@@ -1,3 +1,15 @@
+2020-05-20  Sam Weinig  <weinig@apple.com>
+
+        Replace Color::getHSL() with sRGBToHSL to ensure it at least gives somewhat sensible results for ExtendedColors and reduce code duplication
+        https://bugs.webkit.org/show_bug.cgi?id=212143
+
+        Reviewed by Simon Fraser.
+
+        * TestWebKitAPI/Tests/WebCore/ColorTests.cpp:
+        (TestWebKitAPI::TEST):
+        Update tests to use sRGBToHSL instead of Color::getHSL() and add testing of WebCore::lightness() and 
+        round tripping. Update results for hue to be 0->1 rather than 0->6 which allows the round tripping to succeed.
+
 2020-05-20  ChangSeok Oh  <changseok@webkit.org>
 
         [GTK] Implement connected and disconnected events of GAMEPAD API with libmanette
index 76645df..6158c0b 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "Test.h"
 #include <WebCore/Color.h>
+#include <WebCore/ColorUtilities.h>
 
 using namespace WebCore;
 
@@ -36,96 +37,128 @@ TEST(Color, RGBToHSL_White)
 {
     Color color = Color::white;
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
 
-    EXPECT_DOUBLE_EQ(0, hue);
-    EXPECT_DOUBLE_EQ(0, saturation);
-    EXPECT_DOUBLE_EQ(1, lightness);
+    EXPECT_FLOAT_EQ(0, hue);
+    EXPECT_FLOAT_EQ(0, saturation);
+    EXPECT_FLOAT_EQ(1, lightness);
+    
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
+    
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, RGBToHSL_Black)
 {
     Color color = Color::black;
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
 
-    EXPECT_DOUBLE_EQ(0, hue);
-    EXPECT_DOUBLE_EQ(0, saturation);
-    EXPECT_DOUBLE_EQ(0, lightness);
+    EXPECT_FLOAT_EQ(0, hue);
+    EXPECT_FLOAT_EQ(0, saturation);
+    EXPECT_FLOAT_EQ(0, lightness);
+
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
+
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, RGBToHSL_Red)
 {
     Color color(255, 0, 0);
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
+
+    EXPECT_FLOAT_EQ(0, hue);
+    EXPECT_FLOAT_EQ(1, saturation);
+    EXPECT_FLOAT_EQ(0.5, lightness);
+
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
 
-    EXPECT_DOUBLE_EQ(0, hue);
-    EXPECT_DOUBLE_EQ(1, saturation);
-    EXPECT_DOUBLE_EQ(0.5, lightness);
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, RGBToHSL_Green)
 {
     Color color(0, 255, 0);
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
 
-    EXPECT_DOUBLE_EQ(2, hue);
-    EXPECT_DOUBLE_EQ(1, saturation);
-    EXPECT_DOUBLE_EQ(0.5, lightness);
+    EXPECT_FLOAT_EQ(0.33333334, hue);
+    EXPECT_FLOAT_EQ(1, saturation);
+    EXPECT_FLOAT_EQ(0.5, lightness);
+
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
+
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, RGBToHSL_Blue)
 {
     Color color(0, 0, 255);
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
+
+    EXPECT_FLOAT_EQ(0.66666669, hue);
+    EXPECT_FLOAT_EQ(1, saturation);
+    EXPECT_FLOAT_EQ(0.5, lightness);
 
-    EXPECT_DOUBLE_EQ(4, hue);
-    EXPECT_DOUBLE_EQ(1, saturation);
-    EXPECT_DOUBLE_EQ(0.5, lightness);
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
+
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, RGBToHSL_DarkGray)
 {
     Color color = Color::darkGray;
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
+
+    EXPECT_FLOAT_EQ(0, hue);
+    EXPECT_FLOAT_EQ(0, saturation);
+    EXPECT_FLOAT_EQ(0.50196078431372548, lightness);
+    
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
 
-    EXPECT_DOUBLE_EQ(0, hue);
-    EXPECT_DOUBLE_EQ(0, saturation);
-    EXPECT_DOUBLE_EQ(0.50196078431372548, lightness);
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, RGBToHSL_Gray)
 {
     Color color = Color::gray;
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
+
+    EXPECT_FLOAT_EQ(0, hue);
+    EXPECT_FLOAT_EQ(0, saturation);
+    EXPECT_FLOAT_EQ(0.62745098039215685, lightness);
 
-    EXPECT_DOUBLE_EQ(0, hue);
-    EXPECT_DOUBLE_EQ(0, saturation);
-    EXPECT_DOUBLE_EQ(0.62745098039215685, lightness);
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
+
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, RGBToHSL_LightGray)
 {
     Color color = Color::lightGray;
 
-    double hue, saturation, lightness;
-    color.getHSL(hue, saturation, lightness);
+    auto [hue, saturation, lightness, alpha] = sRGBToHSL(color.toSRGBAComponentsLossy());
+
+    EXPECT_FLOAT_EQ(0, hue);
+    EXPECT_FLOAT_EQ(0, saturation);
+    EXPECT_FLOAT_EQ(0.75294117647058822, lightness);
+
+    EXPECT_FLOAT_EQ(color.lightness(), lightness);
 
-    EXPECT_DOUBLE_EQ(0, hue);
-    EXPECT_DOUBLE_EQ(0, saturation);
-    EXPECT_DOUBLE_EQ(0.75294117647058822, lightness);
+    auto roundTrippedColor = Color(makeRGBAFromHSLA(hue, saturation, lightness, alpha));
+    EXPECT_EQ(color, roundTrippedColor);
 }
 
 TEST(Color, Validity)