blob: 8c6eb2ca687f254c57f8ef6e19a8f72405f30c9e [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"
11#include "SkColorPriv.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000012#include "SkFlattenableBuffers.h"
reed@google.com4b163ed2012-08-07 21:35:13 +000013#include "SkMathPriv.h"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +000014#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015
robertphillips@google.com0456e0b2012-06-27 14:03:26 +000016SK_DEFINE_INST_COUNT(SkXfermode)
17
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#define SkAlphaMulAlpha(a, b) SkMulDiv255Round(a, b)
19
reed@android.comfc25abd2009-01-15 14:38:33 +000020#if 0
reed@android.com8a1c16f2008-12-17 15:59:43 +000021// idea for higher precision blends in xfer procs (and slightly faster)
22// see DstATop as a probable caller
23static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
24 SkASSERT(a <= 255);
25 SkASSERT(b <= 255);
26 SkASSERT(c <= 255);
27 SkASSERT(d <= 255);
28 unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
29 unsigned result = (prod + (prod >> 8)) >> 8;
30 SkASSERT(result <= 255);
31 return result;
32}
reed@android.comfc25abd2009-01-15 14:38:33 +000033#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000034
deanm@chromium.orgda946992009-07-03 12:54:24 +000035static inline unsigned saturated_add(unsigned a, unsigned b) {
reed@android.com543ed932009-04-24 12:43:40 +000036 SkASSERT(a <= 255);
37 SkASSERT(b <= 255);
38 unsigned sum = a + b;
39 if (sum > 255) {
40 sum = 255;
41 }
42 return sum;
43}
44
deanm@chromium.orgda946992009-07-03 12:54:24 +000045static inline int clamp_signed_byte(int n) {
reed@android.coma0f5d152009-06-22 17:38:10 +000046 if (n < 0) {
47 n = 0;
48 } else if (n > 255) {
49 n = 255;
50 }
51 return n;
52}
53
deanm@chromium.orgda946992009-07-03 12:54:24 +000054static inline int clamp_div255round(int prod) {
reed@android.coma0f5d152009-06-22 17:38:10 +000055 if (prod <= 0) {
56 return 0;
57 } else if (prod >= 255*255) {
58 return 255;
59 } else {
60 return SkDiv255Round(prod);
61 }
62}
63
reed@android.com8a1c16f2008-12-17 15:59:43 +000064///////////////////////////////////////////////////////////////////////////////
65
reed@android.com8a1c16f2008-12-17 15:59:43 +000066// kClear_Mode, //!< [0, 0]
67static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
68 return 0;
69}
70
71// kSrc_Mode, //!< [Sa, Sc]
72static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) {
73 return src;
74}
75
76// kDst_Mode, //!< [Da, Dc]
77static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) {
78 return dst;
79}
80
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000081// kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Sc + (1 - Sa)*Dc]
reed@android.com8a1c16f2008-12-17 15:59:43 +000082static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +000083#if 0
84 // this is the old, more-correct way, but it doesn't guarantee that dst==255
85 // will always stay opaque
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
reed@android.com1116fb22009-03-03 20:31:12 +000087#else
88 // this is slightly faster, but more importantly guarantees that dst==255
89 // will always stay opaque
90 return src + SkAlphaMulQ(dst, 256 - SkGetPackedA32(src));
91#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000092}
93
reed@android.com1116fb22009-03-03 20:31:12 +000094// kDstOver_Mode, //!< [Sa + Da - Sa*Da, Dc + (1 - Da)*Sc]
reed@android.com8a1c16f2008-12-17 15:59:43 +000095static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +000096 // this is the reverse of srcover, just flipping src and dst
97 // see srcover's comment about the 256 for opaqueness guarantees
98 return dst + SkAlphaMulQ(src, 256 - SkGetPackedA32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +000099}
100
101// kSrcIn_Mode, //!< [Sa * Da, Sc * Da]
102static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) {
103 return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst)));
104}
105
106// kDstIn_Mode, //!< [Sa * Da, Sa * Dc]
107static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) {
108 return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src)));
109}
110
111// kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)]
112static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) {
113 return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst)));
114}
115
116// kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)]
117static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) {
118 return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
119}
120
121// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc]
122static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) {
123 unsigned sa = SkGetPackedA32(src);
124 unsigned da = SkGetPackedA32(dst);
125 unsigned isa = 255 - sa;
126
127 return SkPackARGB32(da,
128 SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
129 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
130 SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
131 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
132 SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
133 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
134}
135
136// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
137static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) {
138 unsigned sa = SkGetPackedA32(src);
139 unsigned da = SkGetPackedA32(dst);
140 unsigned ida = 255 - da;
141
142 return SkPackARGB32(sa,
143 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
144 SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
145 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
146 SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
147 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
148 SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
149}
150
151// kXor_Mode [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
152static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) {
153 unsigned sa = SkGetPackedA32(src);
154 unsigned da = SkGetPackedA32(dst);
155 unsigned isa = 255 - sa;
156 unsigned ida = 255 - da;
157
158 return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
159 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
160 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
161 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
162 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
163 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
164 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
165}
166
reed@android.coma0f5d152009-06-22 17:38:10 +0000167///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
reed@android.coma0f5d152009-06-22 17:38:10 +0000169// kPlus_Mode
170static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000171 unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
deanm@chromium.orgda946992009-07-03 12:54:24 +0000172 unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
173 unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
174 unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
reed@android.coma0f5d152009-06-22 17:38:10 +0000175 return SkPackARGB32(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176}
177
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000178// kModulate_Mode
179static SkPMColor modulate_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
181 int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
182 int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
183 int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
184 return SkPackARGB32(a, r, g, b);
185}
186
reed@android.coma0f5d152009-06-22 17:38:10 +0000187static inline int srcover_byte(int a, int b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 return a + b - SkAlphaMulAlpha(a, b);
189}
reed@google.com25cfa692013-02-04 20:06:00 +0000190
191// kMultiply_Mode
192// B(Cb, Cs) = Cb x Cs
193// multiply uses its own version of blendfunc_byte because sa and da are not needed
194static int blendfunc_multiply_byte(int sc, int dc, int sa, int da) {
195 return clamp_div255round(sc * (255 - da) + dc * (255 - sa) + sc * dc);
196}
197
198static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
199 int sa = SkGetPackedA32(src);
200 int da = SkGetPackedA32(dst);
201 int a = srcover_byte(sa, da);
202 int r = blendfunc_multiply_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
203 int g = blendfunc_multiply_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
204 int b = blendfunc_multiply_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
205 return SkPackARGB32(a, r, g, b);
206}
207
208// kScreen_Mode
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000210 int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
211 int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
212 int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
213 int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 return SkPackARGB32(a, r, g, b);
215}
216
reed@android.coma0f5d152009-06-22 17:38:10 +0000217// kOverlay_Mode
218static inline int overlay_byte(int sc, int dc, int sa, int da) {
219 int tmp = sc * (255 - da) + dc * (255 - sa);
220 int rc;
221 if (2 * dc <= da) {
222 rc = 2 * sc * dc;
223 } else {
224 rc = sa * da - 2 * (da - dc) * (sa - sc);
225 }
226 return clamp_div255round(rc + tmp);
227}
228static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
229 int sa = SkGetPackedA32(src);
230 int da = SkGetPackedA32(dst);
231 int a = srcover_byte(sa, da);
232 int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
233 int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
234 int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
235 return SkPackARGB32(a, r, g, b);
236}
237
238// kDarken_Mode
239static inline int darken_byte(int sc, int dc, int sa, int da) {
240 int sd = sc * da;
241 int ds = dc * sa;
242 if (sd < ds) {
243 // srcover
244 return sc + dc - SkDiv255Round(ds);
245 } else {
246 // dstover
247 return dc + sc - SkDiv255Round(sd);
248 }
249}
250static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
251 int sa = SkGetPackedA32(src);
252 int da = SkGetPackedA32(dst);
253 int a = srcover_byte(sa, da);
254 int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
255 int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
256 int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
257 return SkPackARGB32(a, r, g, b);
258}
259
260// kLighten_Mode
261static inline int lighten_byte(int sc, int dc, int sa, int da) {
262 int sd = sc * da;
263 int ds = dc * sa;
264 if (sd > ds) {
265 // srcover
266 return sc + dc - SkDiv255Round(ds);
267 } else {
268 // dstover
269 return dc + sc - SkDiv255Round(sd);
270 }
271}
272static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
273 int sa = SkGetPackedA32(src);
274 int da = SkGetPackedA32(dst);
275 int a = srcover_byte(sa, da);
276 int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
277 int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
278 int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
279 return SkPackARGB32(a, r, g, b);
280}
281
282// kColorDodge_Mode
283static inline int colordodge_byte(int sc, int dc, int sa, int da) {
284 int diff = sa - sc;
285 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000286 if (0 == dc) {
287 return SkAlphaMulAlpha(sc, 255 - da);
288 } else if (0 == diff) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000289 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000290 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000291 diff = dc * sa / diff;
292 rc = sa * ((da < diff) ? da : diff) + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000293 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000294 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000295}
296static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000297 int sa = SkGetPackedA32(src);
298 int da = SkGetPackedA32(dst);
299 int a = srcover_byte(sa, da);
300 int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
301 int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
302 int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.coma0f5d152009-06-22 17:38:10 +0000303 return SkPackARGB32(a, r, g, b);
304}
305
306// kColorBurn_Mode
307static inline int colorburn_byte(int sc, int dc, int sa, int da) {
308 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000309 if (dc == da) {
310 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000311 } else if (0 == sc) {
312 return SkAlphaMulAlpha(dc, 255 - sa);
313 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000314 int tmp = (da - dc) * sa / sc;
315 rc = sa * (da - ((da < tmp) ? da : tmp))
316 + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000317 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000318 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000319}
320static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000321 int sa = SkGetPackedA32(src);
322 int da = SkGetPackedA32(dst);
323 int a = srcover_byte(sa, da);
324 int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
325 int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
326 int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
327 return SkPackARGB32(a, r, g, b);
328}
329
330// kHardLight_Mode
331static inline int hardlight_byte(int sc, int dc, int sa, int da) {
332 int rc;
333 if (2 * sc <= sa) {
334 rc = 2 * sc * dc;
335 } else {
336 rc = sa * da - 2 * (da - dc) * (sa - sc);
337 }
338 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
339}
340static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
341 int sa = SkGetPackedA32(src);
342 int da = SkGetPackedA32(dst);
343 int a = srcover_byte(sa, da);
344 int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
345 int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
346 int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
347 return SkPackARGB32(a, r, g, b);
348}
349
350// returns 255 * sqrt(n/255)
351static U8CPU sqrt_unit_byte(U8CPU n) {
352 return SkSqrtBits(n, 15+4);
353}
354
355// kSoftLight_Mode
356static inline int softlight_byte(int sc, int dc, int sa, int da) {
357 int m = da ? dc * 256 / da : 0;
358 int rc;
359 if (2 * sc <= sa) {
360 rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
361 } else if (4 * dc <= da) {
362 int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
363 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
364 } else {
365 int tmp = sqrt_unit_byte(m) - m;
366 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
367 }
368 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
369}
370static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
371 int sa = SkGetPackedA32(src);
372 int da = SkGetPackedA32(dst);
373 int a = srcover_byte(sa, da);
374 int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
375 int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
376 int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
377 return SkPackARGB32(a, r, g, b);
378}
379
380// kDifference_Mode
381static inline int difference_byte(int sc, int dc, int sa, int da) {
382 int tmp = SkMin32(sc * da, dc * sa);
383 return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
384}
385static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
386 int sa = SkGetPackedA32(src);
387 int da = SkGetPackedA32(dst);
388 int a = srcover_byte(sa, da);
389 int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
390 int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
391 int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
392 return SkPackARGB32(a, r, g, b);
393}
394
395// kExclusion_Mode
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000396static inline int exclusion_byte(int sc, int dc, int, int) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000397 // this equations is wacky, wait for SVG to confirm it
commit-bot@chromium.orge38e53b2013-07-15 12:20:36 +0000398 //int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
399
400 // The above equation can be simplified as follows
401 int r = 255*(sc + dc) - 2 * sc * dc;
reed@android.coma0f5d152009-06-22 17:38:10 +0000402 return clamp_div255round(r);
403}
404static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
405 int sa = SkGetPackedA32(src);
406 int da = SkGetPackedA32(dst);
407 int a = srcover_byte(sa, da);
408 int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
409 int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
410 int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.com543ed932009-04-24 12:43:40 +0000411 return SkPackARGB32(a, r, g, b);
412}
413
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000414// The CSS compositing spec introduces the following formulas:
415// (See https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnonseparable)
416// SkComputeLuminance is similar to this formula but it uses the new definition from Rec. 709
417// while PDF and CG uses the one from Rec. Rec. 601
418// See http://www.glennchan.info/articles/technical/hd-versus-sd-color-space/hd-versus-sd-color-space.htm
419static inline int Lum(int r, int g, int b)
420{
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000421 return SkDiv255Round(r * 77 + g * 150 + b * 28);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000422}
423
424static inline int min2(int a, int b) { return a < b ? a : b; }
425static inline int max2(int a, int b) { return a > b ? a : b; }
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000426#define minimum(a, b, c) min2(min2(a, b), c)
427#define maximum(a, b, c) max2(max2(a, b), c)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000428
429static inline int Sat(int r, int g, int b) {
430 return maximum(r, g, b) - minimum(r, g, b);
431}
432
433static inline void setSaturationComponents(int* Cmin, int* Cmid, int* Cmax, int s) {
reed@google.com3c1ea3a2013-03-07 15:31:58 +0000434 if(*Cmax > *Cmin) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000435 *Cmid = SkMulDiv(*Cmid - *Cmin, s, *Cmax - *Cmin);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000436 *Cmax = s;
437 } else {
438 *Cmax = 0;
439 *Cmid = 0;
440 }
441
442 *Cmin = 0;
443}
444
445static inline void SetSat(int* r, int* g, int* b, int s) {
446 if(*r <= *g) {
447 if(*g <= *b) {
448 setSaturationComponents(r, g, b, s);
449 } else if(*r <= *b) {
450 setSaturationComponents(r, b, g, s);
451 } else {
452 setSaturationComponents(b, r, g, s);
453 }
454 } else if(*r <= *b) {
455 setSaturationComponents(g, r, b, s);
456 } else if(*g <= *b) {
457 setSaturationComponents(g, b, r, s);
458 } else {
459 setSaturationComponents(b, g, r, s);
460 }
461}
462
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000463static inline void clipColor(int* r, int* g, int* b, int a) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000464 int L = Lum(*r, *g, *b);
465 int n = minimum(*r, *g, *b);
466 int x = maximum(*r, *g, *b);
467 if(n < 0) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000468 *r = L + SkMulDiv(*r - L, L, L - n);
469 *g = L + SkMulDiv(*g - L, L, L - n);
470 *b = L + SkMulDiv(*b - L, L, L - n);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000471 }
472
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000473 if (x > a) {
474 *r = L + SkMulDiv(*r - L, a - L, x - L);
475 *g = L + SkMulDiv(*g - L, a - L, x - L);
476 *b = L + SkMulDiv(*b - L, a - L, x - L);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000477 }
478}
479
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000480static inline void SetLum(int* r, int* g, int* b, int a, int l) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000481 int d = l - Lum(*r, *g, *b);
482 *r += d;
483 *g += d;
484 *b += d;
485
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000486 clipColor(r, g, b, a);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000487}
488
489// non-separable blend modes are done in non-premultiplied alpha
490#define blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000491 clamp_div255round(sc * (255 - da) + dc * (255 - sa) + blendval)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000492
493// kHue_Mode
494// B(Cb, Cs) = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
495// Create a color with the hue of the source color and the saturation and luminosity of the backdrop color.
496static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
497 int sr = SkGetPackedR32(src);
498 int sg = SkGetPackedG32(src);
499 int sb = SkGetPackedB32(src);
500 int sa = SkGetPackedA32(src);
501
502 int dr = SkGetPackedR32(dst);
503 int dg = SkGetPackedG32(dst);
504 int db = SkGetPackedB32(dst);
505 int da = SkGetPackedA32(dst);
506 int Sr, Sg, Sb;
507
508 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000509 Sr = sr * sa;
510 Sg = sg * sa;
511 Sb = sb * sa;
512 SetSat(&Sr, &Sg, &Sb, Sat(dr, dg, db) * sa);
513 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000514 } else {
515 Sr = 0;
516 Sg = 0;
517 Sb = 0;
518 }
519
520 int a = srcover_byte(sa, da);
521 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
522 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
523 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
524 return SkPackARGB32(a, r, g, b);
525}
526
527// kSaturation_Mode
528// B(Cb, Cs) = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000529// 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 +0000530static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
531 int sr = SkGetPackedR32(src);
532 int sg = SkGetPackedG32(src);
533 int sb = SkGetPackedB32(src);
534 int sa = SkGetPackedA32(src);
535
536 int dr = SkGetPackedR32(dst);
537 int dg = SkGetPackedG32(dst);
538 int db = SkGetPackedB32(dst);
539 int da = SkGetPackedA32(dst);
540 int Dr, Dg, Db;
541
542 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000543 Dr = dr * sa;
544 Dg = dg * sa;
545 Db = db * sa;
546 SetSat(&Dr, &Dg, &Db, Sat(sr, sg, sb) * da);
547 SetLum(&Dr, &Dg, &Db, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000548 } else {
549 Dr = 0;
550 Dg = 0;
551 Db = 0;
552 }
553
554 int a = srcover_byte(sa, da);
555 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
556 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
557 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
558 return SkPackARGB32(a, r, g, b);
559}
560
561// kColor_Mode
562// B(Cb, Cs) = SetLum(Cs, Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000563// 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 +0000564static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
565 int sr = SkGetPackedR32(src);
566 int sg = SkGetPackedG32(src);
567 int sb = SkGetPackedB32(src);
568 int sa = SkGetPackedA32(src);
569
570 int dr = SkGetPackedR32(dst);
571 int dg = SkGetPackedG32(dst);
572 int db = SkGetPackedB32(dst);
573 int da = SkGetPackedA32(dst);
574 int Sr, Sg, Sb;
575
576 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000577 Sr = sr * da;
578 Sg = sg * da;
579 Sb = sb * da;
580 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000581 } else {
582 Sr = 0;
583 Sg = 0;
584 Sb = 0;
585 }
586
587 int a = srcover_byte(sa, da);
588 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
589 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
590 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
591 return SkPackARGB32(a, r, g, b);
592}
593
594// kLuminosity_Mode
595// B(Cb, Cs) = SetLum(Cb, Lum(Cs))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000596// 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 +0000597static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
598 int sr = SkGetPackedR32(src);
599 int sg = SkGetPackedG32(src);
600 int sb = SkGetPackedB32(src);
601 int sa = SkGetPackedA32(src);
602
603 int dr = SkGetPackedR32(dst);
604 int dg = SkGetPackedG32(dst);
605 int db = SkGetPackedB32(dst);
606 int da = SkGetPackedA32(dst);
607 int Dr, Dg, Db;
608
609 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000610 Dr = dr * sa;
611 Dg = dg * sa;
612 Db = db * sa;
613 SetLum(&Dr, &Dg, &Db, sa * da, Lum(sr, sg, sb) * da);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000614 } else {
615 Dr = 0;
616 Dg = 0;
617 Db = 0;
618 }
619
620 int a = srcover_byte(sa, da);
621 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
622 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
623 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
624 return SkPackARGB32(a, r, g, b);
625}
626
djsollen@google.com6f980c62013-10-08 16:59:53 +0000627
628struct ProcCoeff {
629 SkXfermodeProc fProc;
630 SkXfermode::Coeff fSC;
631 SkXfermode::Coeff fDC;
632};
633
634#define CANNOT_USE_COEFF SkXfermode::Coeff(-1)
635
636static const ProcCoeff gProcCoeffs[] = {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000637 { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff },
638 { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff },
639 { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff },
640 { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff },
641 { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff },
642 { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff },
643 { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff },
644 { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff },
645 { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff },
646 { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff },
647 { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff },
648 { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff },
649
reed@google.com521e34e2011-04-12 18:55:21 +0000650 { plus_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kOne_Coeff },
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000651 { modulate_modeproc,SkXfermode::kZero_Coeff, SkXfermode::kSC_Coeff },
bsalomon@google.comb0091b82013-04-15 15:16:47 +0000652 { screen_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISC_Coeff },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000653 { overlay_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
654 { darken_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
655 { lighten_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
656 { colordodge_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
657 { colorburn_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
658 { hardlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
659 { softlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
660 { difference_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
661 { exclusion_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
reed@google.com25cfa692013-02-04 20:06:00 +0000662 { multiply_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000663 { hue_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
664 { saturation_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
665 { color_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
666 { luminosity_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000667};
668
669///////////////////////////////////////////////////////////////////////////////
670
reed@google.com30da7452012-12-17 19:55:24 +0000671bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000672 return false;
673}
674
reed@google.com30da7452012-12-17 19:55:24 +0000675bool SkXfermode::asMode(Mode* mode) const {
reed@google.comc0d4aa22011-04-13 21:12:04 +0000676 return false;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000677}
678
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000679bool SkXfermode::asNewEffectOrCoeff(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst, GrTexture*) const {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000680 return this->asCoeff(src, dst);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000681}
682
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000683bool SkXfermode::AsNewEffectOrCoeff(SkXfermode* xfermode,
684 GrContext* context,
685 GrEffectRef** effect,
686 Coeff* src,
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000687 Coeff* dst,
688 GrTexture* background) {
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000689 if (NULL == xfermode) {
690 return ModeAsCoeff(kSrcOver_Mode, src, dst);
691 } else {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000692 return xfermode->asNewEffectOrCoeff(context, effect, src, dst, background);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000693 }
694}
695
reed@google.com30da7452012-12-17 19:55:24 +0000696SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) const{
vandebo@chromium.org48543272011-02-08 19:28:07 +0000697 // no-op. subclasses should override this
698 return dst;
699}
700
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000701void SkXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
702 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000703 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000704 SkASSERT(dst && src && count >= 0);
705
706 if (NULL == aa) {
707 for (int i = count - 1; i >= 0; --i) {
708 dst[i] = this->xferColor(src[i], dst[i]);
709 }
710 } else {
711 for (int i = count - 1; i >= 0; --i) {
712 unsigned a = aa[i];
713 if (0 != a) {
714 SkPMColor dstC = dst[i];
715 SkPMColor C = this->xferColor(src[i], dstC);
716 if (0xFF != a) {
717 C = SkFourByteInterp(C, dstC, a);
718 }
719 dst[i] = C;
720 }
721 }
722 }
723}
724
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000725void SkXfermode::xfer16(uint16_t* dst,
726 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000727 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000728 SkASSERT(dst && src && count >= 0);
729
730 if (NULL == aa) {
731 for (int i = count - 1; i >= 0; --i) {
732 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
733 dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
734 }
735 } else {
736 for (int i = count - 1; i >= 0; --i) {
737 unsigned a = aa[i];
738 if (0 != a) {
739 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
740 SkPMColor C = this->xferColor(src[i], dstC);
741 if (0xFF != a) {
742 C = SkFourByteInterp(C, dstC, a);
743 }
744 dst[i] = SkPixel32ToPixel16_ToU16(C);
745 }
746 }
747 }
748}
749
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000750void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
vandebo@chromium.org48543272011-02-08 19:28:07 +0000751 const SkPMColor src[], int count,
reed@google.com30da7452012-12-17 19:55:24 +0000752 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000753 SkASSERT(dst && src && count >= 0);
754
755 if (NULL == aa) {
756 for (int i = count - 1; i >= 0; --i) {
757 SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
758 dst[i] = SkToU8(SkGetPackedA32(res));
759 }
760 } else {
761 for (int i = count - 1; i >= 0; --i) {
762 unsigned a = aa[i];
763 if (0 != a) {
764 SkAlpha dstA = dst[i];
765 unsigned A = SkGetPackedA32(this->xferColor(src[i],
766 (SkPMColor)(dstA << SK_A32_SHIFT)));
767 if (0xFF != a) {
768 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
769 }
770 dst[i] = SkToU8(A);
771 }
772 }
773 }
774}
775
776///////////////////////////////////////////////////////////////////////////////
777
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000778void SkProcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
779 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000780 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000781 SkASSERT(dst && src && count >= 0);
782
783 SkXfermodeProc proc = fProc;
784
785 if (NULL != proc) {
786 if (NULL == aa) {
787 for (int i = count - 1; i >= 0; --i) {
788 dst[i] = proc(src[i], dst[i]);
789 }
790 } else {
791 for (int i = count - 1; i >= 0; --i) {
792 unsigned a = aa[i];
793 if (0 != a) {
794 SkPMColor dstC = dst[i];
795 SkPMColor C = proc(src[i], dstC);
796 if (a != 0xFF) {
797 C = SkFourByteInterp(C, dstC, a);
798 }
799 dst[i] = C;
800 }
801 }
802 }
803 }
804}
805
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000806void SkProcXfermode::xfer16(uint16_t* SK_RESTRICT dst,
807 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000808 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000809 SkASSERT(dst && src && count >= 0);
810
811 SkXfermodeProc proc = fProc;
812
813 if (NULL != proc) {
814 if (NULL == aa) {
815 for (int i = count - 1; i >= 0; --i) {
816 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
817 dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
818 }
819 } else {
820 for (int i = count - 1; i >= 0; --i) {
821 unsigned a = aa[i];
822 if (0 != a) {
823 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
824 SkPMColor C = proc(src[i], dstC);
825 if (0xFF != a) {
826 C = SkFourByteInterp(C, dstC, a);
827 }
828 dst[i] = SkPixel32ToPixel16_ToU16(C);
829 }
830 }
831 }
832 }
833}
834
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000835void SkProcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
836 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000837 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000838 SkASSERT(dst && src && count >= 0);
839
840 SkXfermodeProc proc = fProc;
841
842 if (NULL != proc) {
843 if (NULL == aa) {
844 for (int i = count - 1; i >= 0; --i) {
845 SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
846 dst[i] = SkToU8(SkGetPackedA32(res));
847 }
848 } else {
849 for (int i = count - 1; i >= 0; --i) {
850 unsigned a = aa[i];
851 if (0 != a) {
852 SkAlpha dstA = dst[i];
853 SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
854 unsigned A = SkGetPackedA32(res);
855 if (0xFF != a) {
856 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
857 }
858 dst[i] = SkToU8(A);
859 }
860 }
861 }
862 }
863}
864
865SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
866 : SkXfermode(buffer) {
reed@google.com34342f62012-06-25 14:36:28 +0000867 fProc = NULL;
868 if (!buffer.isCrossProcess()) {
869 fProc = (SkXfermodeProc)buffer.readFunctionPtr();
870 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000871}
872
djsollen@google.com54924242012-03-29 15:18:04 +0000873void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
874 this->INHERITED::flatten(buffer);
reed@google.com34342f62012-06-25 14:36:28 +0000875 if (!buffer.isCrossProcess()) {
yangsu@google.comf468e472011-08-10 18:34:50 +0000876 buffer.writeFunctionPtr((void*)fProc);
877 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000878}
879
robertphillips@google.comb83b6b42013-01-22 14:32:09 +0000880#ifdef SK_DEVELOPER
881void SkProcXfermode::toString(SkString* str) const {
882 str->appendf("SkProcXfermode: %p", fProc);
883}
884#endif
885
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000886//////////////////////////////////////////////////////////////////////////////
887
888#if SK_SUPPORT_GPU
889
890#include "GrEffect.h"
bsalomon@google.com77af6802013-10-02 13:04:56 +0000891#include "GrCoordTransform.h"
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000892#include "GrEffectUnitTest.h"
893#include "GrTBackendEffectFactory.h"
894#include "gl/GrGLEffect.h"
895
896/**
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000897 * GrEffect that implements the all the separable xfer modes that cannot be expressed as Coeffs.
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000898 */
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000899class XferEffect : public GrEffect {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000900public:
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000901 static bool IsSupportedMode(SkXfermode::Mode mode) {
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000902 return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000903 }
904
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000905 static GrEffectRef* Create(SkXfermode::Mode mode, GrTexture* background) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000906 if (!IsSupportedMode(mode)) {
907 return NULL;
908 } else {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000909 AutoEffectUnref effect(SkNEW_ARGS(XferEffect, (mode, background)));
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000910 return CreateEffectRef(effect);
911 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000912 }
913
914 virtual void getConstantColorComponents(GrColor* color,
915 uint32_t* validFlags) const SK_OVERRIDE {
916 *validFlags = 0;
917 }
918
919 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000920 return GrTBackendEffectFactory<XferEffect>::getInstance();
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000921 }
922
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000923 static const char* Name() { return "XferEffect"; }
924
925 SkXfermode::Mode mode() const { return fMode; }
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000926 const GrTextureAccess& backgroundAccess() const { return fBackgroundAccess; }
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000927
928 class GLEffect : public GrGLEffect {
929 public:
930 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
bsalomon@google.com77af6802013-10-02 13:04:56 +0000931 : GrGLEffect(factory) {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000932 }
933 virtual void emitCode(GrGLShaderBuilder* builder,
934 const GrDrawEffect& drawEffect,
935 EffectKey key,
936 const char* outputColor,
937 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000938 const TransformedCoordsArray& coords,
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000939 const TextureSamplerArray& samplers) SK_OVERRIDE {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000940 SkXfermode::Mode mode = drawEffect.castEffect<XferEffect>().mode();
941 const GrTexture* backgroundTex = drawEffect.castEffect<XferEffect>().backgroundAccess().getTexture();
942 const char* dstColor;
943 if (backgroundTex) {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000944 dstColor = "bgColor";
945 builder->fsCodeAppendf("\t\tvec4 %s = ", dstColor);
bsalomon@google.com77af6802013-10-02 13:04:56 +0000946 builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000947 builder->fsCodeAppendf(";\n");
948 } else {
949 dstColor = builder->dstColor();
950 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000951 SkASSERT(NULL != dstColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000952
953 // We don't try to optimize for this case at all
bsalomon@google.comb79d8652013-03-29 20:30:50 +0000954 if (NULL == inputColor) {
commit-bot@chromium.orgdd72fde2013-04-29 15:25:03 +0000955 builder->fsCodeAppendf("\t\tconst vec4 ones = %s;\n", GrGLSLOnesVecf(4));
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000956 inputColor = "ones";
957 }
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000958 builder->fsCodeAppendf("\t\t// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
959
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000960 // These all perform src-over on the alpha channel.
961 builder->fsCodeAppendf("\t\t%s.a = %s.a + (1.0 - %s.a) * %s.a;\n",
962 outputColor, inputColor, inputColor, dstColor);
963
bsalomon@google.com77cf4602013-04-22 21:05:48 +0000964 switch (mode) {
bsalomon@google.com8da9bc72013-04-19 15:03:21 +0000965 case SkXfermode::kOverlay_Mode:
966 // Overlay is Hard-Light with the src and dst reversed
967 HardLight(builder, outputColor, dstColor, inputColor);
968 break;
969 case SkXfermode::kDarken_Mode:
970 builder->fsCodeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, "
971 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
972 outputColor,
973 inputColor, dstColor, inputColor,
974 dstColor, inputColor, dstColor);
975 break;
976 case SkXfermode::kLighten_Mode:
977 builder->fsCodeAppendf("\t\t%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, "
978 "(1.0 - %s.a) * %s.rgb + %s.rgb);\n",
979 outputColor,
980 inputColor, dstColor, inputColor,
981 dstColor, inputColor, dstColor);
982 break;
983 case SkXfermode::kColorDodge_Mode:
984 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'r');
985 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'g');
986 ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'b');
987 break;
988 case SkXfermode::kColorBurn_Mode:
989 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'r');
990 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'g');
991 ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'b');
992 break;
993 case SkXfermode::kHardLight_Mode:
994 HardLight(builder, outputColor, inputColor, dstColor);
995 break;
996 case SkXfermode::kSoftLight_Mode:
997 builder->fsCodeAppendf("\t\tif (0.0 == %s.a) {\n", dstColor);
998 builder->fsCodeAppendf("\t\t\t%s.rgba = %s;\n", outputColor, inputColor);
999 builder->fsCodeAppendf("\t\t} else {\n");
1000 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'r');
1001 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'g');
1002 SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'b');
1003 builder->fsCodeAppendf("\t\t}\n");
1004 break;
1005 case SkXfermode::kDifference_Mode:
1006 builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb -"
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +00001007 "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001008 outputColor, inputColor, dstColor, inputColor, dstColor,
1009 dstColor, inputColor);
1010 break;
1011 case SkXfermode::kExclusion_Mode:
1012 builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb - "
1013 "2.0 * %s.rgb * %s.rgb;\n",
1014 outputColor, dstColor, inputColor, dstColor, inputColor);
1015 break;
1016 case SkXfermode::kMultiply_Mode:
1017 builder->fsCodeAppendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + "
1018 "(1.0 - %s.a) * %s.rgb + "
1019 "%s.rgb * %s.rgb;\n",
1020 outputColor, inputColor, dstColor, dstColor, inputColor,
1021 inputColor, dstColor);
1022 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001023 case SkXfermode::kHue_Mode: {
1024 // SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S
1025 SkString setSat, setLum;
1026 AddSatFunction(builder, &setSat);
1027 AddLumFunction(builder, &setLum);
1028 builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
1029 dstColor, inputColor);
1030 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
1031 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
1032 dstColor);
1033 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1034 outputColor, inputColor, dstColor, dstColor, inputColor);
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001035 break;
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001036 }
1037 case SkXfermode::kSaturation_Mode: {
1038 // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S
1039 SkString setSat, setLum;
1040 AddSatFunction(builder, &setSat);
1041 AddLumFunction(builder, &setLum);
1042 builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n",
1043 dstColor, inputColor);
1044 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a), dstSrcAlpha.a, dstSrcAlpha.rgb);\n",
1045 outputColor, setLum.c_str(), setSat.c_str(), inputColor,
1046 dstColor);
1047 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1048 outputColor, inputColor, dstColor, dstColor, inputColor);
1049 break;
1050 }
1051 case SkXfermode::kColor_Mode: {
1052 // SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S
1053 SkString setLum;
1054 AddLumFunction(builder, &setLum);
1055 builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
1056 inputColor, dstColor);
1057 builder->fsCodeAppendf("\t\t%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);\n",
1058 outputColor, setLum.c_str(), dstColor, inputColor);
1059 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1060 outputColor, inputColor, dstColor, dstColor, inputColor);
1061 break;
1062 }
1063 case SkXfermode::kLuminosity_Mode: {
1064 // SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S
1065 SkString setLum;
1066 AddLumFunction(builder, &setLum);
1067 builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n",
1068 inputColor, dstColor);
1069 builder->fsCodeAppendf("\t\t%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);\n",
1070 outputColor, setLum.c_str(), dstColor, inputColor);
1071 builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n",
1072 outputColor, inputColor, dstColor, dstColor, inputColor);
1073 break;
1074 }
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001075 default:
1076 GrCrash("Unknown XferEffect mode.");
1077 break;
bsalomon@google.comb79d8652013-03-29 20:30:50 +00001078 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001079 }
1080
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001081 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com77af6802013-10-02 13:04:56 +00001082 return drawEffect.castEffect<XferEffect>().mode();
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001083 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001084
1085 private:
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001086 static void HardLight(GrGLShaderBuilder* builder,
1087 const char* final,
1088 const char* src,
1089 const char* dst) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +00001090 static const char kComponents[] = {'r', 'g', 'b'};
1091 for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) {
1092 char component = kComponents[i];
1093 builder->fsCodeAppendf("\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
1094 builder->fsCodeAppendf("\t\t\t%s.%c = 2.0 * %s.%c * %s.%c;\n", final, component, src, component, dst, component);
1095 builder->fsCodeAppend("\t\t} else {\n");
1096 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s.a - %s.%c);\n",
1097 final, component, src, dst, dst, dst, component, src, src, component);
1098 builder->fsCodeAppend("\t\t}\n");
1099 }
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001100 builder->fsCodeAppendf("\t\t%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);\n",
1101 final, src, dst, dst, src);
1102 }
1103
1104 // Does one component of color-dodge
1105 static void ColorDodgeComponent(GrGLShaderBuilder* builder,
1106 const char* final,
1107 const char* src,
1108 const char* dst,
1109 const char component) {
1110 builder->fsCodeAppendf("\t\tif (0.0 == %s.%c) {\n", dst, component);
bsalomon@google.com58eb1af2013-04-19 16:20:20 +00001111 builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001112 final, component, src, component, dst);
1113 builder->fsCodeAppend("\t\t} else {\n");
1114 builder->fsCodeAppendf("\t\t\tfloat d = %s.a - %s.%c;\n", src, src, component);
bsalomon@google.com07fa3ab2013-04-19 17:21:49 +00001115 builder->fsCodeAppend("\t\t\tif (0.0 == d) {\n");
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001116 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",
1117 final, component, src, dst, src, component, dst, dst, component,
1118 src);
1119 builder->fsCodeAppend("\t\t\t} else {\n");
1120 builder->fsCodeAppendf("\t\t\t\td = min(%s.a, %s.%c * %s.a / d);\n",
1121 dst, dst, component, src);
1122 builder->fsCodeAppendf("\t\t\t\t%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1123 final, component, src, src, component, dst, dst, component, src);
1124 builder->fsCodeAppend("\t\t\t}\n");
1125 builder->fsCodeAppend("\t\t}\n");
1126 }
1127
1128 // Does one component of color-burn
1129 static void ColorBurnComponent(GrGLShaderBuilder* builder,
1130 const char* final,
1131 const char* src,
1132 const char* dst,
1133 const char component) {
1134 builder->fsCodeAppendf("\t\tif (%s.a == %s.%c) {\n", dst, dst, component);
1135 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1136 final, component, src, dst, src, component, dst, dst, component,
1137 src);
1138 builder->fsCodeAppendf("\t\t} else if (0.0 == %s.%c) {\n", src, component);
1139 builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n",
1140 final, component, dst, component, src);
1141 builder->fsCodeAppend("\t\t} else {\n");
1142 builder->fsCodeAppendf("\t\t\tfloat d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);\n",
1143 dst, dst, dst, component, src, src, component);
1144 builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n",
1145 final, component, src, src, component, dst, dst, component, src);
1146 builder->fsCodeAppend("\t\t}\n");
1147 }
1148
1149 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
1150 static void SoftLightComponentPosDstAlpha(GrGLShaderBuilder* builder,
1151 const char* final,
1152 const char* src,
1153 const char* dst,
1154 const char component) {
1155 // if (2S < Sa)
1156 builder->fsCodeAppendf("\t\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src);
1157 // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1)
bsalomon@google.com68567792013-04-19 18:10:50 +00001158 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 +00001159 final, component, dst, component, dst, component, src, src,
1160 component, dst, dst, src, component, dst, component, src, src,
1161 component);
1162 // else if (4D < Da)
1163 builder->fsCodeAppendf("\t\t\t} else if (4.0 * %s.%c <= %s.a) {\n",
1164 dst, component, dst);
1165 builder->fsCodeAppendf("\t\t\t\tfloat DSqd = %s.%c * %s.%c;\n",
1166 dst, component, dst, component);
1167 builder->fsCodeAppendf("\t\t\t\tfloat DCub = DSqd * %s.%c;\n", dst, component);
1168 builder->fsCodeAppendf("\t\t\t\tfloat DaSqd = %s.a * %s.a;\n", dst, dst);
1169 builder->fsCodeAppendf("\t\t\t\tfloat DaCub = DaSqd * %s.a;\n", dst);
1170 // (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 +00001171 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 +00001172 final, component, src, component, src, component, dst, component,
1173 src, src, component, dst, src, src, component, src, src,
1174 component);
1175 builder->fsCodeAppendf("\t\t\t} else {\n");
1176 // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S
bsalomon@google.com68567792013-04-19 18:10:50 +00001177 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 +00001178 final, component, dst, dst, component, src, src, component, dst,
1179 src, component, dst, component, src, src, component, src,
1180 component);
1181 builder->fsCodeAppendf("\t\t\t}\n");
1182 }
1183
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001184 // Adds a function that takes two colors and an alpha as input. It produces a color with the
1185 // hue and saturation of the first color, the luminosity of the second color, and the input
1186 // alpha. It has this signature:
1187 // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
1188 static void AddLumFunction(GrGLShaderBuilder* builder, SkString* setLumFunction) {
1189 // Emit a helper that gets the luminance of a color.
1190 SkString getFunction;
1191 GrGLShaderVar getLumArgs[] = {
1192 GrGLShaderVar("color", kVec3f_GrSLType),
1193 };
1194 SkString getLumBody("\treturn dot(vec3(0.3, 0.59, 0.11), color);\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001195 builder->fsEmitFunction(kFloat_GrSLType,
1196 "luminance",
1197 SK_ARRAY_COUNT(getLumArgs), getLumArgs,
1198 getLumBody.c_str(),
1199 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001200
1201 // Emit the set luminance function.
1202 GrGLShaderVar setLumArgs[] = {
1203 GrGLShaderVar("hueSat", kVec3f_GrSLType),
1204 GrGLShaderVar("alpha", kFloat_GrSLType),
1205 GrGLShaderVar("lumColor", kVec3f_GrSLType),
1206 };
1207 SkString setLumBody;
1208 setLumBody.printf("\tfloat diff = %s(lumColor - hueSat);\n", getFunction.c_str());
1209 setLumBody.append("\tvec3 outColor = hueSat + diff;\n");
1210 setLumBody.appendf("\tfloat outLum = %s(outColor);\n", getFunction.c_str());
1211 setLumBody.append("\tfloat minComp = min(min(outColor.r, outColor.g), outColor.b);\n"
1212 "\tfloat maxComp = max(max(outColor.r, outColor.g), outColor.b);\n"
1213 "\tif (minComp < 0.0) {\n"
1214 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / (outLum - minComp);\n"
1215 "\t}\n"
1216 "\tif (maxComp > alpha) {\n"
1217 "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) / (maxComp - outLum);\n"
1218 "\t}\n"
1219 "\treturn outColor;\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001220 builder->fsEmitFunction(kVec3f_GrSLType,
1221 "set_luminance",
1222 SK_ARRAY_COUNT(setLumArgs), setLumArgs,
1223 setLumBody.c_str(),
1224 setLumFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001225 }
1226
1227 // Adds a function that creates a color with the hue and luminosity of one input color and
1228 // the saturation of another color. It will have this signature:
1229 // float set_saturation(vec3 hueLumColor, vec3 satColor)
1230 static void AddSatFunction(GrGLShaderBuilder* builder, SkString* setSatFunction) {
1231 // Emit a helper that gets the saturation of a color
1232 SkString getFunction;
1233 GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
1234 SkString getSatBody;
1235 getSatBody.printf("\treturn max(max(color.r, color.g), color.b) - "
1236 "min(min(color.r, color.g), color.b);\n");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001237 builder->fsEmitFunction(kFloat_GrSLType,
1238 "saturation",
1239 SK_ARRAY_COUNT(getSatArgs), getSatArgs,
1240 getSatBody.c_str(),
1241 &getFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001242
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001243 // Emit a helper that sets the saturation given sorted input channels. This used
1244 // to use inout params for min, mid, and max components but that seems to cause
1245 // problems on PowerVR drivers. So instead it returns a vec3 where r, g ,b are the
1246 // adjusted min, mid, and max inputs, respectively.
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001247 SkString helperFunction;
1248 GrGLShaderVar helperArgs[] = {
1249 GrGLShaderVar("minComp", kFloat_GrSLType),
1250 GrGLShaderVar("midComp", kFloat_GrSLType),
1251 GrGLShaderVar("maxComp", kFloat_GrSLType),
1252 GrGLShaderVar("sat", kFloat_GrSLType),
1253 };
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001254 static const char kHelperBody[] = "\tif (minComp < maxComp) {\n"
1255 "\t\tvec3 result;\n"
1256 "\t\tresult.r = 0.0;\n"
1257 "\t\tresult.g = sat * (midComp - minComp) / (maxComp - minComp);\n"
1258 "\t\tresult.b = sat;\n"
1259 "\t\treturn result;\n"
1260 "\t} else {\n"
1261 "\t\treturn vec3(0, 0, 0);\n"
1262 "\t}\n";
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001263 builder->fsEmitFunction(kVec3f_GrSLType,
1264 "set_saturation_helper",
1265 SK_ARRAY_COUNT(helperArgs), helperArgs,
1266 kHelperBody,
1267 &helperFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001268
1269 GrGLShaderVar setSatArgs[] = {
1270 GrGLShaderVar("hueLumColor", kVec3f_GrSLType),
1271 GrGLShaderVar("satColor", kVec3f_GrSLType),
1272 };
1273 const char* helpFunc = helperFunction.c_str();
1274 SkString setSatBody;
1275 setSatBody.appendf("\tfloat sat = %s(satColor);\n"
1276 "\tif (hueLumColor.r <= hueLumColor.g) {\n"
1277 "\t\tif (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001278 "\t\t\thueLumColor.rgb = %s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001279 "\t\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001280 "\t\t\thueLumColor.rbg = %s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);\n"
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001281 "\t\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001282 "\t\t\thueLumColor.brg = %s(hueLumColor.b, hueLumColor.r, hueLumColor.g, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001283 "\t\t}\n"
1284 "\t} else if (hueLumColor.r <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001285 "\t\thueLumColor.grb = %s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001286 "\t} else if (hueLumColor.g <= hueLumColor.b) {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001287 "\t\thueLumColor.gbr = %s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001288 "\t} else {\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001289 "\t\thueLumColor.bgr = %s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);\n"
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001290 "\t}\n"
commit-bot@chromium.orgc718b352013-04-29 17:36:14 +00001291 "\treturn hueLumColor;\n",
commit-bot@chromium.org18c41ac2013-04-25 00:40:41 +00001292 getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,
1293 helpFunc, helpFunc);
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +00001294 builder->fsEmitFunction(kVec3f_GrSLType,
1295 "set_saturation",
1296 SK_ARRAY_COUNT(setSatArgs), setSatArgs,
1297 setSatBody.c_str(),
1298 setSatFunction);
bsalomon@google.com77cf4602013-04-22 21:05:48 +00001299
1300 }
1301
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001302 typedef GrGLEffect INHERITED;
1303 };
1304
1305 GR_DECLARE_EFFECT_TEST;
1306
1307private:
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001308 XferEffect(SkXfermode::Mode mode, GrTexture* background)
1309 : fMode(mode) {
1310 if (background) {
bsalomon@google.com77af6802013-10-02 13:04:56 +00001311 fBackgroundTransform.reset(kLocal_GrCoordSet, background);
1312 this->addCoordTransform(&fBackgroundTransform);
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001313 fBackgroundAccess.reset(background);
1314 this->addTextureAccess(&fBackgroundAccess);
1315 } else {
1316 this->setWillReadDstColor();
1317 }
1318 }
1319 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
1320 const XferEffect& s = CastEffect<XferEffect>(other);
1321 return fMode == s.fMode &&
1322 fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture();
1323 }
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +00001324
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001325 SkXfermode::Mode fMode;
bsalomon@google.com77af6802013-10-02 13:04:56 +00001326 GrCoordTransform fBackgroundTransform;
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001327 GrTextureAccess fBackgroundAccess;
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001328
1329 typedef GrEffect INHERITED;
1330};
1331
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001332GR_DEFINE_EFFECT_TEST(XferEffect);
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +00001333GrEffectRef* XferEffect::TestCreate(SkRandom* rand,
bsalomon@google.com8da9bc72013-04-19 15:03:21 +00001334 GrContext*,
1335 const GrDrawTargetCaps&,
1336 GrTexture*[]) {
commit-bot@chromium.orga0d91382013-04-29 17:40:33 +00001337 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001338
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001339 static AutoEffectUnref gEffect(SkNEW_ARGS(XferEffect, (static_cast<SkXfermode::Mode>(mode), NULL)));
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001340 return CreateEffectRef(gEffect);
1341}
1342
1343#endif
1344
vandebo@chromium.org48543272011-02-08 19:28:07 +00001345///////////////////////////////////////////////////////////////////////////////
1346///////////////////////////////////////////////////////////////////////////////
1347
djsollen@google.com6f980c62013-10-08 16:59:53 +00001348class SkProcCoeffXfermode : public SkProcXfermode {
1349public:
1350 SkProcCoeffXfermode(const ProcCoeff& rec, Mode mode)
1351 : INHERITED(rec.fProc) {
1352 fMode = mode;
1353 // these may be valid, or may be CANNOT_USE_COEFF
1354 fSrcCoeff = rec.fSC;
1355 fDstCoeff = rec.fDC;
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001356 }
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001357
djsollen@google.com6f980c62013-10-08 16:59:53 +00001358 virtual bool asMode(Mode* mode) const SK_OVERRIDE {
1359 if (mode) {
1360 *mode = fMode;
commit-bot@chromium.org84cc1eb2013-10-08 16:47:22 +00001361 }
1362 return true;
1363 }
djsollen@google.com6f980c62013-10-08 16:59:53 +00001364
1365 virtual bool asCoeff(Coeff* sc, Coeff* dc) const SK_OVERRIDE {
1366 if (CANNOT_USE_COEFF == fSrcCoeff) {
1367 return false;
1368 }
1369
1370 if (sc) {
1371 *sc = fSrcCoeff;
1372 }
1373 if (dc) {
1374 *dc = fDstCoeff;
1375 }
1376 return true;
1377 }
1378
1379#if SK_SUPPORT_GPU
1380 virtual bool asNewEffectOrCoeff(GrContext*,
1381 GrEffectRef** effect,
1382 Coeff* src,
1383 Coeff* dst,
1384 GrTexture* background) const SK_OVERRIDE {
1385 if (this->asCoeff(src, dst)) {
1386 return true;
1387 }
1388 if (XferEffect::IsSupportedMode(fMode)) {
1389 if (NULL != effect) {
1390 *effect = XferEffect::Create(fMode, background);
1391 SkASSERT(NULL != *effect);
1392 }
1393 return true;
1394 }
1395 return false;
1396 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001397#endif
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001398
djsollen@google.com6f980c62013-10-08 16:59:53 +00001399 SK_DEVELOPER_TO_STRING()
1400 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcCoeffXfermode)
1401
1402protected:
1403 SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
1404 fMode = (SkXfermode::Mode)buffer.read32();
1405
1406 const ProcCoeff& rec = gProcCoeffs[fMode];
1407 // these may be valid, or may be CANNOT_USE_COEFF
1408 fSrcCoeff = rec.fSC;
1409 fDstCoeff = rec.fDC;
1410 // now update our function-ptr in the super class
1411 this->INHERITED::setProc(rec.fProc);
1412 }
1413
1414 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
1415 this->INHERITED::flatten(buffer);
1416 buffer.write32(fMode);
1417 }
1418
1419private:
1420 Mode fMode;
1421 Coeff fSrcCoeff, fDstCoeff;
1422
1423 typedef SkProcXfermode INHERITED;
1424};
vandebo@chromium.org48543272011-02-08 19:28:07 +00001425
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001426const char* SkXfermode::ModeName(Mode mode) {
1427 SkASSERT((unsigned) mode <= (unsigned)kLastMode);
1428 const char* gModeStrings[] = {
1429 "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn",
1430 "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Plus",
1431 "Modulate", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge",
1432 "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion",
1433 "Multiply", "Hue", "Saturation", "Color", "Luminosity"
1434 };
1435 return gModeStrings[mode];
1436 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gModeStrings) == kLastMode + 1, mode_count);
1437}
1438
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001439#ifdef SK_DEVELOPER
1440void SkProcCoeffXfermode::toString(SkString* str) const {
1441 str->append("SkProcCoeffXfermode: ");
1442
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001443 str->append("mode: ");
commit-bot@chromium.orgd7aaf602013-04-01 12:51:34 +00001444 str->append(ModeName(fMode));
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001445
1446 static const char* gCoeffStrings[kCoeffCount] = {
skia.committer@gmail.com98ded842013-01-23 07:06:17 +00001447 "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001448 };
1449
1450 str->append(" src: ");
1451 if (CANNOT_USE_COEFF == fSrcCoeff) {
1452 str->append("can't use");
1453 } else {
1454 str->append(gCoeffStrings[fSrcCoeff]);
1455 }
1456
1457 str->append(" dst: ");
1458 if (CANNOT_USE_COEFF == fDstCoeff) {
1459 str->append("can't use");
1460 } else {
1461 str->append(gCoeffStrings[fDstCoeff]);
1462 }
1463}
1464#endif
1465
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466///////////////////////////////////////////////////////////////////////////////
1467
1468class SkClearXfermode : public SkProcCoeffXfermode {
1469public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001470 SkClearXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kClear_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471
reed@google.com30da7452012-12-17 19:55:24 +00001472 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1473 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001475 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001476 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkClearXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001477
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478private:
1479 SkClearXfermode(SkFlattenableReadBuffer& buffer)
1480 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001481
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001482 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483};
1484
reed@google.com86ab6c62011-11-28 15:26:14 +00001485void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1486 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001487 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001488 SkASSERT(dst && count >= 0);
1489
1490 if (NULL == aa) {
1491 memset(dst, 0, count << 2);
1492 } else {
1493 for (int i = count - 1; i >= 0; --i) {
1494 unsigned a = aa[i];
1495 if (0xFF == a) {
1496 dst[i] = 0;
1497 } else if (a != 0) {
1498 dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
1499 }
1500 }
1501 }
1502}
1503void SkClearXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1504 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001505 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001506 SkASSERT(dst && count >= 0);
1507
1508 if (NULL == aa) {
1509 memset(dst, 0, count);
1510 } else {
1511 for (int i = count - 1; i >= 0; --i) {
1512 unsigned a = aa[i];
1513 if (0xFF == a) {
1514 dst[i] = 0;
1515 } else if (0 != a) {
1516 dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
1517 }
1518 }
1519 }
1520}
1521
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001522#ifdef SK_DEVELOPER
1523void SkClearXfermode::toString(SkString* str) const {
1524 this->INHERITED::toString(str);
1525}
1526#endif
1527
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528///////////////////////////////////////////////////////////////////////////////
1529
1530class SkSrcXfermode : public SkProcCoeffXfermode {
1531public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001532 SkSrcXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kSrc_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533
reed@google.com30da7452012-12-17 19:55:24 +00001534 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1535 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001537 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001538 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSrcXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540private:
1541 SkSrcXfermode(SkFlattenableReadBuffer& buffer)
1542 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001543
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001544 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545};
1546
reed@google.com86ab6c62011-11-28 15:26:14 +00001547void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1548 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001549 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001550 SkASSERT(dst && src && count >= 0);
1551
1552 if (NULL == aa) {
1553 memcpy(dst, src, count << 2);
1554 } else {
1555 for (int i = count - 1; i >= 0; --i) {
1556 unsigned a = aa[i];
1557 if (a == 0xFF) {
1558 dst[i] = src[i];
1559 } else if (a != 0) {
1560 dst[i] = SkFourByteInterp(src[i], dst[i], a);
1561 }
1562 }
1563 }
1564}
1565
1566void SkSrcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1567 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001568 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001569 SkASSERT(dst && src && count >= 0);
1570
1571 if (NULL == aa) {
1572 for (int i = count - 1; i >= 0; --i) {
1573 dst[i] = SkToU8(SkGetPackedA32(src[i]));
1574 }
1575 } else {
1576 for (int i = count - 1; i >= 0; --i) {
1577 unsigned a = aa[i];
1578 if (0 != a) {
1579 unsigned srcA = SkGetPackedA32(src[i]);
1580 if (a == 0xFF) {
1581 dst[i] = SkToU8(srcA);
1582 } else {
1583 dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a));
1584 }
1585 }
1586 }
1587 }
1588}
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001589#ifdef SK_DEVELOPER
1590void SkSrcXfermode::toString(SkString* str) const {
1591 this->INHERITED::toString(str);
1592}
1593#endif
reed@google.com86ab6c62011-11-28 15:26:14 +00001594
reed@google.com30da7452012-12-17 19:55:24 +00001595///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001596
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597class SkDstInXfermode : public SkProcCoeffXfermode {
1598public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001599 SkDstInXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstIn_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001600
reed@google.com30da7452012-12-17 19:55:24 +00001601 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001602
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001603 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001604 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstInXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001605
1606private:
1607 SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
1608
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609 typedef SkProcCoeffXfermode INHERITED;
1610};
1611
reed@google.com86ab6c62011-11-28 15:26:14 +00001612void SkDstInXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1613 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001614 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001615 SkASSERT(dst && src);
1616
1617 if (count <= 0) {
1618 return;
1619 }
1620 if (NULL != aa) {
1621 return this->INHERITED::xfer32(dst, src, count, aa);
1622 }
1623
1624 do {
1625 unsigned a = SkGetPackedA32(*src);
1626 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
1627 dst++;
1628 src++;
1629 } while (--count != 0);
1630}
1631
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001632#ifdef SK_DEVELOPER
1633void SkDstInXfermode::toString(SkString* str) const {
1634 this->INHERITED::toString(str);
1635}
1636#endif
1637
reed@google.com30da7452012-12-17 19:55:24 +00001638///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001639
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640class SkDstOutXfermode : public SkProcCoeffXfermode {
1641public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001642 SkDstOutXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstOut_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001643
reed@google.com30da7452012-12-17 19:55:24 +00001644 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001645
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001646 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001647 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstOutXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001648
1649private:
1650 SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
1651 : INHERITED(buffer) {}
1652
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653 typedef SkProcCoeffXfermode INHERITED;
1654};
1655
reed@google.com86ab6c62011-11-28 15:26:14 +00001656void SkDstOutXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1657 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001658 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001659 SkASSERT(dst && src);
1660
1661 if (count <= 0) {
1662 return;
1663 }
1664 if (NULL != aa) {
1665 return this->INHERITED::xfer32(dst, src, count, aa);
1666 }
1667
1668 do {
1669 unsigned a = SkGetPackedA32(*src);
1670 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
1671 dst++;
1672 src++;
1673 } while (--count != 0);
1674}
1675
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001676#ifdef SK_DEVELOPER
1677void SkDstOutXfermode::toString(SkString* str) const {
1678 this->INHERITED::toString(str);
1679}
1680#endif
1681
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682///////////////////////////////////////////////////////////////////////////////
1683
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001684SK_DECLARE_STATIC_MUTEX(gCachedXfermodesMutex);
1685static SkXfermode* gCachedXfermodes[SkXfermode::kLastMode + 1];
1686
1687void SkXfermode::Term() {
1688 SkAutoMutexAcquire ac(gCachedXfermodesMutex);
1689
1690 for (size_t i = 0; i < SK_ARRAY_COUNT(gCachedXfermodes); ++i) {
1691 SkSafeUnref(gCachedXfermodes[i]);
1692 gCachedXfermodes[i] = NULL;
1693 }
1694}
1695
reed@android.coma0f5d152009-06-22 17:38:10 +00001696SkXfermode* SkXfermode::Create(Mode mode) {
1697 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001698 SkASSERT(SK_ARRAY_COUNT(gCachedXfermodes) == kModeCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001700 if ((unsigned)mode >= kModeCount) {
1701 // report error
1702 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703 }
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001704
1705 // Skia's "defaut" mode is srcover. NULL in SkPaint is interpreted as srcover
1706 // so we can just return NULL from the factory.
1707 if (kSrcOver_Mode == mode) {
1708 return NULL;
1709 }
1710
1711 // guard our access to gCachedXfermodes, since we may write into it
1712 SkAutoMutexAcquire ac(gCachedXfermodesMutex);
1713
1714 SkXfermode* xfer = gCachedXfermodes[mode];
1715 if (NULL == xfer) {
1716 const ProcCoeff& rec = gProcCoeffs[mode];
djsollen@google.com6f980c62013-10-08 16:59:53 +00001717 // All modes can in theory be represented by the ProcCoeff rec, since
1718 // it contains function ptrs. However, a few modes are both simple and
1719 // commonly used, so we call those out for their own subclasses here.
1720 switch (mode) {
1721 case kClear_Mode:
1722 xfer = SkNEW_ARGS(SkClearXfermode, (rec));
1723 break;
1724 case kSrc_Mode:
1725 xfer = SkNEW_ARGS(SkSrcXfermode, (rec));
1726 break;
1727 case kSrcOver_Mode:
1728 SkASSERT(false); // should not land here
1729 break;
1730 case kDstIn_Mode:
1731 xfer = SkNEW_ARGS(SkDstInXfermode, (rec));
1732 break;
1733 case kDstOut_Mode:
1734 xfer = SkNEW_ARGS(SkDstOutXfermode, (rec));
1735 break;
1736 default:
1737 // no special-case, just rely in the rec and its function-ptrs
1738 xfer = SkNEW_ARGS(SkProcCoeffXfermode, (rec, mode));
1739 break;
commit-bot@chromium.org86490572013-10-04 16:52:55 +00001740 }
1741 gCachedXfermodes[mode] = xfer;
1742 }
1743 return SkSafeRef(xfer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744}
1745
reed@google.com43c50c82011-04-14 15:50:52 +00001746SkXfermodeProc SkXfermode::GetProc(Mode mode) {
1747 SkXfermodeProc proc = NULL;
1748 if ((unsigned)mode < kModeCount) {
1749 proc = gProcCoeffs[mode].fProc;
1750 }
1751 return proc;
1752}
1753
1754bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
1755 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001756
reed@google.com43c50c82011-04-14 15:50:52 +00001757 if ((unsigned)mode >= (unsigned)kModeCount) {
1758 // illegal mode parameter
1759 return false;
1760 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001761
reed@google.com43c50c82011-04-14 15:50:52 +00001762 const ProcCoeff& rec = gProcCoeffs[mode];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001763
reed@google.com43c50c82011-04-14 15:50:52 +00001764 if (CANNOT_USE_COEFF == rec.fSC) {
1765 return false;
1766 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001767
reed@google.com43c50c82011-04-14 15:50:52 +00001768 SkASSERT(CANNOT_USE_COEFF != rec.fDC);
1769 if (src) {
1770 *src = rec.fSC;
1771 }
1772 if (dst) {
1773 *dst = rec.fDC;
1774 }
1775 return true;
1776}
1777
reed@google.com30da7452012-12-17 19:55:24 +00001778bool SkXfermode::AsMode(const SkXfermode* xfer, Mode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 if (NULL == xfer) {
1780 if (mode) {
1781 *mode = kSrcOver_Mode;
1782 }
1783 return true;
1784 }
reed@google.comc0d4aa22011-04-13 21:12:04 +00001785 return xfer->asMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
reed@google.com30da7452012-12-17 19:55:24 +00001788bool SkXfermode::AsCoeff(const SkXfermode* xfer, Coeff* src, Coeff* dst) {
reed@google.com43c50c82011-04-14 15:50:52 +00001789 if (NULL == xfer) {
1790 return ModeAsCoeff(kSrcOver_Mode, src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791 }
reed@google.com43c50c82011-04-14 15:50:52 +00001792 return xfer->asCoeff(src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793}
1794
reed@google.com30da7452012-12-17 19:55:24 +00001795bool SkXfermode::IsMode(const SkXfermode* xfer, Mode mode) {
mike@reedtribe.orge303fcf2011-11-17 02:16:43 +00001796 // if xfer==null then the mode is srcover
1797 Mode m = kSrcOver_Mode;
1798 if (xfer && !xfer->asMode(&m)) {
1799 return false;
1800 }
1801 return mode == m;
1802}
1803
reed@android.com8a1c16f2008-12-17 15:59:43 +00001804///////////////////////////////////////////////////////////////////////////////
1805//////////// 16bit xfermode procs
1806
1807#ifdef SK_DEBUG
1808static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
1809static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
1810#endif
1811
1812static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
1813 SkASSERT(require_255(src));
1814 return SkPixel32ToPixel16(src);
1815}
1816
1817static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
1818 return dst;
1819}
1820
1821static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
1822 SkASSERT(require_0(src));
1823 return dst;
1824}
1825
1826static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
1827 SkASSERT(require_255(src));
1828 return SkPixel32ToPixel16(src);
1829}
1830
1831static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
1832 SkASSERT(require_0(src));
1833 return dst;
1834}
1835
1836static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
1837 SkASSERT(require_255(src));
1838 return dst;
1839}
1840
1841static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
1842 SkASSERT(require_255(src));
1843 return SkPixel32ToPixel16(src);
1844}
1845
1846static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
1847 SkASSERT(require_255(src));
1848 return dst;
1849}
1850
1851static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
1852 SkASSERT(require_0(src));
1853 return dst;
1854}
1855
1856static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
1857 unsigned isa = 255 - SkGetPackedA32(src);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001858
reed@android.com8a1c16f2008-12-17 15:59:43 +00001859 return SkPackRGB16(
1860 SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
1861 SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
1862 SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
1863}
1864
1865static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
1866 SkASSERT(require_0(src));
1867 return dst;
1868}
1869
1870static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1871 SkASSERT(require_255(src));
1872 return SkPixel32ToPixel16(src);
1873}
1874
1875static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1876 SkASSERT(require_255(src));
1877 return dst;
1878}
1879
1880/*********
1881 darken and lighten boil down to this.
1882
1883 darken = (1 - Sa) * Dc + min(Sc, Dc)
1884 lighten = (1 - Sa) * Dc + max(Sc, Dc)
1885
1886 if (Sa == 0) these become
1887 darken = Dc + min(0, Dc) = 0
1888 lighten = Dc + max(0, Dc) = Dc
1889
1890 if (Sa == 1) these become
1891 darken = min(Sc, Dc)
1892 lighten = max(Sc, Dc)
1893*/
1894
1895static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
1896 SkASSERT(require_0(src));
1897 return 0;
1898}
1899
1900static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
1901 SkASSERT(require_255(src));
1902 unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1903 unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1904 unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1905 return SkPackRGB16(r, g, b);
1906}
1907
1908static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
1909 SkASSERT(require_0(src));
1910 return dst;
1911}
1912
1913static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
1914 SkASSERT(require_255(src));
1915 unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1916 unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1917 unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1918 return SkPackRGB16(r, g, b);
1919}
1920
1921struct Proc16Rec {
1922 SkXfermodeProc16 fProc16_0;
1923 SkXfermodeProc16 fProc16_255;
1924 SkXfermodeProc16 fProc16_General;
1925};
1926
reed@android.coma0f5d152009-06-22 17:38:10 +00001927static const Proc16Rec gModeProcs16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 { NULL, NULL, NULL }, // CLEAR
1929 { NULL, src_modeproc16_255, NULL },
1930 { dst_modeproc16, dst_modeproc16, dst_modeproc16 },
1931 { srcover_modeproc16_0, srcover_modeproc16_255, NULL },
1932 { dstover_modeproc16_0, dstover_modeproc16_255, NULL },
1933 { NULL, srcin_modeproc16_255, NULL },
1934 { NULL, dstin_modeproc16_255, NULL },
1935 { NULL, NULL, NULL },// SRC_OUT
1936 { dstout_modeproc16_0, NULL, NULL },
1937 { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16 },
1938 { NULL, dstatop_modeproc16_255, NULL },
1939 { NULL, NULL, NULL }, // XOR
reed@android.coma0f5d152009-06-22 17:38:10 +00001940
1941 { NULL, NULL, NULL }, // plus
reed@google.com8d3cd7a2013-01-30 21:36:11 +00001942 { NULL, NULL, NULL }, // modulate
reed@android.coma0f5d152009-06-22 17:38:10 +00001943 { NULL, NULL, NULL }, // screen
1944 { NULL, NULL, NULL }, // overlay
1945 { darken_modeproc16_0, darken_modeproc16_255, NULL }, // darken
1946 { lighten_modeproc16_0, lighten_modeproc16_255, NULL }, // lighten
1947 { NULL, NULL, NULL }, // colordodge
1948 { NULL, NULL, NULL }, // colorburn
1949 { NULL, NULL, NULL }, // hardlight
1950 { NULL, NULL, NULL }, // softlight
1951 { NULL, NULL, NULL }, // difference
1952 { NULL, NULL, NULL }, // exclusion
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +00001953 { NULL, NULL, NULL }, // multiply
1954 { NULL, NULL, NULL }, // hue
1955 { NULL, NULL, NULL }, // saturation
1956 { NULL, NULL, NULL }, // color
1957 { NULL, NULL, NULL }, // luminosity
reed@android.com8a1c16f2008-12-17 15:59:43 +00001958};
1959
reed@android.coma0f5d152009-06-22 17:38:10 +00001960SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001961 SkXfermodeProc16 proc16 = NULL;
reed@android.coma0f5d152009-06-22 17:38:10 +00001962 if ((unsigned)mode < kModeCount) {
1963 const Proc16Rec& rec = gModeProcs16[mode];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964 unsigned a = SkColorGetA(srcColor);
1965
1966 if (0 == a) {
1967 proc16 = rec.fProc16_0;
1968 } else if (255 == a) {
1969 proc16 = rec.fProc16_255;
1970 } else {
1971 proc16 = rec.fProc16_General;
1972 }
1973 }
1974 return proc16;
1975}
1976
caryclark@google.comd26147a2011-12-15 14:16:43 +00001977SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermode)
1978 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkProcCoeffXfermode)
1979 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkClearXfermode)
1980 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSrcXfermode)
1981 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstInXfermode)
1982 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstOutXfermode)
1983SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END