blob: 6cdd97bb4c567fef6ddcab37c91777df688d4068 [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.orgdf187c72013-10-09 14:39:46 +000011#include "SkXfermode_proccoeff.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColorPriv.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000013#include "SkFlattenableBuffers.h"
reed@google.com4b163ed2012-08-07 21:35:13 +000014#include "SkMathPriv.h"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +000015#include "SkString.h"
commit-bot@chromium.orgcd7992b2013-10-17 16:29:34 +000016#include "SkUtilsArm.h"
17
18#if !SK_ARM_NEON_IS_NONE
19#include "SkXfermode_opts_arm_neon.h"
20#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000021
robertphillips@google.com0456e0b2012-06-27 14:03:26 +000022SK_DEFINE_INST_COUNT(SkXfermode)
23
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#define SkAlphaMulAlpha(a, b) SkMulDiv255Round(a, b)
25
reed@android.comfc25abd2009-01-15 14:38:33 +000026#if 0
reed@android.com8a1c16f2008-12-17 15:59:43 +000027// idea for higher precision blends in xfer procs (and slightly faster)
28// see DstATop as a probable caller
29static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
30 SkASSERT(a <= 255);
31 SkASSERT(b <= 255);
32 SkASSERT(c <= 255);
33 SkASSERT(d <= 255);
34 unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
35 unsigned result = (prod + (prod >> 8)) >> 8;
36 SkASSERT(result <= 255);
37 return result;
38}
reed@android.comfc25abd2009-01-15 14:38:33 +000039#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
deanm@chromium.orgda946992009-07-03 12:54:24 +000041static inline unsigned saturated_add(unsigned a, unsigned b) {
reed@android.com543ed932009-04-24 12:43:40 +000042 SkASSERT(a <= 255);
43 SkASSERT(b <= 255);
44 unsigned sum = a + b;
45 if (sum > 255) {
46 sum = 255;
47 }
48 return sum;
49}
50
deanm@chromium.orgda946992009-07-03 12:54:24 +000051static inline int clamp_signed_byte(int n) {
reed@android.coma0f5d152009-06-22 17:38:10 +000052 if (n < 0) {
53 n = 0;
54 } else if (n > 255) {
55 n = 255;
56 }
57 return n;
58}
59
deanm@chromium.orgda946992009-07-03 12:54:24 +000060static inline int clamp_div255round(int prod) {
reed@android.coma0f5d152009-06-22 17:38:10 +000061 if (prod <= 0) {
62 return 0;
63 } else if (prod >= 255*255) {
64 return 255;
65 } else {
66 return SkDiv255Round(prod);
67 }
68}
69
reed@android.com8a1c16f2008-12-17 15:59:43 +000070///////////////////////////////////////////////////////////////////////////////
71
reed@android.com8a1c16f2008-12-17 15:59:43 +000072// kClear_Mode, //!< [0, 0]
73static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
74 return 0;
75}
76
77// kSrc_Mode, //!< [Sa, Sc]
78static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) {
79 return src;
80}
81
82// kDst_Mode, //!< [Da, Dc]
83static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) {
84 return dst;
85}
86
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000087// kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Sc + (1 - Sa)*Dc]
reed@android.com8a1c16f2008-12-17 15:59:43 +000088static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +000089#if 0
90 // this is the old, more-correct way, but it doesn't guarantee that dst==255
91 // will always stay opaque
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
reed@android.com1116fb22009-03-03 20:31:12 +000093#else
94 // this is slightly faster, but more importantly guarantees that dst==255
95 // will always stay opaque
96 return src + SkAlphaMulQ(dst, 256 - SkGetPackedA32(src));
97#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000098}
99
reed@android.com1116fb22009-03-03 20:31:12 +0000100// kDstOver_Mode, //!< [Sa + Da - Sa*Da, Dc + (1 - Da)*Sc]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +0000102 // this is the reverse of srcover, just flipping src and dst
103 // see srcover's comment about the 256 for opaqueness guarantees
104 return dst + SkAlphaMulQ(src, 256 - SkGetPackedA32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105}
106
107// kSrcIn_Mode, //!< [Sa * Da, Sc * Da]
108static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) {
109 return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst)));
110}
111
112// kDstIn_Mode, //!< [Sa * Da, Sa * Dc]
113static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) {
114 return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src)));
115}
116
117// kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)]
118static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) {
119 return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst)));
120}
121
122// kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)]
123static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) {
124 return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
125}
126
127// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc]
128static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) {
129 unsigned sa = SkGetPackedA32(src);
130 unsigned da = SkGetPackedA32(dst);
131 unsigned isa = 255 - sa;
132
133 return SkPackARGB32(da,
134 SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
135 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
136 SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
137 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
138 SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
139 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
140}
141
142// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
143static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) {
144 unsigned sa = SkGetPackedA32(src);
145 unsigned da = SkGetPackedA32(dst);
146 unsigned ida = 255 - da;
147
148 return SkPackARGB32(sa,
149 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
150 SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
151 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
152 SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
153 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
154 SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
155}
156
157// kXor_Mode [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
158static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) {
159 unsigned sa = SkGetPackedA32(src);
160 unsigned da = SkGetPackedA32(dst);
161 unsigned isa = 255 - sa;
162 unsigned ida = 255 - da;
163
164 return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
165 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
166 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
167 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
168 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
169 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
170 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
171}
172
reed@android.coma0f5d152009-06-22 17:38:10 +0000173///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
reed@android.coma0f5d152009-06-22 17:38:10 +0000175// kPlus_Mode
176static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000177 unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
deanm@chromium.orgda946992009-07-03 12:54:24 +0000178 unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
179 unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
180 unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
reed@android.coma0f5d152009-06-22 17:38:10 +0000181 return SkPackARGB32(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182}
183
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000184// kModulate_Mode
185static SkPMColor modulate_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
187 int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
188 int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
189 int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
190 return SkPackARGB32(a, r, g, b);
191}
192
reed@android.coma0f5d152009-06-22 17:38:10 +0000193static inline int srcover_byte(int a, int b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 return a + b - SkAlphaMulAlpha(a, b);
195}
reed@google.com25cfa692013-02-04 20:06:00 +0000196
197// kMultiply_Mode
198// B(Cb, Cs) = Cb x Cs
199// multiply uses its own version of blendfunc_byte because sa and da are not needed
200static int blendfunc_multiply_byte(int sc, int dc, int sa, int da) {
201 return clamp_div255round(sc * (255 - da) + dc * (255 - sa) + sc * dc);
202}
203
204static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
205 int sa = SkGetPackedA32(src);
206 int da = SkGetPackedA32(dst);
207 int a = srcover_byte(sa, da);
208 int r = blendfunc_multiply_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
209 int g = blendfunc_multiply_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
210 int b = blendfunc_multiply_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
211 return SkPackARGB32(a, r, g, b);
212}
213
214// kScreen_Mode
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000216 int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
217 int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
218 int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
219 int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 return SkPackARGB32(a, r, g, b);
221}
222
reed@android.coma0f5d152009-06-22 17:38:10 +0000223// kOverlay_Mode
224static inline int overlay_byte(int sc, int dc, int sa, int da) {
225 int tmp = sc * (255 - da) + dc * (255 - sa);
226 int rc;
227 if (2 * dc <= da) {
228 rc = 2 * sc * dc;
229 } else {
230 rc = sa * da - 2 * (da - dc) * (sa - sc);
231 }
232 return clamp_div255round(rc + tmp);
233}
234static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
235 int sa = SkGetPackedA32(src);
236 int da = SkGetPackedA32(dst);
237 int a = srcover_byte(sa, da);
238 int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
239 int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
240 int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
241 return SkPackARGB32(a, r, g, b);
242}
243
244// kDarken_Mode
245static inline int darken_byte(int sc, int dc, int sa, int da) {
246 int sd = sc * da;
247 int ds = dc * sa;
248 if (sd < ds) {
249 // srcover
250 return sc + dc - SkDiv255Round(ds);
251 } else {
252 // dstover
253 return dc + sc - SkDiv255Round(sd);
254 }
255}
256static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
257 int sa = SkGetPackedA32(src);
258 int da = SkGetPackedA32(dst);
259 int a = srcover_byte(sa, da);
260 int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
261 int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
262 int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
263 return SkPackARGB32(a, r, g, b);
264}
265
266// kLighten_Mode
267static inline int lighten_byte(int sc, int dc, int sa, int da) {
268 int sd = sc * da;
269 int ds = dc * sa;
270 if (sd > ds) {
271 // srcover
272 return sc + dc - SkDiv255Round(ds);
273 } else {
274 // dstover
275 return dc + sc - SkDiv255Round(sd);
276 }
277}
278static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
279 int sa = SkGetPackedA32(src);
280 int da = SkGetPackedA32(dst);
281 int a = srcover_byte(sa, da);
282 int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
283 int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
284 int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
285 return SkPackARGB32(a, r, g, b);
286}
287
288// kColorDodge_Mode
289static inline int colordodge_byte(int sc, int dc, int sa, int da) {
290 int diff = sa - sc;
291 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000292 if (0 == dc) {
293 return SkAlphaMulAlpha(sc, 255 - da);
294 } else if (0 == diff) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000295 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000296 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000297 diff = dc * sa / diff;
298 rc = sa * ((da < diff) ? da : diff) + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000299 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000300 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000301}
302static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000303 int sa = SkGetPackedA32(src);
304 int da = SkGetPackedA32(dst);
305 int a = srcover_byte(sa, da);
306 int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
307 int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
308 int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.coma0f5d152009-06-22 17:38:10 +0000309 return SkPackARGB32(a, r, g, b);
310}
311
312// kColorBurn_Mode
313static inline int colorburn_byte(int sc, int dc, int sa, int da) {
314 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000315 if (dc == da) {
316 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000317 } else if (0 == sc) {
318 return SkAlphaMulAlpha(dc, 255 - sa);
319 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000320 int tmp = (da - dc) * sa / sc;
321 rc = sa * (da - ((da < tmp) ? da : tmp))
322 + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000323 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000324 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000325}
326static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000327 int sa = SkGetPackedA32(src);
328 int da = SkGetPackedA32(dst);
329 int a = srcover_byte(sa, da);
330 int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
331 int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
332 int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
333 return SkPackARGB32(a, r, g, b);
334}
335
336// kHardLight_Mode
337static inline int hardlight_byte(int sc, int dc, int sa, int da) {
338 int rc;
339 if (2 * sc <= sa) {
340 rc = 2 * sc * dc;
341 } else {
342 rc = sa * da - 2 * (da - dc) * (sa - sc);
343 }
344 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
345}
346static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
347 int sa = SkGetPackedA32(src);
348 int da = SkGetPackedA32(dst);
349 int a = srcover_byte(sa, da);
350 int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
351 int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
352 int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
353 return SkPackARGB32(a, r, g, b);
354}
355
356// returns 255 * sqrt(n/255)
357static U8CPU sqrt_unit_byte(U8CPU n) {
358 return SkSqrtBits(n, 15+4);
359}
360
361// kSoftLight_Mode
362static inline int softlight_byte(int sc, int dc, int sa, int da) {
363 int m = da ? dc * 256 / da : 0;
364 int rc;
365 if (2 * sc <= sa) {
366 rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
367 } else if (4 * dc <= da) {
368 int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
369 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
370 } else {
371 int tmp = sqrt_unit_byte(m) - m;
372 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
373 }
374 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
375}
376static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
377 int sa = SkGetPackedA32(src);
378 int da = SkGetPackedA32(dst);
379 int a = srcover_byte(sa, da);
380 int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
381 int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
382 int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
383 return SkPackARGB32(a, r, g, b);
384}
385
386// kDifference_Mode
387static inline int difference_byte(int sc, int dc, int sa, int da) {
388 int tmp = SkMin32(sc * da, dc * sa);
389 return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
390}
391static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
392 int sa = SkGetPackedA32(src);
393 int da = SkGetPackedA32(dst);
394 int a = srcover_byte(sa, da);
395 int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
396 int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
397 int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
398 return SkPackARGB32(a, r, g, b);
399}
400
401// kExclusion_Mode
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000402static inline int exclusion_byte(int sc, int dc, int, int) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000403 // this equations is wacky, wait for SVG to confirm it
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000404 //int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
405
406 // The above equation can be simplified as follows
407 int r = 255*(sc + dc) - 2 * sc * dc;
reed@android.coma0f5d152009-06-22 17:38:10 +0000408 return clamp_div255round(r);
409}
410static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
411 int sa = SkGetPackedA32(src);
412 int da = SkGetPackedA32(dst);
413 int a = srcover_byte(sa, da);
414 int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
415 int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
416 int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.com543ed932009-04-24 12:43:40 +0000417 return SkPackARGB32(a, r, g, b);
418}
419
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000420// The CSS compositing spec introduces the following formulas:
421// (See https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnonseparable)
422// SkComputeLuminance is similar to this formula but it uses the new definition from Rec. 709
423// while PDF and CG uses the one from Rec. Rec. 601
424// See http://www.glennchan.info/articles/technical/hd-versus-sd-color-space/hd-versus-sd-color-space.htm
425static inline int Lum(int r, int g, int b)
426{
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000427 return SkDiv255Round(r * 77 + g * 150 + b * 28);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000428}
429
430static inline int min2(int a, int b) { return a < b ? a : b; }
431static inline int max2(int a, int b) { return a > b ? a : b; }
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000432#define minimum(a, b, c) min2(min2(a, b), c)
433#define maximum(a, b, c) max2(max2(a, b), c)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000434
435static inline int Sat(int r, int g, int b) {
436 return maximum(r, g, b) - minimum(r, g, b);
437}
438
439static inline void setSaturationComponents(int* Cmin, int* Cmid, int* Cmax, int s) {
reed@google.com3c1ea3a2013-03-07 15:31:58 +0000440 if(*Cmax > *Cmin) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000441 *Cmid = SkMulDiv(*Cmid - *Cmin, s, *Cmax - *Cmin);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000442 *Cmax = s;
443 } else {
444 *Cmax = 0;
445 *Cmid = 0;
446 }
447
448 *Cmin = 0;
449}
450
451static inline void SetSat(int* r, int* g, int* b, int s) {
452 if(*r <= *g) {
453 if(*g <= *b) {
454 setSaturationComponents(r, g, b, s);
455 } else if(*r <= *b) {
456 setSaturationComponents(r, b, g, s);
457 } else {
458 setSaturationComponents(b, r, g, s);
459 }
460 } else if(*r <= *b) {
461 setSaturationComponents(g, r, b, s);
462 } else if(*g <= *b) {
463 setSaturationComponents(g, b, r, s);
464 } else {
465 setSaturationComponents(b, g, r, s);
466 }
467}
468
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000469static inline void clipColor(int* r, int* g, int* b, int a) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000470 int L = Lum(*r, *g, *b);
471 int n = minimum(*r, *g, *b);
472 int x = maximum(*r, *g, *b);
473 if(n < 0) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000474 *r = L + SkMulDiv(*r - L, L, L - n);
475 *g = L + SkMulDiv(*g - L, L, L - n);
476 *b = L + SkMulDiv(*b - L, L, L - n);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000477 }
478
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000479 if (x > a) {
480 *r = L + SkMulDiv(*r - L, a - L, x - L);
481 *g = L + SkMulDiv(*g - L, a - L, x - L);
482 *b = L + SkMulDiv(*b - L, a - L, x - L);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000483 }
484}
485
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000486static inline void SetLum(int* r, int* g, int* b, int a, int l) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000487 int d = l - Lum(*r, *g, *b);
488 *r += d;
489 *g += d;
490 *b += d;
491
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000492 clipColor(r, g, b, a);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000493}
494
495// non-separable blend modes are done in non-premultiplied alpha
496#define blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000497 clamp_div255round(sc * (255 - da) + dc * (255 - sa) + blendval)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000498
499// kHue_Mode
500// B(Cb, Cs) = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
501// Create a color with the hue of the source color and the saturation and luminosity of the backdrop color.
502static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
503 int sr = SkGetPackedR32(src);
504 int sg = SkGetPackedG32(src);
505 int sb = SkGetPackedB32(src);
506 int sa = SkGetPackedA32(src);
507
508 int dr = SkGetPackedR32(dst);
509 int dg = SkGetPackedG32(dst);
510 int db = SkGetPackedB32(dst);
511 int da = SkGetPackedA32(dst);
512 int Sr, Sg, Sb;
513
514 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000515 Sr = sr * sa;
516 Sg = sg * sa;
517 Sb = sb * sa;
518 SetSat(&Sr, &Sg, &Sb, Sat(dr, dg, db) * sa);
519 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000520 } else {
521 Sr = 0;
522 Sg = 0;
523 Sb = 0;
524 }
525
526 int a = srcover_byte(sa, da);
527 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
528 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
529 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
530 return SkPackARGB32(a, r, g, b);
531}
532
533// kSaturation_Mode
534// B(Cb, Cs) = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000535// 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 +0000536static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
537 int sr = SkGetPackedR32(src);
538 int sg = SkGetPackedG32(src);
539 int sb = SkGetPackedB32(src);
540 int sa = SkGetPackedA32(src);
541
542 int dr = SkGetPackedR32(dst);
543 int dg = SkGetPackedG32(dst);
544 int db = SkGetPackedB32(dst);
545 int da = SkGetPackedA32(dst);
546 int Dr, Dg, Db;
547
548 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000549 Dr = dr * sa;
550 Dg = dg * sa;
551 Db = db * sa;
552 SetSat(&Dr, &Dg, &Db, Sat(sr, sg, sb) * da);
553 SetLum(&Dr, &Dg, &Db, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000554 } else {
555 Dr = 0;
556 Dg = 0;
557 Db = 0;
558 }
559
560 int a = srcover_byte(sa, da);
561 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
562 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
563 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
564 return SkPackARGB32(a, r, g, b);
565}
566
567// kColor_Mode
568// B(Cb, Cs) = SetLum(Cs, Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000569// 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 +0000570static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
571 int sr = SkGetPackedR32(src);
572 int sg = SkGetPackedG32(src);
573 int sb = SkGetPackedB32(src);
574 int sa = SkGetPackedA32(src);
575
576 int dr = SkGetPackedR32(dst);
577 int dg = SkGetPackedG32(dst);
578 int db = SkGetPackedB32(dst);
579 int da = SkGetPackedA32(dst);
580 int Sr, Sg, Sb;
581
582 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000583 Sr = sr * da;
584 Sg = sg * da;
585 Sb = sb * da;
586 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000587 } else {
588 Sr = 0;
589 Sg = 0;
590 Sb = 0;
591 }
592
593 int a = srcover_byte(sa, da);
594 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
595 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
596 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
597 return SkPackARGB32(a, r, g, b);
598}
599
600// kLuminosity_Mode
601// B(Cb, Cs) = SetLum(Cb, Lum(Cs))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000602// 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 +0000603static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
604 int sr = SkGetPackedR32(src);
605 int sg = SkGetPackedG32(src);
606 int sb = SkGetPackedB32(src);
607 int sa = SkGetPackedA32(src);
608
609 int dr = SkGetPackedR32(dst);
610 int dg = SkGetPackedG32(dst);
611 int db = SkGetPackedB32(dst);
612 int da = SkGetPackedA32(dst);
613 int Dr, Dg, Db;
614
615 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000616 Dr = dr * sa;
617 Dg = dg * sa;
618 Db = db * sa;
619 SetLum(&Dr, &Dg, &Db, sa * da, Lum(sr, sg, sb) * da);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000620 } else {
621 Dr = 0;
622 Dg = 0;
623 Db = 0;
624 }
625
626 int a = srcover_byte(sa, da);
627 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
628 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
629 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
630 return SkPackARGB32(a, r, g, b);
631}
632
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +0000633const ProcCoeff gProcCoeffs[] = {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000634 { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff },
635 { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff },
636 { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff },
637 { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff },
638 { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff },
639 { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff },
640 { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff },
641 { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff },
642 { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff },
643 { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff },
644 { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff },
645 { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff },
646
reed@google.com521e34e2011-04-12 18:55:21 +0000647 { plus_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kOne_Coeff },
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000648 { modulate_modeproc,SkXfermode::kZero_Coeff, SkXfermode::kSC_Coeff },
bsalomon@google.comb0091b82013-04-15 15:16:47 +0000649 { screen_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISC_Coeff },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000650 { overlay_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
651 { darken_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
652 { lighten_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
653 { colordodge_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
654 { colorburn_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
655 { hardlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
656 { softlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
657 { difference_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
658 { exclusion_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
reed@google.com25cfa692013-02-04 20:06:00 +0000659 { multiply_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000660 { hue_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
661 { saturation_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
662 { color_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
663 { luminosity_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000664};
665
666///////////////////////////////////////////////////////////////////////////////
667
reed@google.com30da7452012-12-17 19:55:24 +0000668bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000669 return false;
670}
671
reed@google.com30da7452012-12-17 19:55:24 +0000672bool SkXfermode::asMode(Mode* mode) const {
reed@google.comc0d4aa22011-04-13 21:12:04 +0000673 return false;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000674}
675
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000676bool SkXfermode::asNewEffectOrCoeff(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst, GrTexture*) const {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000677 return this->asCoeff(src, dst);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000678}
679
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000680bool SkXfermode::AsNewEffectOrCoeff(SkXfermode* xfermode,
681 GrContext* context,
682 GrEffectRef** effect,
683 Coeff* src,
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000684 Coeff* dst,
685 GrTexture* background) {
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000686 if (NULL == xfermode) {
687 return ModeAsCoeff(kSrcOver_Mode, src, dst);
688 } else {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000689 return xfermode->asNewEffectOrCoeff(context, effect, src, dst, background);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000690 }
691}
692
reed@google.com30da7452012-12-17 19:55:24 +0000693SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) const{
vandebo@chromium.org48543272011-02-08 19:28:07 +0000694 // no-op. subclasses should override this
695 return dst;
696}
697
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000698void SkXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
699 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000700 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000701 SkASSERT(dst && src && count >= 0);
702
703 if (NULL == aa) {
704 for (int i = count - 1; i >= 0; --i) {
705 dst[i] = this->xferColor(src[i], dst[i]);
706 }
707 } else {
708 for (int i = count - 1; i >= 0; --i) {
709 unsigned a = aa[i];
710 if (0 != a) {
711 SkPMColor dstC = dst[i];
712 SkPMColor C = this->xferColor(src[i], dstC);
713 if (0xFF != a) {
714 C = SkFourByteInterp(C, dstC, a);
715 }
716 dst[i] = C;
717 }
718 }
719 }
720}
721
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000722void SkXfermode::xfer16(uint16_t* dst,
723 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000724 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000725 SkASSERT(dst && src && count >= 0);
726
727 if (NULL == aa) {
728 for (int i = count - 1; i >= 0; --i) {
729 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
730 dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
731 }
732 } else {
733 for (int i = count - 1; i >= 0; --i) {
734 unsigned a = aa[i];
735 if (0 != a) {
736 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
737 SkPMColor C = this->xferColor(src[i], dstC);
738 if (0xFF != a) {
739 C = SkFourByteInterp(C, dstC, a);
740 }
741 dst[i] = SkPixel32ToPixel16_ToU16(C);
742 }
743 }
744 }
745}
746
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000747void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
vandebo@chromium.org48543272011-02-08 19:28:07 +0000748 const SkPMColor src[], int count,
reed@google.com30da7452012-12-17 19:55:24 +0000749 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000750 SkASSERT(dst && src && count >= 0);
751
752 if (NULL == aa) {
753 for (int i = count - 1; i >= 0; --i) {
754 SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
755 dst[i] = SkToU8(SkGetPackedA32(res));
756 }
757 } else {
758 for (int i = count - 1; i >= 0; --i) {
759 unsigned a = aa[i];
760 if (0 != a) {
761 SkAlpha dstA = dst[i];
762 unsigned A = SkGetPackedA32(this->xferColor(src[i],
763 (SkPMColor)(dstA << SK_A32_SHIFT)));
764 if (0xFF != a) {
765 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
766 }
767 dst[i] = SkToU8(A);
768 }
769 }
770 }
771}
772
773///////////////////////////////////////////////////////////////////////////////
774
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000775void SkProcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
776 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000777 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000778 SkASSERT(dst && src && count >= 0);
779
780 SkXfermodeProc proc = fProc;
781
782 if (NULL != proc) {
783 if (NULL == aa) {
784 for (int i = count - 1; i >= 0; --i) {
785 dst[i] = proc(src[i], dst[i]);
786 }
787 } else {
788 for (int i = count - 1; i >= 0; --i) {
789 unsigned a = aa[i];
790 if (0 != a) {
791 SkPMColor dstC = dst[i];
792 SkPMColor C = proc(src[i], dstC);
793 if (a != 0xFF) {
794 C = SkFourByteInterp(C, dstC, a);
795 }
796 dst[i] = C;
797 }
798 }
799 }
800 }
801}
802
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000803void SkProcXfermode::xfer16(uint16_t* SK_RESTRICT dst,
804 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000805 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000806 SkASSERT(dst && src && count >= 0);
807
808 SkXfermodeProc proc = fProc;
809
810 if (NULL != proc) {
811 if (NULL == aa) {
812 for (int i = count - 1; i >= 0; --i) {
813 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
814 dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
815 }
816 } else {
817 for (int i = count - 1; i >= 0; --i) {
818 unsigned a = aa[i];
819 if (0 != a) {
820 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
821 SkPMColor C = proc(src[i], dstC);
822 if (0xFF != a) {
823 C = SkFourByteInterp(C, dstC, a);
824 }
825 dst[i] = SkPixel32ToPixel16_ToU16(C);
826 }
827 }
828 }
829 }
830}
831
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000832void SkProcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
833 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000834 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000835 SkASSERT(dst && src && count >= 0);
836
837 SkXfermodeProc proc = fProc;
838
839 if (NULL != proc) {
840 if (NULL == aa) {
841 for (int i = count - 1; i >= 0; --i) {
842 SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
843 dst[i] = SkToU8(SkGetPackedA32(res));
844 }
845 } else {
846 for (int i = count - 1; i >= 0; --i) {
847 unsigned a = aa[i];
848 if (0 != a) {
849 SkAlpha dstA = dst[i];
850 SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
851 unsigned A = SkGetPackedA32(res);
852 if (0xFF != a) {
853 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
854 }
855 dst[i] = SkToU8(A);
856 }
857 }
858 }
859 }
860}
861
862SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
863 : SkXfermode(buffer) {
reed@google.com34342f62012-06-25 14:36:28 +0000864 fProc = NULL;
865 if (!buffer.isCrossProcess()) {
866 fProc = (SkXfermodeProc)buffer.readFunctionPtr();
867 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000868}
869
djsollen@google.com54924242012-03-29 15:18:04 +0000870void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
871 this->INHERITED::flatten(buffer);
reed@google.com34342f62012-06-25 14:36:28 +0000872 if (!buffer.isCrossProcess()) {
yangsu@google.comf468e472011-08-10 18:34:50 +0000873 buffer.writeFunctionPtr((void*)fProc);
874 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000875}
876
robertphillips@google.comb83b6b42013-01-22 14:32:09 +0000877#ifdef SK_DEVELOPER
878void SkProcXfermode::toString(SkString* str) const {
879 str->appendf("SkProcXfermode: %p", fProc);
880}
881#endif
882
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000883//////////////////////////////////////////////////////////////////////////////
884
885#if SK_SUPPORT_GPU
886
887#include "GrEffect.h"
bsalomon@google.com77af6802013-10-02 13:04:56 +0000888#include "GrCoordTransform.h"
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000889#include "GrEffectUnitTest.h"
890#include "GrTBackendEffectFactory.h"
891#include "gl/GrGLEffect.h"
892
893/**
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000894 * GrEffect that implements the all the separable xfer modes that cannot be expressed as Coeffs.
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000895 */
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000896class XferEffect : public GrEffect {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000897public:
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000898 static bool IsSupportedMode(SkXfermode::Mode mode) {
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000899 return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000900 }
901
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000902 static GrEffectRef* Create(SkXfermode::Mode mode, GrTexture* background) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000903 if (!IsSupportedMode(mode)) {
904 return NULL;
905 } else {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000906 AutoEffectUnref effect(SkNEW_ARGS(XferEffect, (mode, background)));
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000907 return CreateEffectRef(effect);
908 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000909 }
910
911 virtual void getConstantColorComponents(GrColor* color,
912 uint32_t* validFlags) const SK_OVERRIDE {
913 *validFlags = 0;
914 }
915
916 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000917 return GrTBackendEffectFactory<XferEffect>::getInstance();
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000918 }
919
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000920 static const char* Name() { return "XferEffect"; }
921
922 SkXfermode::Mode mode() const { return fMode; }
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000923 const GrTextureAccess& backgroundAccess() const { return fBackgroundAccess; }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000924
925 class GLEffect : public GrGLEffect {
926 public:
927 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
bsalomon@google.com77af6802013-10-02 13:04:56 +0000928 : GrGLEffect(factory) {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000929 }
930 virtual void emitCode(GrGLShaderBuilder* builder,
931 const GrDrawEffect& drawEffect,
932 EffectKey key,
933 const char* outputColor,
934 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000935 const TransformedCoordsArray& coords,
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000936 const TextureSamplerArray& samplers) SK_OVERRIDE {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000937 SkXfermode::Mode mode = drawEffect.castEffect<XferEffect>().mode();
938 const GrTexture* backgroundTex = drawEffect.castEffect<XferEffect>().backgroundAccess().getTexture();
939 const char* dstColor;
940 if (backgroundTex) {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000941 dstColor = "bgColor";
942 builder->fsCodeAppendf("\t\tvec4 %s = ", dstColor);
bsalomon@google.com77af6802013-10-02 13:04:56 +0000943 builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000944 builder->fsCodeAppendf(";\n");
945 } else {
946 dstColor = builder->dstColor();
947 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000948 SkASSERT(NULL != dstColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000949
950 // We don't try to optimize for this case at all
bsalomon@google.comb79d8652013-03-29 20:30:50 +0000951 if (NULL == inputColor) {
commit-bot@chromium.org824c3462013-10-10 06:30:18 +0000952 builder->fsCodeAppendf("\t\tconst vec4 ones = vec4(1);\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000953 inputColor = "ones";
954 }
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000955 builder->fsCodeAppendf("\t\t// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
956
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000957 // These all perform src-over on the alpha channel.
958 builder->fsCodeAppendf("\t\t%s.a = %s.a + (1.0 - %s.a) * %s.a;\n",
959 outputColor, inputColor, inputColor, dstColor);
960
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000961 switch (mode) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000962 case SkXfermode::kOverlay_Mode:
963 // Overlay is Hard-Light with the src and dst reversed
964 HardLight(builder, outputColor, dstColor, inputColor);
965 break;
966 case SkXfermode::kDarken_Mode:
967 builder->fsCodeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
968 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
969 outputColor,
970 inputColor, dstColor, inputColor,
971 dstColor, inputColor, dstColor);
972 break;
973 case SkXfermode::kLighten_Mode:
974 builder->fsCodeAppendf("\t\t%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
975 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
976 outputColor,
977 inputColor, dstColor, inputColor,
978 dstColor, inputColor, dstColor);
979 break;
980 case SkXfermode::kColorDodge_Mode:
981 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'r');
982 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'g');
983 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'b');
984 break;
985 case SkXfermode::kColorBurn_Mode:
986 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'r');
987 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'g');
988 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'b');
989 break;
990 case SkXfermode::kHardLight_Mode:
991 HardLight(builder, outputColor, inputColor, dstColor);
992 break;
993 case SkXfermode::kSoftLight_Mode:
994 builder->fsCodeAppendf("\t\tif (0.0 == %s.a) {\n", dstColor);
995 builder->fsCodeAppendf("\t\t\t%s.rgba = %s;\n", outputColor, inputColor);
996 builder->fsCodeAppendf("\t\t} else {\n");
997 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'r');
998 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'g');
999 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'b');
1000 builder->fsCodeAppendf("\t\t}\n");
1001 break;
1002 case SkXfermode::kDifference_Mode:
1003 builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb -"
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +00001004 "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001005 outputColor, inputColor, dstColor, inputColor, dstColor,
1006 dstColor, inputColor);
1007 break;
1008 case SkXfermode::kExclusion_Mode:
1009 builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb - "
1010 "2.0 * %s.rgb * %s.rgb;\n",
1011 outputColor, dstColor, inputColor, dstColor, inputColor);
1012 break;
1013 case SkXfermode::kMultiply_Mode:
1014 builder->fsCodeAppendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + "
1015 "(1.0 - %s.a) * %s.rgb + "
1016 "%s.rgb * %s.rgb;\n",
1017 outputColor, inputColor, dstColor, dstColor, inputColor,
1018 inputColor, dstColor);
1019 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001020 case SkXfermode::kHue_Mode: {
1021 // SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
1022 SkString setSat, setLum;
1023 AddSatFunction(builder, &setSat);
1024 AddLumFunction(builder, &setLum);
1025 builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
1026 dstColor, inputColor);
1027 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
1028 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
1029 dstColor);
1030 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1031 outputColor, inputColor, dstColor, dstColor, inputColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001032 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001033 }
1034 case SkXfermode::kSaturation_Mode: {
1035 // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
1036 SkString setSat, setLum;
1037 AddSatFunction(builder, &setSat);
1038 AddLumFunction(builder, &setLum);
1039 builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
1040 dstColor, inputColor);
1041 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
1042 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
1043 dstColor);
1044 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1045 outputColor, inputColor, dstColor, dstColor, inputColor);
1046 break;
1047 }
1048 case SkXfermode::kColor_Mode: {
1049 // SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
1050 SkString setLum;
1051 AddLumFunction(builder, &setLum);
1052 builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
1053 inputColor, dstColor);
1054 builder->fsCodeAppendf("\t\t%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);\n",
1055 outputColor, setLum.c_str(), dstColor, inputColor);
1056 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1057 outputColor, inputColor, dstColor, dstColor, inputColor);
1058 break;
1059 }
1060 case SkXfermode::kLuminosity_Mode: {
1061 // SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
1062 SkString setLum;
1063 AddLumFunction(builder, &setLum);
1064 builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
1065 inputColor, dstColor);
1066 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);\n",
1067 outputColor, setLum.c_str(), dstColor, inputColor);
1068 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1069 outputColor, inputColor, dstColor, dstColor, inputColor);
1070 break;
1071 }
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001072 default:
1073 GrCrash("Unknown XferEffect mode.");
1074 break;
bsalomon@google.comb79d8652013-03-29 20:30:50 +00001075 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001076 }
1077
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001078 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com77af6802013-10-02 13:04:56 +00001079 return drawEffect.castEffect<XferEffect>().mode();
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001080 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001081
1082 private:
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001083 static void HardLight(GrGLShaderBuilder* builder,
1084 const char* final,
1085 const char* src,
1086 const char* dst) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +00001087 static const char kComponents[] = {'r', 'g', 'b'};
1088 for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
1089 char component = kComponents[i];
1090 builder->fsCodeAppendf("\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
1091 builder->fsCodeAppendf("\t\t\t%s.%c = 2.0 * %s.%c * %s.%c;\n", final, component, src, component, dst, component);
1092 builder->fsCodeAppend("\t\t} else {\n");
1093 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);\n",
1094 final, component, src, dst, dst, dst, component, src, src, component);
1095 builder->fsCodeAppend("\t\t}\n");
1096 }
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001097 builder->fsCodeAppendf("\t\t%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);\n",
1098 final, src, dst, dst, src);
1099 }
1100
1101 // Does one component of color-dodge
1102 static void ColorDodgeComponent(GrGLShaderBuilder* builder,
1103 const char* final,
1104 const char* src,
1105 const char* dst,
1106 const char component) {
1107 builder->fsCodeAppendf("\t\tif (0.0 == %s.%c) {\n", dst, component);
bsalomon@google.com58eb1af2013-04-19 16:20:20 +00001108 builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001109 final, component, src, component, dst);
1110 builder->fsCodeAppend("\t\t} else {\n");
1111 builder->fsCodeAppendf("\t\t\tfloat d = %s.a - %s.%c;\n", src, src, component);
bsalomon@google.com07fa3ab2013-04-19 17:21:49 +00001112 builder->fsCodeAppend("\t\t\tif (0.0 == d) {\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001113 builder->fsCodeAppendf("\t\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1114 final, component, src, dst, src, component, dst, dst, component,
1115 src);
1116 builder->fsCodeAppend("\t\t\t} else {\n");
1117 builder->fsCodeAppendf("\t\t\t\td = min(%s.a, %s.%c * %s.a / d);\n",
1118 dst, dst, component, src);
1119 builder->fsCodeAppendf("\t\t\t\t%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1120 final, component, src, src, component, dst, dst, component, src);
1121 builder->fsCodeAppend("\t\t\t}\n");
1122 builder->fsCodeAppend("\t\t}\n");
1123 }
1124
1125 // Does one component of color-burn
1126 static void ColorBurnComponent(GrGLShaderBuilder* builder,
1127 const char* final,
1128 const char* src,
1129 const char* dst,
1130 const char component) {
1131 builder->fsCodeAppendf("\t\tif (%s.a == %s.%c) {\n", dst, dst, component);
1132 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1133 final, component, src, dst, src, component, dst, dst, component,
1134 src);
1135 builder->fsCodeAppendf("\t\t} else if (0.0 == %s.%c) {\n", src, component);
1136 builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
1137 final, component, dst, component, src);
1138 builder->fsCodeAppend("\t\t} else {\n");
1139 builder->fsCodeAppendf("\t\t\tfloat d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);\n",
1140 dst, dst, dst, component, src, src, component);
1141 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1142 final, component, src, src, component, dst, dst, component, src);
1143 builder->fsCodeAppend("\t\t}\n");
1144 }
1145
1146 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
1147 static void SoftLightComponentPosDstAlpha(GrGLShaderBuilder* builder,
1148 const char* final,
1149 const char* src,
1150 const char* dst,
1151 const char component) {
1152 // if (2S < Sa)
1153 builder->fsCodeAppendf("\t\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
1154 // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
bsalomon@google.com68567792013-04-19 18:10:50 +00001155 builder->fsCodeAppendf("\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 +00001156 final, component, dst, component, dst, component, src, src,
1157 component, dst, dst, src, component, dst, component, src, src,
1158 component);
1159 // else if (4D < Da)
1160 builder->fsCodeAppendf("\t\t\t} else if (4.0 * %s.%c <= %s.a) {\n",
1161 dst, component, dst);
1162 builder->fsCodeAppendf("\t\t\t\tfloat DSqd = %s.%c * %s.%c;\n",
1163 dst, component, dst, component);
1164 builder->fsCodeAppendf("\t\t\t\tfloat DCub = DSqd * %s.%c;\n", dst, component);
1165 builder->fsCodeAppendf("\t\t\t\tfloat DaSqd = %s.a * %s.a;\n", dst, dst);
1166 builder->fsCodeAppendf("\t\t\t\tfloat DaCub = DaSqd * %s.a;\n", dst);
1167 // (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
bsalomon@google.com68567792013-04-19 18:10:50 +00001168 builder->fsCodeAppendf("\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 +00001169 final, component, src, component, src, component, dst, component,
1170 src, src, component, dst, src, src, component, src, src,
1171 component);
1172 builder->fsCodeAppendf("\t\t\t} else {\n");
1173 // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
bsalomon@google.com68567792013-04-19 18:10:50 +00001174 builder->fsCodeAppendf("\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 +00001175 final, component, dst, dst, component, src, src, component, dst,
1176 src, component, dst, component, src, src, component, src,
1177 component);
1178 builder->fsCodeAppendf("\t\t\t}\n");
1179 }
1180
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001181 // Adds a function that takes two colors and an alpha as input. It produces a color with the
1182 // hue and saturation of the first color, the luminosity of the second color, and the input
1183 // alpha. It has this signature:
1184 // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
1185 static void AddLumFunction(GrGLShaderBuilder* builder, SkString* setLumFunction) {
1186 // Emit a helper that gets the luminance of a color.
1187 SkString getFunction;
1188 GrGLShaderVar getLumArgs[] = {
1189 GrGLShaderVar("color", kVec3f_GrSLType),
1190 };
1191 SkString getLumBody("\treturn dot(vec3(0.3, 0.59, 0.11), color);\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001192 builder->fsEmitFunction(kFloat_GrSLType,
1193 "luminance",
1194 SK_ARRAY_COUNT(getLumArgs), getLumArgs,
1195 getLumBody.c_str(),
1196 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001197
1198 // Emit the set luminance function.
1199 GrGLShaderVar setLumArgs[] = {
1200 GrGLShaderVar("hueSat", kVec3f_GrSLType),
1201 GrGLShaderVar("alpha", kFloat_GrSLType),
1202 GrGLShaderVar("lumColor", kVec3f_GrSLType),
1203 };
1204 SkString setLumBody;
1205 setLumBody.printf("\tfloat diff = %s(lumColor - hueSat);\n", getFunction.c_str());
1206 setLumBody.append("\tvec3 outColor = hueSat + diff;\n");
1207 setLumBody.appendf("\tfloat outLum = %s(outColor);\n", getFunction.c_str());
1208 setLumBody.append("\tfloat minComp = min(min(outColor.r, outColor.g), outColor.b);\n"
1209 "\tfloat maxComp = max(max(outColor.r, outColor.g), outColor.b);\n"
1210 "\tif (minComp < 0.0) {\n"
1211 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / (outLum - minComp);\n"
1212 "\t}\n"
1213 "\tif (maxComp > alpha) {\n"
1214 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) / (maxComp - outLum);\n"
1215 "\t}\n"
1216 "\treturn outColor;\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001217 builder->fsEmitFunction(kVec3f_GrSLType,
1218 "set_luminance",
1219 SK_ARRAY_COUNT(setLumArgs), setLumArgs,
1220 setLumBody.c_str(),
1221 setLumFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001222 }
1223
1224 // Adds a function that creates a color with the hue and luminosity of one input color and
1225 // the saturation of another color. It will have this signature:
1226 // float set_saturation(vec3 hueLumColor, vec3 satColor)
1227 static void AddSatFunction(GrGLShaderBuilder* builder, SkString* setSatFunction) {
1228 // Emit a helper that gets the saturation of a color
1229 SkString getFunction;
1230 GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
1231 SkString getSatBody;
1232 getSatBody.printf("\treturn max(max(color.r, color.g), color.b) - "
1233 "min(min(color.r, color.g), color.b);\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001234 builder->fsEmitFunction(kFloat_GrSLType,
1235 "saturation",
1236 SK_ARRAY_COUNT(getSatArgs), getSatArgs,
1237 getSatBody.c_str(),
1238 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001239
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001240 // Emit a helper that sets the saturation given sorted input channels. This used
1241 // to use inout params for min, mid, and max components but that seems to cause
1242 // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
1243 // adjusted min, mid, and max inputs, respectively.
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001244 SkString helperFunction;
1245 GrGLShaderVar helperArgs[] = {
1246 GrGLShaderVar("minComp", kFloat_GrSLType),
1247 GrGLShaderVar("midComp", kFloat_GrSLType),
1248 GrGLShaderVar("maxComp", kFloat_GrSLType),
1249 GrGLShaderVar("sat", kFloat_GrSLType),
1250 };
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001251 static const char kHelperBody[] = "\tif (minComp < maxComp) {\n"
1252 "\t\tvec3 result;\n"
1253 "\t\tresult.r = 0.0;\n"
1254 "\t\tresult.g = sat * (midComp - minComp) / (maxComp - minComp);\n"
1255 "\t\tresult.b = sat;\n"
1256 "\t\treturn result;\n"
1257 "\t} else {\n"
1258 "\t\treturn vec3(0, 0, 0);\n"
1259 "\t}\n";
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001260 builder->fsEmitFunction(kVec3f_GrSLType,
1261 "set_saturation_helper",
1262 SK_ARRAY_COUNT(helperArgs), helperArgs,
1263 kHelperBody,
1264 &helperFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001265
1266 GrGLShaderVar setSatArgs[] = {
1267 GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
1268 GrGLShaderVar("satColor", kVec3f_GrSLType),
1269 };
1270 const char* helpFunc = helperFunction.c_str();
1271 SkString setSatBody;
1272 setSatBody.appendf("\tfloat sat = %s(satColor);\n"
1273 "\tif (hueLumColor.r <= hueLumColor.g) {\n"
1274 "\t\tif (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001275 "\t\t\thueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001276 "\t\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001277 "\t\t\thueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001278 "\t\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001279 "\t\t\thueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001280 "\t\t}\n"
1281 "\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001282 "\t\thueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001283 "\t} else if (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001284 "\t\thueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001285 "\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001286 "\t\thueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001287 "\t}\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001288 "\treturn hueLumColor;\n",
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001289 getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
1290 helpFunc, helpFunc);
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001291 builder->fsEmitFunction(kVec3f_GrSLType,
1292 "set_saturation",
1293 SK_ARRAY_COUNT(setSatArgs), setSatArgs,
1294 setSatBody.c_str(),
1295 setSatFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001296
1297 }
1298
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001299 typedef GrGLEffect INHERITED;
1300 };
1301
1302 GR_DECLARE_EFFECT_TEST;
1303
1304private:
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001305 XferEffect(SkXfermode::Mode mode, GrTexture* background)
1306 : fMode(mode) {
1307 if (background) {
bsalomon@google.com77af6802013-10-02 13:04:56 +00001308 fBackgroundTransform.reset(kLocal_GrCoordSet, background);
1309 this->addCoordTransform(&fBackgroundTransform);
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001310 fBackgroundAccess.reset(background);
1311 this->addTextureAccess(&fBackgroundAccess);
1312 } else {
1313 this->setWillReadDstColor();
1314 }
1315 }
1316 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
1317 const XferEffect& s = CastEffect<XferEffect>(other);
1318 return fMode == s.fMode &&
1319 fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture();
1320 }
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +00001321
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001322 SkXfermode::Mode fMode;
bsalomon@google.com77af6802013-10-02 13:04:56 +00001323 GrCoordTransform fBackgroundTransform;
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001324 GrTextureAccess fBackgroundAccess;
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001325
1326 typedef GrEffect INHERITED;
1327};
1328
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001329GR_DEFINE_EFFECT_TEST(XferEffect);
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +00001330GrEffectRef* XferEffect::TestCreate(SkRandom* rand,
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001331 GrContext*,
1332 const GrDrawTargetCaps&,
1333 GrTexture*[]) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +00001334 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001335
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001336 static AutoEffectUnref gEffect(SkNEW_ARGS(XferEffect, (static_cast<SkXfermode::Mode>(mode), NULL)));
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001337 return CreateEffectRef(gEffect);
1338}
1339
1340#endif
1341
vandebo@chromium.org48543272011-02-08 19:28:07 +00001342///////////////////////////////////////////////////////////////////////////////
1343///////////////////////////////////////////////////////////////////////////////
1344
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001345bool SkProcCoeffXfermode::asMode(Mode* mode) const {
1346 if (mode) {
1347 *mode = fMode;
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001348 }
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001349 return true;
1350}
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001351
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001352bool SkProcCoeffXfermode::asCoeff(Coeff* sc, Coeff* dc) const {
1353 if (CANNOT_USE_COEFF == fSrcCoeff) {
djsollen@google.com6f980c62013-10-08 16:59:53 +00001354 return false;
1355 }
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001356
1357 if (sc) {
1358 *sc = fSrcCoeff;
1359 }
1360 if (dc) {
1361 *dc = fDstCoeff;
1362 }
1363 return true;
1364}
1365
1366#if SK_SUPPORT_GPU
1367bool SkProcCoeffXfermode::asNewEffectOrCoeff(GrContext*,
1368 GrEffectRef** effect,
1369 Coeff* src,
1370 Coeff* dst,
1371 GrTexture* background) const {
1372 if (this->asCoeff(src, dst)) {
1373 return true;
1374 }
1375 if (XferEffect::IsSupportedMode(fMode)) {
1376 if (NULL != effect) {
1377 *effect = XferEffect::Create(fMode, background);
1378 SkASSERT(NULL != *effect);
1379 }
1380 return true;
1381 }
1382 return false;
1383}
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001384#endif
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001385
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001386void SkProcCoeffXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
1387 this->INHERITED::flatten(buffer);
1388 buffer.write32(fMode);
1389}
vandebo@chromium.org48543272011-02-08 19:28:07 +00001390
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001391const char* SkXfermode::ModeName(Mode mode) {
1392 SkASSERT((unsigned) mode <= (unsigned)kLastMode);
1393 const char* gModeStrings[] = {
1394 "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn",
1395 "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Plus",
1396 "Modulate", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge",
1397 "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion",
1398 "Multiply", "Hue", "Saturation", "Color", "Luminosity"
1399 };
1400 return gModeStrings[mode];
1401 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gModeStrings) == kLastMode + 1, mode_count);
1402}
1403
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001404#ifdef SK_DEVELOPER
1405void SkProcCoeffXfermode::toString(SkString* str) const {
1406 str->append("SkProcCoeffXfermode: ");
1407
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001408 str->append("mode: ");
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001409 str->append(ModeName(fMode));
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001410
1411 static const char* gCoeffStrings[kCoeffCount] = {
skia.committer@gmail.com98ded842013-01-23 07:06:17 +00001412 "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001413 };
1414
1415 str->append(" src: ");
1416 if (CANNOT_USE_COEFF == fSrcCoeff) {
1417 str->append("can't use");
1418 } else {
1419 str->append(gCoeffStrings[fSrcCoeff]);
1420 }
1421
1422 str->append(" dst: ");
1423 if (CANNOT_USE_COEFF == fDstCoeff) {
1424 str->append("can't use");
1425 } else {
1426 str->append(gCoeffStrings[fDstCoeff]);
1427 }
1428}
1429#endif
1430
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431///////////////////////////////////////////////////////////////////////////////
1432
1433class SkClearXfermode : public SkProcCoeffXfermode {
1434public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001435 SkClearXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kClear_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436
reed@google.com30da7452012-12-17 19:55:24 +00001437 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1438 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001440 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001441 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkClearXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001442
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443private:
1444 SkClearXfermode(SkFlattenableReadBuffer& buffer)
1445 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001446
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001447 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448};
1449
reed@google.com86ab6c62011-11-28 15:26:14 +00001450void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1451 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001452 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001453 SkASSERT(dst && count >= 0);
1454
1455 if (NULL == aa) {
1456 memset(dst, 0, count << 2);
1457 } else {
1458 for (int i = count - 1; i >= 0; --i) {
1459 unsigned a = aa[i];
1460 if (0xFF == a) {
1461 dst[i] = 0;
1462 } else if (a != 0) {
1463 dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
1464 }
1465 }
1466 }
1467}
1468void SkClearXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1469 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001470 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001471 SkASSERT(dst && count >= 0);
1472
1473 if (NULL == aa) {
1474 memset(dst, 0, count);
1475 } else {
1476 for (int i = count - 1; i >= 0; --i) {
1477 unsigned a = aa[i];
1478 if (0xFF == a) {
1479 dst[i] = 0;
1480 } else if (0 != a) {
1481 dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
1482 }
1483 }
1484 }
1485}
1486
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001487#ifdef SK_DEVELOPER
1488void SkClearXfermode::toString(SkString* str) const {
1489 this->INHERITED::toString(str);
1490}
1491#endif
1492
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493///////////////////////////////////////////////////////////////////////////////
1494
1495class SkSrcXfermode : public SkProcCoeffXfermode {
1496public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001497 SkSrcXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kSrc_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498
reed@google.com30da7452012-12-17 19:55:24 +00001499 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1500 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001502 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001503 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSrcXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001504
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505private:
1506 SkSrcXfermode(SkFlattenableReadBuffer& buffer)
1507 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001508
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001509 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510};
1511
reed@google.com86ab6c62011-11-28 15:26:14 +00001512void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1513 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001514 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001515 SkASSERT(dst && src && count >= 0);
1516
1517 if (NULL == aa) {
1518 memcpy(dst, src, count << 2);
1519 } else {
1520 for (int i = count - 1; i >= 0; --i) {
1521 unsigned a = aa[i];
1522 if (a == 0xFF) {
1523 dst[i] = src[i];
1524 } else if (a != 0) {
1525 dst[i] = SkFourByteInterp(src[i], dst[i], a);
1526 }
1527 }
1528 }
1529}
1530
1531void SkSrcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1532 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001533 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001534 SkASSERT(dst && src && count >= 0);
1535
1536 if (NULL == aa) {
1537 for (int i = count - 1; i >= 0; --i) {
1538 dst[i] = SkToU8(SkGetPackedA32(src[i]));
1539 }
1540 } else {
1541 for (int i = count - 1; i >= 0; --i) {
1542 unsigned a = aa[i];
1543 if (0 != a) {
1544 unsigned srcA = SkGetPackedA32(src[i]);
1545 if (a == 0xFF) {
1546 dst[i] = SkToU8(srcA);
1547 } else {
1548 dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a));
1549 }
1550 }
1551 }
1552 }
1553}
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001554#ifdef SK_DEVELOPER
1555void SkSrcXfermode::toString(SkString* str) const {
1556 this->INHERITED::toString(str);
1557}
1558#endif
reed@google.com86ab6c62011-11-28 15:26:14 +00001559
reed@google.com30da7452012-12-17 19:55:24 +00001560///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001561
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562class SkDstInXfermode : public SkProcCoeffXfermode {
1563public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001564 SkDstInXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstIn_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001565
reed@google.com30da7452012-12-17 19:55:24 +00001566 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001567
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001568 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001569 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstInXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001570
1571private:
1572 SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
1573
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574 typedef SkProcCoeffXfermode INHERITED;
1575};
1576
reed@google.com86ab6c62011-11-28 15:26:14 +00001577void SkDstInXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1578 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001579 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001580 SkASSERT(dst && src);
1581
1582 if (count <= 0) {
1583 return;
1584 }
1585 if (NULL != aa) {
1586 return this->INHERITED::xfer32(dst, src, count, aa);
1587 }
1588
1589 do {
1590 unsigned a = SkGetPackedA32(*src);
1591 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
1592 dst++;
1593 src++;
1594 } while (--count != 0);
1595}
1596
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001597#ifdef SK_DEVELOPER
1598void SkDstInXfermode::toString(SkString* str) const {
1599 this->INHERITED::toString(str);
1600}
1601#endif
1602
reed@google.com30da7452012-12-17 19:55:24 +00001603///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001604
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605class SkDstOutXfermode : public SkProcCoeffXfermode {
1606public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001607 SkDstOutXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstOut_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001608
reed@google.com30da7452012-12-17 19:55:24 +00001609 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001610
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001611 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001612 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstOutXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001613
1614private:
1615 SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
1616 : INHERITED(buffer) {}
1617
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618 typedef SkProcCoeffXfermode INHERITED;
1619};
1620
reed@google.com86ab6c62011-11-28 15:26:14 +00001621void SkDstOutXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1622 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001623 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001624 SkASSERT(dst && src);
1625
1626 if (count <= 0) {
1627 return;
1628 }
1629 if (NULL != aa) {
1630 return this->INHERITED::xfer32(dst, src, count, aa);
1631 }
1632
1633 do {
1634 unsigned a = SkGetPackedA32(*src);
1635 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
1636 dst++;
1637 src++;
1638 } while (--count != 0);
1639}
1640
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001641#ifdef SK_DEVELOPER
1642void SkDstOutXfermode::toString(SkString* str) const {
1643 this->INHERITED::toString(str);
1644}
1645#endif
1646
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647///////////////////////////////////////////////////////////////////////////////
1648
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001649SK_DECLARE_STATIC_MUTEX(gCachedXfermodesMutex);
1650static SkXfermode* gCachedXfermodes[SkXfermode::kLastMode + 1];
1651
1652void SkXfermode::Term() {
1653 SkAutoMutexAcquire ac(gCachedXfermodesMutex);
1654
1655 for (size_t i = 0; i < SK_ARRAY_COUNT(gCachedXfermodes); ++i) {
1656 SkSafeUnref(gCachedXfermodes[i]);
1657 gCachedXfermodes[i] = NULL;
1658 }
1659}
1660
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001661extern SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec,
1662 SkXfermode::Mode mode);
1663
reed@android.coma0f5d152009-06-22 17:38:10 +00001664SkXfermode* SkXfermode::Create(Mode mode) {
1665 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001666 SkASSERT(SK_ARRAY_COUNT(gCachedXfermodes) == kModeCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001667
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001668 if ((unsigned)mode >= kModeCount) {
1669 // report error
1670 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671 }
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001672
1673 // Skia's "defaut" mode is srcover. NULL in SkPaint is interpreted as srcover
1674 // so we can just return NULL from the factory.
1675 if (kSrcOver_Mode == mode) {
1676 return NULL;
1677 }
1678
1679 // guard our access to gCachedXfermodes, since we may write into it
1680 SkAutoMutexAcquire ac(gCachedXfermodesMutex);
1681
1682 SkXfermode* xfer = gCachedXfermodes[mode];
1683 if (NULL == xfer) {
1684 const ProcCoeff& rec = gProcCoeffs[mode];
commit-bot@chromium.orgdf187c72013-10-09 14:39:46 +00001685
1686 // check if we have a platform optim for that
1687 SkProcCoeffXfermode* xfm = SkPlatformXfermodeFactory(rec, mode);
1688 if (xfm != NULL) {
1689 xfer = xfm;
1690 } else {
1691 // All modes can in theory be represented by the ProcCoeff rec, since
1692 // it contains function ptrs. However, a few modes are both simple and
1693 // commonly used, so we call those out for their own subclasses here.
1694 switch (mode) {
1695 case kClear_Mode:
1696 xfer = SkNEW_ARGS(SkClearXfermode, (rec));
1697 break;
1698 case kSrc_Mode:
1699 xfer = SkNEW_ARGS(SkSrcXfermode, (rec));
1700 break;
1701 case kSrcOver_Mode:
1702 SkASSERT(false); // should not land here
1703 break;
1704 case kDstIn_Mode:
1705 xfer = SkNEW_ARGS(SkDstInXfermode, (rec));
1706 break;
1707 case kDstOut_Mode:
1708 xfer = SkNEW_ARGS(SkDstOutXfermode, (rec));
1709 break;
1710 default:
1711 // no special-case, just rely in the rec and its function-ptrs
1712 xfer = SkNEW_ARGS(SkProcCoeffXfermode, (rec, mode));
1713 break;
1714 }
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001715 }
1716 gCachedXfermodes[mode] = xfer;
1717 }
1718 return SkSafeRef(xfer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719}
1720
reed@google.com43c50c82011-04-14 15:50:52 +00001721SkXfermodeProc SkXfermode::GetProc(Mode mode) {
1722 SkXfermodeProc proc = NULL;
1723 if ((unsigned)mode < kModeCount) {
1724 proc = gProcCoeffs[mode].fProc;
1725 }
1726 return proc;
1727}
1728
1729bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
1730 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001731
reed@google.com43c50c82011-04-14 15:50:52 +00001732 if ((unsigned)mode >= (unsigned)kModeCount) {
1733 // illegal mode parameter
1734 return false;
1735 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001736
reed@google.com43c50c82011-04-14 15:50:52 +00001737 const ProcCoeff& rec = gProcCoeffs[mode];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001738
reed@google.com43c50c82011-04-14 15:50:52 +00001739 if (CANNOT_USE_COEFF == rec.fSC) {
1740 return false;
1741 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001742
reed@google.com43c50c82011-04-14 15:50:52 +00001743 SkASSERT(CANNOT_USE_COEFF != rec.fDC);
1744 if (src) {
1745 *src = rec.fSC;
1746 }
1747 if (dst) {
1748 *dst = rec.fDC;
1749 }
1750 return true;
1751}
1752
reed@google.com30da7452012-12-17 19:55:24 +00001753bool SkXfermode::AsMode(const SkXfermode* xfer, Mode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 if (NULL == xfer) {
1755 if (mode) {
1756 *mode = kSrcOver_Mode;
1757 }
1758 return true;
1759 }
reed@google.comc0d4aa22011-04-13 21:12:04 +00001760 return xfer->asMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761}
1762
reed@google.com30da7452012-12-17 19:55:24 +00001763bool SkXfermode::AsCoeff(const SkXfermode* xfer, Coeff* src, Coeff* dst) {
reed@google.com43c50c82011-04-14 15:50:52 +00001764 if (NULL == xfer) {
1765 return ModeAsCoeff(kSrcOver_Mode, src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766 }
reed@google.com43c50c82011-04-14 15:50:52 +00001767 return xfer->asCoeff(src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768}
1769
reed@google.com30da7452012-12-17 19:55:24 +00001770bool SkXfermode::IsMode(const SkXfermode* xfer, Mode mode) {
mike@reedtribe.orge303fcf2011-11-17 02:16:43 +00001771 // if xfer==null then the mode is srcover
1772 Mode m = kSrcOver_Mode;
1773 if (xfer && !xfer->asMode(&m)) {
1774 return false;
1775 }
1776 return mode == m;
1777}
1778
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779///////////////////////////////////////////////////////////////////////////////
1780//////////// 16bit xfermode procs
1781
1782#ifdef SK_DEBUG
1783static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
1784static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
1785#endif
1786
1787static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
1788 SkASSERT(require_255(src));
1789 return SkPixel32ToPixel16(src);
1790}
1791
1792static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
1793 return dst;
1794}
1795
1796static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
1797 SkASSERT(require_0(src));
1798 return dst;
1799}
1800
1801static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
1802 SkASSERT(require_255(src));
1803 return SkPixel32ToPixel16(src);
1804}
1805
1806static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
1807 SkASSERT(require_0(src));
1808 return dst;
1809}
1810
1811static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
1812 SkASSERT(require_255(src));
1813 return dst;
1814}
1815
1816static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
1817 SkASSERT(require_255(src));
1818 return SkPixel32ToPixel16(src);
1819}
1820
1821static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
1822 SkASSERT(require_255(src));
1823 return dst;
1824}
1825
1826static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
1827 SkASSERT(require_0(src));
1828 return dst;
1829}
1830
1831static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
1832 unsigned isa = 255 - SkGetPackedA32(src);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 return SkPackRGB16(
1835 SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
1836 SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
1837 SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
1838}
1839
1840static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
1841 SkASSERT(require_0(src));
1842 return dst;
1843}
1844
1845static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1846 SkASSERT(require_255(src));
1847 return SkPixel32ToPixel16(src);
1848}
1849
1850static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1851 SkASSERT(require_255(src));
1852 return dst;
1853}
1854
1855/*********
1856 darken and lighten boil down to this.
1857
1858 darken = (1 - Sa) * Dc + min(Sc, Dc)
1859 lighten = (1 - Sa) * Dc + max(Sc, Dc)
1860
1861 if (Sa == 0) these become
1862 darken = Dc + min(0, Dc) = 0
1863 lighten = Dc + max(0, Dc) = Dc
1864
1865 if (Sa == 1) these become
1866 darken = min(Sc, Dc)
1867 lighten = max(Sc, Dc)
1868*/
1869
1870static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
1871 SkASSERT(require_0(src));
1872 return 0;
1873}
1874
1875static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
1876 SkASSERT(require_255(src));
1877 unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1878 unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1879 unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1880 return SkPackRGB16(r, g, b);
1881}
1882
1883static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
1884 SkASSERT(require_0(src));
1885 return dst;
1886}
1887
1888static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
1889 SkASSERT(require_255(src));
1890 unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1891 unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1892 unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1893 return SkPackRGB16(r, g, b);
1894}
1895
1896struct Proc16Rec {
1897 SkXfermodeProc16 fProc16_0;
1898 SkXfermodeProc16 fProc16_255;
1899 SkXfermodeProc16 fProc16_General;
1900};
1901
reed@android.coma0f5d152009-06-22 17:38:10 +00001902static const Proc16Rec gModeProcs16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001903 { NULL, NULL, NULL }, // CLEAR
1904 { NULL, src_modeproc16_255, NULL },
1905 { dst_modeproc16, dst_modeproc16, dst_modeproc16 },
1906 { srcover_modeproc16_0, srcover_modeproc16_255, NULL },
1907 { dstover_modeproc16_0, dstover_modeproc16_255, NULL },
1908 { NULL, srcin_modeproc16_255, NULL },
1909 { NULL, dstin_modeproc16_255, NULL },
1910 { NULL, NULL, NULL },// SRC_OUT
1911 { dstout_modeproc16_0, NULL, NULL },
1912 { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16 },
1913 { NULL, dstatop_modeproc16_255, NULL },
1914 { NULL, NULL, NULL }, // XOR
reed@android.coma0f5d152009-06-22 17:38:10 +00001915
1916 { NULL, NULL, NULL }, // plus
reed@google.com8d3cd7a2013-01-30 21:36:11 +00001917 { NULL, NULL, NULL }, // modulate
reed@android.coma0f5d152009-06-22 17:38:10 +00001918 { NULL, NULL, NULL }, // screen
1919 { NULL, NULL, NULL }, // overlay
1920 { darken_modeproc16_0, darken_modeproc16_255, NULL }, // darken
1921 { lighten_modeproc16_0, lighten_modeproc16_255, NULL }, // lighten
1922 { NULL, NULL, NULL }, // colordodge
1923 { NULL, NULL, NULL }, // colorburn
1924 { NULL, NULL, NULL }, // hardlight
1925 { NULL, NULL, NULL }, // softlight
1926 { NULL, NULL, NULL }, // difference
1927 { NULL, NULL, NULL }, // exclusion
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +00001928 { NULL, NULL, NULL }, // multiply
1929 { NULL, NULL, NULL }, // hue
1930 { NULL, NULL, NULL }, // saturation
1931 { NULL, NULL, NULL }, // color
1932 { NULL, NULL, NULL }, // luminosity
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933};
1934
reed@android.coma0f5d152009-06-22 17:38:10 +00001935SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936 SkXfermodeProc16 proc16 = NULL;
reed@android.coma0f5d152009-06-22 17:38:10 +00001937 if ((unsigned)mode < kModeCount) {
1938 const Proc16Rec& rec = gModeProcs16[mode];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001939 unsigned a = SkColorGetA(srcColor);
1940
1941 if (0 == a) {
1942 proc16 = rec.fProc16_0;
1943 } else if (255 == a) {
1944 proc16 = rec.fProc16_255;
1945 } else {
1946 proc16 = rec.fProc16_General;
1947 }
1948 }
1949 return proc16;
1950}
1951
caryclark@google.comd26147a2011-12-15 14:16:43 +00001952SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermode)
1953 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkProcCoeffXfermode)
1954 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkClearXfermode)
1955 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSrcXfermode)
1956 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstInXfermode)
1957 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstOutXfermode)
commit-bot@chromium.orgcd7992b2013-10-17 16:29:34 +00001958#if !SK_ARM_NEON_IS_NONE
1959 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNEONProcCoeffXfermode)
1960#endif
caryclark@google.comd26147a2011-12-15 14:16:43 +00001961SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END