Extended Color: Move ColorMatrix to its own files
[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 "ColorMatrix.h"
30 #include <wtf/MathExtras.h>
31
32 namespace WebCore {
33
34 ColorComponents::ColorComponents(const FloatComponents& floatComponents)
35 {
36     components[0] = clampedColorComponent(floatComponents.components[0]);
37     components[1] = clampedColorComponent(floatComponents.components[1]);
38     components[2] = clampedColorComponent(floatComponents.components[2]);
39     components[3] = clampedColorComponent(floatComponents.components[3]);
40 }
41
42 bool areEssentiallyEqual(const FloatComponents& a, const FloatComponents& b)
43 {
44     return WTF::areEssentiallyEqual(a.components[0], b.components[0])
45         && WTF::areEssentiallyEqual(a.components[1], b.components[1])
46         && WTF::areEssentiallyEqual(a.components[2], b.components[2])
47         && WTF::areEssentiallyEqual(a.components[3], b.components[3]);
48 }
49
50 // These are the standard sRGB <-> linearRGB conversion functions (https://en.wikipedia.org/wiki/SRGB).
51 float linearToRGBColorComponent(float c)
52 {
53     if (c < 0.0031308f)
54         return 12.92f * c;
55
56     return clampTo<float>(1.055f * std::pow(c, 1.0f / 2.4f) - 0.055f, 0, 1);
57 }
58
59 float rgbToLinearColorComponent(float c)
60 {
61     if (c <= 0.04045f)
62         return c / 12.92f;
63
64     return clampTo<float>(std::pow((c + 0.055f) / 1.055f, 2.4f), 0, 1);
65 }
66
67 FloatComponents rgbToLinearComponents(const FloatComponents& RGBColor)
68 {
69     return {
70         rgbToLinearColorComponent(RGBColor.components[0]),
71         rgbToLinearColorComponent(RGBColor.components[1]),
72         rgbToLinearColorComponent(RGBColor.components[2]),
73         RGBColor.components[3]
74     };
75 }
76
77 FloatComponents linearToRGBComponents(const FloatComponents& linearRGB)
78 {
79     return {
80         linearToRGBColorComponent(linearRGB.components[0]),
81         linearToRGBColorComponent(linearRGB.components[1]),
82         linearToRGBColorComponent(linearRGB.components[2]),
83         linearRGB.components[3]
84     };
85 }
86
87 static FloatComponents xyzToLinearSRGB(const FloatComponents& XYZComponents)
88 {
89     // https://en.wikipedia.org/wiki/SRGB
90     // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
91     const float values[] = {
92          3.2404542f, -1.5371385f, -0.4985314f, 0.0f, 0.0f,
93         -0.9692660f,  1.8760108f,  0.0415560f, 0.0f, 0.0f,
94          0.0556434f, -0.2040259f,  1.0572252f, 0.0f, 0.0f,
95          0.0f,        0.0f,        0.0f,       1.0f, 0.0f,
96     };
97     ColorMatrix xyzToLinearSRGBMatrix(values);
98     return xyzToLinearSRGBMatrix.transformedColorComponents(XYZComponents);
99 }
100
101 static FloatComponents linearSRGBToXYZ(const FloatComponents& XYZComponents)
102 {
103     // https://en.wikipedia.org/wiki/SRGB
104     // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
105     const float values[] = {
106         0.4124564f,  0.3575761f,  0.1804375f, 0.0f, 0.0f,
107         0.2126729f,  0.7151522f,  0.0721750f, 0.0f, 0.0f,
108         0.0193339f,  0.1191920f,  0.9503041f, 0.0f, 0.0f,
109         0.0f,        0.0f,        0.0f,       1.0f, 0.0f,
110     };
111     ColorMatrix linearSRGBToXYZMatrix(values);
112     return linearSRGBToXYZMatrix.transformedColorComponents(XYZComponents);
113 }
114
115 static FloatComponents XYZToLinearP3(const FloatComponents& XYZComponents)
116 {
117     // https://drafts.csswg.org/css-color/#color-conversion-code
118     const float values[] = {
119          2.493496911941425f,  -0.9313836179191239f, -0.4027107844507168f, 0.0f, 0.0f,
120         -0.8294889695615747f,  1.7626640603183463f,  0.0236246858419436f, 0.0f, 0.0f,
121          0.0358458302437845f, -0.0761723892680418f,  0.9568845240076872f, 0.0f, 0.0f,
122          0.0f,                 0.0f,                 0.0f,                1.0f, 0.0f,
123     };
124     ColorMatrix xyzToLinearSRGBMatrix(values);
125     return xyzToLinearSRGBMatrix.transformedColorComponents(XYZComponents);
126 }
127
128 static FloatComponents linearP3ToXYZ(const FloatComponents& XYZComponents)
129 {
130     // https://drafts.csswg.org/css-color/#color-conversion-code
131     const float values[] = {
132         0.4865709486482162f, 0.2656676931690931f, 0.198217285234363f, 0.0f, 0.0f,
133         0.2289745640697488f, 0.6917385218365064f, 0.079286914093745f, 0.0f, 0.0f,
134         0.0f,                0.0451133818589026f, 1.043944368900976f, 0.0f, 0.0f,
135         0.0f,                0.0f,                0.0f,               1.0f, 0.0f,
136     };
137     ColorMatrix linearP3ToXYZMatrix(values);
138     return linearP3ToXYZMatrix.transformedColorComponents(XYZComponents);
139 }
140
141 FloatComponents p3ToSRGB(const FloatComponents& p3)
142 {
143     auto linearP3 = rgbToLinearComponents(p3);
144     auto xyz = linearP3ToXYZ(linearP3);
145     auto linearSRGB = xyzToLinearSRGB(xyz);
146     return linearToRGBComponents(linearSRGB);
147 }
148
149 FloatComponents sRGBToP3(const FloatComponents& sRGB)
150 {
151     auto linearSRGB = rgbToLinearComponents(sRGB);
152     auto xyz = linearSRGBToXYZ(linearSRGB);
153     auto linearP3 = XYZToLinearP3(xyz);
154     return linearToRGBComponents(linearP3);
155 }
156
157 float lightness(const FloatComponents& sRGBCompontents)
158 {
159     auto [r, g, b, a] = sRGBCompontents;
160
161     float max = std::max({ r, g, b });
162     float min = std::min({ r, g, b });
163
164     return 0.5f * (max + min);
165 }
166
167 // This is similar to sRGBToLinearColorComponent but for some reason
168 // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
169 // doesn't use the standard sRGB -> linearRGB threshold of 0.04045.
170 static float sRGBToLinearColorComponentForLuminance(float c)
171 {
172     if (c <= 0.03928f)
173         return c / 12.92f;
174
175     return clampTo<float>(std::pow((c + 0.055f) / 1.055f, 2.4f), 0, 1);
176 }
177
178 float luminance(const FloatComponents& sRGBComponents)
179 {
180     // Values from https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
181     return 0.2126f * sRGBToLinearColorComponentForLuminance(sRGBComponents.components[0])
182         + 0.7152f * sRGBToLinearColorComponentForLuminance(sRGBComponents.components[1])
183         + 0.0722f * sRGBToLinearColorComponentForLuminance(sRGBComponents.components[2]);
184 }
185
186 float contrastRatio(const FloatComponents& componentsA, const FloatComponents& componentsB)
187 {
188     // Uses the WCAG 2.0 definition of contrast ratio.
189     // https://www.w3.org/TR/WCAG20/#contrast-ratiodef
190     float lighterLuminance = luminance(componentsA);
191     float darkerLuminance = luminance(componentsB);
192
193     if (lighterLuminance < darkerLuminance)
194         std::swap(lighterLuminance, darkerLuminance);
195
196     return (lighterLuminance + 0.05) / (darkerLuminance + 0.05);
197 }
198
199 FloatComponents sRGBToHSL(const FloatComponents& sRGBCompontents)
200 {
201     // http://en.wikipedia.org/wiki/HSL_color_space.
202     auto [r, g, b, alpha] = sRGBCompontents;
203
204     float max = std::max({ r, g, b });
205     float min = std::min({ r, g, b });
206     float chroma = max - min;
207
208     float hue;
209     if (!chroma)
210         hue = 0;
211     else if (max == r)
212         hue = (60.0f * ((g - b) / chroma)) + 360.0f;
213     else if (max == g)
214         hue = (60.0f * ((b - r) / chroma)) + 120.0f;
215     else
216         hue = (60.0f * ((r - g) / chroma)) + 240.0f;
217
218     if (hue >= 360.0f)
219         hue -= 360.0f;
220
221     hue /= 360.0f;
222
223     float lightness = 0.5f * (max + min);
224     float saturation;
225     if (!chroma)
226         saturation = 0;
227     else if (lightness <= 0.5f)
228         saturation = (chroma / (max + min));
229     else
230         saturation = (chroma / (2.0f - (max + min)));
231
232     return {
233         hue,
234         saturation,
235         lightness,
236         alpha
237     };
238 }
239
240 // Hue is in the range 0-6, other args in 0-1.
241 static float calcHue(float temp1, float temp2, float hueVal)
242 {
243     if (hueVal < 0.0f)
244         hueVal += 6.0f;
245     else if (hueVal >= 6.0f)
246         hueVal -= 6.0f;
247     if (hueVal < 1.0f)
248         return temp1 + (temp2 - temp1) * hueVal;
249     if (hueVal < 3.0f)
250         return temp2;
251     if (hueVal < 4.0f)
252         return temp1 + (temp2 - temp1) * (4.0f - hueVal);
253     return temp1;
254 }
255
256 // Explanation of this algorithm can be found in the CSS Color 4 Module
257 // specification at https://drafts.csswg.org/css-color-4/#hsl-to-rgb with
258 // further explanation available at http://en.wikipedia.org/wiki/HSL_color_space
259 FloatComponents hslToSRGB(const FloatComponents& hslColor)
260 {
261     float hue = hslColor.components[0];
262     float saturation = hslColor.components[1];
263     float lightness = hslColor.components[2];
264
265     // Convert back to RGB.
266     if (!saturation) {
267         return {
268             lightness,
269             lightness,
270             lightness,
271             hslColor.components[3]
272         };
273     }
274     
275     float temp2 = lightness <= 0.5f ? lightness * (1.0f + saturation) : lightness + saturation - lightness * saturation;
276     float temp1 = 2.0f * lightness - temp2;
277     
278     hue *= 6.0f; // calcHue() wants hue in the 0-6 range.
279     return {
280         calcHue(temp1, temp2, hue + 2.0f),
281         calcHue(temp1, temp2, hue),
282         calcHue(temp1, temp2, hue - 2.0f),
283         hslColor.components[3]
284     };
285 }
286
287 FloatComponents premultiplied(const FloatComponents& sRGBComponents)
288 {
289     auto [r, g, b, a] = sRGBComponents;
290     return {
291         r * a,
292         g * a,
293         b * a,
294         a
295     };
296 }
297
298 } // namespace WebCore