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
{
24 // Helper functions -----------------------------------------------------------
28 int calcHue(double temp1
, double temp2
, double hue
) {
34 double result
= temp1
;
36 result
= temp1
+ (temp2
- temp1
) * hue
* 6.0;
37 else if (hue
* 2.0 < 1.0)
39 else if (hue
* 3.0 < 2.0)
40 result
= temp1
+ (temp2
- temp1
) * (2.0 / 3.0 - hue
) * 6.0;
42 // Scale the result from 0 - 255 and round off the value.
43 return static_cast<int>(result
* 255 + .5);
46 // Next two functions' formulas from:
47 // http://www.w3.org/TR/WCAG20/#relativeluminancedef
48 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
50 double ConvertSRGB(double eight_bit_component
) {
51 const double component
= eight_bit_component
/ 255.0;
52 return (component
<= 0.03928) ?
53 (component
/ 12.92) : pow((component
+ 0.055) / 1.055, 2.4);
56 SkColor
LumaInvertColor(SkColor color
) {
58 SkColorToHSL(color
, &hsl
);
60 return HSLToSkColor(hsl
, 255);
63 double ContrastRatio(double foreground_luminance
, double background_luminance
) {
64 // NOTE: Only pass in numbers obtained from RelativeLuminance(), since those
65 // are guaranteed to be > 0 and thus not cause a divide-by-zero error here.
66 return (foreground_luminance
> background_luminance
) ?
67 (foreground_luminance
/ background_luminance
) :
68 (background_luminance
/ foreground_luminance
);
73 // ----------------------------------------------------------------------------
75 unsigned char GetLuminanceForColor(SkColor color
) {
76 int luma
= static_cast<int>((0.3 * SkColorGetR(color
)) +
77 (0.59 * SkColorGetG(color
)) +
78 (0.11 * SkColorGetB(color
)));
79 return std::max(std::min(luma
, 255), 0);
82 double RelativeLuminance(SkColor color
) {
83 return (0.2126 * ConvertSRGB(SkColorGetR(color
))) +
84 (0.7152 * ConvertSRGB(SkColorGetG(color
))) +
85 (0.0722 * ConvertSRGB(SkColorGetB(color
))) + 0.05;
88 void SkColorToHSL(SkColor c
, HSL
* hsl
) {
89 double r
= static_cast<double>(SkColorGetR(c
)) / 255.0;
90 double g
= static_cast<double>(SkColorGetG(c
)) / 255.0;
91 double b
= static_cast<double>(SkColorGetB(c
)) / 255.0;
92 double vmax
= std::max(std::max(r
, g
), b
);
93 double vmin
= std::min(std::min(r
, g
), b
);
94 double delta
= vmax
- vmin
;
95 hsl
->l
= (vmax
+ vmin
) / 2;
96 if (SkColorGetR(c
) == SkColorGetG(c
) && SkColorGetR(c
) == SkColorGetB(c
)) {
99 double dr
= (((vmax
- r
) / 6.0) + (delta
/ 2.0)) / delta
;
100 double dg
= (((vmax
- g
) / 6.0) + (delta
/ 2.0)) / delta
;
101 double db
= (((vmax
- b
) / 6.0) + (delta
/ 2.0)) / delta
;
102 // We need to compare for the max value because comparing vmax to r,
103 // g or b can sometimes result in values overflowing registers.
104 if (r
>= g
&& r
>= b
)
106 else if (g
>= r
&& g
>= b
)
107 hsl
->h
= (1.0 / 3.0) + dr
- db
;
108 else // (b >= r && b >= g)
109 hsl
->h
= (2.0 / 3.0) + dg
- dr
;
113 else if (hsl
->h
> 1.0)
116 hsl
->s
= delta
/ ((hsl
->l
< 0.5) ? (vmax
+ vmin
) : (2 - vmax
- vmin
));
120 SkColor
HSLToSkColor(const HSL
& hsl
, SkAlpha alpha
) {
122 double saturation
= hsl
.s
;
123 double lightness
= hsl
.l
;
125 // If there's no color, we don't care about hue and can do everything based
132 else if (lightness
>= 1.0)
135 light
= SkDoubleToFixed(lightness
) >> 8;
137 return SkColorSetARGB(alpha
, light
, light
, light
);
140 double temp2
= (lightness
< 0.5) ?
141 (lightness
* (1.0 + saturation
)) :
142 (lightness
+ saturation
- (lightness
* saturation
));
143 double temp1
= 2.0 * lightness
- temp2
;
144 return SkColorSetARGB(alpha
,
145 calcHue(temp1
, temp2
, hue
+ 1.0 / 3.0),
146 calcHue(temp1
, temp2
, hue
),
147 calcHue(temp1
, temp2
, hue
- 1.0 / 3.0));
150 SkColor
HSLShift(SkColor color
, const HSL
& shift
) {
152 int alpha
= SkColorGetA(color
);
153 SkColorToHSL(color
, &hsl
);
155 // Replace the hue with the tint's hue.
159 // Change the saturation.
162 hsl
.s
*= shift
.s
* 2.0;
164 hsl
.s
+= (1.0 - hsl
.s
) * ((shift
.s
- 0.5) * 2.0);
167 SkColor result
= HSLToSkColor(hsl
, alpha
);
172 // Lightness shifts in the style of popular image editors aren't
173 // actually represented in HSL - the L value does have some effect
175 double r
= static_cast<double>(SkColorGetR(result
));
176 double g
= static_cast<double>(SkColorGetG(result
));
177 double b
= static_cast<double>(SkColorGetB(result
));
178 if (shift
.l
<= 0.5) {
179 r
*= (shift
.l
* 2.0);
180 g
*= (shift
.l
* 2.0);
181 b
*= (shift
.l
* 2.0);
183 r
+= (255.0 - r
) * ((shift
.l
- 0.5) * 2.0);
184 g
+= (255.0 - g
) * ((shift
.l
- 0.5) * 2.0);
185 b
+= (255.0 - b
) * ((shift
.l
- 0.5) * 2.0);
187 return SkColorSetARGB(alpha
,
190 static_cast<int>(b
));
193 void BuildLumaHistogram(const SkBitmap
& bitmap
, int histogram
[256]) {
194 SkAutoLockPixels
bitmap_lock(bitmap
);
195 if (!bitmap
.getPixels())
198 // Assume ARGB_8888 format.
199 DCHECK(bitmap
.config() == SkBitmap::kARGB_8888_Config
);
201 int pixel_width
= bitmap
.width();
202 int pixel_height
= bitmap
.height();
203 for (int y
= 0; y
< pixel_height
; ++y
) {
204 SkColor
* current_color
= static_cast<uint32_t*>(bitmap
.getAddr32(0, y
));
205 for (int x
= 0; x
< pixel_width
; ++x
, ++current_color
)
206 histogram
[GetLuminanceForColor(*current_color
)]++;
210 SkColor
AlphaBlend(SkColor foreground
, SkColor background
, SkAlpha alpha
) {
216 int f_alpha
= SkColorGetA(foreground
);
217 int b_alpha
= SkColorGetA(background
);
219 double normalizer
= (f_alpha
* alpha
+ b_alpha
* (255 - alpha
)) / 255.0;
220 if (normalizer
== 0.0)
221 return SkColorSetARGB(0, 0, 0, 0);
223 double f_weight
= f_alpha
* alpha
/ normalizer
;
224 double b_weight
= b_alpha
* (255 - alpha
) / normalizer
;
226 double r
= (SkColorGetR(foreground
) * f_weight
+
227 SkColorGetR(background
) * b_weight
) / 255.0;
228 double g
= (SkColorGetG(foreground
) * f_weight
+
229 SkColorGetG(background
) * b_weight
) / 255.0;
230 double b
= (SkColorGetB(foreground
) * f_weight
+
231 SkColorGetB(background
) * b_weight
) / 255.0;
233 return SkColorSetARGB(static_cast<int>(normalizer
),
236 static_cast<int>(b
));
239 SkColor
GetReadableColor(SkColor foreground
, SkColor background
) {
240 const SkColor foreground2
= LumaInvertColor(foreground
);
241 const double background_luminance
= RelativeLuminance(background
);
242 return (ContrastRatio(RelativeLuminance(foreground
), background_luminance
) >=
243 ContrastRatio(RelativeLuminance(foreground2
), background_luminance
)) ?
244 foreground
: foreground2
;
247 SkColor
InvertColor(SkColor color
) {
248 return SkColorSetARGB(
250 255 - SkColorGetR(color
),
251 255 - SkColorGetG(color
),
252 255 - SkColorGetB(color
));
255 SkColor
GetSysSkColor(int which
) {
257 return skia::COLORREFToSkColor(GetSysColor(which
));
260 return SK_ColorLTGRAY
;
264 } // namespace color_utils