root/ui/gfx/color_utils.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. calcHue
  2. ConvertSRGB
  3. LumaInvertColor
  4. ContrastRatio
  5. GetLuminanceForColor
  6. RelativeLuminance
  7. SkColorToHSL
  8. HSLToSkColor
  9. HSLShift
  10. BuildLumaHistogram
  11. AlphaBlend
  12. BlendTowardOppositeLuminance
  13. GetReadableColor
  14. InvertColor
  15. GetSysSkColor

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/color_utils.h"

#include <math.h>
#if defined(OS_WIN)
#include <windows.h>
#endif

#include <algorithm>

#include "base/basictypes.h"
#include "base/logging.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "skia/ext/skia_utils_win.h"
#endif
#include "third_party/skia/include/core/SkBitmap.h"

namespace color_utils {


// Helper functions -----------------------------------------------------------

namespace {

int calcHue(double temp1, double temp2, double hue) {
  if (hue < 0.0)
    ++hue;
  else if (hue > 1.0)
    --hue;

  double result = temp1;
  if (hue * 6.0 < 1.0)
    result = temp1 + (temp2 - temp1) * hue * 6.0;
  else if (hue * 2.0 < 1.0)
    result = temp2;
  else if (hue * 3.0 < 2.0)
    result = temp1 + (temp2 - temp1) * (2.0 / 3.0 - hue) * 6.0;

  // Scale the result from 0 - 255 and round off the value.
  return static_cast<int>(result * 255 + .5);
}

// Next two functions' formulas from:
// http://www.w3.org/TR/WCAG20/#relativeluminancedef
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef

double ConvertSRGB(double eight_bit_component) {
  const double component = eight_bit_component / 255.0;
  return (component <= 0.03928) ?
      (component / 12.92) : pow((component + 0.055) / 1.055, 2.4);
}

SkColor LumaInvertColor(SkColor color) {
  HSL hsl;
  SkColorToHSL(color, &hsl);
  hsl.l = 1.0 - hsl.l;
  return HSLToSkColor(hsl, 255);
}

double ContrastRatio(double foreground_luminance, double background_luminance) {
  DCHECK_GE(foreground_luminance, 0.0);
  DCHECK_GE(background_luminance, 0.0);
  foreground_luminance += 0.05;
  background_luminance += 0.05;
  return (foreground_luminance > background_luminance) ?
      (foreground_luminance / background_luminance) :
      (background_luminance / foreground_luminance);
}

}  // namespace


// ----------------------------------------------------------------------------

unsigned char GetLuminanceForColor(SkColor color) {
  int luma = static_cast<int>((0.3 * SkColorGetR(color)) +
                              (0.59 * SkColorGetG(color)) +
                              (0.11 * SkColorGetB(color)));
  return std::max(std::min(luma, 255), 0);
}

double RelativeLuminance(SkColor color) {
  return (0.2126 * ConvertSRGB(SkColorGetR(color))) +
         (0.7152 * ConvertSRGB(SkColorGetG(color))) +
         (0.0722 * ConvertSRGB(SkColorGetB(color)));
}

void SkColorToHSL(SkColor c, HSL* hsl) {
  double r = static_cast<double>(SkColorGetR(c)) / 255.0;
  double g = static_cast<double>(SkColorGetG(c)) / 255.0;
  double b = static_cast<double>(SkColorGetB(c)) / 255.0;
  double vmax = std::max(std::max(r, g), b);
  double vmin = std::min(std::min(r, g), b);
  double delta = vmax - vmin;
  hsl->l = (vmax + vmin) / 2;
  if (SkColorGetR(c) == SkColorGetG(c) && SkColorGetR(c) == SkColorGetB(c)) {
    hsl->h = hsl->s = 0;
  } else {
    double dr = (((vmax - r) / 6.0) + (delta / 2.0)) / delta;
    double dg = (((vmax - g) / 6.0) + (delta / 2.0)) / delta;
    double db = (((vmax - b) / 6.0) + (delta / 2.0)) / delta;
    // We need to compare for the max value because comparing vmax to r, g, or b
    // can sometimes result in values overflowing registers.
    if (r >= g && r >= b)
      hsl->h = db - dg;
    else if (g >= r && g >= b)
      hsl->h = (1.0 / 3.0) + dr - db;
    else  // (b >= r && b >= g)
      hsl->h = (2.0 / 3.0) + dg - dr;

    if (hsl->h < 0.0)
      ++hsl->h;
    else if (hsl->h > 1.0)
      --hsl->h;

    hsl->s = delta / ((hsl->l < 0.5) ? (vmax + vmin) : (2 - vmax - vmin));
  }
}

SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) {
  double hue = hsl.h;
  double saturation = hsl.s;
  double lightness = hsl.l;

  // If there's no color, we don't care about hue and can do everything based on
  // brightness.
  if (!saturation) {
    uint8 light;

    if (lightness < 0)
      light = 0;
    else if (lightness >= 1.0)
      light = 255;
    else
      light = SkDoubleToFixed(lightness) >> 8;

    return SkColorSetARGB(alpha, light, light, light);
  }

  double temp2 = (lightness < 0.5) ?
      (lightness * (1.0 + saturation)) :
      (lightness + saturation - (lightness * saturation));
  double temp1 = 2.0 * lightness - temp2;
  return SkColorSetARGB(alpha,
      calcHue(temp1, temp2, hue + 1.0 / 3.0),
      calcHue(temp1, temp2, hue),
      calcHue(temp1, temp2, hue - 1.0 / 3.0));
}

