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