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 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 /** 140 * Get dimensions for the full table set, so it can be allocated as a block. 141 */ 142 void getGammaTableDimensions(int* tableWidth, int* numTables) const { 143 *tableWidth = 256; 144 *numTables = (1 << MAX_LUM_BITS); 145 } 146 147 /** 148 * Provides direct access to the full table set, so it can be uploaded 149 * into a texture. 150 */ 151 const uint8_t* getGammaTables() const { 152 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 SkTMaskPreBlend(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 : fParent(SkSafeRef(parent)), fR(r), fG(g), fB(b) { } 180 181 SkAutoTUnref<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 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(SkSafeRef(that.fParent.get())), fR(that.fR), fG(that.fG), fB(that.fB) { } 193 194 ~SkTMaskPreBlend() { } 195 196 /** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */ 197 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 SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const { 207 return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>() 208 : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(this, 209 fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)], 210 fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)], 211 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 template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) { 223 return component; 224 } 225 template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) { 226 return lut[component]; 227 } 228 ///@} 229 230 #endif 231