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