1 | // Aseprite Document Library |
2 | // Copyright (c) 2019-2020 Igara Studio S.A. |
3 | // Copyright (c) 2001-2017 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | // |
8 | // -- |
9 | // |
10 | // Some references about alpha compositing and blend modes: |
11 | // |
12 | // http://dev.w3.org/fxtf/compositing-1/ |
13 | // http://www.adobe.com/devnet/pdf/pdf_reference.html |
14 | // |
15 | |
16 | #ifdef HAVE_CONFIG_H |
17 | #include "config.h" |
18 | #endif |
19 | |
20 | #include "doc/blend_funcs.h" |
21 | |
22 | #include "base/debug.h" |
23 | #include "doc/blend_internals.h" |
24 | |
25 | #include <algorithm> |
26 | #include <cmath> |
27 | |
28 | namespace { |
29 | |
30 | #define blend_multiply(b, s, t) (MUL_UN8((b), (s), (t))) |
31 | #define blend_screen(b, s, t) ((b) + (s) - MUL_UN8((b), (s), (t))) |
32 | #define blend_overlay(b, s, t) (blend_hard_light(s, b, t)) |
33 | #define blend_darken(b, s) (std::min((b), (s))) |
34 | #define blend_lighten(b, s) (std::max((b), (s))) |
35 | #define blend_hard_light(b, s, t) ((s) < 128 ? \ |
36 | blend_multiply((b), (s)<<1, (t)): \ |
37 | blend_screen((b), ((s)<<1)-255, (t))) |
38 | #define blend_difference(b, s) (ABS((b) - (s))) |
39 | #define blend_exclusion(b, s, t) ((t) = MUL_UN8((b), (s), (t)), ((b) + (s) - 2*(t))) |
40 | |
41 | // New Blender Method macros |
42 | #define RGBA_BLENDER_N(name) \ |
43 | color_t rgba_blender_##name##_n(color_t backdrop, color_t src, int opacity) { \ |
44 | if (backdrop & rgba_a_mask) { \ |
45 | color_t normal = rgba_blender_normal(backdrop, src, opacity); \ |
46 | color_t blend = rgba_blender_##name(backdrop, src, opacity); \ |
47 | int Ba = rgba_geta(backdrop); \ |
48 | color_t normalToBlendMerge = rgba_blender_merge(normal, blend, Ba); \ |
49 | int t; \ |
50 | int srcTotalAlpha = MUL_UN8(rgba_geta(src), opacity, t); \ |
51 | int compositeAlpha = MUL_UN8(Ba, srcTotalAlpha, t); \ |
52 | return rgba_blender_merge(normalToBlendMerge, blend, compositeAlpha); \ |
53 | } \ |
54 | else \ |
55 | return rgba_blender_normal(backdrop, src, opacity); \ |
56 | } |
57 | |
58 | #define GRAYA_BLENDER_N(name) \ |
59 | color_t graya_blender_##name##_n(color_t backdrop, color_t src, int opacity) { \ |
60 | if (backdrop & graya_a_mask) { \ |
61 | color_t normal = graya_blender_normal(backdrop, src, opacity); \ |
62 | color_t blend = graya_blender_##name(backdrop, src, opacity); \ |
63 | int Ba = graya_geta(backdrop); \ |
64 | color_t normalToBlendMerge = graya_blender_merge(normal, blend, Ba); \ |
65 | int t; \ |
66 | int srcTotalAlpha = MUL_UN8(graya_geta(src), opacity, t); \ |
67 | int compositeAlpha = MUL_UN8(Ba, srcTotalAlpha, t); \ |
68 | return graya_blender_merge(normalToBlendMerge, blend, compositeAlpha); \ |
69 | } \ |
70 | else \ |
71 | return graya_blender_normal(backdrop, src, opacity); \ |
72 | } |
73 | |
74 | inline uint32_t blend_divide(uint32_t b, uint32_t s) |
75 | { |
76 | if (b == 0) |
77 | return 0; |
78 | else if (b >= s) |
79 | return 255; |
80 | else |
81 | return DIV_UN8(b, s); // return b / s |
82 | } |
83 | |
84 | inline uint32_t blend_color_dodge(uint32_t b, uint32_t s) |
85 | { |
86 | if (b == 0) |
87 | return 0; |
88 | |
89 | s = (255 - s); |
90 | if (b >= s) |
91 | return 255; |
92 | else |
93 | return DIV_UN8(b, s); // return b / (1-s) |
94 | } |
95 | |
96 | inline uint32_t blend_color_burn(uint32_t b, uint32_t s) |
97 | { |
98 | if (b == 255) |
99 | return 255; |
100 | |
101 | b = (255 - b); |
102 | if (b >= s) |
103 | return 0; |
104 | else |
105 | return 255 - DIV_UN8(b, s); // return 1 - ((1-b)/s) |
106 | } |
107 | |
108 | inline uint32_t blend_soft_light(uint32_t _b, uint32_t _s) |
109 | { |
110 | double b = _b / 255.0; |
111 | double s = _s / 255.0; |
112 | double r, d; |
113 | |
114 | if (b <= 0.25) |
115 | d = ((16*b-12)*b+4)*b; |
116 | else |
117 | d = std::sqrt(b); |
118 | |
119 | if (s <= 0.5) |
120 | r = b - (1.0 - 2.0 * s) * b * (1.0 - b); |
121 | else |
122 | r = b + (2.0 * s - 1.0) * (d - b); |
123 | |
124 | return (uint32_t)(r * 255 + 0.5); |
125 | } |
126 | |
127 | } // annonymous namespace |
128 | |
129 | namespace doc { |
130 | |
131 | ////////////////////////////////////////////////////////////////////// |
132 | // RGB blenders |
133 | |
134 | color_t rgba_blender_src(color_t backdrop, color_t src, int opacity) |
135 | { |
136 | return src; |
137 | } |
138 | |
139 | color_t rgba_blender_merge(color_t backdrop, color_t src, int opacity) |
140 | { |
141 | int Br, Bg, Bb, Ba; |
142 | int Sr, Sg, Sb, Sa; |
143 | int Rr, Rg, Rb, Ra; |
144 | int t; |
145 | |
146 | Br = rgba_getr(backdrop); |
147 | Bg = rgba_getg(backdrop); |
148 | Bb = rgba_getb(backdrop); |
149 | Ba = rgba_geta(backdrop); |
150 | |
151 | Sr = rgba_getr(src); |
152 | Sg = rgba_getg(src); |
153 | Sb = rgba_getb(src); |
154 | Sa = rgba_geta(src); |
155 | |
156 | if (Ba == 0) { |
157 | Rr = Sr; |
158 | Rg = Sg; |
159 | Rb = Sb; |
160 | } |
161 | else if (Sa == 0) { |
162 | Rr = Br; |
163 | Rg = Bg; |
164 | Rb = Bb; |
165 | } |
166 | else { |
167 | Rr = Br + MUL_UN8((Sr - Br), opacity, t); |
168 | Rg = Bg + MUL_UN8((Sg - Bg), opacity, t); |
169 | Rb = Bb + MUL_UN8((Sb - Bb), opacity, t); |
170 | } |
171 | Ra = Ba + MUL_UN8((Sa - Ba), opacity, t); |
172 | if (Ra == 0) |
173 | Rr = Rg = Rb = 0; |
174 | |
175 | return rgba(Rr, Rg, Rb, Ra); |
176 | } |
177 | |
178 | color_t rgba_blender_neg_bw(color_t backdrop, color_t src, int opacity) |
179 | { |
180 | if (!(backdrop & rgba_a_mask)) |
181 | return rgba(0, 0, 0, 255); |
182 | else if (rgba_luma(backdrop) < 128) |
183 | return rgba(255, 255, 255, 255); |
184 | else |
185 | return rgba(0, 0, 0, 255); |
186 | } |
187 | |
188 | color_t rgba_blender_red_tint(color_t backdrop, color_t src, int opacity) |
189 | { |
190 | int v = rgba_luma(src); |
191 | src = rgba((255+v)/2, v/2, v/2, rgba_geta(src)); |
192 | return rgba_blender_normal(backdrop, src, opacity); |
193 | } |
194 | |
195 | color_t rgba_blender_blue_tint(color_t backdrop, color_t src, int opacity) |
196 | { |
197 | int v = rgba_luma(src); |
198 | src = rgba(v/2, v/2, (255+v)/2, rgba_geta(src)); |
199 | return rgba_blender_normal(backdrop, src, opacity); |
200 | } |
201 | |
202 | color_t rgba_blender_normal(color_t backdrop, color_t src, int opacity) |
203 | { |
204 | int t; |
205 | |
206 | if (!(backdrop & rgba_a_mask)) { |
207 | int a = rgba_geta(src); |
208 | a = MUL_UN8(a, opacity, t); |
209 | a <<= rgba_a_shift; |
210 | return (src & rgba_rgb_mask) | a; |
211 | } |
212 | else if (!(src & rgba_a_mask)) { |
213 | return backdrop; |
214 | } |
215 | |
216 | const int Br = rgba_getr(backdrop); |
217 | const int Bg = rgba_getg(backdrop); |
218 | const int Bb = rgba_getb(backdrop); |
219 | const int Ba = rgba_geta(backdrop); |
220 | |
221 | const int Sr = rgba_getr(src); |
222 | const int Sg = rgba_getg(src); |
223 | const int Sb = rgba_getb(src); |
224 | int Sa = rgba_geta(src); |
225 | Sa = MUL_UN8(Sa, opacity, t); |
226 | |
227 | // Ra = Sa + Ba*(1-Sa) |
228 | // = Sa + Ba - Ba*Sa |
229 | const int Ra = Sa + Ba - MUL_UN8(Ba, Sa, t); |
230 | |
231 | // Ra = Sa + Ba*(1-Sa) |
232 | // Ba = (Ra-Sa) / (1-Sa) |
233 | // Rc = (Sc*Sa + Bc*Ba*(1-Sa)) / Ra Replacing Ba with (Ra-Sa) / (1-Sa)... |
234 | // = (Sc*Sa + Bc*(Ra-Sa)/(1-Sa)*(1-Sa)) / Ra |
235 | // = (Sc*Sa + Bc*(Ra-Sa)) / Ra |
236 | // = Sc*Sa/Ra + Bc*Ra/Ra - Bc*Sa/Ra |
237 | // = Sc*Sa/Ra + Bc - Bc*Sa/Ra |
238 | // = Bc + (Sc-Bc)*Sa/Ra |
239 | const int Rr = Br + (Sr-Br) * Sa / Ra; |
240 | const int Rg = Bg + (Sg-Bg) * Sa / Ra; |
241 | const int Rb = Bb + (Sb-Bb) * Sa / Ra; |
242 | |
243 | return rgba(Rr, Rg, Rb, Ra); |
244 | } |
245 | |
246 | color_t rgba_blender_normal_dst_over(color_t backdrop, color_t src, int opacity) |
247 | { |
248 | int t; |
249 | int Sa = MUL_UN8(rgba_geta(src), opacity, t); |
250 | src = (src & rgba_rgb_mask) | (Sa << rgba_a_shift); |
251 | return rgba_blender_normal(src, backdrop); |
252 | } |
253 | |
254 | color_t rgba_blender_multiply(color_t backdrop, color_t src, int opacity) |
255 | { |
256 | int t; |
257 | int r = blend_multiply(rgba_getr(backdrop), rgba_getr(src), t); |
258 | int g = blend_multiply(rgba_getg(backdrop), rgba_getg(src), t); |
259 | int b = blend_multiply(rgba_getb(backdrop), rgba_getb(src), t); |
260 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
261 | return rgba_blender_normal(backdrop, src, opacity); |
262 | } |
263 | |
264 | color_t rgba_blender_screen(color_t backdrop, color_t src, int opacity) |
265 | { |
266 | int t; |
267 | int r = blend_screen(rgba_getr(backdrop), rgba_getr(src), t); |
268 | int g = blend_screen(rgba_getg(backdrop), rgba_getg(src), t); |
269 | int b = blend_screen(rgba_getb(backdrop), rgba_getb(src), t); |
270 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
271 | return rgba_blender_normal(backdrop, src, opacity); |
272 | } |
273 | |
274 | color_t rgba_blender_overlay(color_t backdrop, color_t src, int opacity) |
275 | { |
276 | int t; |
277 | int r = blend_overlay(rgba_getr(backdrop), rgba_getr(src), t); |
278 | int g = blend_overlay(rgba_getg(backdrop), rgba_getg(src), t); |
279 | int b = blend_overlay(rgba_getb(backdrop), rgba_getb(src), t); |
280 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
281 | return rgba_blender_normal(backdrop, src, opacity); |
282 | } |
283 | |
284 | color_t rgba_blender_darken(color_t backdrop, color_t src, int opacity) |
285 | { |
286 | int r = blend_darken(rgba_getr(backdrop), rgba_getr(src)); |
287 | int g = blend_darken(rgba_getg(backdrop), rgba_getg(src)); |
288 | int b = blend_darken(rgba_getb(backdrop), rgba_getb(src)); |
289 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
290 | return rgba_blender_normal(backdrop, src, opacity); |
291 | } |
292 | |
293 | color_t rgba_blender_lighten(color_t backdrop, color_t src, int opacity) |
294 | { |
295 | int r = blend_lighten(rgba_getr(backdrop), rgba_getr(src)); |
296 | int g = blend_lighten(rgba_getg(backdrop), rgba_getg(src)); |
297 | int b = blend_lighten(rgba_getb(backdrop), rgba_getb(src)); |
298 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
299 | return rgba_blender_normal(backdrop, src, opacity); |
300 | } |
301 | |
302 | color_t rgba_blender_color_dodge(color_t backdrop, color_t src, int opacity) |
303 | { |
304 | int r = blend_color_dodge(rgba_getr(backdrop), rgba_getr(src)); |
305 | int g = blend_color_dodge(rgba_getg(backdrop), rgba_getg(src)); |
306 | int b = blend_color_dodge(rgba_getb(backdrop), rgba_getb(src)); |
307 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
308 | return rgba_blender_normal(backdrop, src, opacity); |
309 | } |
310 | |
311 | color_t rgba_blender_color_burn(color_t backdrop, color_t src, int opacity) |
312 | { |
313 | int r = blend_color_burn(rgba_getr(backdrop), rgba_getr(src)); |
314 | int g = blend_color_burn(rgba_getg(backdrop), rgba_getg(src)); |
315 | int b = blend_color_burn(rgba_getb(backdrop), rgba_getb(src)); |
316 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
317 | return rgba_blender_normal(backdrop, src, opacity); |
318 | } |
319 | |
320 | color_t rgba_blender_hard_light(color_t backdrop, color_t src, int opacity) |
321 | { |
322 | int t; |
323 | int r = blend_hard_light(rgba_getr(backdrop), rgba_getr(src), t); |
324 | int g = blend_hard_light(rgba_getg(backdrop), rgba_getg(src), t); |
325 | int b = blend_hard_light(rgba_getb(backdrop), rgba_getb(src), t); |
326 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
327 | return rgba_blender_normal(backdrop, src, opacity); |
328 | } |
329 | |
330 | color_t rgba_blender_soft_light(color_t backdrop, color_t src, int opacity) |
331 | { |
332 | int r = blend_soft_light(rgba_getr(backdrop), rgba_getr(src)); |
333 | int g = blend_soft_light(rgba_getg(backdrop), rgba_getg(src)); |
334 | int b = blend_soft_light(rgba_getb(backdrop), rgba_getb(src)); |
335 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
336 | return rgba_blender_normal(backdrop, src, opacity); |
337 | } |
338 | |
339 | color_t rgba_blender_difference(color_t backdrop, color_t src, int opacity) |
340 | { |
341 | int r = blend_difference(rgba_getr(backdrop), rgba_getr(src)); |
342 | int g = blend_difference(rgba_getg(backdrop), rgba_getg(src)); |
343 | int b = blend_difference(rgba_getb(backdrop), rgba_getb(src)); |
344 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
345 | return rgba_blender_normal(backdrop, src, opacity); |
346 | } |
347 | |
348 | color_t rgba_blender_exclusion(color_t backdrop, color_t src, int opacity) |
349 | { |
350 | int t; |
351 | int r = blend_exclusion(rgba_getr(backdrop), rgba_getr(src), t); |
352 | int g = blend_exclusion(rgba_getg(backdrop), rgba_getg(src), t); |
353 | int b = blend_exclusion(rgba_getb(backdrop), rgba_getb(src), t); |
354 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
355 | return rgba_blender_normal(backdrop, src, opacity); |
356 | } |
357 | |
358 | ////////////////////////////////////////////////////////////////////// |
359 | // HSV blenders |
360 | |
361 | static double lum(double r, double g, double b) |
362 | { |
363 | return 0.3*r + 0.59*g + 0.11*b; |
364 | } |
365 | |
366 | static double sat(double r, double g, double b) |
367 | { |
368 | return std::max(r, std::max(g, b)) - std::min(r, std::min(g, b)); |
369 | } |
370 | |
371 | static void clip_color(double& r, double& g, double& b) |
372 | { |
373 | double l = lum(r, g, b); |
374 | double n = std::min(r, std::min(g, b)); |
375 | double x = std::max(r, std::max(g, b)); |
376 | |
377 | if (n < 0) { |
378 | r = l + (((r - l) * l) / (l - n)); |
379 | g = l + (((g - l) * l) / (l - n)); |
380 | b = l + (((b - l) * l) / (l - n)); |
381 | } |
382 | |
383 | if (x > 1) { |
384 | r = l + (((r - l) * (1 - l)) / (x - l)); |
385 | g = l + (((g - l) * (1 - l)) / (x - l)); |
386 | b = l + (((b - l) * (1 - l)) / (x - l)); |
387 | } |
388 | } |
389 | |
390 | static void set_lum(double& r, double& g, double& b, double l) |
391 | { |
392 | double d = l - lum(r, g, b); |
393 | r += d; |
394 | g += d; |
395 | b += d; |
396 | clip_color(r, g, b); |
397 | } |
398 | |
399 | // TODO replace this with a better impl (and test this, not sure if it's correct) |
400 | static void set_sat(double& r, double& g, double& b, double s) |
401 | { |
402 | #undef MIN |
403 | #undef MAX |
404 | #undef MID |
405 | #define MIN(x,y) (((x) < (y)) ? (x) : (y)) |
406 | #define MAX(x,y) (((x) > (y)) ? (x) : (y)) |
407 | #define MID(x,y,z) ((x) > (y) ? ((y) > (z) ? (y) : ((x) > (z) ? \ |
408 | (z) : (x))) : ((y) > (z) ? ((z) > (x) ? (z) : \ |
409 | (x)): (y))) |
410 | |
411 | double& min = MIN(r, MIN(g, b)); |
412 | double& mid = MID(r, g, b); |
413 | double& max = MAX(r, MAX(g, b)); |
414 | |
415 | if (max > min) { |
416 | mid = ((mid - min)*s) / (max - min); |
417 | max = s; |
418 | } |
419 | else |
420 | mid = max = 0; |
421 | |
422 | min = 0; |
423 | } |
424 | |
425 | color_t rgba_blender_hsl_hue(color_t backdrop, color_t src, int opacity) |
426 | { |
427 | double r = rgba_getr(backdrop)/255.0; |
428 | double g = rgba_getg(backdrop)/255.0; |
429 | double b = rgba_getb(backdrop)/255.0; |
430 | double s = sat(r, g, b); |
431 | double l = lum(r, g, b); |
432 | |
433 | r = rgba_getr(src)/255.0; |
434 | g = rgba_getg(src)/255.0; |
435 | b = rgba_getb(src)/255.0; |
436 | |
437 | set_sat(r, g, b, s); |
438 | set_lum(r, g, b, l); |
439 | |
440 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
441 | return rgba_blender_normal(backdrop, src, opacity); |
442 | } |
443 | |
444 | color_t rgba_blender_hsl_saturation(color_t backdrop, color_t src, int opacity) |
445 | { |
446 | double r = rgba_getr(src)/255.0; |
447 | double g = rgba_getg(src)/255.0; |
448 | double b = rgba_getb(src)/255.0; |
449 | double s = sat(r, g, b); |
450 | |
451 | r = rgba_getr(backdrop)/255.0; |
452 | g = rgba_getg(backdrop)/255.0; |
453 | b = rgba_getb(backdrop)/255.0; |
454 | double l = lum(r, g, b); |
455 | |
456 | set_sat(r, g, b, s); |
457 | set_lum(r, g, b, l); |
458 | |
459 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
460 | return rgba_blender_normal(backdrop, src, opacity); |
461 | } |
462 | |
463 | color_t rgba_blender_hsl_color(color_t backdrop, color_t src, int opacity) |
464 | { |
465 | double r = rgba_getr(backdrop)/255.0; |
466 | double g = rgba_getg(backdrop)/255.0; |
467 | double b = rgba_getb(backdrop)/255.0; |
468 | double l = lum(r, g, b); |
469 | |
470 | r = rgba_getr(src)/255.0; |
471 | g = rgba_getg(src)/255.0; |
472 | b = rgba_getb(src)/255.0; |
473 | |
474 | set_lum(r, g, b, l); |
475 | |
476 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
477 | return rgba_blender_normal(backdrop, src, opacity); |
478 | } |
479 | |
480 | color_t rgba_blender_hsl_luminosity(color_t backdrop, color_t src, int opacity) |
481 | { |
482 | double r = rgba_getr(src)/255.0; |
483 | double g = rgba_getg(src)/255.0; |
484 | double b = rgba_getb(src)/255.0; |
485 | double l = lum(r, g, b); |
486 | |
487 | r = rgba_getr(backdrop)/255.0; |
488 | g = rgba_getg(backdrop)/255.0; |
489 | b = rgba_getb(backdrop)/255.0; |
490 | |
491 | set_lum(r, g, b, l); |
492 | |
493 | src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask); |
494 | return rgba_blender_normal(backdrop, src, opacity); |
495 | } |
496 | |
497 | color_t rgba_blender_addition(color_t backdrop, color_t src, int opacity) |
498 | { |
499 | int r = rgba_getr(backdrop) + rgba_getr(src); |
500 | int g = rgba_getg(backdrop) + rgba_getg(src); |
501 | int b = rgba_getb(backdrop) + rgba_getb(src); |
502 | src = rgba(std::min(r, 255), |
503 | std::min(g, 255), |
504 | std::min(b, 255), 0) | (src & rgba_a_mask); |
505 | return rgba_blender_normal(backdrop, src, opacity); |
506 | } |
507 | |
508 | color_t rgba_blender_subtract(color_t backdrop, color_t src, int opacity) |
509 | { |
510 | int r = rgba_getr(backdrop) - rgba_getr(src); |
511 | int g = rgba_getg(backdrop) - rgba_getg(src); |
512 | int b = rgba_getb(backdrop) - rgba_getb(src); |
513 | src = rgba(MAX(r, 0), MAX(g, 0), MAX(b, 0), 0) | (src & rgba_a_mask); |
514 | return rgba_blender_normal(backdrop, src, opacity); |
515 | } |
516 | |
517 | color_t rgba_blender_divide(color_t backdrop, color_t src, int opacity) |
518 | { |
519 | int r = blend_divide(rgba_getr(backdrop), rgba_getr(src)); |
520 | int g = blend_divide(rgba_getg(backdrop), rgba_getg(src)); |
521 | int b = blend_divide(rgba_getb(backdrop), rgba_getb(src)); |
522 | src = rgba(r, g, b, 0) | (src & rgba_a_mask); |
523 | return rgba_blender_normal(backdrop, src, opacity); |
524 | } |
525 | |
526 | // New Blender Methods: |
527 | RGBA_BLENDER_N(multiply) |
528 | RGBA_BLENDER_N(screen) |
529 | RGBA_BLENDER_N(overlay) |
530 | RGBA_BLENDER_N(darken) |
531 | RGBA_BLENDER_N(lighten) |
532 | RGBA_BLENDER_N(color_dodge) |
533 | RGBA_BLENDER_N(color_burn) |
534 | RGBA_BLENDER_N(hard_light) |
535 | RGBA_BLENDER_N(soft_light) |
536 | RGBA_BLENDER_N(difference) |
537 | RGBA_BLENDER_N(exclusion) |
538 | RGBA_BLENDER_N(hsl_color) |
539 | RGBA_BLENDER_N(hsl_hue) |
540 | RGBA_BLENDER_N(hsl_saturation) |
541 | RGBA_BLENDER_N(hsl_luminosity) |
542 | RGBA_BLENDER_N(addition) |
543 | RGBA_BLENDER_N(subtract) |
544 | RGBA_BLENDER_N(divide) |
545 | |
546 | ////////////////////////////////////////////////////////////////////// |
547 | // GRAY blenders |
548 | |
549 | color_t graya_blender_src(color_t backdrop, color_t src, int opacity) |
550 | { |
551 | return src; |
552 | } |
553 | |
554 | color_t graya_blender_merge(color_t backdrop, color_t src, int opacity) |
555 | { |
556 | int Bk, Ba; |
557 | int Sk, Sa; |
558 | int Rk, Ra; |
559 | int t; |
560 | |
561 | Bk = graya_getv(backdrop); |
562 | Ba = graya_geta(backdrop); |
563 | |
564 | Sk = graya_getv(src); |
565 | Sa = graya_geta(src); |
566 | |
567 | if (Ba == 0) { |
568 | Rk = Sk; |
569 | } |
570 | else if (Sa == 0) { |
571 | Rk = Bk; |
572 | } |
573 | else { |
574 | Rk = Bk + MUL_UN8((Sk-Bk), opacity, t); |
575 | } |
576 | Ra = Ba + MUL_UN8((Sa-Ba), opacity, t); |
577 | if (Ra == 0) |
578 | Rk = 0; |
579 | |
580 | return graya(Rk, Ra); |
581 | } |
582 | |
583 | color_t graya_blender_neg_bw(color_t backdrop, color_t src, int opacity) |
584 | { |
585 | if ((backdrop & graya_a_mask) == 0) |
586 | return src; |
587 | else if (graya_getv(backdrop) < 128) |
588 | return graya(255, 255); |
589 | else |
590 | return graya(0, 255); |
591 | } |
592 | |
593 | color_t graya_blender_normal(color_t backdrop, color_t src, int opacity) |
594 | { |
595 | int t; |
596 | |
597 | if (!(backdrop & graya_a_mask)) { |
598 | int a = graya_geta(src); |
599 | a = MUL_UN8(a, opacity, t); |
600 | a <<= graya_a_shift; |
601 | return (src & 0xff) | a; |
602 | } |
603 | else if (!(src & graya_a_mask)) |
604 | return backdrop; |
605 | |
606 | int Bg, Ba; |
607 | int Sg, Sa; |
608 | int Rg, Ra; |
609 | |
610 | Bg = graya_getv(backdrop); |
611 | Ba = graya_geta(backdrop); |
612 | |
613 | Sg = graya_getv(src); |
614 | Sa = graya_geta(src); |
615 | Sa = MUL_UN8(Sa, opacity, t); |
616 | |
617 | Ra = Ba + Sa - MUL_UN8(Ba, Sa, t); |
618 | Rg = Bg + (Sg-Bg) * Sa / Ra; |
619 | |
620 | return graya(Rg, Ra); |
621 | } |
622 | |
623 | color_t graya_blender_normal_dst_over(color_t backdrop, color_t src, int opacity) |
624 | { |
625 | int t; |
626 | int Sa = MUL_UN8(graya_geta(src), opacity, t); |
627 | src = (src & graya_v_mask) | (Sa << graya_a_shift); |
628 | return graya_blender_normal(src, backdrop); |
629 | } |
630 | |
631 | color_t graya_blender_multiply(color_t backdrop, color_t src, int opacity) |
632 | { |
633 | int t; |
634 | int v = blend_multiply(graya_getv(backdrop), graya_getv(src), t); |
635 | src = graya(v, 0) | (src & graya_a_mask); |
636 | return graya_blender_normal(backdrop, src, opacity); |
637 | } |
638 | |
639 | color_t graya_blender_screen(color_t backdrop, color_t src, int opacity) |
640 | { |
641 | int t; |
642 | int v = blend_screen(graya_getv(backdrop), graya_getv(src), t); |
643 | src = graya(v, 0) | (src & graya_a_mask); |
644 | return graya_blender_normal(backdrop, src, opacity); |
645 | } |
646 | |
647 | color_t graya_blender_overlay(color_t backdrop, color_t src, int opacity) |
648 | { |
649 | int t; |
650 | int v = blend_overlay(graya_getv(backdrop), graya_getv(src), t); |
651 | src = graya(v, 0) | (src & graya_a_mask); |
652 | return graya_blender_normal(backdrop, src, opacity); |
653 | } |
654 | |
655 | color_t graya_blender_darken(color_t backdrop, color_t src, int opacity) |
656 | { |
657 | int v = blend_darken(graya_getv(backdrop), graya_getv(src)); |
658 | src = graya(v, 0) | (src & graya_a_mask); |
659 | return graya_blender_normal(backdrop, src, opacity); |
660 | } |
661 | |
662 | color_t graya_blender_lighten(color_t backdrop, color_t src, int opacity) |
663 | { |
664 | int v = blend_lighten(graya_getv(backdrop), graya_getv(src)); |
665 | src = graya(v, 0) | (src & graya_a_mask); |
666 | return graya_blender_normal(backdrop, src, opacity); |
667 | } |
668 | |
669 | color_t graya_blender_color_dodge(color_t backdrop, color_t src, int opacity) |
670 | { |
671 | int v = blend_color_dodge(graya_getv(backdrop), graya_getv(src)); |
672 | src = graya(v, 0) | (src & graya_a_mask); |
673 | return graya_blender_normal(backdrop, src, opacity); |
674 | } |
675 | |
676 | color_t graya_blender_color_burn(color_t backdrop, color_t src, int opacity) |
677 | { |
678 | int v = blend_color_burn(graya_getv(backdrop), graya_getv(src)); |
679 | src = graya(v, 0) | (src & graya_a_mask); |
680 | return graya_blender_normal(backdrop, src, opacity); |
681 | } |
682 | |
683 | color_t graya_blender_hard_light(color_t backdrop, color_t src, int opacity) |
684 | { |
685 | int t; |
686 | int v = blend_hard_light(graya_getv(backdrop), graya_getv(src), t); |
687 | src = graya(v, 0) | (src & graya_a_mask); |
688 | return graya_blender_normal(backdrop, src, opacity); |
689 | } |
690 | |
691 | color_t graya_blender_soft_light(color_t backdrop, color_t src, int opacity) |
692 | { |
693 | int v = blend_soft_light(graya_getv(backdrop), graya_getv(src)); |
694 | src = graya(v, 0) | (src & graya_a_mask); |
695 | return graya_blender_normal(backdrop, src, opacity); |
696 | } |
697 | |
698 | color_t graya_blender_difference(color_t backdrop, color_t src, int opacity) |
699 | { |
700 | int v = blend_difference(graya_getv(backdrop), graya_getv(src)); |
701 | src = graya(v, 0) | (src & graya_a_mask); |
702 | return graya_blender_normal(backdrop, src, opacity); |
703 | } |
704 | |
705 | color_t graya_blender_exclusion(color_t backdrop, color_t src, int opacity) |
706 | { |
707 | int t; |
708 | int v = blend_exclusion(graya_getv(backdrop), graya_getv(src), t); |
709 | src = graya(v, 0) | (src & graya_a_mask); |
710 | return graya_blender_normal(backdrop, src, opacity); |
711 | } |
712 | |
713 | color_t graya_blender_addition(color_t backdrop, color_t src, int opacity) |
714 | { |
715 | int v = graya_getv(backdrop) + graya_getv(src); |
716 | src = graya(std::min(v, 255), 0) | (src & graya_a_mask); |
717 | return graya_blender_normal(backdrop, src, opacity); |
718 | } |
719 | |
720 | color_t graya_blender_subtract(color_t backdrop, color_t src, int opacity) |
721 | { |
722 | int v = graya_getv(backdrop) - graya_getv(src); |
723 | src = graya(std::max(v, 0), 0) | (src & graya_a_mask); |
724 | return graya_blender_normal(backdrop, src, opacity); |
725 | } |
726 | |
727 | color_t graya_blender_divide(color_t backdrop, color_t src, int opacity) |
728 | { |
729 | int v = blend_divide(graya_getv(backdrop), graya_getv(src)); |
730 | src = graya(v, 0) | (src & graya_a_mask); |
731 | return graya_blender_normal(backdrop, src, opacity); |
732 | } |
733 | |
734 | GRAYA_BLENDER_N(multiply) |
735 | GRAYA_BLENDER_N(screen) |
736 | GRAYA_BLENDER_N(overlay) |
737 | GRAYA_BLENDER_N(darken) |
738 | GRAYA_BLENDER_N(lighten) |
739 | GRAYA_BLENDER_N(color_dodge) |
740 | GRAYA_BLENDER_N(color_burn) |
741 | GRAYA_BLENDER_N(hard_light) |
742 | GRAYA_BLENDER_N(soft_light) |
743 | GRAYA_BLENDER_N(difference) |
744 | GRAYA_BLENDER_N(exclusion) |
745 | GRAYA_BLENDER_N(addition) |
746 | GRAYA_BLENDER_N(subtract) |
747 | GRAYA_BLENDER_N(divide) |
748 | |
749 | ////////////////////////////////////////////////////////////////////// |
750 | // indexed |
751 | |
752 | color_t indexed_blender_src(color_t dst, color_t src, int opacity) |
753 | { |
754 | return src; |
755 | } |
756 | |
757 | ////////////////////////////////////////////////////////////////////// |
758 | // getters |
759 | |
760 | BlendFunc get_rgba_blender(BlendMode blendmode, const bool newBlend) |
761 | { |
762 | switch (blendmode) { |
763 | case BlendMode::SRC: return rgba_blender_src; |
764 | case BlendMode::MERGE: return rgba_blender_merge; |
765 | case BlendMode::NEG_BW: return rgba_blender_neg_bw; |
766 | case BlendMode::RED_TINT: return rgba_blender_red_tint; |
767 | case BlendMode::BLUE_TINT: return rgba_blender_blue_tint; |
768 | case BlendMode::DST_OVER: return rgba_blender_normal_dst_over; |
769 | |
770 | case BlendMode::NORMAL: return rgba_blender_normal; |
771 | case BlendMode::MULTIPLY: return newBlend? rgba_blender_multiply_n: rgba_blender_multiply; |
772 | case BlendMode::SCREEN: return newBlend? rgba_blender_screen_n: rgba_blender_screen; |
773 | case BlendMode::OVERLAY: return newBlend? rgba_blender_overlay_n: rgba_blender_overlay; |
774 | case BlendMode::DARKEN: return newBlend? rgba_blender_darken_n: rgba_blender_darken; |
775 | case BlendMode::LIGHTEN: return newBlend? rgba_blender_lighten_n: rgba_blender_lighten; |
776 | case BlendMode::COLOR_DODGE: return newBlend? rgba_blender_color_dodge_n: rgba_blender_color_dodge; |
777 | case BlendMode::COLOR_BURN: return newBlend? rgba_blender_color_burn_n: rgba_blender_color_burn; |
778 | case BlendMode::HARD_LIGHT: return newBlend? rgba_blender_hard_light_n: rgba_blender_hard_light; |
779 | case BlendMode::SOFT_LIGHT: return newBlend? rgba_blender_soft_light_n: rgba_blender_soft_light; |
780 | case BlendMode::DIFFERENCE: return newBlend? rgba_blender_difference_n: rgba_blender_difference; |
781 | case BlendMode::EXCLUSION: return newBlend? rgba_blender_exclusion_n: rgba_blender_exclusion; |
782 | case BlendMode::HSL_HUE: return newBlend? rgba_blender_hsl_hue_n: rgba_blender_hsl_hue; |
783 | case BlendMode::HSL_SATURATION: return newBlend? rgba_blender_hsl_saturation_n: rgba_blender_hsl_saturation; |
784 | case BlendMode::HSL_COLOR: return newBlend? rgba_blender_hsl_color_n: rgba_blender_hsl_color; |
785 | case BlendMode::HSL_LUMINOSITY: return newBlend? rgba_blender_hsl_luminosity_n: rgba_blender_hsl_luminosity; |
786 | case BlendMode::ADDITION: return newBlend? rgba_blender_addition_n: rgba_blender_addition; |
787 | case BlendMode::SUBTRACT: return newBlend? rgba_blender_subtract_n: rgba_blender_subtract; |
788 | case BlendMode::DIVIDE: return newBlend? rgba_blender_divide_n: rgba_blender_divide; |
789 | } |
790 | ASSERT(false); |
791 | return rgba_blender_src; |
792 | } |
793 | |
794 | BlendFunc get_graya_blender(BlendMode blendmode, const bool newBlend) |
795 | { |
796 | switch (blendmode) { |
797 | case BlendMode::SRC: return graya_blender_src; |
798 | case BlendMode::MERGE: return graya_blender_merge; |
799 | case BlendMode::NEG_BW: return graya_blender_neg_bw; |
800 | case BlendMode::RED_TINT: return graya_blender_normal; |
801 | case BlendMode::BLUE_TINT: return graya_blender_normal; |
802 | case BlendMode::DST_OVER: return graya_blender_normal_dst_over; |
803 | |
804 | case BlendMode::NORMAL: return graya_blender_normal; |
805 | case BlendMode::MULTIPLY: return newBlend? graya_blender_multiply_n: graya_blender_multiply; |
806 | case BlendMode::SCREEN: return newBlend? graya_blender_screen_n: graya_blender_screen; |
807 | case BlendMode::OVERLAY: return newBlend? graya_blender_overlay_n: graya_blender_overlay; |
808 | case BlendMode::DARKEN: return newBlend? graya_blender_darken_n: graya_blender_darken; |
809 | case BlendMode::LIGHTEN: return newBlend? graya_blender_lighten_n: graya_blender_lighten; |
810 | case BlendMode::COLOR_DODGE: return newBlend? graya_blender_color_dodge_n: graya_blender_color_dodge; |
811 | case BlendMode::COLOR_BURN: return newBlend? graya_blender_color_burn_n: graya_blender_color_burn; |
812 | case BlendMode::HARD_LIGHT: return newBlend? graya_blender_hard_light_n: graya_blender_hard_light; |
813 | case BlendMode::SOFT_LIGHT: return newBlend? graya_blender_soft_light_n: graya_blender_soft_light; |
814 | case BlendMode::DIFFERENCE: return newBlend? graya_blender_difference_n: graya_blender_difference; |
815 | case BlendMode::EXCLUSION: return newBlend? graya_blender_exclusion_n: graya_blender_exclusion; |
816 | case BlendMode::HSL_HUE: return graya_blender_normal; |
817 | case BlendMode::HSL_SATURATION: return graya_blender_normal; |
818 | case BlendMode::HSL_COLOR: return graya_blender_normal; |
819 | case BlendMode::HSL_LUMINOSITY: return graya_blender_normal; |
820 | case BlendMode::ADDITION: return newBlend? graya_blender_exclusion_n: graya_blender_addition; |
821 | case BlendMode::SUBTRACT: return newBlend? graya_blender_subtract_n: graya_blender_subtract; |
822 | case BlendMode::DIVIDE: return newBlend? graya_blender_divide_n: graya_blender_divide; |
823 | } |
824 | ASSERT(false); |
825 | return graya_blender_src; |
826 | } |
827 | |
828 | BlendFunc get_indexed_blender(BlendMode blendmode, const bool newBlend) |
829 | { |
830 | return indexed_blender_src; |
831 | } |
832 | |
833 | } // namespace doc |
834 | |