SkMaskGamma.h 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. #ifndef SkMaskGamma_DEFINED
  8. #define SkMaskGamma_DEFINED
  9. #include "include/core/SkColor.h"
  10. #include "include/core/SkRefCnt.h"
  11. #include "include/core/SkTypes.h"
  12. #include "include/private/SkColorData.h"
  13. #include "include/private/SkNoncopyable.h"
  14. /**
  15. * SkColorSpaceLuminance is used to convert luminances to and from linear and
  16. * perceptual color spaces.
  17. *
  18. * Luma is used to specify a linear luminance value [0.0, 1.0].
  19. * Luminance is used to specify a luminance value in an arbitrary color space [0.0, 1.0].
  20. */
  21. class SkColorSpaceLuminance : SkNoncopyable {
  22. public:
  23. virtual ~SkColorSpaceLuminance() { }
  24. /** Converts a color component luminance in the color space to a linear luma. */
  25. virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const = 0;
  26. /** Converts a linear luma to a color component luminance in the color space. */
  27. virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const = 0;
  28. /** Converts a color to a luminance value. */
  29. static U8CPU computeLuminance(SkScalar gamma, SkColor c) {
  30. const SkColorSpaceLuminance& luminance = Fetch(gamma);
  31. SkScalar r = luminance.toLuma(gamma, SkIntToScalar(SkColorGetR(c)) / 255);
  32. SkScalar g = luminance.toLuma(gamma, SkIntToScalar(SkColorGetG(c)) / 255);
  33. SkScalar b = luminance.toLuma(gamma, SkIntToScalar(SkColorGetB(c)) / 255);
  34. SkScalar luma = r * SK_LUM_COEFF_R +
  35. g * SK_LUM_COEFF_G +
  36. b * SK_LUM_COEFF_B;
  37. SkASSERT(luma <= SK_Scalar1);
  38. return SkScalarRoundToInt(luminance.fromLuma(gamma, luma) * 255);
  39. }
  40. /** Retrieves the SkColorSpaceLuminance for the given gamma. */
  41. static const SkColorSpaceLuminance& Fetch(SkScalar gamma);
  42. };
  43. ///@{
  44. /**
  45. * Scales base <= 2^N-1 to 2^8-1
  46. * @param N [1, 8] the number of bits used by base.
  47. * @param base the number to be scaled to [0, 255].
  48. */
  49. template<U8CPU N> static inline U8CPU sk_t_scale255(U8CPU base) {
  50. base <<= (8 - N);
  51. U8CPU lum = base;
  52. for (unsigned int i = N; i < 8; i += N) {
  53. lum |= base >> i;
  54. }
  55. return lum;
  56. }
  57. template<> /*static*/ inline U8CPU sk_t_scale255<1>(U8CPU base) {
  58. return base * 0xFF;
  59. }
  60. template<> /*static*/ inline U8CPU sk_t_scale255<2>(U8CPU base) {
  61. return base * 0x55;
  62. }
  63. template<> /*static*/ inline U8CPU sk_t_scale255<4>(U8CPU base) {
  64. return base * 0x11;
  65. }
  66. template<> /*static*/ inline U8CPU sk_t_scale255<8>(U8CPU base) {
  67. return base;
  68. }
  69. ///@}
  70. template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend;
  71. void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast,
  72. const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma,
  73. const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma);
  74. /**
  75. * A regular mask contains linear alpha values. A gamma correcting mask
  76. * contains non-linear alpha values in an attempt to create gamma correct blits
  77. * in the presence of a gamma incorrect (linear) blend in the blitter.
  78. *
  79. * SkMaskGamma creates and maintains tables which convert linear alpha values
  80. * to gamma correcting alpha values.
  81. * @param R The number of luminance bits to use [1, 8] from the red channel.
  82. * @param G The number of luminance bits to use [1, 8] from the green channel.
  83. * @param B The number of luminance bits to use [1, 8] from the blue channel.
  84. */
  85. template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt {
  86. public:
  87. /** Creates a linear SkTMaskGamma. */
  88. SkTMaskGamma() : fIsLinear(true) { }
  89. /**
  90. * Creates tables to convert linear alpha values to gamma correcting alpha
  91. * values.
  92. *
  93. * @param contrast A value in the range [0.0, 1.0] which indicates the
  94. * amount of artificial contrast to add.
  95. * @param paint The color space in which the paint color was chosen.
  96. * @param device The color space of the target device.
  97. */
  98. SkTMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) : fIsLinear(false) {
  99. const SkColorSpaceLuminance& paintConvert = SkColorSpaceLuminance::Fetch(paintGamma);
  100. const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma);
  101. for (U8CPU i = 0; i < (1 << MAX_LUM_BITS); ++i) {
  102. U8CPU lum = sk_t_scale255<MAX_LUM_BITS>(i);
  103. SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast,
  104. paintConvert, paintGamma,
  105. deviceConvert, deviceGamma);
  106. }
  107. }
  108. /** Given a color, returns the closest canonical color. */
  109. static SkColor CanonicalColor(SkColor color) {
  110. return SkColorSetRGB(
  111. sk_t_scale255<R_LUM_BITS>(SkColorGetR(color) >> (8 - R_LUM_BITS)),
  112. sk_t_scale255<G_LUM_BITS>(SkColorGetG(color) >> (8 - G_LUM_BITS)),
  113. sk_t_scale255<B_LUM_BITS>(SkColorGetB(color) >> (8 - B_LUM_BITS)));
  114. }
  115. /** The type of the mask pre-blend which will be returned from preBlend(SkColor). */
  116. typedef SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> PreBlend;
  117. /**
  118. * Provides access to the tables appropriate for converting linear alpha
  119. * values into gamma correcting alpha values when drawing the given color
  120. * through the mask. The destination color will be approximated.
  121. */
  122. PreBlend preBlend(SkColor color) const;
  123. /**
  124. * Get dimensions for the full table set, so it can be allocated as a block.
  125. */
  126. void getGammaTableDimensions(int* tableWidth, int* numTables) const {
  127. *tableWidth = 256;
  128. *numTables = (1 << MAX_LUM_BITS);
  129. }
  130. /**
  131. * Provides direct access to the full table set, so it can be uploaded
  132. * into a texture or analyzed in other ways.
  133. * Returns nullptr if fGammaTables hasn't been initialized.
  134. */
  135. const uint8_t* getGammaTables() const {
  136. return fIsLinear ? nullptr : (const uint8_t*) fGammaTables;
  137. }
  138. private:
  139. static const int MAX_LUM_BITS =
  140. B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)
  141. ? B_LUM_BITS : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS);
  142. uint8_t fGammaTables[1 << MAX_LUM_BITS][256];
  143. bool fIsLinear;
  144. typedef SkRefCnt INHERITED;
  145. };
  146. /**
  147. * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to
  148. * convert a linear alpha value for a given channel to a gamma correcting alpha
  149. * value for that channel. This class is immutable.
  150. *
  151. * If fR, fG, or fB is nullptr, all of them will be. This indicates that no mask
  152. * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as
  153. * a convenience function to test for the absence of this case.
  154. */
  155. template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
  156. private:
  157. SkTMaskPreBlend(sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> parent,
  158. const uint8_t* r, const uint8_t* g, const uint8_t* b)
  159. : fParent(std::move(parent)), fR(r), fG(g), fB(b) { }
  160. sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> fParent;
  161. friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
  162. public:
  163. /** Creates a non applicable SkTMaskPreBlend. */
  164. SkTMaskPreBlend() : fParent(), fR(nullptr), fG(nullptr), fB(nullptr) { }
  165. /**
  166. * This copy contructor exists for correctness, but should never be called
  167. * when return value optimization is enabled.
  168. */
  169. SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
  170. : fParent(that.fParent), fR(that.fR), fG(that.fG), fB(that.fB) { }
  171. ~SkTMaskPreBlend() { }
  172. /** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */
  173. bool isApplicable() const { return SkToBool(this->fG); }
  174. const uint8_t* fR;
  175. const uint8_t* fG;
  176. const uint8_t* fB;
  177. };
  178. template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
  179. SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
  180. SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const {
  181. return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>()
  182. : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(sk_ref_sp(this),
  183. fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)],
  184. fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)],
  185. fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]);
  186. }
  187. ///@{
  188. /**
  189. * If APPLY_LUT is false, returns component unchanged.
  190. * If APPLY_LUT is true, returns lut[component].
  191. * @param APPLY_LUT whether or not the look-up table should be applied to component.
  192. * @component the initial component.
  193. * @lut a look-up table which transforms the component.
  194. */
  195. template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
  196. return component;
  197. }
  198. template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
  199. return lut[component];
  200. }
  201. ///@}
  202. #endif