[NUI] Add license, delete unnecessary code (#2679)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Utility / ColorUtils.cs
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 /*
19  * Copyright (C) 2017 The Android Open Source Project
20  *
21  * Modified by Woochan Lee(wc0917.lee@samsung.com)
22  */
23
24 using System;
25
26 namespace Tizen.NUI
27 {
28     internal sealed class ColorUtils
29     {
30         private const int minAlphaSearchMaxIterations = 10;
31         private const int minAlphaSearchPrecision = 1;
32
33         /// <summary>
34         /// Convert the ARGB color to its CIE XYZ representative components.
35         ///
36         /// The resulting XYZ representation will use the D65 illuminant and the CIE
37         /// 2° Standard Observer (1931).
38         ///
39         /// outXyz[0] is X [0 ...95.047)
40         /// outXyz[1] is Y [0...100)
41         /// outXyz[2] is Z [0...108.883)
42         ///
43         /// param color  the ARGB color to convert. The alpha component is ignored
44         /// param outXyz 3-element array which holds the resulting LAB components
45         /// </summary>
46         public static void ColorToXyz(int color, double[] outXyz)
47         {
48             System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color);
49             RgbToXyz(rgbColor.R, rgbColor.G, rgbColor.B, outXyz);
50         }
51
52         /// <summary>
53         /// Convert RGB components to its CIE XYZ representative components.
54         ///
55         /// The resulting XYZ representation will use the D65 illuminant and the CIE
56         /// 2° Standard Observer (1931).
57         ///
58         /// outXyz[0] is X [0 ...95.047)
59         /// outXyz[1] is Y [0...100)
60         /// outXyz[2] is Z [0...108.883)
61         ///
62         /// r      red component value [0..255]
63         /// g      green component value [0..255]
64         /// b      blue component value [0..255]
65         /// outXyz 3-element array which holds the resulting XYZ components
66         /// </summary>
67         public static void RgbToXyz(int red, int green, int blue, double[] outXyz)
68         {
69             if (outXyz.Length != 3)
70             {
71                 throw new ArgumentException("Array legnth must be 3", nameof(outXyz));
72             }
73
74             double floatRed = red / 255.0;
75             floatRed = floatRed < 0.04045 ? floatRed / 12.92 : Math.Pow((floatRed + 0.055) / 1.055, 2.4);
76             double floatGreen = green / 255.0;
77             floatGreen = floatGreen < 0.04045 ? floatGreen / 12.92 : Math.Pow((floatGreen + 0.055) / 1.055, 2.4);
78             double floatBlue = blue / 255.0;
79             floatBlue = floatBlue < 0.04045 ? floatBlue / 12.92 : Math.Pow((floatBlue + 0.055) / 1.055, 2.4);
80
81             outXyz[0] = 100 * (floatRed * 0.4124 + floatGreen * 0.3576 + floatBlue * 0.1805);
82             outXyz[1] = 100 * (floatRed * 0.2126 + floatGreen * 0.7152 + floatBlue * 0.0722);
83             outXyz[2] = 100 * (floatRed * 0.0193 + floatGreen * 0.1192 + floatBlue * 0.9505);
84         }
85
86         /// <summary>
87         /// Returns the luminance of a color as a float between 0.0 and 1.0
88         /// Defined as the Y component in the XYZ representation of color.
89         /// </summary>
90         public static double CalculateLuminance(int color)
91         {
92             double[] result = new double[3];
93             ColorToXyz(color, result);
94             // Luminance is the Y component
95             return result[1] / 100;
96         }
97
98         /// <summary>
99         /// Composite two potentially translucent colors over each other and returns the result.
100         /// </summary>
101         public static int CompositeColors(int foreground, int background)
102         {
103             System.Drawing.Color foreColor = System.Drawing.Color.FromArgb(foreground);
104             System.Drawing.Color backColor = System.Drawing.Color.FromArgb(background);
105             int bgAlpha = backColor.A;
106             int fgAlpha = foreColor.A;
107
108             int alpha = CompositeAlpha(fgAlpha, bgAlpha);
109
110             int red = CompositeComponent(foreColor.R, fgAlpha,
111                     backColor.R, bgAlpha, alpha);
112             int green = CompositeComponent(foreColor.G, fgAlpha,
113                     backColor.G, bgAlpha, alpha);
114             int blue = CompositeComponent(foreColor.B, fgAlpha,
115                     backColor.B, bgAlpha, alpha);
116
117             return ((alpha & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff));
118         }
119
120         /// <summary>
121         /// Returns the contrast ratio between foreground and background.
122         /// background must be opaque.
123         ///
124         /// Formula defined
125         /// <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>.
126         /// </summary>
127         public static double CalculateContrast(int foreground, int background)
128         {
129             if (((background >> 24) & 0xff) != 255)
130             {
131                 throw new ArgumentException("background can not be translucent.");
132             }
133             if (((foreground >> 24) & 0xff) < 255)
134             {
135                 System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(foreground);
136
137                 // If the foreground is translucent, composite the foreground over the background
138                 foreground = CompositeColors(foreground, background);
139                 rgbColor = System.Drawing.Color.FromArgb(foreground);
140             }
141
142             double luminance1 = CalculateLuminance(foreground) + 0.05;
143             double luminance2 = CalculateLuminance(background) + 0.05;
144
145             // Now return the lighter luminance divided by the darker luminance
146             return Math.Max(luminance1, luminance2) / Math.Min(luminance1, luminance2);
147         }
148
149         /// <summary>
150         /// Set the alpha component of color to be alpha.
151         /// </summary>
152         public static int SetAlphaComponent(int color, int alpha)
153         {
154             if (alpha < 0 || alpha > 255)
155             {
156                 throw new ArgumentException("alpha must be between 0 and 255.");
157             }
158
159             return (color & 0x00ffffff) | (alpha << 24);
160         }
161
162         /// <summary>
163         /// Calculates the minimum alpha value which can be applied to foreground so that would
164         /// have a contrast value of at least minContrastRatio when compared to
165         /// background.
166         ///
167         /// param foreground       the foreground color
168         /// param background       the opaque background color
169         /// param minContrastRatio the minimum contrast ratio
170         /// return the alpha value in the range 0-255, or -1 if no value could be calculated
171         /// </summary>
172         public static int CalculateMinimumAlpha(int foreground, int background,
173                 float minContrastRatio)
174         {
175             if (((background >> 24) & 0xff) != 255)
176             {
177                 throw new ArgumentException("background can not be translucent");
178             }
179
180             // First lets check that a fully opaque foreground has sufficient contrast
181             int testForeground = SetAlphaComponent(foreground, 255);
182             System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(testForeground);
183             double testRatio = CalculateContrast(testForeground, background);
184
185             if (testRatio < minContrastRatio)
186             {
187                 // Fully opaque foreground does not have sufficient contrast, return error
188                 return -1;
189             }
190
191             // Binary search to find a value with the minimum value which provides sufficient contrast
192             int numIterations = 0;
193             int minAlpha = 0;
194             int maxAlpha = 255;
195
196             while (numIterations <= minAlphaSearchMaxIterations &&
197                     (maxAlpha - minAlpha) > minAlphaSearchPrecision)
198             {
199                 int testAlpha = (minAlpha + maxAlpha) / 2;
200
201                 testForeground = SetAlphaComponent(foreground, testAlpha);
202                 rgbColor = System.Drawing.Color.FromArgb(testForeground);
203                 testRatio = CalculateContrast(testForeground, background);
204                 if (testRatio < minContrastRatio)
205                 {
206                     minAlpha = testAlpha;
207                 }
208                 else
209                 {
210                     maxAlpha = testAlpha;
211                 }
212
213                 numIterations++;
214             }
215
216             // Conservatively return the max of the range of possible alphas, which is known to pass.
217             return maxAlpha;
218         }
219
220         public static void RgbToHsl(int red, int green, int blue, float[] hsl)
221         {
222             float floatRed = red / 255f;
223             float floatGreen = green / 255f;
224             float floatBlue = blue / 255f;
225             float max = Math.Max(floatRed, Math.Max(floatGreen, floatBlue));
226             float min = Math.Min(floatRed, Math.Min(floatGreen, floatBlue));
227             float deltaMaxMin = max - min;
228             float hue, saturation;
229             float lightness = (max + min) / 2f;
230             if (max == min)
231             {
232                 // Monochromatic
233                 hue = saturation = 0f;
234             }
235             else
236             {
237                 if (max == floatRed)
238                 {
239                     hue = ((floatGreen - floatBlue) / deltaMaxMin) % 6f;
240                 }
241                 else if (max == floatGreen)
242                 {
243                     hue = ((floatBlue - floatRed) / deltaMaxMin) + 2f;
244                 }
245                 else
246                 {
247                     hue = ((floatRed - floatGreen) / deltaMaxMin) + 4f;
248                 }
249                 saturation = deltaMaxMin / (1f - Math.Abs(2f * lightness - 1f));
250             }
251             hsl[0] = ((hue * 60f) + 360f) % 360f;
252             hsl[1] = saturation;
253             hsl[2] = lightness;
254         }
255
256         public static int HslToRgb(float[] hsl)
257         {
258             float hue = hsl[0];
259             float saturation = hsl[1];
260             float lightness = hsl[2];
261             float constC = (1f - Math.Abs(2 * lightness - 1f)) * saturation;
262             float constM = lightness - 0.5f * constC;
263             float constX = constC * (1f - Math.Abs((hue / 60f % 2f) - 1f));
264             int hueSegment = (int)hue / 60;
265             int red = 0, green = 0, blue = 0;
266             switch (hueSegment)
267             {
268                 case 0:
269                     red = (int)Math.Round(255 * (constC + constM));
270                     green = (int)Math.Round(255 * (constX + constM));
271                     blue = (int)Math.Round(255 * constM);
272                     break;
273                 case 1:
274                     red = (int)Math.Round(255 * (constX + constM));
275                     green = (int)Math.Round(255 * (constC + constM));
276                     blue = (int)Math.Round(255 * constM);
277                     break;
278                 case 2:
279                     red = (int)Math.Round(255 * constM);
280                     green = (int)Math.Round(255 * (constC + constM));
281                     blue = (int)Math.Round(255 * (constX + constM));
282                     break;
283                 case 3:
284                     red = (int)Math.Round(255 * constM);
285                     green = (int)Math.Round(255 * (constX + constM));
286                     blue = (int)Math.Round(255 * (constC + constM));
287                     break;
288                 case 4:
289                     red = (int)Math.Round(255 * (constX + constM));
290                     green = (int)Math.Round(255 * constM);
291                     blue = (int)Math.Round(255 * (constC + constM));
292                     break;
293                 case 5:
294                 case 6:
295                     red = (int)Math.Round(255 * (constC + constM));
296                     green = (int)Math.Round(255 * constM);
297                     blue = (int)Math.Round(255 * (constX + constM));
298                     break;
299             }
300             red = Math.Max(0, Math.Min(255, red));
301             green = Math.Max(0, Math.Min(255, green));
302             blue = Math.Max(0, Math.Min(255, blue));
303
304             //ARGB Encoding
305             return (255 << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff));
306         }
307
308         /// <summary>
309         /// return luma value according to to XYZ color space in the range 0.0 - 1.0
310         /// </summary>
311         private static int CompositeAlpha(int foregroundAlpha, int backgroundAlpha)
312         {
313             return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
314         }
315
316         private static int CompositeComponent(int fgC, int fgA, int bgC, int bgA, int alpha)
317         {
318             if (alpha == 0) return 0;
319             return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (alpha * 0xFF);
320         }
321     }
322 }