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