blob: 1fdd3b781ff93c2c8d5b660b3700547535779ad6 [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);
bsalomonf7cc8772015-05-11 11:21:14 -070051 GR_STATIC_ASSERT(kGrBlendEquationCnt == SkXfermode::kLastMode + 1 + kOffset);
cdalton8917d622015-05-06 13:40:21 -070052}
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
egdaniel41d4f092015-02-09 07:51:00 -0800528 SkXfermode::Mode mode() const { return fMode; }
bsalomonf7cc8772015-05-11 11:21:14 -0700529 bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
cdalton8917d622015-05-06 13:40:21 -0700530
531 GrBlendEquation hwBlendEquation() const {
532 SkASSERT(this->hasHWBlendEquation());
533 return fHWBlendEquation;
534 }
egdaniel41d4f092015-02-09 07:51:00 -0800535
536private:
537 CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
538
egdanielc19cdc22015-05-10 08:45:18 -0700539 GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
540 const GrProcOptInfo& coveragePOI,
541 bool doesStencilWrite,
542 GrColor* overrideColor,
543 const GrDrawTargetCaps& caps) override;
544
jvanverthcfc18862015-04-28 08:48:20 -0700545 void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
egdaniel41d4f092015-02-09 07:51:00 -0800546
cdalton8917d622015-05-06 13:40:21 -0700547 bool onWillNeedXferBarrier(const GrRenderTarget* rt,
548 const GrDrawTargetCaps& caps,
549 GrXferBarrierType* outBarrierType) const override;
550
551 void onGetBlendInfo(BlendInfo*) const override;
552
mtklein36352bf2015-03-25 18:17:31 -0700553 bool onIsEqual(const GrXferProcessor& xpBase) const override;
egdaniel41d4f092015-02-09 07:51:00 -0800554
555 SkXfermode::Mode fMode;
cdalton8917d622015-05-06 13:40:21 -0700556 GrBlendEquation fHWBlendEquation;
egdaniel41d4f092015-02-09 07:51:00 -0800557
558 typedef GrXferProcessor INHERITED;
559};
560
561///////////////////////////////////////////////////////////////////////////////
562
egdaniel54f0e9d2015-01-16 06:29:47 -0800563GrXPFactory* 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&) {}
mtklein36352bf2015-03-25 18:17:31 -0700576 ~GLCustomXP() override {}
egdaniel54f0e9d2015-01-16 06:29:47 -0800577
cdalton8917d622015-05-06 13:40:21 -0700578 static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
579 const CustomXP& xp = p.cast<CustomXP>();
580 uint32_t key = xp.numTextures();
bsalomon50785a32015-02-06 07:02:37 -0800581 SkASSERT(key <= 1);
egdanielc19cdc22015-05-10 08:45:18 -0700582 key |= xp.readsCoverage() << 1;
cdalton8917d622015-05-06 13:40:21 -0700583 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 }
bsalomon50785a32015-02-06 07:02:37 -0800591 b->add32(key);
592 }
593
594private:
mtklein36352bf2015-03-25 18:17:31 -0700595 void onEmitCode(const EmitArgs& args) override {
cdalton8917d622015-05-06 13:40:21 -0700596 const CustomXP& xp = args.fXP.cast<CustomXP>();
egdaniel29bee0f2015-04-29 11:54:42 -0700597 GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
egdaniel54f0e9d2015-01-16 06:29:47 -0800598
cdalton8917d622015-05-06 13:40:21 -0700599 if (xp.hasHWBlendEquation()) {
600 // The blend mode will be implemented in hardware; only output the src color.
601 fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
egdanielc19cdc22015-05-10 08:45:18 -0700602 if (xp.readsCoverage()) {
cdalton8917d622015-05-06 13:40:21 -0700603 // 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);
egdanielc19cdc22015-05-10 08:45:18 -0700614 if (xp.readsCoverage()) {
cdalton8917d622015-05-06 13:40:21 -0700615 fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
616 args.fOutputPrimary, args.fOutputPrimary,
617 args.fInputCoverage, args.fInputCoverage, dstColor);
618 }
619 }
egdaniel54f0e9d2015-01-16 06:29:47 -0800620 }
621
mtklein36352bf2015-03-25 18:17:31 -0700622 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
egdaniel54f0e9d2015-01-16 06:29:47 -0800623
egdaniel54f0e9d2015-01-16 06:29:47 -0800624 typedef GrGLFragmentProcessor INHERITED;
625};
626
627///////////////////////////////////////////////////////////////////////////////
628
egdaniel41d4f092015-02-09 07:51:00 -0800629CustomXP::CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy,
630 bool willReadDstColor)
cdalton8917d622015-05-06 13:40:21 -0700631 : INHERITED(dstCopy, willReadDstColor),
632 fMode(mode),
bsalomonf7cc8772015-05-11 11:21:14 -0700633 fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
egdaniel41d4f092015-02-09 07:51:00 -0800634 this->initClassID<CustomXP>();
egdaniel54f0e9d2015-01-16 06:29:47 -0800635}
636
jvanverthcfc18862015-04-28 08:48:20 -0700637void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
egdaniel54f0e9d2015-01-16 06:29:47 -0800638 GLCustomXP::GenKey(*this, caps, b);
639}
640
egdaniel41d4f092015-02-09 07:51:00 -0800641GrGLXferProcessor* CustomXP::createGLInstance() const {
cdalton8917d622015-05-06 13:40:21 -0700642 SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
egdaniel54f0e9d2015-01-16 06:29:47 -0800643 return SkNEW_ARGS(GLCustomXP, (*this));
644}
645
egdaniel41d4f092015-02-09 07:51:00 -0800646bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
647 const CustomXP& s = other.cast<CustomXP>();
egdanielc19cdc22015-05-10 08:45:18 -0700648 return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
egdaniel54f0e9d2015-01-16 06:29:47 -0800649}
650
egdanielc19cdc22015-05-10 08:45:18 -0700651GrXferProcessor::OptFlags CustomXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
egdaniel54f0e9d2015-01-16 06:29:47 -0800652 const GrProcOptInfo& coveragePOI,
653 bool doesStencilWrite,
654 GrColor* overrideColor,
655 const GrDrawTargetCaps& caps) {
cdalton8917d622015-05-06 13:40:21 -0700656 /*
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()) {
cdalton32117702015-05-11 11:21:23 -0700754 flags |= kCanTweakAlphaForCoverage_OptFlag;
cdalton8917d622015-05-06 13:40:21 -0700755 }
756 if (coveragePOI.isSolidWhite()) {
cdalton32117702015-05-11 11:21:23 -0700757 flags |= kIgnoreCoverage_OptFlag;
cdalton8917d622015-05-06 13:40:21 -0700758 }
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 }
egdaniel54f0e9d2015-01-16 06:29:47 -0800780}
781
782///////////////////////////////////////////////////////////////////////////////
783
784GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
785 : fMode(mode) {
786 this->initClassID<GrCustomXPFactory>();
787}
788
egdaniel41d4f092015-02-09 07:51:00 -0800789GrXferProcessor*
egdaniel3ad65702015-02-17 11:15:47 -0800790GrCustomXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
791 const GrProcOptInfo& colorPOI,
egdaniel41d4f092015-02-09 07:51:00 -0800792 const GrProcOptInfo& coveragePOI,
793 const GrDeviceCoordTexture* dstCopy) const {
egdaniel3ad65702015-02-17 11:15:47 -0800794 return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI));
egdaniel41d4f092015-02-09 07:51:00 -0800795}
796
cdalton8917d622015-05-06 13:40:21 -0700797bool 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}
egdaniel41d4f092015-02-09 07:51:00 -0800810
egdaniel54f0e9d2015-01-16 06:29:47 -0800811void 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)));
egdaniel0063a9b2015-01-15 10:52:32 -0800826}
827