blob: f90f75a878ed32fb7d90aafe0a3c131aae852efb [file] [log] [blame]
bungeman@google.com97efada2012-07-30 20:40:50 +00001/*
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 "SkColorPriv.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 */
23class SkColorSpaceLuminance : SkNoncopyable {
24public:
bungeman@google.comb1a72cb2012-10-04 22:03:41 +000025 virtual ~SkColorSpaceLuminance() { }
robertphillips@google.comcb73b312012-08-15 14:52:58 +000026
bungeman@google.com97efada2012-07-30 20:40:50 +000027 /** Converts a color component luminance in the color space to a linear luma. */
bungeman@google.comb1a72cb2012-10-04 22:03:41 +000028 virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const = 0;
bungeman@google.com97efada2012-07-30 20:40:50 +000029 /** Converts a linear luma to a color component luminance in the color space. */
bungeman@google.comb1a72cb2012-10-04 22:03:41 +000030 virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const = 0;
bungeman@google.com97efada2012-07-30 20:40:50 +000031
32 /** Converts a color to a luminance value. */
bungeman@google.comb1a72cb2012-10-04 22:03:41 +000033 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);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000038 SkScalar luma = r * SK_LUM_COEFF_R +
39 g * SK_LUM_COEFF_G +
40 b * SK_LUM_COEFF_B;
bungeman@google.com97efada2012-07-30 20:40:50 +000041 SkASSERT(luma <= SK_Scalar1);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +000042 return SkScalarRoundToInt(luminance.fromLuma(gamma, luma) * 255);
bungeman@google.com97efada2012-07-30 20:40:50 +000043 }
bungeman@google.com97efada2012-07-30 20:40:50 +000044
bungeman@google.comb1a72cb2012-10-04 22:03:41 +000045 /** Retrieves the SkColorSpaceLuminance for the given gamma. */
46 static const SkColorSpaceLuminance& Fetch(SkScalar gamma);
bungeman@google.comae30f562012-09-11 18:44:55 +000047};
48
bungeman@google.com97efada2012-07-30 20:40:50 +000049///@{
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 */
55template<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}
63template<> /*static*/ inline U8CPU sk_t_scale255<1>(U8CPU base) {
64 return base * 0xFF;
65}
66template<> /*static*/ inline U8CPU sk_t_scale255<2>(U8CPU base) {
67 return base * 0x55;
68}
69template<> /*static*/ inline U8CPU sk_t_scale255<4>(U8CPU base) {
70 return base * 0x11;
71}
72template<> /*static*/ inline U8CPU sk_t_scale255<8>(U8CPU base) {
73 return base;
74}
75///@}
76
77template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend;
78
79void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast,
bungeman@google.comb1a72cb2012-10-04 22:03:41 +000080 const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma,
81 const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma);
bungeman@google.com97efada2012-07-30 20:40:50 +000082
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 */
94template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt {
halcanary9d524f22016-03-29 09:03:52 -070095
bungeman@google.com97efada2012-07-30 20:40:50 +000096public:
robertphillips@google.coma22e2112012-08-16 14:58:06 +000097
bungeman@google.coma76de722012-10-26 19:35:54 +000098 /** Creates a linear SkTMaskGamma. */
99 SkTMaskGamma() : fIsLinear(true) { }
bungeman@google.comae30f562012-09-11 18:44:55 +0000100
bungeman@google.com97efada2012-07-30 20:40:50 +0000101 /**
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 */
bungeman@google.comb1a72cb2012-10-04 22:03:41 +0000110 SkTMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) : fIsLinear(false) {
111 const SkColorSpaceLuminance& paintConvert = SkColorSpaceLuminance::Fetch(paintGamma);
112 const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma);
bungeman@google.coma76de722012-10-26 19:35:54 +0000113 for (U8CPU i = 0; i < (1 << MAX_LUM_BITS); ++i) {
114 U8CPU lum = sk_t_scale255<MAX_LUM_BITS>(i);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +0000115 SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast,
116 paintConvert, paintGamma,
117 deviceConvert, deviceGamma);
bungeman@google.com97efada2012-07-30 20:40:50 +0000118 }
119 }
120
reed@google.comb5715a12013-01-08 13:07:25 +0000121 /** Given a color, returns the closest canonical color. */
122 static SkColor CanonicalColor(SkColor color) {
bungeman@google.com97efada2012-07-30 20:40:50 +0000123 return SkColorSetRGB(
bungeman@google.coma76de722012-10-26 19:35:54 +0000124 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)));
bungeman@google.com97efada2012-07-30 20:40:50 +0000127 }
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 */
bungeman@google.coma76de722012-10-26 19:35:54 +0000137 PreBlend preBlend(SkColor color) const;
bungeman@google.com97efada2012-07-30 20:40:50 +0000138
jvanverth2d2a68c2014-06-10 06:42:56 -0700139 /**
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.
150 */
151 const uint8_t* getGammaTables() const {
152 return (const uint8_t*) fGammaTables;
153 }
halcanary9d524f22016-03-29 09:03:52 -0700154
bungeman@google.com97efada2012-07-30 20:40:50 +0000155private:
bungeman@google.coma76de722012-10-26 19:35:54 +0000156 static const int MAX_LUM_BITS =
157 B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)
158 ? B_LUM_BITS : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS);
159 uint8_t fGammaTables[1 << MAX_LUM_BITS][256];
bungeman@google.comae30f562012-09-11 18:44:55 +0000160 bool fIsLinear;
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000161
162 typedef SkRefCnt INHERITED;
bungeman@google.com97efada2012-07-30 20:40:50 +0000163};
164
robertphillips@google.com4fa9c9f2012-08-22 17:31:22 +0000165
bungeman@google.com97efada2012-07-30 20:40:50 +0000166/**
167 * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to
168 * convert a linear alpha value for a given channel to a gamma correcting alpha
169 * value for that channel. This class is immutable.
skia.committer@gmail.coma9279f02012-09-12 02:00:57 +0000170 *
halcanary96fcdcc2015-08-27 07:41:13 -0700171 * If fR, fG, or fB is nullptr, all of them will be. This indicates that no mask
bungeman@google.coma76de722012-10-26 19:35:54 +0000172 * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as
173 * a convenience function to test for the absence of this case.
bungeman@google.com97efada2012-07-30 20:40:50 +0000174 */
175template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
176private:
bungeman6bd52842016-10-27 09:30:08 -0700177 SkTMaskPreBlend(sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> parent,
bungeman@google.coma76de722012-10-26 19:35:54 +0000178 const uint8_t* r, const uint8_t* g, const uint8_t* b)
bungeman6bd52842016-10-27 09:30:08 -0700179 : fParent(std::move(parent)), fR(r), fG(g), fB(b) { }
bungeman@google.coma76de722012-10-26 19:35:54 +0000180
bungeman6bd52842016-10-27 09:30:08 -0700181 sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> fParent;
bungeman@google.com97efada2012-07-30 20:40:50 +0000182 friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
183public:
bungeman@google.coma76de722012-10-26 19:35:54 +0000184 /** Creates a non applicable SkTMaskPreBlend. */
halcanary96fcdcc2015-08-27 07:41:13 -0700185 SkTMaskPreBlend() : fParent(), fR(nullptr), fG(nullptr), fB(nullptr) { }
bungeman@google.coma76de722012-10-26 19:35:54 +0000186
bungeman@google.com97efada2012-07-30 20:40:50 +0000187 /**
188 * This copy contructor exists for correctness, but should never be called
189 * when return value optimization is enabled.
190 */
191 SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
bungeman6bd52842016-10-27 09:30:08 -0700192 : fParent(that.fParent), fR(that.fR), fG(that.fG), fB(that.fB) { }
bungeman@google.coma76de722012-10-26 19:35:54 +0000193
bungeman@google.com97efada2012-07-30 20:40:50 +0000194 ~SkTMaskPreBlend() { }
bungeman@google.coma76de722012-10-26 19:35:54 +0000195
halcanary96fcdcc2015-08-27 07:41:13 -0700196 /** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */
bsalomon49f085d2014-09-05 13:34:00 -0700197 bool isApplicable() const { return SkToBool(this->fG); }
bungeman@google.coma76de722012-10-26 19:35:54 +0000198
bungeman@google.com97efada2012-07-30 20:40:50 +0000199 const uint8_t* fR;
200 const uint8_t* fG;
201 const uint8_t* fB;
202};
203
204template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
205SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
bungeman@google.coma76de722012-10-26 19:35:54 +0000206SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const {
207 return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>()
bungeman6bd52842016-10-27 09:30:08 -0700208 : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(sk_ref_sp(this),
bungeman@google.coma76de722012-10-26 19:35:54 +0000209 fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)],
210 fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)],
211 fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]);
bungeman@google.com97efada2012-07-30 20:40:50 +0000212}
213
214///@{
215/**
216 * If APPLY_LUT is false, returns component unchanged.
217 * If APPLY_LUT is true, returns lut[component].
218 * @param APPLY_LUT whether or not the look-up table should be applied to component.
219 * @component the initial component.
220 * @lut a look-up table which transforms the component.
221 */
222template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
223 return component;
224}
225template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
226 return lut[component];
227}
228///@}
229
230#endif