2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * Copyright (C) 2017 The Android Open Source Project
21 * Modified by Woochan Lee(wc0917.lee@samsung.com)
28 internal sealed class ColorUtils
30 private const int minAlphaSearchMaxIterations = 10;
31 private const int minAlphaSearchPrecision = 1;
34 /// Convert the ARGB color to its CIE XYZ representative components.
36 /// The resulting XYZ representation will use the D65 illuminant and the CIE
37 /// 2° Standard Observer (1931).
39 /// outXyz[0] is X [0 ...95.047)
40 /// outXyz[1] is Y [0...100)
41 /// outXyz[2] is Z [0...108.883)
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
46 public static void ColorToXyz(int color, double[] outXyz)
48 RgbToXyz((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, outXyz);
52 /// Convert RGB components to its CIE XYZ representative components.
54 /// The resulting XYZ representation will use the D65 illuminant and the CIE
55 /// 2° Standard Observer (1931).
57 /// outXyz[0] is X [0 ...95.047)
58 /// outXyz[1] is Y [0...100)
59 /// outXyz[2] is Z [0...108.883)
61 /// r red component value [0..255]
62 /// g green component value [0..255]
63 /// b blue component value [0..255]
64 /// outXyz 3-element array which holds the resulting XYZ components
66 public static void RgbToXyz(int red, int green, int blue, double[] outXyz)
68 if (outXyz.Length != 3)
70 throw new ArgumentException("Array legnth must be 3", nameof(outXyz));
73 double floatRed = red / 255.0;
74 floatRed = floatRed < 0.04045 ? floatRed / 12.92 : Math.Pow((floatRed + 0.055) / 1.055, 2.4);
75 double floatGreen = green / 255.0;
76 floatGreen = floatGreen < 0.04045 ? floatGreen / 12.92 : Math.Pow((floatGreen + 0.055) / 1.055, 2.4);
77 double floatBlue = blue / 255.0;
78 floatBlue = floatBlue < 0.04045 ? floatBlue / 12.92 : Math.Pow((floatBlue + 0.055) / 1.055, 2.4);
80 outXyz[0] = 100 * (floatRed * 0.4124 + floatGreen * 0.3576 + floatBlue * 0.1805);
81 outXyz[1] = 100 * (floatRed * 0.2126 + floatGreen * 0.7152 + floatBlue * 0.0722);
82 outXyz[2] = 100 * (floatRed * 0.0193 + floatGreen * 0.1192 + floatBlue * 0.9505);
86 /// Returns the luminance of a color as a float between 0.0 and 1.0
87 /// Defined as the Y component in the XYZ representation of color.
89 public static double CalculateLuminance(int color)
91 double[] result = new double[3];
92 ColorToXyz(color, result);
93 // Luminance is the Y component
94 return result[1] / 100;
98 /// Composite two potentially translucent colors over each other and returns the result.
100 public static int CompositeColors(int foreground, int background)
102 int bgAlpha = (background >> 24) & 0xff;
103 int fgAlpha = (foreground >> 24) & 0xff;
105 int alpha = CompositeAlpha(fgAlpha, bgAlpha);
107 int red = CompositeComponent((foreground >> 16) & 0xff, fgAlpha,
108 (background >> 16) & 0xff, bgAlpha, alpha);
109 int green = CompositeComponent((foreground >> 8) & 0xff, fgAlpha,
110 (background >> 8) & 0xff, bgAlpha, alpha);
111 int blue = CompositeComponent(foreground & 0xff, fgAlpha,
112 background & 0xff, bgAlpha, alpha);
114 return ((alpha & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff));
118 /// Returns the contrast ratio between foreground and background.
119 /// background must be opaque.
122 /// <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>.
124 public static double CalculateContrast(int foreground, int background)
126 if (((background >> 24) & 0xff) != 255)
128 throw new ArgumentException("background can not be translucent.");
130 if (((foreground >> 24) & 0xff) < 255)
132 // If the foreground is translucent, composite the foreground over the background
133 foreground = CompositeColors(foreground, background);
136 double luminance1 = CalculateLuminance(foreground) + 0.05;
137 double luminance2 = CalculateLuminance(background) + 0.05;
139 // Now return the lighter luminance divided by the darker luminance
140 return Math.Max(luminance1, luminance2) / Math.Min(luminance1, luminance2);
144 /// Set the alpha component of color to be alpha.
146 public static int SetAlphaComponent(int color, int alpha)
148 if (alpha < 0 || alpha > 255)
150 throw new ArgumentException("alpha must be between 0 and 255.");
153 return (color & 0x00ffffff) | (alpha << 24);
157 /// Calculates the minimum alpha value which can be applied to foreground so that would
158 /// have a contrast value of at least minContrastRatio when compared to
161 /// param foreground the foreground color
162 /// param background the opaque background color
163 /// param minContrastRatio the minimum contrast ratio
164 /// return the alpha value in the range 0-255, or -1 if no value could be calculated
166 public static int CalculateMinimumAlpha(int foreground, int background, float minContrastRatio)
168 if (((background >> 24) & 0xff) != 255)
170 throw new ArgumentException("background can not be translucent");
173 // First lets check that a fully opaque foreground has sufficient contrast
174 int testForeground = SetAlphaComponent(foreground, 255);
175 double testRatio = CalculateContrast(testForeground, background);
177 if (testRatio < minContrastRatio)
179 // Fully opaque foreground does not have sufficient contrast, return error
183 // Binary search to find a value with the minimum value which provides sufficient contrast
184 int numIterations = 0;
188 while (numIterations <= minAlphaSearchMaxIterations &&
189 (maxAlpha - minAlpha) > minAlphaSearchPrecision)
191 int testAlpha = (minAlpha + maxAlpha) / 2;
193 testForeground = SetAlphaComponent(foreground, testAlpha);
194 testRatio = CalculateContrast(testForeground, background);
195 if (testRatio < minContrastRatio)
197 minAlpha = testAlpha;
201 maxAlpha = testAlpha;
207 // Conservatively return the max of the range of possible alphas, which is known to pass.
211 public static void RgbToHsl(int red, int green, int blue, float[] hsl)
213 float floatRed = red / 255f;
214 float floatGreen = green / 255f;
215 float floatBlue = blue / 255f;
216 float max = Math.Max(floatRed, Math.Max(floatGreen, floatBlue));
217 float min = Math.Min(floatRed, Math.Min(floatGreen, floatBlue));
218 float deltaMaxMin = max - min;
219 float hue, saturation;
220 float lightness = (max + min) / 2f;
224 hue = saturation = 0f;
230 hue = ((floatGreen - floatBlue) / deltaMaxMin) % 6f;
232 else if (max == floatGreen)
234 hue = ((floatBlue - floatRed) / deltaMaxMin) + 2f;
238 hue = ((floatRed - floatGreen) / deltaMaxMin) + 4f;
240 saturation = deltaMaxMin / (1f - Math.Abs(2f * lightness - 1f));
242 hsl[0] = ((hue * 60f) + 360f) % 360f;
247 public static int HslToRgb(float[] hsl)
250 float saturation = hsl[1];
251 float lightness = hsl[2];
252 float constC = (1f - Math.Abs(2 * lightness - 1f)) * saturation;
253 float constM = lightness - 0.5f * constC;
254 float constX = constC * (1f - Math.Abs((hue / 60f % 2f) - 1f));
255 int hueSegment = (int)hue / 60;
256 int red = 0, green = 0, blue = 0;
260 red = (int)Math.Round(255 * (constC + constM));
261 green = (int)Math.Round(255 * (constX + constM));
262 blue = (int)Math.Round(255 * constM);
265 red = (int)Math.Round(255 * (constX + constM));
266 green = (int)Math.Round(255 * (constC + constM));
267 blue = (int)Math.Round(255 * constM);
270 red = (int)Math.Round(255 * constM);
271 green = (int)Math.Round(255 * (constC + constM));
272 blue = (int)Math.Round(255 * (constX + constM));
275 red = (int)Math.Round(255 * constM);
276 green = (int)Math.Round(255 * (constX + constM));
277 blue = (int)Math.Round(255 * (constC + constM));
280 red = (int)Math.Round(255 * (constX + constM));
281 green = (int)Math.Round(255 * constM);
282 blue = (int)Math.Round(255 * (constC + constM));
286 red = (int)Math.Round(255 * (constC + constM));
287 green = (int)Math.Round(255 * constM);
288 blue = (int)Math.Round(255 * (constX + constM));
291 red = Math.Max(0, Math.Min(255, red));
292 green = Math.Max(0, Math.Min(255, green));
293 blue = Math.Max(0, Math.Min(255, blue));
296 return (255 << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff));
299 public static uint GetBytesPerPixel(PixelFormat pixelFormat)
309 case PixelFormat.LA88:
310 case PixelFormat.RGB565:
311 case PixelFormat.RGBA4444:
312 case PixelFormat.RGBA5551:
313 case PixelFormat.BGR565:
314 case PixelFormat.BGRA4444:
315 case PixelFormat.BGRA5551:
320 case PixelFormat.RGB888:
325 case PixelFormat.RGB8888:
326 case PixelFormat.BGR8888:
327 case PixelFormat.RGBA8888:
328 case PixelFormat.BGRA8888:
333 Tizen.Log.Error("Palette", "Invalied PixelFormat(" + pixelFormat + ") has been givien \n");
339 /// return luma value according to to XYZ color space in the range 0.0 - 1.0
341 private static int CompositeAlpha(int foregroundAlpha, int backgroundAlpha)
343 return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
346 private static int CompositeComponent(int fgC, int fgA, int bgC, int bgA, int alpha)
348 if (alpha == 0) return 0;
349 return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (alpha * 0xFF);