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