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