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