• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
calcHue(double temp1,double temp2,double hue)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 
ConvertSRGB(double eight_bit_component)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 
LumaInvertColor(SkColor color)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 
ContrastRatio(double foreground_luminance,double background_luminance)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 
GetLuminanceForColor(SkColor color)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 
RelativeLuminance(SkColor color)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 
SkColorToHSL(SkColor c,HSL * hsl)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 
HSLToSkColor(const HSL & hsl,SkAlpha alpha)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 
IsWithinHSLRange(const HSL & hsl,const HSL & lower_bound,const HSL & upper_bound)154 bool IsWithinHSLRange(const HSL& hsl,
155                       const HSL& lower_bound,
156                       const HSL& upper_bound) {
157   DCHECK(hsl.h >= 0 && hsl.h <= 1) << hsl.h;
158   DCHECK(hsl.s >= 0 && hsl.s <= 1) << hsl.s;
159   DCHECK(hsl.l >= 0 && hsl.l <= 1) << hsl.l;
160   DCHECK(lower_bound.h < 0 || upper_bound.h < 0 ||
161          (lower_bound.h <= 1 && upper_bound.h <= lower_bound.h + 1))
162       << "lower_bound.h: " << lower_bound.h
163       << ", upper_bound.h: " << upper_bound.h;
164   DCHECK(lower_bound.s < 0 || upper_bound.s < 0 ||
165          (lower_bound.s <= upper_bound.s && upper_bound.s <= 1))
166       << "lower_bound.s: " << lower_bound.s
167       << ", upper_bound.s: " << upper_bound.s;
168   DCHECK(lower_bound.l < 0 || upper_bound.l < 0 ||
169          (lower_bound.l <= upper_bound.l && upper_bound.l <= 1))
170       << "lower_bound.l: " << lower_bound.l
171       << ", upper_bound.l: " << upper_bound.l;
172 
173   // If the upper hue is >1, the given hue bounds wrap around at 1.
174   bool matches_hue = upper_bound.h > 1
175                          ? hsl.h >= lower_bound.h || hsl.h <= upper_bound.h - 1
176                          : hsl.h >= lower_bound.h && hsl.h <= upper_bound.h;
177   return (upper_bound.h < 0 || lower_bound.h < 0 || matches_hue) &&
178          (upper_bound.s < 0 || lower_bound.s < 0 ||
179           (hsl.s >= lower_bound.s && hsl.s <= upper_bound.s)) &&
180          (upper_bound.l < 0 || lower_bound.l < 0 ||
181           (hsl.l >= lower_bound.l && hsl.l <= upper_bound.l));
182 }
183 
HSLShift(SkColor color,const HSL & shift)184 SkColor HSLShift(SkColor color, const HSL& shift) {
185   HSL hsl;
186   int alpha = SkColorGetA(color);
187   SkColorToHSL(color, &hsl);
188 
189   // Replace the hue with the tint's hue.
190   if (shift.h >= 0)
191     hsl.h = shift.h;
192 
193   // Change the saturation.
194   if (shift.s >= 0) {
195     if (shift.s <= 0.5)
196       hsl.s *= shift.s * 2.0;
197     else
198       hsl.s += (1.0 - hsl.s) * ((shift.s - 0.5) * 2.0);
199   }
200 
201   SkColor result = HSLToSkColor(hsl, alpha);
202 
203   if (shift.l < 0)
204     return result;
205 
206   // Lightness shifts in the style of popular image editors aren't actually
207   // represented in HSL - the L value does have some effect on saturation.
208   double r = static_cast<double>(SkColorGetR(result));
209   double g = static_cast<double>(SkColorGetG(result));
210   double b = static_cast<double>(SkColorGetB(result));
211   if (shift.l <= 0.5) {
212     r *= (shift.l * 2.0);
213     g *= (shift.l * 2.0);
214     b *= (shift.l * 2.0);
215   } else {
216     r += (255.0 - r) * ((shift.l - 0.5) * 2.0);
217     g += (255.0 - g) * ((shift.l - 0.5) * 2.0);
218     b += (255.0 - b) * ((shift.l - 0.5) * 2.0);
219   }
220   return SkColorSetARGB(alpha,
221                         static_cast<int>(r),
222                         static_cast<int>(g),
223                         static_cast<int>(b));
224 }
225 
BuildLumaHistogram(const SkBitmap & bitmap,int histogram[256])226 void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) {
227   DCHECK_EQ(kPMColor_SkColorType, bitmap.colorType());
228 
229   SkAutoLockPixels bitmap_lock(bitmap);
230 
231   int pixel_width = bitmap.width();
232   int pixel_height = bitmap.height();
233   for (int y = 0; y < pixel_height; ++y) {
234     for (int x = 0; x < pixel_width; ++x)
235       ++histogram[GetLuminanceForColor(bitmap.getColor(x, y))];
236   }
237 }
238 
AlphaBlend(SkColor foreground,SkColor background,SkAlpha alpha)239 SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha) {
240   if (alpha == 0)
241     return background;
242   if (alpha == 255)
243     return foreground;
244 
245   int f_alpha = SkColorGetA(foreground);
246   int b_alpha = SkColorGetA(background);
247 
248   double normalizer = (f_alpha * alpha + b_alpha * (255 - alpha)) / 255.0;
249   if (normalizer == 0.0)
250     return SK_ColorTRANSPARENT;
251 
252   double f_weight = f_alpha * alpha / normalizer;
253   double b_weight = b_alpha * (255 - alpha) / normalizer;
254 
255   double r = (SkColorGetR(foreground) * f_weight +
256               SkColorGetR(background) * b_weight) / 255.0;
257   double g = (SkColorGetG(foreground) * f_weight +
258               SkColorGetG(background) * b_weight) / 255.0;
259   double b = (SkColorGetB(foreground) * f_weight +
260               SkColorGetB(background) * b_weight) / 255.0;
261 
262   return SkColorSetARGB(static_cast<int>(normalizer),
263                         static_cast<int>(r),
264                         static_cast<int>(g),
265                         static_cast<int>(b));
266 }
267 
BlendTowardOppositeLuminance(SkColor color,SkAlpha alpha)268 SkColor BlendTowardOppositeLuminance(SkColor color, SkAlpha alpha) {
269   unsigned char background_luminance =
270       color_utils::GetLuminanceForColor(color);
271   const SkColor blend_color =
272       (background_luminance < 128) ? SK_ColorWHITE : SK_ColorBLACK;
273   return color_utils::AlphaBlend(blend_color, color, alpha);
274 }
275 
GetReadableColor(SkColor foreground,SkColor background)276 SkColor GetReadableColor(SkColor foreground, SkColor background) {
277   const SkColor foreground2 = LumaInvertColor(foreground);
278   const double background_luminance = RelativeLuminance(background);
279   return (ContrastRatio(RelativeLuminance(foreground), background_luminance) >=
280           ContrastRatio(RelativeLuminance(foreground2), background_luminance)) ?
281       foreground : foreground2;
282 }
283 
InvertColor(SkColor color)284 SkColor InvertColor(SkColor color) {
285   return SkColorSetARGB(
286       SkColorGetA(color),
287       255 - SkColorGetR(color),
288       255 - SkColorGetG(color),
289       255 - SkColorGetB(color));
290 }
291 
GetSysSkColor(int which)292 SkColor GetSysSkColor(int which) {
293 #if defined(OS_WIN)
294   return skia::COLORREFToSkColor(GetSysColor(which));
295 #else
296   NOTIMPLEMENTED();
297   return SK_ColorLTGRAY;
298 #endif
299 }
300 
301 }  // namespace color_utils
302