1 /*
2 * Copyright 2013 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 skgpu_Blend_DEFINED
9 #define skgpu_Blend_DEFINED
10
11 #include "include/core/SkSpan.h"
12 #include "include/core/SkTypes.h"
13 #include "include/private/SkColorData.h"
14
15 enum class SkBlendMode;
16 class SkString;
17
18 namespace skgpu {
19
20 /**
21 * Equations for alpha-blending.
22 */
23 enum class BlendEquation : uint8_t {
24 // Basic blend equations.
25 kAdd, //<! Cs*S + Cd*D
26 kSubtract, //<! Cs*S - Cd*D
27 kReverseSubtract, //<! Cd*D - Cs*S
28
29 // Advanced blend equations. These are described in the SVG and PDF specs.
30 kScreen,
31 kOverlay,
32 kDarken,
33 kLighten,
34 kColorDodge,
35 kColorBurn,
36 kHardLight,
37 kSoftLight,
38 kDifference,
39 kExclusion,
40 kMultiply,
41 kHSLHue,
42 kHSLSaturation,
43 kHSLColor,
44 kHSLLuminosity,
45
46 kIllegal,
47
48 kFirstAdvanced = kScreen,
49 kLast = kIllegal,
50 };
51
52 static const int kBlendEquationCnt = static_cast<int>(BlendEquation::kLast) + 1;
53
54 /**
55 * Coefficients for alpha-blending.
56 */
57 enum class BlendCoeff : uint8_t {
58 kZero, //<! 0
59 kOne, //<! 1
60 kSC, //<! src color
61 kISC, //<! one minus src color
62 kDC, //<! dst color
63 kIDC, //<! one minus dst color
64 kSA, //<! src alpha
65 kISA, //<! one minus src alpha
66 kDA, //<! dst alpha
67 kIDA, //<! one minus dst alpha
68 kConstC, //<! constant color
69 kIConstC, //<! one minus constant color
70 kS2C,
71 kIS2C,
72 kS2A,
73 kIS2A,
74
75 kIllegal,
76
77 kLast = kIllegal,
78 };
79
80 struct BlendInfo {
81 SkDEBUGCODE(SkString dump() const;)
82
83 bool operator==(const BlendInfo& other) const {
84 return fEquation == other.fEquation &&
85 fSrcBlend == other.fSrcBlend &&
86 fDstBlend == other.fDstBlend &&
87 fBlendConstant == other.fBlendConstant &&
88 fWritesColor == other.fWritesColor;
89 }
90
91 skgpu::BlendEquation fEquation = skgpu::BlendEquation::kAdd;
92 skgpu::BlendCoeff fSrcBlend = skgpu::BlendCoeff::kOne;
93 skgpu::BlendCoeff fDstBlend = skgpu::BlendCoeff::kZero;
94 SkPMColor4f fBlendConstant = SK_PMColor4fTRANSPARENT;
95 bool fWritesColor = true;
96 };
97
98 static const int kBlendCoeffCnt = static_cast<int>(BlendCoeff::kLast) + 1;
99
BlendCoeffRefsSrc(const BlendCoeff coeff)100 static constexpr bool BlendCoeffRefsSrc(const BlendCoeff coeff) {
101 return BlendCoeff::kSC == coeff || BlendCoeff::kISC == coeff || BlendCoeff::kSA == coeff ||
102 BlendCoeff::kISA == coeff;
103 }
104
BlendCoeffRefsDst(const BlendCoeff coeff)105 static constexpr bool BlendCoeffRefsDst(const BlendCoeff coeff) {
106 return BlendCoeff::kDC == coeff || BlendCoeff::kIDC == coeff || BlendCoeff::kDA == coeff ||
107 BlendCoeff::kIDA == coeff;
108 }
109
BlendCoeffRefsSrc2(const BlendCoeff coeff)110 static constexpr bool BlendCoeffRefsSrc2(const BlendCoeff coeff) {
111 return BlendCoeff::kS2C == coeff || BlendCoeff::kIS2C == coeff ||
112 BlendCoeff::kS2A == coeff || BlendCoeff::kIS2A == coeff;
113 }
114
BlendCoeffsUseSrcColor(BlendCoeff srcCoeff,BlendCoeff dstCoeff)115 static constexpr bool BlendCoeffsUseSrcColor(BlendCoeff srcCoeff, BlendCoeff dstCoeff) {
116 return BlendCoeff::kZero != srcCoeff || BlendCoeffRefsSrc(dstCoeff);
117 }
118
BlendCoeffsUseDstColor(BlendCoeff srcCoeff,BlendCoeff dstCoeff,bool srcColorIsOpaque)119 static constexpr bool BlendCoeffsUseDstColor(BlendCoeff srcCoeff,
120 BlendCoeff dstCoeff,
121 bool srcColorIsOpaque) {
122 return BlendCoeffRefsDst(srcCoeff) ||
123 (dstCoeff != BlendCoeff::kZero && !(dstCoeff == BlendCoeff::kISA && srcColorIsOpaque));
124 }
125
BlendEquationIsAdvanced(BlendEquation equation)126 static constexpr bool BlendEquationIsAdvanced(BlendEquation equation) {
127 return equation >= BlendEquation::kFirstAdvanced &&
128 equation != BlendEquation::kIllegal;
129 }
130
BlendModifiesDst(BlendEquation equation,BlendCoeff srcCoeff,BlendCoeff dstCoeff)131 static constexpr bool BlendModifiesDst(BlendEquation equation,
132 BlendCoeff srcCoeff,
133 BlendCoeff dstCoeff) {
134 return (BlendEquation::kAdd != equation && BlendEquation::kReverseSubtract != equation) ||
135 BlendCoeff::kZero != srcCoeff || BlendCoeff::kOne != dstCoeff;
136 }
137
BlendCoeffRefsConstant(const BlendCoeff coeff)138 static constexpr bool BlendCoeffRefsConstant(const BlendCoeff coeff) {
139 return coeff == BlendCoeff::kConstC || coeff == BlendCoeff::kIConstC;
140 }
141
BlendShouldDisable(BlendEquation equation,BlendCoeff srcCoeff,BlendCoeff dstCoeff)142 static constexpr bool BlendShouldDisable(BlendEquation equation,
143 BlendCoeff srcCoeff,
144 BlendCoeff dstCoeff) {
145 return (BlendEquation::kAdd == equation || BlendEquation::kSubtract == equation) &&
146 BlendCoeff::kOne == srcCoeff && BlendCoeff::kZero == dstCoeff;
147 }
148
149 /**
150 * Advanced blend equations can always tweak alpha for coverage. (See GrCustomXfermode.cpp)
151 *
152 * For "add" and "reverse subtract" the blend equation with f=coverage is:
153 *
154 * D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D
155 * = f * S * srcCoeff + D * (f * dstCoeff + (1 - f))
156 *
157 * (Let srcCoeff be negative for reverse subtract.) We can tweak alpha for coverage when the
158 * following relationship holds:
159 *
160 * (f*S) * srcCoeff' + D * dstCoeff' == f * S * srcCoeff + D * (f * dstCoeff + (1 - f))
161 *
162 * (Where srcCoeff' and dstCoeff' have any reference to S pre-multiplied by f.)
163 *
164 * It's easy to see this works for the src term as long as srcCoeff' == srcCoeff (meaning srcCoeff
165 * does not reference S). For the dst term, this will work as long as the following is true:
166 *|
167 * dstCoeff' == f * dstCoeff + (1 - f)
168 * dstCoeff' == 1 - f * (1 - dstCoeff)
169 *
170 * By inspection we can see this will work as long as dstCoeff has a 1, and any other term in
171 * dstCoeff references S.
172 *
173 * Moreover, if the blend doesn't modify the dst at all then it is ok to arbitrarily modify the src
174 * color so folding in coverage is allowed.
175 */
BlendAllowsCoverageAsAlpha(BlendEquation equation,BlendCoeff srcCoeff,BlendCoeff dstCoeff)176 static constexpr bool BlendAllowsCoverageAsAlpha(BlendEquation equation,
177 BlendCoeff srcCoeff,
178 BlendCoeff dstCoeff) {
179 return BlendEquationIsAdvanced(equation) ||
180 !BlendModifiesDst(equation, srcCoeff, dstCoeff) ||
181 ((BlendEquation::kAdd == equation || BlendEquation::kReverseSubtract == equation) &&
182 !BlendCoeffRefsSrc(srcCoeff) &&
183 (BlendCoeff::kOne == dstCoeff || BlendCoeff::kISC == dstCoeff ||
184 BlendCoeff::kISA == dstCoeff));
185 }
186
187 /**
188 * Returns the name of the SkSL built-in blend function for a SkBlendMode.
189 */
190 const char* BlendFuncName(SkBlendMode mode);
191
192 /**
193 * If a blend can be represented by `blend_porter_duff`, returns the associated blend constants as
194 * an array of four floats. If not, returns an empty span.
195 */
196 SkSpan<const float> GetPorterDuffBlendConstants(SkBlendMode mode);
197
198 /**
199 * Returns a pair of "blend function + uniform data" for a particular SkBlendMode.
200 * This allows us to use fewer unique functions when generating shaders, e.g. every Porter-Duff
201 * blend can use the same function.
202 */
203 struct ReducedBlendModeInfo {
204 const char* fFunction;
205 SkSpan<const float> fUniformData;
206 };
207 ReducedBlendModeInfo GetReducedBlendModeInfo(SkBlendMode mode);
208
209 } // namespace skgpu
210
211 #endif // skgpu_Blend_DEFINED
212