// Copyright (c) 2006-2008 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 "app/gfx/color_utils.h" #include #if defined(OS_WIN) #include #endif #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 "skia/include/SkBitmap.h" namespace color_utils { // These transformations are based on the equations in: // http://en.wikipedia.org/wiki/Lab_color // http://en.wikipedia.org/wiki/SRGB_color_space#Specification_of_the_transformation // See also: // http://www.brucelindbloom.com/index.html?ColorCalculator.html static const double kCIEConversionAlpha = 0.055; static const double kCIEConversionGamma = 2.2; static const double kE = 0.008856; static const double kK = 903.3; static double CIEConvertNonLinear(uint8 color_component) { double color_component_d = static_cast(color_component) / 255.0; if (color_component_d > 0.04045) { double base = (color_component_d + kCIEConversionAlpha) / (1 + kCIEConversionAlpha); return pow(base, kCIEConversionGamma); } else { return color_component_d / 12.92; } } // Note: this works only for sRGB. void SkColorToCIEXYZ(SkColor c, CIE_XYZ* xyz) { uint8 r = SkColorGetR(c); uint8 g = SkColorGetG(c); uint8 b = SkColorGetB(c); xyz->X = 0.4124 * CIEConvertNonLinear(r) + 0.3576 * CIEConvertNonLinear(g) + 0.1805 * CIEConvertNonLinear(b); xyz->Y = 0.2126 * CIEConvertNonLinear(r) + 0.7152 * CIEConvertNonLinear(g) + 0.0722 * CIEConvertNonLinear(g); xyz->Z = 0.0193 * CIEConvertNonLinear(r) + 0.1192 * CIEConvertNonLinear(g) + 0.9505 * CIEConvertNonLinear(b); } static double LabConvertNonLinear(double value) { if (value > 0.008856) { double goat = pow(value, static_cast(1) / 3); return goat; } return (kK * value + 16) / 116; } void CIEXYZToLabColor(const CIE_XYZ& xyz, LabColor* lab) { CIE_XYZ white_xyz; SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); double fx = LabConvertNonLinear(xyz.X / white_xyz.X); double fy = LabConvertNonLinear(xyz.Y / white_xyz.Y); double fz = LabConvertNonLinear(xyz.Z / white_xyz.Z); lab->L = static_cast(116 * fy) - 16; lab->a = static_cast(500 * (fx - fy)); lab->b = static_cast(200 * (fy - fz)); } static uint8 sRGBColorComponentFromLinearComponent(double component) { double result; if (component <= 0.0031308) { result = 12.92 * component; } else { result = (1 + kCIEConversionAlpha) * pow(component, (static_cast(1) / 2.4)) - kCIEConversionAlpha; } return std::min(static_cast(255), static_cast(result * 255)); } SkColor CIEXYZToSkColor(SkAlpha alpha, const CIE_XYZ& xyz) { double r_linear = 3.2410 * xyz.X - 1.5374 * xyz.Y - 0.4986 * xyz.Z; double g_linear = -0.9692 * xyz.X + 1.8760 * xyz.Y + 0.0416 * xyz.Z; double b_linear = 0.0556 * xyz.X - 0.2040 * xyz.Y + 1.0570 * xyz.Z; uint8 r = sRGBColorComponentFromLinearComponent(r_linear); uint8 g = sRGBColorComponentFromLinearComponent(g_linear); uint8 b = sRGBColorComponentFromLinearComponent(b_linear); return SkColorSetARGB(alpha, r, g, b); } static double gen_yr(const LabColor& lab) { if (lab.L > (kE * kK)) return pow((lab.L + 16.0) / 116, 3.0); return static_cast(lab.L) / kK; } static double fy(const LabColor& lab) { double yr = gen_yr(lab); if (yr > kE) return (lab.L + 16.0) / 116; return (kK * yr + 16.0) / 116; } static double fx(const LabColor& lab) { return (static_cast(lab.a) / 500) + fy(lab); } static double gen_xr(const LabColor& lab) { double x = fx(lab); double x_cubed = pow(x, 3.0); if (x_cubed > kE) return x_cubed; return (116.0 * x - 16.0) / kK; } static double fz(const LabColor& lab) { return fy(lab) - (static_cast(lab.b) / 200); } static double gen_zr(const LabColor& lab) { double z = fz(lab); double z_cubed = pow(z, 3.0); if (z_cubed > kE) return z_cubed; return (116.0 * z - 16.0) / kK; } void LabColorToCIEXYZ(const LabColor& lab, CIE_XYZ* xyz) { CIE_XYZ result; CIE_XYZ white_xyz; SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); result.X = gen_xr(lab) * white_xyz.X; result.Y = gen_yr(lab) * white_xyz.Y; result.Z = gen_zr(lab) * white_xyz.Z; *xyz = result; } void SkColorToLabColor(SkColor c, LabColor* lab) { CIE_XYZ xyz; SkColorToCIEXYZ(c, &xyz); CIEXYZToLabColor(xyz, lab); } SkColor LabColorToSkColor(const LabColor& lab, SkAlpha alpha) { CIE_XYZ xyz; LabColorToCIEXYZ(lab, &xyz); return CIEXYZToSkColor(alpha, xyz); } static const int kCloseToBoundary = 64; static const int kAverageBoundary = 15; bool IsColorCloseToTransparent(SkAlpha alpha) { return alpha < kCloseToBoundary; } bool IsColorCloseToGrey(int r, int g, int b) { int average = (r + g + b) / 3; return (abs(r - average) < kAverageBoundary) && (abs(g - average) < kAverageBoundary) && (abs(b - average) < kAverageBoundary); } SkColor GetAverageColorOfFavicon(SkBitmap* favicon, SkAlpha alpha) { int r = 0, g = 0, b = 0; SkAutoLockPixels favicon_lock(*favicon); SkColor* pixels = static_cast(favicon->getPixels()); // Assume ARGB_8888 format. DCHECK(favicon->getConfig() == SkBitmap::kARGB_8888_Config); SkColor* current_color = pixels; DCHECK(favicon->width() <= 16 && favicon->height() <= 16); int pixel_count = favicon->width() * favicon->height(); int color_count = 0; for (int i = 0; i < pixel_count; ++i, ++current_color) { // Disregard this color if it is close to black, close to white, or close // to transparent since any of those pixels do not contribute much to the // color makeup of this icon. int cr = SkColorGetR(*current_color); int cg = SkColorGetG(*current_color); int cb = SkColorGetB(*current_color); if (IsColorCloseToTransparent(SkColorGetA(*current_color)) || IsColorCloseToGrey(cr, cg, cb)) continue; r += cr; g += cg; b += cb; ++color_count; } SkColor result; if (color_count > 0) { result = SkColorSetARGB(alpha, r / color_count, g / color_count, b / color_count); } else { result = SkColorSetARGB(alpha, 0, 0, 0); } return result; } inline int GetLumaForColor(SkColor* color) { int r = SkColorGetR(*color); int g = SkColorGetG(*color); int b = SkColorGetB(*color); int luma = static_cast(0.3*r + 0.59*g + 0.11*b); if (luma < 0) luma = 0; else if (luma > 255) luma = 255; return luma; } void BuildLumaHistogram(SkBitmap* bitmap, int histogram[256]) { SkAutoLockPixels bitmap_lock(*bitmap); // Assume ARGB_8888 format. DCHECK(bitmap->getConfig() == SkBitmap::kARGB_8888_Config); int pixel_width = bitmap->width(); int pixel_height = bitmap->height(); for (int y = 0; y < pixel_height; ++y) { SkColor* current_color = static_cast(bitmap->getAddr32(0, y)); for (int x = 0; x < pixel_width; ++x, ++current_color) { histogram[GetLumaForColor(current_color)]++; } } } SkColor SetColorAlpha(SkColor c, SkAlpha alpha) { return SkColorSetARGB(alpha, SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); } SkColor GetSysSkColor(int which) { #if defined(OS_WIN) return skia::COLORREFToSkColor(::GetSysColor(which)); #else NOTIMPLEMENTED(); return SK_ColorLTGRAY; #endif } } // namespace color_utils