OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/color_utils.h" | |
6 | |
7 #include <math.h> | |
8 #if defined(OS_WIN) | |
9 #include <windows.h> | |
10 #endif | |
11 | |
12 #include <algorithm> | |
13 | |
14 #include "base/basictypes.h" | |
15 #include "base/logging.h" | |
16 #include "build/build_config.h" | |
17 #if defined(OS_WIN) | |
18 #include "skia/ext/skia_utils_win.h" | |
19 #endif | |
20 #include "third_party/skia/include/core/SkBitmap.h" | |
21 | |
22 namespace color_utils { | |
23 | |
24 | |
25 // Helper functions ----------------------------------------------------------- | |
26 | |
27 namespace { | |
28 | |
29 int calcHue(double temp1, double temp2, double hue) { | |
30 if (hue < 0.0) | |
31 ++hue; | |
32 else if (hue > 1.0) | |
33 --hue; | |
34 | |
35 double result = temp1; | |
36 if (hue * 6.0 < 1.0) | |
37 result = temp1 + (temp2 - temp1) * hue * 6.0; | |
38 else if (hue * 2.0 < 1.0) | |
39 result = temp2; | |
40 else if (hue * 3.0 < 2.0) | |
41 result = temp1 + (temp2 - temp1) * (2.0 / 3.0 - hue) * 6.0; | |
42 | |
43 // Scale the result from 0 - 255 and round off the value. | |
44 return static_cast<int>(result * 255 + .5); | |
45 } | |
46 | |
47 // Next two functions' formulas from: | |
48 // http://www.w3.org/TR/WCAG20/#relativeluminancedef | |
49 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef | |
50 | |
51 double ConvertSRGB(double eight_bit_component) { | |
52 const double component = eight_bit_component / 255.0; | |
53 return (component <= 0.03928) ? | |
54 (component / 12.92) : pow((component + 0.055) / 1.055, 2.4); | |
55 } | |
56 | |
57 SkColor LumaInvertColor(SkColor color) { | |
58 HSL hsl; | |
59 SkColorToHSL(color, &hsl); | |
60 hsl.l = 1.0 - hsl.l; | |
61 return HSLToSkColor(hsl, 255); | |
62 } | |
63 | |
64 double ContrastRatio(double foreground_luminance, double background_luminance) { | |
65 DCHECK_GE(foreground_luminance, 0.0); | |
66 DCHECK_GE(background_luminance, 0.0); | |
67 foreground_luminance += 0.05; | |
68 background_luminance += 0.05; | |
69 return (foreground_luminance > background_luminance) ? | |
70 (foreground_luminance / background_luminance) : | |
71 (background_luminance / foreground_luminance); | |
72 } | |
73 | |
74 } // namespace | |
75 | |
76 | |
77 // ---------------------------------------------------------------------------- | |
78 | |
79 unsigned char GetLuminanceForColor(SkColor color) { | |
80 int luma = static_cast<int>((0.3 * SkColorGetR(color)) + | |
81 (0.59 * SkColorGetG(color)) + | |
82 (0.11 * SkColorGetB(color))); | |
83 return std::max(std::min(luma, 255), 0); | |
84 } | |
85 | |
86 double RelativeLuminance(SkColor color) { | |
87 return (0.2126 * ConvertSRGB(SkColorGetR(color))) + | |
88 (0.7152 * ConvertSRGB(SkColorGetG(color))) + | |
89 (0.0722 * ConvertSRGB(SkColorGetB(color))); | |
90 } | |
91 | |
92 void SkColorToHSL(SkColor c, HSL* hsl) { | |
93 double r = static_cast<double>(SkColorGetR(c)) / 255.0; | |
94 double g = static_cast<double>(SkColorGetG(c)) / 255.0; | |
95 double b = static_cast<double>(SkColorGetB(c)) / 255.0; | |
96 double vmax = std::max(std::max(r, g), b); | |
97 double vmin = std::min(std::min(r, g), b); | |
98 double delta = vmax - vmin; | |
99 hsl->l = (vmax + vmin) / 2; | |
100 if (SkColorGetR(c) == SkColorGetG(c) && SkColorGetR(c) == SkColorGetB(c)) { | |
101 hsl->h = hsl->s = 0; | |
102 } else { | |
103 double dr = (((vmax - r) / 6.0) + (delta / 2.0)) / delta; | |
104 double dg = (((vmax - g) / 6.0) + (delta / 2.0)) / delta; | |
105 double db = (((vmax - b) / 6.0) + (delta / 2.0)) / delta; | |
106 // We need to compare for the max value because comparing vmax to r, g, or b | |
107 // can sometimes result in values overflowing registers. | |
108 if (r >= g && r >= b) | |
109 hsl->h = db - dg; | |
110 else if (g >= r && g >= b) | |
111 hsl->h = (1.0 / 3.0) + dr - db; | |
112 else // (b >= r && b >= g) | |
113 hsl->h = (2.0 / 3.0) + dg - dr; | |
114 | |
115 if (hsl->h < 0.0) | |
116 ++hsl->h; | |
117 else if (hsl->h > 1.0) | |
118 --hsl->h; | |
119 | |
120 hsl->s = delta / ((hsl->l < 0.5) ? (vmax + vmin) : (2 - vmax - vmin)); | |
121 } | |
122 } | |
123 | |
124 SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) { | |
125 double hue = hsl.h; | |
126 double saturation = hsl.s; | |
127 double lightness = hsl.l; | |
128 | |
129 // If there's no color, we don't care about hue and can do everything based on | |
130 // brightness. | |
131 if (!saturation) { | |
132 uint8 light; | |
133 | |
134 if (lightness < 0) | |
135 light = 0; | |
136 else if (lightness >= 1.0) | |
137 light = 255; | |
138 else | |
139 light = SkDoubleToFixed(lightness) >> 8; | |
140 | |
141 return SkColorSetARGB(alpha, light, light, light); | |
142 } | |
143 | |
144 double temp2 = (lightness < 0.5) ? | |
145 (lightness * (1.0 + saturation)) : | |
146 (lightness + saturation - (lightness * saturation)); | |
147 double temp1 = 2.0 * lightness - temp2; | |
148 return SkColorSetARGB(alpha, | |
149 calcHue(temp1, temp2, hue + 1.0 / 3.0), | |
150 calcHue(temp1, temp2, hue), | |
151 calcHue(temp1, temp2, hue - 1.0 / 3.0)); | |
152 } | |
153 | |
154 void ClampHSL(HSL* hsl) { | |
155 if (hsl->h < 0) | |
156 hsl->h = 0; | |
157 if (hsl->h > 1) | |
158 hsl->h = 1; | |
159 if (hsl->s < 0) | |
160 hsl->s = 0; | |
161 if (hsl->s > 1) | |
162 hsl->s = 1; | |
163 if (hsl->l < 0) | |
164 hsl->l = 0; | |
165 if (hsl->l > 1) | |
166 hsl->l = 1; | |
167 } | |
168 | |
169 bool IsWithinHSLRange(const HSL& hsl, | |
170 const HSL& lower_bound, | |
171 const HSL& upper_bound) { | |
172 DCHECK(hsl.h >= 0 && hsl.h <= 1) << hsl.h; | |
173 DCHECK(hsl.s >= 0 && hsl.s <= 1) << hsl.s; | |
174 DCHECK(hsl.l >= 0 && hsl.l <= 1) << hsl.l; | |
175 DCHECK(lower_bound.h < 0 || upper_bound.h < 0 || | |
176 (lower_bound.h <= 1 && upper_bound.h <= lower_bound.h + 1)) | |
177 << "lower_bound.h: " << lower_bound.h | |
178 << ", upper_bound.h: " << upper_bound.h; | |
179 DCHECK(lower_bound.s < 0 || upper_bound.s < 0 || | |
180 (lower_bound.s <= upper_bound.s && upper_bound.s <= 1)) | |
181 << "lower_bound.s: " << lower_bound.s | |
182 << ", upper_bound.s: " << upper_bound.s; | |
183 DCHECK(lower_bound.l < 0 || upper_bound.l < 0 || | |
184 (lower_bound.l <= upper_bound.l && upper_bound.l <= 1)) | |
185 << "lower_bound.l: " << lower_bound.l | |
186 << ", upper_bound.l: " << upper_bound.l; | |
187 | |
188 // If the upper hue is >1, the given hue bounds wrap around at 1. | |
189 bool matches_hue = upper_bound.h > 1 | |
190 ? hsl.h >= lower_bound.h || hsl.h <= upper_bound.h - 1 | |
191 : hsl.h >= lower_bound.h && hsl.h <= upper_bound.h; | |
192 return (upper_bound.h < 0 || lower_bound.h < 0 || matches_hue) && | |
193 (upper_bound.s < 0 || lower_bound.s < 0 || | |
194 (hsl.s >= lower_bound.s && hsl.s <= upper_bound.s)) && | |
195 (upper_bound.l < 0 || lower_bound.l < 0 || | |
196 (hsl.l >= lower_bound.l && hsl.l <= upper_bound.l)); | |
197 } | |
198 | |
199 SkColor HSLShift(SkColor color, const HSL& shift) { | |
200 HSL hsl; | |
201 int alpha = SkColorGetA(color); | |
202 SkColorToHSL(color, &hsl); | |
203 | |
204 // Replace the hue with the tint's hue. | |
205 if (shift.h >= 0) | |
206 hsl.h = shift.h; | |
207 | |
208 // Change the saturation. | |
209 if (shift.s >= 0) { | |
210 if (shift.s <= 0.5) | |
211 hsl.s *= shift.s * 2.0; | |
212 else | |
213 hsl.s += (1.0 - hsl.s) * ((shift.s - 0.5) * 2.0); | |
214 } | |
215 | |
216 SkColor result = HSLToSkColor(hsl, alpha); | |
217 | |
218 if (shift.l < 0) | |
219 return result; | |
220 | |
221 // Lightness shifts in the style of popular image editors aren't actually | |
222 // represented in HSL - the L value does have some effect on saturation. | |
223 double r = static_cast<double>(SkColorGetR(result)); | |
224 double g = static_cast<double>(SkColorGetG(result)); | |
225 double b = static_cast<double>(SkColorGetB(result)); | |
226 if (shift.l <= 0.5) { | |
227 r *= (shift.l * 2.0); | |
228 g *= (shift.l * 2.0); | |
229 b *= (shift.l * 2.0); | |
230 } else { | |
231 r += (255.0 - r) * ((shift.l - 0.5) * 2.0); | |
232 g += (255.0 - g) * ((shift.l - 0.5) * 2.0); | |
233 b += (255.0 - b) * ((shift.l - 0.5) * 2.0); | |
234 } | |
235 return SkColorSetARGB(alpha, | |
236 static_cast<int>(r), | |
237 static_cast<int>(g), | |
238 static_cast<int>(b)); | |
239 } | |
240 | |
241 void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) { | |
242 DCHECK_EQ(kN32_SkColorType, bitmap.colorType()); | |
243 | |
244 SkAutoLockPixels bitmap_lock(bitmap); | |
245 | |
246 int pixel_width = bitmap.width(); | |
247 int pixel_height = bitmap.height(); | |
248 for (int y = 0; y < pixel_height; ++y) { | |
249 for (int x = 0; x < pixel_width; ++x) | |
250 ++histogram[GetLuminanceForColor(bitmap.getColor(x, y))]; | |
251 } | |
252 } | |
253 | |
254 SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha) { | |
255 if (alpha == 0) | |
256 return background; | |
257 if (alpha == 255) | |
258 return foreground; | |
259 | |
260 int f_alpha = SkColorGetA(foreground); | |
261 int b_alpha = SkColorGetA(background); | |
262 | |
263 double normalizer = (f_alpha * alpha + b_alpha * (255 - alpha)) / 255.0; | |
264 if (normalizer == 0.0) | |
265 return SK_ColorTRANSPARENT; | |
266 | |
267 double f_weight = f_alpha * alpha / normalizer; | |
268 double b_weight = b_alpha * (255 - alpha) / normalizer; | |
269 | |
270 double r = (SkColorGetR(foreground) * f_weight + | |
271 SkColorGetR(background) * b_weight) / 255.0; | |
272 double g = (SkColorGetG(foreground) * f_weight + | |
273 SkColorGetG(background) * b_weight) / 255.0; | |
274 double b = (SkColorGetB(foreground) * f_weight + | |
275 SkColorGetB(background) * b_weight) / 255.0; | |
276 | |
277 return SkColorSetARGB(static_cast<int>(normalizer), | |
278 static_cast<int>(r), | |
279 static_cast<int>(g), | |
280 static_cast<int>(b)); | |
281 } | |
282 | |
283 SkColor BlendTowardOppositeLuminance(SkColor color, SkAlpha alpha) { | |
284 unsigned char background_luminance = | |
285 color_utils::GetLuminanceForColor(color); | |
286 const SkColor blend_color = | |
287 (background_luminance < 128) ? SK_ColorWHITE : SK_ColorBLACK; | |
288 return color_utils::AlphaBlend(blend_color, color, alpha); | |
289 } | |
290 | |
291 SkColor GetReadableColor(SkColor foreground, SkColor background) { | |
292 const SkColor foreground2 = LumaInvertColor(foreground); | |
293 const double background_luminance = RelativeLuminance(background); | |
294 return (ContrastRatio(RelativeLuminance(foreground), background_luminance) >= | |
295 ContrastRatio(RelativeLuminance(foreground2), background_luminance)) ? | |
296 foreground : foreground2; | |
297 } | |
298 | |
299 SkColor InvertColor(SkColor color) { | |
300 return SkColorSetARGB( | |
301 SkColorGetA(color), | |
302 255 - SkColorGetR(color), | |
303 255 - SkColorGetG(color), | |
304 255 - SkColorGetB(color)); | |
305 } | |
306 | |
307 SkColor GetSysSkColor(int which) { | |
308 #if defined(OS_WIN) | |
309 return skia::COLORREFToSkColor(GetSysColor(which)); | |
310 #else | |
311 NOTIMPLEMENTED(); | |
312 return SK_ColorLTGRAY; | |
313 #endif | |
314 } | |
315 | |
316 } // namespace color_utils | |
OLD | NEW |