197efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com/* 297efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com * Copyright 2012 Google Inc. 397efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com * 497efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com * Use of this source code is governed by a BSD-style license that can be 597efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com * found in the LICENSE file. 697efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com */ 797efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 897efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com#include "SkTypes.h" 997efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 1097efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com#include "SkColor.h" 1197efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com#include "SkFloatingPoint.h" 1297efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com#include "SkMaskGamma.h" 1397efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 14b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.comclass SkLinearColorSpaceLuminance : public SkColorSpaceLuminance { 15b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com virtual SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE { 16b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com SkASSERT(SK_Scalar1 == gamma); 17b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com return luminance; 1897efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com } 19b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com virtual SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE { 20b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com SkASSERT(SK_Scalar1 == gamma); 21b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com return luma; 2297efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com } 23b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com}; 2497efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 25b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.comclass SkGammaColorSpaceLuminance : public SkColorSpaceLuminance { 26b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const SK_OVERRIDE { 27b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com return SkScalarPow(luminance, gamma); 28b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } 29b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const SK_OVERRIDE { 30b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com return SkScalarPow(luma, SkScalarInvert(gamma)); 31b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } 32b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com}; 33b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com 34b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.comclass SkSRGBColorSpaceLuminance : public SkColorSpaceLuminance { 35b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com virtual SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE { 36b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com SkASSERT(0 == gamma); 37b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com //The magic numbers are derived from the sRGB specification. 38b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com //See http://www.color.org/chardata/rgb/srgb.xalter . 394b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org if (luminance <= 0.04045f) { 404b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org return luminance / 12.92f; 41b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } 424b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org return SkScalarPow((luminance + 0.055f) / 1.055f, 434b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org 2.4f); 44b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } 45b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com virtual SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE { 46b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com SkASSERT(0 == gamma); 47b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com //The magic numbers are derived from the sRGB specification. 48b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com //See http://www.color.org/chardata/rgb/srgb.xalter . 494b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org if (luma <= 0.0031308f) { 504b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org return luma * 12.92f; 51b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } 524b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org return 1.055f * SkScalarPow(luma, SkScalarInvert(2.4f)) 534b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org - 0.055f; 54b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } 55b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com}; 5697efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 57b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com/*static*/ const SkColorSpaceLuminance& SkColorSpaceLuminance::Fetch(SkScalar gamma) { 58b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com static SkLinearColorSpaceLuminance gSkLinearColorSpaceLuminance; 59b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com static SkGammaColorSpaceLuminance gSkGammaColorSpaceLuminance; 60b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com static SkSRGBColorSpaceLuminance gSkSRGBColorSpaceLuminance; 61ae30f5601940c78f4537ee48a6316cfac6740712bungeman@google.com 62b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com if (0 == gamma) { 63b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com return gSkSRGBColorSpaceLuminance; 64b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } else if (SK_Scalar1 == gamma) { 65b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com return gSkLinearColorSpaceLuminance; 66b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } else { 67b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com return gSkGammaColorSpaceLuminance; 68b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com } 69ae30f5601940c78f4537ee48a6316cfac6740712bungeman@google.com} 70ae30f5601940c78f4537ee48a6316cfac6740712bungeman@google.com 7197efada074e4806479f1350ab1508939c2fdcb53bungeman@google.comstatic float apply_contrast(float srca, float contrast) { 7297efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com return srca + ((1.0f - srca) * contrast * srca); 7397efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com} 7497efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 7597efada074e4806479f1350ab1508939c2fdcb53bungeman@google.comvoid SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast, 76b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma, 77b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma) { 7897efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com const float src = (float)srcI / 255.0f; 79b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com const float linSrc = srcConvert.toLuma(srcGamma, src); 80fd668cfffe3fdcfbf6e0b858343a62818d337590bungeman@google.com //Guess at the dst. The perceptual inverse provides smaller visual 81fd668cfffe3fdcfbf6e0b858343a62818d337590bungeman@google.com //discontinuities when slight changes to desaturated colors cause a channel 82fd668cfffe3fdcfbf6e0b858343a62818d337590bungeman@google.com //to map to a different correcting lut with neighboring srcI. 83fd668cfffe3fdcfbf6e0b858343a62818d337590bungeman@google.com //See https://code.google.com/p/chromium/issues/detail?id=141425#c59 . 84fd668cfffe3fdcfbf6e0b858343a62818d337590bungeman@google.com const float dst = 1.0f - src; 85b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com const float linDst = dstConvert.toLuma(dstGamma, dst); 8697efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 8797efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com //Contrast value tapers off to 0 as the src luminance becomes white 8897efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com const float adjustedContrast = SkScalarToFloat(contrast) * linDst; 8997efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 9097efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com //Remove discontinuity and instability when src is close to dst. 9197efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com //The value 1/256 is arbitrary and appears to contain the instability. 9297efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com if (fabs(src - dst) < (1.0f / 256.0f)) { 93d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com float ii = 0.0f; 94d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com for (int i = 0; i < 256; ++i, ii += 1.0f) { 95d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com float rawSrca = ii / 255.0f; 9697efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com float srca = apply_contrast(rawSrca, adjustedContrast); 9797efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com table[i] = SkToU8(sk_float_round2int(255.0f * srca)); 9897efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com } 9997efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com } else { 100d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com // Avoid slow int to float conversion. 101d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com float ii = 0.0f; 102d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com for (int i = 0; i < 256; ++i, ii += 1.0f) { 103d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com // 'rawSrca += 1.0f / 255.0f' and even 104d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com // 'rawSrca = i * (1.0f / 255.0f)' can add up to more than 1.0f. 105d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com // When this happens the table[255] == 0x0 instead of 0xff. 106d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com // See http://code.google.com/p/chromium/issues/detail?id=146466 107d92f336ac73d48128e1c2d82de736ed250749b64bungeman@google.com float rawSrca = ii / 255.0f; 10897efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com float srca = apply_contrast(rawSrca, adjustedContrast); 10997efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com SkASSERT(srca <= 1.0f); 11097efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com float dsta = 1.0f - srca; 11197efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 11297efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com //Calculate the output we want. 11397efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com float linOut = (linSrc * srca + dsta * linDst); 11497efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com SkASSERT(linOut <= 1.0f); 115b1a72cb90a9b515978bc9abda1f267f31a544e08bungeman@google.com float out = dstConvert.fromLuma(dstGamma, linOut); 11697efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 11797efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com //Undo what the blit blend will do. 11897efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com float result = (out - dst) / (src - dst); 11997efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com SkASSERT(sk_float_round2int(255.0f * result) <= 255); 12097efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com 12197efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com table[i] = SkToU8(sk_float_round2int(255.0f * result)); 12297efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com } 12397efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com } 12497efada074e4806479f1350ab1508939c2fdcb53bungeman@google.com} 125