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/GrCustomXfermode.h"
9 #include "effects/GrCustomXfermodePriv.h"
10 
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"
24 
IsSupportedMode(SkXfermode::Mode mode)25 bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) {
26     return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
27 }
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 // Static helpers
31 ///////////////////////////////////////////////////////////////////////////////
32 
hw_blend_equation(SkXfermode::Mode mode)33 static GrBlendEquation hw_blend_equation(SkXfermode::Mode mode) {
34     enum { kOffset = kOverlay_GrBlendEquation - SkXfermode::kOverlay_Mode };
35     return static_cast<GrBlendEquation>(mode + kOffset);
36 
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);
52 }
53 
hard_light(GrGLFragmentBuilder * fsBuilder,const char * final,const char * src,const char * dst)54 static void hard_light(GrGLFragmentBuilder* fsBuilder,
55                        const char* final,
56                        const char* src,
57                        const char* dst) {
58     static const char kComponents[] = {'r', 'g', 'b'};
59     for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
60         char component = kComponents[i];
61         fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
62         fsBuilder->codeAppendf("%s.%c = 2.0 * %s.%c * %s.%c;",
63                                final, component, src, component, dst, component);
64         fsBuilder->codeAppend("} else {");
65         fsBuilder->codeAppendf("%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);",
66                                final, component, src, dst, dst, dst, component, src, src,
67                                component);
68         fsBuilder->codeAppend("}");
69     }
70     fsBuilder->codeAppendf("%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);",
71                            final, src, dst, dst, src);
72 }
73 
74 // Does one component of color-dodge
color_dodge_component(GrGLFragmentBuilder * fsBuilder,const char * final,const char * src,const char * dst,const char component)75 static void color_dodge_component(GrGLFragmentBuilder* fsBuilder,
76                                   const char* final,
77                                   const char* src,
78                                   const char* dst,
79                                   const char component) {
80     fsBuilder->codeAppendf("if (0.0 == %s.%c) {", dst, component);
81     fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
82                            final, component, src, component, dst);
83     fsBuilder->codeAppend("} else {");
84     fsBuilder->codeAppendf("float d = %s.a - %s.%c;", src, src, component);
85     fsBuilder->codeAppend("if (0.0 == d) {");
86     fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
87                            final, component, src, dst, src, component, dst, dst, component,
88                            src);
89     fsBuilder->codeAppend("} else {");
90     fsBuilder->codeAppendf("d = min(%s.a, %s.%c * %s.a / d);",
91                            dst, dst, component, src);
92     fsBuilder->codeAppendf("%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
93                            final, component, src, src, component, dst, dst, component, src);
94     fsBuilder->codeAppend("}");
95     fsBuilder->codeAppend("}");
96 }
97 
98 // Does one component of color-burn
color_burn_component(GrGLFragmentBuilder * fsBuilder,const char * final,const char * src,const char * dst,const char component)99 static void color_burn_component(GrGLFragmentBuilder* fsBuilder,
100                                  const char* final,
101                                  const char* src,
102                                  const char* dst,
103                                  const char component) {
104     fsBuilder->codeAppendf("if (%s.a == %s.%c) {", dst, dst, component);
105     fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
106                            final, component, src, dst, src, component, dst, dst, component,
107                            src);
108     fsBuilder->codeAppendf("} else if (0.0 == %s.%c) {", src, component);
109     fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
110                            final, component, dst, component, src);
111     fsBuilder->codeAppend("} else {");
112     fsBuilder->codeAppendf("float d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);",
113                            dst, dst, dst, component, src, src, component);
114     fsBuilder->codeAppendf("%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
115                            final, component, src, src, component, dst, dst, component, src);
116     fsBuilder->codeAppend("}");
117 }
118 
119 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
soft_light_component_pos_dst_alpha(GrGLFragmentBuilder * fsBuilder,const char * final,const char * src,const char * dst,const char component)120 static void soft_light_component_pos_dst_alpha(GrGLFragmentBuilder* fsBuilder,
121                                                const char* final,
122                                                const char* src,
123                                                const char* dst,
124                                                const char component) {
125     // if (2S < Sa)
126     fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
127     // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
128     fsBuilder->codeAppendf("%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a +"
129                                    "(1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s.%c + 1.0);",
130                            final, component, dst, component, dst, component, src, src,
131                            component, dst, dst, src, component, dst, component, src, src,
132                            component);
133     // else if (4D < Da)
134     fsBuilder->codeAppendf("} else if (4.0 * %s.%c <= %s.a) {",
135                            dst, component, dst);
136     fsBuilder->codeAppendf("float DSqd = %s.%c * %s.%c;",
137                            dst, component, dst, component);
138     fsBuilder->codeAppendf("float DCub = DSqd * %s.%c;", dst, component);
139     fsBuilder->codeAppendf("float DaSqd = %s.a * %s.a;", dst, dst);
140     fsBuilder->codeAppendf("float DaCub = DaSqd * %s.a;", dst);
141     // (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
142     fsBuilder->codeAppendf("%s.%c ="
143                            "(-DaCub*%s.%c + DaSqd*(%s.%c - %s.%c * (3.0*%s.a - 6.0*%s.%c - 1.0)) +"
144                            " 12.0*%s.a*DSqd*(%s.a - 2.0*%s.%c) - 16.0*DCub * (%s.a - 2.0*%s.%c)) /"
145                            "DaSqd;",
146                            final, component, src, component, src, component, dst, component,
147                            src, src, component, dst, src, src, component, src, src,
148                            component);
149     fsBuilder->codeAppendf("} else {");
150     // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
151     fsBuilder->codeAppendf("%s.%c = -sqrt(%s.a*%s.%c)*(%s.a - 2.0*%s.%c) - %s.a*%s.%c +"
152                                    "%s.%c*(%s.a - 2.0*%s.%c + 1.0) + %s.%c;",
153                            final, component, dst, dst, component, src, src, component, dst,
154                            src, component, dst, component, src, src, component, src,
155                            component);
156     fsBuilder->codeAppendf("}");
157 }
158 
159 // Adds a function that takes two colors and an alpha as input. It produces a color with the
160 // hue and saturation of the first color, the luminosity of the second color, and the input
161 // alpha. It has this signature:
162 //      vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
add_lum_function(GrGLFragmentBuilder * fsBuilder,SkString * setLumFunction)163 static void add_lum_function(GrGLFragmentBuilder* fsBuilder, SkString* setLumFunction) {
164     // Emit a helper that gets the luminance of a color.
165     SkString getFunction;
166     GrGLShaderVar getLumArgs[] = {
167         GrGLShaderVar("color", kVec3f_GrSLType),
168     };
169     SkString getLumBody("return dot(vec3(0.3, 0.59, 0.11), color);");
170     fsBuilder->emitFunction(kFloat_GrSLType,
171                             "luminance",
172                             SK_ARRAY_COUNT(getLumArgs), getLumArgs,
173                             getLumBody.c_str(),
174                             &getFunction);
175 
176     // Emit the set luminance function.
177     GrGLShaderVar setLumArgs[] = {
178         GrGLShaderVar("hueSat", kVec3f_GrSLType),
179         GrGLShaderVar("alpha", kFloat_GrSLType),
180         GrGLShaderVar("lumColor", kVec3f_GrSLType),
181     };
182     SkString setLumBody;
183     setLumBody.printf("float diff = %s(lumColor - hueSat);", getFunction.c_str());
184     setLumBody.append("vec3 outColor = hueSat + diff;");
185     setLumBody.appendf("float outLum = %s(outColor);", getFunction.c_str());
186     setLumBody.append("float minComp = min(min(outColor.r, outColor.g), outColor.b);"
187                       "float maxComp = max(max(outColor.r, outColor.g), outColor.b);"
188                       "if (minComp < 0.0 && outLum != minComp) {"
189                       "outColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) /"
190                                           "(outLum - minComp);"
191                       "}"
192                       "if (maxComp > alpha && maxComp != outLum) {"
193                       "outColor = outLum +"
194                                  "((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) /"
195                                  "(maxComp - outLum);"
196                       "}"
197                       "return outColor;");
198     fsBuilder->emitFunction(kVec3f_GrSLType,
199                             "set_luminance",
200                             SK_ARRAY_COUNT(setLumArgs), setLumArgs,
201                             setLumBody.c_str(),
202                             setLumFunction);
203 }
204 
205 // Adds a function that creates a color with the hue and luminosity of one input color and
206 // the saturation of another color. It will have this signature:
207 //      float set_saturation(vec3 hueLumColor, vec3 satColor)
add_sat_function(GrGLFragmentBuilder * fsBuilder,SkString * setSatFunction)208 static void add_sat_function(GrGLFragmentBuilder* fsBuilder, SkString* setSatFunction) {
209     // Emit a helper that gets the saturation of a color
210     SkString getFunction;
211     GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
212     SkString getSatBody;
213     getSatBody.printf("return max(max(color.r, color.g), color.b) - "
214                       "min(min(color.r, color.g), color.b);");
215     fsBuilder->emitFunction(kFloat_GrSLType,
216                             "saturation",
217                             SK_ARRAY_COUNT(getSatArgs), getSatArgs,
218                             getSatBody.c_str(),
219                             &getFunction);
220 
221     // Emit a helper that sets the saturation given sorted input channels. This used
222     // to use inout params for min, mid, and max components but that seems to cause
223     // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
224     // adjusted min, mid, and max inputs, respectively.
225     SkString helperFunction;
226     GrGLShaderVar helperArgs[] = {
227         GrGLShaderVar("minComp", kFloat_GrSLType),
228         GrGLShaderVar("midComp", kFloat_GrSLType),
229         GrGLShaderVar("maxComp", kFloat_GrSLType),
230         GrGLShaderVar("sat", kFloat_GrSLType),
231     };
232     static const char kHelperBody[] = "if (minComp < maxComp) {"
233         "vec3 result;"
234         "result.r = 0.0;"
235         "result.g = sat * (midComp - minComp) / (maxComp - minComp);"
236         "result.b = sat;"
237         "return result;"
238         "} else {"
239         "return vec3(0, 0, 0);"
240         "}";
241     fsBuilder->emitFunction(kVec3f_GrSLType,
242                             "set_saturation_helper",
243                             SK_ARRAY_COUNT(helperArgs), helperArgs,
244                             kHelperBody,
245                             &helperFunction);
246 
247     GrGLShaderVar setSatArgs[] = {
248         GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
249         GrGLShaderVar("satColor", kVec3f_GrSLType),
250     };
251     const char* helpFunc = helperFunction.c_str();
252     SkString setSatBody;
253     setSatBody.appendf("float sat = %s(satColor);"
254                        "if (hueLumColor.r <= hueLumColor.g) {"
255                        "if (hueLumColor.g <= hueLumColor.b) {"
256                        "hueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);"
257                        "} else if (hueLumColor.r <= hueLumColor.b) {"
258                        "hueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);"
259                        "} else {"
260                        "hueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);"
261                        "}"
262                        "} else if (hueLumColor.r <= hueLumColor.b) {"
263                        "hueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);"
264                        "} else if (hueLumColor.g <= hueLumColor.b) {"
265                        "hueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);"
266                        "} else {"
267                        "hueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);"
268                        "}"
269                        "return hueLumColor;",
270                        getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
271                        helpFunc, helpFunc);
272     fsBuilder->emitFunction(kVec3f_GrSLType,
273                             "set_saturation",
274                             SK_ARRAY_COUNT(setSatArgs), setSatArgs,
275                             setSatBody.c_str(),
276                             setSatFunction);
277 
278 }
279 
emit_custom_xfermode_code(SkXfermode::Mode mode,GrGLFragmentBuilder * fsBuilder,const char * outputColor,const char * inputColor,const char * dstColor)280 static void emit_custom_xfermode_code(SkXfermode::Mode mode,
281                                       GrGLFragmentBuilder* fsBuilder,
282                                       const char* outputColor,
283                                       const char* inputColor,
284                                       const char* dstColor) {
285     // We don't try to optimize for this case at all
286     if (NULL == inputColor) {
287         fsBuilder->codeAppendf("const vec4 ones = vec4(1);");
288         inputColor = "ones";
289     }
290     fsBuilder->codeAppendf("// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
291 
292     // These all perform src-over on the alpha channel.
293     fsBuilder->codeAppendf("%s.a = %s.a + (1.0 - %s.a) * %s.a;",
294                            outputColor, inputColor, inputColor, dstColor);
295 
296     switch (mode) {
297         case SkXfermode::kOverlay_Mode:
298             // Overlay is Hard-Light with the src and dst reversed
299             hard_light(fsBuilder, outputColor, dstColor, inputColor);
300             break;
301         case SkXfermode::kDarken_Mode:
302             fsBuilder->codeAppendf("%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
303                                    "(1.0 - %s.a) * %s.rgb + %s.rgb);",
304                                    outputColor,
305                                    inputColor, dstColor, inputColor,
306                                    dstColor, inputColor, dstColor);
307             break;
308         case SkXfermode::kLighten_Mode:
309             fsBuilder->codeAppendf("%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
310                                    "(1.0 - %s.a) * %s.rgb + %s.rgb);",
311                                    outputColor,
312                                    inputColor, dstColor, inputColor,
313                                    dstColor, inputColor, dstColor);
314             break;
315         case SkXfermode::kColorDodge_Mode:
316             color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'r');
317             color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'g');
318             color_dodge_component(fsBuilder, outputColor, inputColor, dstColor, 'b');
319             break;
320         case SkXfermode::kColorBurn_Mode:
321             color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'r');
322             color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'g');
323             color_burn_component(fsBuilder, outputColor, inputColor, dstColor, 'b');
324             break;
325         case SkXfermode::kHardLight_Mode:
326             hard_light(fsBuilder, outputColor, inputColor, dstColor);
327             break;
328         case SkXfermode::kSoftLight_Mode:
329             fsBuilder->codeAppendf("if (0.0 == %s.a) {", dstColor);
330             fsBuilder->codeAppendf("%s.rgba = %s;", outputColor, inputColor);
331             fsBuilder->codeAppendf("} else {");
332             soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'r');
333             soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'g');
334             soft_light_component_pos_dst_alpha(fsBuilder, outputColor, inputColor, dstColor, 'b');
335             fsBuilder->codeAppendf("}");
336             break;
337         case SkXfermode::kDifference_Mode:
338             fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb -"
339                                    "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);",
340                                    outputColor, inputColor, dstColor, inputColor, dstColor,
341                                    dstColor, inputColor);
342             break;
343         case SkXfermode::kExclusion_Mode:
344             fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb - "
345                                    "2.0 * %s.rgb * %s.rgb;",
346                                    outputColor, dstColor, inputColor, dstColor, inputColor);
347             break;
348         case SkXfermode::kMultiply_Mode:
349             fsBuilder->codeAppendf("%s.rgb = (1.0 - %s.a) * %s.rgb + "
350                                    "(1.0 - %s.a) * %s.rgb + "
351                                    "%s.rgb * %s.rgb;",
352                                    outputColor, inputColor, dstColor, dstColor, inputColor,
353                                    inputColor, dstColor);
354             break;
355         case SkXfermode::kHue_Mode: {
356             //  SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
357             SkString setSat, setLum;
358             add_sat_function(fsBuilder, &setSat);
359             add_lum_function(fsBuilder, &setLum);
360             fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
361                                    dstColor, inputColor);
362             fsBuilder->codeAppendf("%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb),"
363                                                "dstSrcAlpha.a, dstSrcAlpha.rgb);",
364                                    outputColor, setLum.c_str(), setSat.c_str(), inputColor,
365                                    dstColor);
366             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
367                                    outputColor, inputColor, dstColor, dstColor, inputColor);
368             break;
369         }
370         case SkXfermode::kSaturation_Mode: {
371             // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
372             SkString setSat, setLum;
373             add_sat_function(fsBuilder, &setSat);
374             add_lum_function(fsBuilder, &setLum);
375             fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
376                                    dstColor, inputColor);
377             fsBuilder->codeAppendf("%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a),"
378                                                "dstSrcAlpha.a, dstSrcAlpha.rgb);",
379                                    outputColor, setLum.c_str(), setSat.c_str(), inputColor,
380                                    dstColor);
381             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
382                                    outputColor, inputColor, dstColor, dstColor, inputColor);
383             break;
384         }
385         case SkXfermode::kColor_Mode: {
386             //  SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
387             SkString setLum;
388             add_lum_function(fsBuilder, &setLum);
389             fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
390                                    inputColor, dstColor);
391             fsBuilder->codeAppendf("%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);",
392                                    outputColor, setLum.c_str(), dstColor, inputColor);
393             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
394                                    outputColor, inputColor, dstColor, dstColor, inputColor);
395             break;
396         }
397         case SkXfermode::kLuminosity_Mode: {
398             //  SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
399             SkString setLum;
400             add_lum_function(fsBuilder, &setLum);
401             fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
402                                    inputColor, dstColor);
403             fsBuilder->codeAppendf("%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);",
404                                    outputColor, setLum.c_str(), dstColor, inputColor);
405             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
406                                    outputColor, inputColor, dstColor, dstColor, inputColor);
407             break;
408         }
409         default:
410             SkFAIL("Unknown Custom Xfer mode.");
411             break;
412     }
413 }
414 
415 ///////////////////////////////////////////////////////////////////////////////
416 // Fragment Processor
417 ///////////////////////////////////////////////////////////////////////////////
418 
CreateFP(SkXfermode::Mode mode,GrTexture * background)419 GrFragmentProcessor* GrCustomXfermode::CreateFP(SkXfermode::Mode mode, GrTexture* background) {
420     if (!GrCustomXfermode::IsSupportedMode(mode)) {
421         return NULL;
422     } else {
423         return SkNEW_ARGS(GrCustomXferFP, (mode, background));
424     }
425 }
426 
427 ///////////////////////////////////////////////////////////////////////////////
428 
429 class GLCustomXferFP : public GrGLFragmentProcessor {
430 public:
GLCustomXferFP(const GrFragmentProcessor &)431     GLCustomXferFP(const GrFragmentProcessor&) {}
~GLCustomXferFP()432     ~GLCustomXferFP() override {};
433 
emitCode(GrGLFPBuilder * builder,const GrFragmentProcessor & fp,const char * outputColor,const char * inputColor,const TransformedCoordsArray & coords,const TextureSamplerArray & samplers)434     void emitCode(GrGLFPBuilder* builder,
435                   const GrFragmentProcessor& fp,
436                   const char* outputColor,
437                   const char* inputColor,
438                   const TransformedCoordsArray& coords,
439                   const TextureSamplerArray& samplers) override {
440         SkXfermode::Mode mode = fp.cast<GrCustomXferFP>().mode();
441         GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
442         const char* dstColor = "bgColor";
443         fsBuilder->codeAppendf("vec4 %s = ", dstColor);
444         fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
445         fsBuilder->codeAppendf(";");
446 
447         emit_custom_xfermode_code(mode, fsBuilder, outputColor, inputColor, dstColor);
448     }
449 
setData(const GrGLProgramDataManager &,const GrProcessor &)450     void setData(const GrGLProgramDataManager&, const GrProcessor&) override {}
451 
GenKey(const GrFragmentProcessor & proc,const GrGLSLCaps &,GrProcessorKeyBuilder * b)452     static void GenKey(const GrFragmentProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
453         // The background may come from the dst or from a texture.
454         uint32_t key = proc.numTextures();
455         SkASSERT(key <= 1);
456         key |= proc.cast<GrCustomXferFP>().mode() << 1;
457         b->add32(key);
458     }
459 
460 private:
461     typedef GrGLFragmentProcessor INHERITED;
462 };
463 
464 ///////////////////////////////////////////////////////////////////////////////
465 
GrCustomXferFP(SkXfermode::Mode mode,GrTexture * background)466 GrCustomXferFP::GrCustomXferFP(SkXfermode::Mode mode, GrTexture* background)
467     : fMode(mode) {
468     this->initClassID<GrCustomXferFP>();
469 
470     SkASSERT(background);
471     fBackgroundTransform.reset(kLocal_GrCoordSet, background,
472                                GrTextureParams::kNone_FilterMode);
473     this->addCoordTransform(&fBackgroundTransform);
474     fBackgroundAccess.reset(background);
475     this->addTextureAccess(&fBackgroundAccess);
476 }
477 
getGLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const478 void GrCustomXferFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
479     GLCustomXferFP::GenKey(*this, caps, b);
480 }
481 
createGLInstance() const482 GrGLFragmentProcessor* GrCustomXferFP::createGLInstance() const {
483     return SkNEW_ARGS(GLCustomXferFP, (*this));
484 }
485 
onIsEqual(const GrFragmentProcessor & other) const486 bool GrCustomXferFP::onIsEqual(const GrFragmentProcessor& other) const {
487     const GrCustomXferFP& s = other.cast<GrCustomXferFP>();
488     return fMode == s.fMode;
489 }
490 
onComputeInvariantOutput(GrInvariantOutput * inout) const491 void GrCustomXferFP::onComputeInvariantOutput(GrInvariantOutput* inout) const {
492     inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
493 }
494 
495 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCustomXferFP);
TestCreate(SkRandom * rand,GrContext *,const GrDrawTargetCaps &,GrTexture * textures[])496 GrFragmentProcessor* GrCustomXferFP::TestCreate(SkRandom* rand,
497                                                 GrContext*,
498                                                 const GrDrawTargetCaps&,
499                                                 GrTexture* textures[]) {
500     int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
501 
502     return SkNEW_ARGS(GrCustomXferFP, (static_cast<SkXfermode::Mode>(mode), textures[0]));
503 }
504 
505 ///////////////////////////////////////////////////////////////////////////////
506 // Xfer Processor
507 ///////////////////////////////////////////////////////////////////////////////
508 
509 class CustomXP : public GrXferProcessor {
510 public:
Create(SkXfermode::Mode mode,const GrDeviceCoordTexture * dstCopy,bool willReadDstColor)511     static GrXferProcessor* Create(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy,
512                                    bool willReadDstColor) {
513         if (!GrCustomXfermode::IsSupportedMode(mode)) {
514             return NULL;
515         } else {
516             return SkNEW_ARGS(CustomXP, (mode, dstCopy, willReadDstColor));
517         }
518     }
519 
~CustomXP()520     ~CustomXP() override {};
521 
name() const522     const char* name() const override { return "Custom Xfermode"; }
523 
524     GrGLXferProcessor* createGLInstance() const override;
525 
hasSecondaryOutput() const526     bool hasSecondaryOutput() const override { return false; }
527 
mode() const528     SkXfermode::Mode mode() const { return fMode; }
hasHWBlendEquation() const529     bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
530 
hwBlendEquation() const531     GrBlendEquation hwBlendEquation() const {
532         SkASSERT(this->hasHWBlendEquation());
533         return fHWBlendEquation;
534     }
535 
536 private:
537     CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
538 
539     GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
540                                                  const GrProcOptInfo& coveragePOI,
541                                                  bool doesStencilWrite,
542                                                  GrColor* overrideColor,
543                                                  const GrDrawTargetCaps& caps) override;
544 
545     void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
546 
547     bool onWillNeedXferBarrier(const GrRenderTarget* rt,
548                                const GrDrawTargetCaps& caps,
549                                GrXferBarrierType* outBarrierType) const override;
550 
551     void onGetBlendInfo(BlendInfo*) const override;
552 
553     bool onIsEqual(const GrXferProcessor& xpBase) const override;
554 
555     SkXfermode::Mode fMode;
556     GrBlendEquation  fHWBlendEquation;
557 
558     typedef GrXferProcessor INHERITED;
559 };
560 
561 ///////////////////////////////////////////////////////////////////////////////
562 
CreateXPFactory(SkXfermode::Mode mode)563 GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) {
564     if (!GrCustomXfermode::IsSupportedMode(mode)) {
565         return NULL;
566     } else {
567         return SkNEW_ARGS(GrCustomXPFactory, (mode));
568     }
569 }
570 
571 ///////////////////////////////////////////////////////////////////////////////
572 
573 class GLCustomXP : public GrGLXferProcessor {
574 public:
GLCustomXP(const GrXferProcessor &)575     GLCustomXP(const GrXferProcessor&) {}
~GLCustomXP()576     ~GLCustomXP() override {}
577 
GenKey(const GrXferProcessor & p,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b)578     static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
579         const CustomXP& xp = p.cast<CustomXP>();
580         uint32_t key = xp.numTextures();
581         SkASSERT(key <= 1);
582         key |= xp.readsCoverage() << 1;
583         if (xp.hasHWBlendEquation()) {
584             SkASSERT(caps.advBlendEqInteraction() > 0);  // 0 will mean !xp.hasHWBlendEquation().
585             key |= caps.advBlendEqInteraction() << 2;
586         }
587         if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
588             GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
589             key |= xp.mode() << 4;
590         }
591         b->add32(key);
592     }
593 
594 private:
onEmitCode(const EmitArgs & args)595     void onEmitCode(const EmitArgs& args) override {
596         const CustomXP& xp = args.fXP.cast<CustomXP>();
597         GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
598 
599         if (xp.hasHWBlendEquation()) {
600             // The blend mode will be implemented in hardware; only output the src color.
601             fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
602             if (xp.readsCoverage()) {
603                 // Do coverage modulation by multiplying it into the src color before blending.
604                 // (See getOptimizations())
605                 fsBuilder->codeAppendf("%s = %s * %s;",
606                                        args.fOutputPrimary, args.fInputCoverage, args.fInputColor);
607             } else {
608                 fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
609             }
610         } else {
611             const char* dstColor = fsBuilder->dstColor();
612             emit_custom_xfermode_code(xp.mode(), fsBuilder, args.fOutputPrimary, args.fInputColor,
613                                       dstColor);
614             if (xp.readsCoverage()) {
615                 fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
616                                        args.fOutputPrimary, args.fOutputPrimary,
617                                        args.fInputCoverage, args.fInputCoverage, dstColor);
618             }
619         }
620     }
621 
onSetData(const GrGLProgramDataManager &,const GrXferProcessor &)622     void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
623 
624     typedef GrGLFragmentProcessor INHERITED;
625 };
626 
627 ///////////////////////////////////////////////////////////////////////////////
628 
CustomXP(SkXfermode::Mode mode,const GrDeviceCoordTexture * dstCopy,bool willReadDstColor)629 CustomXP::CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy,
630                    bool willReadDstColor)
631     : INHERITED(dstCopy, willReadDstColor),
632       fMode(mode),
633       fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
634     this->initClassID<CustomXP>();
635 }
636 
onGetGLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const637 void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
638     GLCustomXP::GenKey(*this, caps, b);
639 }
640 
createGLInstance() const641 GrGLXferProcessor* CustomXP::createGLInstance() const {
642     SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
643     return SkNEW_ARGS(GLCustomXP, (*this));
644 }
645 
onIsEqual(const GrXferProcessor & other) const646 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
647     const CustomXP& s = other.cast<CustomXP>();
648     return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
649 }
650 
onGetOptimizations(const GrProcOptInfo & colorPOI,const GrProcOptInfo & coveragePOI,bool doesStencilWrite,GrColor * overrideColor,const GrDrawTargetCaps & caps)651 GrXferProcessor::OptFlags CustomXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
652                                                        const GrProcOptInfo& coveragePOI,
653                                                        bool doesStencilWrite,
654                                                        GrColor* overrideColor,
655                                                        const GrDrawTargetCaps& caps) {
656   /*
657     Most the optimizations we do here are based on tweaking alpha for coverage.
658 
659     The general SVG blend equation is defined in the spec as follows:
660 
661       Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
662       Da'  = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
663 
664     (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
665      and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
666      RGB colors.)
667 
668     For every blend mode supported by this class, i.e. the "advanced" blend
669     modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
670 
671     It can be shown that when X=Y=Z=1, these equations can modulate alpha for
672     coverage.
673 
674 
675     == Color ==
676 
677     We substitute Y=Z=1 and define a blend() function that calculates Dca' in
678     terms of premultiplied alpha only:
679 
680       blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
681                                  Sca : if Da == 0,
682                                  B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0}
683 
684     And for coverage modulation, we use a post blend src-over model:
685 
686       Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
687 
688     (Where f is the fractional coverage.)
689 
690     Next we show that canTweakAlphaForCoverage() is true by proving the
691     following relationship:
692 
693       blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
694 
695     General case (f,Sa,Da != 0):
696 
697       f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
698         = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca  [Sa,Da != 0, definition of blend()]
699         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
700         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
701         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
702         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
703         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
704         = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)  [f!=0]
705         = blend(f*Sca, Dca, f*Sa, Da)  [definition of blend()]
706 
707     Corner cases (Sa=0, Da=0, and f=0):
708 
709       Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
710               = f * Dca + (1-f) * Dca  [Sa=0, definition of blend()]
711               = Dca
712               = blend(0, Dca, 0, Da)  [definition of blend()]
713               = blend(f*Sca, Dca, f*Sa, Da)  [Sa=0]
714 
715       Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
716               = f * Sca + (1-f) * Dca  [Da=0, definition of blend()]
717               = f * Sca  [Da=0]
718               = blend(f*Sca, 0, f*Sa, 0)  [definition of blend()]
719               = blend(f*Sca, Dca, f*Sa, Da)  [Da=0]
720 
721       f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
722              = Dca  [f=0]
723              = blend(0, Dca, 0, Da)  [definition of blend()]
724              = blend(f*Sca, Dca, f*Sa, Da)  [f=0]
725 
726     == Alpha ==
727 
728     We substitute X=Y=Z=1 and define a blend() function that calculates Da':
729 
730       blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
731                     = Sa * Da + Sa - Sa * Da + Da - Da * Sa
732                     = Sa + Da - Sa * Da
733 
734     We use the same model for coverage modulation as we did with color:
735 
736       Da'' = f * blend(Sa, Da) + (1-f) * Da
737 
738     And show that canTweakAlphaForCoverage() is true by proving the following
739     relationship:
740 
741       blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
742 
743 
744       f * blend(Sa, Da) + (1-f) * Da
745         = f * (Sa + Da - Sa * Da) + (1-f) * Da
746         = f*Sa + f*Da - f*Sa * Da + Da - f*Da
747         = f*Sa - f*Sa * Da + Da
748         = f*Sa + Da - f*Sa * Da
749         = blend(f*Sa, Da)
750    */
751 
752     OptFlags flags = kNone_Opt;
753     if (colorPOI.allStagesMultiplyInput()) {
754         flags |= kCanTweakAlphaForCoverage_OptFlag;
755     }
756     if (coveragePOI.isSolidWhite()) {
757         flags |= kIgnoreCoverage_OptFlag;
758     }
759     if (caps.advancedBlendEquationSupport() && !coveragePOI.isFourChannelOutput()) {
760         // This blend mode can be implemented in hardware.
761         fHWBlendEquation = hw_blend_equation(fMode);
762     }
763     return flags;
764 }
765 
onWillNeedXferBarrier(const GrRenderTarget * rt,const GrDrawTargetCaps & caps,GrXferBarrierType * outBarrierType) const766 bool CustomXP::onWillNeedXferBarrier(const GrRenderTarget* rt,
767                                      const GrDrawTargetCaps& caps,
768                                      GrXferBarrierType* outBarrierType) const {
769     if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
770         *outBarrierType = kBlend_GrXferBarrierType;
771         return true;
772     }
773     return false;
774 }
775 
onGetBlendInfo(BlendInfo * blendInfo) const776 void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
777     if (this->hasHWBlendEquation()) {
778         blendInfo->fEquation = this->hwBlendEquation();
779     }
780 }
781 
782 ///////////////////////////////////////////////////////////////////////////////
783 
GrCustomXPFactory(SkXfermode::Mode mode)784 GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
785     : fMode(mode) {
786     this->initClassID<GrCustomXPFactory>();
787 }
788 
789 GrXferProcessor*
onCreateXferProcessor(const GrDrawTargetCaps & caps,const GrProcOptInfo & colorPOI,const GrProcOptInfo & coveragePOI,const GrDeviceCoordTexture * dstCopy) const790 GrCustomXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
791                                          const GrProcOptInfo& colorPOI,
792                                          const GrProcOptInfo& coveragePOI,
793                                          const GrDeviceCoordTexture* dstCopy) const {
794     return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI));
795 }
796 
willReadDstColor(const GrDrawTargetCaps & caps,const GrProcOptInfo & colorPOI,const GrProcOptInfo & coveragePOI) const797 bool GrCustomXPFactory::willReadDstColor(const GrDrawTargetCaps& caps,
798                                          const GrProcOptInfo& colorPOI,
799                                          const GrProcOptInfo& coveragePOI) const {
800     if (!caps.advancedBlendEquationSupport()) {
801         // No hardware support for advanced blend equations; we will need to do it in the shader.
802         return true;
803     }
804     if (coveragePOI.isFourChannelOutput()) {
805         // Advanced blend equations can't tweak alpha for RGB coverage.
806         return true;
807     }
808     return false;
809 }
810 
getInvariantOutput(const GrProcOptInfo & colorPOI,const GrProcOptInfo & coveragePOI,GrXPFactory::InvariantOutput * output) const811 void GrCustomXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
812                                                const GrProcOptInfo& coveragePOI,
813                                                GrXPFactory::InvariantOutput* output) const {
814     output->fWillBlendWithDst = true;
815     output->fBlendedColorFlags = 0;
816 }
817 
818 GR_DEFINE_XP_FACTORY_TEST(GrCustomXPFactory);
TestCreate(SkRandom * rand,GrContext *,const GrDrawTargetCaps &,GrTexture * [])819 GrXPFactory* GrCustomXPFactory::TestCreate(SkRandom* rand,
820                                            GrContext*,
821                                            const GrDrawTargetCaps&,
822                                            GrTexture*[]) {
823     int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
824 
825     return SkNEW_ARGS(GrCustomXPFactory, (static_cast<SkXfermode::Mode>(mode)));
826 }
827 
828