OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2015 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 #include "effects/GrAdvancedEquationXPFactory.h" | |
9 | |
10 #include "GrDrawTargetCaps.h" | |
11 #include "GrGpu.h" | |
12 #include "gl/GrGLXferProcessor.h" | |
13 #include "gl/builders/GrGLFragmentShaderBuilder.h" | |
14 #include "gl/builders/GrGLProgramBuilder.h" | |
15 | |
16 bool GrAdvancedEquationXPFactory::IsSupported(const GrContext* context, SkXfermo de::Mode mode) { | |
17 if (!context->getGpu()->caps()->advancedBlendEquationSupport()) { | |
18 return false; | |
19 } | |
20 if (mode > SkXfermode::kLastMode) { | |
21 return false; | |
22 } | |
23 return mode == SkXfermode::kScreen_Mode || mode > SkXfermode::kLastCoeffMode ; | |
24 } | |
25 | |
26 template<GrBlendEquation Equation> GrXPFactory* GrAdvancedEquationXPFactory::Tak eFactoryRef() { | |
27 GR_STATIC_ASSERT(GrBlendEquationIsAdvanced(Equation)); | |
28 static GrAdvancedEquationXPFactory* factory; | |
29 if (!factory) { | |
30 factory = SkNEW_ARGS(GrAdvancedEquationXPFactory, (Equation)); | |
31 } | |
32 return SkRef(factory); | |
33 } | |
34 | |
35 GrXPFactory* GrAdvancedEquationXPFactory::Create(const GrContext* context, SkXfe rmode::Mode mode) { | |
36 if (!context->getGpu()->caps()->advancedBlendEquationSupport()) { | |
37 return NULL; | |
38 } | |
39 | |
40 switch (mode) { | |
41 default: break; | |
42 case SkXfermode::kScreen_Mode: return TakeFactoryRef<kScreen_GrBlendEqua tion>(); | |
43 case SkXfermode::kOverlay_Mode: return TakeFactoryRef<kOverlay_GrBlendEq uation>(); | |
44 case SkXfermode::kDarken_Mode: return TakeFactoryRef<kDarken_GrBlendEqua tion>(); | |
45 case SkXfermode::kLighten_Mode: return TakeFactoryRef<kLighten_GrBlendEq uation>(); | |
46 case SkXfermode::kColorDodge_Mode: return TakeFactoryRef<kColorDodge_GrB lendEquation>(); | |
47 case SkXfermode::kColorBurn_Mode: return TakeFactoryRef<kColorBurn_GrBle ndEquation>(); | |
48 case SkXfermode::kHardLight_Mode: return TakeFactoryRef<kHardLight_GrBle ndEquation>(); | |
49 case SkXfermode::kSoftLight_Mode: return TakeFactoryRef<kSoftLight_GrBle ndEquation>(); | |
50 case SkXfermode::kDifference_Mode: return TakeFactoryRef<kDifference_GrB lendEquation>(); | |
51 case SkXfermode::kExclusion_Mode: return TakeFactoryRef<kExclusion_GrBle ndEquation>(); | |
52 case SkXfermode::kMultiply_Mode: return TakeFactoryRef<kMultiply_GrBlend Equation>(); | |
53 case SkXfermode::kHue_Mode: return TakeFactoryRef<kHSLHue_GrBlendEquatio n>(); | |
54 case SkXfermode::kSaturation_Mode: return TakeFactoryRef<kHSLSaturation_ GrBlendEquation>(); | |
55 case SkXfermode::kColor_Mode: return TakeFactoryRef<kHSLColor_GrBlendEqu ation>(); | |
56 case SkXfermode::kLuminosity_Mode: return TakeFactoryRef<kHSLLuminosity_ GrBlendEquation>(); | |
57 } | |
58 | |
59 return NULL; | |
60 } | |
61 | |
62 bool GrAdvancedEquationXPFactory::canTweakAlphaForCoverage() const { | |
63 /* | |
64 The general SVG blend equation is defined in the spec as follows: | |
65 | |
66 Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa) | |
67 Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa) | |
68 | |
69 (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha, | |
70 and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied | |
71 RGB colors.) | |
72 | |
73 For every "advanced" blend mode, X=Y=Z=1 and this equation reduces to the | |
74 PDF blend equation. | |
75 | |
76 It can be shown that when X=Y=Z=1, canTweakAlphaForCoverage() is true. | |
Mark Kilgard
2015/04/03 18:30:03
great to see this analysis
| |
77 | |
78 | |
79 == Color == | |
80 | |
81 We substitute Y=Z=1 and define a blend() function that calculates Dca' in | |
82 terms of premultiplied alpha only: | |
83 | |
84 blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0, | |
85 Sca : if Da == 0, | |
86 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dc a * (1-Sa) : if Sa,Da != 0} | |
87 | |
88 And for coverage modulation, Skia uses a post blend src-over model: | |
89 | |
90 Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
91 | |
92 (Where f is the fractional coverage.) | |
93 | |
94 Next we show that canTweakAlphaForCoverage() is true by proving the | |
95 following relationship: | |
96 | |
97 blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
98 | |
99 Corner cases (Sa=0, Da=0, and f=0): | |
100 | |
101 Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
102 = f * Dca + (1-f) * Dca [Sa=0, definition of blend()] | |
103 = Dca | |
104 = blend(0, Dca, 0, Da) [definition of blend()] | |
105 = blend(f*Sca, Dca, f*Sa, Da) [Sa=0] | |
106 | |
107 Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
108 = f * Sca + (1-f) * Dca [Da=0, definition of blend()] | |
109 = f * Sca [Da=0] | |
110 = blend(f*Sca, 0, f*Sa, 0) [definition of blend()] | |
111 = blend(f*Sca, Dca, f*Sa, Da) [Da=0] | |
112 | |
113 f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
114 = Dca [f=0] | |
115 = blend(0, Dca, 0, Da) [definition of blend()] | |
116 = blend(f*Sca, Dca, f*Sa, Da) [f=0] | |
117 | |
118 General case (f,Sa,Da != 0): | |
119 | |
120 f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca | |
121 = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f ) * Dca [Sa,Da != 0, definition of blend()] | |
122 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca | |
123 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * S a + Dca - f*Dca | |
124 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca | |
125 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca | |
126 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) | |
127 = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0] | |
128 = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()] | |
Mark Kilgard
2015/04/03 18:30:04
great analysis
I think you should motivate the "C
Chris Dalton
2015/04/17 08:37:18
Done.
| |
129 | |
130 | |
131 == Alpha == | |
132 | |
133 We substitute X=Y=Z=1 and define a blend() function that calculates Da': | |
134 | |
135 blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa) | |
136 = Sa * Da + Sa - Sa * Da + Da - Da * Sa | |
137 = Sa + Da - Sa * Da | |
138 | |
139 We use the same model for coverage modulation as we did with color: | |
140 | |
141 Da'' = f * blend(Sa, Da) + (1-f) * Da | |
142 | |
143 And show that canTweakAlphaForCoverage() is true by proving the following | |
144 relationship: | |
145 | |
146 blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da | |
147 | |
148 | |
149 f * blend(Sa, Da) + (1-f) * Da | |
150 = f * (Sa + Da - Sa * Da) + (1-f) * Da | |
151 = f*Sa + f*Da - f*Sa * Da + Da - f*Da | |
152 = f*Sa - f*Sa * Da + Da | |
153 = f*Sa + Da - f*Sa * Da | |
154 = blend(f*Sa, Da) | |
155 */ | |
156 | |
157 return true; | |
158 } | |
159 | |
160 bool GrAdvancedEquationXPFactory::supportsRGBCoverage(GrColor knownColor, | |
161 uint32_t knownColorFlags) const { | |
162 /* | |
163 We do coverage modulation by multiplying it into the src color before | |
164 blending. The GPU runs the blend equation *after* the fragment shader, so | |
165 it is not within our control to apply coverage at that point. This model | |
166 is mathematically correct, but the proof lies in the following property: | |
167 | |
168 f*Sca / f*Sa == Sc | |
169 | |
170 (That is, the general case, f,Sa,Da != 0. See canTweakAlphaForCoverage().) | |
171 | |
172 But when coverage is unique per channel, this property no longer holds: | |
Mark Kilgard
2015/04/03 18:30:03
"when coverage is unique per channel" ??
huh? Wh
Chris Dalton
2015/04/17 08:37:19
Yeah, "RGB coverage" in the context of GrXferProce
| |
173 | |
174 fc*Sca / fa*Sa == fc/fa * Sc != Sc [fc!=fa] | |
175 | |
176 The only time it is still true is when Sc == 0. | |
Chris Dalton
2015/03/31 10:51:49
This is most likely wrong. I'll update the comment
| |
177 | |
178 (It would also work out if we could know the dst color was trans black.) | |
Mark Kilgard
2015/04/03 18:30:03
I'm assume by "trans black" you mean transparent b
| |
179 */ | |
180 | |
181 return kRGB_GrColorComponentFlags == (knownColorFlags & kRGB_GrColorComponen tFlags) && | |
182 0 == (knownColor & ~(0xffu << GrColor_SHIFT_A)); // RGB=0 | |
Mark Kilgard
2015/04/03 18:30:03
Is there not some constant to use instead of 0xffu
Chris Dalton
2015/04/17 08:37:19
That code is completely gone now.
| |
183 } | |
184 | |
185 bool GrAdvancedEquationXPFactory::willBlendCoherently(const GrDrawTargetCaps& ca ps) const { | |
186 return GrDrawTargetCaps::kAdvancedCoherent_BlendEquationSupport == caps.blen dEquationSupport(); | |
187 } | |
188 | |
189 GrXferProcessor* GrAdvancedEquationXPFactory::onCreateXferProcessor( | |
190 const GrDrawTargetCaps& caps, | |
191 const GrProcOptInfo& col orPOI, | |
192 const GrProcOptInfo& cov eragePOI, | |
Mark Kilgard
2015/04/03 18:30:03
wish I knew what "XP" stands for
can I assume POI
Chris Dalton
2015/04/17 08:37:19
XP = Xfer Processor = An object that controls the
| |
193 const GrDeviceCoordTextu re* dstCopy) const { | |
194 SkASSERT(caps.advancedBlendEquationSupport()); | |
195 SkASSERT(!dstCopy || !dstCopy->texture()); | |
196 | |
197 if (coveragePOI.isFourChannelOutput() && !this->supportsRGBCoverage(colorPOI .color(), | |
198 colorPOI .validFlags())) { | |
199 return NULL; | |
200 } | |
201 if (!coveragePOI.isSolidWhite()) { | |
202 return SkRef(&fXferProcessorWithCoverage); | |
203 } | |
204 return SkRef(&fXferProcessor); | |
205 } | |
206 | |
207 void GrAdvancedEquationXPFactory::XferProcessorBase::onGetGLProcessorKey(const G rGLCaps&, | |
208 GrProcessor KeyBuilder*) const { | |
209 // Our XP's don't emit variable fragment code. (HasCoverage is accounted for by the class ID.) | |
210 return; | |
211 } | |
212 | |
213 template</*HasCoverage = true*/> | |
214 GrGLXferProcessor* GrAdvancedEquationXPFactory::XferProcessor<true>::createGLIns tance() const { | |
215 class GLXferProcessorWithCoverage : public GrGLXferProcessor { | |
216 private: | |
217 void onEmitCode(const EmitArgs& args) override { | |
218 // We do coverage modulation by multiplying it into the src color be fore blending. | |
219 // (See canTweakAlphaForCoverage()) | |
220 GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilde r(); | |
221 fsBuilder->codeAppendf("%s = %s * %s;", | |
222 args.fOutputPrimary, args.fInputColor, args.f InputCoverage); | |
223 } | |
224 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) ov erride {} | |
225 }; | |
226 return SkNEW(GLXferProcessorWithCoverage); | |
227 } | |
228 | |
229 template</*HasCoverage = false*/> | |
230 GrGLXferProcessor* GrAdvancedEquationXPFactory::XferProcessor<false>::createGLIn stance() const { | |
231 class GLXferProcessor : public GrGLXferProcessor { | |
232 private: | |
233 void onEmitCode(const EmitArgs& args) override { | |
234 GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilde r(); | |
235 fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputC olor); | |
236 } | |
237 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) ov erride {} | |
238 }; | |
239 return SkNEW(GLXferProcessor); | |
240 } | |
241 | |
242 void GrAdvancedEquationXPFactory::XferProcessorBase::onGetBlendInfo(BlendInfo* b lendInfo) const { | |
243 SkASSERT(GrBlendEquationIsAdvanced(fEquation)); | |
244 blendInfo->fEquation = fEquation; | |
245 } | |
246 | |
247 | |
248 GR_DEFINE_XP_FACTORY_TEST(GrAdvancedEquationXPFactory); | |
249 | |
250 GrXPFactory* GrAdvancedEquationXPFactory::TestCreate(SkRandom* random, | |
251 GrContext* context, | |
252 const GrDrawTargetCaps& cap s, | |
253 GrTexture*[]) { | |
254 int mode = random->nextRangeU(SkXfermode::kLastCoeffMode, SkXfermode::kLastM ode); | |
255 if (mode == SkXfermode::kLastCoeffMode) { | |
256 mode = SkXfermode::kScreen_Mode; | |
257 } | |
258 return GrAdvancedEquationXPFactory::Create(context, static_cast<SkXfermode:: Mode>(mode)); | |
259 } | |
OLD | NEW |