OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "effects/GrPorterDuffXferProcessor.h" | 8 #include "effects/GrPorterDuffXferProcessor.h" |
9 | 9 |
10 #include "GrBlend.h" | 10 #include "GrBlend.h" |
11 #include "GrDrawTargetCaps.h" | 11 #include "GrDrawTargetCaps.h" |
12 #include "GrProcessor.h" | 12 #include "GrProcessor.h" |
13 #include "GrProcOptInfo.h" | 13 #include "GrProcOptInfo.h" |
14 #include "GrTypes.h" | 14 #include "GrTypes.h" |
15 #include "GrXferProcessor.h" | 15 #include "GrXferProcessor.h" |
16 #include "gl/GrGLXferProcessor.h" | 16 #include "gl/GrGLXferProcessor.h" |
17 #include "gl/builders/GrGLFragmentShaderBuilder.h" | 17 #include "gl/builders/GrGLFragmentShaderBuilder.h" |
18 #include "gl/builders/GrGLProgramBuilder.h" | 18 #include "gl/builders/GrGLProgramBuilder.h" |
19 | 19 |
20 static bool can_tweak_alpha_for_coverage(GrBlendCoeff dstCoeff) { | 20 /** |
21 /* | 21 * Wraps the shader outputs and HW blend state that comprise a Porter Duff blend mode with coverage. |
22 The fractional coverage is f. | 22 */ |
23 The src and dst coeffs are Cs and Cd. | 23 struct BlendFormula { |
24 The dst and src colors are S and D. | 24 public: |
25 We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D. By tweaking the sou rce color's alpha | 25 /** |
26 we're replacing S with S'=fS. It's obvious that that first term will always be ok. The second | 26 * Values the shader can write to primary and secondary outputs. These must all be modulated by |
27 term can be rearranged as [1-(1-Cd)f]D. By substituting in the various poss ibilities for Cd we | 27 * coverage to support mixed samples. The XP will ignore the multiplies when not using coverage. |
28 find that only 1, ISA, and ISC produce the correct destination when applied to S' and D. | 28 */ |
29 */ | 29 enum OutputType { |
30 return kOne_GrBlendCoeff == dstCoeff || | 30 kNone_OutputType, //<! 0 |
31 kISA_GrBlendCoeff == dstCoeff || | 31 kCoverage_OutputType, //<! inputCoverage |
32 kISC_GrBlendCoeff == dstCoeff; | 32 kModulate_OutputType, //<! inputColor * inputCoverage |
33 kISAModulate_OutputType, //<! (1 - inputColor.a) * inputCoverage | |
34 kISCModulate_OutputType, //<! (1 - inputColor) * inputCoverage | |
35 | |
36 kLast_OutputType = kISCModulate_OutputType | |
37 }; | |
38 | |
39 enum CoverageStrategy { | |
egdaniel
2015/05/21 18:36:45
comments on what these strategies mean?
Chris Dalton
2015/05/21 23:14:49
Done. (Code removed)
| |
40 kModulate_CoverageStrategy, | |
41 kSrcCoeffZero_CoverageStrategy, | |
42 kDstCoeffZero_CoverageStrategy, | |
43 kGeneral_CoverageStrategy, | |
44 | |
45 kLast_CoverageStrategy = kGeneral_CoverageStrategy | |
46 }; | |
47 | |
48 enum Properties { | |
49 kModifiesDst_Property = 1, | |
50 kUsesDstColor_Property = 1 << 1, | |
51 kUsesInputColor_Property = 1 << 2, | |
52 kCanTweakAlphaForCoverage_Property = 1 << 3, | |
53 | |
54 kLast_Property = kCanTweakAlphaForCoverage_Property | |
55 }; | |
56 | |
57 /** | |
58 * Returns an optimized blend formula for the input color and coverage. | |
59 */ | |
60 static BlendFormula Get(SkXfermode::Mode xfermode, | |
61 const GrProcOptInfo& colorPOI, | |
62 const GrProcOptInfo& coveragePOI); | |
63 | |
64 BlendFormula& operator =(const BlendFormula& other) { | |
65 fData = other.fData; | |
66 return *this; | |
67 } | |
68 | |
69 bool operator ==(const BlendFormula& other) const { | |
70 return fData == other.fData; | |
71 } | |
72 | |
73 bool hasSecondaryOutput() const { return kNone_OutputType != fSecondaryOutpu tType; } | |
74 bool modifiesDst() const { return fProps & kModifiesDst_Property; } | |
75 bool usesDstColor() const { return fProps & kUsesDstColor_Property; } | |
76 bool usesInputColor() const { return fProps & kUsesInputColor_Property; } | |
77 bool canTweakAlphaForCoverage() const { return fProps & kCanTweakAlphaForCov erage_Property; } | |
78 | |
79 /** | |
80 * Rewrite the blend formula to account for coverage. | |
81 */ | |
82 void addCoverage(const GrProcOptInfo& colorPOI); | |
83 | |
84 /** | |
85 * Deduce the coverage strategy for a basic, compile-time constant Porter Du ff formula. | |
86 */ | |
87 template<GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff> | |
88 struct get_coverage_strategy : SkTIntegralConstant<CoverageStrategy, | |
89 | |
90 (GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(kAdd_GrBlendEquation, SrcCoeff, D stCoeff) ? | |
91 kModulate_CoverageStrategy : | |
92 | |
93 (kZero_GrBlendCoeff == SrcCoeff ? | |
94 kSrcCoeffZero_CoverageStrategy : | |
95 | |
96 (kZero_GrBlendCoeff == DstCoeff ? | |
97 kDstCoeffZero_CoverageStrategy : | |
98 | |
99 kGeneral_CoverageStrategy)))> { | |
100 }; | |
101 | |
102 /** | |
103 * Deduce the properties of a basic, compile-time constant Porter Duff formu la. | |
104 */ | |
105 template<GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff> | |
106 struct get_properties : SkTIntegralConstant<Properties, static_cast<Properti es>( | |
107 | |
108 (GR_BLEND_MODIFIES_DST(kAdd_GrBlendEquation, SrcCoeff, DstCoeff) ? | |
109 kModifiesDst_Property : 0) | | |
110 | |
111 (GR_BLEND_COEFFS_USE_DST_COLOR(SrcCoeff, DstCoeff) ? | |
112 kUsesDstColor_Property : 0) | | |
113 | |
114 (GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff, DstCoeff) ? | |
115 kUsesInputColor_Property : 0) | | |
116 | |
117 (GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(kAdd_GrBlendEquation, SrcCoeff, D stCoeff) ? | |
118 kCanTweakAlphaForCoverage_Property : 0))> { | |
119 }; | |
120 | |
121 union { | |
122 struct { | |
123 OutputType fPrimaryOutputType : 3; | |
124 OutputType fSecondaryOutputType : 3; | |
125 GrBlendEquation fBlendEquation : 5; | |
126 GrBlendCoeff fSrcCoeff : 5; | |
127 GrBlendCoeff fDstCoeff : 5; | |
128 CoverageStrategy fCoverageStrategy : 2; | |
129 Properties fProps : 9; // Should bring the l ength to 32. | |
130 | |
131 GR_STATIC_ASSERT(kLast_OutputType < (1 << 3)); | |
132 GR_STATIC_ASSERT(kLast_GrBlendEquation < (1 << 5)); | |
133 GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << 5)); | |
134 GR_STATIC_ASSERT(kLast_CoverageStrategy < (1 << 2)); | |
135 GR_STATIC_ASSERT(kLast_Property < (1 << 9)); | |
136 }; | |
137 uint32_t fData; | |
138 }; | |
139 }; | |
140 | |
141 GR_STATIC_ASSERT(4 == sizeof(BlendFormula)); | |
142 | |
143 GR_MAKE_BITFIELD_OPS(BlendFormula::Properties); | |
144 | |
145 /** | |
146 * Init a compile-time constant BlendFormula and automatically deduce fCoverageS trategy and fProps. | |
147 * | |
148 */ | |
149 #define COEFF_FORMULA(SRC_COEFF, DST_COEFF) \ | |
150 {GR_BLEND_COEFFS_USE_SRC_COLOR(SRC_COEFF, DST_COEFF) ? \ | |
151 BlendFormula::kModulate_OutputType : \ | |
152 BlendFormula::kNone_OutputType, \ | |
153 BlendFormula::kNone_OutputType, \ | |
154 kAdd_GrBlendEquation, SRC_COEFF, DST_COEFF, \ | |
155 BlendFormula::get_coverage_strategy<SRC_COEFF, DST_COEFF>::value, \ | |
156 BlendFormula::get_properties<SRC_COEFF, DST_COEFF>::value} | |
157 | |
158 #define DST_CLEAR_FORMULA COEFF_FORMULA(kZero_GrBlendCoeff, kZero_GrBlendCoeff) | |
egdaniel
2015/05/21 18:36:45
I don't really care one way or the other, but do y
Chris Dalton
2015/05/21 23:14:49
Done. (They have a purpose again now that we've go
| |
159 | |
160 #define NO_DST_WRITE_FORMULA COEFF_FORMULA(kZero_GrBlendCoeff, kOne_GrBlendCoeff ) | |
161 | |
162 /** | |
163 * This table outlines the blend formulas we use with each xfermode, both with a nd without an opaque | |
164 * input color. Optimization properties are deduced at compile time so we can ma ke runtime decisions | |
165 * quickly. RGB coverage is not supported. | |
166 */ | |
167 static const BlendFormula gBlendTable[2][SkXfermode::kLastCoeffMode + 1] = { | |
168 | |
169 /*>> Input color unknown <<*/ { | |
170 | |
171 /* clear */ DST_CLEAR_FORMULA, | |
172 /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), | |
173 /* dst */ NO_DST_WRITE_FORMULA, | |
174 /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), | |
175 /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), | |
176 /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), | |
177 /* dst-in */ COEFF_FORMULA( kZero_GrBlendCoeff, kSA_GrBlendCoeff), | |
178 /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), | |
179 /* dst-out */ COEFF_FORMULA( kZero_GrBlendCoeff, kISA_GrBlendCoeff), | |
180 /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), | |
181 /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kSA_GrBlendCoeff), | |
182 /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), | |
183 /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), | |
184 /* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff), | |
185 /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), | |
186 | |
187 }, /*>> Input color opaque <<*/ { | |
188 | |
189 /* clear */ DST_CLEAR_FORMULA, | |
190 /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), | |
191 /* dst */ NO_DST_WRITE_FORMULA, | |
192 /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), | |
193 /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), | |
194 /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), | |
195 /* dst-in */ NO_DST_WRITE_FORMULA, | |
196 /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), | |
197 /* dst-out */ DST_CLEAR_FORMULA, | |
198 /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), | |
199 /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), | |
200 /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), | |
201 /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), | |
202 /* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff), | |
203 /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), | |
204 }}; | |
205 | |
206 /** | |
207 * Finds the shader output for "(1 - dstCoeff) * inputCoverage". | |
208 */ | |
209 static BlendFormula::OutputType inverse_dst_coeff_modulate(GrBlendCoeff dstCoeff ) { | |
210 static BlendFormula::OutputType gOneMinusDstCoeffModulate[] = { | |
211 BlendFormula::kCoverage_OutputType, //<! kZero_GrBlendCoeff | |
212 BlendFormula::kNone_OutputType, //<! kOne_GrBlendCoeff | |
213 BlendFormula::kISCModulate_OutputType, //<! kSC_GrBlendCoeff | |
214 static_cast<BlendFormula::OutputType>(-1), | |
215 static_cast<BlendFormula::OutputType>(-1), | |
216 static_cast<BlendFormula::OutputType>(-1), | |
217 BlendFormula::kISAModulate_OutputType //<! kSA_GrBlendCoeff | |
218 }; | |
219 | |
220 SkASSERT(dstCoeff >= 0 && dstCoeff < SK_ARRAY_COUNT(gOneMinusDstCoeffModulat e)); | |
221 SkASSERT(-1 != gOneMinusDstCoeffModulate[dstCoeff]); | |
222 return gOneMinusDstCoeffModulate[dstCoeff]; | |
223 | |
224 GR_STATIC_ASSERT(0 == kZero_GrBlendCoeff); | |
225 GR_STATIC_ASSERT(1 == kOne_GrBlendCoeff); | |
226 GR_STATIC_ASSERT(2 == kSC_GrBlendCoeff); | |
227 GR_STATIC_ASSERT(6 == kSA_GrBlendCoeff); | |
33 } | 228 } |
34 | 229 |
230 void BlendFormula::addCoverage(const GrProcOptInfo& colorPOI) { | |
231 SkASSERT(kModulate_CoverageStrategy == fCoverageStrategy || !this->canTweakA lphaForCoverage()); | |
232 SkASSERT(kModulate_OutputType == fPrimaryOutputType || kNone_OutputType == f PrimaryOutputType); | |
233 SkASSERT(kNone_OutputType == fSecondaryOutputType); | |
234 SkASSERT(kAdd_GrBlendEquation == fBlendEquation); | |
235 | |
236 switch (fCoverageStrategy) { | |
237 /** | |
238 * We will get the right answer just by modulating the output color with coverage. | |
239 */ | |
240 case kModulate_CoverageStrategy: | |
241 SkASSERT(this->canTweakAlphaForCoverage()); | |
242 return; | |
243 | |
244 /** | |
245 * When the src coeff is Zero, the equation with f=coverage becomes: | |
246 * | |
247 * D' = f * D * dstCoeff + (1-f) * D | |
248 * | |
249 * This can be rewritten as: | |
250 * | |
251 * D' = D - D * [f * (1 - dstCoeff)] | |
252 * | |
253 * To implement this formula, we output [f * (1 - dstCoeff)] for the pri mary color and use a | |
254 * reverse subtract HW blend equation with coeffs of (DC, One). | |
255 */ | |
256 case kSrcCoeffZero_CoverageStrategy: | |
257 SkASSERT(kZero_GrBlendCoeff == fSrcCoeff); | |
258 fPrimaryOutputType = inverse_dst_coeff_modulate(fDstCoeff); | |
259 fBlendEquation = kReverseSubtract_GrBlendEquation; | |
260 fSrcCoeff = kDC_GrBlendCoeff; | |
261 fDstCoeff = kOne_GrBlendCoeff; | |
262 break; | |
263 | |
264 /** | |
265 * When the dst coeff is Zero, the equation with f=coverage becomes: | |
266 * | |
267 * D' = f * S * srcCoeff + (1-f) * D | |
268 * | |
269 * To implement this formula, we output [f] for the secondary color and r eplace the HW dst | |
270 * coeff with IS2A (or ISA in the case of opaque color). | |
271 */ | |
272 case kDstCoeffZero_CoverageStrategy: | |
273 SkASSERT(kZero_GrBlendCoeff != fSrcCoeff && kZero_GrBlendCoeff == fD stCoeff); | |
274 if (colorPOI.isOpaque()) { | |
275 fDstCoeff = kISA_GrBlendCoeff; | |
276 fProps = fProps | kCanTweakAlphaForCoverage_Property; | |
277 } else { | |
278 fSecondaryOutputType = kCoverage_OutputType; | |
279 fDstCoeff = kIS2A_GrBlendCoeff; | |
280 } | |
281 break; | |
282 | |
283 /** | |
284 * The general equation with f=coverage is: | |
285 * | |
286 * D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D | |
287 * | |
288 * This can be rewritten as: | |
289 * | |
290 * D' = f * S * srcCoeff + D * (1 - [f * (1 - dstCoeff)]) | |
291 * | |
292 * To implement this formula, we output [f * (1 - dstCoeff)] for the sec ondary color and | |
293 * replace the HW dst coeff with IS2C. | |
294 */ | |
295 case kGeneral_CoverageStrategy: | |
296 SkASSERT(kZero_GrBlendCoeff != fSrcCoeff && kZero_GrBlendCoeff != fD stCoeff); | |
297 fSecondaryOutputType = inverse_dst_coeff_modulate(fDstCoeff); | |
298 fDstCoeff = kIS2C_GrBlendCoeff; | |
299 break; | |
300 }; | |
301 | |
302 fProps = fProps | (kModifiesDst_Property | kUsesDstColor_Property); | |
303 } | |
304 | |
305 BlendFormula BlendFormula::Get(SkXfermode::Mode xfermode, | |
306 const GrProcOptInfo& colorPOI, | |
307 const GrProcOptInfo& coveragePOI) { | |
308 SkASSERT(xfermode >= 0 && xfermode <= SkXfermode::kLastCoeffMode); | |
309 SkASSERT(!coveragePOI.isFourChannelOutput()); | |
310 | |
311 BlendFormula blendFormula = gBlendTable[colorPOI.isOpaque()][xfermode]; | |
312 if (!coveragePOI.isSolidWhite()) { | |
313 blendFormula.addCoverage(colorPOI); | |
314 } | |
315 return blendFormula; | |
316 } | |
317 | |
318 /////////////////////////////////////////////////////////////////////////////// | |
319 | |
35 class PorterDuffXferProcessor : public GrXferProcessor { | 320 class PorterDuffXferProcessor : public GrXferProcessor { |
36 public: | 321 public: |
37 static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, | 322 static GrXferProcessor* Create(SkXfermode::Mode xfermode, const GrDeviceCoor dTexture* dstCopy, |
38 GrColor constant, const GrDeviceCoordTexture* dstCopy, | |
39 bool willReadDstColor) { | 323 bool willReadDstColor) { |
40 return SkNEW_ARGS(PorterDuffXferProcessor, (srcBlend, dstBlend, constant , dstCopy, | 324 return SkNEW_ARGS(PorterDuffXferProcessor, (xfermode, dstCopy, willReadD stColor)); |
41 willReadDstColor)); | |
42 } | 325 } |
43 | 326 |
44 ~PorterDuffXferProcessor() override; | 327 ~PorterDuffXferProcessor() override; |
45 | 328 |
46 const char* name() const override { return "Porter Duff"; } | 329 const char* name() const override { return "Porter Duff"; } |
47 | 330 |
48 GrGLXferProcessor* createGLInstance() const override; | 331 GrGLXferProcessor* createGLInstance() const override; |
49 | 332 |
50 bool hasSecondaryOutput() const override; | 333 bool hasSecondaryOutput() const override { |
334 return fBlendFormula.hasSecondaryOutput(); | |
335 } | |
51 | 336 |
52 /////////////////////////////////////////////////////////////////////////// | 337 SkXfermode::Mode getXfermode() const { return fXfermode; } |
53 /// @name Stage Output Types | 338 BlendFormula getBlendFormula() const { return fBlendFormula; } |
54 //// | |
55 | |
56 enum PrimaryOutputType { | |
57 kNone_PrimaryOutputType, | |
58 kColor_PrimaryOutputType, | |
59 kCoverage_PrimaryOutputType, | |
60 // Modulate color and coverage, write result as the color output. | |
61 kModulate_PrimaryOutputType, | |
62 // Custom Porter-Duff output, used for when we explictly are reading the dst and blending | |
63 // in the shader. Secondary Output must be none if you use this. The cus tom blend uses the | |
64 // equation: cov * (coeffS * S + coeffD * D) + (1 - cov) * D | |
65 kCustom_PrimaryOutputType | |
66 }; | |
67 | |
68 enum SecondaryOutputType { | |
69 // There is no secondary output | |
70 kNone_SecondaryOutputType, | |
71 // Writes coverage as the secondary output. Only set if dual source blen ding is supported | |
72 // and primary output is kModulate. | |
73 kCoverage_SecondaryOutputType, | |
74 // Writes coverage * (1 - colorA) as the secondary output. Only set if d ual source blending | |
75 // is supported and primary output is kModulate. | |
76 kCoverageISA_SecondaryOutputType, | |
77 // Writes coverage * (1 - colorRGBA) as the secondary output. Only set i f dual source | |
78 // blending is supported and primary output is kModulate. | |
79 kCoverageISC_SecondaryOutputType, | |
80 | |
81 kSecondaryOutputTypeCnt, | |
82 }; | |
83 | |
84 PrimaryOutputType primaryOutputType() const { return fPrimaryOutputType; } | |
85 SecondaryOutputType secondaryOutputType() const { return fSecondaryOutputTyp e; } | |
86 | |
87 GrBlendCoeff getSrcBlend() const { return fSrcBlend; } | |
88 GrBlendCoeff getDstBlend() const { return fDstBlend; } | |
89 | 339 |
90 private: | 340 private: |
91 PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColo r constant, | 341 PorterDuffXferProcessor(SkXfermode::Mode, const GrDeviceCoordTexture* dstCop y, |
92 const GrDeviceCoordTexture* dstCopy, bool willReadDs tColor); | 342 bool willReadDstColor); |
93 | 343 |
94 GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI, | 344 GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI, |
95 const GrProcOptInfo& coveragePO I, | 345 const GrProcOptInfo& coveragePO I, |
96 bool doesStencilWrite, | 346 bool doesStencilWrite, |
97 GrColor* overrideColor, | 347 GrColor* overrideColor, |
98 const GrCaps& caps) override; | 348 const GrCaps& caps) override; |
99 | 349 |
100 void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) c onst override; | 350 void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) c onst override; |
101 | 351 |
102 void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { | 352 void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { |
103 if (!this->willReadDstColor()) { | 353 if (!this->willReadDstColor()) { |
104 blendInfo->fSrcBlend = fSrcBlend; | 354 blendInfo->fEquation = fBlendFormula.fBlendEquation; |
105 blendInfo->fDstBlend = fDstBlend; | 355 blendInfo->fSrcBlend = fBlendFormula.fSrcCoeff; |
106 } else { | 356 blendInfo->fDstBlend = fBlendFormula.fDstCoeff; |
107 blendInfo->fSrcBlend = kOne_GrBlendCoeff; | 357 blendInfo->fWriteColor = fBlendFormula.modifiesDst(); |
108 blendInfo->fDstBlend = kZero_GrBlendCoeff; | |
109 } | 358 } |
110 blendInfo->fBlendConstant = fBlendConstant; | |
111 } | 359 } |
112 | 360 |
113 bool onIsEqual(const GrXferProcessor& xpBase) const override { | 361 bool onIsEqual(const GrXferProcessor& xpBase) const override { |
114 const PorterDuffXferProcessor& xp = xpBase.cast<PorterDuffXferProcessor> (); | 362 const PorterDuffXferProcessor& xp = xpBase.cast<PorterDuffXferProcessor> (); |
115 if (fSrcBlend != xp.fSrcBlend || | 363 return fXfermode == xp.fXfermode && |
116 fDstBlend != xp.fDstBlend || | 364 fBlendFormula == xp.fBlendFormula; |
117 fBlendConstant != xp.fBlendConstant || | |
118 fPrimaryOutputType != xp.fPrimaryOutputType || | |
119 fSecondaryOutputType != xp.fSecondaryOutputType) { | |
120 return false; | |
121 } | |
122 return true; | |
123 } | 365 } |
124 | 366 |
125 GrXferProcessor::OptFlags internalGetOptimizations(const GrProcOptInfo& colo rPOI, | 367 SkXfermode::Mode fXfermode; |
126 const GrProcOptInfo& cove ragePOI, | 368 BlendFormula fBlendFormula; |
127 bool doesStencilWrite); | |
128 | |
129 void calcOutputTypes(GrXferProcessor::OptFlags blendOpts, const GrCaps& caps , | |
130 bool hasSolidCoverage); | |
131 | |
132 GrBlendCoeff fSrcBlend; | |
133 GrBlendCoeff fDstBlend; | |
134 GrColor fBlendConstant; | |
135 PrimaryOutputType fPrimaryOutputType; | |
136 SecondaryOutputType fSecondaryOutputType; | |
137 | 369 |
138 typedef GrXferProcessor INHERITED; | 370 typedef GrXferProcessor INHERITED; |
139 }; | 371 }; |
140 | 372 |
141 /////////////////////////////////////////////////////////////////////////////// | 373 /////////////////////////////////////////////////////////////////////////////// |
142 | 374 |
143 bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, GrBlendCoeff coeff , | 375 void append_color_output(const PorterDuffXferProcessor& xp, GrGLXPFragmentBuilde r* fsBuilder, |
376 BlendFormula::OutputType outputType, const char* output , | |
377 const char* inColor, const char* inCoverage) { | |
378 switch (outputType) { | |
379 case BlendFormula::kNone_OutputType: | |
380 fsBuilder->codeAppendf("%s = vec4(0.0);", output); | |
381 break; | |
382 case BlendFormula::kCoverage_OutputType: | |
383 fsBuilder->codeAppendf("%s = %s;", | |
384 output, xp.readsCoverage() ? inCoverage : "ve c4(1.0)"); | |
385 break; | |
386 case BlendFormula::kModulate_OutputType: | |
387 if (xp.readsCoverage()) { | |
388 fsBuilder->codeAppendf("%s = %s * %s;", output, inColor, inCover age); | |
389 } else { | |
390 fsBuilder->codeAppendf("%s = %s;", output, inColor); | |
391 } | |
392 break; | |
393 case BlendFormula::kISAModulate_OutputType: | |
394 if (xp.readsCoverage()) { | |
395 fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", output, inColo r, inCoverage); | |
396 } else { | |
397 fsBuilder->codeAppendf("%s = vec4(1.0 - %s.a);", output, inColor ); | |
398 } | |
399 break; | |
400 case BlendFormula::kISCModulate_OutputType: | |
401 if (xp.readsCoverage()) { | |
402 fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", output, in Color, inCoverage); | |
403 } else { | |
404 fsBuilder->codeAppendf("%s = vec4(1.0) - %s;", output, inColor); | |
405 } | |
406 break; | |
407 default: | |
408 SkFAIL("Unsupported output type."); | |
409 break; | |
410 } | |
411 } | |
412 | |
413 bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff, | |
144 const char* colorName, const char* srcColorName, | 414 const char* colorName, const char* srcColorName, |
145 const char* dstColorName, bool hasPrevious) { | 415 const char* dstColorName, bool hasPrevious) { |
146 if (kZero_GrBlendCoeff == coeff) { | 416 if (SkXfermode::kZero_Coeff == coeff) { |
147 return hasPrevious; | 417 return hasPrevious; |
148 } else { | 418 } else { |
149 if (hasPrevious) { | 419 if (hasPrevious) { |
150 fsBuilder->codeAppend(" + "); | 420 fsBuilder->codeAppend(" + "); |
151 } | 421 } |
152 fsBuilder->codeAppendf("%s", colorName); | 422 fsBuilder->codeAppendf("%s", colorName); |
153 switch (coeff) { | 423 switch (coeff) { |
154 case kOne_GrBlendCoeff: | 424 case SkXfermode::kOne_Coeff: |
155 break; | 425 break; |
156 case kSC_GrBlendCoeff: | 426 case SkXfermode::kSC_Coeff: |
157 fsBuilder->codeAppendf(" * %s", srcColorName); | 427 fsBuilder->codeAppendf(" * %s", srcColorName); |
158 break; | 428 break; |
159 case kISC_GrBlendCoeff: | 429 case SkXfermode::kISC_Coeff: |
160 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName); | 430 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName); |
161 break; | 431 break; |
162 case kDC_GrBlendCoeff: | 432 case SkXfermode::kDC_Coeff: |
163 fsBuilder->codeAppendf(" * %s", dstColorName); | 433 fsBuilder->codeAppendf(" * %s", dstColorName); |
164 break; | 434 break; |
165 case kIDC_GrBlendCoeff: | 435 case SkXfermode::kIDC_Coeff: |
166 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName); | 436 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName); |
167 break; | 437 break; |
168 case kSA_GrBlendCoeff: | 438 case SkXfermode::kSA_Coeff: |
169 fsBuilder->codeAppendf(" * %s.a", srcColorName); | 439 fsBuilder->codeAppendf(" * %s.a", srcColorName); |
170 break; | 440 break; |
171 case kISA_GrBlendCoeff: | 441 case SkXfermode::kISA_Coeff: |
172 fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName); | 442 fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName); |
173 break; | 443 break; |
174 case kDA_GrBlendCoeff: | 444 case SkXfermode::kDA_Coeff: |
175 fsBuilder->codeAppendf(" * %s.a", dstColorName); | 445 fsBuilder->codeAppendf(" * %s.a", dstColorName); |
176 break; | 446 break; |
177 case kIDA_GrBlendCoeff: | 447 case SkXfermode::kIDA_Coeff: |
178 fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName); | 448 fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName); |
179 break; | 449 break; |
180 default: | 450 default: |
181 SkFAIL("Unsupported Blend Coeff"); | 451 SkFAIL("Unsupported Blend Coeff"); |
182 } | 452 } |
183 return true; | 453 return true; |
184 } | 454 } |
185 } | 455 } |
186 | 456 |
187 class GLPorterDuffXferProcessor : public GrGLXferProcessor { | 457 class GLPorterDuffXferProcessor : public GrGLXferProcessor { |
188 public: | 458 public: |
189 GLPorterDuffXferProcessor(const GrProcessor&) {} | 459 GLPorterDuffXferProcessor(const GrProcessor&) {} |
190 | 460 |
191 virtual ~GLPorterDuffXferProcessor() {} | 461 virtual ~GLPorterDuffXferProcessor() {} |
192 | 462 |
193 static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps, | 463 static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps, |
194 GrProcessorKeyBuilder* b) { | 464 GrProcessorKeyBuilder* b) { |
195 const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcess or>(); | 465 const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcess or>(); |
196 b->add32(xp.primaryOutputType()); | |
197 b->add32(xp.secondaryOutputType()); | |
198 if (xp.willReadDstColor()) { | 466 if (xp.willReadDstColor()) { |
199 b->add32(xp.getSrcBlend()); | 467 b->add32(xp.getXfermode()); // Parent class includes willReadDstCol or() in key. |
200 b->add32(xp.getDstBlend()); | 468 } else { |
469 b->add32(xp.readsCoverage() | | |
470 (xp.getBlendFormula().fPrimaryOutputType << 1) | | |
471 (xp.getBlendFormula().fSecondaryOutputType << 4)); | |
472 GR_STATIC_ASSERT(BlendFormula::kLast_OutputType < 8); | |
201 } | 473 } |
202 }; | 474 }; |
203 | 475 |
204 private: | 476 private: |
205 void onEmitCode(const EmitArgs& args) override { | 477 void onEmitCode(const EmitArgs& args) override { |
206 const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcesso r>(); | 478 const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcesso r>(); |
207 GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); | 479 GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
208 if (PorterDuffXferProcessor::kCustom_PrimaryOutputType != xp.primaryOutp utType()) { | 480 if (!xp.willReadDstColor()) { |
209 SkASSERT(!xp.willReadDstColor()); | 481 BlendFormula blendFormula = xp.getBlendFormula(); |
210 switch(xp.secondaryOutputType()) { | 482 if (blendFormula.hasSecondaryOutput()) { |
211 case PorterDuffXferProcessor::kNone_SecondaryOutputType: | 483 append_color_output(xp, fsBuilder, blendFormula.fSecondaryOutput Type, |
212 break; | 484 args.fOutputSecondary, args.fInputColor, arg s.fInputCoverage); |
213 case PorterDuffXferProcessor::kCoverage_SecondaryOutputType: | |
214 fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary, | |
215 args.fInputCoverage); | |
216 break; | |
217 case PorterDuffXferProcessor::kCoverageISA_SecondaryOutputType: | |
218 fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", | |
219 args.fOutputSecondary, args.fInputCol or, | |
220 args.fInputCoverage); | |
221 break; | |
222 case PorterDuffXferProcessor::kCoverageISC_SecondaryOutputType: | |
223 fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", | |
224 args.fOutputSecondary, args.fInputCol or, | |
225 args.fInputCoverage); | |
226 break; | |
227 default: | |
228 SkFAIL("Unexpected Secondary Output"); | |
229 } | 485 } |
230 | 486 append_color_output(xp, fsBuilder, blendFormula.fPrimaryOutputType, |
231 switch (xp.primaryOutputType()) { | 487 args.fOutputPrimary, args.fInputColor, args.fInp utCoverage); |
232 case PorterDuffXferProcessor::kNone_PrimaryOutputType: | |
233 fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary) ; | |
234 break; | |
235 case PorterDuffXferProcessor::kColor_PrimaryOutputType: | |
236 fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args .fInputColor); | |
237 break; | |
238 case PorterDuffXferProcessor::kCoverage_PrimaryOutputType: | |
239 fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args .fInputCoverage); | |
240 break; | |
241 case PorterDuffXferProcessor::kModulate_PrimaryOutputType: | |
242 fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor, | |
243 args.fInputCoverage); | |
244 break; | |
245 default: | |
246 SkFAIL("Unexpected Primary Output"); | |
247 } | |
248 } else { | 488 } else { |
249 SkASSERT(xp.willReadDstColor()); | 489 SkASSERT(xp.willReadDstColor()); |
250 | 490 |
491 SkXfermode::Coeff srcCoeff, dstCoeff; | |
492 SkXfermode::ModeAsCoeff(xp.getXfermode(), &srcCoeff, &dstCoeff); | |
493 | |
251 const char* dstColor = fsBuilder->dstColor(); | 494 const char* dstColor = fsBuilder->dstColor(); |
252 | 495 |
253 fsBuilder->codeAppend("vec4 colorBlend ="); | 496 fsBuilder->codeAppend("vec4 colorBlend ="); |
254 // append src blend | 497 // append src blend |
255 bool didAppend = append_porterduff_term(fsBuilder, xp.getSrcBlend(), | 498 bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, |
256 args.fInputColor, args.fInpu tColor, | 499 args.fInputColor, args.fInpu tColor, |
257 dstColor, false); | 500 dstColor, false); |
258 // append dst blend | 501 // append dst blend |
259 SkAssertResult(append_porterduff_term(fsBuilder, xp.getDstBlend(), | 502 SkAssertResult(append_porterduff_term(fsBuilder, dstCoeff, |
260 dstColor, args.fInputColor, | 503 dstColor, args.fInputColor, |
261 dstColor, didAppend)); | 504 dstColor, didAppend)); |
262 fsBuilder->codeAppend(";"); | 505 fsBuilder->codeAppend(";"); |
263 | 506 |
264 fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s ;", | 507 fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s ;", |
265 args.fOutputPrimary, args.fInputCoverage, arg s.fInputCoverage, | 508 args.fOutputPrimary, args.fInputCoverage, arg s.fInputCoverage, |
266 dstColor); | 509 dstColor); |
267 } | 510 } |
268 } | 511 } |
269 | 512 |
270 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) overri de {}; | 513 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) overri de {}; |
271 | 514 |
272 typedef GrGLXferProcessor INHERITED; | 515 typedef GrGLXferProcessor INHERITED; |
273 }; | 516 }; |
274 | 517 |
275 /////////////////////////////////////////////////////////////////////////////// | 518 /////////////////////////////////////////////////////////////////////////////// |
276 | 519 |
277 PorterDuffXferProcessor::PorterDuffXferProcessor(GrBlendCoeff srcBlend, | 520 PorterDuffXferProcessor::PorterDuffXferProcessor(SkXfermode::Mode xfermode, |
278 GrBlendCoeff dstBlend, | |
279 GrColor constant, | |
280 const GrDeviceCoordTexture* dst Copy, | 521 const GrDeviceCoordTexture* dst Copy, |
281 bool willReadDstColor) | 522 bool willReadDstColor) |
282 : INHERITED(dstCopy, willReadDstColor) | 523 : INHERITED(dstCopy, willReadDstColor) |
283 , fSrcBlend(srcBlend) | 524 , fXfermode(xfermode) { |
284 , fDstBlend(dstBlend) | |
285 , fBlendConstant(constant) | |
286 , fPrimaryOutputType(kModulate_PrimaryOutputType) | |
287 , fSecondaryOutputType(kNone_SecondaryOutputType) { | |
288 this->initClassID<PorterDuffXferProcessor>(); | 525 this->initClassID<PorterDuffXferProcessor>(); |
289 } | 526 } |
290 | 527 |
291 PorterDuffXferProcessor::~PorterDuffXferProcessor() { | 528 PorterDuffXferProcessor::~PorterDuffXferProcessor() { |
292 } | 529 } |
293 | 530 |
294 void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps, | 531 void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps, |
295 GrProcessorKeyBuilder* b) cons t { | 532 GrProcessorKeyBuilder* b) cons t { |
296 GLPorterDuffXferProcessor::GenKey(*this, caps, b); | 533 GLPorterDuffXferProcessor::GenKey(*this, caps, b); |
297 } | 534 } |
298 | 535 |
299 GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const { | 536 GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const { |
300 return SkNEW_ARGS(GLPorterDuffXferProcessor, (*this)); | 537 return SkNEW_ARGS(GLPorterDuffXferProcessor, (*this)); |
301 } | 538 } |
302 | 539 |
303 GrXferProcessor::OptFlags | 540 GrXferProcessor::OptFlags |
304 PorterDuffXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI, | 541 PorterDuffXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI, |
305 const GrProcOptInfo& coveragePOI, | 542 const GrProcOptInfo& coveragePOI, |
306 bool doesStencilWrite, | 543 bool doesStencilWrite, |
307 GrColor* overrideColor, | 544 GrColor* overrideColor, |
308 const GrCaps& caps) { | 545 const GrCaps& caps) { |
309 GrXferProcessor::OptFlags optFlags = this->internalGetOptimizations(colorPOI , | |
310 coverage POI, | |
311 doesSten cilWrite); | |
312 this->calcOutputTypes(optFlags, caps, coveragePOI.isSolidWhite()); | |
313 return optFlags; | |
314 } | |
315 | |
316 void PorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags , | |
317 const GrCaps& caps, | |
318 bool hasSolidCoverage) { | |
319 if (this->willReadDstColor()) { | |
320 fPrimaryOutputType = kCustom_PrimaryOutputType; | |
321 return; | |
322 } | |
323 | |
324 if (optFlags & kIgnoreColor_OptFlag) { | |
325 if (optFlags & kIgnoreCoverage_OptFlag) { | |
326 fPrimaryOutputType = kNone_PrimaryOutputType; | |
327 return; | |
328 } else { | |
329 fPrimaryOutputType = kCoverage_PrimaryOutputType; | |
330 return; | |
331 } | |
332 } else if (optFlags & kIgnoreCoverage_OptFlag) { | |
333 fPrimaryOutputType = kColor_PrimaryOutputType; | |
334 return; | |
335 } | |
336 | |
337 // If we do have coverage determine whether it matters. Dual source blendin g is expensive so | |
338 // we don't do it if we are doing coverage drawing. If we aren't then We al ways do dual source | |
339 // blending if we have any effective coverage stages OR the geometry process or doesn't emits | |
340 // solid coverage. | |
341 if (!(optFlags & kSetCoverageDrawing_OptFlag) && !hasSolidCoverage) { | |
342 if (caps.shaderCaps()->dualSourceBlendingSupport()) { | |
343 if (kZero_GrBlendCoeff == fDstBlend) { | |
344 // write the coverage value to second color | |
345 fSecondaryOutputType = kCoverage_SecondaryOutputType; | |
346 fDstBlend = kIS2C_GrBlendCoeff; | |
347 } else if (kSA_GrBlendCoeff == fDstBlend) { | |
348 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. | |
349 fSecondaryOutputType = kCoverageISA_SecondaryOutputType; | |
350 fDstBlend = kIS2C_GrBlendCoeff; | |
351 } else if (kSC_GrBlendCoeff == fDstBlend) { | |
352 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. | |
353 fSecondaryOutputType = kCoverageISC_SecondaryOutputType; | |
354 fDstBlend = kIS2C_GrBlendCoeff; | |
355 } | |
356 } | |
357 } | |
358 } | |
359 | |
360 GrXferProcessor::OptFlags | |
361 PorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI, | |
362 const GrProcOptInfo& coverageP OI, | |
363 bool doesStencilWrite) { | |
364 if (this->willReadDstColor()) { | 546 if (this->willReadDstColor()) { |
365 return GrXferProcessor::kNone_Opt; | 547 return GrXferProcessor::kNone_Opt; |
366 } | 548 } |
367 | 549 |
368 bool srcAIsOne = colorPOI.isOpaque(); | 550 fBlendFormula = BlendFormula::Get(fXfermode, colorPOI, coveragePOI); |
369 bool hasCoverage = !coveragePOI.isSolidWhite(); | |
370 | 551 |
371 bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend || | 552 GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_Opt; |
372 (kSA_GrBlendCoeff == fDstBlend && srcAIsOne); | 553 if (!fBlendFormula.modifiesDst()) { |
373 bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstBlend || | 554 if (!doesStencilWrite) { |
374 (kISA_GrBlendCoeff == fDstBlend && srcAIsOne); | 555 optFlags |= GrXferProcessor::kSkipDraw_OptFlag; |
375 | 556 } |
376 // When coeffs are (0,1) there is no reason to draw at all, unless | 557 optFlags |= (GrXferProcessor::kIgnoreColor_OptFlag | |
377 // stenciling is enabled. Having color writes disabled is effectively | 558 GrXferProcessor::kIgnoreCoverage_OptFlag | |
378 // (0,1). | 559 GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag); |
379 if ((kZero_GrBlendCoeff == fSrcBlend && dstCoeffIsOne)) { | 560 } else { |
380 if (doesStencilWrite) { | 561 if (!fBlendFormula.usesInputColor()) { |
381 return GrXferProcessor::kIgnoreColor_OptFlag | | 562 optFlags |= GrXferProcessor::kIgnoreColor_OptFlag; |
382 GrXferProcessor::kSetCoverageDrawing_OptFlag; | 563 } |
383 } else { | 564 if (coveragePOI.isSolidWhite()) { |
384 fDstBlend = kOne_GrBlendCoeff; | 565 optFlags |= GrXferProcessor::kIgnoreCoverage_OptFlag; |
385 return GrXferProcessor::kSkipDraw_OptFlag; | 566 } |
567 if (colorPOI.allStagesMultiplyInput() && fBlendFormula.canTweakAlphaForC overage()) { | |
568 optFlags |= GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; | |
386 } | 569 } |
387 } | 570 } |
388 | 571 |
389 // if we don't have coverage we can check whether the dst | 572 return optFlags; |
390 // has to read at all. If not, we'll disable blending. | |
391 if (!hasCoverage) { | |
392 if (dstCoeffIsZero) { | |
393 if (kOne_GrBlendCoeff == fSrcBlend) { | |
394 // if there is no coverage and coeffs are (1,0) then we | |
395 // won't need to read the dst at all, it gets replaced by src | |
396 fDstBlend = kZero_GrBlendCoeff; | |
397 return GrXferProcessor::kNone_Opt | | |
398 GrXferProcessor::kIgnoreCoverage_OptFlag; | |
399 } else if (kZero_GrBlendCoeff == fSrcBlend) { | |
400 // if the op is "clear" then we don't need to emit a color | |
401 // or blend, just write transparent black into the dst. | |
402 fSrcBlend = kOne_GrBlendCoeff; | |
403 fDstBlend = kZero_GrBlendCoeff; | |
404 return GrXferProcessor::kIgnoreColor_OptFlag | | |
405 GrXferProcessor::kIgnoreCoverage_OptFlag; | |
406 } | |
407 } | |
408 return GrXferProcessor::kIgnoreCoverage_OptFlag; | |
409 } | |
410 | |
411 // check whether coverage can be safely rolled into alpha | |
412 // of if we can skip color computation and just emit coverage | |
413 if (can_tweak_alpha_for_coverage(fDstBlend)) { | |
414 if (colorPOI.allStagesMultiplyInput()) { | |
415 return GrXferProcessor::kSetCoverageDrawing_OptFlag | | |
416 GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; | |
417 } else { | |
418 return GrXferProcessor::kSetCoverageDrawing_OptFlag; | |
419 | |
420 } | |
421 } | |
422 if (dstCoeffIsZero) { | |
423 if (kZero_GrBlendCoeff == fSrcBlend) { | |
424 // the source color is not included in the blend | |
425 // the dst coeff is effectively zero so blend works out to: | |
426 // (c)(0)D + (1-c)D = (1-c)D. | |
427 fDstBlend = kISA_GrBlendCoeff; | |
428 return GrXferProcessor::kIgnoreColor_OptFlag | | |
429 GrXferProcessor::kSetCoverageDrawing_OptFlag; | |
430 } else if (srcAIsOne) { | |
431 // the dst coeff is effectively zero so blend works out to: | |
432 // cS + (c)(0)D + (1-c)D = cS + (1-c)D. | |
433 // If Sa is 1 then we can replace Sa with c | |
434 // and set dst coeff to 1-Sa. | |
435 fDstBlend = kISA_GrBlendCoeff; | |
436 if (colorPOI.allStagesMultiplyInput()) { | |
437 return GrXferProcessor::kSetCoverageDrawing_OptFlag | | |
438 GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; | |
439 } else { | |
440 return GrXferProcessor::kSetCoverageDrawing_OptFlag; | |
441 | |
442 } | |
443 } | |
444 } else if (dstCoeffIsOne) { | |
445 // the dst coeff is effectively one so blend works out to: | |
446 // cS + (c)(1)D + (1-c)D = cS + D. | |
447 fDstBlend = kOne_GrBlendCoeff; | |
448 if (colorPOI.allStagesMultiplyInput()) { | |
449 return GrXferProcessor::kSetCoverageDrawing_OptFlag | | |
450 GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; | |
451 } else { | |
452 return GrXferProcessor::kSetCoverageDrawing_OptFlag; | |
453 | |
454 } | |
455 return GrXferProcessor::kSetCoverageDrawing_OptFlag; | |
456 } | |
457 | |
458 return GrXferProcessor::kNone_Opt; | |
459 } | |
460 | |
461 bool PorterDuffXferProcessor::hasSecondaryOutput() const { | |
462 return kNone_SecondaryOutputType != fSecondaryOutputType; | |
463 } | 573 } |
464 | 574 |
465 /////////////////////////////////////////////////////////////////////////////// | 575 /////////////////////////////////////////////////////////////////////////////// |
466 | 576 |
467 class PDLCDXferProcessor : public GrXferProcessor { | 577 class PDLCDXferProcessor : public GrXferProcessor { |
468 public: | 578 public: |
469 static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, | 579 static GrXferProcessor* Create(SkXfermode::Mode xfermode, const GrProcOptInf o& colorPOI); |
470 const GrProcOptInfo& colorPOI); | |
471 | 580 |
472 ~PDLCDXferProcessor() override; | 581 ~PDLCDXferProcessor() override; |
473 | 582 |
474 const char* name() const override { return "Porter Duff LCD"; } | 583 const char* name() const override { return "Porter Duff LCD"; } |
475 | 584 |
476 GrGLXferProcessor* createGLInstance() const override; | 585 GrGLXferProcessor* createGLInstance() const override; |
477 | 586 |
478 bool hasSecondaryOutput() const override { return false; } | 587 bool hasSecondaryOutput() const override { return false; } |
479 | 588 |
480 private: | 589 private: |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
534 }; | 643 }; |
535 | 644 |
536 /////////////////////////////////////////////////////////////////////////////// | 645 /////////////////////////////////////////////////////////////////////////////// |
537 | 646 |
538 PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha) | 647 PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha) |
539 : fBlendConstant(blendConstant) | 648 : fBlendConstant(blendConstant) |
540 , fAlpha(alpha) { | 649 , fAlpha(alpha) { |
541 this->initClassID<PDLCDXferProcessor>(); | 650 this->initClassID<PDLCDXferProcessor>(); |
542 } | 651 } |
543 | 652 |
544 GrXferProcessor* PDLCDXferProcessor::Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, | 653 GrXferProcessor* PDLCDXferProcessor::Create(SkXfermode::Mode xfermode, |
545 const GrProcOptInfo& colorPOI) { | 654 const GrProcOptInfo& colorPOI) { |
546 if (kOne_GrBlendCoeff != srcBlend || kISA_GrBlendCoeff != dstBlend) { | 655 if (SkXfermode::kSrcOver_Mode != xfermode) { |
547 return NULL; | 656 return NULL; |
548 } | 657 } |
549 | 658 |
550 if (kRGBA_GrColorComponentFlags != colorPOI.validFlags()) { | 659 if (kRGBA_GrColorComponentFlags != colorPOI.validFlags()) { |
551 return NULL; | 660 return NULL; |
552 } | 661 } |
553 | 662 |
554 GrColor blendConstant = GrUnPreMulColor(colorPOI.color()); | 663 GrColor blendConstant = GrUnPreMulColor(colorPOI.color()); |
555 uint8_t alpha = GrColorUnpackA(blendConstant); | 664 uint8_t alpha = GrColorUnpackA(blendConstant); |
556 blendConstant |= (0xff << GrColor_SHIFT_A); | 665 blendConstant |= (0xff << GrColor_SHIFT_A); |
(...skipping 21 matching lines...) Expand all Loading... | |
578 const GrCaps& caps) { | 687 const GrCaps& caps) { |
579 // We want to force our primary output to be alpha * Coverage, where alp ha is the alpha | 688 // We want to force our primary output to be alpha * Coverage, where alp ha is the alpha |
580 // value of the blend the constant. We should already have valid blend c oeff's if we are at | 689 // value of the blend the constant. We should already have valid blend c oeff's if we are at |
581 // a point where we have RGB coverage. We don't need any color stages si nce the known color | 690 // a point where we have RGB coverage. We don't need any color stages si nce the known color |
582 // output is already baked into the blendConstant. | 691 // output is already baked into the blendConstant. |
583 *overrideColor = GrColorPackRGBA(fAlpha, fAlpha, fAlpha, fAlpha); | 692 *overrideColor = GrColorPackRGBA(fAlpha, fAlpha, fAlpha, fAlpha); |
584 return GrXferProcessor::kOverrideColor_OptFlag; | 693 return GrXferProcessor::kOverrideColor_OptFlag; |
585 } | 694 } |
586 | 695 |
587 /////////////////////////////////////////////////////////////////////////////// | 696 /////////////////////////////////////////////////////////////////////////////// |
588 GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst) | 697 |
589 : fSrcCoeff(src), fDstCoeff(dst) { | 698 GrPorterDuffXPFactory::GrPorterDuffXPFactory(SkXfermode::Mode xfermode) |
699 : fXfermode(xfermode) { | |
590 this->initClassID<GrPorterDuffXPFactory>(); | 700 this->initClassID<GrPorterDuffXPFactory>(); |
591 } | 701 } |
592 | 702 |
593 GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) { | 703 GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode xfermode) { |
594 switch (mode) { | 704 static GrPorterDuffXPFactory gClearPDXPF(SkXfermode::kClear_Mode); |
595 case SkXfermode::kClear_Mode: { | 705 static GrPorterDuffXPFactory gSrcPDXPF(SkXfermode::kSrc_Mode); |
596 static GrPorterDuffXPFactory gClearPDXPF(kZero_GrBlendCoeff, kZero_G rBlendCoeff); | 706 static GrPorterDuffXPFactory gDstPDXPF(SkXfermode::kDst_Mode); |
597 return SkRef(&gClearPDXPF); | 707 static GrPorterDuffXPFactory gSrcOverPDXPF(SkXfermode::kSrcOver_Mode); |
598 break; | 708 static GrPorterDuffXPFactory gDstOverPDXPF(SkXfermode::kDstOver_Mode); |
599 } | 709 static GrPorterDuffXPFactory gSrcInPDXPF(SkXfermode::kSrcIn_Mode); |
600 case SkXfermode::kSrc_Mode: { | 710 static GrPorterDuffXPFactory gDstInPDXPF(SkXfermode::kDstIn_Mode); |
601 static GrPorterDuffXPFactory gSrcPDXPF(kOne_GrBlendCoeff, kZero_GrBl endCoeff); | 711 static GrPorterDuffXPFactory gSrcOutPDXPF(SkXfermode::kSrcOut_Mode); |
602 return SkRef(&gSrcPDXPF); | 712 static GrPorterDuffXPFactory gDstOutPDXPF(SkXfermode::kDstOut_Mode); |
603 break; | 713 static GrPorterDuffXPFactory gSrcATopPDXPF(SkXfermode::kSrcATop_Mode); |
604 } | 714 static GrPorterDuffXPFactory gDstATopPDXPF(SkXfermode::kDstATop_Mode); |
605 case SkXfermode::kDst_Mode: { | 715 static GrPorterDuffXPFactory gXorPDXPF(SkXfermode::kXor_Mode); |
606 static GrPorterDuffXPFactory gDstPDXPF(kZero_GrBlendCoeff, kOne_GrBl endCoeff); | 716 static GrPorterDuffXPFactory gPlusPDXPF(SkXfermode::kPlus_Mode); |
607 return SkRef(&gDstPDXPF); | 717 static GrPorterDuffXPFactory gModulatePDXPF(SkXfermode::kModulate_Mode); |
608 break; | 718 static GrPorterDuffXPFactory gScreenPDXPF(SkXfermode::kScreen_Mode); |
609 } | 719 |
610 case SkXfermode::kSrcOver_Mode: { | 720 static GrPorterDuffXPFactory* gFactories[] = { |
611 static GrPorterDuffXPFactory gSrcOverPDXPF(kOne_GrBlendCoeff, kISA_G rBlendCoeff); | 721 &gClearPDXPF, &gSrcPDXPF, &gDstPDXPF, &gSrcOverPDXPF, &gDstOverPDXPF, &g SrcInPDXPF, |
612 return SkRef(&gSrcOverPDXPF); | 722 &gDstInPDXPF, &gSrcOutPDXPF, &gDstOutPDXPF, &gSrcATopPDXPF, &gDstATopPDX PF, &gXorPDXPF, |
613 break; | 723 &gPlusPDXPF, &gModulatePDXPF, &gScreenPDXPF |
614 } | 724 }; |
615 case SkXfermode::kDstOver_Mode: { | 725 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFactories) == SkXfermode::kLastCoeffMode + 1); |
616 static GrPorterDuffXPFactory gDstOverPDXPF(kIDA_GrBlendCoeff, kOne_G rBlendCoeff); | 726 |
617 return SkRef(&gDstOverPDXPF); | 727 if (xfermode < 0 || xfermode > SkXfermode::kLastCoeffMode) { |
618 break; | 728 return NULL; |
619 } | |
620 case SkXfermode::kSrcIn_Mode: { | |
621 static GrPorterDuffXPFactory gSrcInPDXPF(kDA_GrBlendCoeff, kZero_GrB lendCoeff); | |
622 return SkRef(&gSrcInPDXPF); | |
623 break; | |
624 } | |
625 case SkXfermode::kDstIn_Mode: { | |
626 static GrPorterDuffXPFactory gDstInPDXPF(kZero_GrBlendCoeff, kSA_GrB lendCoeff); | |
627 return SkRef(&gDstInPDXPF); | |
628 break; | |
629 } | |
630 case SkXfermode::kSrcOut_Mode: { | |
631 static GrPorterDuffXPFactory gSrcOutPDXPF(kIDA_GrBlendCoeff, kZero_G rBlendCoeff); | |
632 return SkRef(&gSrcOutPDXPF); | |
633 break; | |
634 } | |
635 case SkXfermode::kDstOut_Mode: { | |
636 static GrPorterDuffXPFactory gDstOutPDXPF(kZero_GrBlendCoeff, kISA_G rBlendCoeff); | |
637 return SkRef(&gDstOutPDXPF); | |
638 break; | |
639 } | |
640 case SkXfermode::kSrcATop_Mode: { | |
641 static GrPorterDuffXPFactory gSrcATopPDXPF(kDA_GrBlendCoeff, kISA_Gr BlendCoeff); | |
642 return SkRef(&gSrcATopPDXPF); | |
643 break; | |
644 } | |
645 case SkXfermode::kDstATop_Mode: { | |
646 static GrPorterDuffXPFactory gDstATopPDXPF(kIDA_GrBlendCoeff, kSA_Gr BlendCoeff); | |
647 return SkRef(&gDstATopPDXPF); | |
648 break; | |
649 } | |
650 case SkXfermode::kXor_Mode: { | |
651 static GrPorterDuffXPFactory gXorPDXPF(kIDA_GrBlendCoeff, kISA_GrBle ndCoeff); | |
652 return SkRef(&gXorPDXPF); | |
653 break; | |
654 } | |
655 case SkXfermode::kPlus_Mode: { | |
656 static GrPorterDuffXPFactory gPlusPDXPF(kOne_GrBlendCoeff, kOne_GrBl endCoeff); | |
657 return SkRef(&gPlusPDXPF); | |
658 break; | |
659 } | |
660 case SkXfermode::kModulate_Mode: { | |
661 static GrPorterDuffXPFactory gModulatePDXPF(kZero_GrBlendCoeff, kSC_ GrBlendCoeff); | |
662 return SkRef(&gModulatePDXPF); | |
663 break; | |
664 } | |
665 case SkXfermode::kScreen_Mode: { | |
666 static GrPorterDuffXPFactory gScreenPDXPF(kOne_GrBlendCoeff, kISC_Gr BlendCoeff); | |
667 return SkRef(&gScreenPDXPF); | |
668 break; | |
669 } | |
670 default: | |
671 return NULL; | |
672 } | 729 } |
730 return SkRef(gFactories[xfermode]); | |
673 } | 731 } |
674 | 732 |
675 GrXferProcessor* | 733 GrXferProcessor* |
676 GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps, | 734 GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps, |
677 const GrProcOptInfo& colorPOI, | 735 const GrProcOptInfo& colorPOI, |
678 const GrProcOptInfo& covPOI, | 736 const GrProcOptInfo& covPOI, |
679 const GrDeviceCoordTexture* dstCopy ) const { | 737 const GrDeviceCoordTexture* dstCopy ) const { |
680 if (covPOI.isFourChannelOutput()) { | 738 if (covPOI.isFourChannelOutput()) { |
681 return PDLCDXferProcessor::Create(fSrcCoeff, fDstCoeff, colorPOI); | 739 return PDLCDXferProcessor::Create(fXfermode, colorPOI); |
682 } else { | 740 } else { |
683 return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstCopy, | 741 return PorterDuffXferProcessor::Create(fXfermode, dstCopy, |
684 this->willReadDstColor(caps, colo rPOI, covPOI)); | 742 this->willReadDstColor(caps, colo rPOI, covPOI)); |
685 } | 743 } |
686 } | 744 } |
687 | 745 |
688 bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, | 746 bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, |
689 uint32_t knownColorFlags) const { | 747 uint32_t knownColorFlags) const { |
690 if (kOne_GrBlendCoeff == fSrcCoeff && kISA_GrBlendCoeff == fDstCoeff && | 748 if (SkXfermode::kSrcOver_Mode == fXfermode && |
691 kRGBA_GrColorComponentFlags == knownColorFlags) { | 749 kRGBA_GrColorComponentFlags == knownColorFlags) { |
692 return true; | 750 return true; |
693 } | 751 } |
694 return false; | 752 return false; |
695 } | 753 } |
696 | 754 |
697 void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, | 755 void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, |
698 const GrProcOptInfo& coveragePOI, | 756 const GrProcOptInfo& coveragePOI, |
699 GrXPFactory::InvariantOutput* out put) const { | 757 GrXPFactory::InvariantOutput* out put) const { |
700 if (!coveragePOI.isSolidWhite()) { | 758 const BlendFormula& blendFormula = BlendFormula::Get(fXfermode, colorPOI, co veragePOI); |
759 | |
760 if (blendFormula.usesDstColor()) { | |
701 output->fWillBlendWithDst = true; | 761 output->fWillBlendWithDst = true; |
702 output->fBlendedColorFlags = 0; | 762 output->fBlendedColorFlags = kNone_GrColorComponentFlags; |
703 return; | 763 return; |
704 } | 764 } |
705 | 765 |
706 GrBlendCoeff srcCoeff = fSrcCoeff; | 766 SkASSERT(coveragePOI.isSolidWhite()); |
707 GrBlendCoeff dstCoeff = fDstCoeff; | 767 SkASSERT(kAdd_GrBlendEquation == blendFormula.fBlendEquation); |
708 | 768 |
709 // TODO: figure out to merge this simplify with other current optimization c ode paths and | 769 output->fWillBlendWithDst = false; |
710 // eventually remove from GrBlend | |
711 GrSimplifyBlend(&srcCoeff, &dstCoeff, colorPOI.color(), colorPOI.validFlags( ), | |
712 0, 0, 0); | |
713 | 770 |
714 if (GrBlendCoeffRefsDst(srcCoeff)) { | 771 switch (blendFormula.fSrcCoeff) { |
715 output->fWillBlendWithDst = true; | |
716 output->fBlendedColorFlags = 0; | |
717 return; | |
718 } | |
719 | |
720 if (kZero_GrBlendCoeff != dstCoeff) { | |
721 bool srcAIsOne = colorPOI.isOpaque(); | |
722 if (kISA_GrBlendCoeff != dstCoeff || !srcAIsOne) { | |
723 output->fWillBlendWithDst = true; | |
724 } | |
725 output->fBlendedColorFlags = 0; | |
726 return; | |
727 } | |
728 | |
729 switch (srcCoeff) { | |
730 case kZero_GrBlendCoeff: | 772 case kZero_GrBlendCoeff: |
731 output->fBlendedColor = 0; | 773 output->fBlendedColor = 0; |
732 output->fBlendedColorFlags = kRGBA_GrColorComponentFlags; | 774 output->fBlendedColorFlags = kRGBA_GrColorComponentFlags; |
733 break; | 775 return; |
734 | 776 |
735 case kOne_GrBlendCoeff: | 777 case kOne_GrBlendCoeff: |
736 output->fBlendedColor = colorPOI.color(); | 778 output->fBlendedColor = colorPOI.color(); |
737 output->fBlendedColorFlags = colorPOI.validFlags(); | 779 output->fBlendedColorFlags = colorPOI.validFlags(); |
738 break; | 780 return; |
739 | 781 |
740 // The src coeff should never refer to the src and if it refers to d st then opaque | 782 // TODO: update if we ever use const color. |
741 // should have been false. | |
742 case kSC_GrBlendCoeff: | |
743 case kISC_GrBlendCoeff: | |
744 case kDC_GrBlendCoeff: | |
745 case kIDC_GrBlendCoeff: | |
746 case kSA_GrBlendCoeff: | |
747 case kISA_GrBlendCoeff: | |
748 case kDA_GrBlendCoeff: | |
749 case kIDA_GrBlendCoeff: | |
750 default: | 783 default: |
751 SkFAIL("srcCoeff should not refer to src or dst."); | 784 output->fBlendedColorFlags = kNone_GrColorComponentFlags; |
752 break; | 785 return; |
753 | |
754 // TODO: update this once GrPaint actually has a const color. | |
755 case kConstC_GrBlendCoeff: | |
756 case kIConstC_GrBlendCoeff: | |
757 case kConstA_GrBlendCoeff: | |
758 case kIConstA_GrBlendCoeff: | |
759 output->fBlendedColorFlags = 0; | |
760 break; | |
761 } | 786 } |
762 | |
763 output->fWillBlendWithDst = false; | |
764 } | 787 } |
765 | 788 |
766 bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, | 789 bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, |
767 const GrProcOptInfo& colorPOI, | 790 const GrProcOptInfo& colorPOI, |
768 const GrProcOptInfo& coveragePOI) c onst { | 791 const GrProcOptInfo& coveragePOI) c onst { |
769 // We can always blend correctly if we have dual source blending. | 792 // Some formulas use dual source blending, so we fall back if it is required but not supported. |
770 if (caps.shaderCaps()->dualSourceBlendingSupport()) { | 793 return !caps.shaderCaps()->dualSourceBlendingSupport() && |
771 return false; | 794 BlendFormula::Get(fXfermode, colorPOI, coveragePOI).hasSecondaryOutpu t(); |
772 } | |
773 | |
774 if (can_tweak_alpha_for_coverage(fDstCoeff)) { | |
775 return false; | |
776 } | |
777 | |
778 bool srcAIsOne = colorPOI.isOpaque(); | |
779 | |
780 if (kZero_GrBlendCoeff == fDstCoeff) { | |
781 if (kZero_GrBlendCoeff == fSrcCoeff || srcAIsOne) { | |
782 return false; | |
783 } | |
784 } | |
785 | |
786 // Reduces to: coeffS * (Cov*S) + D | |
787 if (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne) { | |
788 return false; | |
789 } | |
790 | |
791 // We can always blend correctly if we have solid coverage. | |
792 if (coveragePOI.isSolidWhite()) { | |
793 return false; | |
794 } | |
795 | |
796 return true; | |
797 } | 795 } |
798 | 796 |
799 GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); | 797 GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); |
800 | 798 |
801 GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random, | 799 GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random, |
802 GrContext*, | 800 GrContext*, |
803 const GrCaps&, | 801 const GrCaps&, |
804 GrTexture*[]) { | 802 GrTexture*[]) { |
805 SkXfermode::Mode mode = SkXfermode::Mode(random->nextULessThan(SkXfermode::k LastCoeffMode)); | 803 SkXfermode::Mode mode = SkXfermode::Mode(random->nextULessThan(SkXfermode::k LastCoeffMode)); |
806 return GrPorterDuffXPFactory::Create(mode); | 804 return GrPorterDuffXPFactory::Create(mode); |
807 } | 805 } |
808 | 806 |
807 void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp, | |
808 int* outPrimary, | |
809 int* outSecondary) { | |
810 if (!!strcmp(xp->name(), "Porter Duff")) { | |
811 *outPrimary = *outSecondary = -1; | |
812 return; | |
813 } | |
814 BlendFormula blendFormula = static_cast<const PorterDuffXferProcessor*>(xp)- >getBlendFormula(); | |
815 *outPrimary = blendFormula.fPrimaryOutputType; | |
816 *outSecondary = blendFormula.fSecondaryOutputType; | |
817 } | |
818 | |
OLD | NEW |