Flutter Engine
The Flutter Engine
BlendFormula.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
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
9
11
12namespace {
13
15
16/**
17 * When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard
18 * Porter Duff formula.
19 */
20constexpr BlendFormula MakeCoeffFormula(skgpu::BlendCoeff srcCoeff, skgpu::BlendCoeff dstCoeff) {
21 // When the coeffs are (Zero, Zero) or (Zero, One) we set the primary output to none.
22 return (skgpu::BlendCoeff::kZero == srcCoeff &&
23 (skgpu::BlendCoeff::kZero == dstCoeff || skgpu::BlendCoeff::kOne == dstCoeff))
26 : BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kNone_OutputType,
27 skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff);
28}
29
30/**
31 * Basic coeff formula similar to MakeCoeffFormula but we will make the src f*Sa. This is used in
32 * LCD dst-out.
33 */
34constexpr BlendFormula MakeSAModulateFormula(skgpu::BlendCoeff srcCoeff,
35 skgpu::BlendCoeff dstCoeff) {
37 skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff);
38}
39
40/**
41 * When there is coverage, the equation with f=coverage is:
42 *
43 * D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D
44 *
45 * This can be rewritten as:
46 *
47 * D' = f * S * srcCoeff + D * (1 - [f * (1 - dstCoeff)])
48 *
49 * To implement this formula, we output [f * (1 - dstCoeff)] for the secondary color and replace the
50 * HW dst coeff with IS2C.
51 *
52 * Xfer modes: dst-atop (Sa!=1)
53 */
54constexpr BlendFormula MakeCoverageFormula(BlendFormula::OutputType oneMinusDstCoeffModulateOutput,
55 skgpu::BlendCoeff srcCoeff) {
56 return BlendFormula(BlendFormula::kModulate_OutputType, oneMinusDstCoeffModulateOutput,
58}
59
60/**
61 * When there is coverage and the src coeff is Zero, the equation with f=coverage becomes:
62 *
63 * D' = f * D * dstCoeff + (1-f) * D
64 *
65 * This can be rewritten as:
66 *
67 * D' = D - D * [f * (1 - dstCoeff)]
68 *
69 * To implement this formula, we output [f * (1 - dstCoeff)] for the primary color and use a reverse
70 * subtract HW blend equation with coeffs of (DC, One).
71 *
72 * Xfer modes: clear, dst-out (Sa=1), dst-in (Sa!=1), modulate (Sc!=1)
73 */
74constexpr BlendFormula MakeCoverageSrcCoeffZeroFormula(
75 BlendFormula::OutputType oneMinusDstCoeffModulateOutput) {
76 return BlendFormula(oneMinusDstCoeffModulateOutput, BlendFormula::kNone_OutputType,
79}
80
81/**
82 * When there is coverage and the dst coeff is Zero, the equation with f=coverage becomes:
83 *
84 * D' = f * S * srcCoeff + (1-f) * D
85 *
86 * To implement this formula, we output [f] for the secondary color and replace the HW dst coeff
87 * with IS2A. (Note that we can avoid dual source blending when Sa=1 by using ISA.)
88 *
89 * Xfer modes (Sa!=1): src, src-in, src-out
90 */
91constexpr BlendFormula MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff srcCoeff) {
94}
95
96/**
97 * This table outlines the blend formulas we will use with each xfermode, with and without coverage,
98 * with and without an opaque input color. Optimization properties are deduced at compile time so we
99 * can make runtime decisions quickly. RGB coverage is not supported.
100 */
101constexpr BlendFormula gBlendTable[2][2][(int)SkBlendMode::kLastCoeffMode + 1] = {
102 /*>> No coverage, input color unknown <<*/ {{
103
104 /* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
105 /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
106 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
107 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
108 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
109 /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
110 /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSA),
111 /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
112 /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
113 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
114 /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kSA),
115 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
116 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
117 /* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
118 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
119
120 }, /*>> Has coverage, input color unknown <<*/ {
121
122 /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
123 /* src */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kOne),
124 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
125 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
126 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
127 /* src-in */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kDA),
128 /* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
129 /* src-out */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kIDA),
130 /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
131 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
132 /* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType,
134 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
135 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
136 /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
137 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
138
139 }}, /*>> No coverage, input color opaque <<*/ {{
140
141 /* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
142 /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
143 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
144 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
145 skgpu::BlendCoeff::kISA), // see comment below
146 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
147 /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
148 /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
149 /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
150 /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
151 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
152 /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
153 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
154 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
155 /* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
156 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
157
158 }, /*>> Has coverage, input color opaque <<*/ {
159
160 /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
161 /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
162 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
163 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
164 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
165 /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
166 /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
167 /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
168 /* dst-out */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
169 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
170 /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
171 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
172 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
173 /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
174 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
175}}};
176// In the above table src-over is not optimized to src mode when the color is opaque because we
177// found no advantage to doing so. Also, we are using a global src-over blend in most cases which is
178// not specialized for opaque input. For GPUs where dropping to src (and thus able to disable
179// blending) is an advantage we change the blend mode to src before getting the blend formula from
180// this table.
181
182constexpr BlendFormula gLCDBlendTable[(int)SkBlendMode::kLastCoeffMode + 1] = {
183 /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
184 /* src */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
186 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero,
188 /* src-over */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
190 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA,
192 /* src-in */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
194 /* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
195 /* src-out */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
197 /* dst-out */ MakeSAModulateFormula(skgpu::BlendCoeff::kZero,
199 /* src-atop */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
201 /* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType,
203 /* xor */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
205 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
207 /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
208 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
210};
211
212} // anonymous namespace
213
214namespace skgpu {
215
216BlendFormula GetBlendFormula(bool isOpaque, bool hasCoverage, SkBlendMode xfermode) {
217 SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
218 return gBlendTable[isOpaque][hasCoverage][(int)xfermode];
219}
220
222 SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
223 return gLCDBlendTable[(int)xfermode];
224}
225
226} // namespace skgpu
@ kISAModulate_OutputType
@ kSAModulate_OutputType
@ kModulate_OutputType
@ kISCModulate_OutputType
@ kNone_OutputType
@ kCoverage_OutputType
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlendMode
Definition: SkBlendMode.h:38
@ kLastCoeffMode
last porter duff blend mode
@ kAdd
Definition: embedder.h:990
Definition: GpuTools.h:21
BlendEquation
Definition: Blend.h:26
BlendFormula GetBlendFormula(bool isOpaque, bool hasCoverage, SkBlendMode xfermode)
BlendCoeff
Definition: Blend.h:60
BlendFormula GetLCDBlendFormula(SkBlendMode xfermode)