michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkTypes.h" michael@0: michael@0: #include "SkColor.h" michael@0: #include "SkFloatingPoint.h" michael@0: #include "SkMaskGamma.h" michael@0: michael@0: class SkLinearColorSpaceLuminance : public SkColorSpaceLuminance { michael@0: virtual SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE { michael@0: SkASSERT(SK_Scalar1 == gamma); michael@0: return luminance; michael@0: } michael@0: virtual SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE { michael@0: SkASSERT(SK_Scalar1 == gamma); michael@0: return luma; michael@0: } michael@0: }; michael@0: michael@0: class SkGammaColorSpaceLuminance : public SkColorSpaceLuminance { michael@0: virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const SK_OVERRIDE { michael@0: return SkScalarPow(luminance, gamma); michael@0: } michael@0: virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const SK_OVERRIDE { michael@0: return SkScalarPow(luma, SkScalarInvert(gamma)); michael@0: } michael@0: }; michael@0: michael@0: class SkSRGBColorSpaceLuminance : public SkColorSpaceLuminance { michael@0: virtual SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE { michael@0: SkASSERT(0 == gamma); michael@0: //The magic numbers are derived from the sRGB specification. michael@0: //See http://www.color.org/chardata/rgb/srgb.xalter . michael@0: if (luminance <= 0.04045f) { michael@0: return luminance / 12.92f; michael@0: } michael@0: return SkScalarPow((luminance + 0.055f) / 1.055f, michael@0: 2.4f); michael@0: } michael@0: virtual SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE { michael@0: SkASSERT(0 == gamma); michael@0: //The magic numbers are derived from the sRGB specification. michael@0: //See http://www.color.org/chardata/rgb/srgb.xalter . michael@0: if (luma <= 0.0031308f) { michael@0: return luma * 12.92f; michael@0: } michael@0: return 1.055f * SkScalarPow(luma, SkScalarInvert(2.4f)) michael@0: - 0.055f; michael@0: } michael@0: }; michael@0: michael@0: /*static*/ const SkColorSpaceLuminance& SkColorSpaceLuminance::Fetch(SkScalar gamma) { michael@0: static SkLinearColorSpaceLuminance gSkLinearColorSpaceLuminance; michael@0: static SkGammaColorSpaceLuminance gSkGammaColorSpaceLuminance; michael@0: static SkSRGBColorSpaceLuminance gSkSRGBColorSpaceLuminance; michael@0: michael@0: if (0 == gamma) { michael@0: return gSkSRGBColorSpaceLuminance; michael@0: } else if (SK_Scalar1 == gamma) { michael@0: return gSkLinearColorSpaceLuminance; michael@0: } else { michael@0: return gSkGammaColorSpaceLuminance; michael@0: } michael@0: } michael@0: michael@0: static float apply_contrast(float srca, float contrast) { michael@0: return srca + ((1.0f - srca) * contrast * srca); michael@0: } michael@0: michael@0: void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast, michael@0: const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma, michael@0: const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma) { michael@0: const float src = (float)srcI / 255.0f; michael@0: const float linSrc = srcConvert.toLuma(srcGamma, src); michael@0: //Guess at the dst. The perceptual inverse provides smaller visual michael@0: //discontinuities when slight changes to desaturated colors cause a channel michael@0: //to map to a different correcting lut with neighboring srcI. michael@0: //See https://code.google.com/p/chromium/issues/detail?id=141425#c59 . michael@0: const float dst = 1.0f - src; michael@0: const float linDst = dstConvert.toLuma(dstGamma, dst); michael@0: michael@0: //Contrast value tapers off to 0 as the src luminance becomes white michael@0: const float adjustedContrast = SkScalarToFloat(contrast) * linDst; michael@0: michael@0: //Remove discontinuity and instability when src is close to dst. michael@0: //The value 1/256 is arbitrary and appears to contain the instability. michael@0: if (fabs(src - dst) < (1.0f / 256.0f)) { michael@0: float ii = 0.0f; michael@0: for (int i = 0; i < 256; ++i, ii += 1.0f) { michael@0: float rawSrca = ii / 255.0f; michael@0: float srca = apply_contrast(rawSrca, adjustedContrast); michael@0: table[i] = SkToU8(sk_float_round2int(255.0f * srca)); michael@0: } michael@0: } else { michael@0: // Avoid slow int to float conversion. michael@0: float ii = 0.0f; michael@0: for (int i = 0; i < 256; ++i, ii += 1.0f) { michael@0: // 'rawSrca += 1.0f / 255.0f' and even michael@0: // 'rawSrca = i * (1.0f / 255.0f)' can add up to more than 1.0f. michael@0: // When this happens the table[255] == 0x0 instead of 0xff. michael@0: // See http://code.google.com/p/chromium/issues/detail?id=146466 michael@0: float rawSrca = ii / 255.0f; michael@0: float srca = apply_contrast(rawSrca, adjustedContrast); michael@0: SkASSERT(srca <= 1.0f); michael@0: float dsta = 1.0f - srca; michael@0: michael@0: //Calculate the output we want. michael@0: float linOut = (linSrc * srca + dsta * linDst); michael@0: SkASSERT(linOut <= 1.0f); michael@0: float out = dstConvert.fromLuma(dstGamma, linOut); michael@0: michael@0: //Undo what the blit blend will do. michael@0: float result = (out - dst) / (src - dst); michael@0: SkASSERT(sk_float_round2int(255.0f * result) <= 255); michael@0: michael@0: table[i] = SkToU8(sk_float_round2int(255.0f * result)); michael@0: } michael@0: } michael@0: }