2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "effects/GrCustomXfermode.h"
9 #include "effects/GrCustomXfermodePriv.h"
11 #include "GrCoordTransform.h"
12 #include "GrContext.h"
13 #include "GrFragmentProcessor.h"
14 #include "GrInvariantOutput.h"
15 #include "GrProcessor.h"
16 #include "GrTexture.h"
17 #include "GrTextureAccess.h"
18 #include "SkXfermode.h"
19 #include "gl/GrGLCaps.h"
20 #include "gl/GrGLGpu.h"
21 #include "gl/GrGLProcessor.h"
22 #include "gl/GrGLProgramDataManager.h"
23 #include "gl/builders/GrGLProgramBuilder.h"
25 bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) {
26 return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
29 ///////////////////////////////////////////////////////////////////////////////
31 ///////////////////////////////////////////////////////////////////////////////
33 static GrBlendEquation hw_blend_equation(SkXfermode::Mode mode) {
34 enum { kOffset = kOverlay_GrBlendEquation - SkXfermode::kOverlay_Mode };
35 return static_cast<GrBlendEquation>(mode + kOffset);
37 GR_STATIC_ASSERT(kOverlay_GrBlendEquation == SkXfermode::kOverlay_Mode + kOffset);
38 GR_STATIC_ASSERT(kDarken_GrBlendEquation == SkXfermode::kDarken_Mode + kOffset);
39 GR_STATIC_ASSERT(kLighten_GrBlendEquation == SkXfermode::kLighten_Mode + kOffset);
40 GR_STATIC_ASSERT(kColorDodge_GrBlendEquation == SkXfermode::kColorDodge_Mode + kOffset);
41 GR_STATIC_ASSERT(kColorBurn_GrBlendEquation == SkXfermode::kColorBurn_Mode + kOffset);
42 GR_STATIC_ASSERT(kHardLight_GrBlendEquation == SkXfermode::kHardLight_Mode + kOffset);
43 GR_STATIC_ASSERT(kSoftLight_GrBlendEquation == SkXfermode::kSoftLight_Mode + kOffset);
44 GR_STATIC_ASSERT(kDifference_GrBlendEquation == SkXfermode::kDifference_Mode + kOffset);
45 GR_STATIC_ASSERT(kExclusion_GrBlendEquation == SkXfermode::kExclusion_Mode + kOffset);
46 GR_STATIC_ASSERT(kMultiply_GrBlendEquation == SkXfermode::kMultiply_Mode + kOffset);
47 GR_STATIC_ASSERT(kHSLHue_GrBlendEquation == SkXfermode::kHue_Mode + kOffset);
48 GR_STATIC_ASSERT(kHSLSaturation_GrBlendEquation == SkXfermode::kSaturation_Mode + kOffset);
49 GR_STATIC_ASSERT(kHSLColor_GrBlendEquation == SkXfermode::kColor_Mode + kOffset);
50 GR_STATIC_ASSERT(kHSLLuminosity_GrBlendEquation == SkXfermode::kLuminosity_Mode + kOffset);
51 GR_STATIC_ASSERT(kGrBlendEquationCnt == SkXfermode::kLastMode + 1 + kOffset);
54 static bool can_use_hw_blend_equation(const GrProcOptInfo& coveragePOI, const GrCaps& caps) {
55 if (!caps.advancedBlendEquationSupport()) {
58 if (coveragePOI.isFourChannelOutput()) {
59 return false; // LCD coverage must be applied after the blend equation.
64 static void hard_light(GrGLFragmentBuilder* fsBuilder,
68 static const char kComponents[] = {'r', 'g', 'b'};
69 for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
70 char component = kComponents[i];
71 fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
72 fsBuilder->codeAppendf("%s.%c = 2.0 * %s.%c * %s.%c;",
73 final, component, src, component, dst, component);
74 fsBuilder->codeAppend("} else {");
75 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);",
76 final, component, src, dst, dst, dst, component, src, src,
78 fsBuilder->codeAppend("}");
80 fsBuilder->codeAppendf("%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);",
81 final, src, dst, dst, src);
84 // Does one component of color-dodge
85 static void color_dodge_component(GrGLFragmentBuilder* fsBuilder,
89 const char component) {
90 fsBuilder->codeAppendf("if (0.0 == %s.%c) {", dst, component);
91 fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
92 final, component, src, component, dst);
93 fsBuilder->codeAppend("} else {");
94 fsBuilder->codeAppendf("float d = %s.a - %s.%c;", src, src, component);
95 fsBuilder->codeAppend("if (0.0 == d) {");
96 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
97 final, component, src, dst, src, component, dst, dst, component,
99 fsBuilder->codeAppend("} else {");
100 fsBuilder->codeAppendf("d = min(%s.a, %s.%c * %s.a / d);",
101 dst, dst, component, src);
102 fsBuilder->codeAppendf("%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
103 final, component, src, src, component, dst, dst, component, src);
104 fsBuilder->codeAppend("}");
105 fsBuilder->codeAppend("}");
108 // Does one component of color-burn
109 static void color_burn_component(GrGLFragmentBuilder* fsBuilder,
113 const char component) {
114 fsBuilder->codeAppendf("if (%s.a == %s.%c) {", dst, dst, component);
115 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
116 final, component, src, dst, src, component, dst, dst, component,
118 fsBuilder->codeAppendf("} else if (0.0 == %s.%c) {", src, component);
119 fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
120 final, component, dst, component, src);
121 fsBuilder->codeAppend("} else {");
122 fsBuilder->codeAppendf("float d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);",
123 dst, dst, dst, component, src, src, component);
124 fsBuilder->codeAppendf("%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
125 final, component, src, src, component, dst, dst, component, src);
126 fsBuilder->codeAppend("}");
129 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
130 static void soft_light_component_pos_dst_alpha(GrGLFragmentBuilder* fsBuilder,
134 const char component) {
136 fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
137 // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
138 fsBuilder->codeAppendf("%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a +"
139 "(1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s.%c + 1.0);",
140 final, component, dst, component, dst, component, src, src,
141 component, dst, dst, src, component, dst, component, src, src,
144 fsBuilder->codeAppendf("} else if (4.0 * %s.%c <= %s.a) {",
145 dst, component, dst);
146 fsBuilder->codeAppendf("float DSqd = %s.%c * %s.%c;",
147 dst, component, dst, component);
148 fsBuilder->codeAppendf("float DCub = DSqd * %s.%c;", dst, component);
149 fsBuilder->codeAppendf("float DaSqd = %s.a * %s.a;", dst, dst);
150 fsBuilder->codeAppendf("float DaCub = DaSqd * %s.a;", dst);
151 // (Da^3 (-S)+Da^2 (S-D (3 Sa-6 S-1))+12 Da D^2 (Sa-2 S)-16 D^3 (Sa-2 S))/Da^2
152 fsBuilder->codeAppendf("%s.%c ="
153 "(-DaCub*%s.%c + DaSqd*(%s.%c - %s.%c * (3.0*%s.a - 6.0*%s.%c - 1.0)) +"
154 " 12.0*%s.a*DSqd*(%s.a - 2.0*%s.%c) - 16.0*DCub * (%s.a - 2.0*%s.%c)) /"
156 final, component, src, component, src, component, dst, component,
157 src, src, component, dst, src, src, component, src, src,
159 fsBuilder->codeAppendf("} else {");
160 // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
161 fsBuilder->codeAppendf("%s.%c = -sqrt(%s.a*%s.%c)*(%s.a - 2.0*%s.%c) - %s.a*%s.%c +"
162 "%s.%c*(%s.a - 2.0*%s.%c + 1.0) + %s.%c;",
163 final, component, dst, dst, component, src, src, component, dst,
164 src, component, dst, component, src, src, component, src,
166 fsBuilder->codeAppendf("}");
169 // Adds a function that takes two colors and an alpha as input. It produces a color with the
170 // hue and saturation of the first color, the luminosity of the second color, and the input
171 // alpha. It has this signature:
172 // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
173 static void add_lum_function(GrGLFragmentBuilder* fsBuilder, SkString* setLumFunction) {
174 // Emit a helper that gets the luminance of a color.
175 SkString getFunction;
176 GrGLShaderVar getLumArgs[] = {
177 GrGLShaderVar("color", kVec3f_GrSLType),
179 SkString getLumBody("return dot(vec3(0.3, 0.59, 0.11), color);");
180 fsBuilder->emitFunction(kFloat_GrSLType,
182 SK_ARRAY_COUNT(getLumArgs), getLumArgs,
186 // Emit the set luminance function.
187 GrGLShaderVar setLumArgs[] = {
188 GrGLShaderVar("hueSat", kVec3f_GrSLType),
189 GrGLShaderVar("alpha", kFloat_GrSLType),
190 GrGLShaderVar("lumColor", kVec3f_GrSLType),
193 setLumBody.printf("float diff = %s(lumColor - hueSat);", getFunction.c_str());
194 setLumBody.append("vec3 outColor = hueSat + diff;");
195 setLumBody.appendf("float outLum = %s(outColor);", getFunction.c_str());
196 setLumBody.append("float minComp = min(min(outColor.r, outColor.g), outColor.b);"
197 "float maxComp = max(max(outColor.r, outColor.g), outColor.b);"
198 "if (minComp < 0.0 && outLum != minComp) {"
199 "outColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) /"
200 "(outLum - minComp);"
202 "if (maxComp > alpha && maxComp != outLum) {"
203 "outColor = outLum +"
204 "((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) /"
205 "(maxComp - outLum);"
208 fsBuilder->emitFunction(kVec3f_GrSLType,
210 SK_ARRAY_COUNT(setLumArgs), setLumArgs,
215 // Adds a function that creates a color with the hue and luminosity of one input color and
216 // the saturation of another color. It will have this signature:
217 // float set_saturation(vec3 hueLumColor, vec3 satColor)
218 static void add_sat_function(GrGLFragmentBuilder* fsBuilder, SkString* setSatFunction) {
219 // Emit a helper that gets the saturation of a color
220 SkString getFunction;
221 GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
223 getSatBody.printf("return max(max(color.r, color.g), color.b) - "
224 "min(min(color.r, color.g), color.b);");
225 fsBuilder->emitFunction(kFloat_GrSLType,
227 SK_ARRAY_COUNT(getSatArgs), getSatArgs,
231 // Emit a helper that sets the saturation given sorted input channels. This used
232 // to use inout params for min, mid, and max components but that seems to cause
233 // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
234 // adjusted min, mid, and max inputs, respectively.
235 SkString helperFunction;
236 GrGLShaderVar helperArgs[] = {
237 GrGLShaderVar("minComp", kFloat_GrSLType),
238 GrGLShaderVar("midComp", kFloat_GrSLType),
239 GrGLShaderVar("maxComp", kFloat_GrSLType),
240 GrGLShaderVar("sat", kFloat_GrSLType),
242 static const char kHelperBody[] = "if (minComp < maxComp) {"
245 "result.g = sat * (midComp - minComp) / (maxComp - minComp);"
249 "return vec3(0, 0, 0);"
251 fsBuilder->emitFunction(kVec3f_GrSLType,
252 "set_saturation_helper",
253 SK_ARRAY_COUNT(helperArgs), helperArgs,
257 GrGLShaderVar setSatArgs[] = {
258 GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
259 GrGLShaderVar("satColor", kVec3f_GrSLType),
261 const char* helpFunc = helperFunction.c_str();
263 setSatBody.appendf("float sat = %s(satColor);"
264 "if (hueLumColor.r <= hueLumColor.g) {"
265 "if (hueLumColor.g <= hueLumColor.b) {"
266 "hueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);"
267 "} else if (hueLumColor.r <= hueLumColor.b) {"
268 "hueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);"
270 "hueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);"
272 "} else if (hueLumColor.r <= hueLumColor.b) {"
273 "hueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);"
274 "} else if (hueLumColor.g <= hueLumColor.b) {"
275 "hueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);"
277 "hueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);"
279 "return hueLumColor;",
280 getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
282 fsBuilder->emitFunction(kVec3f_GrSLType,
284 SK_ARRAY_COUNT(setSatArgs), setSatArgs,
290 static void emit_custom_xfermode_code(SkXfermode::Mode mode,
291 GrGLFragmentBuilder* fsBuilder,
292 const char* outputColor,
293 const char* inputColor,
294 const char* dstColor) {
295 // We don't try to optimize for this case at all
296 if (NULL == inputColor) {
297 fsBuilder->codeAppendf("const vec4 ones = vec4(1);");
300 fsBuilder->codeAppendf("// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
302 // These all perform src-over on the alpha channel.
303 fsBuilder->codeAppendf("%s.a = %s.a + (1.0 - %s.a) * %s.a;",
304 outputColor, inputColor, inputColor, dstColor);
307 case SkXfermode::kOverlay_Mode:
308 // Overlay is Hard-Light with the src and dst reversed
309 hard_light(fsBuilder, outputColor, dstColor, inputColor);
311 case SkXfermode::kDarken_Mode:
312 fsBuilder->codeAppendf("%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
313 "(1.0 - %s.a) * %s.rgb + %s.rgb);",
315 inputColor, dstColor, inputColor,
316 dstColor, inputColor, dstColor);
318 case SkXfermode::kLighten_Mode:
319 fsBuilder->codeAppendf("%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
320 "(1.0 - %s.a) * %s.rgb + %s.rgb);",
322 inputColor, dstColor, inputColor,
323 dstColor, inputColor, dstColor);
325 case SkXfermode::kColorDodge_Mode:
326 color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'r');
327 color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'g');
328 color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'b');
330 case SkXfermode::kColorBurn_Mode:
331 color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'r');
332 color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'g');
333 color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'b');
335 case SkXfermode::kHardLight_Mode:
336 hard_light(fsBuilder, outputColor, inputColor, dstColor);
338 case SkXfermode::kSoftLight_Mode:
339 fsBuilder->codeAppendf("if (0.0 == %s.a) {", dstColor);
340 fsBuilder->codeAppendf("%s.rgba = %s;", outputColor, inputColor);
341 fsBuilder->codeAppendf("} else {");
342 soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'r');
343 soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'g');
344 soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'b');
345 fsBuilder->codeAppendf("}");
347 case SkXfermode::kDifference_Mode:
348 fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb -"
349 "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);",
350 outputColor, inputColor, dstColor, inputColor, dstColor,
351 dstColor, inputColor);
353 case SkXfermode::kExclusion_Mode:
354 fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb - "
355 "2.0 * %s.rgb * %s.rgb;",
356 outputColor, dstColor, inputColor, dstColor, inputColor);
358 case SkXfermode::kMultiply_Mode:
359 fsBuilder->codeAppendf("%s.rgb = (1.0 - %s.a) * %s.rgb + "
360 "(1.0 - %s.a) * %s.rgb + "
362 outputColor, inputColor, dstColor, dstColor, inputColor,
363 inputColor, dstColor);
365 case SkXfermode::kHue_Mode: {
366 // SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
367 SkString setSat, setLum;
368 add_sat_function(fsBuilder, &setSat);
369 add_lum_function(fsBuilder, &setLum);
370 fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
371 dstColor, inputColor);
372 fsBuilder->codeAppendf("%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb),"
373 "dstSrcAlpha.a, dstSrcAlpha.rgb);",
374 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
376 fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
377 outputColor, inputColor, dstColor, dstColor, inputColor);
380 case SkXfermode::kSaturation_Mode: {
381 // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
382 SkString setSat, setLum;
383 add_sat_function(fsBuilder, &setSat);
384 add_lum_function(fsBuilder, &setLum);
385 fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
386 dstColor, inputColor);
387 fsBuilder->codeAppendf("%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a),"
388 "dstSrcAlpha.a, dstSrcAlpha.rgb);",
389 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
391 fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
392 outputColor, inputColor, dstColor, dstColor, inputColor);
395 case SkXfermode::kColor_Mode: {
396 // SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
398 add_lum_function(fsBuilder, &setLum);
399 fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
400 inputColor, dstColor);
401 fsBuilder->codeAppendf("%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);",
402 outputColor, setLum.c_str(), dstColor, inputColor);
403 fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
404 outputColor, inputColor, dstColor, dstColor, inputColor);
407 case SkXfermode::kLuminosity_Mode: {
408 // SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
410 add_lum_function(fsBuilder, &setLum);
411 fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
412 inputColor, dstColor);
413 fsBuilder->codeAppendf("%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);",
414 outputColor, setLum.c_str(), dstColor, inputColor);
415 fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
416 outputColor, inputColor, dstColor, dstColor, inputColor);
420 SkFAIL("Unknown Custom Xfer mode.");
425 ///////////////////////////////////////////////////////////////////////////////
426 // Fragment Processor
427 ///////////////////////////////////////////////////////////////////////////////
429 GrFragmentProcessor* GrCustomXfermode::CreateFP(SkXfermode::Mode mode, GrTexture* background) {
430 if (!GrCustomXfermode::IsSupportedMode(mode)) {
433 return SkNEW_ARGS(GrCustomXferFP, (mode, background));
437 ///////////////////////////////////////////////////////////////////////////////
439 class GLCustomXferFP : public GrGLFragmentProcessor {
441 GLCustomXferFP(const GrFragmentProcessor&) {}
442 ~GLCustomXferFP() override {};
444 void emitCode(GrGLFPBuilder* builder,
445 const GrFragmentProcessor& fp,
446 const char* outputColor,
447 const char* inputColor,
448 const TransformedCoordsArray& coords,
449 const TextureSamplerArray& samplers) override {
450 SkXfermode::Mode mode = fp.cast<GrCustomXferFP>().mode();
451 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
452 const char* dstColor = "bgColor";
453 fsBuilder->codeAppendf("vec4 %s = ", dstColor);
454 fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
455 fsBuilder->codeAppendf(";");
457 emit_custom_xfermode_code(mode, fsBuilder, outputColor, inputColor, dstColor);
460 void setData(const GrGLProgramDataManager&, const GrProcessor&) override {}
462 static void GenKey(const GrFragmentProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
463 // The background may come from the dst or from a texture.
464 uint32_t key = proc.numTextures();
466 key |= proc.cast<GrCustomXferFP>().mode() << 1;
471 typedef GrGLFragmentProcessor INHERITED;
474 ///////////////////////////////////////////////////////////////////////////////
476 GrCustomXferFP::GrCustomXferFP(SkXfermode::Mode mode, GrTexture* background)
478 this->initClassID<GrCustomXferFP>();
480 SkASSERT(background);
481 fBackgroundTransform.reset(kLocal_GrCoordSet, background,
482 GrTextureParams::kNone_FilterMode);
483 this->addCoordTransform(&fBackgroundTransform);
484 fBackgroundAccess.reset(background);
485 this->addTextureAccess(&fBackgroundAccess);
488 void GrCustomXferFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
489 GLCustomXferFP::GenKey(*this, caps, b);
492 GrGLFragmentProcessor* GrCustomXferFP::createGLInstance() const {
493 return SkNEW_ARGS(GLCustomXferFP, (*this));
496 bool GrCustomXferFP::onIsEqual(const GrFragmentProcessor& other) const {
497 const GrCustomXferFP& s = other.cast<GrCustomXferFP>();
498 return fMode == s.fMode;
501 void GrCustomXferFP::onComputeInvariantOutput(GrInvariantOutput* inout) const {
502 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
505 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCustomXferFP);
506 GrFragmentProcessor* GrCustomXferFP::TestCreate(SkRandom* rand,
509 GrTexture* textures[]) {
510 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
512 return SkNEW_ARGS(GrCustomXferFP, (static_cast<SkXfermode::Mode>(mode), textures[0]));
515 ///////////////////////////////////////////////////////////////////////////////
517 ///////////////////////////////////////////////////////////////////////////////
519 class CustomXP : public GrXferProcessor {
521 CustomXP(SkXfermode::Mode mode, GrBlendEquation hwBlendEquation)
523 fHWBlendEquation(hwBlendEquation) {
524 this->initClassID<CustomXP>();
527 CustomXP(SkXfermode::Mode mode, const DstTexture* dstTexture)
528 : INHERITED(dstTexture, true),
530 fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
531 this->initClassID<CustomXP>();
534 const char* name() const override { return "Custom Xfermode"; }
536 GrGLXferProcessor* createGLInstance() const override;
538 SkXfermode::Mode mode() const { return fMode; }
539 bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
541 GrBlendEquation hwBlendEquation() const {
542 SkASSERT(this->hasHWBlendEquation());
543 return fHWBlendEquation;
547 GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
548 const GrProcOptInfo& coveragePOI,
549 bool doesStencilWrite,
550 GrColor* overrideColor,
551 const GrCaps& caps) override;
553 void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
555 bool onWillNeedXferBarrier(const GrRenderTarget* rt,
557 GrXferBarrierType* outBarrierType) const override;
559 void onGetBlendInfo(BlendInfo*) const override;
561 bool onIsEqual(const GrXferProcessor& xpBase) const override;
563 const SkXfermode::Mode fMode;
564 const GrBlendEquation fHWBlendEquation;
566 typedef GrXferProcessor INHERITED;
569 ///////////////////////////////////////////////////////////////////////////////
571 GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) {
572 if (!GrCustomXfermode::IsSupportedMode(mode)) {
575 return SkNEW_ARGS(GrCustomXPFactory, (mode));
579 ///////////////////////////////////////////////////////////////////////////////
581 class GLCustomXP : public GrGLXferProcessor {
583 GLCustomXP(const GrXferProcessor&) {}
584 ~GLCustomXP() override {}
586 static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
587 const CustomXP& xp = p.cast<CustomXP>();
589 if (xp.hasHWBlendEquation()) {
590 SkASSERT(caps.advBlendEqInteraction() > 0); // 0 will mean !xp.hasHWBlendEquation().
591 key |= caps.advBlendEqInteraction();
592 key |= xp.readsCoverage() << 2;
593 GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
595 if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
596 key |= xp.mode() << 3;
602 void emitOutputsForBlendState(const EmitArgs& args) override {
603 const CustomXP& xp = args.fXP.cast<CustomXP>();
604 SkASSERT(xp.hasHWBlendEquation());
606 GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
607 fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
609 // Apply coverage by multiplying it into the src color before blending.
610 // (See onGetOptimizations())
611 if (xp.readsCoverage()) {
612 fsBuilder->codeAppendf("%s = %s * %s;",
613 args.fOutputPrimary, args.fInputCoverage, args.fInputColor);
615 fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
619 void emitBlendCodeForDstRead(GrGLXPBuilder* pb, const char* srcColor, const char* dstColor,
620 const char* outColor, const GrXferProcessor& proc) override {
621 const CustomXP& xp = proc.cast<CustomXP>();
622 SkASSERT(!xp.hasHWBlendEquation());
624 GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
625 emit_custom_xfermode_code(xp.mode(), fsBuilder, outColor, srcColor, dstColor);
628 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
630 typedef GrGLFragmentProcessor INHERITED;
633 ///////////////////////////////////////////////////////////////////////////////
635 void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
636 GLCustomXP::GenKey(*this, caps, b);
639 GrGLXferProcessor* CustomXP::createGLInstance() const {
640 SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
641 return SkNEW_ARGS(GLCustomXP, (*this));
644 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
645 const CustomXP& s = other.cast<CustomXP>();
646 return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
649 GrXferProcessor::OptFlags CustomXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
650 const GrProcOptInfo& coveragePOI,
651 bool doesStencilWrite,
652 GrColor* overrideColor,
653 const GrCaps& caps) {
655 Most the optimizations we do here are based on tweaking alpha for coverage.
657 The general SVG blend equation is defined in the spec as follows:
659 Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
660 Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
662 (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
663 and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
666 For every blend mode supported by this class, i.e. the "advanced" blend
667 modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
669 It can be shown that when X=Y=Z=1, these equations can modulate alpha for
675 We substitute Y=Z=1 and define a blend() function that calculates Dca' in
676 terms of premultiplied alpha only:
678 blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
680 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0}
682 And for coverage modulation, we use a post blend src-over model:
684 Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
686 (Where f is the fractional coverage.)
688 Next we show that canTweakAlphaForCoverage() is true by proving the
689 following relationship:
691 blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
693 General case (f,Sa,Da != 0):
695 f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
696 = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da != 0, definition of blend()]
697 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
698 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
699 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
700 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
701 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
702 = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0]
703 = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()]
705 Corner cases (Sa=0, Da=0, and f=0):
707 Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
708 = f * Dca + (1-f) * Dca [Sa=0, definition of blend()]
710 = blend(0, Dca, 0, Da) [definition of blend()]
711 = blend(f*Sca, Dca, f*Sa, Da) [Sa=0]
713 Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
714 = f * Sca + (1-f) * Dca [Da=0, definition of blend()]
716 = blend(f*Sca, 0, f*Sa, 0) [definition of blend()]
717 = blend(f*Sca, Dca, f*Sa, Da) [Da=0]
719 f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
721 = blend(0, Dca, 0, Da) [definition of blend()]
722 = blend(f*Sca, Dca, f*Sa, Da) [f=0]
726 We substitute X=Y=Z=1 and define a blend() function that calculates Da':
728 blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
729 = Sa * Da + Sa - Sa * Da + Da - Da * Sa
732 We use the same model for coverage modulation as we did with color:
734 Da'' = f * blend(Sa, Da) + (1-f) * Da
736 And show that canTweakAlphaForCoverage() is true by proving the following
739 blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
742 f * blend(Sa, Da) + (1-f) * Da
743 = f * (Sa + Da - Sa * Da) + (1-f) * Da
744 = f*Sa + f*Da - f*Sa * Da + Da - f*Da
745 = f*Sa - f*Sa * Da + Da
746 = f*Sa + Da - f*Sa * Da
750 OptFlags flags = kNone_Opt;
751 if (colorPOI.allStagesMultiplyInput()) {
752 flags |= kCanTweakAlphaForCoverage_OptFlag;
754 if (this->hasHWBlendEquation() && coveragePOI.isSolidWhite()) {
755 flags |= kIgnoreCoverage_OptFlag;
760 bool CustomXP::onWillNeedXferBarrier(const GrRenderTarget* rt,
762 GrXferBarrierType* outBarrierType) const {
763 if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
764 *outBarrierType = kBlend_GrXferBarrierType;
770 void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
771 if (this->hasHWBlendEquation()) {
772 blendInfo->fEquation = this->hwBlendEquation();
776 ///////////////////////////////////////////////////////////////////////////////
778 GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
780 fHWBlendEquation(hw_blend_equation(mode)) {
781 SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
782 this->initClassID<GrCustomXPFactory>();
786 GrCustomXPFactory::onCreateXferProcessor(const GrCaps& caps,
787 const GrProcOptInfo& colorPOI,
788 const GrProcOptInfo& coveragePOI,
789 const DstTexture* dstTexture) const {
790 if (can_use_hw_blend_equation(coveragePOI, caps)) {
791 SkASSERT(!dstTexture || !dstTexture->texture());
792 return SkNEW_ARGS(CustomXP, (fMode, fHWBlendEquation));
794 return SkNEW_ARGS(CustomXP, (fMode, dstTexture));
797 bool GrCustomXPFactory::willReadDstColor(const GrCaps& caps,
798 const GrProcOptInfo& colorPOI,
799 const GrProcOptInfo& coveragePOI) const {
800 return !can_use_hw_blend_equation(coveragePOI, caps);
803 void GrCustomXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
804 InvariantBlendedColor* blendedColor) const {
805 blendedColor->fWillBlendWithDst = true;
806 blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags;
809 GR_DEFINE_XP_FACTORY_TEST(GrCustomXPFactory);
810 GrXPFactory* GrCustomXPFactory::TestCreate(SkRandom* rand,
814 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
816 return SkNEW_ARGS(GrCustomXPFactory, (static_cast<SkXfermode::Mode>(mode)));