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. 4 5 #include "ui/gfx/color_utils.h" 6 7 #include <math.h> 8 #if defined(OS_WIN) 9 #include <windows.h> 10 #endif 11 12 #include <algorithm> 13 14 #include "base/basictypes.h" 15 #include "base/logging.h" 16 #include "build/build_config.h" 17 #if defined(OS_WIN) 18 #include "skia/ext/skia_utils_win.h" 19 #endif 20 #include "third_party/skia/include/core/SkBitmap.h" 21 22 namespace color_utils { 23 24 25 // Helper functions ----------------------------------------------------------- 26 27 namespace { 28 29 int calcHue(double temp1, double temp2, double hue) { 30 if (hue < 0.0) 31 ++hue; 32 else if (hue > 1.0) 33 --hue; 34 35 double result = temp1; 36 if (hue * 6.0 < 1.0) 37 result = temp1 + (temp2 - temp1) * hue * 6.0; 38 else if (hue * 2.0 < 1.0) 39 result = temp2; 40 else if (hue * 3.0 < 2.0) 41 result = temp1 + (temp2 - temp1) * (2.0 / 3.0 - hue) * 6.0; 42 43 // Scale the result from 0 - 255 and round off the value. 44 return static_cast<int>(result * 255 + .5); 45 } 46 47 // Next two functions' formulas from: 48 // http://www.w3.org/TR/WCAG20/#relativeluminancedef 49 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef 50 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); 55 } 56 57 SkColor LumaInvertColor(SkColor color) { 58 HSL hsl; 59 SkColorToHSL(color, &hsl); 60 hsl.l = 1.0 - hsl.l; 61 return HSLToSkColor(hsl, 255); 62 } 63 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); 72 } 73 74 } // namespace 75 76 77 // ---------------------------------------------------------------------------- 78 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); 84 } 85 86 double RelativeLuminance(SkColor color) { 87 return (0.2126 * ConvertSRGB(SkColorGetR(color))) + 88 (0.7152 * ConvertSRGB(SkColorGetG(color))) + 89 (0.0722 * ConvertSRGB(SkColorGetB(color))); 90 } 91 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)) { 101 hsl->h = hsl->s = 0; 102 } else { 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) 109 hsl->h = db - dg; 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; 114 115 if (hsl->h < 0.0) 116 ++hsl->h; 117 else if (hsl->h > 1.0) 118 --hsl->h; 119 120 hsl->s = delta / ((hsl->l < 0.5) ? (vmax + vmin) : (2 - vmax - vmin)); 121 } 122 } 123 124 SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) { 125 double hue = hsl.h; 126 double saturation = hsl.s; 127 double lightness = hsl.l; 128 129 // If there's no color, we don't care about hue and can do everything based on 130 // brightness. 131 if (!saturation) { 132 uint8 light; 133 134 if (lightness < 0) 135 light = 0; 136 else if (lightness >= 1.0) 137 light = 255; 138 else 139 light = SkDoubleToFixed(lightness) >> 8; 140 141 return SkColorSetARGB(alpha, light, light, light); 142 } 143 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)); 152 } 153 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; 172 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)); 182 } 183 184 SkColor HSLShift(SkColor color, const HSL& shift) { 185 HSL hsl; 186 int alpha = SkColorGetA(color); 187 SkColorToHSL(color, &hsl); 188 189 // Replace the hue with the tint's hue. 190 if (shift.h >= 0) 191 hsl.h = shift.h; 192 193 // Change the saturation. 194 if (shift.s >= 0) { 195 if (shift.s <= 0.5) 196 hsl.s *= shift.s * 2.0; 197 else 198 hsl.s += (1.0 - hsl.s) * ((shift.s - 0.5) * 2.0); 199 } 200 201 SkColor result = HSLToSkColor(hsl, alpha); 202 203 if (shift.l < 0) 204 return result; 205 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); 215 } else { 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); 219 } 220 return SkColorSetARGB(alpha, 221 static_cast<int>(r), 222 static_cast<int>(g), 223 static_cast<int>(b)); 224 } 225 226 void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) { 227 DCHECK_EQ(kN32_SkColorType, bitmap.colorType()); 228 229 SkAutoLockPixels bitmap_lock(bitmap); 230 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))]; 236 } 237 } 238 239 SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha) { 240 if (alpha == 0) 241 return background; 242 if (alpha == 255) 243 return foreground; 244 245 int f_alpha = SkColorGetA(foreground); 246 int b_alpha = SkColorGetA(background); 247 248 double normalizer = (f_alpha * alpha + b_alpha * (255 - alpha)) / 255.0; 249 if (normalizer == 0.0) 250 return SK_ColorTRANSPARENT; 251 252 double f_weight = f_alpha * alpha / normalizer; 253 double b_weight = b_alpha * (255 - alpha) / normalizer; 254 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; 261 262 return SkColorSetARGB(static_cast<int>(normalizer), 263 static_cast<int>(r), 264 static_cast<int>(g), 265 static_cast<int>(b)); 266 } 267 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); 274 } 275 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; 282 } 283 284 SkColor InvertColor(SkColor color) { 285 return SkColorSetARGB( 286 SkColorGetA(color), 287 255 - SkColorGetR(color), 288 255 - SkColorGetG(color), 289 255 - SkColorGetB(color)); 290 } 291 292 SkColor GetSysSkColor(int which) { 293 #if defined(OS_WIN) 294 return skia::COLORREFToSkColor(GetSysColor(which)); 295 #else 296 NOTIMPLEMENTED(); 297 return SK_ColorLTGRAY; 298 #endif 299 } 300 301 } // namespace color_utils 302