SkColor HSLShift(SkColor color, const HSL& shift) {
  HSL hsl;
  int alpha = SkColorGetA(color);
  SkColorToHSL(color, &hsl);

  // Replace the hue with the tint's hue.
  if (shift.h >= 0)
    hsl.h = shift.h;

  // Change the saturation.
  if (shift.s >= 0) {
    if (shift.s <= 0.5)
      hsl.s *= shift.s * 2.0;
    else
      hsl.s += (1.0 - hsl.s) * ((shift.s - 0.5) * 2.0);
  }

  SkColor result = HSLToSkColor(hsl, alpha);

  if (shift.l < 0)
    return result;

  // Lightness shifts in the style of popular image editors aren't actually
  // represented in HSL - the L value does have some effect on saturation.
  double r = static_cast<double>(SkColorGetR(result));
  double g = static_cast<double>(SkColorGetG(result));
  double b = static_cast<double>(SkColorGetB(result));
  if (shift.l <= 0.5) {
    r *= (shift.l * 2.0);
    g *= (shift.l * 2.0);
    b *= (shift.l * 2.0);
  } else {
    r += (255.0 - r) * ((shift.l - 0.5) * 2.0);
    g += (255.0 - g) * ((shift.l - 0.5) * 2.0);
    b += (255.0 - b) * ((shift.l - 0.5) * 2.0);
  }
  return SkColorSetARGB(alpha,
                        static_cast<int>(r),
                        static_cast<int>(g),
                        static_cast<int>(b));
}

void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) {
  DCHECK_EQ(kPMColor_SkColorType, bitmap.colorType());

  SkAutoLockPixels bitmap_lock(bitmap);

  int pixel_width = bitmap.width();
  int pixel_height = bitmap.height();
  for (int y = 0; y < pixel_height; ++y) {
    for (int x = 0; x < pixel_width; ++x)
      ++histogram[GetLuminanceForColor(bitmap.getColor(x, y))];
  }
}

SkColor AlphaBlend(SkColor foreground, SkColor background, SkAlpha alpha) {
  if (alpha == 0)
    return background;
  if (alpha == 255)
    return foreground;

  int f_alpha = SkColorGetA(foreground);
  int b_alpha = SkColorGetA(background);

  double normalizer = (f_alpha * alpha + b_alpha * (255 - alpha)) / 255.0;
  if (normalizer == 0.0)
    return SK_ColorTRANSPARENT;

  double f_weight = f_alpha * alpha / normalizer;
  double b_weight = b_alpha * (255 - alpha) / normalizer;

  double r = (SkColorGetR(foreground) * f_weight +
              SkColorGetR(background) * b_weight) / 255.0;
  double g = (SkColorGetG(foreground) * f_weight +
              SkColorGetG(background) * b_weight) / 255.0;
  double b = (SkColorGetB(foreground) * f_weight +
              SkColorGetB(background) * b_weight) / 255.0;

  return SkColorSetARGB(static_cast<int>(normalizer),
                        static_cast<int>(r),
                        static_cast<int>(g),
                        static_cast<int>(b));
}

SkColor BlendTowardOppositeLuminance(SkColor color, SkAlpha alpha) {
  unsigned char background_luminance =
      color_utils::GetLuminanceForColor(color);
  const SkColor blend_color =
      (background_luminance < 128) ? SK_ColorWHITE : SK_ColorBLACK;
  return color_utils::AlphaBlend(blend_color, color, alpha);
}

SkColor GetReadableColor(SkColor foreground, SkColor background) {
  const SkColor foreground2 = LumaInvertColor(foreground);
  const double background_luminance = RelativeLuminance(background);
  return (ContrastRatio(RelativeLuminance(foreground), background_luminance) >=
          ContrastRatio(RelativeLuminance(foreground2), background_luminance)) ?
      foreground : foreground2;
}

SkColor InvertColor(SkColor color) {
  return SkColorSetARGB(
      SkColorGetA(color),
      255 - SkColorGetR(color),
      255 - SkColorGetG(color),
      255 - SkColorGetB(color));
}

SkColor GetSysSkColor(int which) {
#if defined(OS_WIN)
  return skia::COLORREFToSkColor(GetSysColor(which));
#else
  NOTIMPLEMENTED();
  return SK_ColorLTGRAY;
#endif
}

}  // namespace color_utils

/* [<][>][^][v][top][bottom][index][help] */