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 | |
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 | |
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 | |
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 | |
115 | static constexpr bool BlendCoeffsUseSrcColor(BlendCoeff srcCoeff, BlendCoeff dstCoeff) { |
116 | return BlendCoeff::kZero != srcCoeff || BlendCoeffRefsSrc(coeff: dstCoeff); |
117 | } |
118 | |
119 | static constexpr bool BlendCoeffsUseDstColor(BlendCoeff srcCoeff, |
120 | BlendCoeff dstCoeff, |
121 | bool srcColorIsOpaque) { |
122 | return BlendCoeffRefsDst(coeff: srcCoeff) || |
123 | (dstCoeff != BlendCoeff::kZero && !(dstCoeff == BlendCoeff::kISA && srcColorIsOpaque)); |
124 | } |
125 | |
126 | static constexpr bool BlendEquationIsAdvanced(BlendEquation equation) { |
127 | return equation >= BlendEquation::kFirstAdvanced && |
128 | equation != BlendEquation::kIllegal; |
129 | } |
130 | |
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 | |
138 | static constexpr bool BlendCoeffRefsConstant(const BlendCoeff coeff) { |
139 | return coeff == BlendCoeff::kConstC || coeff == BlendCoeff::kIConstC; |
140 | } |
141 | |
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 | */ |
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(coeff: 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 | |