| /* | 
 |  * Copyright 2012 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #ifndef SkMaskGamma_DEFINED | 
 | #define SkMaskGamma_DEFINED | 
 |  | 
 | #include "SkTypes.h" | 
 | #include "SkColor.h" | 
 | #include "SkColorPriv.h" | 
 | #include "SkRefCnt.h" | 
 |  | 
 | /** | 
 |  * SkColorSpaceLuminance is used to convert luminances to and from linear and | 
 |  * perceptual color spaces. | 
 |  * | 
 |  * Luma is used to specify a linear luminance value [0.0, 1.0]. | 
 |  * Luminance is used to specify a luminance value in an arbitrary color space [0.0, 1.0]. | 
 |  */ | 
 | class SkColorSpaceLuminance : SkNoncopyable { | 
 | public: | 
 |     virtual ~SkColorSpaceLuminance() { } | 
 |  | 
 |     /** Converts a color component luminance in the color space to a linear luma. */ | 
 |     virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const = 0; | 
 |     /** Converts a linear luma to a color component luminance in the color space. */ | 
 |     virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const = 0; | 
 |  | 
 |     /** Converts a color to a luminance value. */ | 
 |     static U8CPU computeLuminance(SkScalar gamma, SkColor c) { | 
 |         const SkColorSpaceLuminance& luminance = Fetch(gamma); | 
 |         SkScalar r = luminance.toLuma(gamma, SkIntToScalar(SkColorGetR(c)) / 255); | 
 |         SkScalar g = luminance.toLuma(gamma, SkIntToScalar(SkColorGetG(c)) / 255); | 
 |         SkScalar b = luminance.toLuma(gamma, SkIntToScalar(SkColorGetB(c)) / 255); | 
 |         SkScalar luma = r * SK_LUM_COEFF_R + | 
 |                         g * SK_LUM_COEFF_G + | 
 |                         b * SK_LUM_COEFF_B; | 
 |         SkASSERT(luma <= SK_Scalar1); | 
 |         return SkScalarRoundToInt(luminance.fromLuma(gamma, luma) * 255); | 
 |     } | 
 |  | 
 |     /** Retrieves the SkColorSpaceLuminance for the given gamma. */ | 
 |     static const SkColorSpaceLuminance& Fetch(SkScalar gamma); | 
 | }; | 
 |  | 
 | ///@{ | 
 | /** | 
 |  * Scales base <= 2^N-1 to 2^8-1 | 
 |  * @param N [1, 8] the number of bits used by base. | 
 |  * @param base the number to be scaled to [0, 255]. | 
 |  */ | 
 | template<U8CPU N> static inline U8CPU sk_t_scale255(U8CPU base) { | 
 |     base <<= (8 - N); | 
 |     U8CPU lum = base; | 
 |     for (unsigned int i = N; i < 8; i += N) { | 
 |         lum |= base >> i; | 
 |     } | 
 |     return lum; | 
 | } | 
 | template<> /*static*/ inline U8CPU sk_t_scale255<1>(U8CPU base) { | 
 |     return base * 0xFF; | 
 | } | 
 | template<> /*static*/ inline U8CPU sk_t_scale255<2>(U8CPU base) { | 
 |     return base * 0x55; | 
 | } | 
 | template<> /*static*/ inline U8CPU sk_t_scale255<4>(U8CPU base) { | 
 |     return base * 0x11; | 
 | } | 
 | template<> /*static*/ inline U8CPU sk_t_scale255<8>(U8CPU base) { | 
 |     return base; | 
 | } | 
 | ///@} | 
 |  | 
 | template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend; | 
 |  | 
 | void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast, | 
 |                                        const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma, | 
 |                                        const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma); | 
 |  | 
 | /** | 
 |  * A regular mask contains linear alpha values. A gamma correcting mask | 
 |  * contains non-linear alpha values in an attempt to create gamma correct blits | 
 |  * in the presence of a gamma incorrect (linear) blend in the blitter. | 
 |  * | 
 |  * SkMaskGamma creates and maintains tables which convert linear alpha values | 
 |  * to gamma correcting alpha values. | 
 |  * @param R The number of luminance bits to use [1, 8] from the red channel. | 
 |  * @param G The number of luminance bits to use [1, 8] from the green channel. | 
 |  * @param B The number of luminance bits to use [1, 8] from the blue channel. | 
 |  */ | 
 | template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt { | 
 |  | 
 | public: | 
 |  | 
 |     /** Creates a linear SkTMaskGamma. */ | 
 |     SkTMaskGamma() : fIsLinear(true) { } | 
 |  | 
 |     /** | 
 |      * Creates tables to convert linear alpha values to gamma correcting alpha | 
 |      * values. | 
 |      * | 
 |      * @param contrast A value in the range [0.0, 1.0] which indicates the | 
 |      *                 amount of artificial contrast to add. | 
 |      * @param paint The color space in which the paint color was chosen. | 
 |      * @param device The color space of the target device. | 
 |      */ | 
 |     SkTMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) : fIsLinear(false) { | 
 |         const SkColorSpaceLuminance& paintConvert = SkColorSpaceLuminance::Fetch(paintGamma); | 
 |         const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma); | 
 |         for (U8CPU i = 0; i < (1 << MAX_LUM_BITS); ++i) { | 
 |             U8CPU lum = sk_t_scale255<MAX_LUM_BITS>(i); | 
 |             SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast, | 
 |                                               paintConvert, paintGamma, | 
 |                                               deviceConvert, deviceGamma); | 
 |         } | 
 |     } | 
 |  | 
 |     /** Given a color, returns the closest canonical color. */ | 
 |     static SkColor CanonicalColor(SkColor color) { | 
 |         return SkColorSetRGB( | 
 |                    sk_t_scale255<R_LUM_BITS>(SkColorGetR(color) >> (8 - R_LUM_BITS)), | 
 |                    sk_t_scale255<G_LUM_BITS>(SkColorGetG(color) >> (8 - G_LUM_BITS)), | 
 |                    sk_t_scale255<B_LUM_BITS>(SkColorGetB(color) >> (8 - B_LUM_BITS))); | 
 |     } | 
 |  | 
 |     /** The type of the mask pre-blend which will be returned from preBlend(SkColor). */ | 
 |     typedef SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> PreBlend; | 
 |  | 
 |     /** | 
 |      * Provides access to the tables appropriate for converting linear alpha | 
 |      * values into gamma correcting alpha values when drawing the given color | 
 |      * through the mask. The destination color will be approximated. | 
 |      */ | 
 |     PreBlend preBlend(SkColor color) const; | 
 |  | 
 |     /** | 
 |      * Get dimensions for the full table set, so it can be allocated as a block. | 
 |      */ | 
 |     void getGammaTableDimensions(int* tableWidth, int* numTables) const { | 
 |         *tableWidth = 256; | 
 |         *numTables = (1 << MAX_LUM_BITS); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Provides direct access to the full table set, so it can be uploaded | 
 |      * into a texture. | 
 |      */ | 
 |     const uint8_t* getGammaTables() const { | 
 |         return (const uint8_t*) fGammaTables; | 
 |     } | 
 |  | 
 | private: | 
 |     static const int MAX_LUM_BITS = | 
 |           B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS) | 
 |         ? B_LUM_BITS : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS); | 
 |     uint8_t fGammaTables[1 << MAX_LUM_BITS][256]; | 
 |     bool fIsLinear; | 
 |  | 
 |     typedef SkRefCnt INHERITED; | 
 | }; | 
 |  | 
 |  | 
 | /** | 
 |  * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to | 
 |  * convert a linear alpha value for a given channel to a gamma correcting alpha | 
 |  * value for that channel. This class is immutable. | 
 |  * | 
 |  * If fR, fG, or fB is nullptr, all of them will be. This indicates that no mask | 
 |  * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as | 
 |  * a convenience function to test for the absence of this case. | 
 |  */ | 
 | template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend { | 
 | 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) | 
 |     : fParent(SkSafeRef(parent)), fR(r), fG(g), fB(b) { } | 
 |  | 
 |     SkAutoTUnref<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> > fParent; | 
 |     friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>; | 
 | public: | 
 |     /** Creates a non applicable SkTMaskPreBlend. */ | 
 |     SkTMaskPreBlend() : fParent(), fR(nullptr), fG(nullptr), fB(nullptr) { } | 
 |  | 
 |     /** | 
 |      * This copy contructor exists for correctness, but should never be called | 
 |      * when return value optimization is enabled. | 
 |      */ | 
 |     SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that) | 
 |     : fParent(SkSafeRef(that.fParent.get())), fR(that.fR), fG(that.fG), fB(that.fB) { } | 
 |  | 
 |     ~SkTMaskPreBlend() { } | 
 |  | 
 |     /** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */ | 
 |     bool isApplicable() const { return SkToBool(this->fG); } | 
 |  | 
 |     const uint8_t* fR; | 
 |     const uint8_t* fG; | 
 |     const uint8_t* fB; | 
 | }; | 
 |  | 
 | template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> | 
 | SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> | 
 | SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const { | 
 |     return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>() | 
 |                      : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(this, | 
 |                          fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)], | 
 |                          fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)], | 
 |                          fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]); | 
 | } | 
 |  | 
 | ///@{ | 
 | /** | 
 |  *  If APPLY_LUT is false, returns component unchanged. | 
 |  *  If APPLY_LUT is true, returns lut[component]. | 
 |  *  @param APPLY_LUT whether or not the look-up table should be applied to component. | 
 |  *  @component the initial component. | 
 |  *  @lut a look-up table which transforms the component. | 
 |  */ | 
 | template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) { | 
 |     return component; | 
 | } | 
 | template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) { | 
 |     return lut[component]; | 
 | } | 
 | ///@} | 
 |  | 
 | #endif |