bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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: |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 25 | virtual ~SkColorSpaceLuminance() { } |
robertphillips@google.com | cb73b31 | 2012-08-15 14:52:58 +0000 | [diff] [blame] | 26 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 27 | /** Converts a color component luminance in the color space to a linear luma. */ |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 28 | virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const = 0; |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 29 | /** Converts a linear luma to a color component luminance in the color space. */ |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 30 | virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const = 0; |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 31 | |
| 32 | /** Converts a color to a luminance value. */ |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 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); |
commit-bot@chromium.org | 4b413c8 | 2013-11-25 19:44:07 +0000 | [diff] [blame] | 38 | SkScalar luma = r * SK_LUM_COEFF_R + |
| 39 | g * SK_LUM_COEFF_G + |
| 40 | b * SK_LUM_COEFF_B; |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 41 | SkASSERT(luma <= SK_Scalar1); |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 42 | return SkScalarRoundToInt(luminance.fromLuma(gamma, luma) * 255); |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 43 | } |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 44 | |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 45 | /** Retrieves the SkColorSpaceLuminance for the given gamma. */ |
| 46 | static const SkColorSpaceLuminance& Fetch(SkScalar gamma); |
bungeman@google.com | ae30f56 | 2012-09-11 18:44:55 +0000 | [diff] [blame] | 47 | }; |
| 48 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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, |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 80 | const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma, |
| 81 | const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma); |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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 { |
halcanary | 9d524f2 | 2016-03-29 09:03:52 -0700 | [diff] [blame] | 95 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 96 | public: |
robertphillips@google.com | a22e211 | 2012-08-16 14:58:06 +0000 | [diff] [blame] | 97 | |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 98 | /** Creates a linear SkTMaskGamma. */ |
| 99 | SkTMaskGamma() : fIsLinear(true) { } |
bungeman@google.com | ae30f56 | 2012-09-11 18:44:55 +0000 | [diff] [blame] | 100 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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 | */ |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 110 | SkTMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) : fIsLinear(false) { |
| 111 | const SkColorSpaceLuminance& paintConvert = SkColorSpaceLuminance::Fetch(paintGamma); |
| 112 | const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma); |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 113 | for (U8CPU i = 0; i < (1 << MAX_LUM_BITS); ++i) { |
| 114 | U8CPU lum = sk_t_scale255<MAX_LUM_BITS>(i); |
bungeman@google.com | b1a72cb | 2012-10-04 22:03:41 +0000 | [diff] [blame] | 115 | SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast, |
| 116 | paintConvert, paintGamma, |
| 117 | deviceConvert, deviceGamma); |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 118 | } |
| 119 | } |
| 120 | |
reed@google.com | b5715a1 | 2013-01-08 13:07:25 +0000 | [diff] [blame] | 121 | /** Given a color, returns the closest canonical color. */ |
| 122 | static SkColor CanonicalColor(SkColor color) { |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 123 | return SkColorSetRGB( |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 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))); |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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 | */ |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 137 | PreBlend preBlend(SkColor color) const; |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 138 | |
jvanverth | 2d2a68c | 2014-06-10 06:42:56 -0700 | [diff] [blame] | 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 | } |
halcanary | 9d524f2 | 2016-03-29 09:03:52 -0700 | [diff] [blame] | 154 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 155 | private: |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 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]; |
bungeman@google.com | ae30f56 | 2012-09-11 18:44:55 +0000 | [diff] [blame] | 160 | bool fIsLinear; |
robertphillips@google.com | a22e211 | 2012-08-16 14:58:06 +0000 | [diff] [blame] | 161 | |
| 162 | typedef SkRefCnt INHERITED; |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 163 | }; |
| 164 | |
robertphillips@google.com | 4fa9c9f | 2012-08-22 17:31:22 +0000 | [diff] [blame] | 165 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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. |
skia.committer@gmail.com | a9279f0 | 2012-09-12 02:00:57 +0000 | [diff] [blame] | 170 | * |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 171 | * If fR, fG, or fB is nullptr, all of them will be. This indicates that no mask |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 172 | * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as |
| 173 | * a convenience function to test for the absence of this case. |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 174 | */ |
| 175 | template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend { |
| 176 | private: |
bungeman | 6bd5284 | 2016-10-27 09:30:08 -0700 | [diff] [blame] | 177 | SkTMaskPreBlend(sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> parent, |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 178 | const uint8_t* r, const uint8_t* g, const uint8_t* b) |
bungeman | 6bd5284 | 2016-10-27 09:30:08 -0700 | [diff] [blame] | 179 | : fParent(std::move(parent)), fR(r), fG(g), fB(b) { } |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 180 | |
bungeman | 6bd5284 | 2016-10-27 09:30:08 -0700 | [diff] [blame] | 181 | sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> fParent; |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 182 | friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>; |
| 183 | public: |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 184 | /** Creates a non applicable SkTMaskPreBlend. */ |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 185 | SkTMaskPreBlend() : fParent(), fR(nullptr), fG(nullptr), fB(nullptr) { } |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 186 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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) |
bungeman | 6bd5284 | 2016-10-27 09:30:08 -0700 | [diff] [blame] | 192 | : fParent(that.fParent), fR(that.fR), fG(that.fG), fB(that.fB) { } |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 193 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 194 | ~SkTMaskPreBlend() { } |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 195 | |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 196 | /** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */ |
bsalomon | 49f085d | 2014-09-05 13:34:00 -0700 | [diff] [blame] | 197 | bool isApplicable() const { return SkToBool(this->fG); } |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 198 | |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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> |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 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>() |
bungeman | 6bd5284 | 2016-10-27 09:30:08 -0700 | [diff] [blame] | 208 | : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(sk_ref_sp(this), |
bungeman@google.com | a76de72 | 2012-10-26 19:35:54 +0000 | [diff] [blame] | 209 | fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)], |
| 210 | fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)], |
| 211 | fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]); |
bungeman@google.com | 97efada | 2012-07-30 20:40:50 +0000 | [diff] [blame] | 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 |