blob: b15e2c2b9a133ad47bae7cd672a65bec36a2fbee [file] [log] [blame]
egdaniel0063a9b2015-01-15 10:52:32 -08001/*
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"
cdalton8917d622015-05-06 13:40:21 -070020#include "gl/GrGLGpu.h"
egdaniel0063a9b2015-01-15 10:52:32 -080021#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
cdalton8917d622015-05-06 13:40:21 -070033static 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(kTotalGrBlendEquationCount == SkXfermode::kLastMode + 1 + kOffset);
52}
53
egdaniel29bee0f2015-04-29 11:54:42 -070054static void hard_light(GrGLFragmentBuilder* fsBuilder,
egdaniel0063a9b2015-01-15 10:52:32 -080055 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
egdaniel29bee0f2015-04-29 11:54:42 -070075static void color_dodge_component(GrGLFragmentBuilder* fsBuilder,
egdaniel0063a9b2015-01-15 10:52:32 -080076 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
egdaniel29bee0f2015-04-29 11:54:42 -070099static void color_burn_component(GrGLFragmentBuilder* fsBuilder,
egdaniel0063a9b2015-01-15 10:52:32 -0800100 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.
egdaniel29bee0f2015-04-29 11:54:42 -0700120static void soft_light_component_pos_dst_alpha(GrGLFragmentBuilder* fsBuilder,
egdaniel0063a9b2015-01-15 10:52:32 -0800121 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).
egdaniel29bee0f2015-04-29 11:54:42 -0700163static void add_lum_function(GrGLFragmentBuilder* fsBuilder, SkString* setLumFunction) {
egdaniel0063a9b2015-01-15 10:52:32 -0800164 // 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)
egdaniel29bee0f2015-04-29 11:54:42 -0700208static void add_sat_function(GrGLFragmentBuilder* fsBuilder, SkString* setSatFunction) {
egdaniel0063a9b2015-01-15 10:52:32 -0800209 // 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,
egdaniel29bee0f2015-04-29 11:54:42 -0700281 GrGLFragmentBuilder* fsBuilder,
egdaniel0063a9b2015-01-15 10:52:32 -0800282 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&) {}
mtklein36352bf2015-03-25 18:17:31 -0700432 ~GLCustomXferFP() override {};
egdaniel0063a9b2015-01-15 10:52:32 -0800433
434 void emitCode(GrGLFPBuilder* builder,
435 const GrFragmentProcessor& fp,
436 const char* outputColor,
437 const char* inputColor,
438 const TransformedCoordsArray& coords,
mtklein36352bf2015-03-25 18:17:31 -0700439 const TextureSamplerArray& samplers) override {
egdaniel0063a9b2015-01-15 10:52:32 -0800440 SkXfermode::Mode mode = fp.cast<GrCustomXferFP>().mode();
egdaniel29bee0f2015-04-29 11:54:42 -0700441 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
egdaniel54f0e9d2015-01-16 06:29:47 -0800442 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(";");
egdaniel0063a9b2015-01-15 10:52:32 -0800446
447 emit_custom_xfermode_code(mode, fsBuilder, outputColor, inputColor, dstColor);
448 }
449
mtklein36352bf2015-03-25 18:17:31 -0700450 void setData(const GrGLProgramDataManager&, const GrProcessor&) override {}
egdaniel0063a9b2015-01-15 10:52:32 -0800451
jvanverthcfc18862015-04-28 08:48:20 -0700452 static void GenKey(const GrFragmentProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
egdaniel0063a9b2015-01-15 10:52:32 -0800453 // 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>();
egdaniel54f0e9d2015-01-16 06:29:47 -0800469
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);
egdaniel0063a9b2015-01-15 10:52:32 -0800476}
477
jvanverthcfc18862015-04-28 08:48:20 -0700478void GrCustomXferFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
egdaniel0063a9b2015-01-15 10:52:32 -0800479 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&,
egdaniel54f0e9d2015-01-16 06:29:47 -0800499 GrTexture* textures[]) {
egdaniel0063a9b2015-01-15 10:52:32 -0800500 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
501
egdaniel54f0e9d2015-01-16 06:29:47 -0800502 return SkNEW_ARGS(GrCustomXferFP, (static_cast<SkXfermode::Mode>(mode), textures[0]));
503}
504
505///////////////////////////////////////////////////////////////////////////////
506// Xfer Processor
507///////////////////////////////////////////////////////////////////////////////
508
egdaniel41d4f092015-02-09 07:51:00 -0800509class 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
mtklein36352bf2015-03-25 18:17:31 -0700520 ~CustomXP() override {};
egdaniel41d4f092015-02-09 07:51:00 -0800521
mtklein36352bf2015-03-25 18:17:31 -0700522 const char* name() const override { return "Custom Xfermode"; }
egdaniel41d4f092015-02-09 07:51:00 -0800523
mtklein36352bf2015-03-25 18:17:31 -0700524 GrGLXferProcessor* createGLInstance() const override;
egdaniel41d4f092015-02-09 07:51:00 -0800525
mtklein36352bf2015-03-25 18:17:31 -0700526 bool hasSecondaryOutput() const override { return false; }
egdaniel41d4f092015-02-09 07:51:00 -0800527
528 GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
529 const GrProcOptInfo& coveragePOI,
530 bool doesStencilWrite,
531 GrColor* overrideColor,
mtklein36352bf2015-03-25 18:17:31 -0700532 const GrDrawTargetCaps& caps) override;
egdaniel41d4f092015-02-09 07:51:00 -0800533
egdaniel41d4f092015-02-09 07:51:00 -0800534 SkXfermode::Mode mode() const { return fMode; }
cdalton8917d622015-05-06 13:40:21 -0700535 bool hasCoverage() const { return fHasCoverage; }
536 bool hasHWBlendEquation() const { return kInvalid_GrBlendEquation != fHWBlendEquation; }
537
538 GrBlendEquation hwBlendEquation() const {
539 SkASSERT(this->hasHWBlendEquation());
540 return fHWBlendEquation;
541 }
egdaniel41d4f092015-02-09 07:51:00 -0800542
543private:
544 CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
545
jvanverthcfc18862015-04-28 08:48:20 -0700546 void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
egdaniel41d4f092015-02-09 07:51:00 -0800547
cdalton8917d622015-05-06 13:40:21 -0700548 bool onWillNeedXferBarrier(const GrRenderTarget* rt,
549 const GrDrawTargetCaps& caps,
550 GrXferBarrierType* outBarrierType) const override;
551
552 void onGetBlendInfo(BlendInfo*) const override;
553
mtklein36352bf2015-03-25 18:17:31 -0700554 bool onIsEqual(const GrXferProcessor& xpBase) const override;
egdaniel41d4f092015-02-09 07:51:00 -0800555
556 SkXfermode::Mode fMode;
cdalton8917d622015-05-06 13:40:21 -0700557 bool fHasCoverage;
558 GrBlendEquation fHWBlendEquation;
egdaniel41d4f092015-02-09 07:51:00 -0800559
560 typedef GrXferProcessor INHERITED;
561};
562
563///////////////////////////////////////////////////////////////////////////////
564
egdaniel54f0e9d2015-01-16 06:29:47 -0800565GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) {
566 if (!GrCustomXfermode::IsSupportedMode(mode)) {
567 return NULL;
568 } else {
569 return SkNEW_ARGS(GrCustomXPFactory, (mode));
570 }
571}
572
573///////////////////////////////////////////////////////////////////////////////
574
575class GLCustomXP : public GrGLXferProcessor {
576public:
577 GLCustomXP(const GrXferProcessor&) {}
mtklein36352bf2015-03-25 18:17:31 -0700578 ~GLCustomXP() override {}
egdaniel54f0e9d2015-01-16 06:29:47 -0800579
cdalton8917d622015-05-06 13:40:21 -0700580 static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
581 const CustomXP& xp = p.cast<CustomXP>();
582 uint32_t key = xp.numTextures();
bsalomon50785a32015-02-06 07:02:37 -0800583 SkASSERT(key <= 1);
cdalton8917d622015-05-06 13:40:21 -0700584 key |= xp.hasCoverage() << 1;
585 if (xp.hasHWBlendEquation()) {
586 SkASSERT(caps.advBlendEqInteraction() > 0); // 0 will mean !xp.hasHWBlendEquation().
587 key |= caps.advBlendEqInteraction() << 2;
588 }
589 if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
590 GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
591 key |= xp.mode() << 4;
592 }
bsalomon50785a32015-02-06 07:02:37 -0800593 b->add32(key);
594 }
595
596private:
mtklein36352bf2015-03-25 18:17:31 -0700597 void onEmitCode(const EmitArgs& args) override {
cdalton8917d622015-05-06 13:40:21 -0700598 const CustomXP& xp = args.fXP.cast<CustomXP>();
egdaniel29bee0f2015-04-29 11:54:42 -0700599 GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
egdaniel54f0e9d2015-01-16 06:29:47 -0800600
cdalton8917d622015-05-06 13:40:21 -0700601 if (xp.hasHWBlendEquation()) {
602 // The blend mode will be implemented in hardware; only output the src color.
603 fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
604 if (xp.hasCoverage()) {
605 // Do coverage modulation by multiplying it into the src color before blending.
606 // (See getOptimizations())
607 fsBuilder->codeAppendf("%s = %s * %s;",
608 args.fOutputPrimary, args.fInputCoverage, args.fInputColor);
609 } else {
610 fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
611 }
612 } else {
613 const char* dstColor = fsBuilder->dstColor();
614 emit_custom_xfermode_code(xp.mode(), fsBuilder, args.fOutputPrimary, args.fInputColor,
615 dstColor);
616 if (xp.hasCoverage()) {
617 fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
618 args.fOutputPrimary, args.fOutputPrimary,
619 args.fInputCoverage, args.fInputCoverage, dstColor);
620 }
621 }
egdaniel54f0e9d2015-01-16 06:29:47 -0800622 }
623
mtklein36352bf2015-03-25 18:17:31 -0700624 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
egdaniel54f0e9d2015-01-16 06:29:47 -0800625
egdaniel54f0e9d2015-01-16 06:29:47 -0800626 typedef GrGLFragmentProcessor INHERITED;
627};
628
629///////////////////////////////////////////////////////////////////////////////
630
egdaniel41d4f092015-02-09 07:51:00 -0800631CustomXP::CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy,
632 bool willReadDstColor)
cdalton8917d622015-05-06 13:40:21 -0700633 : INHERITED(dstCopy, willReadDstColor),
634 fMode(mode),
635 fHasCoverage(true),
636 fHWBlendEquation(kInvalid_GrBlendEquation) {
egdaniel41d4f092015-02-09 07:51:00 -0800637 this->initClassID<CustomXP>();
egdaniel54f0e9d2015-01-16 06:29:47 -0800638}
639
jvanverthcfc18862015-04-28 08:48:20 -0700640void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
egdaniel54f0e9d2015-01-16 06:29:47 -0800641 GLCustomXP::GenKey(*this, caps, b);
642}
643
egdaniel41d4f092015-02-09 07:51:00 -0800644GrGLXferProcessor* CustomXP::createGLInstance() const {
cdalton8917d622015-05-06 13:40:21 -0700645 SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
egdaniel54f0e9d2015-01-16 06:29:47 -0800646 return SkNEW_ARGS(GLCustomXP, (*this));
647}
648
egdaniel41d4f092015-02-09 07:51:00 -0800649bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
650 const CustomXP& s = other.cast<CustomXP>();
cdalton8917d622015-05-06 13:40:21 -0700651 return fMode == s.fMode &&
652 fHasCoverage == s.fHasCoverage &&
653 fHWBlendEquation == s.fHWBlendEquation;
egdaniel54f0e9d2015-01-16 06:29:47 -0800654}
655
egdaniel41d4f092015-02-09 07:51:00 -0800656GrXferProcessor::OptFlags CustomXP::getOptimizations(const GrProcOptInfo& colorPOI,
egdaniel54f0e9d2015-01-16 06:29:47 -0800657 const GrProcOptInfo& coveragePOI,
658 bool doesStencilWrite,
659 GrColor* overrideColor,
660 const GrDrawTargetCaps& caps) {
cdalton8917d622015-05-06 13:40:21 -0700661 /*
662 Most the optimizations we do here are based on tweaking alpha for coverage.
663
664 The general SVG blend equation is defined in the spec as follows:
665
666 Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
667 Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
668
669 (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
670 and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
671 RGB colors.)
672
673 For every blend mode supported by this class, i.e. the "advanced" blend
674 modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
675
676 It can be shown that when X=Y=Z=1, these equations can modulate alpha for
677 coverage.
678
679
680 == Color ==
681
682 We substitute Y=Z=1 and define a blend() function that calculates Dca' in
683 terms of premultiplied alpha only:
684
685 blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
686 Sca : if Da == 0,
687 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0}
688
689 And for coverage modulation, we use a post blend src-over model:
690
691 Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
692
693 (Where f is the fractional coverage.)
694
695 Next we show that canTweakAlphaForCoverage() is true by proving the
696 following relationship:
697
698 blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
699
700 General case (f,Sa,Da != 0):
701
702 f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
703 = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da != 0, definition of blend()]
704 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
705 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
706 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
707 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
708 = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
709 = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0]
710 = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()]
711
712 Corner cases (Sa=0, Da=0, and f=0):
713
714 Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
715 = f * Dca + (1-f) * Dca [Sa=0, definition of blend()]
716 = Dca
717 = blend(0, Dca, 0, Da) [definition of blend()]
718 = blend(f*Sca, Dca, f*Sa, Da) [Sa=0]
719
720 Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
721 = f * Sca + (1-f) * Dca [Da=0, definition of blend()]
722 = f * Sca [Da=0]
723 = blend(f*Sca, 0, f*Sa, 0) [definition of blend()]
724 = blend(f*Sca, Dca, f*Sa, Da) [Da=0]
725
726 f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
727 = Dca [f=0]
728 = blend(0, Dca, 0, Da) [definition of blend()]
729 = blend(f*Sca, Dca, f*Sa, Da) [f=0]
730
731 == Alpha ==
732
733 We substitute X=Y=Z=1 and define a blend() function that calculates Da':
734
735 blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
736 = Sa * Da + Sa - Sa * Da + Da - Da * Sa
737 = Sa + Da - Sa * Da
738
739 We use the same model for coverage modulation as we did with color:
740
741 Da'' = f * blend(Sa, Da) + (1-f) * Da
742
743 And show that canTweakAlphaForCoverage() is true by proving the following
744 relationship:
745
746 blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
747
748
749 f * blend(Sa, Da) + (1-f) * Da
750 = f * (Sa + Da - Sa * Da) + (1-f) * Da
751 = f*Sa + f*Da - f*Sa * Da + Da - f*Da
752 = f*Sa - f*Sa * Da + Da
753 = f*Sa + Da - f*Sa * Da
754 = blend(f*Sa, Da)
755 */
756
757 OptFlags flags = kNone_Opt;
758 if (colorPOI.allStagesMultiplyInput()) {
759 flags = flags | kCanTweakAlphaForCoverage_OptFlag;
760 }
761 if (coveragePOI.isSolidWhite()) {
762 flags = flags | kIgnoreCoverage_OptFlag;
763 fHasCoverage = false;
764 }
765 if (caps.advancedBlendEquationSupport() && !coveragePOI.isFourChannelOutput()) {
766 // This blend mode can be implemented in hardware.
767 fHWBlendEquation = hw_blend_equation(fMode);
768 }
769 return flags;
770}
771
772bool CustomXP::onWillNeedXferBarrier(const GrRenderTarget* rt,
773 const GrDrawTargetCaps& caps,
774 GrXferBarrierType* outBarrierType) const {
775 if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
776 *outBarrierType = kBlend_GrXferBarrierType;
777 return true;
778 }
779 return false;
780}
781
782void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
783 if (this->hasHWBlendEquation()) {
784 blendInfo->fEquation = this->hwBlendEquation();
785 }
egdaniel54f0e9d2015-01-16 06:29:47 -0800786}
787
788///////////////////////////////////////////////////////////////////////////////
789
790GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
791 : fMode(mode) {
792 this->initClassID<GrCustomXPFactory>();
793}
794
egdaniel41d4f092015-02-09 07:51:00 -0800795GrXferProcessor*
egdaniel3ad65702015-02-17 11:15:47 -0800796GrCustomXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
797 const GrProcOptInfo& colorPOI,
egdaniel41d4f092015-02-09 07:51:00 -0800798 const GrProcOptInfo& coveragePOI,
799 const GrDeviceCoordTexture* dstCopy) const {
egdaniel3ad65702015-02-17 11:15:47 -0800800 return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI));
egdaniel41d4f092015-02-09 07:51:00 -0800801}
802
cdalton8917d622015-05-06 13:40:21 -0700803bool GrCustomXPFactory::willReadDstColor(const GrDrawTargetCaps& caps,
804 const GrProcOptInfo& colorPOI,
805 const GrProcOptInfo& coveragePOI) const {
806 if (!caps.advancedBlendEquationSupport()) {
807 // No hardware support for advanced blend equations; we will need to do it in the shader.
808 return true;
809 }
810 if (coveragePOI.isFourChannelOutput()) {
811 // Advanced blend equations can't tweak alpha for RGB coverage.
812 return true;
813 }
814 return false;
815}
egdaniel41d4f092015-02-09 07:51:00 -0800816
egdaniel54f0e9d2015-01-16 06:29:47 -0800817void GrCustomXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
818 const GrProcOptInfo& coveragePOI,
819 GrXPFactory::InvariantOutput* output) const {
820 output->fWillBlendWithDst = true;
821 output->fBlendedColorFlags = 0;
822}
823
824GR_DEFINE_XP_FACTORY_TEST(GrCustomXPFactory);
825GrXPFactory* GrCustomXPFactory::TestCreate(SkRandom* rand,
826 GrContext*,
827 const GrDrawTargetCaps&,
828 GrTexture*[]) {
829 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
830
831 return SkNEW_ARGS(GrCustomXPFactory, (static_cast<SkXfermode::Mode>(mode)));
egdaniel0063a9b2015-01-15 10:52:32 -0800832}
833