Line data Source code
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 0 : class SkColorSpaceLuminance : SkNoncopyable {
24 : public:
25 0 : 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 63 : template<U8CPU N> static inline U8CPU sk_t_scale255(U8CPU base) {
56 63 : base <<= (8 - N);
57 63 : U8CPU lum = base;
58 189 : for (unsigned int i = N; i < 8; i += N) {
59 126 : lum |= base >> i;
60 : }
61 63 : 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 0 : template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt {
95 :
96 : public:
97 :
98 : /** Creates a linear SkTMaskGamma. */
99 2 : 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 0 : SkTMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) : fIsLinear(false) {
111 0 : const SkColorSpaceLuminance& paintConvert = SkColorSpaceLuminance::Fetch(paintGamma);
112 0 : const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma);
113 0 : for (U8CPU i = 0; i < (1 << MAX_LUM_BITS); ++i) {
114 0 : U8CPU lum = sk_t_scale255<MAX_LUM_BITS>(i);
115 0 : SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast,
116 : paintConvert, paintGamma,
117 : deviceConvert, deviceGamma);
118 : }
119 0 : }
120 :
121 : /** Given a color, returns the closest canonical color. */
122 21 : static SkColor CanonicalColor(SkColor color) {
123 21 : 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 : /**
140 : * Get dimensions for the full table set, so it can be allocated as a block.
141 : */
142 0 : void getGammaTableDimensions(int* tableWidth, int* numTables) const {
143 0 : *tableWidth = 256;
144 0 : *numTables = (1 << MAX_LUM_BITS);
145 0 : }
146 :
147 : /**
148 : * Provides direct access to the full table set, so it can be uploaded
149 : * into a texture.
150 : */
151 0 : const uint8_t* getGammaTables() const {
152 0 : return (const uint8_t*) fGammaTables;
153 : }
154 :
155 : private:
156 : static const int MAX_LUM_BITS =
157 : B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)
158 : ? B_LUM_BITS : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS);
159 : uint8_t fGammaTables[1 << MAX_LUM_BITS][256];
160 : bool fIsLinear;
161 :
162 : typedef SkRefCnt INHERITED;
163 : };
164 :
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 : *
171 : * If fR, fG, or fB is nullptr, all of them will be. This indicates that no mask
172 : * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as
173 : * a convenience function to test for the absence of this case.
174 : */
175 : template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
176 : private:
177 0 : SkTMaskPreBlend(sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> parent,
178 : const uint8_t* r, const uint8_t* g, const uint8_t* b)
179 0 : : fParent(std::move(parent)), fR(r), fG(g), fB(b) { }
180 :
181 : sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> fParent;
182 : friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
183 : public:
184 : /** Creates a non applicable SkTMaskPreBlend. */
185 4 : SkTMaskPreBlend() : fParent(), fR(nullptr), fG(nullptr), fB(nullptr) { }
186 :
187 : /**
188 : * This copy contructor exists for correctness, but should never be called
189 : * when return value optimization is enabled.
190 : */
191 : SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
192 : : fParent(that.fParent), fR(that.fR), fG(that.fG), fB(that.fB) { }
193 :
194 0 : ~SkTMaskPreBlend() { }
195 :
196 : /** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */
197 58 : bool isApplicable() const { return SkToBool(this->fG); }
198 :
199 : const uint8_t* fR;
200 : const uint8_t* fG;
201 : const uint8_t* fB;
202 : };
203 :
204 : template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
205 : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
206 2 : SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const {
207 2 : return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>()
208 : : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(sk_ref_sp(this),
209 0 : fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)],
210 0 : fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)],
211 2 : fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]);
212 : }
213 :
214 : ///@{
215 : /**
216 : * If APPLY_LUT is false, returns component unchanged.
217 : * If APPLY_LUT is true, returns lut[component].
218 : * @param APPLY_LUT whether or not the look-up table should be applied to component.
219 : * @component the initial component.
220 : * @lut a look-up table which transforms the component.
221 : */
222 51573 : template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
223 51573 : return component;
224 : }
225 0 : template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
226 0 : return lut[component];
227 : }
228 : ///@}
229 :
230 : #endif
|