1 /* === S Y N F I G ========================================================= */
2 /*! \file
3 ** \brief Color blending function specializations for CairoColor
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007, 2008 Chris Moore
10 ** Copyright (c) 2012-2013 Carlos López
11 ** Copyright (c) 2015 Diego Barrios Romero
12 **
13 ** This package is free software; you can redistribute it and/or
14 ** modify it under the terms of the GNU General Public License as
15 ** published by the Free Software Foundation; either version 2 of
16 ** the License, or (at your option) any later version.
17 **
18 ** This package is distributed in the hope that it will be useful,
19 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ** General Public License for more details.
22 ** \endlegal
23 */
24 /* ========================================================================= */
25
26 #ifndef __SYNFIG_COLOR_CAIROCOLORBLENDINGFUNCTIONS_H
27 #define __SYNFIG_COLOR_CAIROCOLORBLENDINGFUNCTIONS_H
28
29 #include "colorblendingfunctions.h"
30
31 namespace synfig {
32
33 typedef CairoColor (*cairoblendfunc)(CairoColor&, CairoColor&, float);
34
35 template <>
36 CairoColor
blendfunc_COMPOSITE(CairoColor & a,CairoColor & b,float amount)37 blendfunc_COMPOSITE(CairoColor &a, CairoColor &b, float amount)
38 {
39 int ra, ga, ba, aa;
40 int rb, gb, bb, ab;
41 int rc, gc, bc;
42 float ac;
43
44 float faa, fab, A, AA;
45
46 ra=a.get_r();
47 ga=a.get_g();
48 ba=a.get_b();
49 aa=a.get_a();
50 aa=aa*amount;
51 A=aa/255.0;
52 AA=1.0-A;
53
54 rb=b.get_r();
55 gb=b.get_g();
56 bb=b.get_b();
57 ab=b.get_a();
58
59 ac=aa+ab*AA;
60 if(fabsf(ac)<COLOR_EPSILON)
61 return CairoColor::alpha();
62
63 faa=aa/ac;
64 fab=ab*AA/ac;
65
66 rc=ra*faa+rb*fab;
67 gc=ga*faa+gb*fab;
68 bc=ba*faa+bb*fab;
69
70 return CairoColor(rc, gc, bc, ac);
71
72 }
73
74 template <>
75 CairoColor
blendfunc_STRAIGHT(CairoColor & a,CairoColor & b,float amount)76 blendfunc_STRAIGHT(CairoColor &a, CairoColor &b, float amount)
77 {
78 int ra, ga, ba, aa; //src
79 int rb, gb, bb, ab; //bg
80 int rc, gc, bc;
81 float ac; //out
82
83 ra=a.get_r();
84 ga=a.get_g();
85 ba=a.get_b();
86 aa=a.get_a();
87
88 rb=b.get_r();
89 gb=b.get_g();
90 bb=b.get_b();
91 ab=b.get_a();
92
93 ac=(aa-ab)*amount+ab;
94
95 // if ac!=0.0
96 if(fabsf(ac)>COLOR_EPSILON)
97 {
98 rc= ((ra*aa-rb*ab)*amount + rb*ab)/ac;
99 gc= ((ga*aa-gb*ab)*amount + gb*ab)/ac;
100 bc= ((ba*aa-bb*ab)*amount + bb*ab)/ac;
101 return CairoColor(rc, gc, bc, ac);
102 }
103 else
104 return CairoColor::alpha();
105 }
106
107 template <>
108 CairoColor
blendfunc_ONTO(CairoColor & a,CairoColor & b,float amount)109 blendfunc_ONTO(CairoColor &a, CairoColor &b, float amount)
110 {
111 unsigned char alpha(b.get_a());
112 return blendfunc_COMPOSITE(a,b.set_a(255),amount).set_a(alpha);
113 }
114
115 template <>
116 CairoColor
blendfunc_STRAIGHT_ONTO(CairoColor & a,CairoColor & b,float amount)117 blendfunc_STRAIGHT_ONTO(CairoColor &a, CairoColor &b, float amount)
118 {
119 a.set_a(a.get_a()*b.get_a()/255.0);
120 return CairoColor::blend(a, b, amount, Color::BLEND_STRAIGHT);
121 }
122
123 template <>
124 CairoColor
blendfunc_BRIGHTEN(CairoColor & a,CairoColor & b,float amount)125 blendfunc_BRIGHTEN(CairoColor &a, CairoColor &b, float amount)
126 {
127 int ra, ga, ba, aa;
128 int rb, gb, bb, ab;
129 int rc, gc, bc, ac;
130
131 ra=a.get_r();
132 ga=a.get_g();
133 ba=a.get_b();
134 aa=a.get_a();
135
136 rb=b.get_r();
137 gb=b.get_g();
138 bb=b.get_b();
139 ab=b.get_a();
140
141 const float alpha = aa*amount/255.0;
142 const int raab(ra*alpha);
143 const int gaab(ga*alpha);
144 const int baab(ba*alpha);
145
146 if(rb<raab)
147 rc=raab;
148 else
149 rc=rb;
150
151 if(gb<gaab)
152 gc=gaab;
153 else
154 gc=gb;
155
156 if(bb<baab)
157 bc=baab;
158 else
159 bc=bb;
160
161 ac=ab;
162
163 return CairoColor(rc, gc, bc, ac);
164 }
165
166 template <>
167 CairoColor
blendfunc_DARKEN(CairoColor & a,CairoColor & b,float amount)168 blendfunc_DARKEN(CairoColor &a, CairoColor &b, float amount)
169 {
170 int ra, ga, ba, aa;
171 int rb, gb, bb, ab;
172 int rc, gc, bc, ac;
173
174 ra=a.get_r();
175 ga=a.get_g();
176 ba=a.get_b();
177 aa=a.get_a();
178
179 rb=b.get_r();
180 gb=b.get_g();
181 bb=b.get_b();
182 ab=b.get_a();
183
184 const float alpha=aa*amount/255.0;
185
186 int rcompare=(ra-255)*alpha+255;
187 if(rb > rcompare)
188 rc=rcompare;
189 else
190 rc=rb;
191
192 int gcompare=(ga-255)*alpha+255;
193 if(gb > gcompare)
194 gc=gcompare;
195 else
196 gc=gb;
197
198 int bcompare=(ba-255)*alpha+255;
199 if(bb > bcompare)
200 bc=bcompare;
201 else
202 bc=bb;
203
204 ac=ab;
205
206 return CairoColor(rc, gc, bc, ac);
207 }
208
209 template <>
210 CairoColor
blendfunc_ADD(CairoColor & a,CairoColor & b,float amount)211 blendfunc_ADD(CairoColor &a, CairoColor &b, float amount)
212 {
213 int ra, ga, ba, aa;
214 int rb, gb, bb, ab;
215 int rc, gc, bc, ac;
216
217 ra=a.get_r();
218 ga=a.get_g();
219 ba=a.get_b();
220 aa=a.get_a();
221
222 rb=b.get_r();
223 gb=b.get_g();
224 bb=b.get_b();
225 ab=b.get_a();
226
227 const float aaa=aa*amount/255.0;
228
229 rc=rb+ra*aaa;
230 gc=gb+ga*aaa;
231 bc=bb+ba*aaa;
232 ac=ab;
233
234 return CairoColor(rc, gc, bc, ac);
235 }
236
237 //Specialization for CairoColor
238 template <>
239 CairoColor
blendfunc_SUBTRACT(CairoColor & a,CairoColor & b,float amount)240 blendfunc_SUBTRACT(CairoColor &a, CairoColor &b, float amount)
241 {
242 int ra, ga, ba, aa;
243 int rb, gb, bb, ab;
244 int rc, gc, bc, ac;
245
246 ra=a.get_r();
247 ga=a.get_g();
248 ba=a.get_b();
249 aa=a.get_a();
250
251 rb=b.get_r();
252 gb=b.get_g();
253 bb=b.get_b();
254 ab=b.get_a();
255
256 const float aaa=aa*amount/255.0;
257
258 rc=rb-ra*aaa;
259 gc=gb-ga*aaa;
260 bc=bb-ba*aaa;
261 ac=ab;
262
263 return CairoColor(rc, gc, bc, ac);
264 }
265
266 template <>
267 CairoColor
blendfunc_DIFFERENCE(CairoColor & a,CairoColor & b,float amount)268 blendfunc_DIFFERENCE(CairoColor &a, CairoColor &b, float amount)
269 {
270 int ra, ga, ba, aa;
271 int rb, gb, bb, ab;
272 int rc, gc, bc, ac;
273
274 ra=a.get_r();
275 ga=a.get_g();
276 ba=a.get_b();
277 aa=a.get_a();
278
279 rb=b.get_r();
280 gb=b.get_g();
281 bb=b.get_b();
282 ab=b.get_a();
283
284 const float aaa=aa*amount/255.0;
285
286 rc=std::abs(rb-ra*aaa);
287 gc=std::abs(gb-ga*aaa);
288 bc=std::abs(bb-ba*aaa);
289 ac=ab;
290
291 return CairoColor(rc, gc, bc, ac);
292 }
293
294 template <>
295 CairoColor
blendfunc_MULTIPLY(CairoColor & a,CairoColor & b,float amount)296 blendfunc_MULTIPLY(CairoColor &a,CairoColor &b, float amount)
297 {
298 if(amount<0) a=~a, amount=-amount;
299 amount*=a.get_a()/255.0;
300 int ra, ga, ba;
301 int rb, gb, bb;
302
303 ra=a.get_r();
304 ga=a.get_g();
305 ba=a.get_b();
306
307 rb=b.get_r();
308 gb=b.get_g();
309 bb=b.get_b();
310
311 b.set_r((rb*ra*amount/255.0)+rb*(1.0-amount));
312 b.set_g((gb*ga*amount/255.0)+gb*(1.0-amount));
313 b.set_b((bb*ba*amount/255.0)+bb*(1.0-amount));
314 return b;
315 }
316
317 template <>
318 CairoColor
blendfunc_DIVIDE(CairoColor & a,CairoColor & b,float amount)319 blendfunc_DIVIDE(CairoColor &a, CairoColor &b, float amount)
320 {
321 int ra, ga, ba, aa;
322 int rb, gb, bb, ab;
323 int rc, gc, bc, ac;
324
325 ra=a.get_r();
326 ga=a.get_g();
327 ba=a.get_b();
328 aa=a.get_a();
329
330 rb=b.get_r();
331 gb=b.get_g();
332 bb=b.get_b();
333 ab=b.get_a();
334
335 const float alpha=amount*aa/255.0;
336 const float ahpla=1.0-alpha;
337
338 if(alpha<COLOR_EPSILON)
339 return b;
340
341 ac=ab;
342 if(ra==0)
343 rc=rb;
344 else
345 rc=rb*(alpha*255)/(ra) + ahpla*rb;
346
347 if(ga==0)
348 gc=gb;
349 else
350 gc=gb*(alpha*255)/(ga) + ahpla*gb;
351
352 if(ba==0)
353 bc=bb;
354 else
355 bc=bb*(alpha*255)/(ba) + ahpla*bb;
356
357 return CairoColor(rc, gc, bc, ac);
358 }
359
360 template <>
361 CairoColor
blendfunc_COLOR(CairoColor & a,CairoColor & b,float amount)362 blendfunc_COLOR(CairoColor &a, CairoColor &b, float amount)
363 {
364 return CairoColor(Color::blend(Color(a), Color(b), amount, Color::BLEND_COLOR));
365 }
366
367 template <>
368 CairoColor
blendfunc_HUE(CairoColor & a,CairoColor & b,float amount)369 blendfunc_HUE(CairoColor &a, CairoColor &b, float amount)
370 {
371 return CairoColor(Color::blend(Color(a), Color(b), amount, Color::BLEND_HUE));
372 }
373
374 template <>
375 CairoColor
blendfunc_SATURATION(CairoColor & a,CairoColor & b,float amount)376 blendfunc_SATURATION(CairoColor &a, CairoColor &b, float amount)
377 {
378 return CairoColor(Color::blend(Color(a), Color(b), amount, Color::BLEND_SATURATION));
379 }
380
381 template <>
382 CairoColor
blendfunc_LUMINANCE(CairoColor & a,CairoColor & b,float amount)383 blendfunc_LUMINANCE(CairoColor &a, CairoColor &b, float amount)
384 {
385 return CairoColor(Color::blend(Color(a), Color(b), amount, Color::BLEND_LUMINANCE));
386 }
387
388 template <>
389 CairoColor
blendfunc_BEHIND(CairoColor & a,CairoColor & b,float amount)390 blendfunc_BEHIND(CairoColor &a, CairoColor &b, float amount)
391 {
392 a.set_a(a.get_a()*amount);
393 return CairoColor::blend(b, a, 1.0, Color::BLEND_COMPOSITE);
394 }
395
396 template <>
397 CairoColor
blendfunc_ALPHA_BRIGHTEN(CairoColor & a,CairoColor & b,float amount)398 blendfunc_ALPHA_BRIGHTEN(CairoColor &a, CairoColor &b, float amount)
399 {
400 // \todo can this be right, multiplying amount by *b*'s alpha?
401 // compare with blendfunc_BRIGHTEN where it is multiplied by *a*'s
402 //if(a.get_a() < b.get_a()*amount)
403 // return a.set_a(a.get_a()*amount);
404 //return b;
405 unsigned char ra, ga, ba, aa;
406 unsigned char ab;
407 unsigned char rc, gc, bc, ac;
408
409 ra=a.get_r();
410 ga=a.get_g();
411 ba=a.get_b();
412 aa=a.get_a();
413
414 ab=b.get_a();
415
416 ac=aa*amount;
417 if(aa < ab*amount)
418 {
419 float acaa=(aa*amount)/aa;
420 rc=ra*acaa;
421 gc=ga*acaa;
422 bc=ba*acaa;
423 return CairoColor(rc, gc, bc, ac);
424 }
425 else
426 return b;
427 }
428
429 template <>
430 CairoColor
blendfunc_ALPHA_DARKEN(CairoColor & a,CairoColor & b,float amount)431 blendfunc_ALPHA_DARKEN(CairoColor &a, CairoColor &b, float amount)
432 {
433 unsigned char ra, ga, ba, aa;
434 unsigned char ab;
435 unsigned char rc, gc, bc, ac;
436
437 ra=a.get_r();
438 ga=a.get_g();
439 ba=a.get_b();
440 aa=a.get_a();
441
442 ab=b.get_a();
443
444 ac=aa*amount;
445 if(ac > ab)
446 {
447 float acaa=(aa*amount)/aa;
448 rc=ra*acaa;
449 gc=ga*acaa;
450 bc=ba*acaa;
451 return CairoColor(rc, gc, bc, ac);
452 }
453 else
454 return b;
455 }
456
457 template <>
458 CairoColor
blendfunc_SCREEN(CairoColor & a,CairoColor & b,float amount)459 blendfunc_SCREEN(CairoColor &a, CairoColor &b, float amount)
460 {
461 if(amount<0) a=~a, amount=-amount;
462
463 a.set_r(255-(255-a.get_r())*(1.0-b.get_r()/255.0));
464 a.set_g(255-(255-a.get_g())*(1.0-b.get_g()/255.0));
465 a.set_b(255-(255-a.get_b())*(1.0-b.get_b()/255.0));
466
467 return blendfunc_ONTO(a,b,amount);
468 }
469
470 template <>
471 CairoColor
472 blendfunc_OVERLAY<CairoColor>(CairoColor &a,CairoColor &b,float amount)
473 {
474 if(amount<0) a=~a, amount=-amount;
475
476 int ra, ga, ba, aa, ras, gas, bas;
477 int rb, gb, bb, ab;
478
479 ra=a.get_r();
480 ras=ra*ra;
481 ga=a.get_g();
482 gas=ga*ga;
483 ba=a.get_b();
484 bas=ba*ba;
485 aa=a.get_a();
486
487 rb=b.get_r();
488 gb=b.get_g();
489 bb=b.get_b();
490 ab=b.get_a();
491
492
493 int rc, gc, bc, ac;
494
495 if(aa==0 || ab==0) return CairoColor();
496
497 rc=(2*rb*ra+ras-2*rb*ras/255.0)/255.0;
498 gc=(2*gb*ga+gas-2*gb*gas/255.0)/255.0;
499 bc=(2*bb*ba+bas-2*bb*bas/255.0)/255.0;
500 ac=aa;
501
502 return CairoColor::blend(CairoColor(rc, gc, bc, ac), b, amount, Color::BLEND_ONTO);
503 }
504
505 template <>
506 CairoColor
blendfunc_HARD_LIGHT(CairoColor & a,CairoColor & b,float amount)507 blendfunc_HARD_LIGHT(CairoColor &a, CairoColor &b, float amount)
508 {
509 if(amount<0) a=~a, amount=-amount;
510
511 int ra, ga, ba, aa;
512 int rb, gb, bb;
513 int rc, gc, bc;
514
515 ra=a.get_r();
516 ga=a.get_g();
517 ba=a.get_b();
518 aa=a.get_a();
519
520 rb=b.get_r();
521 gb=b.get_g();
522 bb=b.get_b();
523
524 if(ra>127) rc =255 - (255-(ra*2-255)) * (255-rb)/255.0;
525 else rc= rb*(ra*2)/255.0;
526 if(ga>127) gc =255 - (255-(ga*2-255)) * (255-gb)/255.0;
527 else gc= gb*(ga*2)/255.0;
528 if(ba>127) bc =255 - (255-(ba*2-255)) * (255-bb)/255.0;
529 else bc= bb*(ba*2)/255.0;
530
531 return CairoColor::blend(CairoColor(rc, gc, bc, aa),b,amount, Color::BLEND_ONTO);
532 //
533 // if(a.get_r()>half) a.set_r(one-(one-(a.get_r()*2*one-one))*(one-b.get_r()));
534 // else a.set_r(b.get_r()*(a.get_r()*2*one));
535 // if(a.get_g()>half) a.set_g(one-(one-(a.get_g()*2*one-one))*(one-b.get_g()));
536 // else a.set_g(b.get_g()*(a.get_g()*2*one));
537 // if(a.get_b()>half) a.set_b(one-(one-(a.get_b()*2*one-one))*(one-b.get_b()));
538 // else a.set_b(b.get_b()*(a.get_b()*2*one));
539 //
540 // return blendfunc_ONTO(a,b,amount);
541 }
542
543 template <>
544 CairoColor
blendfunc_ALPHA_OVER(CairoColor & a,CairoColor & b,float amount)545 blendfunc_ALPHA_OVER(CairoColor &a, CairoColor &b, float amount)
546 {
547 CairoColor rm(b);
548
549 //multiply the inverse alpha channel with the one below us
550 rm.set_a((255-a.get_a())*b.get_a()/255.0);
551
552 return CairoColor::blend(rm,b,amount, Color::BLEND_STRAIGHT);
553 }
554
555
556 } // synfig namespace
557
558
559 #endif // __SYNFIG_COLOR_CAIROCOLORBLENDINGFUNCTIONS_H
560
561