OLD | NEW |
---|---|
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "app/gfx/color_utils.h" | 5 #include "app/gfx/color_utils.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 #if defined(OS_WIN) | 8 #if defined(OS_WIN) |
9 #include <windows.h> | 9 #include <windows.h> |
10 #endif | 10 #endif |
11 | 11 |
12 #include "base/basictypes.h" | 12 #include "base/basictypes.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "build/build_config.h" | 14 #include "build/build_config.h" |
15 #if defined(OS_WIN) | 15 #if defined(OS_WIN) |
16 #include "skia/ext/skia_utils_win.h" | 16 #include "skia/ext/skia_utils_win.h" |
17 #endif | 17 #endif |
18 #include "third_party/skia/include/core/SkBitmap.h" | 18 #include "third_party/skia/include/core/SkBitmap.h" |
19 | 19 |
20 namespace color_utils { | 20 namespace color_utils { |
21 | 21 |
22 // Helper functions ----------------------------------------------------------- | |
23 | |
24 namespace { | |
25 | |
22 // These transformations are based on the equations in: | 26 // These transformations are based on the equations in: |
23 // http://en.wikipedia.org/wiki/Lab_color | 27 // http://en.wikipedia.org/wiki/Lab_color |
24 // http://en.wikipedia.org/wiki/SRGB_color_space#Specification_of_the_transforma tion | 28 // http://en.wikipedia.org/wiki/SRGB_color_space# |
brettw
2009/09/22 16:40:51
I would have kept this link as-is, since now nobod
| |
29 // Specification_of_the_transformation | |
25 // See also: | 30 // See also: |
26 // http://www.brucelindbloom.com/index.html?ColorCalculator.html | 31 // http://www.brucelindbloom.com/index.html?ColorCalculator.html |
27 | 32 |
28 static const double kCIEConversionAlpha = 0.055; | 33 const double kCIEConversionAlpha = 0.055; |
29 static const double kCIEConversionGamma = 2.2; | 34 const double kCIEConversionGamma = 2.2; |
30 static const double kE = 0.008856; | 35 const double kE = 0.008856; |
31 static const double kK = 903.3; | 36 const double kK = 903.3; |
32 | 37 |
33 static double CIEConvertNonLinear(uint8 color_component) { | 38 double CIEConvertNonLinear(uint8 color_component) { |
34 double color_component_d = static_cast<double>(color_component) / 255.0; | 39 double color_component_d = static_cast<double>(color_component) / 255.0; |
35 if (color_component_d > 0.04045) { | 40 if (color_component_d > 0.04045) { |
36 double base = (color_component_d + kCIEConversionAlpha) / | 41 double base = (color_component_d + kCIEConversionAlpha) / |
37 (1 + kCIEConversionAlpha); | 42 (1 + kCIEConversionAlpha); |
38 return pow(base, kCIEConversionGamma); | 43 return pow(base, kCIEConversionGamma); |
39 } else { | 44 } else { |
40 return color_component_d / 12.92; | 45 return color_component_d / 12.92; |
41 } | 46 } |
42 } | 47 } |
43 | 48 |
44 // Note: this works only for sRGB. | 49 uint8 sRGBColorComponentFromLinearComponent(double component) { |
45 void SkColorToCIEXYZ(SkColor c, CIE_XYZ* xyz) { | |
46 uint8 r = SkColorGetR(c); | |
47 uint8 g = SkColorGetG(c); | |
48 uint8 b = SkColorGetB(c); | |
49 | |
50 xyz->X = | |
51 0.4124 * CIEConvertNonLinear(r) + | |
52 0.3576 * CIEConvertNonLinear(g) + | |
53 0.1805 * CIEConvertNonLinear(b); | |
54 xyz->Y = | |
55 0.2126 * CIEConvertNonLinear(r) + | |
56 0.7152 * CIEConvertNonLinear(g) + | |
57 0.0722 * CIEConvertNonLinear(g); | |
58 xyz->Z = | |
59 0.0193 * CIEConvertNonLinear(r) + | |
60 0.1192 * CIEConvertNonLinear(g) + | |
61 0.9505 * CIEConvertNonLinear(b); | |
62 } | |
63 | |
64 static double LabConvertNonLinear(double value) { | |
65 if (value > 0.008856) { | |
66 double goat = pow(value, static_cast<double>(1) / 3); | |
67 return goat; | |
68 } | |
69 return (kK * value + 16) / 116; | |
70 } | |
71 | |
72 void CIEXYZToLabColor(const CIE_XYZ& xyz, LabColor* lab) { | |
73 CIE_XYZ white_xyz; | |
74 SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); | |
75 double fx = LabConvertNonLinear(xyz.X / white_xyz.X); | |
76 double fy = LabConvertNonLinear(xyz.Y / white_xyz.Y); | |
77 double fz = LabConvertNonLinear(xyz.Z / white_xyz.Z); | |
78 lab->L = static_cast<int>(116 * fy) - 16; | |
79 lab->a = static_cast<int>(500 * (fx - fy)); | |
80 lab->b = static_cast<int>(200 * (fy - fz)); | |
81 } | |
82 | |
83 static uint8 sRGBColorComponentFromLinearComponent(double component) { | |
84 double result; | 50 double result; |
85 if (component <= 0.0031308) { | 51 if (component <= 0.0031308) { |
86 result = 12.92 * component; | 52 result = 12.92 * component; |
87 } else { | 53 } else { |
88 result = (1 + kCIEConversionAlpha) * | 54 result = (1 + kCIEConversionAlpha) * |
89 pow(component, (static_cast<double>(1) / 2.4)) - | 55 pow(component, (static_cast<double>(1) / 2.4)) - |
90 kCIEConversionAlpha; | 56 kCIEConversionAlpha; |
91 } | 57 } |
92 return std::min(static_cast<uint8>(255), static_cast<uint8>(result * 255)); | 58 return std::min(static_cast<uint8>(255), static_cast<uint8>(result * 255)); |
93 } | 59 } |
94 | 60 |
95 SkColor CIEXYZToSkColor(SkAlpha alpha, const CIE_XYZ& xyz) { | 61 double LabConvertNonLinear(double value) { |
96 double r_linear = 3.2410 * xyz.X - 1.5374 * xyz.Y - 0.4986 * xyz.Z; | 62 if (value > 0.008856) { |
97 double g_linear = -0.9692 * xyz.X + 1.8760 * xyz.Y + 0.0416 * xyz.Z; | 63 double goat = pow(value, static_cast<double>(1) / 3); |
98 double b_linear = 0.0556 * xyz.X - 0.2040 * xyz.Y + 1.0570 * xyz.Z; | 64 return goat; |
99 uint8 r = sRGBColorComponentFromLinearComponent(r_linear); | 65 } |
100 uint8 g = sRGBColorComponentFromLinearComponent(g_linear); | 66 return (kK * value + 16) / 116; |
101 uint8 b = sRGBColorComponentFromLinearComponent(b_linear); | |
102 return SkColorSetARGB(alpha, r, g, b); | |
103 } | 67 } |
104 | 68 |
105 static double gen_yr(const LabColor& lab) { | 69 double gen_yr(const LabColor& lab) { |
106 if (lab.L > (kE * kK)) | 70 if (lab.L > (kE * kK)) |
107 return pow((lab.L + 16.0) / 116, 3.0); | 71 return pow((lab.L + 16.0) / 116, 3.0); |
108 return static_cast<double>(lab.L) / kK; | 72 return static_cast<double>(lab.L) / kK; |
109 } | 73 } |
110 | 74 |
111 static double fy(const LabColor& lab) { | 75 double fy(const LabColor& lab) { |
112 double yr = gen_yr(lab); | 76 double yr = gen_yr(lab); |
113 if (yr > kE) | 77 if (yr > kE) |
114 return (lab.L + 16.0) / 116; | 78 return (lab.L + 16.0) / 116; |
115 return (kK * yr + 16.0) / 116; | 79 return (kK * yr + 16.0) / 116; |
116 } | 80 } |
117 | 81 |
118 static double fx(const LabColor& lab) { | 82 double fx(const LabColor& lab) { |
119 return (static_cast<double>(lab.a) / 500) + fy(lab); | 83 return (static_cast<double>(lab.a) / 500) + fy(lab); |
120 } | 84 } |
121 | 85 |
122 static double gen_xr(const LabColor& lab) { | 86 double gen_xr(const LabColor& lab) { |
123 double x = fx(lab); | 87 double x = fx(lab); |
124 double x_cubed = pow(x, 3.0); | 88 double x_cubed = pow(x, 3.0); |
125 if (x_cubed > kE) | 89 if (x_cubed > kE) |
126 return x_cubed; | 90 return x_cubed; |
127 return (116.0 * x - 16.0) / kK; | 91 return (116.0 * x - 16.0) / kK; |
128 } | 92 } |
129 | 93 |
130 static double fz(const LabColor& lab) { | 94 double fz(const LabColor& lab) { |
131 return fy(lab) - (static_cast<double>(lab.b) / 200); | 95 return fy(lab) - (static_cast<double>(lab.b) / 200); |
132 } | 96 } |
133 | 97 |
134 static double gen_zr(const LabColor& lab) { | 98 double gen_zr(const LabColor& lab) { |
135 double z = fz(lab); | 99 double z = fz(lab); |
136 double z_cubed = pow(z, 3.0); | 100 double z_cubed = pow(z, 3.0); |
137 if (z_cubed > kE) | 101 if (z_cubed > kE) |
138 return z_cubed; | 102 return z_cubed; |
139 return (116.0 * z - 16.0) / kK; | 103 return (116.0 * z - 16.0) / kK; |
140 } | 104 } |
141 | 105 |
142 void LabColorToCIEXYZ(const LabColor& lab, CIE_XYZ* xyz) { | 106 int GetLumaForColor(SkColor* color) { |
143 CIE_XYZ result; | 107 int r = SkColorGetR(*color); |
108 int g = SkColorGetG(*color); | |
109 int b = SkColorGetB(*color); | |
144 | 110 |
145 CIE_XYZ white_xyz; | 111 int luma = static_cast<int>(0.3*r + 0.59*g + 0.11*b); |
146 SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); | 112 if (luma < 0) |
113 luma = 0; | |
114 else if (luma > 255) | |
115 luma = 255; | |
147 | 116 |
148 result.X = gen_xr(lab) * white_xyz.X; | 117 return luma; |
149 result.Y = gen_yr(lab) * white_xyz.Y; | 118 } |
150 result.Z = gen_zr(lab) * white_xyz.Z; | |
151 | 119 |
152 *xyz = result; | 120 // Next three functions' formulas from: |
121 // http://www.w3.org/TR/WCAG20/#relativeluminancedef | |
122 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef | |
123 | |
124 double ConvertSRGB(double eight_bit_component) { | |
125 const double component = eight_bit_component / 255.0; | |
126 return (component <= 0.03928) ? | |
127 (component / 12.92) : pow((component + 0.055) / 1.055, 2.4); | |
128 } | |
129 | |
130 double RelativeLuminance(SkColor color) { | |
131 return (0.2126 * ConvertSRGB(SkColorGetR(color))) + | |
132 (0.7152 * ConvertSRGB(SkColorGetG(color))) + | |
133 (0.0722 * ConvertSRGB(SkColorGetB(color))); | |
134 } | |
135 | |
136 double ContrastRatio(SkColor color1, SkColor color2) { | |
137 const double l1 = RelativeLuminance(color1) + 0.05; | |
138 const double l2 = RelativeLuminance(color2) + 0.05; | |
139 return (l1 > l2) ? (l1 / l2) : (l2 / l1); | |
140 } | |
141 | |
142 } // namespace | |
143 | |
144 // ---------------------------------------------------------------------------- | |
145 | |
146 // Note: this works only for sRGB. | |
147 void SkColorToCIEXYZ(SkColor c, CIE_XYZ* xyz) { | |
148 uint8 r = SkColorGetR(c); | |
149 uint8 g = SkColorGetG(c); | |
150 uint8 b = SkColorGetB(c); | |
151 | |
152 xyz->X = | |
brettw
2009/09/22 16:40:51
Can you indent the wrapped lines 2 more spaces whi
| |
153 0.4124 * CIEConvertNonLinear(r) + | |
154 0.3576 * CIEConvertNonLinear(g) + | |
155 0.1805 * CIEConvertNonLinear(b); | |
156 xyz->Y = | |
157 0.2126 * CIEConvertNonLinear(r) + | |
158 0.7152 * CIEConvertNonLinear(g) + | |
159 0.0722 * CIEConvertNonLinear(g); | |
160 xyz->Z = | |
161 0.0193 * CIEConvertNonLinear(r) + | |
162 0.1192 * CIEConvertNonLinear(g) + | |
163 0.9505 * CIEConvertNonLinear(b); | |
164 } | |
165 | |
166 SkColor CIEXYZToSkColor(SkAlpha alpha, const CIE_XYZ& xyz) { | |
167 double r_linear = 3.2410 * xyz.X - 1.5374 * xyz.Y - 0.4986 * xyz.Z; | |
168 double g_linear = -0.9692 * xyz.X + 1.8760 * xyz.Y + 0.0416 * xyz.Z; | |
169 double b_linear = 0.0556 * xyz.X - 0.2040 * xyz.Y + 1.0570 * xyz.Z; | |
170 uint8 r = sRGBColorComponentFromLinearComponent(r_linear); | |
171 uint8 g = sRGBColorComponentFromLinearComponent(g_linear); | |
172 uint8 b = sRGBColorComponentFromLinearComponent(b_linear); | |
173 return SkColorSetARGB(alpha, r, g, b); | |
153 } | 174 } |
154 | 175 |
155 void SkColorToLabColor(SkColor c, LabColor* lab) { | 176 void SkColorToLabColor(SkColor c, LabColor* lab) { |
156 CIE_XYZ xyz; | 177 CIE_XYZ xyz; |
157 SkColorToCIEXYZ(c, &xyz); | 178 SkColorToCIEXYZ(c, &xyz); |
158 CIEXYZToLabColor(xyz, lab); | 179 CIEXYZToLabColor(xyz, lab); |
159 } | 180 } |
160 | 181 |
161 SkColor LabColorToSkColor(const LabColor& lab, SkAlpha alpha) { | 182 SkColor LabColorToSkColor(const LabColor& lab, SkAlpha alpha) { |
162 CIE_XYZ xyz; | 183 CIE_XYZ xyz; |
163 LabColorToCIEXYZ(lab, &xyz); | 184 LabColorToCIEXYZ(lab, &xyz); |
164 return CIEXYZToSkColor(alpha, xyz); | 185 return CIEXYZToSkColor(alpha, xyz); |
165 } | 186 } |
166 | 187 |
167 static const int kCloseToBoundary = 64; | 188 void CIEXYZToLabColor(const CIE_XYZ& xyz, LabColor* lab) { |
168 static const int kAverageBoundary = 15; | 189 CIE_XYZ white_xyz; |
190 SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); | |
191 double fx = LabConvertNonLinear(xyz.X / white_xyz.X); | |
192 double fy = LabConvertNonLinear(xyz.Y / white_xyz.Y); | |
193 double fz = LabConvertNonLinear(xyz.Z / white_xyz.Z); | |
194 lab->L = static_cast<int>(116 * fy) - 16; | |
195 lab->a = static_cast<int>(500 * (fx - fy)); | |
196 lab->b = static_cast<int>(200 * (fy - fz)); | |
197 } | |
198 | |
199 void LabColorToCIEXYZ(const LabColor& lab, CIE_XYZ* xyz) { | |
200 CIE_XYZ result; | |
201 | |
202 CIE_XYZ white_xyz; | |
203 SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); | |
204 | |
205 result.X = gen_xr(lab) * white_xyz.X; | |
206 result.Y = gen_yr(lab) * white_xyz.Y; | |
207 result.Z = gen_zr(lab) * white_xyz.Z; | |
208 | |
209 *xyz = result; | |
210 } | |
169 | 211 |
170 bool IsColorCloseToTransparent(SkAlpha alpha) { | 212 bool IsColorCloseToTransparent(SkAlpha alpha) { |
213 const int kCloseToBoundary = 64; | |
171 return alpha < kCloseToBoundary; | 214 return alpha < kCloseToBoundary; |
172 } | 215 } |
173 | 216 |
174 bool IsColorCloseToGrey(int r, int g, int b) { | 217 bool IsColorCloseToGrey(int r, int g, int b) { |
218 const int kAverageBoundary = 15; | |
175 int average = (r + g + b) / 3; | 219 int average = (r + g + b) / 3; |
176 return (abs(r - average) < kAverageBoundary) && | 220 return (abs(r - average) < kAverageBoundary) && |
177 (abs(g - average) < kAverageBoundary) && | 221 (abs(g - average) < kAverageBoundary) && |
178 (abs(b - average) < kAverageBoundary); | 222 (abs(b - average) < kAverageBoundary); |
179 } | 223 } |
180 | 224 |
181 SkColor GetAverageColorOfFavicon(SkBitmap* favicon, SkAlpha alpha) { | 225 SkColor GetAverageColorOfFavicon(SkBitmap* favicon, SkAlpha alpha) { |
182 int r = 0, g = 0, b = 0; | 226 int r = 0, g = 0, b = 0; |
183 | 227 |
184 SkAutoLockPixels favicon_lock(*favicon); | 228 SkAutoLockPixels favicon_lock(*favicon); |
(...skipping 29 matching lines...) Expand all Loading... | |
214 result = SkColorSetARGB(alpha, | 258 result = SkColorSetARGB(alpha, |
215 r / color_count, | 259 r / color_count, |
216 g / color_count, | 260 g / color_count, |
217 b / color_count); | 261 b / color_count); |
218 } else { | 262 } else { |
219 result = SkColorSetARGB(alpha, 0, 0, 0); | 263 result = SkColorSetARGB(alpha, 0, 0, 0); |
220 } | 264 } |
221 return result; | 265 return result; |
222 } | 266 } |
223 | 267 |
224 inline int GetLumaForColor(SkColor* color) { | |
225 int r = SkColorGetR(*color); | |
226 int g = SkColorGetG(*color); | |
227 int b = SkColorGetB(*color); | |
228 | |
229 int luma = static_cast<int>(0.3*r + 0.59*g + 0.11*b); | |
230 if (luma < 0) | |
231 luma = 0; | |
232 else if (luma > 255) | |
233 luma = 255; | |
234 | |
235 return luma; | |
236 } | |
237 | |
238 void BuildLumaHistogram(SkBitmap* bitmap, int histogram[256]) { | 268 void BuildLumaHistogram(SkBitmap* bitmap, int histogram[256]) { |
239 SkAutoLockPixels bitmap_lock(*bitmap); | 269 SkAutoLockPixels bitmap_lock(*bitmap); |
240 // Assume ARGB_8888 format. | 270 // Assume ARGB_8888 format. |
241 DCHECK(bitmap->getConfig() == SkBitmap::kARGB_8888_Config); | 271 DCHECK(bitmap->getConfig() == SkBitmap::kARGB_8888_Config); |
242 | 272 |
243 int pixel_width = bitmap->width(); | 273 int pixel_width = bitmap->width(); |
244 int pixel_height = bitmap->height(); | 274 int pixel_height = bitmap->height(); |
245 for (int y = 0; y < pixel_height; ++y) { | 275 for (int y = 0; y < pixel_height; ++y) { |
246 SkColor* current_color = static_cast<uint32_t*>(bitmap->getAddr32(0, y)); | 276 SkColor* current_color = static_cast<uint32_t*>(bitmap->getAddr32(0, y)); |
247 for (int x = 0; x < pixel_width; ++x, ++current_color) { | 277 for (int x = 0; x < pixel_width; ++x, ++current_color) { |
(...skipping 10 matching lines...) Expand all Loading... | |
258 | 288 |
259 return SkColorSetRGB( | 289 return SkColorSetRGB( |
260 ((SkColorGetR(foreground) * alpha) + | 290 ((SkColorGetR(foreground) * alpha) + |
261 (SkColorGetR(background) * (0xFF - alpha))) / 0xFF, | 291 (SkColorGetR(background) * (0xFF - alpha))) / 0xFF, |
262 ((SkColorGetG(foreground) * alpha) + | 292 ((SkColorGetG(foreground) * alpha) + |
263 (SkColorGetG(background) * (0xFF - alpha))) / 0xFF, | 293 (SkColorGetG(background) * (0xFF - alpha))) / 0xFF, |
264 ((SkColorGetB(foreground) * alpha) + | 294 ((SkColorGetB(foreground) * alpha) + |
265 (SkColorGetB(background) * (0xFF - alpha))) / 0xFF); | 295 (SkColorGetB(background) * (0xFF - alpha))) / 0xFF); |
266 } | 296 } |
267 | 297 |
268 // Next three functions' formulas from: | |
269 // http://www.w3.org/TR/WCAG20/#relativeluminancedef | |
270 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef | |
271 static double ConvertSRGB(double eight_bit_component) { | |
272 const double component = eight_bit_component / 255.0; | |
273 return (component <= 0.03928) ? | |
274 (component / 12.92) : pow((component + 0.055) / 1.055, 2.4); | |
275 } | |
276 | |
277 static double RelativeLuminance(SkColor color) { | |
278 return (0.2126 * ConvertSRGB(SkColorGetR(color))) + | |
279 (0.7152 * ConvertSRGB(SkColorGetG(color))) + | |
280 (0.0722 * ConvertSRGB(SkColorGetB(color))); | |
281 } | |
282 | |
283 static double ContrastRatio(SkColor color1, SkColor color2) { | |
284 const double l1 = RelativeLuminance(color1) + 0.05; | |
285 const double l2 = RelativeLuminance(color2) + 0.05; | |
286 return (l1 > l2) ? (l1 / l2) : (l2 / l1); | |
287 } | |
288 | |
289 SkColor PickMoreReadableColor(SkColor foreground1, | 298 SkColor PickMoreReadableColor(SkColor foreground1, |
290 SkColor foreground2, | 299 SkColor foreground2, |
291 SkColor background) { | 300 SkColor background) { |
292 return (ContrastRatio(foreground1, background) >= | 301 return (ContrastRatio(foreground1, background) >= |
293 ContrastRatio(foreground2, background)) ? foreground1 : foreground2; | 302 ContrastRatio(foreground2, background)) ? foreground1 : foreground2; |
294 } | 303 } |
295 | 304 |
296 SkColor GetSysSkColor(int which) { | 305 SkColor GetSysSkColor(int which) { |
297 #if defined(OS_WIN) | 306 #if defined(OS_WIN) |
298 return skia::COLORREFToSkColor(::GetSysColor(which)); | 307 return skia::COLORREFToSkColor(::GetSysColor(which)); |
299 #else | 308 #else |
300 NOTIMPLEMENTED(); | 309 NOTIMPLEMENTED(); |
301 return SK_ColorLTGRAY; | 310 return SK_ColorLTGRAY; |
302 #endif | 311 #endif |
303 } | 312 } |
304 | 313 |
305 } // namespace color_utils | 314 } // namespace color_utils |
OLD | NEW |