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