remove xfermode from public api
[platform/upstream/libSkiaSharp.git] / src / gpu / glsl / GrGLSLBlend.cpp
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 #include "GrGLSLBlend.h"
8 #include "glsl/GrGLSLFragmentShaderBuilder.h"
9
10 //////////////////////////////////////////////////////////////////////////////
11 //  Advanced (non-coeff) blend helpers
12 //////////////////////////////////////////////////////////////////////////////
13
14 static void hard_light(GrGLSLFragmentBuilder* fsBuilder,
15                        const char* final,
16                        const char* src,
17                        const char* dst) {
18     static const char kComponents[] = { 'r', 'g', 'b' };
19     for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
20         char component = kComponents[i];
21         fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
22         fsBuilder->codeAppendf("%s.%c = 2.0 * %s.%c * %s.%c;",
23                                final, component, src, component, dst, component);
24         fsBuilder->codeAppend("} else {");
25         fsBuilder->codeAppendf("%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);",
26                                final, component, src, dst, dst, dst, component, src, src,
27                                component);
28         fsBuilder->codeAppend("}");
29     }
30     fsBuilder->codeAppendf("%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);",
31                            final, src, dst, dst, src);
32 }
33
34 // Does one component of color-dodge
35 static void color_dodge_component(GrGLSLFragmentBuilder* fsBuilder,
36                                   const char* final,
37                                   const char* src,
38                                   const char* dst,
39                                   const char component) {
40     fsBuilder->codeAppendf("if (0.0 == %s.%c) {", dst, component);
41     fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
42                            final, component, src, component, dst);
43     fsBuilder->codeAppend("} else {");
44     fsBuilder->codeAppendf("float d = %s.a - %s.%c;", src, src, component);
45     fsBuilder->codeAppend("if (0.0 == d) {");
46     fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
47                            final, component, src, dst, src, component, dst, dst, component,
48                            src);
49     fsBuilder->codeAppend("} else {");
50     fsBuilder->codeAppendf("d = min(%s.a, %s.%c * %s.a / d);",
51                            dst, dst, component, src);
52     fsBuilder->codeAppendf("%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
53                            final, component, src, src, component, dst, dst, component, src);
54     fsBuilder->codeAppend("}");
55     fsBuilder->codeAppend("}");
56 }
57
58 // Does one component of color-burn
59 static void color_burn_component(GrGLSLFragmentBuilder* fsBuilder,
60                                  const char* final,
61                                  const char* src,
62                                  const char* dst,
63                                  const char component) {
64     fsBuilder->codeAppendf("if (%s.a == %s.%c) {", dst, dst, component);
65     fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
66                            final, component, src, dst, src, component, dst, dst, component,
67                            src);
68     fsBuilder->codeAppendf("} else if (0.0 == %s.%c) {", src, component);
69     fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);",
70                            final, component, dst, component, src);
71     fsBuilder->codeAppend("} else {");
72     fsBuilder->codeAppendf("float d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);",
73                            dst, dst, dst, component, src, src, component);
74     fsBuilder->codeAppendf("%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);",
75                            final, component, src, src, component, dst, dst, component, src);
76     fsBuilder->codeAppend("}");
77 }
78
79 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
80 static void soft_light_component_pos_dst_alpha(GrGLSLFragmentBuilder* fsBuilder,
81                                                const char* final,
82                                                const char* src,
83                                                const char* dst,
84                                                const char component) {
85     // if (2S < Sa)
86     fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src);
87     // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
88     fsBuilder->codeAppendf("%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a +"
89                            "(1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s.%c + 1.0);",
90                            final, component, dst, component, dst, component, src, src,
91                            component, dst, dst, src, component, dst, component, src, src,
92                            component);
93     // else if (4D < Da)
94     fsBuilder->codeAppendf("} else if (4.0 * %s.%c <= %s.a) {",
95                            dst, component, dst);
96     fsBuilder->codeAppendf("float DSqd = %s.%c * %s.%c;",
97                            dst, component, dst, component);
98     fsBuilder->codeAppendf("float DCub = DSqd * %s.%c;", dst, component);
99     fsBuilder->codeAppendf("float DaSqd = %s.a * %s.a;", dst, dst);
100     fsBuilder->codeAppendf("float DaCub = DaSqd * %s.a;", dst);
101     // (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
102     fsBuilder->codeAppendf("%s.%c ="
103                            "(DaSqd*(%s.%c - %s.%c * (3.0*%s.a - 6.0*%s.%c - 1.0)) +"
104                            " 12.0*%s.a*DSqd*(%s.a - 2.0*%s.%c) - 16.0*DCub * (%s.a - 2.0*%s.%c) -"
105                            " DaCub*%s.%c) / DaSqd;",
106                            final, component, src, component, dst, component,
107                            src, src, component, dst, src, src, component, src, src,
108                            component, src, component);
109     fsBuilder->codeAppendf("} else {");
110     // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
111     fsBuilder->codeAppendf("%s.%c = %s.%c*(%s.a - 2.0*%s.%c + 1.0) + %s.%c -"
112                            " sqrt(%s.a*%s.%c)*(%s.a - 2.0*%s.%c) - %s.a*%s.%c;",
113                            final, component, dst, component, src, src, component, src, component,
114                            dst, dst, component, src, src, component, dst, src, component);
115     fsBuilder->codeAppendf("}");
116 }
117
118 // Adds a function that takes two colors and an alpha as input. It produces a color with the
119 // hue and saturation of the first color, the luminosity of the second color, and the input
120 // alpha. It has this signature:
121 //      vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
122 static void add_lum_function(GrGLSLFragmentBuilder* fsBuilder, SkString* setLumFunction) {
123     // Emit a helper that gets the luminance of a color.
124     SkString getFunction;
125     GrGLSLShaderVar getLumArgs[] = {
126         GrGLSLShaderVar("color", kVec3f_GrSLType),
127     };
128     SkString getLumBody("return dot(vec3(0.3, 0.59, 0.11), color);");
129     fsBuilder->emitFunction(kFloat_GrSLType,
130                             "luminance",
131                             SK_ARRAY_COUNT(getLumArgs), getLumArgs,
132                             getLumBody.c_str(),
133                             &getFunction);
134
135     // Emit the set luminance function.
136     GrGLSLShaderVar setLumArgs[] = {
137         GrGLSLShaderVar("hueSat", kVec3f_GrSLType),
138         GrGLSLShaderVar("alpha", kFloat_GrSLType),
139         GrGLSLShaderVar("lumColor", kVec3f_GrSLType),
140     };
141     SkString setLumBody;
142     setLumBody.printf("float diff = %s(lumColor - hueSat);", getFunction.c_str());
143     setLumBody.append("vec3 outColor = hueSat + diff;");
144     setLumBody.appendf("float outLum = %s(outColor);", getFunction.c_str());
145     setLumBody.append("float minComp = min(min(outColor.r, outColor.g), outColor.b);"
146                       "float maxComp = max(max(outColor.r, outColor.g), outColor.b);"
147                       "if (minComp < 0.0 && outLum != minComp) {"
148                       "outColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) /"
149                       "(outLum - minComp);"
150                       "}"
151                       "if (maxComp > alpha && maxComp != outLum) {"
152                       "outColor = outLum +"
153                       "((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) /"
154                       "(maxComp - outLum);"
155                       "}"
156                       "return outColor;");
157     fsBuilder->emitFunction(kVec3f_GrSLType,
158                             "set_luminance",
159                             SK_ARRAY_COUNT(setLumArgs), setLumArgs,
160                             setLumBody.c_str(),
161                             setLumFunction);
162 }
163
164 // Adds a function that creates a color with the hue and luminosity of one input color and
165 // the saturation of another color. It will have this signature:
166 //      float set_saturation(vec3 hueLumColor, vec3 satColor)
167 static void add_sat_function(GrGLSLFragmentBuilder* fsBuilder, SkString* setSatFunction) {
168     // Emit a helper that gets the saturation of a color
169     SkString getFunction;
170     GrGLSLShaderVar getSatArgs[] = { GrGLSLShaderVar("color", kVec3f_GrSLType) };
171     SkString getSatBody;
172     getSatBody.printf("return max(max(color.r, color.g), color.b) - "
173                       "min(min(color.r, color.g), color.b);");
174     fsBuilder->emitFunction(kFloat_GrSLType,
175                             "saturation",
176                             SK_ARRAY_COUNT(getSatArgs), getSatArgs,
177                             getSatBody.c_str(),
178                             &getFunction);
179
180     // Emit a helper that sets the saturation given sorted input channels. This used
181     // to use inout params for min, mid, and max components but that seems to cause
182     // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
183     // adjusted min, mid, and max inputs, respectively.
184     SkString helperFunction;
185     GrGLSLShaderVar helperArgs[] = {
186         GrGLSLShaderVar("minComp", kFloat_GrSLType),
187         GrGLSLShaderVar("midComp", kFloat_GrSLType),
188         GrGLSLShaderVar("maxComp", kFloat_GrSLType),
189         GrGLSLShaderVar("sat", kFloat_GrSLType),
190     };
191     static const char kHelperBody[] = "if (minComp < maxComp) {"
192         "vec3 result;"
193         "result.r = 0.0;"
194         "result.g = sat * (midComp - minComp) / (maxComp - minComp);"
195         "result.b = sat;"
196         "return result;"
197         "} else {"
198         "return vec3(0, 0, 0);"
199         "}";
200     fsBuilder->emitFunction(kVec3f_GrSLType,
201                             "set_saturation_helper",
202                             SK_ARRAY_COUNT(helperArgs), helperArgs,
203                             kHelperBody,
204                             &helperFunction);
205
206     GrGLSLShaderVar setSatArgs[] = {
207         GrGLSLShaderVar("hueLumColor", kVec3f_GrSLType),
208         GrGLSLShaderVar("satColor", kVec3f_GrSLType),
209     };
210     const char* helpFunc = helperFunction.c_str();
211     SkString setSatBody;
212     setSatBody.appendf("float sat = %s(satColor);"
213                        "if (hueLumColor.r <= hueLumColor.g) {"
214                        "if (hueLumColor.g <= hueLumColor.b) {"
215                        "hueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);"
216                        "} else if (hueLumColor.r <= hueLumColor.b) {"
217                        "hueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);"
218                        "} else {"
219                        "hueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);"
220                        "}"
221                        "} else if (hueLumColor.r <= hueLumColor.b) {"
222                        "hueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);"
223                        "} else if (hueLumColor.g <= hueLumColor.b) {"
224                        "hueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);"
225                        "} else {"
226                        "hueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);"
227                        "}"
228                        "return hueLumColor;",
229                        getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
230                        helpFunc, helpFunc);
231     fsBuilder->emitFunction(kVec3f_GrSLType,
232                             "set_saturation",
233                             SK_ARRAY_COUNT(setSatArgs), setSatArgs,
234                             setSatBody.c_str(),
235                             setSatFunction);
236 }
237
238 static void emit_advanced_xfermode_code(GrGLSLFragmentBuilder* fsBuilder, const char* srcColor,
239                                         const char* dstColor, const char* outputColor,
240                                         SkBlendMode mode) {
241     SkASSERT(srcColor);
242     SkASSERT(dstColor);
243     SkASSERT(outputColor);
244     // These all perform src-over on the alpha channel.
245     fsBuilder->codeAppendf("%s.a = %s.a + (1.0 - %s.a) * %s.a;",
246                            outputColor, srcColor, srcColor, dstColor);
247
248     switch (mode) {
249         case SkBlendMode::kOverlay:
250             // Overlay is Hard-Light with the src and dst reversed
251             hard_light(fsBuilder, outputColor, dstColor, srcColor);
252             break;
253         case SkBlendMode::kDarken:
254             fsBuilder->codeAppendf("%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
255                                    "(1.0 - %s.a) * %s.rgb + %s.rgb);",
256                                    outputColor,
257                                    srcColor, dstColor, srcColor,
258                                    dstColor, srcColor, dstColor);
259             break;
260         case SkBlendMode::kLighten:
261             fsBuilder->codeAppendf("%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
262                                    "(1.0 - %s.a) * %s.rgb + %s.rgb);",
263                                    outputColor,
264                                    srcColor, dstColor, srcColor,
265                                    dstColor, srcColor, dstColor);
266             break;
267         case SkBlendMode::kColorDodge:
268             color_dodge_component(fsBuilder, outputColor, srcColor, dstColor, 'r');
269             color_dodge_component(fsBuilder, outputColor, srcColor, dstColor, 'g');
270             color_dodge_component(fsBuilder, outputColor, srcColor, dstColor, 'b');
271             break;
272         case SkBlendMode::kColorBurn:
273             color_burn_component(fsBuilder, outputColor, srcColor, dstColor, 'r');
274             color_burn_component(fsBuilder, outputColor, srcColor, dstColor, 'g');
275             color_burn_component(fsBuilder, outputColor, srcColor, dstColor, 'b');
276             break;
277         case SkBlendMode::kHardLight:
278             hard_light(fsBuilder, outputColor, srcColor, dstColor);
279             break;
280         case SkBlendMode::kSoftLight:
281             fsBuilder->codeAppendf("if (0.0 == %s.a) {", dstColor);
282             fsBuilder->codeAppendf("%s.rgba = %s;", outputColor, srcColor);
283             fsBuilder->codeAppendf("} else {");
284             soft_light_component_pos_dst_alpha(fsBuilder, outputColor, srcColor, dstColor, 'r');
285             soft_light_component_pos_dst_alpha(fsBuilder, outputColor, srcColor, dstColor, 'g');
286             soft_light_component_pos_dst_alpha(fsBuilder, outputColor, srcColor, dstColor, 'b');
287             fsBuilder->codeAppendf("}");
288             break;
289         case SkBlendMode::kDifference:
290             fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb -"
291                                    "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);",
292                                    outputColor, srcColor, dstColor, srcColor, dstColor,
293                                    dstColor, srcColor);
294             break;
295         case SkBlendMode::kExclusion:
296             fsBuilder->codeAppendf("%s.rgb = %s.rgb + %s.rgb - "
297                                    "2.0 * %s.rgb * %s.rgb;",
298                                    outputColor, dstColor, srcColor, dstColor, srcColor);
299             break;
300         case SkBlendMode::kMultiply:
301             fsBuilder->codeAppendf("%s.rgb = (1.0 - %s.a) * %s.rgb + "
302                                    "(1.0 - %s.a) * %s.rgb + "
303                                    "%s.rgb * %s.rgb;",
304                                    outputColor, srcColor, dstColor, dstColor, srcColor,
305                                    srcColor, dstColor);
306             break;
307         case SkBlendMode::kHue: {
308             //  SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
309             SkString setSat, setLum;
310             add_sat_function(fsBuilder, &setSat);
311             add_lum_function(fsBuilder, &setLum);
312             fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
313                                    dstColor, srcColor);
314             fsBuilder->codeAppendf("%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb),"
315                                    "dstSrcAlpha.a, dstSrcAlpha.rgb);",
316                                    outputColor, setLum.c_str(), setSat.c_str(), srcColor,
317                                    dstColor);
318             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
319                                    outputColor, srcColor, dstColor, dstColor, srcColor);
320             break;
321         }
322         case SkBlendMode::kSaturation: {
323             // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
324             SkString setSat, setLum;
325             add_sat_function(fsBuilder, &setSat);
326             add_lum_function(fsBuilder, &setLum);
327             fsBuilder->codeAppendf("vec4 dstSrcAlpha = %s * %s.a;",
328                                    dstColor, srcColor);
329             fsBuilder->codeAppendf("%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a),"
330                                    "dstSrcAlpha.a, dstSrcAlpha.rgb);",
331                                    outputColor, setLum.c_str(), setSat.c_str(), srcColor,
332                                    dstColor);
333             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
334                                    outputColor, srcColor, dstColor, dstColor, srcColor);
335             break;
336         }
337         case SkBlendMode::kColor: {
338             //  SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
339             SkString setLum;
340             add_lum_function(fsBuilder, &setLum);
341             fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
342                                    srcColor, dstColor);
343             fsBuilder->codeAppendf("%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);",
344                                    outputColor, setLum.c_str(), dstColor, srcColor);
345             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
346                                    outputColor, srcColor, dstColor, dstColor, srcColor);
347             break;
348         }
349         case SkBlendMode::kLuminosity: {
350             //  SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
351             SkString setLum;
352             add_lum_function(fsBuilder, &setLum);
353             fsBuilder->codeAppendf("vec4 srcDstAlpha = %s * %s.a;",
354                                    srcColor, dstColor);
355             fsBuilder->codeAppendf("%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);",
356                                    outputColor, setLum.c_str(), dstColor, srcColor);
357             fsBuilder->codeAppendf("%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;",
358                                    outputColor, srcColor, dstColor, dstColor, srcColor);
359             break;
360         }
361         default:
362             SkFAIL("Unknown Custom Xfer mode.");
363             break;
364     }
365 }
366
367 //////////////////////////////////////////////////////////////////////////////
368 //  Porter-Duff blend helper
369 //////////////////////////////////////////////////////////////////////////////
370
371 static bool append_porterduff_term(GrGLSLFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff,
372                                    const char* colorName, const char* srcColorName,
373                                    const char* dstColorName, bool hasPrevious) {
374     if (SkXfermode::kZero_Coeff == coeff) {
375         return hasPrevious;
376     } else {
377         if (hasPrevious) {
378             fsBuilder->codeAppend(" + ");
379         }
380         fsBuilder->codeAppendf("%s", colorName);
381         switch (coeff) {
382             case SkXfermode::kOne_Coeff:
383                 break;
384             case SkXfermode::kSC_Coeff:
385                 fsBuilder->codeAppendf(" * %s", srcColorName);
386                 break;
387             case SkXfermode::kISC_Coeff:
388                 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName);
389                 break;
390             case SkXfermode::kDC_Coeff:
391                 fsBuilder->codeAppendf(" * %s", dstColorName);
392                 break;
393             case SkXfermode::kIDC_Coeff:
394                 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName);
395                 break;
396             case SkXfermode::kSA_Coeff:
397                 fsBuilder->codeAppendf(" * %s.a", srcColorName);
398                 break;
399             case SkXfermode::kISA_Coeff:
400                 fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName);
401                 break;
402             case SkXfermode::kDA_Coeff:
403                 fsBuilder->codeAppendf(" * %s.a", dstColorName);
404                 break;
405             case SkXfermode::kIDA_Coeff:
406                 fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName);
407                 break;
408             default:
409                 SkFAIL("Unsupported Blend Coeff");
410         }
411         return true;
412     }
413 }
414
415 //////////////////////////////////////////////////////////////////////////////
416
417 void GrGLSLBlend::AppendMode(GrGLSLFragmentBuilder* fsBuilder, const char* srcColor,
418                              const char* dstColor, const char* outColor,
419                              SkBlendMode mode) {
420
421     SkXfermode::Coeff srcCoeff, dstCoeff;
422     if (SkXfermode::ModeAsCoeff(mode, &srcCoeff, &dstCoeff)) {
423         fsBuilder->codeAppendf("%s = ", outColor);
424         // append src blend
425         bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, srcColor, srcColor, dstColor,
426                                                 false);
427         // append dst blend
428         if(!append_porterduff_term(fsBuilder, dstCoeff, dstColor, srcColor, dstColor, didAppend)) {
429             fsBuilder->codeAppend("vec4(0, 0, 0, 0)");
430         }
431         fsBuilder->codeAppend(";");
432     } else {
433         emit_advanced_xfermode_code(fsBuilder, srcColor, dstColor, outColor, mode);
434     }
435 }
436
437 void GrGLSLBlend::AppendRegionOp(GrGLSLFragmentBuilder* fsBuilder, const char* srcColor,
438                                  const char* dstColor, const char* outColor,
439                                  SkRegion::Op regionOp) {
440     SkXfermode::Coeff srcCoeff, dstCoeff;
441     switch (regionOp) {
442         case SkRegion::kReplace_Op:
443             srcCoeff = SkXfermode::kOne_Coeff;
444             dstCoeff = SkXfermode::kZero_Coeff;
445             break;
446         case SkRegion::kIntersect_Op:
447             srcCoeff = SkXfermode::kDC_Coeff;
448             dstCoeff = SkXfermode::kZero_Coeff;
449             break;
450         case SkRegion::kUnion_Op:
451             srcCoeff = SkXfermode::kOne_Coeff;
452             dstCoeff = SkXfermode::kISC_Coeff;
453             break;
454         case SkRegion::kXOR_Op:
455             srcCoeff = SkXfermode::kIDC_Coeff;
456             dstCoeff = SkXfermode::kISC_Coeff;
457             break;
458         case SkRegion::kDifference_Op:
459             srcCoeff = SkXfermode::kZero_Coeff;
460             dstCoeff = SkXfermode::kISC_Coeff;
461             break;
462         case SkRegion::kReverseDifference_Op:
463             srcCoeff = SkXfermode::kIDC_Coeff;
464             dstCoeff = SkXfermode::kZero_Coeff;
465             break;
466         default:
467             SkFAIL("Unsupported Op");
468             // We should never get here but to make compiler happy
469             srcCoeff = SkXfermode::kZero_Coeff;
470             dstCoeff = SkXfermode::kZero_Coeff;
471     }
472     fsBuilder->codeAppendf("%s = ", outColor);
473     // append src blend
474     bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, srcColor, srcColor, dstColor,
475                                             false);
476     // append dst blend
477     if(!append_porterduff_term(fsBuilder, dstCoeff, dstColor, srcColor, dstColor, didAppend)) {
478         fsBuilder->codeAppend("vec4(0, 0, 0, 0)");
479     }
480     fsBuilder->codeAppend(";");
481 }