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
25bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) {
26    return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
27}
28
29///////////////////////////////////////////////////////////////////////////////
30// Static helpers
31///////////////////////////////////////////////////////////////////////////////
32
33static 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
54static 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
75static 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
99static 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.
120static 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).
163static 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)
208static 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
280static 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
419GrFragmentProcessor* 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
429class GLCustomXferFP : public GrGLFragmentProcessor {
430public:
431    GLCustomXferFP(const GrFragmentProcessor&) {}
432    ~GLCustomXferFP() override {};
433
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
450    void setData(const GrGLProgramDataManager&, const GrProcessor&) override {}
451
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
460private:
461    typedef GrGLFragmentProcessor INHERITED;
462};
463
464///////////////////////////////////////////////////////////////////////////////
465
466GrCustomXferFP::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
478void GrCustomXferFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
479    GLCustomXferFP::GenKey(*this, caps, b);
480}
481
482GrGLFragmentProcessor* GrCustomXferFP::createGLInstance() const {
483    return SkNEW_ARGS(GLCustomXferFP, (*this));
484}
485
486bool GrCustomXferFP::onIsEqual(const GrFragmentProcessor& other) const {
487    const GrCustomXferFP& s = other.cast<GrCustomXferFP>();
488    return fMode == s.fMode;
489}
490
491void GrCustomXferFP::onComputeInvariantOutput(GrInvariantOutput* inout) const {
492    inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
493}
494
495GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCustomXferFP);
496GrFragmentProcessor* 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
509class CustomXP : public GrXferProcessor {
510public:
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
520    ~CustomXP() override {};
521
522    const char* name() const override { return "Custom Xfermode"; }
523
524    GrGLXferProcessor* createGLInstance() const override;
525
526    bool hasSecondaryOutput() const override { return false; }
527
528    SkXfermode::Mode mode() const { return fMode; }
529    bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
530
531    GrBlendEquation hwBlendEquation() const {
532        SkASSERT(this->hasHWBlendEquation());
533        return fHWBlendEquation;
534    }
535
536private:
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
563GrXPFactory* 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
573class GLCustomXP : public GrGLXferProcessor {
574public:
575    GLCustomXP(const GrXferProcessor&) {}
576    ~GLCustomXP() override {}
577
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
594private:
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
622    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
623
624    typedef GrGLFragmentProcessor INHERITED;
625};
626
627///////////////////////////////////////////////////////////////////////////////
628
629CustomXP::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
637void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
638    GLCustomXP::GenKey(*this, caps, b);
639}
640
641GrGLXferProcessor* CustomXP::createGLInstance() const {
642    SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
643    return SkNEW_ARGS(GLCustomXP, (*this));
644}
645
646bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
647    const CustomXP& s = other.cast<CustomXP>();
648    return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
649}
650
651GrXferProcessor::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
766bool 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
776void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
777    if (this->hasHWBlendEquation()) {
778        blendInfo->fEquation = this->hwBlendEquation();
779    }
780}
781
782///////////////////////////////////////////////////////////////////////////////
783
784GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
785    : fMode(mode) {
786    this->initClassID<GrCustomXPFactory>();
787}
788
789GrXferProcessor*
790GrCustomXPFactory::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
797bool 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
811void GrCustomXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
812                                               const GrProcOptInfo& coveragePOI,
813                                               GrXPFactory::InvariantOutput* output) const {
814    output->fWillBlendWithDst = true;
815    output->fBlendedColorFlags = 0;
816}
817
818GR_DEFINE_XP_FACTORY_TEST(GrCustomXPFactory);
819GrXPFactory* 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