blob: 6f2fee691320fffdc944063db0e279dae5ca34cd [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.org84cc1eb2013-10-08 16:47:22 +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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016
robertphillips@google.com0456e0b2012-06-27 14:03:26 +000017SK_DEFINE_INST_COUNT(SkXfermode)
18
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#define SkAlphaMulAlpha(a, b) SkMulDiv255Round(a, b)
20
reed@android.comfc25abd2009-01-15 14:38:33 +000021#if 0
reed@android.com8a1c16f2008-12-17 15:59:43 +000022// idea for higher precision blends in xfer procs (and slightly faster)
23// see DstATop as a probable caller
24static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
25 SkASSERT(a <= 255);
26 SkASSERT(b <= 255);
27 SkASSERT(c <= 255);
28 SkASSERT(d <= 255);
29 unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
30 unsigned result = (prod + (prod >> 8)) >> 8;
31 SkASSERT(result <= 255);
32 return result;
33}
reed@android.comfc25abd2009-01-15 14:38:33 +000034#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000035
deanm@chromium.orgda946992009-07-03 12:54:24 +000036static inline unsigned saturated_add(unsigned a, unsigned b) {
reed@android.com543ed932009-04-24 12:43:40 +000037 SkASSERT(a <= 255);
38 SkASSERT(b <= 255);
39 unsigned sum = a + b;
40 if (sum > 255) {
41 sum = 255;
42 }
43 return sum;
44}
45
deanm@chromium.orgda946992009-07-03 12:54:24 +000046static inline int clamp_signed_byte(int n) {
reed@android.coma0f5d152009-06-22 17:38:10 +000047 if (n < 0) {
48 n = 0;
49 } else if (n > 255) {
50 n = 255;
51 }
52 return n;
53}
54
deanm@chromium.orgda946992009-07-03 12:54:24 +000055static inline int clamp_div255round(int prod) {
reed@android.coma0f5d152009-06-22 17:38:10 +000056 if (prod <= 0) {
57 return 0;
58 } else if (prod >= 255*255) {
59 return 255;
60 } else {
61 return SkDiv255Round(prod);
62 }
63}
64
reed@android.com8a1c16f2008-12-17 15:59:43 +000065///////////////////////////////////////////////////////////////////////////////
66
reed@android.com8a1c16f2008-12-17 15:59:43 +000067// kClear_Mode, //!< [0, 0]
68static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
69 return 0;
70}
71
72// kSrc_Mode, //!< [Sa, Sc]
73static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) {
74 return src;
75}
76
77// kDst_Mode, //!< [Da, Dc]
78static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) {
79 return dst;
80}
81
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000082// kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Sc + (1 - Sa)*Dc]
reed@android.com8a1c16f2008-12-17 15:59:43 +000083static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +000084#if 0
85 // this is the old, more-correct way, but it doesn't guarantee that dst==255
86 // will always stay opaque
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
reed@android.com1116fb22009-03-03 20:31:12 +000088#else
89 // this is slightly faster, but more importantly guarantees that dst==255
90 // will always stay opaque
91 return src + SkAlphaMulQ(dst, 256 - SkGetPackedA32(src));
92#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000093}
94
reed@android.com1116fb22009-03-03 20:31:12 +000095// kDstOver_Mode, //!< [Sa + Da - Sa*Da, Dc + (1 - Da)*Sc]
reed@android.com8a1c16f2008-12-17 15:59:43 +000096static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +000097 // this is the reverse of srcover, just flipping src and dst
98 // see srcover's comment about the 256 for opaqueness guarantees
99 return dst + SkAlphaMulQ(src, 256 - SkGetPackedA32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100}
101
102// kSrcIn_Mode, //!< [Sa * Da, Sc * Da]
103static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) {
104 return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst)));
105}
106
107// kDstIn_Mode, //!< [Sa * Da, Sa * Dc]
108static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) {
109 return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src)));
110}
111
112// kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)]
113static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) {
114 return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst)));
115}
116
117// kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)]
118static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) {
119 return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
120}
121
122// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc]
123static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) {
124 unsigned sa = SkGetPackedA32(src);
125 unsigned da = SkGetPackedA32(dst);
126 unsigned isa = 255 - sa;
127
128 return SkPackARGB32(da,
129 SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
130 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
131 SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
132 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
133 SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
134 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
135}
136
137// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
138static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) {
139 unsigned sa = SkGetPackedA32(src);
140 unsigned da = SkGetPackedA32(dst);
141 unsigned ida = 255 - da;
142
143 return SkPackARGB32(sa,
144 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
145 SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
146 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
147 SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
148 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
149 SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
150}
151
152// kXor_Mode [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
153static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) {
154 unsigned sa = SkGetPackedA32(src);
155 unsigned da = SkGetPackedA32(dst);
156 unsigned isa = 255 - sa;
157 unsigned ida = 255 - da;
158
159 return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
160 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
161 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
162 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
163 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
164 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
165 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
166}
167
reed@android.coma0f5d152009-06-22 17:38:10 +0000168///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
reed@android.coma0f5d152009-06-22 17:38:10 +0000170// kPlus_Mode
171static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000172 unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
deanm@chromium.orgda946992009-07-03 12:54:24 +0000173 unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
174 unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
175 unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
reed@android.coma0f5d152009-06-22 17:38:10 +0000176 return SkPackARGB32(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177}
178
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000179// kModulate_Mode
180static SkPMColor modulate_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
182 int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
183 int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
184 int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
185 return SkPackARGB32(a, r, g, b);
186}
187
reed@android.coma0f5d152009-06-22 17:38:10 +0000188static inline int srcover_byte(int a, int b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 return a + b - SkAlphaMulAlpha(a, b);
190}
reed@google.com25cfa692013-02-04 20:06:00 +0000191
192// kMultiply_Mode
193// B(Cb, Cs) = Cb x Cs
194// multiply uses its own version of blendfunc_byte because sa and da are not needed
195static int blendfunc_multiply_byte(int sc, int dc, int sa, int da) {
196 return clamp_div255round(sc * (255 - da) + dc * (255 - sa) + sc * dc);
197}
198
199static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
200 int sa = SkGetPackedA32(src);
201 int da = SkGetPackedA32(dst);
202 int a = srcover_byte(sa, da);
203 int r = blendfunc_multiply_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
204 int g = blendfunc_multiply_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
205 int b = blendfunc_multiply_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
206 return SkPackARGB32(a, r, g, b);
207}
208
209// kScreen_Mode
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000211 int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
212 int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
213 int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
214 int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 return SkPackARGB32(a, r, g, b);
216}
217
reed@android.coma0f5d152009-06-22 17:38:10 +0000218// kOverlay_Mode
219static inline int overlay_byte(int sc, int dc, int sa, int da) {
220 int tmp = sc * (255 - da) + dc * (255 - sa);
221 int rc;
222 if (2 * dc <= da) {
223 rc = 2 * sc * dc;
224 } else {
225 rc = sa * da - 2 * (da - dc) * (sa - sc);
226 }
227 return clamp_div255round(rc + tmp);
228}
229static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
230 int sa = SkGetPackedA32(src);
231 int da = SkGetPackedA32(dst);
232 int a = srcover_byte(sa, da);
233 int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
234 int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
235 int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
236 return SkPackARGB32(a, r, g, b);
237}
238
239// kDarken_Mode
240static inline int darken_byte(int sc, int dc, int sa, int da) {
241 int sd = sc * da;
242 int ds = dc * sa;
243 if (sd < ds) {
244 // srcover
245 return sc + dc - SkDiv255Round(ds);
246 } else {
247 // dstover
248 return dc + sc - SkDiv255Round(sd);
249 }
250}
251static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
252 int sa = SkGetPackedA32(src);
253 int da = SkGetPackedA32(dst);
254 int a = srcover_byte(sa, da);
255 int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
256 int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
257 int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
258 return SkPackARGB32(a, r, g, b);
259}
260
261// kLighten_Mode
262static inline int lighten_byte(int sc, int dc, int sa, int da) {
263 int sd = sc * da;
264 int ds = dc * sa;
265 if (sd > ds) {
266 // srcover
267 return sc + dc - SkDiv255Round(ds);
268 } else {
269 // dstover
270 return dc + sc - SkDiv255Round(sd);
271 }
272}
273static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
274 int sa = SkGetPackedA32(src);
275 int da = SkGetPackedA32(dst);
276 int a = srcover_byte(sa, da);
277 int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
278 int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
279 int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
280 return SkPackARGB32(a, r, g, b);
281}
282
283// kColorDodge_Mode
284static inline int colordodge_byte(int sc, int dc, int sa, int da) {
285 int diff = sa - sc;
286 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000287 if (0 == dc) {
288 return SkAlphaMulAlpha(sc, 255 - da);
289 } else if (0 == diff) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000290 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000291 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000292 diff = dc * sa / diff;
293 rc = sa * ((da < diff) ? da : diff) + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000294 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000295 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000296}
297static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000298 int sa = SkGetPackedA32(src);
299 int da = SkGetPackedA32(dst);
300 int a = srcover_byte(sa, da);
301 int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
302 int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
303 int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.coma0f5d152009-06-22 17:38:10 +0000304 return SkPackARGB32(a, r, g, b);
305}
306
307// kColorBurn_Mode
308static inline int colorburn_byte(int sc, int dc, int sa, int da) {
309 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000310 if (dc == da) {
311 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000312 } else if (0 == sc) {
313 return SkAlphaMulAlpha(dc, 255 - sa);
314 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000315 int tmp = (da - dc) * sa / sc;
316 rc = sa * (da - ((da < tmp) ? da : tmp))
317 + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000318 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000319 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000320}
321static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000322 int sa = SkGetPackedA32(src);
323 int da = SkGetPackedA32(dst);
324 int a = srcover_byte(sa, da);
325 int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
326 int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
327 int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
328 return SkPackARGB32(a, r, g, b);
329}
330
331// kHardLight_Mode
332static inline int hardlight_byte(int sc, int dc, int sa, int da) {
333 int rc;
334 if (2 * sc <= sa) {
335 rc = 2 * sc * dc;
336 } else {
337 rc = sa * da - 2 * (da - dc) * (sa - sc);
338 }
339 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
340}
341static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
342 int sa = SkGetPackedA32(src);
343 int da = SkGetPackedA32(dst);
344 int a = srcover_byte(sa, da);
345 int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
346 int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
347 int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
348 return SkPackARGB32(a, r, g, b);
349}
350
351// returns 255 * sqrt(n/255)
352static U8CPU sqrt_unit_byte(U8CPU n) {
353 return SkSqrtBits(n, 15+4);
354}
355
356// kSoftLight_Mode
357static inline int softlight_byte(int sc, int dc, int sa, int da) {
358 int m = da ? dc * 256 / da : 0;
359 int rc;
360 if (2 * sc <= sa) {
361 rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
362 } else if (4 * dc <= da) {
363 int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
364 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
365 } else {
366 int tmp = sqrt_unit_byte(m) - m;
367 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
368 }
369 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
370}
371static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
372 int sa = SkGetPackedA32(src);
373 int da = SkGetPackedA32(dst);
374 int a = srcover_byte(sa, da);
375 int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
376 int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
377 int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
378 return SkPackARGB32(a, r, g, b);
379}
380
381// kDifference_Mode
382static inline int difference_byte(int sc, int dc, int sa, int da) {
383 int tmp = SkMin32(sc * da, dc * sa);
384 return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
385}
386static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
387 int sa = SkGetPackedA32(src);
388 int da = SkGetPackedA32(dst);
389 int a = srcover_byte(sa, da);
390 int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
391 int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
392 int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
393 return SkPackARGB32(a, r, g, b);
394}
395
396// kExclusion_Mode
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000397static inline int exclusion_byte(int sc, int dc, int, int) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000398 // this equations is wacky, wait for SVG to confirm it
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000399 //int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
400
401 // The above equation can be simplified as follows
402 int r = 255*(sc + dc) - 2 * sc * dc;
reed@android.coma0f5d152009-06-22 17:38:10 +0000403 return clamp_div255round(r);
404}
405static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
406 int sa = SkGetPackedA32(src);
407 int da = SkGetPackedA32(dst);
408 int a = srcover_byte(sa, da);
409 int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
410 int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
411 int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.com543ed932009-04-24 12:43:40 +0000412 return SkPackARGB32(a, r, g, b);
413}
414
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000415// The CSS compositing spec introduces the following formulas:
416// (See https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnonseparable)
417// SkComputeLuminance is similar to this formula but it uses the new definition from Rec. 709
418// while PDF and CG uses the one from Rec. Rec. 601
419// See http://www.glennchan.info/articles/technical/hd-versus-sd-color-space/hd-versus-sd-color-space.htm
420static inline int Lum(int r, int g, int b)
421{
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000422 return SkDiv255Round(r * 77 + g * 150 + b * 28);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000423}
424
425static inline int min2(int a, int b) { return a < b ? a : b; }
426static inline int max2(int a, int b) { return a > b ? a : b; }
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000427#define minimum(a, b, c) min2(min2(a, b), c)
428#define maximum(a, b, c) max2(max2(a, b), c)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000429
430static inline int Sat(int r, int g, int b) {
431 return maximum(r, g, b) - minimum(r, g, b);
432}
433
434static inline void setSaturationComponents(int* Cmin, int* Cmid, int* Cmax, int s) {
reed@google.com3c1ea3a2013-03-07 15:31:58 +0000435 if(*Cmax > *Cmin) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000436 *Cmid = SkMulDiv(*Cmid - *Cmin, s, *Cmax - *Cmin);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000437 *Cmax = s;
438 } else {
439 *Cmax = 0;
440 *Cmid = 0;
441 }
442
443 *Cmin = 0;
444}
445
446static inline void SetSat(int* r, int* g, int* b, int s) {
447 if(*r <= *g) {
448 if(*g <= *b) {
449 setSaturationComponents(r, g, b, s);
450 } else if(*r <= *b) {
451 setSaturationComponents(r, b, g, s);
452 } else {
453 setSaturationComponents(b, r, g, s);
454 }
455 } else if(*r <= *b) {
456 setSaturationComponents(g, r, b, s);
457 } else if(*g <= *b) {
458 setSaturationComponents(g, b, r, s);
459 } else {
460 setSaturationComponents(b, g, r, s);
461 }
462}
463
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000464static inline void clipColor(int* r, int* g, int* b, int a) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000465 int L = Lum(*r, *g, *b);
466 int n = minimum(*r, *g, *b);
467 int x = maximum(*r, *g, *b);
468 if(n < 0) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000469 *r = L + SkMulDiv(*r - L, L, L - n);
470 *g = L + SkMulDiv(*g - L, L, L - n);
471 *b = L + SkMulDiv(*b - L, L, L - n);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000472 }
473
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000474 if (x > a) {
475 *r = L + SkMulDiv(*r - L, a - L, x - L);
476 *g = L + SkMulDiv(*g - L, a - L, x - L);
477 *b = L + SkMulDiv(*b - L, a - L, x - L);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000478 }
479}
480
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000481static inline void SetLum(int* r, int* g, int* b, int a, int l) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000482 int d = l - Lum(*r, *g, *b);
483 *r += d;
484 *g += d;
485 *b += d;
486
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000487 clipColor(r, g, b, a);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000488}
489
490// non-separable blend modes are done in non-premultiplied alpha
491#define blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000492 clamp_div255round(sc * (255 - da) + dc * (255 - sa) + blendval)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000493
494// kHue_Mode
495// B(Cb, Cs) = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
496// Create a color with the hue of the source color and the saturation and luminosity of the backdrop color.
497static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
498 int sr = SkGetPackedR32(src);
499 int sg = SkGetPackedG32(src);
500 int sb = SkGetPackedB32(src);
501 int sa = SkGetPackedA32(src);
502
503 int dr = SkGetPackedR32(dst);
504 int dg = SkGetPackedG32(dst);
505 int db = SkGetPackedB32(dst);
506 int da = SkGetPackedA32(dst);
507 int Sr, Sg, Sb;
508
509 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000510 Sr = sr * sa;
511 Sg = sg * sa;
512 Sb = sb * sa;
513 SetSat(&Sr, &Sg, &Sb, Sat(dr, dg, db) * sa);
514 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000515 } else {
516 Sr = 0;
517 Sg = 0;
518 Sb = 0;
519 }
520
521 int a = srcover_byte(sa, da);
522 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
523 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
524 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
525 return SkPackARGB32(a, r, g, b);
526}
527
528// kSaturation_Mode
529// B(Cb, Cs) = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000530// 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 +0000531static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
532 int sr = SkGetPackedR32(src);
533 int sg = SkGetPackedG32(src);
534 int sb = SkGetPackedB32(src);
535 int sa = SkGetPackedA32(src);
536
537 int dr = SkGetPackedR32(dst);
538 int dg = SkGetPackedG32(dst);
539 int db = SkGetPackedB32(dst);
540 int da = SkGetPackedA32(dst);
541 int Dr, Dg, Db;
542
543 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000544 Dr = dr * sa;
545 Dg = dg * sa;
546 Db = db * sa;
547 SetSat(&Dr, &Dg, &Db, Sat(sr, sg, sb) * da);
548 SetLum(&Dr, &Dg, &Db, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000549 } else {
550 Dr = 0;
551 Dg = 0;
552 Db = 0;
553 }
554
555 int a = srcover_byte(sa, da);
556 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
557 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
558 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
559 return SkPackARGB32(a, r, g, b);
560}
561
562// kColor_Mode
563// B(Cb, Cs) = SetLum(Cs, Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000564// 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 +0000565static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
566 int sr = SkGetPackedR32(src);
567 int sg = SkGetPackedG32(src);
568 int sb = SkGetPackedB32(src);
569 int sa = SkGetPackedA32(src);
570
571 int dr = SkGetPackedR32(dst);
572 int dg = SkGetPackedG32(dst);
573 int db = SkGetPackedB32(dst);
574 int da = SkGetPackedA32(dst);
575 int Sr, Sg, Sb;
576
577 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000578 Sr = sr * da;
579 Sg = sg * da;
580 Sb = sb * da;
581 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000582 } else {
583 Sr = 0;
584 Sg = 0;
585 Sb = 0;
586 }
587
588 int a = srcover_byte(sa, da);
589 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
590 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
591 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
592 return SkPackARGB32(a, r, g, b);
593}
594
595// kLuminosity_Mode
596// B(Cb, Cs) = SetLum(Cb, Lum(Cs))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000597// 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 +0000598static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
599 int sr = SkGetPackedR32(src);
600 int sg = SkGetPackedG32(src);
601 int sb = SkGetPackedB32(src);
602 int sa = SkGetPackedA32(src);
603
604 int dr = SkGetPackedR32(dst);
605 int dg = SkGetPackedG32(dst);
606 int db = SkGetPackedB32(dst);
607 int da = SkGetPackedA32(dst);
608 int Dr, Dg, Db;
609
610 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000611 Dr = dr * sa;
612 Dg = dg * sa;
613 Db = db * sa;
614 SetLum(&Dr, &Dg, &Db, sa * da, Lum(sr, sg, sb) * da);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000615 } else {
616 Dr = 0;
617 Dg = 0;
618 Db = 0;
619 }
620
621 int a = srcover_byte(sa, da);
622 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
623 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
624 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
625 return SkPackARGB32(a, r, g, b);
626}
627
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +0000628const ProcCoeff gProcCoeffs[] = {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000629 { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff },
630 { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff },
631 { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff },
632 { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff },
633 { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff },
634 { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff },
635 { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff },
636 { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff },
637 { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff },
638 { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff },
639 { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff },
640 { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff },
641
reed@google.com521e34e2011-04-12 18:55:21 +0000642 { plus_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kOne_Coeff },
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000643 { modulate_modeproc,SkXfermode::kZero_Coeff, SkXfermode::kSC_Coeff },
bsalomon@google.comb0091b82013-04-15 15:16:47 +0000644 { screen_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISC_Coeff },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000645 { overlay_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
646 { darken_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
647 { lighten_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
648 { colordodge_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
649 { colorburn_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
650 { hardlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
651 { softlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
652 { difference_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
653 { exclusion_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
reed@google.com25cfa692013-02-04 20:06:00 +0000654 { multiply_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000655 { hue_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
656 { saturation_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
657 { color_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
658 { luminosity_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000659};
660
661///////////////////////////////////////////////////////////////////////////////
662
reed@google.com30da7452012-12-17 19:55:24 +0000663bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000664 return false;
665}
666
reed@google.com30da7452012-12-17 19:55:24 +0000667bool SkXfermode::asMode(Mode* mode) const {
reed@google.comc0d4aa22011-04-13 21:12:04 +0000668 return false;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000669}
670
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000671bool SkXfermode::asNewEffectOrCoeff(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst, GrTexture*) const {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000672 return this->asCoeff(src, dst);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000673}
674
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000675bool SkXfermode::AsNewEffectOrCoeff(SkXfermode* xfermode,
676 GrContext* context,
677 GrEffectRef** effect,
678 Coeff* src,
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000679 Coeff* dst,
680 GrTexture* background) {
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000681 if (NULL == xfermode) {
682 return ModeAsCoeff(kSrcOver_Mode, src, dst);
683 } else {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000684 return xfermode->asNewEffectOrCoeff(context, effect, src, dst, background);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000685 }
686}
687
reed@google.com30da7452012-12-17 19:55:24 +0000688SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) const{
vandebo@chromium.org48543272011-02-08 19:28:07 +0000689 // no-op. subclasses should override this
690 return dst;
691}
692
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000693void SkXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
694 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000695 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000696 SkASSERT(dst && src && count >= 0);
697
698 if (NULL == aa) {
699 for (int i = count - 1; i >= 0; --i) {
700 dst[i] = this->xferColor(src[i], dst[i]);
701 }
702 } else {
703 for (int i = count - 1; i >= 0; --i) {
704 unsigned a = aa[i];
705 if (0 != a) {
706 SkPMColor dstC = dst[i];
707 SkPMColor C = this->xferColor(src[i], dstC);
708 if (0xFF != a) {
709 C = SkFourByteInterp(C, dstC, a);
710 }
711 dst[i] = C;
712 }
713 }
714 }
715}
716
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000717void SkXfermode::xfer16(uint16_t* dst,
718 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000719 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000720 SkASSERT(dst && src && count >= 0);
721
722 if (NULL == aa) {
723 for (int i = count - 1; i >= 0; --i) {
724 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
725 dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
726 }
727 } else {
728 for (int i = count - 1; i >= 0; --i) {
729 unsigned a = aa[i];
730 if (0 != a) {
731 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
732 SkPMColor C = this->xferColor(src[i], dstC);
733 if (0xFF != a) {
734 C = SkFourByteInterp(C, dstC, a);
735 }
736 dst[i] = SkPixel32ToPixel16_ToU16(C);
737 }
738 }
739 }
740}
741
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000742void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
vandebo@chromium.org48543272011-02-08 19:28:07 +0000743 const SkPMColor src[], int count,
reed@google.com30da7452012-12-17 19:55:24 +0000744 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000745 SkASSERT(dst && src && count >= 0);
746
747 if (NULL == aa) {
748 for (int i = count - 1; i >= 0; --i) {
749 SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
750 dst[i] = SkToU8(SkGetPackedA32(res));
751 }
752 } else {
753 for (int i = count - 1; i >= 0; --i) {
754 unsigned a = aa[i];
755 if (0 != a) {
756 SkAlpha dstA = dst[i];
757 unsigned A = SkGetPackedA32(this->xferColor(src[i],
758 (SkPMColor)(dstA << SK_A32_SHIFT)));
759 if (0xFF != a) {
760 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
761 }
762 dst[i] = SkToU8(A);
763 }
764 }
765 }
766}
767
768///////////////////////////////////////////////////////////////////////////////
769
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000770void SkProcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
771 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000772 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000773 SkASSERT(dst && src && count >= 0);
774
775 SkXfermodeProc proc = fProc;
776
777 if (NULL != proc) {
778 if (NULL == aa) {
779 for (int i = count - 1; i >= 0; --i) {
780 dst[i] = proc(src[i], dst[i]);
781 }
782 } else {
783 for (int i = count - 1; i >= 0; --i) {
784 unsigned a = aa[i];
785 if (0 != a) {
786 SkPMColor dstC = dst[i];
787 SkPMColor C = proc(src[i], dstC);
788 if (a != 0xFF) {
789 C = SkFourByteInterp(C, dstC, a);
790 }
791 dst[i] = C;
792 }
793 }
794 }
795 }
796}
797
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000798void SkProcXfermode::xfer16(uint16_t* SK_RESTRICT dst,
799 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000800 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000801 SkASSERT(dst && src && count >= 0);
802
803 SkXfermodeProc proc = fProc;
804
805 if (NULL != proc) {
806 if (NULL == aa) {
807 for (int i = count - 1; i >= 0; --i) {
808 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
809 dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
810 }
811 } else {
812 for (int i = count - 1; i >= 0; --i) {
813 unsigned a = aa[i];
814 if (0 != a) {
815 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
816 SkPMColor C = proc(src[i], dstC);
817 if (0xFF != a) {
818 C = SkFourByteInterp(C, dstC, a);
819 }
820 dst[i] = SkPixel32ToPixel16_ToU16(C);
821 }
822 }
823 }
824 }
825}
826
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000827void SkProcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
828 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000829 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000830 SkASSERT(dst && src && count >= 0);
831
832 SkXfermodeProc proc = fProc;
833
834 if (NULL != proc) {
835 if (NULL == aa) {
836 for (int i = count - 1; i >= 0; --i) {
837 SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
838 dst[i] = SkToU8(SkGetPackedA32(res));
839 }
840 } else {
841 for (int i = count - 1; i >= 0; --i) {
842 unsigned a = aa[i];
843 if (0 != a) {
844 SkAlpha dstA = dst[i];
845 SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
846 unsigned A = SkGetPackedA32(res);
847 if (0xFF != a) {
848 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
849 }
850 dst[i] = SkToU8(A);
851 }
852 }
853 }
854 }
855}
856
857SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
858 : SkXfermode(buffer) {
reed@google.com34342f62012-06-25 14:36:28 +0000859 fProc = NULL;
860 if (!buffer.isCrossProcess()) {
861 fProc = (SkXfermodeProc)buffer.readFunctionPtr();
862 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000863}
864
djsollen@google.com54924242012-03-29 15:18:04 +0000865void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
866 this->INHERITED::flatten(buffer);
reed@google.com34342f62012-06-25 14:36:28 +0000867 if (!buffer.isCrossProcess()) {
yangsu@google.comf468e472011-08-10 18:34:50 +0000868 buffer.writeFunctionPtr((void*)fProc);
869 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000870}
871
robertphillips@google.comb83b6b42013-01-22 14:32:09 +0000872#ifdef SK_DEVELOPER
873void SkProcXfermode::toString(SkString* str) const {
874 str->appendf("SkProcXfermode: %p", fProc);
875}
876#endif
877
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000878//////////////////////////////////////////////////////////////////////////////
879
880#if SK_SUPPORT_GPU
881
882#include "GrEffect.h"
bsalomon@google.com77af6802013-10-02 13:04:56 +0000883#include "GrCoordTransform.h"
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000884#include "GrEffectUnitTest.h"
885#include "GrTBackendEffectFactory.h"
886#include "gl/GrGLEffect.h"
887
888/**
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000889 * GrEffect that implements the all the separable xfer modes that cannot be expressed as Coeffs.
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000890 */
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000891class XferEffect : public GrEffect {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000892public:
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000893 static bool IsSupportedMode(SkXfermode::Mode mode) {
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000894 return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000895 }
896
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000897 static GrEffectRef* Create(SkXfermode::Mode mode, GrTexture* background) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000898 if (!IsSupportedMode(mode)) {
899 return NULL;
900 } else {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000901 AutoEffectUnref effect(SkNEW_ARGS(XferEffect, (mode, background)));
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000902 return CreateEffectRef(effect);
903 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000904 }
905
906 virtual void getConstantColorComponents(GrColor* color,
907 uint32_t* validFlags) const SK_OVERRIDE {
908 *validFlags = 0;
909 }
910
911 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000912 return GrTBackendEffectFactory<XferEffect>::getInstance();
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000913 }
914
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000915 static const char* Name() { return "XferEffect"; }
916
917 SkXfermode::Mode mode() const { return fMode; }
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000918 const GrTextureAccess& backgroundAccess() const { return fBackgroundAccess; }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000919
920 class GLEffect : public GrGLEffect {
921 public:
922 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
bsalomon@google.com77af6802013-10-02 13:04:56 +0000923 : GrGLEffect(factory) {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000924 }
925 virtual void emitCode(GrGLShaderBuilder* builder,
926 const GrDrawEffect& drawEffect,
927 EffectKey key,
928 const char* outputColor,
929 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000930 const TransformedCoordsArray& coords,
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000931 const TextureSamplerArray& samplers) SK_OVERRIDE {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000932 SkXfermode::Mode mode = drawEffect.castEffect<XferEffect>().mode();
933 const GrTexture* backgroundTex = drawEffect.castEffect<XferEffect>().backgroundAccess().getTexture();
934 const char* dstColor;
935 if (backgroundTex) {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000936 dstColor = "bgColor";
937 builder->fsCodeAppendf("\t\tvec4 %s = ", dstColor);
bsalomon@google.com77af6802013-10-02 13:04:56 +0000938 builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000939 builder->fsCodeAppendf(";\n");
940 } else {
941 dstColor = builder->dstColor();
942 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000943 SkASSERT(NULL != dstColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000944
945 // We don't try to optimize for this case at all
bsalomon@google.comb79d8652013-03-29 20:30:50 +0000946 if (NULL == inputColor) {
commit-bot@chromium.orgdd72fde2013-04-29 15:25:03 +0000947 builder->fsCodeAppendf("\t\tconst vec4 ones = %s;\n", GrGLSLOnesVecf(4));
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000948 inputColor = "ones";
949 }
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000950 builder->fsCodeAppendf("\t\t// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
951
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000952 // These all perform src-over on the alpha channel.
953 builder->fsCodeAppendf("\t\t%s.a = %s.a + (1.0 - %s.a) * %s.a;\n",
954 outputColor, inputColor, inputColor, dstColor);
955
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000956 switch (mode) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000957 case SkXfermode::kOverlay_Mode:
958 // Overlay is Hard-Light with the src and dst reversed
959 HardLight(builder, outputColor, dstColor, inputColor);
960 break;
961 case SkXfermode::kDarken_Mode:
962 builder->fsCodeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
963 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
964 outputColor,
965 inputColor, dstColor, inputColor,
966 dstColor, inputColor, dstColor);
967 break;
968 case SkXfermode::kLighten_Mode:
969 builder->fsCodeAppendf("\t\t%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
970 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
971 outputColor,
972 inputColor, dstColor, inputColor,
973 dstColor, inputColor, dstColor);
974 break;
975 case SkXfermode::kColorDodge_Mode:
976 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'r');
977 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'g');
978 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'b');
979 break;
980 case SkXfermode::kColorBurn_Mode:
981 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'r');
982 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'g');
983 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'b');
984 break;
985 case SkXfermode::kHardLight_Mode:
986 HardLight(builder, outputColor, inputColor, dstColor);
987 break;
988 case SkXfermode::kSoftLight_Mode:
989 builder->fsCodeAppendf("\t\tif (0.0 == %s.a) {\n", dstColor);
990 builder->fsCodeAppendf("\t\t\t%s.rgba = %s;\n", outputColor, inputColor);
991 builder->fsCodeAppendf("\t\t} else {\n");
992 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'r');
993 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'g');
994 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'b');
995 builder->fsCodeAppendf("\t\t}\n");
996 break;
997 case SkXfermode::kDifference_Mode:
998 builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb -"
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000999 "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001000 outputColor, inputColor, dstColor, inputColor, dstColor,
1001 dstColor, inputColor);
1002 break;
1003 case SkXfermode::kExclusion_Mode:
1004 builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb - "
1005 "2.0 * %s.rgb * %s.rgb;\n",
1006 outputColor, dstColor, inputColor, dstColor, inputColor);
1007 break;
1008 case SkXfermode::kMultiply_Mode:
1009 builder->fsCodeAppendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + "
1010 "(1.0 - %s.a) * %s.rgb + "
1011 "%s.rgb * %s.rgb;\n",
1012 outputColor, inputColor, dstColor, dstColor, inputColor,
1013 inputColor, dstColor);
1014 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001015 case SkXfermode::kHue_Mode: {
1016 // SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
1017 SkString setSat, setLum;
1018 AddSatFunction(builder, &setSat);
1019 AddLumFunction(builder, &setLum);
1020 builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
1021 dstColor, inputColor);
1022 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
1023 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
1024 dstColor);
1025 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1026 outputColor, inputColor, dstColor, dstColor, inputColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001027 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001028 }
1029 case SkXfermode::kSaturation_Mode: {
1030 // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
1031 SkString setSat, setLum;
1032 AddSatFunction(builder, &setSat);
1033 AddLumFunction(builder, &setLum);
1034 builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
1035 dstColor, inputColor);
1036 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
1037 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
1038 dstColor);
1039 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1040 outputColor, inputColor, dstColor, dstColor, inputColor);
1041 break;
1042 }
1043 case SkXfermode::kColor_Mode: {
1044 // SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
1045 SkString setLum;
1046 AddLumFunction(builder, &setLum);
1047 builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
1048 inputColor, dstColor);
1049 builder->fsCodeAppendf("\t\t%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);\n",
1050 outputColor, setLum.c_str(), dstColor, inputColor);
1051 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1052 outputColor, inputColor, dstColor, dstColor, inputColor);
1053 break;
1054 }
1055 case SkXfermode::kLuminosity_Mode: {
1056 // SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
1057 SkString setLum;
1058 AddLumFunction(builder, &setLum);
1059 builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
1060 inputColor, dstColor);
1061 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);\n",
1062 outputColor, setLum.c_str(), dstColor, inputColor);
1063 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1064 outputColor, inputColor, dstColor, dstColor, inputColor);
1065 break;
1066 }
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001067 default:
1068 GrCrash("Unknown XferEffect mode.");
1069 break;
bsalomon@google.comb79d8652013-03-29 20:30:50 +00001070 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001071 }
1072
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001073 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com77af6802013-10-02 13:04:56 +00001074 return drawEffect.castEffect<XferEffect>().mode();
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001075 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001076
1077 private:
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001078 static void HardLight(GrGLShaderBuilder* builder,
1079 const char* final,
1080 const char* src,
1081 const char* dst) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +00001082 static const char kComponents[] = {'r', 'g', 'b'};
1083 for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
1084 char component = kComponents[i];
1085 builder->fsCodeAppendf("\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
1086 builder->fsCodeAppendf("\t\t\t%s.%c = 2.0 * %s.%c * %s.%c;\n", final, component, src, component, dst, component);
1087 builder->fsCodeAppend("\t\t} else {\n");
1088 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);\n",
1089 final, component, src, dst, dst, dst, component, src, src, component);
1090 builder->fsCodeAppend("\t\t}\n");
1091 }
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001092 builder->fsCodeAppendf("\t\t%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);\n",
1093 final, src, dst, dst, src);
1094 }
1095
1096 // Does one component of color-dodge
1097 static void ColorDodgeComponent(GrGLShaderBuilder* builder,
1098 const char* final,
1099 const char* src,
1100 const char* dst,
1101 const char component) {
1102 builder->fsCodeAppendf("\t\tif (0.0 == %s.%c) {\n", dst, component);
bsalomon@google.com58eb1af2013-04-19 16:20:20 +00001103 builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001104 final, component, src, component, dst);
1105 builder->fsCodeAppend("\t\t} else {\n");
1106 builder->fsCodeAppendf("\t\t\tfloat d = %s.a - %s.%c;\n", src, src, component);
bsalomon@google.com07fa3ab2013-04-19 17:21:49 +00001107 builder->fsCodeAppend("\t\t\tif (0.0 == d) {\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001108 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",
1109 final, component, src, dst, src, component, dst, dst, component,
1110 src);
1111 builder->fsCodeAppend("\t\t\t} else {\n");
1112 builder->fsCodeAppendf("\t\t\t\td = min(%s.a, %s.%c * %s.a / d);\n",
1113 dst, dst, component, src);
1114 builder->fsCodeAppendf("\t\t\t\t%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1115 final, component, src, src, component, dst, dst, component, src);
1116 builder->fsCodeAppend("\t\t\t}\n");
1117 builder->fsCodeAppend("\t\t}\n");
1118 }
1119
1120 // Does one component of color-burn
1121 static void ColorBurnComponent(GrGLShaderBuilder* builder,
1122 const char* final,
1123 const char* src,
1124 const char* dst,
1125 const char component) {
1126 builder->fsCodeAppendf("\t\tif (%s.a == %s.%c) {\n", dst, dst, component);
1127 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1128 final, component, src, dst, src, component, dst, dst, component,
1129 src);
1130 builder->fsCodeAppendf("\t\t} else if (0.0 == %s.%c) {\n", src, component);
1131 builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
1132 final, component, dst, component, src);
1133 builder->fsCodeAppend("\t\t} else {\n");
1134 builder->fsCodeAppendf("\t\t\tfloat d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);\n",
1135 dst, dst, dst, component, src, src, component);
1136 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1137 final, component, src, src, component, dst, dst, component, src);
1138 builder->fsCodeAppend("\t\t}\n");
1139 }
1140
1141 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
1142 static void SoftLightComponentPosDstAlpha(GrGLShaderBuilder* builder,
1143 const char* final,
1144 const char* src,
1145 const char* dst,
1146 const char component) {
1147 // if (2S < Sa)
1148 builder->fsCodeAppendf("\t\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
1149 // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
bsalomon@google.com68567792013-04-19 18:10:50 +00001150 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 +00001151 final, component, dst, component, dst, component, src, src,
1152 component, dst, dst, src, component, dst, component, src, src,
1153 component);
1154 // else if (4D < Da)
1155 builder->fsCodeAppendf("\t\t\t} else if (4.0 * %s.%c <= %s.a) {\n",
1156 dst, component, dst);
1157 builder->fsCodeAppendf("\t\t\t\tfloat DSqd = %s.%c * %s.%c;\n",
1158 dst, component, dst, component);
1159 builder->fsCodeAppendf("\t\t\t\tfloat DCub = DSqd * %s.%c;\n", dst, component);
1160 builder->fsCodeAppendf("\t\t\t\tfloat DaSqd = %s.a * %s.a;\n", dst, dst);
1161 builder->fsCodeAppendf("\t\t\t\tfloat DaCub = DaSqd * %s.a;\n", dst);
1162 // (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 +00001163 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 +00001164 final, component, src, component, src, component, dst, component,
1165 src, src, component, dst, src, src, component, src, src,
1166 component);
1167 builder->fsCodeAppendf("\t\t\t} else {\n");
1168 // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
bsalomon@google.com68567792013-04-19 18:10:50 +00001169 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 +00001170 final, component, dst, dst, component, src, src, component, dst,
1171 src, component, dst, component, src, src, component, src,
1172 component);
1173 builder->fsCodeAppendf("\t\t\t}\n");
1174 }
1175
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001176 // Adds a function that takes two colors and an alpha as input. It produces a color with the
1177 // hue and saturation of the first color, the luminosity of the second color, and the input
1178 // alpha. It has this signature:
1179 // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
1180 static void AddLumFunction(GrGLShaderBuilder* builder, SkString* setLumFunction) {
1181 // Emit a helper that gets the luminance of a color.
1182 SkString getFunction;
1183 GrGLShaderVar getLumArgs[] = {
1184 GrGLShaderVar("color", kVec3f_GrSLType),
1185 };
1186 SkString getLumBody("\treturn dot(vec3(0.3, 0.59, 0.11), color);\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001187 builder->fsEmitFunction(kFloat_GrSLType,
1188 "luminance",
1189 SK_ARRAY_COUNT(getLumArgs), getLumArgs,
1190 getLumBody.c_str(),
1191 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001192
1193 // Emit the set luminance function.
1194 GrGLShaderVar setLumArgs[] = {
1195 GrGLShaderVar("hueSat", kVec3f_GrSLType),
1196 GrGLShaderVar("alpha", kFloat_GrSLType),
1197 GrGLShaderVar("lumColor", kVec3f_GrSLType),
1198 };
1199 SkString setLumBody;
1200 setLumBody.printf("\tfloat diff = %s(lumColor - hueSat);\n", getFunction.c_str());
1201 setLumBody.append("\tvec3 outColor = hueSat + diff;\n");
1202 setLumBody.appendf("\tfloat outLum = %s(outColor);\n", getFunction.c_str());
1203 setLumBody.append("\tfloat minComp = min(min(outColor.r, outColor.g), outColor.b);\n"
1204 "\tfloat maxComp = max(max(outColor.r, outColor.g), outColor.b);\n"
1205 "\tif (minComp < 0.0) {\n"
1206 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / (outLum - minComp);\n"
1207 "\t}\n"
1208 "\tif (maxComp > alpha) {\n"
1209 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) / (maxComp - outLum);\n"
1210 "\t}\n"
1211 "\treturn outColor;\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001212 builder->fsEmitFunction(kVec3f_GrSLType,
1213 "set_luminance",
1214 SK_ARRAY_COUNT(setLumArgs), setLumArgs,
1215 setLumBody.c_str(),
1216 setLumFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001217 }
1218
1219 // Adds a function that creates a color with the hue and luminosity of one input color and
1220 // the saturation of another color. It will have this signature:
1221 // float set_saturation(vec3 hueLumColor, vec3 satColor)
1222 static void AddSatFunction(GrGLShaderBuilder* builder, SkString* setSatFunction) {
1223 // Emit a helper that gets the saturation of a color
1224 SkString getFunction;
1225 GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
1226 SkString getSatBody;
1227 getSatBody.printf("\treturn max(max(color.r, color.g), color.b) - "
1228 "min(min(color.r, color.g), color.b);\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001229 builder->fsEmitFunction(kFloat_GrSLType,
1230 "saturation",
1231 SK_ARRAY_COUNT(getSatArgs), getSatArgs,
1232 getSatBody.c_str(),
1233 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001234
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001235 // Emit a helper that sets the saturation given sorted input channels. This used
1236 // to use inout params for min, mid, and max components but that seems to cause
1237 // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
1238 // adjusted min, mid, and max inputs, respectively.
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001239 SkString helperFunction;
1240 GrGLShaderVar helperArgs[] = {
1241 GrGLShaderVar("minComp", kFloat_GrSLType),
1242 GrGLShaderVar("midComp", kFloat_GrSLType),
1243 GrGLShaderVar("maxComp", kFloat_GrSLType),
1244 GrGLShaderVar("sat", kFloat_GrSLType),
1245 };
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001246 static const char kHelperBody[] = "\tif (minComp < maxComp) {\n"
1247 "\t\tvec3 result;\n"
1248 "\t\tresult.r = 0.0;\n"
1249 "\t\tresult.g = sat * (midComp - minComp) / (maxComp - minComp);\n"
1250 "\t\tresult.b = sat;\n"
1251 "\t\treturn result;\n"
1252 "\t} else {\n"
1253 "\t\treturn vec3(0, 0, 0);\n"
1254 "\t}\n";
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001255 builder->fsEmitFunction(kVec3f_GrSLType,
1256 "set_saturation_helper",
1257 SK_ARRAY_COUNT(helperArgs), helperArgs,
1258 kHelperBody,
1259 &helperFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001260
1261 GrGLShaderVar setSatArgs[] = {
1262 GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
1263 GrGLShaderVar("satColor", kVec3f_GrSLType),
1264 };
1265 const char* helpFunc = helperFunction.c_str();
1266 SkString setSatBody;
1267 setSatBody.appendf("\tfloat sat = %s(satColor);\n"
1268 "\tif (hueLumColor.r <= hueLumColor.g) {\n"
1269 "\t\tif (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001270 "\t\t\thueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001271 "\t\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001272 "\t\t\thueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001273 "\t\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001274 "\t\t\thueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001275 "\t\t}\n"
1276 "\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001277 "\t\thueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001278 "\t} else if (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001279 "\t\thueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001280 "\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001281 "\t\thueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001282 "\t}\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001283 "\treturn hueLumColor;\n",
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001284 getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
1285 helpFunc, helpFunc);
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001286 builder->fsEmitFunction(kVec3f_GrSLType,
1287 "set_saturation",
1288 SK_ARRAY_COUNT(setSatArgs), setSatArgs,
1289 setSatBody.c_str(),
1290 setSatFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001291
1292 }
1293
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001294 typedef GrGLEffect INHERITED;
1295 };
1296
1297 GR_DECLARE_EFFECT_TEST;
1298
1299private:
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001300 XferEffect(SkXfermode::Mode mode, GrTexture* background)
1301 : fMode(mode) {
1302 if (background) {
bsalomon@google.com77af6802013-10-02 13:04:56 +00001303 fBackgroundTransform.reset(kLocal_GrCoordSet, background);
1304 this->addCoordTransform(&fBackgroundTransform);
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001305 fBackgroundAccess.reset(background);
1306 this->addTextureAccess(&fBackgroundAccess);
1307 } else {
1308 this->setWillReadDstColor();
1309 }
1310 }
1311 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
1312 const XferEffect& s = CastEffect<XferEffect>(other);
1313 return fMode == s.fMode &&
1314 fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture();
1315 }
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +00001316
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001317 SkXfermode::Mode fMode;
bsalomon@google.com77af6802013-10-02 13:04:56 +00001318 GrCoordTransform fBackgroundTransform;
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001319 GrTextureAccess fBackgroundAccess;
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001320
1321 typedef GrEffect INHERITED;
1322};
1323
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001324GR_DEFINE_EFFECT_TEST(XferEffect);
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +00001325GrEffectRef* XferEffect::TestCreate(SkRandom* rand,
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001326 GrContext*,
1327 const GrDrawTargetCaps&,
1328 GrTexture*[]) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +00001329 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001330
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001331 static AutoEffectUnref gEffect(SkNEW_ARGS(XferEffect, (static_cast<SkXfermode::Mode>(mode), NULL)));
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001332 return CreateEffectRef(gEffect);
1333}
1334
1335#endif
1336
vandebo@chromium.org48543272011-02-08 19:28:07 +00001337///////////////////////////////////////////////////////////////////////////////
1338///////////////////////////////////////////////////////////////////////////////
1339
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001340bool SkProcCoeffXfermode::asMode(Mode* mode) const {
1341 if (mode) {
1342 *mode = fMode;
vandebo@chromium.org48543272011-02-08 19:28:07 +00001343 }
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001344 return true;
1345}
reed@google.comc0d4aa22011-04-13 21:12:04 +00001346
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001347bool SkProcCoeffXfermode::asCoeff(Coeff* sc, Coeff* dc) const {
1348 if (CANNOT_USE_COEFF == fSrcCoeff) {
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001349 return false;
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001350 }
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001351
1352 if (sc) {
1353 *sc = fSrcCoeff;
1354 }
1355 if (dc) {
1356 *dc = fDstCoeff;
1357 }
1358 return true;
1359}
1360
1361#if SK_SUPPORT_GPU
1362bool SkProcCoeffXfermode::asNewEffectOrCoeff(GrContext*,
1363 GrEffectRef** effect,
1364 Coeff* src,
1365 Coeff* dst,
1366 GrTexture* background) const {
1367 if (this->asCoeff(src, dst)) {
1368 return true;
1369 }
1370 if (XferEffect::IsSupportedMode(fMode)) {
1371 if (NULL != effect) {
1372 *effect = XferEffect::Create(fMode, background);
1373 SkASSERT(NULL != *effect);
1374 }
1375 return true;
1376 }
1377 return false;
1378}
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001379#endif
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001380
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001381void SkProcCoeffXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
1382 this->INHERITED::flatten(buffer);
1383 buffer.write32(fMode);
1384}
vandebo@chromium.org48543272011-02-08 19:28:07 +00001385
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001386const char* SkXfermode::ModeName(Mode mode) {
1387 SkASSERT((unsigned) mode <= (unsigned)kLastMode);
1388 const char* gModeStrings[] = {
1389 "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn",
1390 "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Plus",
1391 "Modulate", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge",
1392 "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion",
1393 "Multiply", "Hue", "Saturation", "Color", "Luminosity"
1394 };
1395 return gModeStrings[mode];
1396 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gModeStrings) == kLastMode + 1, mode_count);
1397}
1398
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001399#ifdef SK_DEVELOPER
1400void SkProcCoeffXfermode::toString(SkString* str) const {
1401 str->append("SkProcCoeffXfermode: ");
1402
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001403 str->append("mode: ");
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001404 str->append(ModeName(fMode));
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001405
1406 static const char* gCoeffStrings[kCoeffCount] = {
skia.committer@gmail.com98ded842013-01-23 07:06:17 +00001407 "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001408 };
1409
1410 str->append(" src: ");
1411 if (CANNOT_USE_COEFF == fSrcCoeff) {
1412 str->append("can't use");
1413 } else {
1414 str->append(gCoeffStrings[fSrcCoeff]);
1415 }
1416
1417 str->append(" dst: ");
1418 if (CANNOT_USE_COEFF == fDstCoeff) {
1419 str->append("can't use");
1420 } else {
1421 str->append(gCoeffStrings[fDstCoeff]);
1422 }
1423}
1424#endif
1425
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426///////////////////////////////////////////////////////////////////////////////
1427
1428class SkClearXfermode : public SkProcCoeffXfermode {
1429public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001430 SkClearXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kClear_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431
reed@google.com30da7452012-12-17 19:55:24 +00001432 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1433 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001435 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001436 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkClearXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001437
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438private:
1439 SkClearXfermode(SkFlattenableReadBuffer& buffer)
1440 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001441
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001442 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443};
1444
reed@google.com86ab6c62011-11-28 15:26:14 +00001445void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1446 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001447 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001448 SkASSERT(dst && count >= 0);
1449
1450 if (NULL == aa) {
1451 memset(dst, 0, count << 2);
1452 } else {
1453 for (int i = count - 1; i >= 0; --i) {
1454 unsigned a = aa[i];
1455 if (0xFF == a) {
1456 dst[i] = 0;
1457 } else if (a != 0) {
1458 dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
1459 }
1460 }
1461 }
1462}
1463void SkClearXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1464 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001465 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001466 SkASSERT(dst && count >= 0);
1467
1468 if (NULL == aa) {
1469 memset(dst, 0, count);
1470 } else {
1471 for (int i = count - 1; i >= 0; --i) {
1472 unsigned a = aa[i];
1473 if (0xFF == a) {
1474 dst[i] = 0;
1475 } else if (0 != a) {
1476 dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
1477 }
1478 }
1479 }
1480}
1481
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001482#ifdef SK_DEVELOPER
1483void SkClearXfermode::toString(SkString* str) const {
1484 this->INHERITED::toString(str);
1485}
1486#endif
1487
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488///////////////////////////////////////////////////////////////////////////////
1489
1490class SkSrcXfermode : public SkProcCoeffXfermode {
1491public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001492 SkSrcXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kSrc_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493
reed@google.com30da7452012-12-17 19:55:24 +00001494 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1495 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001497 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001498 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSrcXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001499
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500private:
1501 SkSrcXfermode(SkFlattenableReadBuffer& buffer)
1502 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001503
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001504 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505};
1506
reed@google.com86ab6c62011-11-28 15:26:14 +00001507void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1508 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001509 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001510 SkASSERT(dst && src && count >= 0);
1511
1512 if (NULL == aa) {
1513 memcpy(dst, src, count << 2);
1514 } else {
1515 for (int i = count - 1; i >= 0; --i) {
1516 unsigned a = aa[i];
1517 if (a == 0xFF) {
1518 dst[i] = src[i];
1519 } else if (a != 0) {
1520 dst[i] = SkFourByteInterp(src[i], dst[i], a);
1521 }
1522 }
1523 }
1524}
1525
1526void SkSrcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1527 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001528 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001529 SkASSERT(dst && src && count >= 0);
1530
1531 if (NULL == aa) {
1532 for (int i = count - 1; i >= 0; --i) {
1533 dst[i] = SkToU8(SkGetPackedA32(src[i]));
1534 }
1535 } else {
1536 for (int i = count - 1; i >= 0; --i) {
1537 unsigned a = aa[i];
1538 if (0 != a) {
1539 unsigned srcA = SkGetPackedA32(src[i]);
1540 if (a == 0xFF) {
1541 dst[i] = SkToU8(srcA);
1542 } else {
1543 dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a));
1544 }
1545 }
1546 }
1547 }
1548}
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001549#ifdef SK_DEVELOPER
1550void SkSrcXfermode::toString(SkString* str) const {
1551 this->INHERITED::toString(str);
1552}
1553#endif
reed@google.com86ab6c62011-11-28 15:26:14 +00001554
reed@google.com30da7452012-12-17 19:55:24 +00001555///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001556
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557class SkDstInXfermode : public SkProcCoeffXfermode {
1558public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001559 SkDstInXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstIn_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001560
reed@google.com30da7452012-12-17 19:55:24 +00001561 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001562
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001563 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001564 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstInXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001565
1566private:
1567 SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
1568
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 typedef SkProcCoeffXfermode INHERITED;
1570};
1571
reed@google.com86ab6c62011-11-28 15:26:14 +00001572void SkDstInXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1573 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001574 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001575 SkASSERT(dst && src);
1576
1577 if (count <= 0) {
1578 return;
1579 }
1580 if (NULL != aa) {
1581 return this->INHERITED::xfer32(dst, src, count, aa);
1582 }
1583
1584 do {
1585 unsigned a = SkGetPackedA32(*src);
1586 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
1587 dst++;
1588 src++;
1589 } while (--count != 0);
1590}
1591
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001592#ifdef SK_DEVELOPER
1593void SkDstInXfermode::toString(SkString* str) const {
1594 this->INHERITED::toString(str);
1595}
1596#endif
1597
reed@google.com30da7452012-12-17 19:55:24 +00001598///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001599
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600class SkDstOutXfermode : public SkProcCoeffXfermode {
1601public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001602 SkDstOutXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstOut_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001603
reed@google.com30da7452012-12-17 19:55:24 +00001604 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001605
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001606 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001607 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstOutXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001608
1609private:
1610 SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
1611 : INHERITED(buffer) {}
1612
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 typedef SkProcCoeffXfermode INHERITED;
1614};
1615
reed@google.com86ab6c62011-11-28 15:26:14 +00001616void SkDstOutXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1617 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001618 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001619 SkASSERT(dst && src);
1620
1621 if (count <= 0) {
1622 return;
1623 }
1624 if (NULL != aa) {
1625 return this->INHERITED::xfer32(dst, src, count, aa);
1626 }
1627
1628 do {
1629 unsigned a = SkGetPackedA32(*src);
1630 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
1631 dst++;
1632 src++;
1633 } while (--count != 0);
1634}
1635
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001636#ifdef SK_DEVELOPER
1637void SkDstOutXfermode::toString(SkString* str) const {
1638 this->INHERITED::toString(str);
1639}
1640#endif
1641
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642///////////////////////////////////////////////////////////////////////////////
1643
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001644SK_DECLARE_STATIC_MUTEX(gCachedXfermodesMutex);
1645static SkXfermode* gCachedXfermodes[SkXfermode::kLastMode + 1];
1646
1647void SkXfermode::Term() {
1648 SkAutoMutexAcquire ac(gCachedXfermodesMutex);
1649
1650 for (size_t i = 0; i < SK_ARRAY_COUNT(gCachedXfermodes); ++i) {
1651 SkSafeUnref(gCachedXfermodes[i]);
1652 gCachedXfermodes[i] = NULL;
1653 }
1654}
1655
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001656extern SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec,
1657 SkXfermode::Mode mode);
1658
reed@android.coma0f5d152009-06-22 17:38:10 +00001659SkXfermode* SkXfermode::Create(Mode mode) {
1660 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001661 SkASSERT(SK_ARRAY_COUNT(gCachedXfermodes) == kModeCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001663 if ((unsigned)mode >= kModeCount) {
1664 // report error
1665 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001666 }
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001667
1668 // Skia's "defaut" mode is srcover. NULL in SkPaint is interpreted as srcover
1669 // so we can just return NULL from the factory.
1670 if (kSrcOver_Mode == mode) {
1671 return NULL;
1672 }
1673
1674 // guard our access to gCachedXfermodes, since we may write into it
1675 SkAutoMutexAcquire ac(gCachedXfermodesMutex);
1676
1677 SkXfermode* xfer = gCachedXfermodes[mode];
1678 if (NULL == xfer) {
1679 const ProcCoeff& rec = gProcCoeffs[mode];
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001680
1681 // check if we have a platform optim for that
1682 SkProcCoeffXfermode* xfm = SkPlatformXfermodeFactory(rec, mode);
1683 if (xfm != NULL) {
1684 xfer = xfm;
1685 } else {
1686 // All modes can in theory be represented by the ProcCoeff rec, since
1687 // it contains function ptrs. However, a few modes are both simple and
1688 // commonly used, so we call those out for their own subclasses here.
1689 switch (mode) {
1690 case kClear_Mode:
1691 xfer = SkNEW_ARGS(SkClearXfermode, (rec));
1692 break;
1693 case kSrc_Mode:
1694 xfer = SkNEW_ARGS(SkSrcXfermode, (rec));
1695 break;
1696 case kSrcOver_Mode:
1697 SkASSERT(false); // should not land here
1698 break;
1699 case kDstIn_Mode:
1700 xfer = SkNEW_ARGS(SkDstInXfermode, (rec));
1701 break;
1702 case kDstOut_Mode:
1703 xfer = SkNEW_ARGS(SkDstOutXfermode, (rec));
1704 break;
1705 default:
1706 // no special-case, just rely in the rec and its function-ptrs
1707 xfer = SkNEW_ARGS(SkProcCoeffXfermode, (rec, mode));
1708 break;
1709 }
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001710 }
1711 gCachedXfermodes[mode] = xfer;
1712 }
1713 return SkSafeRef(xfer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714}
1715
reed@google.com43c50c82011-04-14 15:50:52 +00001716SkXfermodeProc SkXfermode::GetProc(Mode mode) {
1717 SkXfermodeProc proc = NULL;
1718 if ((unsigned)mode < kModeCount) {
1719 proc = gProcCoeffs[mode].fProc;
1720 }
1721 return proc;
1722}
1723
1724bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
1725 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001726
reed@google.com43c50c82011-04-14 15:50:52 +00001727 if ((unsigned)mode >= (unsigned)kModeCount) {
1728 // illegal mode parameter
1729 return false;
1730 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001731
reed@google.com43c50c82011-04-14 15:50:52 +00001732 const ProcCoeff& rec = gProcCoeffs[mode];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001733
reed@google.com43c50c82011-04-14 15:50:52 +00001734 if (CANNOT_USE_COEFF == rec.fSC) {
1735 return false;
1736 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001737
reed@google.com43c50c82011-04-14 15:50:52 +00001738 SkASSERT(CANNOT_USE_COEFF != rec.fDC);
1739 if (src) {
1740 *src = rec.fSC;
1741 }
1742 if (dst) {
1743 *dst = rec.fDC;
1744 }
1745 return true;
1746}
1747
reed@google.com30da7452012-12-17 19:55:24 +00001748bool SkXfermode::AsMode(const SkXfermode* xfer, Mode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749 if (NULL == xfer) {
1750 if (mode) {
1751 *mode = kSrcOver_Mode;
1752 }
1753 return true;
1754 }
reed@google.comc0d4aa22011-04-13 21:12:04 +00001755 return xfer->asMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756}
1757
reed@google.com30da7452012-12-17 19:55:24 +00001758bool SkXfermode::AsCoeff(const SkXfermode* xfer, Coeff* src, Coeff* dst) {
reed@google.com43c50c82011-04-14 15:50:52 +00001759 if (NULL == xfer) {
1760 return ModeAsCoeff(kSrcOver_Mode, src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 }
reed@google.com43c50c82011-04-14 15:50:52 +00001762 return xfer->asCoeff(src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763}
1764
reed@google.com30da7452012-12-17 19:55:24 +00001765bool SkXfermode::IsMode(const SkXfermode* xfer, Mode mode) {
mike@reedtribe.orge303fcf2011-11-17 02:16:43 +00001766 // if xfer==null then the mode is srcover
1767 Mode m = kSrcOver_Mode;
1768 if (xfer && !xfer->asMode(&m)) {
1769 return false;
1770 }
1771 return mode == m;
1772}
1773
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774///////////////////////////////////////////////////////////////////////////////
1775//////////// 16bit xfermode procs
1776
1777#ifdef SK_DEBUG
1778static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
1779static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
1780#endif
1781
1782static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
1783 SkASSERT(require_255(src));
1784 return SkPixel32ToPixel16(src);
1785}
1786
1787static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
1788 return dst;
1789}
1790
1791static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
1792 SkASSERT(require_0(src));
1793 return dst;
1794}
1795
1796static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
1797 SkASSERT(require_255(src));
1798 return SkPixel32ToPixel16(src);
1799}
1800
1801static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
1802 SkASSERT(require_0(src));
1803 return dst;
1804}
1805
1806static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
1807 SkASSERT(require_255(src));
1808 return dst;
1809}
1810
1811static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
1812 SkASSERT(require_255(src));
1813 return SkPixel32ToPixel16(src);
1814}
1815
1816static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
1817 SkASSERT(require_255(src));
1818 return dst;
1819}
1820
1821static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
1822 SkASSERT(require_0(src));
1823 return dst;
1824}
1825
1826static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
1827 unsigned isa = 255 - SkGetPackedA32(src);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001828
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 return SkPackRGB16(
1830 SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
1831 SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
1832 SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
1833}
1834
1835static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
1836 SkASSERT(require_0(src));
1837 return dst;
1838}
1839
1840static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1841 SkASSERT(require_255(src));
1842 return SkPixel32ToPixel16(src);
1843}
1844
1845static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1846 SkASSERT(require_255(src));
1847 return dst;
1848}
1849
1850/*********
1851 darken and lighten boil down to this.
1852
1853 darken = (1 - Sa) * Dc + min(Sc, Dc)
1854 lighten = (1 - Sa) * Dc + max(Sc, Dc)
1855
1856 if (Sa == 0) these become
1857 darken = Dc + min(0, Dc) = 0
1858 lighten = Dc + max(0, Dc) = Dc
1859
1860 if (Sa == 1) these become
1861 darken = min(Sc, Dc)
1862 lighten = max(Sc, Dc)
1863*/
1864
1865static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
1866 SkASSERT(require_0(src));
1867 return 0;
1868}
1869
1870static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
1871 SkASSERT(require_255(src));
1872 unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1873 unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1874 unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1875 return SkPackRGB16(r, g, b);
1876}
1877
1878static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
1879 SkASSERT(require_0(src));
1880 return dst;
1881}
1882
1883static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
1884 SkASSERT(require_255(src));
1885 unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1886 unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1887 unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1888 return SkPackRGB16(r, g, b);
1889}
1890
1891struct Proc16Rec {
1892 SkXfermodeProc16 fProc16_0;
1893 SkXfermodeProc16 fProc16_255;
1894 SkXfermodeProc16 fProc16_General;
1895};
1896
reed@android.coma0f5d152009-06-22 17:38:10 +00001897static const Proc16Rec gModeProcs16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898 { NULL, NULL, NULL }, // CLEAR
1899 { NULL, src_modeproc16_255, NULL },
1900 { dst_modeproc16, dst_modeproc16, dst_modeproc16 },
1901 { srcover_modeproc16_0, srcover_modeproc16_255, NULL },
1902 { dstover_modeproc16_0, dstover_modeproc16_255, NULL },
1903 { NULL, srcin_modeproc16_255, NULL },
1904 { NULL, dstin_modeproc16_255, NULL },
1905 { NULL, NULL, NULL },// SRC_OUT
1906 { dstout_modeproc16_0, NULL, NULL },
1907 { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16 },
1908 { NULL, dstatop_modeproc16_255, NULL },
1909 { NULL, NULL, NULL }, // XOR
reed@android.coma0f5d152009-06-22 17:38:10 +00001910
1911 { NULL, NULL, NULL }, // plus
reed@google.com8d3cd7a2013-01-30 21:36:11 +00001912 { NULL, NULL, NULL }, // modulate
reed@android.coma0f5d152009-06-22 17:38:10 +00001913 { NULL, NULL, NULL }, // screen
1914 { NULL, NULL, NULL }, // overlay
1915 { darken_modeproc16_0, darken_modeproc16_255, NULL }, // darken
1916 { lighten_modeproc16_0, lighten_modeproc16_255, NULL }, // lighten
1917 { NULL, NULL, NULL }, // colordodge
1918 { NULL, NULL, NULL }, // colorburn
1919 { NULL, NULL, NULL }, // hardlight
1920 { NULL, NULL, NULL }, // softlight
1921 { NULL, NULL, NULL }, // difference
1922 { NULL, NULL, NULL }, // exclusion
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +00001923 { NULL, NULL, NULL }, // multiply
1924 { NULL, NULL, NULL }, // hue
1925 { NULL, NULL, NULL }, // saturation
1926 { NULL, NULL, NULL }, // color
1927 { NULL, NULL, NULL }, // luminosity
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928};
1929
reed@android.coma0f5d152009-06-22 17:38:10 +00001930SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001931 SkXfermodeProc16 proc16 = NULL;
reed@android.coma0f5d152009-06-22 17:38:10 +00001932 if ((unsigned)mode < kModeCount) {
1933 const Proc16Rec& rec = gModeProcs16[mode];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934 unsigned a = SkColorGetA(srcColor);
1935
1936 if (0 == a) {
1937 proc16 = rec.fProc16_0;
1938 } else if (255 == a) {
1939 proc16 = rec.fProc16_255;
1940 } else {
1941 proc16 = rec.fProc16_General;
1942 }
1943 }
1944 return proc16;
1945}
1946
caryclark@google.comd26147a2011-12-15 14:16:43 +00001947SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermode)
1948 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkProcCoeffXfermode)
1949 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkClearXfermode)
1950 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSrcXfermode)
1951 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstInXfermode)
1952 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstOutXfermode)
1953SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END