|
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 #ifndef SkMaskGamma_DEFINED |
|
9 #define SkMaskGamma_DEFINED |
|
10 |
|
11 #include "SkTypes.h" |
|
12 #include "SkColor.h" |
|
13 #include "SkColorPriv.h" |
|
14 #include "SkRefCnt.h" |
|
15 |
|
16 /** |
|
17 * SkColorSpaceLuminance is used to convert luminances to and from linear and |
|
18 * perceptual color spaces. |
|
19 * |
|
20 * Luma is used to specify a linear luminance value [0.0, 1.0]. |
|
21 * Luminance is used to specify a luminance value in an arbitrary color space [0.0, 1.0]. |
|
22 */ |
|
23 class SkColorSpaceLuminance : SkNoncopyable { |
|
24 public: |
|
25 virtual ~SkColorSpaceLuminance() { } |
|
26 |
|
27 /** Converts a color component luminance in the color space to a linear luma. */ |
|
28 virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const = 0; |
|
29 /** Converts a linear luma to a color component luminance in the color space. */ |
|
30 virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const = 0; |
|
31 |
|
32 /** Converts a color to a luminance value. */ |
|
33 static U8CPU computeLuminance(SkScalar gamma, SkColor c) { |
|
34 const SkColorSpaceLuminance& luminance = Fetch(gamma); |
|
35 SkScalar r = luminance.toLuma(gamma, SkIntToScalar(SkColorGetR(c)) / 255); |
|
36 SkScalar g = luminance.toLuma(gamma, SkIntToScalar(SkColorGetG(c)) / 255); |
|
37 SkScalar b = luminance.toLuma(gamma, SkIntToScalar(SkColorGetB(c)) / 255); |
|
38 SkScalar luma = r * SK_LUM_COEFF_R + |
|
39 g * SK_LUM_COEFF_G + |
|
40 b * SK_LUM_COEFF_B; |
|
41 SkASSERT(luma <= SK_Scalar1); |
|
42 return SkScalarRoundToInt(luminance.fromLuma(gamma, luma) * 255); |
|
43 } |
|
44 |
|
45 /** Retrieves the SkColorSpaceLuminance for the given gamma. */ |
|
46 static const SkColorSpaceLuminance& Fetch(SkScalar gamma); |
|
47 }; |
|
48 |
|
49 ///@{ |
|
50 /** |
|
51 * Scales base <= 2^N-1 to 2^8-1 |
|
52 * @param N [1, 8] the number of bits used by base. |
|
53 * @param base the number to be scaled to [0, 255]. |
|
54 */ |
|
55 template<U8CPU N> static inline U8CPU sk_t_scale255(U8CPU base) { |
|
56 base <<= (8 - N); |
|
57 U8CPU lum = base; |
|
58 for (unsigned int i = N; i < 8; i += N) { |
|
59 lum |= base >> i; |
|
60 } |
|
61 return lum; |
|
62 } |
|
63 template<> /*static*/ inline U8CPU sk_t_scale255<1>(U8CPU base) { |
|
64 return base * 0xFF; |
|
65 } |
|
66 template<> /*static*/ inline U8CPU sk_t_scale255<2>(U8CPU base) { |
|
67 return base * 0x55; |
|
68 } |
|
69 template<> /*static*/ inline U8CPU sk_t_scale255<4>(U8CPU base) { |
|
70 return base * 0x11; |
|
71 } |
|
72 template<> /*static*/ inline U8CPU sk_t_scale255<8>(U8CPU base) { |
|
73 return base; |
|
74 } |
|
75 ///@} |
|
76 |
|
77 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend; |
|
78 |
|
79 void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast, |
|
80 const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma, |
|
81 const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma); |
|
82 |
|
83 /** |
|
84 * A regular mask contains linear alpha values. A gamma correcting mask |
|
85 * contains non-linear alpha values in an attempt to create gamma correct blits |
|
86 * in the presence of a gamma incorrect (linear) blend in the blitter. |
|
87 * |
|
88 * SkMaskGamma creates and maintains tables which convert linear alpha values |
|
89 * to gamma correcting alpha values. |
|
90 * @param R The number of luminance bits to use [1, 8] from the red channel. |
|
91 * @param G The number of luminance bits to use [1, 8] from the green channel. |
|
92 * @param B The number of luminance bits to use [1, 8] from the blue channel. |
|
93 */ |
|
94 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt { |
|
95 SK_DECLARE_INST_COUNT(SkTMaskGamma) |
|
96 public: |
|
97 |
|
98 /** Creates a linear SkTMaskGamma. */ |
|
99 SkTMaskGamma() : fIsLinear(true) { } |
|
100 |
|
101 /** |
|
102 * Creates tables to convert linear alpha values to gamma correcting alpha |
|
103 * values. |
|
104 * |
|
105 * @param contrast A value in the range [0.0, 1.0] which indicates the |
|
106 * amount of artificial contrast to add. |
|
107 * @param paint The color space in which the paint color was chosen. |
|
108 * @param device The color space of the target device. |
|
109 */ |
|
110 SkTMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) : fIsLinear(false) { |
|
111 const SkColorSpaceLuminance& paintConvert = SkColorSpaceLuminance::Fetch(paintGamma); |
|
112 const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma); |
|
113 for (U8CPU i = 0; i < (1 << MAX_LUM_BITS); ++i) { |
|
114 U8CPU lum = sk_t_scale255<MAX_LUM_BITS>(i); |
|
115 SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast, |
|
116 paintConvert, paintGamma, |
|
117 deviceConvert, deviceGamma); |
|
118 } |
|
119 } |
|
120 |
|
121 /** Given a color, returns the closest canonical color. */ |
|
122 static SkColor CanonicalColor(SkColor color) { |
|
123 return SkColorSetRGB( |
|
124 sk_t_scale255<R_LUM_BITS>(SkColorGetR(color) >> (8 - R_LUM_BITS)), |
|
125 sk_t_scale255<G_LUM_BITS>(SkColorGetG(color) >> (8 - G_LUM_BITS)), |
|
126 sk_t_scale255<B_LUM_BITS>(SkColorGetB(color) >> (8 - B_LUM_BITS))); |
|
127 } |
|
128 |
|
129 /** The type of the mask pre-blend which will be returned from preBlend(SkColor). */ |
|
130 typedef SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> PreBlend; |
|
131 |
|
132 /** |
|
133 * Provides access to the tables appropriate for converting linear alpha |
|
134 * values into gamma correcting alpha values when drawing the given color |
|
135 * through the mask. The destination color will be approximated. |
|
136 */ |
|
137 PreBlend preBlend(SkColor color) const; |
|
138 |
|
139 private: |
|
140 static const int MAX_LUM_BITS = |
|
141 B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS) |
|
142 ? B_LUM_BITS : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS); |
|
143 uint8_t fGammaTables[1 << MAX_LUM_BITS][256]; |
|
144 bool fIsLinear; |
|
145 |
|
146 typedef SkRefCnt INHERITED; |
|
147 }; |
|
148 |
|
149 |
|
150 /** |
|
151 * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to |
|
152 * convert a linear alpha value for a given channel to a gamma correcting alpha |
|
153 * value for that channel. This class is immutable. |
|
154 * |
|
155 * If fR, fG, or fB is NULL, all of them will be. This indicates that no mask |
|
156 * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as |
|
157 * a convenience function to test for the absence of this case. |
|
158 */ |
|
159 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend { |
|
160 private: |
|
161 SkTMaskPreBlend(const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>* parent, |
|
162 const uint8_t* r, const uint8_t* g, const uint8_t* b) |
|
163 : fParent(SkSafeRef(parent)), fR(r), fG(g), fB(b) { } |
|
164 |
|
165 SkAutoTUnref<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> > fParent; |
|
166 friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>; |
|
167 public: |
|
168 /** Creates a non applicable SkTMaskPreBlend. */ |
|
169 SkTMaskPreBlend() : fParent(), fR(NULL), fG(NULL), fB(NULL) { } |
|
170 |
|
171 /** |
|
172 * This copy contructor exists for correctness, but should never be called |
|
173 * when return value optimization is enabled. |
|
174 */ |
|
175 SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that) |
|
176 : fParent(SkSafeRef(that.fParent.get())), fR(that.fR), fG(that.fG), fB(that.fB) { } |
|
177 |
|
178 ~SkTMaskPreBlend() { } |
|
179 |
|
180 /** True if this PreBlend should be applied. When false, fR, fG, and fB are NULL. */ |
|
181 bool isApplicable() const { |
|
182 return NULL != this->fG; |
|
183 } |
|
184 |
|
185 const uint8_t* fR; |
|
186 const uint8_t* fG; |
|
187 const uint8_t* fB; |
|
188 }; |
|
189 |
|
190 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> |
|
191 SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> |
|
192 SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const { |
|
193 return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>() |
|
194 : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(this, |
|
195 fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)], |
|
196 fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)], |
|
197 fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]); |
|
198 } |
|
199 |
|
200 ///@{ |
|
201 /** |
|
202 * If APPLY_LUT is false, returns component unchanged. |
|
203 * If APPLY_LUT is true, returns lut[component]. |
|
204 * @param APPLY_LUT whether or not the look-up table should be applied to component. |
|
205 * @component the initial component. |
|
206 * @lut a look-up table which transforms the component. |
|
207 */ |
|
208 template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) { |
|
209 return component; |
|
210 } |
|
211 template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) { |
|
212 return lut[component]; |
|
213 } |
|
214 ///@} |
|
215 |
|
216 #endif |