blob: 8a3218fcbd0087f0d264f5550412c8a6171acf9c [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkXfermode.h"
commit-bot@chromium.orgc524e982014-04-09 15:43:46 +000011#include "SkXfermode_opts_SSE2.h"
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +000012#include "SkXfermode_proccoeff.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkColorPriv.h"
commit-bot@chromium.org97de3572014-05-29 20:13:22 +000014#include "SkLazyPtr.h"
reed@google.com4b163ed2012-08-07 21:35:13 +000015#include "SkMathPriv.h"
commit-bot@chromium.org140950c2014-03-28 20:04:11 +000016#include "SkReadBuffer.h"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +000017#include "SkString.h"
commit-bot@chromium.orgcd7992b2013-10-17 16:29:34 +000018#include "SkUtilsArm.h"
commit-bot@chromium.org140950c2014-03-28 20:04:11 +000019#include "SkWriteBuffer.h"
commit-bot@chromium.orgcd7992b2013-10-17 16:29:34 +000020
21#if !SK_ARM_NEON_IS_NONE
22#include "SkXfermode_opts_arm_neon.h"
23#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000024
25#define SkAlphaMulAlpha(a, b) SkMulDiv255Round(a, b)
26
reed@android.comfc25abd2009-01-15 14:38:33 +000027#if 0
reed@android.com8a1c16f2008-12-17 15:59:43 +000028// idea for higher precision blends in xfer procs (and slightly faster)
29// see DstATop as a probable caller
30static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
31 SkASSERT(a <= 255);
32 SkASSERT(b <= 255);
33 SkASSERT(c <= 255);
34 SkASSERT(d <= 255);
35 unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
36 unsigned result = (prod + (prod >> 8)) >> 8;
37 SkASSERT(result <= 255);
38 return result;
39}
reed@android.comfc25abd2009-01-15 14:38:33 +000040#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000041
deanm@chromium.orgda946992009-07-03 12:54:24 +000042static inline unsigned saturated_add(unsigned a, unsigned b) {
reed@android.com543ed932009-04-24 12:43:40 +000043 SkASSERT(a <= 255);
44 SkASSERT(b <= 255);
45 unsigned sum = a + b;
46 if (sum > 255) {
47 sum = 255;
48 }
49 return sum;
50}
51
deanm@chromium.orgda946992009-07-03 12:54:24 +000052static inline int clamp_signed_byte(int n) {
reed@android.coma0f5d152009-06-22 17:38:10 +000053 if (n < 0) {
54 n = 0;
55 } else if (n > 255) {
56 n = 255;
57 }
58 return n;
59}
60
deanm@chromium.orgda946992009-07-03 12:54:24 +000061static inline int clamp_div255round(int prod) {
reed@android.coma0f5d152009-06-22 17:38:10 +000062 if (prod <= 0) {
63 return 0;
64 } else if (prod >= 255*255) {
65 return 255;
66 } else {
67 return SkDiv255Round(prod);
68 }
69}
70
reed@android.com8a1c16f2008-12-17 15:59:43 +000071///////////////////////////////////////////////////////////////////////////////
72
reed@android.com8a1c16f2008-12-17 15:59:43 +000073// kClear_Mode, //!< [0, 0]
74static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
75 return 0;
76}
77
78// kSrc_Mode, //!< [Sa, Sc]
79static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) {
80 return src;
81}
82
83// kDst_Mode, //!< [Da, Dc]
84static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) {
85 return dst;
86}
87
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000088// kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Sc + (1 - Sa)*Dc]
reed@android.com8a1c16f2008-12-17 15:59:43 +000089static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +000090#if 0
91 // this is the old, more-correct way, but it doesn't guarantee that dst==255
92 // will always stay opaque
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
reed@android.com1116fb22009-03-03 20:31:12 +000094#else
95 // this is slightly faster, but more importantly guarantees that dst==255
96 // will always stay opaque
97 return src + SkAlphaMulQ(dst, 256 - SkGetPackedA32(src));
98#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000099}
100
reed@android.com1116fb22009-03-03 20:31:12 +0000101// kDstOver_Mode, //!< [Sa + Da - Sa*Da, Dc + (1 - Da)*Sc]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +0000103 // this is the reverse of srcover, just flipping src and dst
104 // see srcover's comment about the 256 for opaqueness guarantees
105 return dst + SkAlphaMulQ(src, 256 - SkGetPackedA32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106}
107
108// kSrcIn_Mode, //!< [Sa * Da, Sc * Da]
109static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) {
110 return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst)));
111}
112
113// kDstIn_Mode, //!< [Sa * Da, Sa * Dc]
114static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) {
115 return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src)));
116}
117
118// kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)]
119static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) {
120 return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst)));
121}
122
123// kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)]
124static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) {
125 return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
126}
127
128// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc]
129static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) {
130 unsigned sa = SkGetPackedA32(src);
131 unsigned da = SkGetPackedA32(dst);
132 unsigned isa = 255 - sa;
133
134 return SkPackARGB32(da,
135 SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
136 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
137 SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
138 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
139 SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
140 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
141}
142
143// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
144static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) {
145 unsigned sa = SkGetPackedA32(src);
146 unsigned da = SkGetPackedA32(dst);
147 unsigned ida = 255 - da;
148
149 return SkPackARGB32(sa,
150 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
151 SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
152 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
153 SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
154 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
155 SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
156}
157
158// kXor_Mode [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
159static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) {
160 unsigned sa = SkGetPackedA32(src);
161 unsigned da = SkGetPackedA32(dst);
162 unsigned isa = 255 - sa;
163 unsigned ida = 255 - da;
164
165 return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
166 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
167 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
168 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
169 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
170 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
171 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
172}
173
reed@android.coma0f5d152009-06-22 17:38:10 +0000174///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
reed@android.coma0f5d152009-06-22 17:38:10 +0000176// kPlus_Mode
177static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000178 unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
deanm@chromium.orgda946992009-07-03 12:54:24 +0000179 unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
180 unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
181 unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
reed@android.coma0f5d152009-06-22 17:38:10 +0000182 return SkPackARGB32(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183}
184
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000185// kModulate_Mode
186static SkPMColor modulate_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
188 int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
189 int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
190 int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
191 return SkPackARGB32(a, r, g, b);
192}
193
reed@android.coma0f5d152009-06-22 17:38:10 +0000194static inline int srcover_byte(int a, int b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 return a + b - SkAlphaMulAlpha(a, b);
196}
reed@google.com25cfa692013-02-04 20:06:00 +0000197
198// kMultiply_Mode
199// B(Cb, Cs) = Cb x Cs
200// multiply uses its own version of blendfunc_byte because sa and da are not needed
201static int blendfunc_multiply_byte(int sc, int dc, int sa, int da) {
202 return clamp_div255round(sc * (255 - da) + dc * (255 - sa) + sc * dc);
203}
204
205static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
206 int sa = SkGetPackedA32(src);
207 int da = SkGetPackedA32(dst);
208 int a = srcover_byte(sa, da);
209 int r = blendfunc_multiply_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
210 int g = blendfunc_multiply_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
211 int b = blendfunc_multiply_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
212 return SkPackARGB32(a, r, g, b);
213}
214
215// kScreen_Mode
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000217 int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
218 int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
219 int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
220 int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 return SkPackARGB32(a, r, g, b);
222}
223
reed@android.coma0f5d152009-06-22 17:38:10 +0000224// kOverlay_Mode
225static inline int overlay_byte(int sc, int dc, int sa, int da) {
226 int tmp = sc * (255 - da) + dc * (255 - sa);
227 int rc;
228 if (2 * dc <= da) {
229 rc = 2 * sc * dc;
230 } else {
231 rc = sa * da - 2 * (da - dc) * (sa - sc);
232 }
233 return clamp_div255round(rc + tmp);
234}
235static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
236 int sa = SkGetPackedA32(src);
237 int da = SkGetPackedA32(dst);
238 int a = srcover_byte(sa, da);
239 int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
240 int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
241 int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
242 return SkPackARGB32(a, r, g, b);
243}
244
245// kDarken_Mode
246static inline int darken_byte(int sc, int dc, int sa, int da) {
247 int sd = sc * da;
248 int ds = dc * sa;
249 if (sd < ds) {
250 // srcover
251 return sc + dc - SkDiv255Round(ds);
252 } else {
253 // dstover
254 return dc + sc - SkDiv255Round(sd);
255 }
256}
257static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
258 int sa = SkGetPackedA32(src);
259 int da = SkGetPackedA32(dst);
260 int a = srcover_byte(sa, da);
261 int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
262 int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
263 int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
264 return SkPackARGB32(a, r, g, b);
265}
266
267// kLighten_Mode
268static inline int lighten_byte(int sc, int dc, int sa, int da) {
269 int sd = sc * da;
270 int ds = dc * sa;
271 if (sd > ds) {
272 // srcover
273 return sc + dc - SkDiv255Round(ds);
274 } else {
275 // dstover
276 return dc + sc - SkDiv255Round(sd);
277 }
278}
279static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
280 int sa = SkGetPackedA32(src);
281 int da = SkGetPackedA32(dst);
282 int a = srcover_byte(sa, da);
283 int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
284 int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
285 int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
286 return SkPackARGB32(a, r, g, b);
287}
288
289// kColorDodge_Mode
290static inline int colordodge_byte(int sc, int dc, int sa, int da) {
291 int diff = sa - sc;
292 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000293 if (0 == dc) {
294 return SkAlphaMulAlpha(sc, 255 - da);
295 } else if (0 == diff) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000296 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000297 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000298 diff = dc * sa / diff;
299 rc = sa * ((da < diff) ? da : diff) + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000300 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000301 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000302}
303static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000304 int sa = SkGetPackedA32(src);
305 int da = SkGetPackedA32(dst);
306 int a = srcover_byte(sa, da);
307 int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
308 int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
309 int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.coma0f5d152009-06-22 17:38:10 +0000310 return SkPackARGB32(a, r, g, b);
311}
312
313// kColorBurn_Mode
314static inline int colorburn_byte(int sc, int dc, int sa, int da) {
315 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000316 if (dc == da) {
317 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000318 } else if (0 == sc) {
319 return SkAlphaMulAlpha(dc, 255 - sa);
320 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000321 int tmp = (da - dc) * sa / sc;
322 rc = sa * (da - ((da < tmp) ? da : tmp))
323 + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000324 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000325 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000326}
327static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000328 int sa = SkGetPackedA32(src);
329 int da = SkGetPackedA32(dst);
330 int a = srcover_byte(sa, da);
331 int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
332 int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
333 int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
334 return SkPackARGB32(a, r, g, b);
335}
336
337// kHardLight_Mode
338static inline int hardlight_byte(int sc, int dc, int sa, int da) {
339 int rc;
340 if (2 * sc <= sa) {
341 rc = 2 * sc * dc;
342 } else {
343 rc = sa * da - 2 * (da - dc) * (sa - sc);
344 }
345 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
346}
347static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
348 int sa = SkGetPackedA32(src);
349 int da = SkGetPackedA32(dst);
350 int a = srcover_byte(sa, da);
351 int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
352 int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
353 int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
354 return SkPackARGB32(a, r, g, b);
355}
356
357// returns 255 * sqrt(n/255)
358static U8CPU sqrt_unit_byte(U8CPU n) {
359 return SkSqrtBits(n, 15+4);
360}
361
362// kSoftLight_Mode
363static inline int softlight_byte(int sc, int dc, int sa, int da) {
364 int m = da ? dc * 256 / da : 0;
365 int rc;
366 if (2 * sc <= sa) {
367 rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
368 } else if (4 * dc <= da) {
369 int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
370 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
371 } else {
372 int tmp = sqrt_unit_byte(m) - m;
373 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
374 }
375 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
376}
377static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
378 int sa = SkGetPackedA32(src);
379 int da = SkGetPackedA32(dst);
380 int a = srcover_byte(sa, da);
381 int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
382 int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
383 int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
384 return SkPackARGB32(a, r, g, b);
385}
386
387// kDifference_Mode
388static inline int difference_byte(int sc, int dc, int sa, int da) {
389 int tmp = SkMin32(sc * da, dc * sa);
390 return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
391}
392static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
393 int sa = SkGetPackedA32(src);
394 int da = SkGetPackedA32(dst);
395 int a = srcover_byte(sa, da);
396 int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
397 int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
398 int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
399 return SkPackARGB32(a, r, g, b);
400}
401
402// kExclusion_Mode
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000403static inline int exclusion_byte(int sc, int dc, int, int) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000404 // this equations is wacky, wait for SVG to confirm it
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000405 //int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
406
407 // The above equation can be simplified as follows
408 int r = 255*(sc + dc) - 2 * sc * dc;
reed@android.coma0f5d152009-06-22 17:38:10 +0000409 return clamp_div255round(r);
410}
411static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
412 int sa = SkGetPackedA32(src);
413 int da = SkGetPackedA32(dst);
414 int a = srcover_byte(sa, da);
415 int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
416 int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
417 int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.com543ed932009-04-24 12:43:40 +0000418 return SkPackARGB32(a, r, g, b);
419}
420
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000421// The CSS compositing spec introduces the following formulas:
422// (See https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnonseparable)
423// SkComputeLuminance is similar to this formula but it uses the new definition from Rec. 709
424// while PDF and CG uses the one from Rec. Rec. 601
425// See http://www.glennchan.info/articles/technical/hd-versus-sd-color-space/hd-versus-sd-color-space.htm
426static inline int Lum(int r, int g, int b)
427{
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000428 return SkDiv255Round(r * 77 + g * 150 + b * 28);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000429}
430
431static inline int min2(int a, int b) { return a < b ? a : b; }
432static inline int max2(int a, int b) { return a > b ? a : b; }
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000433#define minimum(a, b, c) min2(min2(a, b), c)
434#define maximum(a, b, c) max2(max2(a, b), c)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000435
436static inline int Sat(int r, int g, int b) {
437 return maximum(r, g, b) - minimum(r, g, b);
438}
439
440static inline void setSaturationComponents(int* Cmin, int* Cmid, int* Cmax, int s) {
reed@google.com3c1ea3a2013-03-07 15:31:58 +0000441 if(*Cmax > *Cmin) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000442 *Cmid = SkMulDiv(*Cmid - *Cmin, s, *Cmax - *Cmin);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000443 *Cmax = s;
444 } else {
445 *Cmax = 0;
446 *Cmid = 0;
447 }
448
449 *Cmin = 0;
450}
451
452static inline void SetSat(int* r, int* g, int* b, int s) {
453 if(*r <= *g) {
454 if(*g <= *b) {
455 setSaturationComponents(r, g, b, s);
456 } else if(*r <= *b) {
457 setSaturationComponents(r, b, g, s);
458 } else {
459 setSaturationComponents(b, r, g, s);
460 }
461 } else if(*r <= *b) {
462 setSaturationComponents(g, r, b, s);
463 } else if(*g <= *b) {
464 setSaturationComponents(g, b, r, s);
465 } else {
466 setSaturationComponents(b, g, r, s);
467 }
468}
469
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000470static inline void clipColor(int* r, int* g, int* b, int a) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000471 int L = Lum(*r, *g, *b);
472 int n = minimum(*r, *g, *b);
473 int x = maximum(*r, *g, *b);
commit-bot@chromium.orgb85ebea2013-12-12 19:47:09 +0000474 int denom;
475 if ((n < 0) && (denom = L - n)) { // Compute denom and make sure it's non zero
476 *r = L + SkMulDiv(*r - L, L, denom);
477 *g = L + SkMulDiv(*g - L, L, denom);
478 *b = L + SkMulDiv(*b - L, L, denom);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000479 }
480
commit-bot@chromium.orgb85ebea2013-12-12 19:47:09 +0000481 if ((x > a) && (denom = x - L)) { // Compute denom and make sure it's non zero
482 int numer = a - L;
483 *r = L + SkMulDiv(*r - L, numer, denom);
484 *g = L + SkMulDiv(*g - L, numer, denom);
485 *b = L + SkMulDiv(*b - L, numer, denom);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000486 }
487}
488
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000489static inline void SetLum(int* r, int* g, int* b, int a, int l) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000490 int d = l - Lum(*r, *g, *b);
491 *r += d;
492 *g += d;
493 *b += d;
494
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000495 clipColor(r, g, b, a);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000496}
497
498// non-separable blend modes are done in non-premultiplied alpha
499#define blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000500 clamp_div255round(sc * (255 - da) + dc * (255 - sa) + blendval)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000501
502// kHue_Mode
503// B(Cb, Cs) = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
504// Create a color with the hue of the source color and the saturation and luminosity of the backdrop color.
505static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
506 int sr = SkGetPackedR32(src);
507 int sg = SkGetPackedG32(src);
508 int sb = SkGetPackedB32(src);
509 int sa = SkGetPackedA32(src);
510
511 int dr = SkGetPackedR32(dst);
512 int dg = SkGetPackedG32(dst);
513 int db = SkGetPackedB32(dst);
514 int da = SkGetPackedA32(dst);
515 int Sr, Sg, Sb;
516
517 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000518 Sr = sr * sa;
519 Sg = sg * sa;
520 Sb = sb * sa;
521 SetSat(&Sr, &Sg, &Sb, Sat(dr, dg, db) * sa);
522 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000523 } else {
524 Sr = 0;
525 Sg = 0;
526 Sb = 0;
527 }
528
529 int a = srcover_byte(sa, da);
530 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
531 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
532 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
533 return SkPackARGB32(a, r, g, b);
534}
535
536// kSaturation_Mode
537// B(Cb, Cs) = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000538// Create a color with the saturation of the source color and the hue and luminosity of the backdrop color.
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000539static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
540 int sr = SkGetPackedR32(src);
541 int sg = SkGetPackedG32(src);
542 int sb = SkGetPackedB32(src);
543 int sa = SkGetPackedA32(src);
544
545 int dr = SkGetPackedR32(dst);
546 int dg = SkGetPackedG32(dst);
547 int db = SkGetPackedB32(dst);
548 int da = SkGetPackedA32(dst);
549 int Dr, Dg, Db;
550
551 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000552 Dr = dr * sa;
553 Dg = dg * sa;
554 Db = db * sa;
555 SetSat(&Dr, &Dg, &Db, Sat(sr, sg, sb) * da);
556 SetLum(&Dr, &Dg, &Db, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000557 } else {
558 Dr = 0;
559 Dg = 0;
560 Db = 0;
561 }
562
563 int a = srcover_byte(sa, da);
564 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
565 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
566 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
567 return SkPackARGB32(a, r, g, b);
568}
569
570// kColor_Mode
571// B(Cb, Cs) = SetLum(Cs, Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000572// Create a color with the hue and saturation of the source color and the luminosity of the backdrop color.
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000573static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
574 int sr = SkGetPackedR32(src);
575 int sg = SkGetPackedG32(src);
576 int sb = SkGetPackedB32(src);
577 int sa = SkGetPackedA32(src);
578
579 int dr = SkGetPackedR32(dst);
580 int dg = SkGetPackedG32(dst);
581 int db = SkGetPackedB32(dst);
582 int da = SkGetPackedA32(dst);
583 int Sr, Sg, Sb;
584
585 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000586 Sr = sr * da;
587 Sg = sg * da;
588 Sb = sb * da;
589 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000590 } else {
591 Sr = 0;
592 Sg = 0;
593 Sb = 0;
594 }
595
596 int a = srcover_byte(sa, da);
597 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
598 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
599 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
600 return SkPackARGB32(a, r, g, b);
601}
602
603// kLuminosity_Mode
604// B(Cb, Cs) = SetLum(Cb, Lum(Cs))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000605// Create a color with the luminosity of the source color and the hue and saturation of the backdrop color.
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000606static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
607 int sr = SkGetPackedR32(src);
608 int sg = SkGetPackedG32(src);
609 int sb = SkGetPackedB32(src);
610 int sa = SkGetPackedA32(src);
611
612 int dr = SkGetPackedR32(dst);
613 int dg = SkGetPackedG32(dst);
614 int db = SkGetPackedB32(dst);
615 int da = SkGetPackedA32(dst);
616 int Dr, Dg, Db;
617
618 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000619 Dr = dr * sa;
620 Dg = dg * sa;
621 Db = db * sa;
622 SetLum(&Dr, &Dg, &Db, sa * da, Lum(sr, sg, sb) * da);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000623 } else {
624 Dr = 0;
625 Dg = 0;
626 Db = 0;
627 }
628
629 int a = srcover_byte(sa, da);
630 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
631 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
632 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
633 return SkPackARGB32(a, r, g, b);
634}
635
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +0000636const ProcCoeff gProcCoeffs[] = {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000637 { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff },
638 { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff },
639 { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff },
640 { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff },
641 { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff },
642 { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff },
643 { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff },
644 { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff },
645 { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff },
646 { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff },
647 { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff },
648 { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff },
649
reed@google.com521e34e2011-04-12 18:55:21 +0000650 { plus_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kOne_Coeff },
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000651 { modulate_modeproc,SkXfermode::kZero_Coeff, SkXfermode::kSC_Coeff },
bsalomon@google.comb0091b82013-04-15 15:16:47 +0000652 { screen_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISC_Coeff },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000653 { overlay_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
654 { darken_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
655 { lighten_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
656 { colordodge_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
657 { colorburn_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
658 { hardlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
659 { softlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
660 { difference_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
661 { exclusion_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
reed@google.com25cfa692013-02-04 20:06:00 +0000662 { multiply_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000663 { hue_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
664 { saturation_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
665 { color_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
666 { luminosity_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000667};
668
669///////////////////////////////////////////////////////////////////////////////
670
reed@google.com30da7452012-12-17 19:55:24 +0000671bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000672 return false;
673}
674
reed@google.com30da7452012-12-17 19:55:24 +0000675bool SkXfermode::asMode(Mode* mode) const {
reed@google.comc0d4aa22011-04-13 21:12:04 +0000676 return false;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000677}
678
joshualittb0a8a372014-09-23 09:50:21 -0700679bool SkXfermode::asFragmentProcessor(GrFragmentProcessor**, GrTexture*) const {
senorblanco@chromium.org1a6382f2013-10-23 18:41:36 +0000680 return false;
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000681}
682
joshualittb0a8a372014-09-23 09:50:21 -0700683bool SkXfermode::asFragmentProcessorOrCoeff(SkXfermode* xfermode, GrFragmentProcessor** fp,
684 Coeff* src, Coeff* dst, GrTexture* background) {
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000685 if (NULL == xfermode) {
686 return ModeAsCoeff(kSrcOver_Mode, src, dst);
senorblanco@chromium.org1a6382f2013-10-23 18:41:36 +0000687 } else if (xfermode->asCoeff(src, dst)) {
688 return true;
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000689 } else {
joshualittb0a8a372014-09-23 09:50:21 -0700690 return xfermode->asFragmentProcessor(fp, background);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000691 }
692}
693
reed@google.com30da7452012-12-17 19:55:24 +0000694SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) const{
vandebo@chromium.org48543272011-02-08 19:28:07 +0000695 // no-op. subclasses should override this
696 return dst;
697}
698
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000699void SkXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
700 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000701 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000702 SkASSERT(dst && src && count >= 0);
703
704 if (NULL == aa) {
705 for (int i = count - 1; i >= 0; --i) {
706 dst[i] = this->xferColor(src[i], dst[i]);
707 }
708 } else {
709 for (int i = count - 1; i >= 0; --i) {
710 unsigned a = aa[i];
711 if (0 != a) {
712 SkPMColor dstC = dst[i];
713 SkPMColor C = this->xferColor(src[i], dstC);
714 if (0xFF != a) {
715 C = SkFourByteInterp(C, dstC, a);
716 }
717 dst[i] = C;
718 }
719 }
720 }
721}
722
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000723void SkXfermode::xfer16(uint16_t* dst,
724 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000725 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000726 SkASSERT(dst && src && count >= 0);
727
728 if (NULL == aa) {
729 for (int i = count - 1; i >= 0; --i) {
730 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
731 dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
732 }
733 } else {
734 for (int i = count - 1; i >= 0; --i) {
735 unsigned a = aa[i];
736 if (0 != a) {
737 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
738 SkPMColor C = this->xferColor(src[i], dstC);
739 if (0xFF != a) {
740 C = SkFourByteInterp(C, dstC, a);
741 }
742 dst[i] = SkPixel32ToPixel16_ToU16(C);
743 }
744 }
745 }
746}
747
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000748void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
vandebo@chromium.org48543272011-02-08 19:28:07 +0000749 const SkPMColor src[], int count,
reed@google.com30da7452012-12-17 19:55:24 +0000750 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000751 SkASSERT(dst && src && count >= 0);
752
753 if (NULL == aa) {
754 for (int i = count - 1; i >= 0; --i) {
755 SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
756 dst[i] = SkToU8(SkGetPackedA32(res));
757 }
758 } else {
759 for (int i = count - 1; i >= 0; --i) {
760 unsigned a = aa[i];
761 if (0 != a) {
762 SkAlpha dstA = dst[i];
763 unsigned A = SkGetPackedA32(this->xferColor(src[i],
764 (SkPMColor)(dstA << SK_A32_SHIFT)));
765 if (0xFF != a) {
766 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
767 }
768 dst[i] = SkToU8(A);
769 }
770 }
771 }
772}
773
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000774//////////////////////////////////////////////////////////////////////////////
775
776#if SK_SUPPORT_GPU
777
joshualittb0a8a372014-09-23 09:50:21 -0700778#include "GrProcessor.h"
bsalomon@google.com77af6802013-10-02 13:04:56 +0000779#include "GrCoordTransform.h"
joshualittb0a8a372014-09-23 09:50:21 -0700780#include "GrProcessorUnitTest.h"
781#include "GrTBackendProcessorFactory.h"
782#include "gl/GrGLProcessor.h"
joshualitt30ba4362014-08-21 20:18:45 -0700783#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000784
785/**
joshualittb0a8a372014-09-23 09:50:21 -0700786 * GrProcessor that implements the all the separable xfer modes that cannot be expressed as Coeffs.
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000787 */
joshualittb0a8a372014-09-23 09:50:21 -0700788class XferEffect : public GrFragmentProcessor {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000789public:
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000790 static bool IsSupportedMode(SkXfermode::Mode mode) {
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000791 return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000792 }
793
joshualittb0a8a372014-09-23 09:50:21 -0700794 static GrFragmentProcessor* Create(SkXfermode::Mode mode, GrTexture* background) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000795 if (!IsSupportedMode(mode)) {
796 return NULL;
797 } else {
bsalomon55fad7a2014-07-08 07:34:20 -0700798 return SkNEW_ARGS(XferEffect, (mode, background));
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000799 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000800 }
801
802 virtual void getConstantColorComponents(GrColor* color,
803 uint32_t* validFlags) const SK_OVERRIDE {
804 *validFlags = 0;
805 }
806
joshualittb0a8a372014-09-23 09:50:21 -0700807 virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE {
808 return GrTBackendFragmentProcessorFactory<XferEffect>::getInstance();
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000809 }
810
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000811 static const char* Name() { return "XferEffect"; }
812
813 SkXfermode::Mode mode() const { return fMode; }
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000814 const GrTextureAccess& backgroundAccess() const { return fBackgroundAccess; }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000815
joshualittb0a8a372014-09-23 09:50:21 -0700816 class GLProcessor : public GrGLFragmentProcessor {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000817 public:
joshualittb0a8a372014-09-23 09:50:21 -0700818 GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
819 : INHERITED(factory) {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000820 }
joshualitt30ba4362014-08-21 20:18:45 -0700821 virtual void emitCode(GrGLProgramBuilder* builder,
joshualittb0a8a372014-09-23 09:50:21 -0700822 const GrFragmentProcessor& fp,
823 const GrProcessorKey& key,
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000824 const char* outputColor,
825 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000826 const TransformedCoordsArray& coords,
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000827 const TextureSamplerArray& samplers) SK_OVERRIDE {
joshualittb0a8a372014-09-23 09:50:21 -0700828 SkXfermode::Mode mode = fp.cast<XferEffect>().mode();
joshualitt49586be2014-09-16 08:21:41 -0700829 const GrTexture* backgroundTex =
joshualittb0a8a372014-09-23 09:50:21 -0700830 fp.cast<XferEffect>().backgroundAccess().getTexture();
joshualitt30ba4362014-08-21 20:18:45 -0700831 GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000832 const char* dstColor;
833 if (backgroundTex) {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000834 dstColor = "bgColor";
joshualitt30ba4362014-08-21 20:18:45 -0700835 fsBuilder->codeAppendf("\t\tvec4 %s = ", dstColor);
joshualitt23e280d2014-09-18 12:26:38 -0700836 fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
joshualitt30ba4362014-08-21 20:18:45 -0700837 fsBuilder->codeAppendf(";\n");
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000838 } else {
joshualitt30ba4362014-08-21 20:18:45 -0700839 dstColor = fsBuilder->dstColor();
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000840 }
bsalomon49f085d2014-09-05 13:34:00 -0700841 SkASSERT(dstColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000842
843 // We don't try to optimize for this case at all
bsalomon@google.comb79d8652013-03-29 20:30:50 +0000844 if (NULL == inputColor) {
joshualitt30ba4362014-08-21 20:18:45 -0700845 fsBuilder->codeAppendf("\t\tconst vec4 ones = vec4(1);\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000846 inputColor = "ones";
847 }
joshualitt30ba4362014-08-21 20:18:45 -0700848 fsBuilder->codeAppendf("\t\t// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000849
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000850 // These all perform src-over on the alpha channel.
joshualitt30ba4362014-08-21 20:18:45 -0700851 fsBuilder->codeAppendf("\t\t%s.a = %s.a + (1.0 - %s.a) * %s.a;\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000852 outputColor, inputColor, inputColor, dstColor);
853
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000854 switch (mode) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000855 case SkXfermode::kOverlay_Mode:
856 // Overlay is Hard-Light with the src and dst reversed
joshualitt30ba4362014-08-21 20:18:45 -0700857 HardLight(fsBuilder, outputColor, dstColor, inputColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000858 break;
859 case SkXfermode::kDarken_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700860 fsBuilder->codeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000861 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
862 outputColor,
863 inputColor, dstColor, inputColor,
864 dstColor, inputColor, dstColor);
865 break;
866 case SkXfermode::kLighten_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700867 fsBuilder->codeAppendf("\t\t%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000868 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
869 outputColor,
870 inputColor, dstColor, inputColor,
871 dstColor, inputColor, dstColor);
872 break;
873 case SkXfermode::kColorDodge_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700874 ColorDodgeComponent(fsBuilder, outputColor, inputColor, dstColor, 'r');
875 ColorDodgeComponent(fsBuilder, outputColor, inputColor, dstColor, 'g');
876 ColorDodgeComponent(fsBuilder, outputColor, inputColor, dstColor, 'b');
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000877 break;
878 case SkXfermode::kColorBurn_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700879 ColorBurnComponent(fsBuilder, outputColor, inputColor, dstColor, 'r');
880 ColorBurnComponent(fsBuilder, outputColor, inputColor, dstColor, 'g');
881 ColorBurnComponent(fsBuilder, outputColor, inputColor, dstColor, 'b');
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000882 break;
883 case SkXfermode::kHardLight_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700884 HardLight(fsBuilder, outputColor, inputColor, dstColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000885 break;
886 case SkXfermode::kSoftLight_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700887 fsBuilder->codeAppendf("\t\tif (0.0 == %s.a) {\n", dstColor);
888 fsBuilder->codeAppendf("\t\t\t%s.rgba = %s;\n", outputColor, inputColor);
889 fsBuilder->codeAppendf("\t\t} else {\n");
890 SoftLightComponentPosDstAlpha(fsBuilder, outputColor, inputColor, dstColor, 'r');
891 SoftLightComponentPosDstAlpha(fsBuilder, outputColor, inputColor, dstColor, 'g');
892 SoftLightComponentPosDstAlpha(fsBuilder, outputColor, inputColor, dstColor, 'b');
893 fsBuilder->codeAppendf("\t\t}\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000894 break;
895 case SkXfermode::kDifference_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700896 fsBuilder->codeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb -"
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000897 "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000898 outputColor, inputColor, dstColor, inputColor, dstColor,
899 dstColor, inputColor);
900 break;
901 case SkXfermode::kExclusion_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700902 fsBuilder->codeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb - "
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000903 "2.0 * %s.rgb * %s.rgb;\n",
904 outputColor, dstColor, inputColor, dstColor, inputColor);
905 break;
906 case SkXfermode::kMultiply_Mode:
joshualitt30ba4362014-08-21 20:18:45 -0700907 fsBuilder->codeAppendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + "
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000908 "(1.0 - %s.a) * %s.rgb + "
909 "%s.rgb * %s.rgb;\n",
910 outputColor, inputColor, dstColor, dstColor, inputColor,
911 inputColor, dstColor);
912 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000913 case SkXfermode::kHue_Mode: {
914 // SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
915 SkString setSat, setLum;
joshualitt30ba4362014-08-21 20:18:45 -0700916 AddSatFunction(fsBuilder, &setSat);
917 AddLumFunction(fsBuilder, &setLum);
918 fsBuilder->codeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000919 dstColor, inputColor);
joshualitt30ba4362014-08-21 20:18:45 -0700920 fsBuilder->codeAppendf("\t\t%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000921 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
922 dstColor);
joshualitt30ba4362014-08-21 20:18:45 -0700923 fsBuilder->codeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000924 outputColor, inputColor, dstColor, dstColor, inputColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000925 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000926 }
927 case SkXfermode::kSaturation_Mode: {
928 // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
929 SkString setSat, setLum;
joshualitt30ba4362014-08-21 20:18:45 -0700930 AddSatFunction(fsBuilder, &setSat);
931 AddLumFunction(fsBuilder, &setLum);
932 fsBuilder->codeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000933 dstColor, inputColor);
joshualitt30ba4362014-08-21 20:18:45 -0700934 fsBuilder->codeAppendf("\t\t%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000935 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
936 dstColor);
joshualitt30ba4362014-08-21 20:18:45 -0700937 fsBuilder->codeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000938 outputColor, inputColor, dstColor, dstColor, inputColor);
939 break;
940 }
941 case SkXfermode::kColor_Mode: {
942 // SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
943 SkString setLum;
joshualitt30ba4362014-08-21 20:18:45 -0700944 AddLumFunction(fsBuilder, &setLum);
945 fsBuilder->codeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000946 inputColor, dstColor);
joshualitt30ba4362014-08-21 20:18:45 -0700947 fsBuilder->codeAppendf("\t\t%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000948 outputColor, setLum.c_str(), dstColor, inputColor);
joshualitt30ba4362014-08-21 20:18:45 -0700949 fsBuilder->codeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000950 outputColor, inputColor, dstColor, dstColor, inputColor);
951 break;
952 }
953 case SkXfermode::kLuminosity_Mode: {
954 // SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
955 SkString setLum;
joshualitt30ba4362014-08-21 20:18:45 -0700956 AddLumFunction(fsBuilder, &setLum);
957 fsBuilder->codeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000958 inputColor, dstColor);
joshualitt30ba4362014-08-21 20:18:45 -0700959 fsBuilder->codeAppendf("\t\t%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000960 outputColor, setLum.c_str(), dstColor, inputColor);
joshualitt30ba4362014-08-21 20:18:45 -0700961 fsBuilder->codeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000962 outputColor, inputColor, dstColor, dstColor, inputColor);
963 break;
964 }
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000965 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000966 SkFAIL("Unknown XferEffect mode.");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000967 break;
bsalomon@google.comb79d8652013-03-29 20:30:50 +0000968 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000969 }
970
joshualittb0a8a372014-09-23 09:50:21 -0700971 static inline void GenKey(const GrProcessor& proc, const GrGLCaps&,
972 GrProcessorKeyBuilder* b) {
commit-bot@chromium.org372a9c32014-04-07 17:13:10 +0000973 // The background may come from the dst or from a texture.
joshualittb0a8a372014-09-23 09:50:21 -0700974 uint32_t key = proc.numTextures();
bsalomon63e99f72014-07-21 08:03:14 -0700975 SkASSERT(key <= 1);
joshualittb0a8a372014-09-23 09:50:21 -0700976 key |= proc.cast<XferEffect>().mode() << 1;
bsalomon63e99f72014-07-21 08:03:14 -0700977 b->add32(key);
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000978 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000979
980 private:
joshualitt30ba4362014-08-21 20:18:45 -0700981 static void HardLight(GrGLFragmentShaderBuilder* fsBuilder,
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000982 const char* final,
983 const char* src,
984 const char* dst) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +0000985 static const char kComponents[] = {'r', 'g', 'b'};
986 for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
987 char component = kComponents[i];
joshualitt30ba4362014-08-21 20:18:45 -0700988 fsBuilder->codeAppendf("\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
989 fsBuilder->codeAppendf("\t\t\t%s.%c = 2.0 * %s.%c * %s.%c;\n", final, component, src, component, dst, component);
990 fsBuilder->codeAppend("\t\t} else {\n");
991 fsBuilder->codeAppendf("\t\t\t%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);\n",
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +0000992 final, component, src, dst, dst, dst, component, src, src, component);
joshualitt30ba4362014-08-21 20:18:45 -0700993 fsBuilder->codeAppend("\t\t}\n");
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +0000994 }
joshualitt30ba4362014-08-21 20:18:45 -0700995 fsBuilder->codeAppendf("\t\t%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000996 final, src, dst, dst, src);
997 }
998
999 // Does one component of color-dodge
joshualitt30ba4362014-08-21 20:18:45 -07001000 static void ColorDodgeComponent(GrGLFragmentShaderBuilder* fsBuilder,
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001001 const char* final,
1002 const char* src,
1003 const char* dst,
1004 const char component) {
joshualitt30ba4362014-08-21 20:18:45 -07001005 fsBuilder->codeAppendf("\t\tif (0.0 == %s.%c) {\n", dst, component);
1006 fsBuilder->codeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001007 final, component, src, component, dst);
joshualitt30ba4362014-08-21 20:18:45 -07001008 fsBuilder->codeAppend("\t\t} else {\n");
1009 fsBuilder->codeAppendf("\t\t\tfloat d = %s.a - %s.%c;\n", src, src, component);
1010 fsBuilder->codeAppend("\t\t\tif (0.0 == d) {\n");
1011 fsBuilder->codeAppendf("\t\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001012 final, component, src, dst, src, component, dst, dst, component,
1013 src);
joshualitt30ba4362014-08-21 20:18:45 -07001014 fsBuilder->codeAppend("\t\t\t} else {\n");
1015 fsBuilder->codeAppendf("\t\t\t\td = min(%s.a, %s.%c * %s.a / d);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001016 dst, dst, component, src);
joshualitt30ba4362014-08-21 20:18:45 -07001017 fsBuilder->codeAppendf("\t\t\t\t%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001018 final, component, src, src, component, dst, dst, component, src);
joshualitt30ba4362014-08-21 20:18:45 -07001019 fsBuilder->codeAppend("\t\t\t}\n");
1020 fsBuilder->codeAppend("\t\t}\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001021 }
1022
1023 // Does one component of color-burn
joshualitt30ba4362014-08-21 20:18:45 -07001024 static void ColorBurnComponent(GrGLFragmentShaderBuilder* fsBuilder,
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001025 const char* final,
1026 const char* src,
1027 const char* dst,
1028 const char component) {
joshualitt30ba4362014-08-21 20:18:45 -07001029 fsBuilder->codeAppendf("\t\tif (%s.a == %s.%c) {\n", dst, dst, component);
1030 fsBuilder->codeAppendf("\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001031 final, component, src, dst, src, component, dst, dst, component,
1032 src);
joshualitt30ba4362014-08-21 20:18:45 -07001033 fsBuilder->codeAppendf("\t\t} else if (0.0 == %s.%c) {\n", src, component);
1034 fsBuilder->codeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001035 final, component, dst, component, src);
joshualitt30ba4362014-08-21 20:18:45 -07001036 fsBuilder->codeAppend("\t\t} else {\n");
1037 fsBuilder->codeAppendf("\t\t\tfloat d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001038 dst, dst, dst, component, src, src, component);
joshualitt30ba4362014-08-21 20:18:45 -07001039 fsBuilder->codeAppendf("\t\t\t%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001040 final, component, src, src, component, dst, dst, component, src);
joshualitt30ba4362014-08-21 20:18:45 -07001041 fsBuilder->codeAppend("\t\t}\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001042 }
1043
1044 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
joshualitt30ba4362014-08-21 20:18:45 -07001045 static void SoftLightComponentPosDstAlpha(GrGLFragmentShaderBuilder* fsBuilder,
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001046 const char* final,
1047 const char* src,
1048 const char* dst,
1049 const char component) {
1050 // if (2S < Sa)
joshualitt30ba4362014-08-21 20:18:45 -07001051 fsBuilder->codeAppendf("\t\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001052 // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
joshualitt30ba4362014-08-21 20:18:45 -07001053 fsBuilder->codeAppendf("\t\t\t\t%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a + (1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s.%c + 1.0);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001054 final, component, dst, component, dst, component, src, src,
1055 component, dst, dst, src, component, dst, component, src, src,
1056 component);
1057 // else if (4D < Da)
joshualitt30ba4362014-08-21 20:18:45 -07001058 fsBuilder->codeAppendf("\t\t\t} else if (4.0 * %s.%c <= %s.a) {\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001059 dst, component, dst);
joshualitt30ba4362014-08-21 20:18:45 -07001060 fsBuilder->codeAppendf("\t\t\t\tfloat DSqd = %s.%c * %s.%c;\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001061 dst, component, dst, component);
joshualitt30ba4362014-08-21 20:18:45 -07001062 fsBuilder->codeAppendf("\t\t\t\tfloat DCub = DSqd * %s.%c;\n", dst, component);
1063 fsBuilder->codeAppendf("\t\t\t\tfloat DaSqd = %s.a * %s.a;\n", dst, dst);
1064 fsBuilder->codeAppendf("\t\t\t\tfloat DaCub = DaSqd * %s.a;\n", dst);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001065 // (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
joshualitt30ba4362014-08-21 20:18:45 -07001066 fsBuilder->codeAppendf("\t\t\t\t%s.%c = (-DaCub*%s.%c + DaSqd*(%s.%c - %s.%c * (3.0*%s.a - 6.0*%s.%c - 1.0)) + 12.0*%s.a*DSqd*(%s.a - 2.0*%s.%c) - 16.0*DCub * (%s.a - 2.0*%s.%c)) / DaSqd;\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001067 final, component, src, component, src, component, dst, component,
1068 src, src, component, dst, src, src, component, src, src,
1069 component);
joshualitt30ba4362014-08-21 20:18:45 -07001070 fsBuilder->codeAppendf("\t\t\t} else {\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001071 // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
joshualitt30ba4362014-08-21 20:18:45 -07001072 fsBuilder->codeAppendf("\t\t\t\t%s.%c = -sqrt(%s.a*%s.%c)*(%s.a - 2.0*%s.%c) - %s.a*%s.%c + %s.%c*(%s.a - 2.0*%s.%c + 1.0) + %s.%c;\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001073 final, component, dst, dst, component, src, src, component, dst,
1074 src, component, dst, component, src, src, component, src,
1075 component);
joshualitt30ba4362014-08-21 20:18:45 -07001076 fsBuilder->codeAppendf("\t\t\t}\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001077 }
1078
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001079 // Adds a function that takes two colors and an alpha as input. It produces a color with the
1080 // hue and saturation of the first color, the luminosity of the second color, and the input
1081 // alpha. It has this signature:
1082 // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
joshualitt30ba4362014-08-21 20:18:45 -07001083 static void AddLumFunction(GrGLFragmentShaderBuilder* fsBuilder, SkString* setLumFunction) {
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001084 // Emit a helper that gets the luminance of a color.
1085 SkString getFunction;
1086 GrGLShaderVar getLumArgs[] = {
1087 GrGLShaderVar("color", kVec3f_GrSLType),
1088 };
1089 SkString getLumBody("\treturn dot(vec3(0.3, 0.59, 0.11), color);\n");
joshualitt30ba4362014-08-21 20:18:45 -07001090 fsBuilder->emitFunction(kFloat_GrSLType,
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001091 "luminance",
1092 SK_ARRAY_COUNT(getLumArgs), getLumArgs,
1093 getLumBody.c_str(),
1094 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001095
1096 // Emit the set luminance function.
1097 GrGLShaderVar setLumArgs[] = {
1098 GrGLShaderVar("hueSat", kVec3f_GrSLType),
1099 GrGLShaderVar("alpha", kFloat_GrSLType),
1100 GrGLShaderVar("lumColor", kVec3f_GrSLType),
1101 };
1102 SkString setLumBody;
1103 setLumBody.printf("\tfloat diff = %s(lumColor - hueSat);\n", getFunction.c_str());
1104 setLumBody.append("\tvec3 outColor = hueSat + diff;\n");
1105 setLumBody.appendf("\tfloat outLum = %s(outColor);\n", getFunction.c_str());
1106 setLumBody.append("\tfloat minComp = min(min(outColor.r, outColor.g), outColor.b);\n"
1107 "\tfloat maxComp = max(max(outColor.r, outColor.g), outColor.b);\n"
1108 "\tif (minComp < 0.0) {\n"
1109 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / (outLum - minComp);\n"
1110 "\t}\n"
1111 "\tif (maxComp > alpha) {\n"
1112 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) / (maxComp - outLum);\n"
1113 "\t}\n"
1114 "\treturn outColor;\n");
joshualitt30ba4362014-08-21 20:18:45 -07001115 fsBuilder->emitFunction(kVec3f_GrSLType,
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001116 "set_luminance",
1117 SK_ARRAY_COUNT(setLumArgs), setLumArgs,
1118 setLumBody.c_str(),
1119 setLumFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001120 }
1121
1122 // Adds a function that creates a color with the hue and luminosity of one input color and
1123 // the saturation of another color. It will have this signature:
1124 // float set_saturation(vec3 hueLumColor, vec3 satColor)
joshualitt30ba4362014-08-21 20:18:45 -07001125 static void AddSatFunction(GrGLFragmentShaderBuilder* fsBuilder, SkString* setSatFunction) {
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001126 // Emit a helper that gets the saturation of a color
1127 SkString getFunction;
1128 GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
1129 SkString getSatBody;
1130 getSatBody.printf("\treturn max(max(color.r, color.g), color.b) - "
1131 "min(min(color.r, color.g), color.b);\n");
joshualitt30ba4362014-08-21 20:18:45 -07001132 fsBuilder->emitFunction(kFloat_GrSLType,
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001133 "saturation",
1134 SK_ARRAY_COUNT(getSatArgs), getSatArgs,
1135 getSatBody.c_str(),
1136 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001137
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001138 // Emit a helper that sets the saturation given sorted input channels. This used
1139 // to use inout params for min, mid, and max components but that seems to cause
1140 // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
1141 // adjusted min, mid, and max inputs, respectively.
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001142 SkString helperFunction;
1143 GrGLShaderVar helperArgs[] = {
1144 GrGLShaderVar("minComp", kFloat_GrSLType),
1145 GrGLShaderVar("midComp", kFloat_GrSLType),
1146 GrGLShaderVar("maxComp", kFloat_GrSLType),
1147 GrGLShaderVar("sat", kFloat_GrSLType),
1148 };
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001149 static const char kHelperBody[] = "\tif (minComp < maxComp) {\n"
1150 "\t\tvec3 result;\n"
1151 "\t\tresult.r = 0.0;\n"
1152 "\t\tresult.g = sat * (midComp - minComp) / (maxComp - minComp);\n"
1153 "\t\tresult.b = sat;\n"
1154 "\t\treturn result;\n"
1155 "\t} else {\n"
1156 "\t\treturn vec3(0, 0, 0);\n"
1157 "\t}\n";
joshualitt30ba4362014-08-21 20:18:45 -07001158 fsBuilder->emitFunction(kVec3f_GrSLType,
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001159 "set_saturation_helper",
1160 SK_ARRAY_COUNT(helperArgs), helperArgs,
1161 kHelperBody,
1162 &helperFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001163
1164 GrGLShaderVar setSatArgs[] = {
1165 GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
1166 GrGLShaderVar("satColor", kVec3f_GrSLType),
1167 };
1168 const char* helpFunc = helperFunction.c_str();
1169 SkString setSatBody;
1170 setSatBody.appendf("\tfloat sat = %s(satColor);\n"
1171 "\tif (hueLumColor.r <= hueLumColor.g) {\n"
1172 "\t\tif (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001173 "\t\t\thueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001174 "\t\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001175 "\t\t\thueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001176 "\t\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001177 "\t\t\thueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001178 "\t\t}\n"
1179 "\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001180 "\t\thueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001181 "\t} else if (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001182 "\t\thueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001183 "\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001184 "\t\thueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001185 "\t}\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001186 "\treturn hueLumColor;\n",
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001187 getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
1188 helpFunc, helpFunc);
joshualitt30ba4362014-08-21 20:18:45 -07001189 fsBuilder->emitFunction(kVec3f_GrSLType,
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001190 "set_saturation",
1191 SK_ARRAY_COUNT(setSatArgs), setSatArgs,
1192 setSatBody.c_str(),
1193 setSatFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001194
1195 }
1196
joshualittb0a8a372014-09-23 09:50:21 -07001197 typedef GrGLFragmentProcessor INHERITED;
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001198 };
1199
joshualittb0a8a372014-09-23 09:50:21 -07001200 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001201
1202private:
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001203 XferEffect(SkXfermode::Mode mode, GrTexture* background)
1204 : fMode(mode) {
1205 if (background) {
bsalomon@google.com77af6802013-10-02 13:04:56 +00001206 fBackgroundTransform.reset(kLocal_GrCoordSet, background);
1207 this->addCoordTransform(&fBackgroundTransform);
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001208 fBackgroundAccess.reset(background);
1209 this->addTextureAccess(&fBackgroundAccess);
1210 } else {
1211 this->setWillReadDstColor();
1212 }
1213 }
joshualittb0a8a372014-09-23 09:50:21 -07001214 virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -07001215 const XferEffect& s = other.cast<XferEffect>();
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001216 return fMode == s.fMode &&
1217 fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture();
1218 }
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +00001219
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001220 SkXfermode::Mode fMode;
bsalomon@google.com77af6802013-10-02 13:04:56 +00001221 GrCoordTransform fBackgroundTransform;
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001222 GrTextureAccess fBackgroundAccess;
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001223
joshualittb0a8a372014-09-23 09:50:21 -07001224 typedef GrFragmentProcessor INHERITED;
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001225};
1226
joshualittb0a8a372014-09-23 09:50:21 -07001227GR_DEFINE_FRAGMENT_PROCESSOR_TEST(XferEffect);
1228GrFragmentProcessor* XferEffect::TestCreate(SkRandom* rand,
1229 GrContext*,
1230 const GrDrawTargetCaps&,
1231 GrTexture*[]) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +00001232 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001233
bsalomon55fad7a2014-07-08 07:34:20 -07001234 return SkNEW_ARGS(XferEffect, (static_cast<SkXfermode::Mode>(mode), NULL));
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001235}
1236
1237#endif
1238
vandebo@chromium.org48543272011-02-08 19:28:07 +00001239///////////////////////////////////////////////////////////////////////////////
1240///////////////////////////////////////////////////////////////////////////////
1241
reed9fa60da2014-08-21 07:59:51 -07001242#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001243SkProcCoeffXfermode::SkProcCoeffXfermode(SkReadBuffer& buffer) : INHERITED(buffer) {
reed@google.com52314f82013-11-21 21:05:06 +00001244 uint32_t mode32 = buffer.read32() % SK_ARRAY_COUNT(gProcCoeffs);
1245 if (mode32 >= SK_ARRAY_COUNT(gProcCoeffs)) {
1246 // out of range, just set to something harmless
1247 mode32 = SkXfermode::kSrcOut_Mode;
1248 }
1249 fMode = (SkXfermode::Mode)mode32;
skia.committer@gmail.comf61ebc02013-11-22 07:02:24 +00001250
reed@google.com52314f82013-11-21 21:05:06 +00001251 const ProcCoeff& rec = gProcCoeffs[fMode];
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001252 fProc = rec.fProc;
reed@google.com52314f82013-11-21 21:05:06 +00001253 // these may be valid, or may be CANNOT_USE_COEFF
1254 fSrcCoeff = rec.fSC;
1255 fDstCoeff = rec.fDC;
reed@google.com52314f82013-11-21 21:05:06 +00001256}
reed9fa60da2014-08-21 07:59:51 -07001257#endif
1258
1259SkFlattenable* SkProcCoeffXfermode::CreateProc(SkReadBuffer& buffer) {
1260 uint32_t mode32 = buffer.read32();
senorblanco0f7197b2014-09-24 11:09:38 -07001261 if (!buffer.validate(mode32 < SK_ARRAY_COUNT(gProcCoeffs))) {
reed9fa60da2014-08-21 07:59:51 -07001262 return NULL;
1263 }
1264 return SkXfermode::Create((SkXfermode::Mode)mode32);
1265}
1266
1267void SkProcCoeffXfermode::flatten(SkWriteBuffer& buffer) const {
1268 buffer.write32(fMode);
1269}
reed@google.com52314f82013-11-21 21:05:06 +00001270
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001271bool SkProcCoeffXfermode::asMode(Mode* mode) const {
1272 if (mode) {
1273 *mode = fMode;
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001274 }
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001275 return true;
1276}
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001277
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001278bool SkProcCoeffXfermode::asCoeff(Coeff* sc, Coeff* dc) const {
1279 if (CANNOT_USE_COEFF == fSrcCoeff) {
djsollen@google.com6f980c62013-10-08 16:59:53 +00001280 return false;
1281 }
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001282
1283 if (sc) {
1284 *sc = fSrcCoeff;
1285 }
1286 if (dc) {
1287 *dc = fDstCoeff;
1288 }
1289 return true;
1290}
1291
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001292void SkProcCoeffXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1293 const SkPMColor* SK_RESTRICT src, int count,
1294 const SkAlpha* SK_RESTRICT aa) const {
1295 SkASSERT(dst && src && count >= 0);
skia.committer@gmail.com60bd7512014-04-18 03:03:54 +00001296
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001297 SkXfermodeProc proc = fProc;
skia.committer@gmail.com60bd7512014-04-18 03:03:54 +00001298
bsalomon49f085d2014-09-05 13:34:00 -07001299 if (proc) {
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001300 if (NULL == aa) {
1301 for (int i = count - 1; i >= 0; --i) {
1302 dst[i] = proc(src[i], dst[i]);
1303 }
1304 } else {
1305 for (int i = count - 1; i >= 0; --i) {
1306 unsigned a = aa[i];
1307 if (0 != a) {
1308 SkPMColor dstC = dst[i];
1309 SkPMColor C = proc(src[i], dstC);
1310 if (a != 0xFF) {
1311 C = SkFourByteInterp(C, dstC, a);
1312 }
1313 dst[i] = C;
1314 }
1315 }
1316 }
1317 }
1318}
1319
1320void SkProcCoeffXfermode::xfer16(uint16_t* SK_RESTRICT dst,
1321 const SkPMColor* SK_RESTRICT src, int count,
1322 const SkAlpha* SK_RESTRICT aa) const {
1323 SkASSERT(dst && src && count >= 0);
skia.committer@gmail.com60bd7512014-04-18 03:03:54 +00001324
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001325 SkXfermodeProc proc = fProc;
skia.committer@gmail.com60bd7512014-04-18 03:03:54 +00001326
bsalomon49f085d2014-09-05 13:34:00 -07001327 if (proc) {
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001328 if (NULL == aa) {
1329 for (int i = count - 1; i >= 0; --i) {
1330 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
1331 dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
1332 }
1333 } else {
1334 for (int i = count - 1; i >= 0; --i) {
1335 unsigned a = aa[i];
1336 if (0 != a) {
1337 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
1338 SkPMColor C = proc(src[i], dstC);
1339 if (0xFF != a) {
1340 C = SkFourByteInterp(C, dstC, a);
1341 }
1342 dst[i] = SkPixel32ToPixel16_ToU16(C);
1343 }
1344 }
1345 }
1346 }
1347}
1348
1349void SkProcCoeffXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1350 const SkPMColor* SK_RESTRICT src, int count,
1351 const SkAlpha* SK_RESTRICT aa) const {
1352 SkASSERT(dst && src && count >= 0);
skia.committer@gmail.com60bd7512014-04-18 03:03:54 +00001353
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001354 SkXfermodeProc proc = fProc;
skia.committer@gmail.com60bd7512014-04-18 03:03:54 +00001355
bsalomon49f085d2014-09-05 13:34:00 -07001356 if (proc) {
commit-bot@chromium.orgcc277b72014-04-17 15:19:32 +00001357 if (NULL == aa) {
1358 for (int i = count - 1; i >= 0; --i) {
1359 SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
1360 dst[i] = SkToU8(SkGetPackedA32(res));
1361 }
1362 } else {
1363 for (int i = count - 1; i >= 0; --i) {
1364 unsigned a = aa[i];
1365 if (0 != a) {
1366 SkAlpha dstA = dst[i];
1367 SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
1368 unsigned A = SkGetPackedA32(res);
1369 if (0xFF != a) {
1370 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
1371 }
1372 dst[i] = SkToU8(A);
1373 }
1374 }
1375 }
1376 }
1377}
1378
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001379#if SK_SUPPORT_GPU
joshualittb0a8a372014-09-23 09:50:21 -07001380bool SkProcCoeffXfermode::asFragmentProcessor(GrFragmentProcessor** fp,
1381 GrTexture* background) const {
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001382 if (XferEffect::IsSupportedMode(fMode)) {
joshualittb0a8a372014-09-23 09:50:21 -07001383 if (fp) {
1384 *fp = XferEffect::Create(fMode, background);
1385 SkASSERT(*fp);
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001386 }
1387 return true;
1388 }
1389 return false;
1390}
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001391#endif
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001392
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001393const char* SkXfermode::ModeName(Mode mode) {
1394 SkASSERT((unsigned) mode <= (unsigned)kLastMode);
1395 const char* gModeStrings[] = {
1396 "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn",
1397 "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Plus",
1398 "Modulate", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge",
1399 "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion",
1400 "Multiply", "Hue", "Saturation", "Color", "Luminosity"
1401 };
1402 return gModeStrings[mode];
1403 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gModeStrings) == kLastMode + 1, mode_count);
1404}
1405
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001406#ifndef SK_IGNORE_TO_STRING
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001407void SkProcCoeffXfermode::toString(SkString* str) const {
1408 str->append("SkProcCoeffXfermode: ");
1409
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001410 str->append("mode: ");
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001411 str->append(ModeName(fMode));
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001412
1413 static const char* gCoeffStrings[kCoeffCount] = {
skia.committer@gmail.com98ded842013-01-23 07:06:17 +00001414 "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001415 };
1416
1417 str->append(" src: ");
1418 if (CANNOT_USE_COEFF == fSrcCoeff) {
1419 str->append("can't use");
1420 } else {
1421 str->append(gCoeffStrings[fSrcCoeff]);
1422 }
1423
1424 str->append(" dst: ");
1425 if (CANNOT_USE_COEFF == fDstCoeff) {
1426 str->append("can't use");
1427 } else {
1428 str->append(gCoeffStrings[fDstCoeff]);
1429 }
1430}
1431#endif
1432
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433///////////////////////////////////////////////////////////////////////////////
1434
1435class SkClearXfermode : public SkProcCoeffXfermode {
1436public:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001437 static SkClearXfermode* Create(const ProcCoeff& rec) {
1438 return SkNEW_ARGS(SkClearXfermode, (rec));
1439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440
reed@google.com30da7452012-12-17 19:55:24 +00001441 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1442 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001444 SK_TO_STRING_OVERRIDE()
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001445
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446private:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001447 SkClearXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kClear_Mode) {}
reed9fa60da2014-08-21 07:59:51 -07001448#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
1449 SkClearXfermode(SkReadBuffer& buffer) : SkProcCoeffXfermode(buffer) {}
1450#endif
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001451
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001452 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453};
1454
reed@google.com86ab6c62011-11-28 15:26:14 +00001455void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1456 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001457 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001458 SkASSERT(dst && count >= 0);
1459
1460 if (NULL == aa) {
1461 memset(dst, 0, count << 2);
1462 } else {
1463 for (int i = count - 1; i >= 0; --i) {
1464 unsigned a = aa[i];
1465 if (0xFF == a) {
1466 dst[i] = 0;
1467 } else if (a != 0) {
1468 dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
1469 }
1470 }
1471 }
1472}
1473void SkClearXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1474 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001475 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001476 SkASSERT(dst && count >= 0);
1477
1478 if (NULL == aa) {
1479 memset(dst, 0, count);
1480 } else {
1481 for (int i = count - 1; i >= 0; --i) {
1482 unsigned a = aa[i];
1483 if (0xFF == a) {
1484 dst[i] = 0;
1485 } else if (0 != a) {
1486 dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
1487 }
1488 }
1489 }
1490}
1491
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001492#ifndef SK_IGNORE_TO_STRING
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001493void SkClearXfermode::toString(SkString* str) const {
1494 this->INHERITED::toString(str);
1495}
1496#endif
1497
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498///////////////////////////////////////////////////////////////////////////////
1499
1500class SkSrcXfermode : public SkProcCoeffXfermode {
1501public:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001502 static SkSrcXfermode* Create(const ProcCoeff& rec) {
1503 return SkNEW_ARGS(SkSrcXfermode, (rec));
1504 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505
reed@google.com30da7452012-12-17 19:55:24 +00001506 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1507 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001509 SK_TO_STRING_OVERRIDE()
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001510
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511private:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001512 SkSrcXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kSrc_Mode) {}
reed9fa60da2014-08-21 07:59:51 -07001513#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
1514 SkSrcXfermode(SkReadBuffer& buffer) : SkProcCoeffXfermode(buffer) {}
1515#endif
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001516 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517};
1518
reed@google.com86ab6c62011-11-28 15:26:14 +00001519void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1520 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001521 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001522 SkASSERT(dst && src && count >= 0);
1523
1524 if (NULL == aa) {
1525 memcpy(dst, src, count << 2);
1526 } else {
1527 for (int i = count - 1; i >= 0; --i) {
1528 unsigned a = aa[i];
1529 if (a == 0xFF) {
1530 dst[i] = src[i];
1531 } else if (a != 0) {
1532 dst[i] = SkFourByteInterp(src[i], dst[i], a);
1533 }
1534 }
1535 }
1536}
1537
1538void SkSrcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1539 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001540 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001541 SkASSERT(dst && src && count >= 0);
1542
1543 if (NULL == aa) {
1544 for (int i = count - 1; i >= 0; --i) {
1545 dst[i] = SkToU8(SkGetPackedA32(src[i]));
1546 }
1547 } else {
1548 for (int i = count - 1; i >= 0; --i) {
1549 unsigned a = aa[i];
1550 if (0 != a) {
1551 unsigned srcA = SkGetPackedA32(src[i]);
1552 if (a == 0xFF) {
1553 dst[i] = SkToU8(srcA);
1554 } else {
1555 dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a));
1556 }
1557 }
1558 }
1559 }
1560}
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001561#ifndef SK_IGNORE_TO_STRING
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001562void SkSrcXfermode::toString(SkString* str) const {
1563 this->INHERITED::toString(str);
1564}
1565#endif
reed@google.com86ab6c62011-11-28 15:26:14 +00001566
reed@google.com30da7452012-12-17 19:55:24 +00001567///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001568
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569class SkDstInXfermode : public SkProcCoeffXfermode {
1570public:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001571 static SkDstInXfermode* Create(const ProcCoeff& rec) {
1572 return SkNEW_ARGS(SkDstInXfermode, (rec));
1573 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001574
reed@google.com30da7452012-12-17 19:55:24 +00001575 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001576
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001577 SK_TO_STRING_OVERRIDE()
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001578
1579private:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001580 SkDstInXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstIn_Mode) {}
reed9fa60da2014-08-21 07:59:51 -07001581#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001582 SkDstInXfermode(SkReadBuffer& buffer) : INHERITED(buffer) {}
reed9fa60da2014-08-21 07:59:51 -07001583#endif
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001584
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 typedef SkProcCoeffXfermode INHERITED;
1586};
1587
reed@google.com86ab6c62011-11-28 15:26:14 +00001588void SkDstInXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1589 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001590 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001591 SkASSERT(dst && src);
1592
1593 if (count <= 0) {
1594 return;
1595 }
bsalomon49f085d2014-09-05 13:34:00 -07001596 if (aa) {
reed@google.com86ab6c62011-11-28 15:26:14 +00001597 return this->INHERITED::xfer32(dst, src, count, aa);
1598 }
1599
1600 do {
1601 unsigned a = SkGetPackedA32(*src);
1602 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
1603 dst++;
1604 src++;
1605 } while (--count != 0);
1606}
1607
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001608#ifndef SK_IGNORE_TO_STRING
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001609void SkDstInXfermode::toString(SkString* str) const {
1610 this->INHERITED::toString(str);
1611}
1612#endif
1613
reed@google.com30da7452012-12-17 19:55:24 +00001614///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001615
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616class SkDstOutXfermode : public SkProcCoeffXfermode {
1617public:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001618 static SkDstOutXfermode* Create(const ProcCoeff& rec) {
1619 return SkNEW_ARGS(SkDstOutXfermode, (rec));
1620 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001621
reed@google.com30da7452012-12-17 19:55:24 +00001622 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001623
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001624 SK_TO_STRING_OVERRIDE()
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001625
1626private:
commit-bot@chromium.org0a2bf902014-02-20 20:40:19 +00001627 SkDstOutXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstOut_Mode) {}
reed9fa60da2014-08-21 07:59:51 -07001628#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
1629 SkDstOutXfermode(SkReadBuffer& buffer) : INHERITED(buffer) {}
1630#endif
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001631
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 typedef SkProcCoeffXfermode INHERITED;
1633};
1634
reed@google.com86ab6c62011-11-28 15:26:14 +00001635void SkDstOutXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1636 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001637 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001638 SkASSERT(dst && src);
1639
1640 if (count <= 0) {
1641 return;
1642 }
bsalomon49f085d2014-09-05 13:34:00 -07001643 if (aa) {
reed@google.com86ab6c62011-11-28 15:26:14 +00001644 return this->INHERITED::xfer32(dst, src, count, aa);
1645 }
1646
1647 do {
1648 unsigned a = SkGetPackedA32(*src);
1649 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
1650 dst++;
1651 src++;
1652 } while (--count != 0);
1653}
1654
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001655#ifndef SK_IGNORE_TO_STRING
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001656void SkDstOutXfermode::toString(SkString* str) const {
1657 this->INHERITED::toString(str);
1658}
1659#endif
1660
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661///////////////////////////////////////////////////////////////////////////////
1662
commit-bot@chromium.org97de3572014-05-29 20:13:22 +00001663extern SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec, SkXfermode::Mode mode);
commit-bot@chromium.orgd6118642013-12-06 11:32:27 +00001664extern SkXfermodeProc SkPlatformXfermodeProcFactory(SkXfermode::Mode mode);
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001665
commit-bot@chromium.org97de3572014-05-29 20:13:22 +00001666// Technically, can't be static and passed as a template parameter. So we use anonymous namespace.
1667namespace {
1668SkXfermode* create_mode(int iMode) {
1669 SkXfermode::Mode mode = (SkXfermode::Mode)iMode;
commit-bot@chromium.org140950c2014-03-28 20:04:11 +00001670
1671 ProcCoeff rec = gProcCoeffs[mode];
1672 SkXfermodeProc pp = SkPlatformXfermodeProcFactory(mode);
1673 if (pp != NULL) {
1674 rec.fProc = pp;
1675 }
1676
1677 SkXfermode* xfer = NULL;
1678 // check if we have a platform optim for that
1679 SkProcCoeffXfermode* xfm = SkPlatformXfermodeFactory(rec, mode);
1680 if (xfm != NULL) {
1681 xfer = xfm;
1682 } else {
1683 // All modes can in theory be represented by the ProcCoeff rec, since
1684 // it contains function ptrs. However, a few modes are both simple and
1685 // commonly used, so we call those out for their own subclasses here.
1686 switch (mode) {
1687 case SkXfermode::kClear_Mode:
1688 xfer = SkClearXfermode::Create(rec);
1689 break;
1690 case SkXfermode::kSrc_Mode:
1691 xfer = SkSrcXfermode::Create(rec);
1692 break;
1693 case SkXfermode::kSrcOver_Mode:
1694 SkASSERT(false); // should not land here
1695 break;
1696 case SkXfermode::kDstIn_Mode:
1697 xfer = SkDstInXfermode::Create(rec);
1698 break;
1699 case SkXfermode::kDstOut_Mode:
1700 xfer = SkDstOutXfermode::Create(rec);
1701 break;
1702 default:
1703 // no special-case, just rely in the rec and its function-ptrs
reed9fa60da2014-08-21 07:59:51 -07001704 xfer = SkNEW_ARGS(SkProcCoeffXfermode, (rec, mode));
commit-bot@chromium.org140950c2014-03-28 20:04:11 +00001705 break;
1706 }
1707 }
commit-bot@chromium.org97de3572014-05-29 20:13:22 +00001708 return xfer;
commit-bot@chromium.org140950c2014-03-28 20:04:11 +00001709}
commit-bot@chromium.org97de3572014-05-29 20:13:22 +00001710} // namespace
1711
commit-bot@chromium.org140950c2014-03-28 20:04:11 +00001712
reed@android.coma0f5d152009-06-22 17:38:10 +00001713SkXfermode* SkXfermode::Create(Mode mode) {
1714 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001716 if ((unsigned)mode >= kModeCount) {
1717 // report error
1718 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719 }
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001720
commit-bot@chromium.org97de3572014-05-29 20:13:22 +00001721 // Skia's "default" mode is srcover. NULL in SkPaint is interpreted as srcover
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001722 // so we can just return NULL from the factory.
1723 if (kSrcOver_Mode == mode) {
1724 return NULL;
1725 }
1726
commit-bot@chromium.org97de3572014-05-29 20:13:22 +00001727 SK_DECLARE_STATIC_LAZY_PTR_ARRAY(SkXfermode, cached, kModeCount, create_mode);
1728 return SkSafeRef(cached[mode]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729}
1730
reed@google.com43c50c82011-04-14 15:50:52 +00001731SkXfermodeProc SkXfermode::GetProc(Mode mode) {
1732 SkXfermodeProc proc = NULL;
1733 if ((unsigned)mode < kModeCount) {
1734 proc = gProcCoeffs[mode].fProc;
1735 }
1736 return proc;
1737}
1738
1739bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
1740 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001741
reed@google.com43c50c82011-04-14 15:50:52 +00001742 if ((unsigned)mode >= (unsigned)kModeCount) {
1743 // illegal mode parameter
1744 return false;
1745 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001746
reed@google.com43c50c82011-04-14 15:50:52 +00001747 const ProcCoeff& rec = gProcCoeffs[mode];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001748
reed@google.com43c50c82011-04-14 15:50:52 +00001749 if (CANNOT_USE_COEFF == rec.fSC) {
1750 return false;
1751 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001752
reed@google.com43c50c82011-04-14 15:50:52 +00001753 SkASSERT(CANNOT_USE_COEFF != rec.fDC);
1754 if (src) {
1755 *src = rec.fSC;
1756 }
1757 if (dst) {
1758 *dst = rec.fDC;
1759 }
1760 return true;
1761}
1762
reed@google.com30da7452012-12-17 19:55:24 +00001763bool SkXfermode::AsMode(const SkXfermode* xfer, Mode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 if (NULL == xfer) {
1765 if (mode) {
1766 *mode = kSrcOver_Mode;
1767 }
1768 return true;
1769 }
reed@google.comc0d4aa22011-04-13 21:12:04 +00001770 return xfer->asMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771}
1772
reed@google.com30da7452012-12-17 19:55:24 +00001773bool SkXfermode::AsCoeff(const SkXfermode* xfer, Coeff* src, Coeff* dst) {
reed@google.com43c50c82011-04-14 15:50:52 +00001774 if (NULL == xfer) {
1775 return ModeAsCoeff(kSrcOver_Mode, src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776 }
reed@google.com43c50c82011-04-14 15:50:52 +00001777 return xfer->asCoeff(src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778}
1779
reed@google.com30da7452012-12-17 19:55:24 +00001780bool SkXfermode::IsMode(const SkXfermode* xfer, Mode mode) {
mike@reedtribe.orge303fcf2011-11-17 02:16:43 +00001781 // if xfer==null then the mode is srcover
1782 Mode m = kSrcOver_Mode;
1783 if (xfer && !xfer->asMode(&m)) {
1784 return false;
1785 }
1786 return mode == m;
1787}
1788
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789///////////////////////////////////////////////////////////////////////////////
1790//////////// 16bit xfermode procs
1791
1792#ifdef SK_DEBUG
1793static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
1794static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
1795#endif
1796
1797static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
1798 SkASSERT(require_255(src));
1799 return SkPixel32ToPixel16(src);
1800}
1801
1802static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
1803 return dst;
1804}
1805
1806static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
1807 SkASSERT(require_0(src));
1808 return dst;
1809}
1810
1811static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
1812 SkASSERT(require_255(src));
1813 return SkPixel32ToPixel16(src);
1814}
1815
1816static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
1817 SkASSERT(require_0(src));
1818 return dst;
1819}
1820
1821static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
1822 SkASSERT(require_255(src));
1823 return dst;
1824}
1825
1826static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
1827 SkASSERT(require_255(src));
1828 return SkPixel32ToPixel16(src);
1829}
1830
1831static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
1832 SkASSERT(require_255(src));
1833 return dst;
1834}
1835
1836static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
1837 SkASSERT(require_0(src));
1838 return dst;
1839}
1840
1841static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
1842 unsigned isa = 255 - SkGetPackedA32(src);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001843
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844 return SkPackRGB16(
1845 SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
1846 SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
1847 SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
1848}
1849
1850static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
1851 SkASSERT(require_0(src));
1852 return dst;
1853}
1854
1855static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1856 SkASSERT(require_255(src));
1857 return SkPixel32ToPixel16(src);
1858}
1859
1860static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1861 SkASSERT(require_255(src));
1862 return dst;
1863}
1864
1865/*********
1866 darken and lighten boil down to this.
1867
1868 darken = (1 - Sa) * Dc + min(Sc, Dc)
1869 lighten = (1 - Sa) * Dc + max(Sc, Dc)
1870
1871 if (Sa == 0) these become
1872 darken = Dc + min(0, Dc) = 0
1873 lighten = Dc + max(0, Dc) = Dc
1874
1875 if (Sa == 1) these become
1876 darken = min(Sc, Dc)
1877 lighten = max(Sc, Dc)
1878*/
1879
1880static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
1881 SkASSERT(require_0(src));
1882 return 0;
1883}
1884
1885static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
1886 SkASSERT(require_255(src));
1887 unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1888 unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1889 unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1890 return SkPackRGB16(r, g, b);
1891}
1892
1893static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
1894 SkASSERT(require_0(src));
1895 return dst;
1896}
1897
1898static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
1899 SkASSERT(require_255(src));
1900 unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1901 unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1902 unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1903 return SkPackRGB16(r, g, b);
1904}
1905
1906struct Proc16Rec {
1907 SkXfermodeProc16 fProc16_0;
1908 SkXfermodeProc16 fProc16_255;
1909 SkXfermodeProc16 fProc16_General;
1910};
1911
reed@android.coma0f5d152009-06-22 17:38:10 +00001912static const Proc16Rec gModeProcs16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 { NULL, NULL, NULL }, // CLEAR
1914 { NULL, src_modeproc16_255, NULL },
1915 { dst_modeproc16, dst_modeproc16, dst_modeproc16 },
1916 { srcover_modeproc16_0, srcover_modeproc16_255, NULL },
1917 { dstover_modeproc16_0, dstover_modeproc16_255, NULL },
1918 { NULL, srcin_modeproc16_255, NULL },
1919 { NULL, dstin_modeproc16_255, NULL },
1920 { NULL, NULL, NULL },// SRC_OUT
1921 { dstout_modeproc16_0, NULL, NULL },
1922 { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16 },
1923 { NULL, dstatop_modeproc16_255, NULL },
1924 { NULL, NULL, NULL }, // XOR
reed@android.coma0f5d152009-06-22 17:38:10 +00001925
1926 { NULL, NULL, NULL }, // plus
reed@google.com8d3cd7a2013-01-30 21:36:11 +00001927 { NULL, NULL, NULL }, // modulate
reed@android.coma0f5d152009-06-22 17:38:10 +00001928 { NULL, NULL, NULL }, // screen
1929 { NULL, NULL, NULL }, // overlay
1930 { darken_modeproc16_0, darken_modeproc16_255, NULL }, // darken
1931 { lighten_modeproc16_0, lighten_modeproc16_255, NULL }, // lighten
1932 { NULL, NULL, NULL }, // colordodge
1933 { NULL, NULL, NULL }, // colorburn
1934 { NULL, NULL, NULL }, // hardlight
1935 { NULL, NULL, NULL }, // softlight
1936 { NULL, NULL, NULL }, // difference
1937 { NULL, NULL, NULL }, // exclusion
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +00001938 { NULL, NULL, NULL }, // multiply
1939 { NULL, NULL, NULL }, // hue
1940 { NULL, NULL, NULL }, // saturation
1941 { NULL, NULL, NULL }, // color
1942 { NULL, NULL, NULL }, // luminosity
reed@android.com8a1c16f2008-12-17 15:59:43 +00001943};
1944
reed@android.coma0f5d152009-06-22 17:38:10 +00001945SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946 SkXfermodeProc16 proc16 = NULL;
reed@android.coma0f5d152009-06-22 17:38:10 +00001947 if ((unsigned)mode < kModeCount) {
1948 const Proc16Rec& rec = gModeProcs16[mode];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949 unsigned a = SkColorGetA(srcColor);
1950
1951 if (0 == a) {
1952 proc16 = rec.fProc16_0;
1953 } else if (255 == a) {
1954 proc16 = rec.fProc16_255;
1955 } else {
1956 proc16 = rec.fProc16_General;
1957 }
1958 }
1959 return proc16;
1960}
1961
caryclark@google.comd26147a2011-12-15 14:16:43 +00001962SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermode)
1963 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkProcCoeffXfermode)
caryclark@google.comd26147a2011-12-15 14:16:43 +00001964SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END