blob: 6f22aa43a6fae3179a2a49f101536d7dbb258af5 [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
deanm@chromium.orgda946992009-07-03 12:54:24 +000064static inline int clamp_max(int value, int max) {
reed@android.coma0f5d152009-06-22 17:38:10 +000065 if (value > max) {
66 value = max;
67 }
68 return value;
69}
70
reed@android.com8a1c16f2008-12-17 15:59:43 +000071///////////////////////////////////////////////////////////////////////////////
72
reed@android.com8a1c16f2008-12-17 15:59:43 +000073// kClear_Mode, //!< [0, 0]
74static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
75 return 0;
76}
77
78// kSrc_Mode, //!< [Sa, Sc]
79static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) {
80 return src;
81}
82
83// kDst_Mode, //!< [Da, Dc]
84static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) {
85 return dst;
86}
87
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000088// kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Sc + (1 - Sa)*Dc]
reed@android.com8a1c16f2008-12-17 15:59:43 +000089static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +000090#if 0
91 // this is the old, more-correct way, but it doesn't guarantee that dst==255
92 // will always stay opaque
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
reed@android.com1116fb22009-03-03 20:31:12 +000094#else
95 // this is slightly faster, but more importantly guarantees that dst==255
96 // will always stay opaque
97 return src + SkAlphaMulQ(dst, 256 - SkGetPackedA32(src));
98#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000099}
100
reed@android.com1116fb22009-03-03 20:31:12 +0000101// kDstOver_Mode, //!< [Sa + Da - Sa*Da, Dc + (1 - Da)*Sc]
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com1116fb22009-03-03 20:31:12 +0000103 // this is the reverse of srcover, just flipping src and dst
104 // see srcover's comment about the 256 for opaqueness guarantees
105 return dst + SkAlphaMulQ(src, 256 - SkGetPackedA32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106}
107
108// kSrcIn_Mode, //!< [Sa * Da, Sc * Da]
109static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) {
110 return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst)));
111}
112
113// kDstIn_Mode, //!< [Sa * Da, Sa * Dc]
114static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) {
115 return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src)));
116}
117
118// kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)]
119static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) {
120 return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst)));
121}
122
123// kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)]
124static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) {
125 return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
126}
127
128// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc]
129static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) {
130 unsigned sa = SkGetPackedA32(src);
131 unsigned da = SkGetPackedA32(dst);
132 unsigned isa = 255 - sa;
133
134 return SkPackARGB32(da,
135 SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
136 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
137 SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
138 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
139 SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
140 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
141}
142
143// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
144static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) {
145 unsigned sa = SkGetPackedA32(src);
146 unsigned da = SkGetPackedA32(dst);
147 unsigned ida = 255 - da;
148
149 return SkPackARGB32(sa,
150 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
151 SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
152 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
153 SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
154 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
155 SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
156}
157
158// kXor_Mode [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
159static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) {
160 unsigned sa = SkGetPackedA32(src);
161 unsigned da = SkGetPackedA32(dst);
162 unsigned isa = 255 - sa;
163 unsigned ida = 255 - da;
164
165 return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
166 SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
167 SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
168 SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
169 SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
170 SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
171 SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
172}
173
reed@android.coma0f5d152009-06-22 17:38:10 +0000174///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
reed@android.coma0f5d152009-06-22 17:38:10 +0000176// kPlus_Mode
177static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000178 unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
deanm@chromium.orgda946992009-07-03 12:54:24 +0000179 unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
180 unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
181 unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
reed@android.coma0f5d152009-06-22 17:38:10 +0000182 return SkPackARGB32(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183}
184
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000185// kModulate_Mode
186static SkPMColor modulate_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
188 int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
189 int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
190 int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
191 return SkPackARGB32(a, r, g, b);
192}
193
reed@android.coma0f5d152009-06-22 17:38:10 +0000194static inline int srcover_byte(int a, int b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 return a + b - SkAlphaMulAlpha(a, b);
196}
reed@google.com25cfa692013-02-04 20:06:00 +0000197
198// kMultiply_Mode
199// B(Cb, Cs) = Cb x Cs
200// multiply uses its own version of blendfunc_byte because sa and da are not needed
201static int blendfunc_multiply_byte(int sc, int dc, int sa, int da) {
202 return clamp_div255round(sc * (255 - da) + dc * (255 - sa) + sc * dc);
203}
204
205static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
206 int sa = SkGetPackedA32(src);
207 int da = SkGetPackedA32(dst);
208 int a = srcover_byte(sa, da);
209 int r = blendfunc_multiply_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
210 int g = blendfunc_multiply_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
211 int b = blendfunc_multiply_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
212 return SkPackARGB32(a, r, g, b);
213}
214
215// kScreen_Mode
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000217 int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
218 int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
219 int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
220 int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 return SkPackARGB32(a, r, g, b);
222}
223
reed@android.coma0f5d152009-06-22 17:38:10 +0000224// kOverlay_Mode
225static inline int overlay_byte(int sc, int dc, int sa, int da) {
226 int tmp = sc * (255 - da) + dc * (255 - sa);
227 int rc;
228 if (2 * dc <= da) {
229 rc = 2 * sc * dc;
230 } else {
231 rc = sa * da - 2 * (da - dc) * (sa - sc);
232 }
233 return clamp_div255round(rc + tmp);
234}
235static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
236 int sa = SkGetPackedA32(src);
237 int da = SkGetPackedA32(dst);
238 int a = srcover_byte(sa, da);
239 int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
240 int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
241 int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
242 return SkPackARGB32(a, r, g, b);
243}
244
245// kDarken_Mode
246static inline int darken_byte(int sc, int dc, int sa, int da) {
247 int sd = sc * da;
248 int ds = dc * sa;
249 if (sd < ds) {
250 // srcover
251 return sc + dc - SkDiv255Round(ds);
252 } else {
253 // dstover
254 return dc + sc - SkDiv255Round(sd);
255 }
256}
257static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
258 int sa = SkGetPackedA32(src);
259 int da = SkGetPackedA32(dst);
260 int a = srcover_byte(sa, da);
261 int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
262 int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
263 int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
264 return SkPackARGB32(a, r, g, b);
265}
266
267// kLighten_Mode
268static inline int lighten_byte(int sc, int dc, int sa, int da) {
269 int sd = sc * da;
270 int ds = dc * sa;
271 if (sd > ds) {
272 // srcover
273 return sc + dc - SkDiv255Round(ds);
274 } else {
275 // dstover
276 return dc + sc - SkDiv255Round(sd);
277 }
278}
279static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
280 int sa = SkGetPackedA32(src);
281 int da = SkGetPackedA32(dst);
282 int a = srcover_byte(sa, da);
283 int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
284 int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
285 int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
286 return SkPackARGB32(a, r, g, b);
287}
288
289// kColorDodge_Mode
290static inline int colordodge_byte(int sc, int dc, int sa, int da) {
291 int diff = sa - sc;
292 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000293 if (0 == dc) {
294 return SkAlphaMulAlpha(sc, 255 - da);
295 } else if (0 == diff) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000296 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000297 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000298 diff = dc * sa / diff;
299 rc = sa * ((da < diff) ? da : diff) + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000300 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000301 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000302}
303static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000304 int sa = SkGetPackedA32(src);
305 int da = SkGetPackedA32(dst);
306 int a = srcover_byte(sa, da);
307 int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
308 int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
309 int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.coma0f5d152009-06-22 17:38:10 +0000310 return SkPackARGB32(a, r, g, b);
311}
312
313// kColorBurn_Mode
314static inline int colorburn_byte(int sc, int dc, int sa, int da) {
315 int rc;
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000316 if (dc == da) {
317 rc = sa * da + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000318 } else if (0 == sc) {
319 return SkAlphaMulAlpha(dc, 255 - sa);
320 } else {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000321 int tmp = (da - dc) * sa / sc;
322 rc = sa * (da - ((da < tmp) ? da : tmp))
323 + sc * (255 - da) + dc * (255 - sa);
reed@android.coma0f5d152009-06-22 17:38:10 +0000324 }
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000325 return clamp_div255round(rc);
reed@android.coma0f5d152009-06-22 17:38:10 +0000326}
327static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
reed@android.coma0f5d152009-06-22 17:38:10 +0000328 int sa = SkGetPackedA32(src);
329 int da = SkGetPackedA32(dst);
330 int a = srcover_byte(sa, da);
331 int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
332 int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
333 int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
334 return SkPackARGB32(a, r, g, b);
335}
336
337// kHardLight_Mode
338static inline int hardlight_byte(int sc, int dc, int sa, int da) {
339 int rc;
340 if (2 * sc <= sa) {
341 rc = 2 * sc * dc;
342 } else {
343 rc = sa * da - 2 * (da - dc) * (sa - sc);
344 }
345 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
346}
347static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
348 int sa = SkGetPackedA32(src);
349 int da = SkGetPackedA32(dst);
350 int a = srcover_byte(sa, da);
351 int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
352 int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
353 int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
354 return SkPackARGB32(a, r, g, b);
355}
356
357// returns 255 * sqrt(n/255)
358static U8CPU sqrt_unit_byte(U8CPU n) {
359 return SkSqrtBits(n, 15+4);
360}
361
362// kSoftLight_Mode
363static inline int softlight_byte(int sc, int dc, int sa, int da) {
364 int m = da ? dc * 256 / da : 0;
365 int rc;
366 if (2 * sc <= sa) {
367 rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
368 } else if (4 * dc <= da) {
369 int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
370 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
371 } else {
372 int tmp = sqrt_unit_byte(m) - m;
373 rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
374 }
375 return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
376}
377static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
378 int sa = SkGetPackedA32(src);
379 int da = SkGetPackedA32(dst);
380 int a = srcover_byte(sa, da);
381 int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
382 int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
383 int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
384 return SkPackARGB32(a, r, g, b);
385}
386
387// kDifference_Mode
388static inline int difference_byte(int sc, int dc, int sa, int da) {
389 int tmp = SkMin32(sc * da, dc * sa);
390 return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
391}
392static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
393 int sa = SkGetPackedA32(src);
394 int da = SkGetPackedA32(dst);
395 int a = srcover_byte(sa, da);
396 int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
397 int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
398 int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
399 return SkPackARGB32(a, r, g, b);
400}
401
402// kExclusion_Mode
403static inline int exclusion_byte(int sc, int dc, int sa, int da) {
404 // this equations is wacky, wait for SVG to confirm it
405 int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
406 return clamp_div255round(r);
407}
408static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
409 int sa = SkGetPackedA32(src);
410 int da = SkGetPackedA32(dst);
411 int a = srcover_byte(sa, da);
412 int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
413 int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
414 int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
reed@android.com543ed932009-04-24 12:43:40 +0000415 return SkPackARGB32(a, r, g, b);
416}
417
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000418// The CSS compositing spec introduces the following formulas:
419// (See https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnonseparable)
420// SkComputeLuminance is similar to this formula but it uses the new definition from Rec. 709
421// while PDF and CG uses the one from Rec. Rec. 601
422// See http://www.glennchan.info/articles/technical/hd-versus-sd-color-space/hd-versus-sd-color-space.htm
423static inline int Lum(int r, int g, int b)
424{
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000425 return SkDiv255Round(r * 77 + g * 150 + b * 28);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000426}
427
428static inline int min2(int a, int b) { return a < b ? a : b; }
429static inline int max2(int a, int b) { return a > b ? a : b; }
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000430#define minimum(a, b, c) min2(min2(a, b), c)
431#define maximum(a, b, c) max2(max2(a, b), c)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000432
433static inline int Sat(int r, int g, int b) {
434 return maximum(r, g, b) - minimum(r, g, b);
435}
436
437static inline void setSaturationComponents(int* Cmin, int* Cmid, int* Cmax, int s) {
reed@google.com3c1ea3a2013-03-07 15:31:58 +0000438 if(*Cmax > *Cmin) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000439 *Cmid = SkMulDiv(*Cmid - *Cmin, s, *Cmax - *Cmin);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000440 *Cmax = s;
441 } else {
442 *Cmax = 0;
443 *Cmid = 0;
444 }
445
446 *Cmin = 0;
447}
448
449static inline void SetSat(int* r, int* g, int* b, int s) {
450 if(*r <= *g) {
451 if(*g <= *b) {
452 setSaturationComponents(r, g, b, s);
453 } else if(*r <= *b) {
454 setSaturationComponents(r, b, g, s);
455 } else {
456 setSaturationComponents(b, r, g, s);
457 }
458 } else if(*r <= *b) {
459 setSaturationComponents(g, r, b, s);
460 } else if(*g <= *b) {
461 setSaturationComponents(g, b, r, s);
462 } else {
463 setSaturationComponents(b, g, r, s);
464 }
465}
466
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000467static inline void clipColor(int* r, int* g, int* b, int a) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000468 int L = Lum(*r, *g, *b);
469 int n = minimum(*r, *g, *b);
470 int x = maximum(*r, *g, *b);
471 if(n < 0) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000472 *r = L + SkMulDiv(*r - L, L, L - n);
473 *g = L + SkMulDiv(*g - L, L, L - n);
474 *b = L + SkMulDiv(*b - L, L, L - n);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000475 }
476
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000477 if (x > a) {
478 *r = L + SkMulDiv(*r - L, a - L, x - L);
479 *g = L + SkMulDiv(*g - L, a - L, x - L);
480 *b = L + SkMulDiv(*b - L, a - L, x - L);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000481 }
482}
483
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000484static inline void SetLum(int* r, int* g, int* b, int a, int l) {
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000485 int d = l - Lum(*r, *g, *b);
486 *r += d;
487 *g += d;
488 *b += d;
489
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000490 clipColor(r, g, b, a);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000491}
492
493// non-separable blend modes are done in non-premultiplied alpha
494#define blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000495 clamp_div255round(sc * (255 - da) + dc * (255 - sa) + blendval)
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000496
497// kHue_Mode
498// B(Cb, Cs) = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
499// Create a color with the hue of the source color and the saturation and luminosity of the backdrop color.
500static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
501 int sr = SkGetPackedR32(src);
502 int sg = SkGetPackedG32(src);
503 int sb = SkGetPackedB32(src);
504 int sa = SkGetPackedA32(src);
505
506 int dr = SkGetPackedR32(dst);
507 int dg = SkGetPackedG32(dst);
508 int db = SkGetPackedB32(dst);
509 int da = SkGetPackedA32(dst);
510 int Sr, Sg, Sb;
511
512 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000513 Sr = sr * sa;
514 Sg = sg * sa;
515 Sb = sb * sa;
516 SetSat(&Sr, &Sg, &Sb, Sat(dr, dg, db) * sa);
517 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000518 } else {
519 Sr = 0;
520 Sg = 0;
521 Sb = 0;
522 }
523
524 int a = srcover_byte(sa, da);
525 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
526 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
527 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
528 return SkPackARGB32(a, r, g, b);
529}
530
531// kSaturation_Mode
532// B(Cb, Cs) = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000533// 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 +0000534static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
535 int sr = SkGetPackedR32(src);
536 int sg = SkGetPackedG32(src);
537 int sb = SkGetPackedB32(src);
538 int sa = SkGetPackedA32(src);
539
540 int dr = SkGetPackedR32(dst);
541 int dg = SkGetPackedG32(dst);
542 int db = SkGetPackedB32(dst);
543 int da = SkGetPackedA32(dst);
544 int Dr, Dg, Db;
545
546 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000547 Dr = dr * sa;
548 Dg = dg * sa;
549 Db = db * sa;
550 SetSat(&Dr, &Dg, &Db, Sat(sr, sg, sb) * da);
551 SetLum(&Dr, &Dg, &Db, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000552 } else {
553 Dr = 0;
554 Dg = 0;
555 Db = 0;
556 }
557
558 int a = srcover_byte(sa, da);
559 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
560 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
561 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
562 return SkPackARGB32(a, r, g, b);
563}
564
565// kColor_Mode
566// B(Cb, Cs) = SetLum(Cs, Lum(Cb))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000567// 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 +0000568static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
569 int sr = SkGetPackedR32(src);
570 int sg = SkGetPackedG32(src);
571 int sb = SkGetPackedB32(src);
572 int sa = SkGetPackedA32(src);
573
574 int dr = SkGetPackedR32(dst);
575 int dg = SkGetPackedG32(dst);
576 int db = SkGetPackedB32(dst);
577 int da = SkGetPackedA32(dst);
578 int Sr, Sg, Sb;
579
580 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000581 Sr = sr * da;
582 Sg = sg * da;
583 Sb = sb * da;
584 SetLum(&Sr, &Sg, &Sb, sa * da, Lum(dr, dg, db) * sa);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000585 } else {
586 Sr = 0;
587 Sg = 0;
588 Sb = 0;
589 }
590
591 int a = srcover_byte(sa, da);
592 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
593 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
594 int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
595 return SkPackARGB32(a, r, g, b);
596}
597
598// kLuminosity_Mode
599// B(Cb, Cs) = SetLum(Cb, Lum(Cs))
skia.committer@gmail.com64334352013-03-06 07:01:46 +0000600// 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 +0000601static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
602 int sr = SkGetPackedR32(src);
603 int sg = SkGetPackedG32(src);
604 int sb = SkGetPackedB32(src);
605 int sa = SkGetPackedA32(src);
606
607 int dr = SkGetPackedR32(dst);
608 int dg = SkGetPackedG32(dst);
609 int db = SkGetPackedB32(dst);
610 int da = SkGetPackedA32(dst);
611 int Dr, Dg, Db;
612
613 if(sa && da) {
commit-bot@chromium.org311d4ea2013-03-21 12:40:08 +0000614 Dr = dr * sa;
615 Dg = dg * sa;
616 Db = db * sa;
617 SetLum(&Dr, &Dg, &Db, sa * da, Lum(sr, sg, sb) * da);
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000618 } else {
619 Dr = 0;
620 Dg = 0;
621 Db = 0;
622 }
623
624 int a = srcover_byte(sa, da);
625 int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
626 int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
627 int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
628 return SkPackARGB32(a, r, g, b);
629}
630
631
vandebo@chromium.org48543272011-02-08 19:28:07 +0000632struct ProcCoeff {
633 SkXfermodeProc fProc;
634 SkXfermode::Coeff fSC;
635 SkXfermode::Coeff fDC;
636};
637
638#define CANNOT_USE_COEFF SkXfermode::Coeff(-1)
639
640static const ProcCoeff gProcCoeffs[] = {
641 { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff },
642 { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff },
643 { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff },
644 { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff },
645 { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff },
646 { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff },
647 { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff },
648 { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff },
649 { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff },
650 { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff },
651 { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff },
652 { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff },
653
reed@google.com521e34e2011-04-12 18:55:21 +0000654 { plus_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kOne_Coeff },
reed@google.com8d3cd7a2013-01-30 21:36:11 +0000655 { modulate_modeproc,SkXfermode::kZero_Coeff, SkXfermode::kSC_Coeff },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000656 { screen_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
657 { overlay_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
658 { darken_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
659 { lighten_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
660 { colordodge_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
661 { colorburn_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
662 { hardlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
663 { softlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
664 { difference_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
665 { exclusion_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
reed@google.com25cfa692013-02-04 20:06:00 +0000666 { multiply_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +0000667 { hue_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
668 { saturation_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
669 { color_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
670 { luminosity_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF },
vandebo@chromium.org48543272011-02-08 19:28:07 +0000671};
672
673///////////////////////////////////////////////////////////////////////////////
674
reed@google.com30da7452012-12-17 19:55:24 +0000675bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000676 return false;
677}
678
reed@google.com30da7452012-12-17 19:55:24 +0000679bool SkXfermode::asMode(Mode* mode) const {
reed@google.comc0d4aa22011-04-13 21:12:04 +0000680 return false;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000681}
682
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000683bool SkXfermode::asNewEffectOrCoeff(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst) const {
684 return this->asCoeff(src, dst);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000685}
686
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000687bool SkXfermode::AsNewEffectOrCoeff(SkXfermode* xfermode,
688 GrContext* context,
689 GrEffectRef** effect,
690 Coeff* src,
691 Coeff* dst) {
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000692 if (NULL == xfermode) {
693 return ModeAsCoeff(kSrcOver_Mode, src, dst);
694 } else {
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000695 return xfermode->asNewEffectOrCoeff(context, effect, src, dst);
bsalomon@google.comf51c0132013-03-27 18:31:15 +0000696 }
697}
698
reed@google.com30da7452012-12-17 19:55:24 +0000699SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) const{
vandebo@chromium.org48543272011-02-08 19:28:07 +0000700 // no-op. subclasses should override this
701 return dst;
702}
703
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000704void SkXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
705 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000706 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000707 SkASSERT(dst && src && count >= 0);
708
709 if (NULL == aa) {
710 for (int i = count - 1; i >= 0; --i) {
711 dst[i] = this->xferColor(src[i], dst[i]);
712 }
713 } else {
714 for (int i = count - 1; i >= 0; --i) {
715 unsigned a = aa[i];
716 if (0 != a) {
717 SkPMColor dstC = dst[i];
718 SkPMColor C = this->xferColor(src[i], dstC);
719 if (0xFF != a) {
720 C = SkFourByteInterp(C, dstC, a);
721 }
722 dst[i] = C;
723 }
724 }
725 }
726}
727
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000728void SkXfermode::xfer16(uint16_t* dst,
729 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000730 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000731 SkASSERT(dst && src && count >= 0);
732
733 if (NULL == aa) {
734 for (int i = count - 1; i >= 0; --i) {
735 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
736 dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
737 }
738 } else {
739 for (int i = count - 1; i >= 0; --i) {
740 unsigned a = aa[i];
741 if (0 != a) {
742 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
743 SkPMColor C = this->xferColor(src[i], dstC);
744 if (0xFF != a) {
745 C = SkFourByteInterp(C, dstC, a);
746 }
747 dst[i] = SkPixel32ToPixel16_ToU16(C);
748 }
749 }
750 }
751}
752
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000753void SkXfermode::xfer4444(SkPMColor16* SK_RESTRICT dst,
754 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000755 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000756 SkASSERT(dst && src && count >= 0);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000757
vandebo@chromium.org48543272011-02-08 19:28:07 +0000758 if (NULL == aa) {
759 for (int i = count - 1; i >= 0; --i) {
760 SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
761 dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC));
762 }
763 } else {
764 for (int i = count - 1; i >= 0; --i) {
765 unsigned a = aa[i];
766 if (0 != a) {
767 SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
768 SkPMColor C = this->xferColor(src[i], dstC);
769 if (0xFF != a) {
770 C = SkFourByteInterp(C, dstC, a);
771 }
772 dst[i] = SkPixel32ToPixel4444(C);
773 }
774 }
775 }
776}
777
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000778void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
vandebo@chromium.org48543272011-02-08 19:28:07 +0000779 const SkPMColor 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 if (NULL == aa) {
784 for (int i = count - 1; i >= 0; --i) {
785 SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
786 dst[i] = SkToU8(SkGetPackedA32(res));
787 }
788 } else {
789 for (int i = count - 1; i >= 0; --i) {
790 unsigned a = aa[i];
791 if (0 != a) {
792 SkAlpha dstA = dst[i];
793 unsigned A = SkGetPackedA32(this->xferColor(src[i],
794 (SkPMColor)(dstA << SK_A32_SHIFT)));
795 if (0xFF != a) {
796 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
797 }
798 dst[i] = SkToU8(A);
799 }
800 }
801 }
802}
803
804///////////////////////////////////////////////////////////////////////////////
805
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000806void SkProcXfermode::xfer32(SkPMColor* 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 dst[i] = proc(src[i], dst[i]);
817 }
818 } else {
819 for (int i = count - 1; i >= 0; --i) {
820 unsigned a = aa[i];
821 if (0 != a) {
822 SkPMColor dstC = dst[i];
823 SkPMColor C = proc(src[i], dstC);
824 if (a != 0xFF) {
825 C = SkFourByteInterp(C, dstC, a);
826 }
827 dst[i] = C;
828 }
829 }
830 }
831 }
832}
833
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000834void SkProcXfermode::xfer16(uint16_t* SK_RESTRICT dst,
835 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000836 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000837 SkASSERT(dst && src && count >= 0);
838
839 SkXfermodeProc proc = fProc;
840
841 if (NULL != proc) {
842 if (NULL == aa) {
843 for (int i = count - 1; i >= 0; --i) {
844 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
845 dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
846 }
847 } else {
848 for (int i = count - 1; i >= 0; --i) {
849 unsigned a = aa[i];
850 if (0 != a) {
851 SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
852 SkPMColor C = proc(src[i], dstC);
853 if (0xFF != a) {
854 C = SkFourByteInterp(C, dstC, a);
855 }
856 dst[i] = SkPixel32ToPixel16_ToU16(C);
857 }
858 }
859 }
860 }
861}
862
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000863void SkProcXfermode::xfer4444(SkPMColor16* SK_RESTRICT dst,
864 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000865 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000866 SkASSERT(dst && src && count >= 0);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000867
vandebo@chromium.org48543272011-02-08 19:28:07 +0000868 SkXfermodeProc proc = fProc;
869
870 if (NULL != proc) {
871 if (NULL == aa) {
872 for (int i = count - 1; i >= 0; --i) {
873 SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
874 dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC));
875 }
876 } else {
877 for (int i = count - 1; i >= 0; --i) {
878 unsigned a = aa[i];
879 if (0 != a) {
880 SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
881 SkPMColor C = proc(src[i], dstC);
882 if (0xFF != a) {
883 C = SkFourByteInterp(C, dstC, a);
884 }
885 dst[i] = SkPixel32ToPixel4444(C);
886 }
887 }
888 }
889 }
890}
891
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000892void SkProcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
893 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +0000894 const SkAlpha* SK_RESTRICT aa) const {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000895 SkASSERT(dst && src && count >= 0);
896
897 SkXfermodeProc proc = fProc;
898
899 if (NULL != proc) {
900 if (NULL == aa) {
901 for (int i = count - 1; i >= 0; --i) {
902 SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
903 dst[i] = SkToU8(SkGetPackedA32(res));
904 }
905 } else {
906 for (int i = count - 1; i >= 0; --i) {
907 unsigned a = aa[i];
908 if (0 != a) {
909 SkAlpha dstA = dst[i];
910 SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
911 unsigned A = SkGetPackedA32(res);
912 if (0xFF != a) {
913 A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
914 }
915 dst[i] = SkToU8(A);
916 }
917 }
918 }
919 }
920}
921
922SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
923 : SkXfermode(buffer) {
reed@google.com34342f62012-06-25 14:36:28 +0000924 fProc = NULL;
925 if (!buffer.isCrossProcess()) {
926 fProc = (SkXfermodeProc)buffer.readFunctionPtr();
927 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000928}
929
djsollen@google.com54924242012-03-29 15:18:04 +0000930void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
931 this->INHERITED::flatten(buffer);
reed@google.com34342f62012-06-25 14:36:28 +0000932 if (!buffer.isCrossProcess()) {
yangsu@google.comf468e472011-08-10 18:34:50 +0000933 buffer.writeFunctionPtr((void*)fProc);
934 }
vandebo@chromium.org48543272011-02-08 19:28:07 +0000935}
936
robertphillips@google.comb83b6b42013-01-22 14:32:09 +0000937#ifdef SK_DEVELOPER
938void SkProcXfermode::toString(SkString* str) const {
939 str->appendf("SkProcXfermode: %p", fProc);
940}
941#endif
942
bsalomon@google.com26e18b52013-03-29 19:22:36 +0000943//////////////////////////////////////////////////////////////////////////////
944
945#if SK_SUPPORT_GPU
946
947#include "GrEffect.h"
948#include "GrEffectUnitTest.h"
949#include "GrTBackendEffectFactory.h"
950#include "gl/GrGLEffect.h"
951
952/**
953 * GrEffect that implements the kDarken_Mode Xfermode. It requires access to the dst pixel color
954 * in the shader. TODO: Make this work for all non-Coeff SkXfermode::Mode values.
955 */
956class DarkenEffect : public GrEffect {
957public:
958 static GrEffectRef* Create() {
959 static AutoEffectUnref gEffect(SkNEW(DarkenEffect));
960 return CreateEffectRef(gEffect);
961 }
962
963 virtual void getConstantColorComponents(GrColor* color,
964 uint32_t* validFlags) const SK_OVERRIDE {
965 *validFlags = 0;
966 }
967
968 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
969 return GrTBackendEffectFactory<DarkenEffect>::getInstance();
970 }
971
972 static const char* Name() { return "XfermodeDarken"; }
973
974 class GLEffect : public GrGLEffect {
975 public:
976 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
977 : GrGLEffect(factory ) {
978 }
979 virtual void emitCode(GrGLShaderBuilder* builder,
980 const GrDrawEffect& drawEffect,
981 EffectKey key,
982 const char* outputColor,
983 const char* inputColor,
984 const TextureSamplerArray& samplers) SK_OVERRIDE {
985 const char* dstColorName = builder->dstColor();
986 GrAssert(NULL != dstColorName);
bsalomon@google.comb79d8652013-03-29 20:30:50 +0000987 if (NULL == inputColor) {
988 // the input color is solid white
989 builder->fsCodeAppendf("\t\t%s.a = 1.0;\n", outputColor);
990 builder->fsCodeAppendf("\t\t%s.rgb = vec3(1.0, 1.0, 1.0) - %s.aaa + %s.rgb;\n",
991 outputColor, dstColorName, dstColorName);
992 } else {
993 builder->fsCodeAppendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.a);\n",
994 outputColor, dstColorName, inputColor);
995 builder->fsCodeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb,"
996 " (1.0 - %s.a) * %s.rgb + %s.rgb);\n",
997 outputColor,
998 inputColor, dstColorName, inputColor,
999 dstColorName, inputColor, dstColorName);
1000 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001001 }
1002
1003 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
1004
1005 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
1006
1007 private:
1008 typedef GrGLEffect INHERITED;
1009 };
1010
1011 GR_DECLARE_EFFECT_TEST;
1012
1013private:
1014 DarkenEffect() { this->setWillReadDst(); }
1015 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { return true; }
1016
1017 typedef GrEffect INHERITED;
1018};
1019
1020GR_DEFINE_EFFECT_TEST(DarkenEffect);
1021GrEffectRef* DarkenEffect::TestCreate(SkMWCRandom*,
1022 GrContext*,
1023 const GrDrawTargetCaps&,
1024 GrTexture*[]) {
1025 static AutoEffectUnref gEffect(SkNEW(DarkenEffect));
1026 return CreateEffectRef(gEffect);
1027}
1028
1029#endif
1030
vandebo@chromium.org48543272011-02-08 19:28:07 +00001031///////////////////////////////////////////////////////////////////////////////
1032///////////////////////////////////////////////////////////////////////////////
1033
1034class SkProcCoeffXfermode : public SkProcXfermode {
1035public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001036 SkProcCoeffXfermode(const ProcCoeff& rec, Mode mode)
1037 : INHERITED(rec.fProc) {
1038 fMode = mode;
1039 // these may be valid, or may be CANNOT_USE_COEFF
1040 fSrcCoeff = rec.fSC;
1041 fDstCoeff = rec.fDC;
vandebo@chromium.org48543272011-02-08 19:28:07 +00001042 }
reed@google.comc0d4aa22011-04-13 21:12:04 +00001043
reed@google.com30da7452012-12-17 19:55:24 +00001044 virtual bool asMode(Mode* mode) const SK_OVERRIDE {
reed@google.comc0d4aa22011-04-13 21:12:04 +00001045 if (mode) {
1046 *mode = fMode;
1047 }
1048 return true;
1049 }
1050
reed@google.com30da7452012-12-17 19:55:24 +00001051 virtual bool asCoeff(Coeff* sc, Coeff* dc) const SK_OVERRIDE {
reed@google.comc0d4aa22011-04-13 21:12:04 +00001052 if (CANNOT_USE_COEFF == fSrcCoeff) {
1053 return false;
1054 }
1055
vandebo@chromium.org48543272011-02-08 19:28:07 +00001056 if (sc) {
1057 *sc = fSrcCoeff;
1058 }
1059 if (dc) {
1060 *dc = fDstCoeff;
1061 }
1062 return true;
1063 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001064
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001065#if SK_SUPPORT_GPU
1066 virtual bool asNewEffectOrCoeff(GrContext*,
1067 GrEffectRef** effect,
1068 Coeff* src,
1069 Coeff* dst) const SK_OVERRIDE {
1070 if (this->asCoeff(src, dst)) {
1071 return true;
1072 }
1073 if (kDarken_Mode == fMode) {
1074 if (NULL != effect) {
1075 *effect = DarkenEffect::Create();
1076 }
1077 return true;
1078 }
1079 return false;
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001080 }
bsalomon@google.com26e18b52013-03-29 19:22:36 +00001081#endif
bsalomon@google.comf51c0132013-03-27 18:31:15 +00001082
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001083 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001084 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcCoeffXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001085
vandebo@chromium.org48543272011-02-08 19:28:07 +00001086protected:
djsollen@google.com54924242012-03-29 15:18:04 +00001087 SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001088 fMode = (SkXfermode::Mode)buffer.read32();
reed@google.comc34d7cf2011-08-09 22:42:10 +00001089
1090 const ProcCoeff& rec = gProcCoeffs[fMode];
1091 // these may be valid, or may be CANNOT_USE_COEFF
1092 fSrcCoeff = rec.fSC;
1093 fDstCoeff = rec.fDC;
1094 // now update our function-ptr in the super class
1095 this->INHERITED::setProc(rec.fProc);
vandebo@chromium.org48543272011-02-08 19:28:07 +00001096 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001097
djsollen@google.com54924242012-03-29 15:18:04 +00001098 virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
1099 this->INHERITED::flatten(buffer);
1100 buffer.write32(fMode);
1101 }
1102
vandebo@chromium.org48543272011-02-08 19:28:07 +00001103private:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001104 Mode fMode;
vandebo@chromium.org48543272011-02-08 19:28:07 +00001105 Coeff fSrcCoeff, fDstCoeff;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001106
vandebo@chromium.org48543272011-02-08 19:28:07 +00001107 typedef SkProcXfermode INHERITED;
1108};
1109
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001110#ifdef SK_DEVELOPER
1111void SkProcCoeffXfermode::toString(SkString* str) const {
1112 str->append("SkProcCoeffXfermode: ");
1113
1114 const char *gModeStrings[kLastMode+1] = {
1115 "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn",
1116 "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Plus",
reed@google.com8d3cd7a2013-01-30 21:36:11 +00001117 "Modulate", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge",
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001118 "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion"
1119 };
1120
1121 str->append("mode: ");
1122 str->append(gModeStrings[fMode]);
1123
1124 static const char* gCoeffStrings[kCoeffCount] = {
skia.committer@gmail.com98ded842013-01-23 07:06:17 +00001125 "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001126 };
1127
1128 str->append(" src: ");
1129 if (CANNOT_USE_COEFF == fSrcCoeff) {
1130 str->append("can't use");
1131 } else {
1132 str->append(gCoeffStrings[fSrcCoeff]);
1133 }
1134
1135 str->append(" dst: ");
1136 if (CANNOT_USE_COEFF == fDstCoeff) {
1137 str->append("can't use");
1138 } else {
1139 str->append(gCoeffStrings[fDstCoeff]);
1140 }
1141}
1142#endif
1143
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144///////////////////////////////////////////////////////////////////////////////
1145
1146class SkClearXfermode : public SkProcCoeffXfermode {
1147public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001148 SkClearXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kClear_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149
reed@google.com30da7452012-12-17 19:55:24 +00001150 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1151 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001153 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001154 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkClearXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001155
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156private:
1157 SkClearXfermode(SkFlattenableReadBuffer& buffer)
1158 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001159
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001160 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161};
1162
reed@google.com86ab6c62011-11-28 15:26:14 +00001163void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1164 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001165 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001166 SkASSERT(dst && count >= 0);
1167
1168 if (NULL == aa) {
1169 memset(dst, 0, count << 2);
1170 } else {
1171 for (int i = count - 1; i >= 0; --i) {
1172 unsigned a = aa[i];
1173 if (0xFF == a) {
1174 dst[i] = 0;
1175 } else if (a != 0) {
1176 dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
1177 }
1178 }
1179 }
1180}
1181void SkClearXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1182 const SkPMColor* SK_RESTRICT, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001183 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001184 SkASSERT(dst && count >= 0);
1185
1186 if (NULL == aa) {
1187 memset(dst, 0, count);
1188 } else {
1189 for (int i = count - 1; i >= 0; --i) {
1190 unsigned a = aa[i];
1191 if (0xFF == a) {
1192 dst[i] = 0;
1193 } else if (0 != a) {
1194 dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
1195 }
1196 }
1197 }
1198}
1199
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001200#ifdef SK_DEVELOPER
1201void SkClearXfermode::toString(SkString* str) const {
1202 this->INHERITED::toString(str);
1203}
1204#endif
1205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206///////////////////////////////////////////////////////////////////////////////
1207
1208class SkSrcXfermode : public SkProcCoeffXfermode {
1209public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001210 SkSrcXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kSrc_Mode) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211
reed@google.com30da7452012-12-17 19:55:24 +00001212 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
1213 virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001215 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001216 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSrcXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001217
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218private:
1219 SkSrcXfermode(SkFlattenableReadBuffer& buffer)
1220 : SkProcCoeffXfermode(buffer) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001221
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001222 typedef SkProcCoeffXfermode INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223};
1224
reed@google.com86ab6c62011-11-28 15:26:14 +00001225void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1226 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001227 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001228 SkASSERT(dst && src && count >= 0);
1229
1230 if (NULL == aa) {
1231 memcpy(dst, src, count << 2);
1232 } else {
1233 for (int i = count - 1; i >= 0; --i) {
1234 unsigned a = aa[i];
1235 if (a == 0xFF) {
1236 dst[i] = src[i];
1237 } else if (a != 0) {
1238 dst[i] = SkFourByteInterp(src[i], dst[i], a);
1239 }
1240 }
1241 }
1242}
1243
1244void SkSrcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
1245 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001246 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001247 SkASSERT(dst && src && count >= 0);
1248
1249 if (NULL == aa) {
1250 for (int i = count - 1; i >= 0; --i) {
1251 dst[i] = SkToU8(SkGetPackedA32(src[i]));
1252 }
1253 } else {
1254 for (int i = count - 1; i >= 0; --i) {
1255 unsigned a = aa[i];
1256 if (0 != a) {
1257 unsigned srcA = SkGetPackedA32(src[i]);
1258 if (a == 0xFF) {
1259 dst[i] = SkToU8(srcA);
1260 } else {
1261 dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a));
1262 }
1263 }
1264 }
1265 }
1266}
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001267#ifdef SK_DEVELOPER
1268void SkSrcXfermode::toString(SkString* str) const {
1269 this->INHERITED::toString(str);
1270}
1271#endif
reed@google.com86ab6c62011-11-28 15:26:14 +00001272
reed@google.com30da7452012-12-17 19:55:24 +00001273///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275class SkDstInXfermode : public SkProcCoeffXfermode {
1276public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001277 SkDstInXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstIn_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001278
reed@google.com30da7452012-12-17 19:55:24 +00001279 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001280
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001281 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001282 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstInXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001283
1284private:
1285 SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
1286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 typedef SkProcCoeffXfermode INHERITED;
1288};
1289
reed@google.com86ab6c62011-11-28 15:26:14 +00001290void SkDstInXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1291 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001292 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001293 SkASSERT(dst && src);
1294
1295 if (count <= 0) {
1296 return;
1297 }
1298 if (NULL != aa) {
1299 return this->INHERITED::xfer32(dst, src, count, aa);
1300 }
1301
1302 do {
1303 unsigned a = SkGetPackedA32(*src);
1304 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
1305 dst++;
1306 src++;
1307 } while (--count != 0);
1308}
1309
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001310#ifdef SK_DEVELOPER
1311void SkDstInXfermode::toString(SkString* str) const {
1312 this->INHERITED::toString(str);
1313}
1314#endif
1315
reed@google.com30da7452012-12-17 19:55:24 +00001316///////////////////////////////////////////////////////////////////////////////
reed@google.com86ab6c62011-11-28 15:26:14 +00001317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318class SkDstOutXfermode : public SkProcCoeffXfermode {
1319public:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001320 SkDstOutXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstOut_Mode) {}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001321
reed@google.com30da7452012-12-17 19:55:24 +00001322 virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001323
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001324 SK_DEVELOPER_TO_STRING()
djsollen@google.comba28d032012-03-26 17:57:35 +00001325 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstOutXfermode)
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001326
1327private:
1328 SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
1329 : INHERITED(buffer) {}
1330
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 typedef SkProcCoeffXfermode INHERITED;
1332};
1333
reed@google.com86ab6c62011-11-28 15:26:14 +00001334void SkDstOutXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
1335 const SkPMColor* SK_RESTRICT src, int count,
reed@google.com30da7452012-12-17 19:55:24 +00001336 const SkAlpha* SK_RESTRICT aa) const {
reed@google.com86ab6c62011-11-28 15:26:14 +00001337 SkASSERT(dst && src);
1338
1339 if (count <= 0) {
1340 return;
1341 }
1342 if (NULL != aa) {
1343 return this->INHERITED::xfer32(dst, src, count, aa);
1344 }
1345
1346 do {
1347 unsigned a = SkGetPackedA32(*src);
1348 *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
1349 dst++;
1350 src++;
1351 } while (--count != 0);
1352}
1353
robertphillips@google.comb83b6b42013-01-22 14:32:09 +00001354#ifdef SK_DEVELOPER
1355void SkDstOutXfermode::toString(SkString* str) const {
1356 this->INHERITED::toString(str);
1357}
1358#endif
1359
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360///////////////////////////////////////////////////////////////////////////////
1361
reed@android.coma0f5d152009-06-22 17:38:10 +00001362SkXfermode* SkXfermode::Create(Mode mode) {
1363 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
1364 SkASSERT((unsigned)mode < kModeCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365
reed@google.comc0d4aa22011-04-13 21:12:04 +00001366 const ProcCoeff& rec = gProcCoeffs[mode];
1367
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 switch (mode) {
1369 case kClear_Mode:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001370 return SkNEW_ARGS(SkClearXfermode, (rec));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 case kSrc_Mode:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001372 return SkNEW_ARGS(SkSrcXfermode, (rec));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 case kSrcOver_Mode:
1374 return NULL;
1375 case kDstIn_Mode:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001376 return SkNEW_ARGS(SkDstInXfermode, (rec));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 case kDstOut_Mode:
reed@google.comc0d4aa22011-04-13 21:12:04 +00001378 return SkNEW_ARGS(SkDstOutXfermode, (rec));
1379 default:
1380 return SkNEW_ARGS(SkProcCoeffXfermode, (rec, mode));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 }
1382}
1383
reed@google.com43c50c82011-04-14 15:50:52 +00001384SkXfermodeProc SkXfermode::GetProc(Mode mode) {
1385 SkXfermodeProc proc = NULL;
1386 if ((unsigned)mode < kModeCount) {
1387 proc = gProcCoeffs[mode].fProc;
1388 }
1389 return proc;
1390}
1391
1392bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
1393 SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001394
reed@google.com43c50c82011-04-14 15:50:52 +00001395 if ((unsigned)mode >= (unsigned)kModeCount) {
1396 // illegal mode parameter
1397 return false;
1398 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001399
reed@google.com43c50c82011-04-14 15:50:52 +00001400 const ProcCoeff& rec = gProcCoeffs[mode];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001401
reed@google.com43c50c82011-04-14 15:50:52 +00001402 if (CANNOT_USE_COEFF == rec.fSC) {
1403 return false;
1404 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001405
reed@google.com43c50c82011-04-14 15:50:52 +00001406 SkASSERT(CANNOT_USE_COEFF != rec.fDC);
1407 if (src) {
1408 *src = rec.fSC;
1409 }
1410 if (dst) {
1411 *dst = rec.fDC;
1412 }
1413 return true;
1414}
1415
reed@google.com30da7452012-12-17 19:55:24 +00001416bool SkXfermode::AsMode(const SkXfermode* xfer, Mode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 if (NULL == xfer) {
1418 if (mode) {
1419 *mode = kSrcOver_Mode;
1420 }
1421 return true;
1422 }
reed@google.comc0d4aa22011-04-13 21:12:04 +00001423 return xfer->asMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424}
1425
reed@google.com30da7452012-12-17 19:55:24 +00001426bool SkXfermode::AsCoeff(const SkXfermode* xfer, Coeff* src, Coeff* dst) {
reed@google.com43c50c82011-04-14 15:50:52 +00001427 if (NULL == xfer) {
1428 return ModeAsCoeff(kSrcOver_Mode, src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 }
reed@google.com43c50c82011-04-14 15:50:52 +00001430 return xfer->asCoeff(src, dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431}
1432
reed@google.com30da7452012-12-17 19:55:24 +00001433bool SkXfermode::IsMode(const SkXfermode* xfer, Mode mode) {
mike@reedtribe.orge303fcf2011-11-17 02:16:43 +00001434 // if xfer==null then the mode is srcover
1435 Mode m = kSrcOver_Mode;
1436 if (xfer && !xfer->asMode(&m)) {
1437 return false;
1438 }
1439 return mode == m;
1440}
1441
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442///////////////////////////////////////////////////////////////////////////////
1443//////////// 16bit xfermode procs
1444
1445#ifdef SK_DEBUG
1446static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
1447static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
1448#endif
1449
1450static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
1451 SkASSERT(require_255(src));
1452 return SkPixel32ToPixel16(src);
1453}
1454
1455static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
1456 return dst;
1457}
1458
1459static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
1460 SkASSERT(require_0(src));
1461 return dst;
1462}
1463
1464static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
1465 SkASSERT(require_255(src));
1466 return SkPixel32ToPixel16(src);
1467}
1468
1469static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
1470 SkASSERT(require_0(src));
1471 return dst;
1472}
1473
1474static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
1475 SkASSERT(require_255(src));
1476 return dst;
1477}
1478
1479static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
1480 SkASSERT(require_255(src));
1481 return SkPixel32ToPixel16(src);
1482}
1483
1484static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
1485 SkASSERT(require_255(src));
1486 return dst;
1487}
1488
1489static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
1490 SkASSERT(require_0(src));
1491 return dst;
1492}
1493
1494static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
1495 unsigned isa = 255 - SkGetPackedA32(src);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +00001496
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 return SkPackRGB16(
1498 SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
1499 SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
1500 SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
1501}
1502
1503static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
1504 SkASSERT(require_0(src));
1505 return dst;
1506}
1507
1508static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1509 SkASSERT(require_255(src));
1510 return SkPixel32ToPixel16(src);
1511}
1512
1513static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
1514 SkASSERT(require_255(src));
1515 return dst;
1516}
1517
1518/*********
1519 darken and lighten boil down to this.
1520
1521 darken = (1 - Sa) * Dc + min(Sc, Dc)
1522 lighten = (1 - Sa) * Dc + max(Sc, Dc)
1523
1524 if (Sa == 0) these become
1525 darken = Dc + min(0, Dc) = 0
1526 lighten = Dc + max(0, Dc) = Dc
1527
1528 if (Sa == 1) these become
1529 darken = min(Sc, Dc)
1530 lighten = max(Sc, Dc)
1531*/
1532
1533static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
1534 SkASSERT(require_0(src));
1535 return 0;
1536}
1537
1538static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
1539 SkASSERT(require_255(src));
1540 unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1541 unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1542 unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1543 return SkPackRGB16(r, g, b);
1544}
1545
1546static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
1547 SkASSERT(require_0(src));
1548 return dst;
1549}
1550
1551static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
1552 SkASSERT(require_255(src));
1553 unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
1554 unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
1555 unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
1556 return SkPackRGB16(r, g, b);
1557}
1558
1559struct Proc16Rec {
1560 SkXfermodeProc16 fProc16_0;
1561 SkXfermodeProc16 fProc16_255;
1562 SkXfermodeProc16 fProc16_General;
1563};
1564
reed@android.coma0f5d152009-06-22 17:38:10 +00001565static const Proc16Rec gModeProcs16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 { NULL, NULL, NULL }, // CLEAR
1567 { NULL, src_modeproc16_255, NULL },
1568 { dst_modeproc16, dst_modeproc16, dst_modeproc16 },
1569 { srcover_modeproc16_0, srcover_modeproc16_255, NULL },
1570 { dstover_modeproc16_0, dstover_modeproc16_255, NULL },
1571 { NULL, srcin_modeproc16_255, NULL },
1572 { NULL, dstin_modeproc16_255, NULL },
1573 { NULL, NULL, NULL },// SRC_OUT
1574 { dstout_modeproc16_0, NULL, NULL },
1575 { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16 },
1576 { NULL, dstatop_modeproc16_255, NULL },
1577 { NULL, NULL, NULL }, // XOR
reed@android.coma0f5d152009-06-22 17:38:10 +00001578
1579 { NULL, NULL, NULL }, // plus
reed@google.com8d3cd7a2013-01-30 21:36:11 +00001580 { NULL, NULL, NULL }, // modulate
reed@android.coma0f5d152009-06-22 17:38:10 +00001581 { NULL, NULL, NULL }, // screen
1582 { NULL, NULL, NULL }, // overlay
1583 { darken_modeproc16_0, darken_modeproc16_255, NULL }, // darken
1584 { lighten_modeproc16_0, lighten_modeproc16_255, NULL }, // lighten
1585 { NULL, NULL, NULL }, // colordodge
1586 { NULL, NULL, NULL }, // colorburn
1587 { NULL, NULL, NULL }, // hardlight
1588 { NULL, NULL, NULL }, // softlight
1589 { NULL, NULL, NULL }, // difference
1590 { NULL, NULL, NULL }, // exclusion
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +00001591 { NULL, NULL, NULL }, // multiply
1592 { NULL, NULL, NULL }, // hue
1593 { NULL, NULL, NULL }, // saturation
1594 { NULL, NULL, NULL }, // color
1595 { NULL, NULL, NULL }, // luminosity
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596};
1597
reed@android.coma0f5d152009-06-22 17:38:10 +00001598SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599 SkXfermodeProc16 proc16 = NULL;
reed@android.coma0f5d152009-06-22 17:38:10 +00001600 if ((unsigned)mode < kModeCount) {
1601 const Proc16Rec& rec = gModeProcs16[mode];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602 unsigned a = SkColorGetA(srcColor);
1603
1604 if (0 == a) {
1605 proc16 = rec.fProc16_0;
1606 } else if (255 == a) {
1607 proc16 = rec.fProc16_255;
1608 } else {
1609 proc16 = rec.fProc16_General;
1610 }
1611 }
1612 return proc16;
1613}
1614
caryclark@google.comd26147a2011-12-15 14:16:43 +00001615SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermode)
1616 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkProcCoeffXfermode)
1617 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkClearXfermode)
1618 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSrcXfermode)
1619 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstInXfermode)
1620 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstOutXfermode)
1621SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END