diff options
Diffstat (limited to 'core/SkMaskGamma.h')
-rw-r--r-- | core/SkMaskGamma.h | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/core/SkMaskGamma.h b/core/SkMaskGamma.h new file mode 100644 index 00000000..fafe4ac7 --- /dev/null +++ b/core/SkMaskGamma.h @@ -0,0 +1,221 @@ +/* + * 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 * SkFloatToScalar(SK_LUM_COEFF_R) + + g * SkFloatToScalar(SK_LUM_COEFF_G) + + b * SkFloatToScalar(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: + SK_DECLARE_INST_COUNT_TEMPLATE(SkTMaskGamma) + + /** 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; + +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; +}; + + +#define MacroComma , +SK_DEFINE_INST_COUNT_TEMPLATE( + template <int R_LUM_BITS MacroComma int G_LUM_BITS MacroComma int B_LUM_BITS>, + SkTMaskGamma<R_LUM_BITS MacroComma G_LUM_BITS MacroComma B_LUM_BITS>); + +/** + * 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 NULL, 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(NULL), fG(NULL), fB(NULL) { } + + /** + * 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 NULL. */ + bool isApplicable() const { + return NULL != 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 |