1 | /* |
2 | * Copyright 2012 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "src/core/SkMaskGamma.h" |
9 | |
10 | #include "include/core/SkColor.h" |
11 | #include "include/core/SkTypes.h" |
12 | #include "include/private/SkFloatingPoint.h" |
13 | #include "include/private/SkTo.h" |
14 | |
15 | class SkLinearColorSpaceLuminance : public SkColorSpaceLuminance { |
16 | SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const override { |
17 | SkASSERT(SK_Scalar1 == gamma); |
18 | return luminance; |
19 | } |
20 | SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const override { |
21 | SkASSERT(SK_Scalar1 == gamma); |
22 | return luma; |
23 | } |
24 | }; |
25 | |
26 | class SkGammaColorSpaceLuminance : public SkColorSpaceLuminance { |
27 | SkScalar toLuma(SkScalar gamma, SkScalar luminance) const override { |
28 | return SkScalarPow(luminance, gamma); |
29 | } |
30 | SkScalar fromLuma(SkScalar gamma, SkScalar luma) const override { |
31 | return SkScalarPow(luma, SkScalarInvert(gamma)); |
32 | } |
33 | }; |
34 | |
35 | class SkSRGBColorSpaceLuminance : public SkColorSpaceLuminance { |
36 | SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const override { |
37 | SkASSERT(0 == gamma); |
38 | //The magic numbers are derived from the sRGB specification. |
39 | //See http://www.color.org/chardata/rgb/srgb.xalter . |
40 | if (luminance <= 0.04045f) { |
41 | return luminance / 12.92f; |
42 | } |
43 | return SkScalarPow((luminance + 0.055f) / 1.055f, |
44 | 2.4f); |
45 | } |
46 | SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const override { |
47 | SkASSERT(0 == gamma); |
48 | //The magic numbers are derived from the sRGB specification. |
49 | //See http://www.color.org/chardata/rgb/srgb.xalter . |
50 | if (luma <= 0.0031308f) { |
51 | return luma * 12.92f; |
52 | } |
53 | return 1.055f * SkScalarPow(luma, SkScalarInvert(2.4f)) |
54 | - 0.055f; |
55 | } |
56 | }; |
57 | |
58 | /*static*/ const SkColorSpaceLuminance& SkColorSpaceLuminance::Fetch(SkScalar gamma) { |
59 | static SkLinearColorSpaceLuminance gSkLinearColorSpaceLuminance; |
60 | static SkGammaColorSpaceLuminance gSkGammaColorSpaceLuminance; |
61 | static SkSRGBColorSpaceLuminance gSkSRGBColorSpaceLuminance; |
62 | |
63 | if (0 == gamma) { |
64 | return gSkSRGBColorSpaceLuminance; |
65 | } else if (SK_Scalar1 == gamma) { |
66 | return gSkLinearColorSpaceLuminance; |
67 | } else { |
68 | return gSkGammaColorSpaceLuminance; |
69 | } |
70 | } |
71 | |
72 | static float apply_contrast(float srca, float contrast) { |
73 | return srca + ((1.0f - srca) * contrast * srca); |
74 | } |
75 | |
76 | void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast, |
77 | const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma, |
78 | const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma) { |
79 | const float src = (float)srcI / 255.0f; |
80 | const float linSrc = srcConvert.toLuma(srcGamma, src); |
81 | //Guess at the dst. The perceptual inverse provides smaller visual |
82 | //discontinuities when slight changes to desaturated colors cause a channel |
83 | //to map to a different correcting lut with neighboring srcI. |
84 | //See https://code.google.com/p/chromium/issues/detail?id=141425#c59 . |
85 | const float dst = 1.0f - src; |
86 | const float linDst = dstConvert.toLuma(dstGamma, dst); |
87 | |
88 | //Contrast value tapers off to 0 as the src luminance becomes white |
89 | const float adjustedContrast = SkScalarToFloat(contrast) * linDst; |
90 | |
91 | //Remove discontinuity and instability when src is close to dst. |
92 | //The value 1/256 is arbitrary and appears to contain the instability. |
93 | if (fabs(src - dst) < (1.0f / 256.0f)) { |
94 | float ii = 0.0f; |
95 | for (int i = 0; i < 256; ++i, ii += 1.0f) { |
96 | float rawSrca = ii / 255.0f; |
97 | float srca = apply_contrast(rawSrca, adjustedContrast); |
98 | table[i] = SkToU8(sk_float_round2int(255.0f * srca)); |
99 | } |
100 | } else { |
101 | // Avoid slow int to float conversion. |
102 | float ii = 0.0f; |
103 | for (int i = 0; i < 256; ++i, ii += 1.0f) { |
104 | // 'rawSrca += 1.0f / 255.0f' and even |
105 | // 'rawSrca = i * (1.0f / 255.0f)' can add up to more than 1.0f. |
106 | // When this happens the table[255] == 0x0 instead of 0xff. |
107 | // See http://code.google.com/p/chromium/issues/detail?id=146466 |
108 | float rawSrca = ii / 255.0f; |
109 | float srca = apply_contrast(rawSrca, adjustedContrast); |
110 | SkASSERT(srca <= 1.0f); |
111 | float dsta = 1.0f - srca; |
112 | |
113 | //Calculate the output we want. |
114 | float linOut = (linSrc * srca + dsta * linDst); |
115 | SkASSERT(linOut <= 1.0f); |
116 | float out = dstConvert.fromLuma(dstGamma, linOut); |
117 | |
118 | //Undo what the blit blend will do. |
119 | float result = (out - dst) / (src - dst); |
120 | SkASSERT(sk_float_round2int(255.0f * result) <= 255); |
121 | |
122 | table[i] = SkToU8(sk_float_round2int(255.0f * result)); |
123 | } |
124 | } |
125 | } |
126 | |