• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
~SkColorSpaceLuminance()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. */
computeLuminance(SkScalar gamma,SkColor c)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 * SkFloatToScalar(SK_LUM_COEFF_R) +
39                         g * SkFloatToScalar(SK_LUM_COEFF_G) +
40                         b * SkFloatToScalar(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  */
sk_t_scale255(U8CPU base)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 public:
SK_DECLARE_INST_COUNT_TEMPLATE(SkTMaskGamma)96     SK_DECLARE_INST_COUNT_TEMPLATE(SkTMaskGamma)
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      */
SkTMaskGamma(SkScalar contrast,SkScalar paintGamma,SkScalar deviceGamma)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. */
CanonicalColor(SkColor 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 #define MacroComma ,
151 SK_DEFINE_INST_COUNT_TEMPLATE(
152     template <int R_LUM_BITS MacroComma int G_LUM_BITS MacroComma int B_LUM_BITS>,
153     SkTMaskGamma<R_LUM_BITS MacroComma G_LUM_BITS MacroComma B_LUM_BITS>);
154 
155 /**
156  * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to
157  * convert a linear alpha value for a given channel to a gamma correcting alpha
158  * value for that channel. This class is immutable.
159  *
160  * If fR, fG, or fB is NULL, all of them will be. This indicates that no mask
161  * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as
162  * a convenience function to test for the absence of this case.
163  */
164 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
165 private:
SkTMaskPreBlend(const SkTMaskGamma<R_LUM_BITS,G_LUM_BITS,B_LUM_BITS> * parent,const uint8_t * r,const uint8_t * g,const uint8_t * b)166     SkTMaskPreBlend(const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>* parent,
167                     const uint8_t* r, const uint8_t* g, const uint8_t* b)
168     : fParent(SkSafeRef(parent)), fR(r), fG(g), fB(b) { }
169 
170     SkAutoTUnref<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> > fParent;
171     friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
172 public:
173     /** Creates a non applicable SkTMaskPreBlend. */
SkTMaskPreBlend()174     SkTMaskPreBlend() : fParent(), fR(NULL), fG(NULL), fB(NULL) { }
175 
176     /**
177      * This copy contructor exists for correctness, but should never be called
178      * when return value optimization is enabled.
179      */
SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS,G_LUM_BITS,B_LUM_BITS> & that)180     SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
181     : fParent(SkSafeRef(that.fParent.get())), fR(that.fR), fG(that.fG), fB(that.fB) { }
182 
~SkTMaskPreBlend()183     ~SkTMaskPreBlend() { }
184 
185     /** True if this PreBlend should be applied. When false, fR, fG, and fB are NULL. */
isApplicable()186     bool isApplicable() const {
187         return NULL != this->fG;
188     }
189 
190     const uint8_t* fR;
191     const uint8_t* fG;
192     const uint8_t* fB;
193 };
194 
195 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
196 SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
preBlend(SkColor color)197 SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const {
198     return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>()
199                      : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(this,
200                          fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)],
201                          fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)],
202                          fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]);
203 }
204 
205 ///@{
206 /**
207  *  If APPLY_LUT is false, returns component unchanged.
208  *  If APPLY_LUT is true, returns lut[component].
209  *  @param APPLY_LUT whether or not the look-up table should be applied to component.
210  *  @component the initial component.
211  *  @lut a look-up table which transforms the component.
212  */
sk_apply_lut_if(U8CPU component,const uint8_t *)213 template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
214     return component;
215 }
216 template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
217     return lut[component];
218 }
219 ///@}
220 
221 #endif
222