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 "build/build_config.h"
18 #include "skia/ext/skia_utils_win.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
22 namespace color_utils
{
25 // Helper functions -----------------------------------------------------------
29 int calcHue(double temp1
, double temp2
, double hue
) {
35 double result
= temp1
;
37 result
= temp1
+ (temp2
- temp1
) * hue
* 6.0;
38 else if (hue
* 2.0 < 1.0)
40 else if (hue
* 3.0 < 2.0)
41 result
= temp1
+ (temp2
- temp1
) * (2.0 / 3.0 - hue
) * 6.0;
43 // Scale the result from 0 - 255 and round off the value.
44 return static_cast<int>(result
* 255 + .5);
47 // Next two functions' formulas from:
48 // http://www.w3.org/TR/WCAG20/#relativeluminancedef
49 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
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);
57 SkColor
LumaInvertColor(SkColor color
) {
59 SkColorToHSL(color
, &hsl
);
61 return HSLToSkColor(hsl
, 255);
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
);
77 // ----------------------------------------------------------------------------
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);
86 double RelativeLuminance(SkColor color
) {
87 return (0.2126 * ConvertSRGB(SkColorGetR(color
))) +
88 (0.7152 * ConvertSRGB(SkColorGetG(color
))) +
89 (0.0722 * ConvertSRGB(SkColorGetB(color
)));
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
)) {
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
)
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
;
117 else if (hsl
->h
> 1.0)
120 hsl
->s
= delta
/ ((hsl
->l
< 0.5) ? (vmax
+ vmin
) : (2 - vmax
- vmin
));
124 SkColor
HSLToSkColor(const HSL
& hsl
, SkAlpha alpha
) {
126 double saturation
= hsl
.s
;
127 double lightness
= hsl
.l
;
129 // If there's no color, we don't care about hue and can do everything based on
136 else if (lightness
>= 1.0)
139 light
= SkDoubleToFixed(lightness
) >> 8;
141 return SkColorSetARGB(alpha
, light
, light
, light
);
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));
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
;
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
));
184 SkColor
HSLShift(SkColor color
, const HSL
& shift
) {
186 int alpha
= SkColorGetA(color
);
187 SkColorToHSL(color
, &hsl
);
189 // Replace the hue with the tint's hue.
193 // Change the saturation.
196 hsl
.s
*= shift
.s
* 2.0;
198 hsl
.s
+= (1.0 - hsl
.s
) * ((shift
.s
- 0.5) * 2.0);
201 SkColor result
= HSLToSkColor(hsl
, alpha
);
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);
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);
220 return SkColorSetARGB(alpha
,
223 static_cast<int>(b
));
226 void BuildLumaHistogram(const SkBitmap
& bitmap
, int histogram
[256]) {
227 DCHECK_EQ(kN32_SkColorType
, bitmap
.colorType());
229 SkAutoLockPixels
bitmap_lock(bitmap
);
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
))];
239 SkColor
AlphaBlend(SkColor foreground
, SkColor background
, SkAlpha alpha
) {
245 int f_alpha
= SkColorGetA(foreground
);
246 int b_alpha
= SkColorGetA(background
);
248 double normalizer
= (f_alpha
* alpha
+ b_alpha
* (255 - alpha
)) / 255.0;
249 if (normalizer
== 0.0)
250 return SK_ColorTRANSPARENT
;
252 double f_weight
= f_alpha
* alpha
/ normalizer
;
253 double b_weight
= b_alpha
* (255 - alpha
) / normalizer
;
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;
262 return SkColorSetARGB(static_cast<int>(normalizer
),
265 static_cast<int>(b
));
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
);
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
;
284 SkColor
InvertColor(SkColor color
) {
285 return SkColorSetARGB(
287 255 - SkColorGetR(color
),
288 255 - SkColorGetG(color
),
289 255 - SkColorGetB(color
));
292 SkColor
GetSysSkColor(int which
) {
294 return skia::COLORREFToSkColor(GetSysColor(which
));
297 return SK_ColorLTGRAY
;
301 } // namespace color_utils