Home | History | Annotate | Download | only in core
      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 "SkColorData.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 or analyzed in other ways.
    150      * Returns nullptr if fGammaTables hasn't been initialized.
    151      */
    152     const uint8_t* getGammaTables() const {
    153         return fIsLinear ? nullptr : (const uint8_t*) fGammaTables;
    154     }
    155 
    156 private:
    157     static const int MAX_LUM_BITS =
    158           B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)
    159         ? B_LUM_BITS : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS);
    160     uint8_t fGammaTables[1 << MAX_LUM_BITS][256];
    161     bool fIsLinear;
    162 
    163     typedef SkRefCnt INHERITED;
    164 };
    165 
    166 
    167 /**
    168  * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to
    169  * convert a linear alpha value for a given channel to a gamma correcting alpha
    170  * value for that channel. This class is immutable.
    171  *
    172  * If fR, fG, or fB is nullptr, all of them will be. This indicates that no mask
    173  * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as
    174  * a convenience function to test for the absence of this case.
    175  */
    176 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
    177 private:
    178     SkTMaskPreBlend(sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> parent,
    179                     const uint8_t* r, const uint8_t* g, const uint8_t* b)
    180     : fParent(std::move(parent)), fR(r), fG(g), fB(b) { }
    181 
    182     sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> fParent;
    183     friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
    184 public:
    185     /** Creates a non applicable SkTMaskPreBlend. */
    186     SkTMaskPreBlend() : fParent(), fR(nullptr), fG(nullptr), fB(nullptr) { }
    187 
    188     /**
    189      * This copy contructor exists for correctness, but should never be called
    190      * when return value optimization is enabled.
    191      */
    192     SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
    193     : fParent(that.fParent), fR(that.fR), fG(that.fG), fB(that.fB) { }
    194 
    195     ~SkTMaskPreBlend() { }
    196 
    197     /** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */
    198     bool isApplicable() const { return SkToBool(this->fG); }
    199 
    200     const uint8_t* fR;
    201     const uint8_t* fG;
    202     const uint8_t* fB;
    203 };
    204 
    205 template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
    206 SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
    207 SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const {
    208     return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>()
    209                      : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(sk_ref_sp(this),
    210                          fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)],
    211                          fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)],
    212                          fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]);
    213 }
    214 
    215 ///@{
    216 /**
    217  *  If APPLY_LUT is false, returns component unchanged.
    218  *  If APPLY_LUT is true, returns lut[component].
    219  *  @param APPLY_LUT whether or not the look-up table should be applied to component.
    220  *  @component the initial component.
    221  *  @lut a look-up table which transforms the component.
    222  */
    223 template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
    224     return component;
    225 }
    226 template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
    227     return lut[component];
    228 }
    229 ///@}
    230 
    231 #endif
    232