Home | History | Annotate | Download | only in gfx
      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