daf3c94acc4df0437ab99a5952c21d9e1bfee271
[WebKit-https.git] / Source / WebCore / platform / graphics / ColorUtilities.cpp
1 /*
2  * Copyright (C) 2017, 2020 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ColorUtilities.h"
28
29 #include "ColorComponents.h"
30 #include "ColorMatrix.h"
31
32 namespace WebCore {
33
34 bool areEssentiallyEqual(const ColorComponents<float>& a, const ColorComponents<float>& b)
35 {
36     return WTF::areEssentiallyEqual(a.components[0], b.components[0])
37         && WTF::areEssentiallyEqual(a.components[1], b.components[1])
38         && WTF::areEssentiallyEqual(a.components[2], b.components[2])
39         && WTF::areEssentiallyEqual(a.components[3], b.components[3]);
40 }
41
42 // These are the standard sRGB <-> linearRGB conversion functions (https://en.wikipedia.org/wiki/SRGB).
43 float linearToRGBColorComponent(float c)
44 {
45     if (c < 0.0031308f)
46         return 12.92f * c;
47
48     return clampTo<float>(1.055f * std::pow(c, 1.0f / 2.4f) - 0.055f, 0, 1);
49 }
50
51 float rgbToLinearColorComponent(float c)
52 {
53     if (c <= 0.04045f)
54         return c / 12.92f;
55
56     return clampTo<float>(std::pow((c + 0.055f) / 1.055f, 2.4f), 0, 1);
57 }
58
59 ColorComponents<float> rgbToLinearComponents(const ColorComponents<float>& RGBColor)
60 {
61     return {
62         rgbToLinearColorComponent(RGBColor.components[0]),
63         rgbToLinearColorComponent(RGBColor.components[1]),
64         rgbToLinearColorComponent(RGBColor.components[2]),
65         RGBColor.components[3]
66     };
67 }
68
69 ColorComponents<float> linearToRGBComponents(const ColorComponents<float>& linearRGB)
70 {
71     return {
72         linearToRGBColorComponent(linearRGB.components[0]),
73         linearToRGBColorComponent(linearRGB.components[1]),
74         linearToRGBColorComponent(linearRGB.components[2]),
75         linearRGB.components[3]
76     };
77 }
78
79 static ColorComponents<float> xyzToLinearSRGB(const ColorComponents<float>& XYZComponents)
80 {
81     // https://en.wikipedia.org/wiki/SRGB
82     // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
83     const float values[] = {
84          3.2404542f, -1.5371385f, -0.4985314f, 0.0f, 0.0f,
85         -0.9692660f,  1.8760108f,  0.0415560f, 0.0f, 0.0f,
86          0.0556434f, -0.2040259f,  1.0572252f, 0.0f, 0.0f,
87          0.0f,        0.0f,        0.0f,       1.0f, 0.0f,
88     };
89     ColorMatrix xyzToLinearSRGBMatrix(values);
90     return xyzToLinearSRGBMatrix.transformedColorComponents(XYZComponents);
91 }
92
93 static ColorComponents<float> linearSRGBToXYZ(const ColorComponents<float>& XYZComponents)
94 {
95     // https://en.wikipedia.org/wiki/SRGB
96     // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
97     const float values[] = {
98         0.4124564f,  0.3575761f,  0.1804375f, 0.0f, 0.0f,
99         0.2126729f,  0.7151522f,  0.0721750f, 0.0f, 0.0f,
100         0.0193339f,  0.1191920f,  0.9503041f, 0.0f, 0.0f,
101         0.0f,        0.0f,        0.0f,       1.0f, 0.0f,
102     };
103     ColorMatrix linearSRGBToXYZMatrix(values);
104     return linearSRGBToXYZMatrix.transformedColorComponents(XYZComponents);
105 }
106
107 static ColorComponents<float> XYZToLinearP3(const ColorComponents<float>& XYZComponents)
108 {
109     // https://drafts.csswg.org/css-color/#color-conversion-code
110     const float values[] = {
111          2.493496911941425f,  -0.9313836179191239f, -0.4027107844507168f, 0.0f, 0.0f,
112         -0.8294889695615747f,  1.7626640603183463f,  0.0236246858419436f, 0.0f, 0.0f,
113          0.0358458302437845f, -0.0761723892680418f,  0.9568845240076872f, 0.0f, 0.0f,
114          0.0f,                 0.0f,                 0.0f,                1.0f, 0.0f,
115     };
116     ColorMatrix xyzToLinearSRGBMatrix(values);
117     return xyzToLinearSRGBMatrix.transformedColorComponents(XYZComponents);
118 }
119
120 static ColorComponents<float> linearP3ToXYZ(const ColorComponents<float>& XYZComponents)
121 {
122     // https://drafts.csswg.org/css-color/#color-conversion-code
123     const float values[] = {
124         0.4865709486482162f, 0.2656676931690931f, 0.198217285234363f, 0.0f, 0.0f,
125         0.2289745640697488f, 0.6917385218365064f, 0.079286914093745f, 0.0f, 0.0f,
126         0.0f,                0.0451133818589026f, 1.043944368900976f, 0.0f, 0.0f,
127         0.0f,                0.0f,                0.0f,               1.0f, 0.0f,
128     };
129     ColorMatrix linearP3ToXYZMatrix(values);
130     return linearP3ToXYZMatrix.transformedColorComponents(XYZComponents);
131 }
132
133 ColorComponents<float> p3ToSRGB(const ColorComponents<float>& p3)
134 {
135     auto linearP3 = rgbToLinearComponents(p3);
136     auto xyz = linearP3ToXYZ(linearP3);
137     auto linearSRGB = xyzToLinearSRGB(xyz);
138     return linearToRGBComponents(linearSRGB);
139 }
140
141 ColorComponents<float> sRGBToP3(const ColorComponents<float>& sRGB)
142 {
143     auto linearSRGB = rgbToLinearComponents(sRGB);
144     auto xyz = linearSRGBToXYZ(linearSRGB);
145     auto linearP3 = XYZToLinearP3(xyz);
146     return linearToRGBComponents(linearP3);
147 }
148
149 float lightness(const ColorComponents<float>& sRGBCompontents)
150 {
151     auto [r, g, b, a] = sRGBCompontents;
152
153     float max = std::max({ r, g, b });
154     float min = std::min({ r, g, b });
155
156     return 0.5f * (max + min);
157 }
158
159 // This is similar to sRGBToLinearColorComponent but for some reason
160 // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
161 // doesn't use the standard sRGB -> linearRGB threshold of 0.04045.
162 static float sRGBToLinearColorComponentForLuminance(float c)
163 {
164     if (c <= 0.03928f)
165         return c / 12.92f;
166
167     return clampTo<float>(std::pow((c + 0.055f) / 1.055f, 2.4f), 0, 1);
168 }
169
170 float luminance(const ColorComponents<float>& sRGBComponents)
171 {
172     // Values from https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
173     return 0.2126f * sRGBToLinearColorComponentForLuminance(sRGBComponents.components[0])
174         + 0.7152f * sRGBToLinearColorComponentForLuminance(sRGBComponents.components[1])
175         + 0.0722f * sRGBToLinearColorComponentForLuminance(sRGBComponents.components[2]);
176 }
177
178 float contrastRatio(const ColorComponents<float>& componentsA, const ColorComponents<float>& componentsB)
179 {
180     // Uses the WCAG 2.0 definition of contrast ratio.
181     // https://www.w3.org/TR/WCAG20/#contrast-ratiodef
182     float lighterLuminance = luminance(componentsA);
183     float darkerLuminance = luminance(componentsB);
184
185     if (lighterLuminance < darkerLuminance)
186         std::swap(lighterLuminance, darkerLuminance);
187
188     return (lighterLuminance + 0.05) / (darkerLuminance + 0.05);
189 }
190
191 ColorComponents<float> sRGBToHSL(const ColorComponents<float>& sRGBCompontents)
192 {
193     // http://en.wikipedia.org/wiki/HSL_color_space.
194     auto [r, g, b, alpha] = sRGBCompontents;
195
196     float max = std::max({ r, g, b });
197     float min = std::min({ r, g, b });
198     float chroma = max - min;
199
200     float hue;
201     if (!chroma)
202         hue = 0;
203     else if (max == r)
204         hue = (60.0f * ((g - b) / chroma)) + 360.0f;
205     else if (max == g)
206         hue = (60.0f * ((b - r) / chroma)) + 120.0f;
207     else
208         hue = (60.0f * ((r - g) / chroma)) + 240.0f;
209
210     if (hue >= 360.0f)
211         hue -= 360.0f;
212
213     hue /= 360.0f;
214
215     float lightness = 0.5f * (max + min);
216     float saturation;
217     if (!chroma)
218         saturation = 0;
219     else if (lightness <= 0.5f)
220         saturation = (chroma / (max + min));
221     else
222         saturation = (chroma / (2.0f - (max + min)));
223
224     return {
225         hue,
226         saturation,
227         lightness,
228         alpha
229     };
230 }
231
232 // Hue is in the range 0-6, other args in 0-1.
233 static float calcHue(float temp1, float temp2, float hueVal)
234 {
235     if (hueVal < 0.0f)
236         hueVal += 6.0f;
237     else if (hueVal >= 6.0f)
238         hueVal -= 6.0f;
239     if (hueVal < 1.0f)
240         return temp1 + (temp2 - temp1) * hueVal;
241     if (hueVal < 3.0f)
242         return temp2;
243     if (hueVal < 4.0f)
244         return temp1 + (temp2 - temp1) * (4.0f - hueVal);
245     return temp1;
246 }
247
248 // Explanation of this algorithm can be found in the CSS Color 4 Module
249 // specification at https://drafts.csswg.org/css-color-4/#hsl-to-rgb with
250 // further explanation available at http://en.wikipedia.org/wiki/HSL_color_space
251 ColorComponents<float> hslToSRGB(const ColorComponents<float>& hslColor)
252 {
253     float hue = hslColor.components[0];
254     float saturation = hslColor.components[1];
255     float lightness = hslColor.components[2];
256
257     // Convert back to RGB.
258     if (!saturation) {
259         return {
260             lightness,
261             lightness,
262             lightness,
263             hslColor.components[3]
264         };
265     }
266     
267     float temp2 = lightness <= 0.5f ? lightness * (1.0f + saturation) : lightness + saturation - lightness * saturation;
268     float temp1 = 2.0f * lightness - temp2;
269     
270     hue *= 6.0f; // calcHue() wants hue in the 0-6 range.
271     return {
272         calcHue(temp1, temp2, hue + 2.0f),
273         calcHue(temp1, temp2, hue),
274         calcHue(temp1, temp2, hue - 2.0f),
275         hslColor.components[3]
276     };
277 }
278
279 ColorComponents<float> premultiplied(const ColorComponents<float>& sRGBComponents)
280 {
281     auto [r, g, b, a] = sRGBComponents;
282     return {
283         r * a,
284         g * a,
285         b * a,
286         a
287     };
288 }
289
290 } // namespace WebCore