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 bool IsColorCloseToTransparent(SkAlpha alpha
) {
194 const int kCloseToBoundary
= 64;
195 return alpha
< kCloseToBoundary
;
198 bool IsColorCloseToGrey(int r
, int g
, int b
) {
199 const int kAverageBoundary
= 15;
200 int average
= (r
+ g
+ b
) / 3;
201 return (abs(r
- average
) < kAverageBoundary
) &&
202 (abs(g
- average
) < kAverageBoundary
) &&
203 (abs(b
- average
) < kAverageBoundary
);
206 SkColor
GetAverageColorOfFavicon(SkBitmap
* favicon
, SkAlpha alpha
) {
207 int r
= 0, g
= 0, b
= 0;
209 SkAutoLockPixels
favicon_lock(*favicon
);
210 SkColor
* pixels
= static_cast<SkColor
*>(favicon
->getPixels());
212 return SkColorSetARGB(alpha
, 0, 0, 0);
214 // Assume ARGB_8888 format.
215 DCHECK(favicon
->config() == SkBitmap::kARGB_8888_Config
);
216 SkColor
* current_color
= pixels
;
218 DCHECK(favicon
->width() <= 16 && favicon
->height() <= 16);
220 int pixel_count
= favicon
->width() * favicon
->height();
222 for (int i
= 0; i
< pixel_count
; ++i
, ++current_color
) {
223 // Disregard this color if it is close to black, close to white, or close
224 // to transparent since any of those pixels do not contribute much to the
225 // color makeup of this icon.
226 int cr
= SkColorGetR(*current_color
);
227 int cg
= SkColorGetG(*current_color
);
228 int cb
= SkColorGetB(*current_color
);
230 if (IsColorCloseToTransparent(SkColorGetA(*current_color
)) ||
231 IsColorCloseToGrey(cr
, cg
, cb
))
241 SkColorSetARGB(alpha
, r
/ color_count
, g
/ color_count
, b
/ color_count
) :
242 SkColorSetARGB(alpha
, 0, 0, 0);
245 void BuildLumaHistogram(const SkBitmap
& bitmap
, int histogram
[256]) {
246 SkAutoLockPixels
bitmap_lock(bitmap
);
247 if (!bitmap
.getPixels())
250 // Assume ARGB_8888 format.
251 DCHECK(bitmap
.config() == SkBitmap::kARGB_8888_Config
);
253 int pixel_width
= bitmap
.width();
254 int pixel_height
= bitmap
.height();
255 for (int y
= 0; y
< pixel_height
; ++y
) {
256 SkColor
* current_color
= static_cast<uint32_t*>(bitmap
.getAddr32(0, y
));
257 for (int x
= 0; x
< pixel_width
; ++x
, ++current_color
)
258 histogram
[GetLuminanceForColor(*current_color
)]++;
262 SkColor
AlphaBlend(SkColor foreground
, SkColor background
, SkAlpha alpha
) {
268 int f_alpha
= SkColorGetA(foreground
);
269 int b_alpha
= SkColorGetA(background
);
271 double normalizer
= (f_alpha
* alpha
+ b_alpha
* (255 - alpha
)) / 255.0;
272 if (normalizer
== 0.0)
273 return SkColorSetARGB(0, 0, 0, 0);
275 double f_weight
= f_alpha
* alpha
/ normalizer
;
276 double b_weight
= b_alpha
* (255 - alpha
) / normalizer
;
278 double r
= (SkColorGetR(foreground
) * f_weight
+
279 SkColorGetR(background
) * b_weight
) / 255.0;
280 double g
= (SkColorGetG(foreground
) * f_weight
+
281 SkColorGetG(background
) * b_weight
) / 255.0;
282 double b
= (SkColorGetB(foreground
) * f_weight
+
283 SkColorGetB(background
) * b_weight
) / 255.0;
285 return SkColorSetARGB(static_cast<int>(normalizer
),
288 static_cast<int>(b
));
291 SkColor
GetReadableColor(SkColor foreground
, SkColor background
) {
292 const SkColor foreground2
= LumaInvertColor(foreground
);
293 const double background_luminance
= RelativeLuminance(background
);
294 return (ContrastRatio(RelativeLuminance(foreground
), background_luminance
) >=
295 ContrastRatio(RelativeLuminance(foreground2
), background_luminance
)) ?
296 foreground
: foreground2
;
299 SkColor
InvertColor(SkColor color
) {
300 return SkColorSetARGB(
302 255 - SkColorGetR(color
),
303 255 - SkColorGetG(color
),
304 255 - SkColorGetB(color
));
307 SkColor
GetSysSkColor(int which
) {
309 return skia::COLORREFToSkColor(GetSysColor(which
));
312 return SK_ColorLTGRAY
;
316 } // namespace color_utils