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"
14 #include "base/basictypes.h"
15 #include "base/logging.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "build/build_config.h"
19 #include "skia/ext/skia_utils_win.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
23 namespace color_utils
{
26 // Helper functions -----------------------------------------------------------
30 int calcHue(double temp1
, double temp2
, double hue
) {
36 double result
= temp1
;
38 result
= temp1
+ (temp2
- temp1
) * hue
* 6.0;
39 else if (hue
* 2.0 < 1.0)
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
) {
60 SkColorToHSL(color
, &hsl
);
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
);
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
)) {
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
)
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
;
118 else if (hsl
->h
> 1.0)
121 hsl
->s
= delta
/ ((hsl
->l
< 0.5) ? (vmax
+ vmin
) : (2 - vmax
- vmin
));
125 SkColor
HSLToSkColor(const HSL
& hsl
, SkAlpha alpha
) {
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
137 else if (lightness
>= 1.0)
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)
188 if (hsl
->s
< 0 || hsl
->s
> 1)
190 if (hsl
->l
< 0 || hsl
->l
> 1)
194 SkColor
HSLShift(SkColor color
, const HSL
& shift
) {
196 SkAlpha alpha
= SkColorGetA(color
);
197 SkColorToHSL(color
, &hsl
);
199 // Replace the hue with the tint's hue.
203 // Change the saturation.
206 hsl
.s
*= shift
.s
* 2.0;
208 hsl
.s
+= (1.0 - hsl
.s
) * ((shift
.s
- 0.5) * 2.0);
211 SkColor result
= HSLToSkColor(hsl
, alpha
);
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);
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
,
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())
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
) {
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
),
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(
308 255 - SkColorGetR(color
),
309 255 - SkColorGetG(color
),
310 255 - SkColorGetB(color
));
313 SkColor
GetSysSkColor(int which
) {
315 return skia::COLORREFToSkColor(GetSysColor(which
));
318 return SK_ColorLTGRAY
;
322 } // namespace color_utils