Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / gfx / color_utils.cc
blob6741dca68d5a98c938e70fbb11a10f6b4225e7dc
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.
5 #include "ui/gfx/color_utils.h"
7 #include <math.h>
8 #if defined(OS_WIN)
9 #include <windows.h>
10 #endif
12 #include <algorithm>
14 #include "base/basictypes.h"
15 #include "base/logging.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "build/build_config.h"
18 #if defined(OS_WIN)
19 #include "skia/ext/skia_utils_win.h"
20 #endif
21 #include "third_party/skia/include/core/SkBitmap.h"
23 namespace color_utils {
26 // Helper functions -----------------------------------------------------------
28 namespace {
30 int calcHue(double temp1, double temp2, double hue) {
31 if (hue < 0.0)
32 ++hue;
33 else if (hue > 1.0)
34 --hue;
36 double result = temp1;
37 if (hue * 6.0 < 1.0)
38 result = temp1 + (temp2 - temp1) * hue * 6.0;
39 else if (hue * 2.0 < 1.0)
40 result = temp2;
41 else if (hue * 3.0 < 2.0)
42 result = temp1 + (temp2 - temp1) * (2.0 / 3.0 - hue) * 6.0;
44 // Scale the result from 0 - 255 and round off the value.
45 return static_cast<int>(result * 255 + .5);
48 // Next two functions' formulas from:
49 // http://www.w3.org/TR/WCAG20/#relativeluminancedef
50 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
52 double ConvertSRGB(double eight_bit_component) {
53 const double component = eight_bit_component / 255.0;
54 return (component <= 0.03928) ?
55 (component / 12.92) : pow((component + 0.055) / 1.055, 2.4);
58 SkColor LumaInvertColor(SkColor color) {
59 HSL hsl;
60 SkColorToHSL(color, &hsl);
61 hsl.l = 1.0 - hsl.l;
62 return HSLToSkColor(hsl, 255);
65 double ContrastRatio(double foreground_luminance, double background_luminance) {
66 DCHECK_GE(foreground_luminance, 0.0);
67 DCHECK_GE(background_luminance, 0.0);
68 foreground_luminance += 0.05;
69 background_luminance += 0.05;
70 return (foreground_luminance > background_luminance) ?
71 (foreground_luminance / background_luminance) :
72 (background_luminance / foreground_luminance);
75 } // namespace
78 // ----------------------------------------------------------------------------
80 unsigned char GetLuminanceForColor(SkColor color) {
81 return base::saturated_cast<unsigned char>(
82 (0.3 * SkColorGetR(color)) +
83 (0.59 * SkColorGetG(color)) +
84 (0.11 * SkColorGetB(color)));
87 double RelativeLuminance(SkColor color) {
88 return (0.2126 * ConvertSRGB(SkColorGetR(color))) +
89 (0.7152 * ConvertSRGB(SkColorGetG(color))) +
90 (0.0722 * ConvertSRGB(SkColorGetB(color)));
93 void SkColorToHSL(SkColor c, HSL* hsl) {
94 double r = static_cast<double>(SkColorGetR(c)) / 255.0;
95 double g = static_cast<double>(SkColorGetG(c)) / 255.0;
96 double b = static_cast<double>(SkColorGetB(c)) / 255.0;
97 double vmax = std::max(std::max(r, g), b);
98 double vmin = std::min(std::min(r, g), b);
99 double delta = vmax - vmin;
100 hsl->l = (vmax + vmin) / 2;
101 if (SkColorGetR(c) == SkColorGetG(c) && SkColorGetR(c) == SkColorGetB(c)) {
102 hsl->h = hsl->s = 0;
103 } else {
104 double dr = (((vmax - r) / 6.0) + (delta / 2.0)) / delta;
105 double dg = (((vmax - g) / 6.0) + (delta / 2.0)) / delta;
106 double db = (((vmax - b) / 6.0) + (delta / 2.0)) / delta;
107 // We need to compare for the max value because comparing vmax to r, g, or b
108 // can sometimes result in values overflowing registers.
109 if (r >= g && r >= b)
110 hsl->h = db - dg;
111 else if (g >= r && g >= b)
112 hsl->h = (1.0 / 3.0) + dr - db;
113 else // (b >= r && b >= g)
114 hsl->h = (2.0 / 3.0) + dg - dr;
116 if (hsl->h < 0.0)
117 ++hsl->h;
118 else if (hsl->h > 1.0)
119 --hsl->h;
121 hsl->s = delta / ((hsl->l < 0.5) ? (vmax + vmin) : (2 - vmax - vmin));
125 SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) {
126 double hue = hsl.h;
127 double saturation = hsl.s;
128 double lightness = hsl.l;
130 // If there's no color, we don't care about hue and can do everything based on
131 // brightness.
132 if (!saturation) {
133 uint8 light;
135 if (lightness < 0)
136 light = 0;
137 else if (lightness >= 1.0)
138 light = 255;
139 else
140 light = static_cast<uint8>(SkDoubleToFixed(lightness) >> 8);
142 return SkColorSetARGB(alpha, light, light, light);
145 double temp2 = (lightness < 0.5) ?
146 (lightness * (1.0 + saturation)) :
147 (lightness + saturation - (lightness * saturation));
148 double temp1 = 2.0 * lightness - temp2;
149 return SkColorSetARGB(alpha,
150 calcHue(temp1, temp2, hue + 1.0 / 3.0),
151 calcHue(temp1, temp2, hue),
152 calcHue(temp1, temp2, hue - 1.0 / 3.0));
155 bool IsWithinHSLRange(const HSL& hsl,
156 const HSL& lower_bound,
157 const HSL& upper_bound) {
158 DCHECK(hsl.h >= 0 && hsl.h <= 1) << hsl.h;
159 DCHECK(hsl.s >= 0 && hsl.s <= 1) << hsl.s;
160 DCHECK(hsl.l >= 0 && hsl.l <= 1) << hsl.l;
161 DCHECK(lower_bound.h < 0 || upper_bound.h < 0 ||
162 (lower_bound.h <= 1 && upper_bound.h <= lower_bound.h + 1))
163 << "lower_bound.h: " << lower_bound.h
164 << ", upper_bound.h: " << upper_bound.h;
165 DCHECK(lower_bound.s < 0 || upper_bound.s < 0 ||
166 (lower_bound.s <= upper_bound.s && upper_bound.s <= 1))
167 << "lower_bound.s: " << lower_bound.s
168 << ", upper_bound.s: " << upper_bound.s;
169 DCHECK(lower_bound.l < 0 || upper_bound.l < 0 ||
170 (lower_bound.l <= upper_bound.l && upper_bound.l <= 1))
171 << "lower_bound.l: " << lower_bound.l
172 << ", upper_bound.l: " << upper_bound.l;
174 // If the upper hue is >1, the given hue bounds wrap around at 1.
175 bool matches_hue = upper_bound.h > 1
176 ? hsl.h >= lower_bound.h || hsl.h <= upper_bound.h - 1
177 : hsl.h >= lower_bound.h && hsl.h <= upper_bound.h;
178 return (upper_bound.h < 0 || lower_bound.h < 0 || matches_hue) &&
179 (upper_bound.s < 0 || lower_bound.s < 0 ||
180 (hsl.s >= lower_bound.s && hsl.s <= upper_bound.s)) &&
181 (upper_bound.l < 0 || lower_bound.l < 0 ||
182 (hsl.l >= lower_bound.l && hsl.l <= upper_bound.l));
185 void MakeHSLShiftValid(HSL* hsl) {
186 if (hsl->h < 0 || hsl->h > 1)
187 hsl->h = -1;
188 if (hsl->s < 0 || hsl->s > 1)
189 hsl->s = -1;
190 if (hsl->l < 0 || hsl->l > 1)
191 hsl->l = -1;
194 SkColor HSLShift(SkColor color, const HSL& shift) {
195 HSL hsl;
196 SkAlpha alpha = SkColorGetA(color);
197 SkColorToHSL(color, &hsl);
199 // Replace the hue with the tint's hue.
200 if (shift.h >= 0)
201 hsl.h = shift.h;
203 // Change the saturation.
204 if (shift.s >= 0) {
205 if (shift.s <= 0.5)
206 hsl.s *= shift.s * 2.0;
207 else
208 hsl.s += (1.0 - hsl.s) * ((shift.s - 0.5) * 2.0);
211 SkColor result = HSLToSkColor(hsl, alpha);
213 if (shift.l < 0)
214 return result;
216 // Lightness shifts in the style of popular image editors aren't actually
217 // represented in HSL - the L value does have some effect on saturation.
218 double r = static_cast<double>(SkColorGetR(result));
219 double g = static_cast<double>(SkColorGetG(result));
220 double b = static_cast<double>(SkColorGetB(result));
221 if (shift.l <= 0.5) {
222 r *= (shift.l * 2.0);
223 g *= (shift.l * 2.0);
224 b *= (shift.l * 2.0);
225 } else {
226 r += (255.0 - r) * ((shift.l - 0.5) * 2.0);
227 g += (255.0 - g) * ((shift.l - 0.5) * 2.0);
228 b += (255.0 - b) * ((shift.l - 0.5) * 2.0);
230 return SkColorSetARGB(alpha,
231 static_cast<int>(r),
232 static_cast<int>(g),
233 static_cast<int>(b));
236 void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) {
237 DCHECK_EQ(kN32_SkColorType, bitmap.colorType());
239 SkAutoLockPixels bitmap_lock(bitmap);
241 int pixel_width = bitmap.width();
242 int pixel_height = bitmap.height();
243 for (int y = 0; y < pixel_height; ++y) {
244 for (int x = 0; x < pixel_width; ++x)
245 ++histogram[GetLuminanceForColor(bitmap.getColor(x, y))];
249 double CalculateBoringScore(const SkBitmap& bitmap) {
250 if (bitmap.isNull() || bitmap.empty())
251 return 1.0;
252 int histogram[256] = {0};
253 BuildLumaHistogram(bitmap, histogram);
255 int color_count = *std::max_element(histogram, histogram + 256);
256 int pixel_count = bitmap.width() * bitmap.height();
257 return static_cast<double>(color_count) / pixel_count;
260 SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha) {
261 if (alpha == 0)
262 return background;
263 if (alpha == 255)
264 return foreground;
266 int f_alpha = SkColorGetA(foreground);
267 int b_alpha = SkColorGetA(background);
269 double normalizer = (f_alpha * alpha + b_alpha * (255 - alpha)) / 255.0;
270 if (normalizer == 0.0)
271 return SK_ColorTRANSPARENT;
273 double f_weight = f_alpha * alpha / normalizer;
274 double b_weight = b_alpha * (255 - alpha) / normalizer;
276 double r = (SkColorGetR(foreground) * f_weight +
277 SkColorGetR(background) * b_weight) / 255.0;
278 double g = (SkColorGetG(foreground) * f_weight +
279 SkColorGetG(background) * b_weight) / 255.0;
280 double b = (SkColorGetB(foreground) * f_weight +
281 SkColorGetB(background) * b_weight) / 255.0;
283 return SkColorSetARGB(static_cast<int>(normalizer),
284 static_cast<int>(r),
285 static_cast<int>(g),
286 static_cast<int>(b));
289 SkColor BlendTowardOppositeLuminance(SkColor color, SkAlpha alpha) {
290 unsigned char background_luminance =
291 color_utils::GetLuminanceForColor(color);
292 const SkColor blend_color =
293 (background_luminance < 128) ? SK_ColorWHITE : SK_ColorBLACK;
294 return color_utils::AlphaBlend(blend_color, color, alpha);
297 SkColor GetReadableColor(SkColor foreground, SkColor background) {
298 const SkColor foreground2 = LumaInvertColor(foreground);
299 const double background_luminance = RelativeLuminance(background);
300 return (ContrastRatio(RelativeLuminance(foreground), background_luminance) >=
301 ContrastRatio(RelativeLuminance(foreground2), background_luminance)) ?
302 foreground : foreground2;
305 SkColor InvertColor(SkColor color) {
306 return SkColorSetARGB(
307 SkColorGetA(color),
308 255 - SkColorGetR(color),
309 255 - SkColorGetG(color),
310 255 - SkColorGetB(color));
313 SkColor GetSysSkColor(int which) {
314 #if defined(OS_WIN)
315 return skia::COLORREFToSkColor(GetSysColor(which));
316 #else
317 NOTIMPLEMENTED();
318 return SK_ColorLTGRAY;
319 #endif
322 } // namespace color_utils