1 /* === S Y N F I G ========================================================= */
2 /*!	\file
3 **	\brief Color blending function implementation
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_COLORBLENDINGFUNCTIONS_H
27 #define __SYNFIG_COLOR_COLORBLENDINGFUNCTIONS_H
28 
29 #define COLOR_EPSILON	(0.000001f)
30 
31 #include <synfig/color.h>
32 
33 namespace synfig {
34 
35 typedef Color (*blendfunc)(Color &,Color &,float);
36 
37 template <class C>
blendfunc_COMPOSITE(C & src,C & dest,float amount)38 C blendfunc_COMPOSITE(C &src,C &dest,float amount)
39 {
40 	//c_dest'=c_src+(1.0-a_src)*c_dest
41 	//a_dest'=a_src+(1.0-a_src)*a_dest
42 
43 	float a_src=src.get_a()*amount;
44 	float a_dest=dest.get_a();
45 	const float one(C::ceil);
46 
47 	// if a_arc==0.0
48 	//if(fabsf(a_src)<COLOR_EPSILON) return dest;
49 
50 	// Scale the source and destination by their alpha values
51 	src*=a_src;
52 	dest*=a_dest;
53 
54 	dest=src + dest*(one-a_src);
55 
56 	a_dest=a_src + a_dest*(one-a_src);
57 
58 	// if a_dest!=0.0
59 	if(fabsf(a_dest)>COLOR_EPSILON)
60 	{
61 		dest/=a_dest;
62 		dest.set_a(a_dest);
63 	}
64 	else
65 	{
66 		dest=C::alpha();
67 	}
68 	assert(dest.is_valid());
69 	return dest;
70 }
71 
72 template <class C>
blendfunc_STRAIGHT(C & src,C & bg,float amount)73 C blendfunc_STRAIGHT(C &src,C &bg,float amount)
74 {
75 	//a_out'=(a_src-a_bg)*amount+a_bg
76 	//c_out'=(((c_src*a_src)-(c_bg*a_bg))*amount+(c_bg*a_bg))/a_out'
77 
78 	// ie: if(amount==1.0)
79 	//if(fabsf(amount-1.0f)<COLOR_EPSILON)return src;
80 
81 	C out;
82 
83 	float a_out((src.get_a()-bg.get_a())*amount+bg.get_a());
84 
85 	// if a_out!=0.0
86 	if(fabsf(a_out)>COLOR_EPSILON)
87 //	if(a_out>COLOR_EPSILON || a_out<-COLOR_EPSILON)
88 	{
89 		out=((src*src.get_a()-bg*bg.get_a())*amount+bg*bg.get_a())/a_out;
90 		out.set_a(a_out);
91 	}
92 	else
93 		out=C::alpha();
94 
95 	assert(out.is_valid());
96 	return out;
97 }
98 
99 template <class C>
blendfunc_ONTO(C & a,C & b,float amount)100 C blendfunc_ONTO(C &a,C &b,float amount)
101 {
102 	float alpha(b.get_a());
103 	const float one(C::ceil);
104 	return blendfunc_COMPOSITE(a,b.set_a(one),amount).set_a(alpha);
105 }
106 
107 template <class C>
blendfunc_STRAIGHT_ONTO(C & a,C & b,float amount)108 C blendfunc_STRAIGHT_ONTO(C &a,C &b,float amount)
109 {
110 	a.set_a(a.get_a()*b.get_a());
111 	return blendfunc_STRAIGHT(a,b,amount);
112 }
113 
114 template <class C>
blendfunc_BRIGHTEN(C & a,C & b,float amount)115 C blendfunc_BRIGHTEN(C &a,C &b,float amount)
116 {
117 	const float alpha(a.get_a()*amount);
118 
119 	if(b.get_r()<a.get_r()*alpha)
120 		b.set_r(a.get_r()*alpha);
121 
122 	if(b.get_g()<a.get_g()*alpha)
123 		b.set_g(a.get_g()*alpha);
124 
125 	if(b.get_b()<a.get_b()*alpha)
126 		b.set_b(a.get_b()*alpha);
127 
128 	return b;
129 }
130 
131 template <class C>
blendfunc_DARKEN(C & a,C & b,float amount)132 C blendfunc_DARKEN(C &a,C &b,float amount)
133 {
134 	const float alpha(a.get_a()*amount);
135 	const float one(C::ceil);
136 
137 	if(b.get_r()>(a.get_r()-one)*alpha+one)
138 		b.set_r((a.get_r()-one)*alpha+one);
139 
140 	if(b.get_g()>(a.get_g()-one)*alpha+one)
141 		b.set_g((a.get_g()-one)*alpha+one);
142 
143 	if(b.get_b()>(a.get_b()-one)*alpha+one)
144 		b.set_b((a.get_b()-one)*alpha+one);
145 
146 
147 	return b;
148 }
149 
150 template <class C>
blendfunc_ADD(C & a,C & b,float amount)151 C blendfunc_ADD(C &a,C &b,float amount)
152 {
153 	float ba(b.get_a());
154 	float aa(a.get_a()*amount);
155 	const float alpha(std::max(0.f, std::min(1.f, ba + aa)));
156 	const float k = fabs(alpha) > 1e-8 ? 1.0/alpha : 0.0;
157 	aa *= k; ba *= k;
158 
159 	b.set_r(b.get_r()*ba+a.get_r()*aa);
160 	b.set_g(b.get_g()*ba+a.get_g()*aa);
161 	b.set_b(b.get_b()*ba+a.get_b()*aa);
162 	b.set_a(alpha);
163 
164 	return b;
165 }
166 
167 template <class C>
blendfunc_SUBTRACT(C & a,C & b,float amount)168 C blendfunc_SUBTRACT(C &a,C &b,float amount)
169 {
170 	float ba(b.get_a());
171 	float aa(a.get_a()*amount);
172 	const float alpha(std::max(0.f, std::min(1.f, ba + aa)));
173 	const float k = fabs(alpha) > 1e-8 ? 1.0/alpha : 0.0;
174 	aa *= k; ba *= k;
175 
176 	b.set_r(b.get_r()*ba-a.get_r()*aa);
177 	b.set_g(b.get_g()*ba-a.get_g()*aa);
178 	b.set_b(b.get_b()*ba-a.get_b()*aa);
179 	b.set_a(alpha);
180 
181 	return b;
182 }
183 
184 template <class C>
blendfunc_DIFFERENCE(C & a,C & b,float amount)185 C blendfunc_DIFFERENCE(C &a,C &b,float amount)
186 {
187 	float ba(b.get_a());
188 	float aa(a.get_a()*amount);
189 	const float alpha(std::max(0.f, std::min(1.f, ba + aa)));
190 	const float k = fabs(alpha) > 1e-8 ? 1.0/alpha : 0.0;
191 	aa *= k; ba *= k;
192 
193 	b.set_r(std::abs(b.get_r()*ba-a.get_r()*aa));
194 	b.set_g(std::abs(b.get_g()*ba-a.get_g()*aa));
195 	b.set_b(std::abs(b.get_b()*ba-a.get_b()*aa));
196 	b.set_a(alpha);
197 
198 	return b;
199 }
200 
201 template <class C>
blendfunc_MULTIPLY(C & a,C & b,float amount)202 C blendfunc_MULTIPLY(C &a,C &b,float amount)
203 {
204 	if(amount<0) a=~a, amount=-amount;
205 
206 	amount*=a.get_a();
207 	b.set_r(((b.get_r()*a.get_r())-b.get_r())*(amount)+b.get_r());
208 	b.set_g(((b.get_g()*a.get_g())-b.get_g())*(amount)+b.get_g());
209 	b.set_b(((b.get_b()*a.get_b())-b.get_b())*(amount)+b.get_b());
210 	return b;
211 }
212 
213 template <class C>
blendfunc_DIVIDE(C & a,C & b,float amount)214 C blendfunc_DIVIDE(C &a,C &b,float amount)
215 {
216 	amount*=a.get_a();
217 
218 	// We add COLOR_EPSILON in order to avoid a divide-by-zero condition.
219 	// This causes DIVIDE to bias toward positive values, but the effect is
220 	// really negligible. There is a reason why we use COLOR_EPSILON--we
221 	// want the change to be imperceptible.
222 
223 	b.set_r(((b.get_r()/(a.get_r()+COLOR_EPSILON))-b.get_r())*(amount)+b.get_r());
224 	b.set_g(((b.get_g()/(a.get_g()+COLOR_EPSILON))-b.get_g())*(amount)+b.get_g());
225 	b.set_b(((b.get_b()/(a.get_b()+COLOR_EPSILON))-b.get_b())*(amount)+b.get_b());
226 
227 	return b;
228 }
229 
230 template <class C>
blendfunc_COLOR(C & a,C & b,float amount)231 C blendfunc_COLOR(C &a,C &b,float amount)
232 {
233 	C temp(b);
234 	temp.set_uv(a.get_u(),a.get_v());
235 	return (temp-b)*amount*a.get_a()+b;
236 }
237 
238 template <class C>
blendfunc_HUE(C & a,C & b,float amount)239 C blendfunc_HUE(C &a,C &b,float amount)
240 {
241 	C temp(b);
242 	temp.set_hue(a.get_hue());
243 	return (temp-b)*amount*a.get_a()+b;
244 }
245 
246 template <class C>
blendfunc_SATURATION(C & a,C & b,float amount)247 C blendfunc_SATURATION(C &a,C &b,float amount)
248 {
249 	C temp(b);
250 	temp.set_s(a.get_s());
251 	return (temp-b)*amount*a.get_a()+b;
252 }
253 
254 template <class C>
blendfunc_LUMINANCE(C & a,C & b,float amount)255 C blendfunc_LUMINANCE(C &a,C &b,float amount)
256 {
257 	C temp(b);
258 	temp.set_y(a.get_y());
259 	return (temp-b)*amount*a.get_a()+b;
260 }
261 
262 template <class C>
blendfunc_BEHIND(C & a,C & b,float amount)263 C blendfunc_BEHIND(C &a,C &b,float amount)
264 {
265 	if(a.get_a()==0)
266 		a.set_a(COLOR_EPSILON*amount);		//!< \todo this is a hack
267 	else
268 		a.set_a(a.get_a()*amount);
269 	return blendfunc_COMPOSITE(b,a,1.0);
270 }
271 
272 template <class C>
blendfunc_ALPHA_BRIGHTEN(C & a,C & b,float amount)273 C blendfunc_ALPHA_BRIGHTEN(C &a,C &b,float amount)
274 {
275 	// \todo can this be right, multiplying amount by *b*'s alpha?
276 	// compare with blendfunc_BRIGHTEN where it is multiplied by *a*'s
277 	if(a.get_a() < b.get_a()*amount)
278 		return a.set_a(a.get_a()*amount);
279 	return b;
280 }
281 
282 template <class C>
blendfunc_ALPHA_DARKEN(C & a,C & b,float amount)283 C blendfunc_ALPHA_DARKEN(C &a,C &b,float amount)
284 {
285 	if(a.get_a()*amount > b.get_a())
286 		return a.set_a(a.get_a()*amount);
287 	return b;
288 }
289 
290 template <class C>
blendfunc_SCREEN(C & a,C & b,float amount)291 C blendfunc_SCREEN(C &a,C &b,float amount)
292 {
293 	const float one(C::ceil);
294 	if(amount<0) a=~a, amount=-amount;
295 
296 	a.set_r(one-(one-a.get_r())*(one-b.get_r()));
297 	a.set_g(one-(one-a.get_g())*(one-b.get_g()));
298 	a.set_b(one-(one-a.get_b())*(one-b.get_b()));
299 
300 	return blendfunc_ONTO(a,b,amount);
301 }
302 
303 template <class C>
blendfunc_OVERLAY(C & a,C & b,float amount)304 C blendfunc_OVERLAY(C &a,C &b,float amount)
305 {
306 	const float one(C::ceil);
307 	if(amount<0) a=~a, amount=-amount;
308 
309 	C rm;
310 	rm.set_r(b.get_r()*a.get_r());
311 	rm.set_g(b.get_g()*a.get_g());
312 	rm.set_b(b.get_b()*a.get_b());
313 
314 	C rs;
315 	rs.set_r(one-(one-a.get_r())*(one-b.get_r()));
316 	rs.set_g(one-(one-a.get_g())*(one-b.get_g()));
317 	rs.set_b(one-(one-a.get_b())*(one-b.get_b()));
318 
319 	C& ret(a);
320 
321 	ret.set_r(a.get_r()*rs.get_r() + (one-a.get_r())*rm.get_r());
322 	ret.set_g(a.get_g()*rs.get_g() + (one-a.get_g())*rm.get_g());
323 	ret.set_b(a.get_b()*rs.get_b() + (one-a.get_b())*rm.get_b());
324 
325 	return blendfunc_ONTO(ret,b,amount);
326 }
327 
328 
329 template <class C>
blendfunc_HARD_LIGHT(C & a,C & b,float amount)330 C blendfunc_HARD_LIGHT(C &a,C &b,float amount)
331 {
332 	const float one(C::ceil);
333 	const float half((one-C::floor)/2);
334 	if(amount<0) a=~a, amount=-amount;
335 
336 	if(a.get_r()>half)	a.set_r(one-(one-(a.get_r()*2*one-one))*(one-b.get_r()));
337 	else				a.set_r(b.get_r()*(a.get_r()*2*one));
338 	if(a.get_g()>half)	a.set_g(one-(one-(a.get_g()*2*one-one))*(one-b.get_g()));
339 	else				a.set_g(b.get_g()*(a.get_g()*2*one));
340 	if(a.get_b()>half)	a.set_b(one-(one-(a.get_b()*2*one-one))*(one-b.get_b()));
341 	else				a.set_b(b.get_b()*(a.get_b()*2*one));
342 
343 	return blendfunc_ONTO(a,b,amount);
344 }
345 
346 template <class C>
blendfunc_ALPHA_OVER(C & a,C & b,float amount)347 C blendfunc_ALPHA_OVER(C &a,C &b,float amount)
348 {
349 	const float one(C::ceil);
350 	C rm(b);
351 
352 	//multiply the inverse alpha channel with the one below us
353 	rm.set_a((one-a.get_a())*b.get_a());
354 
355 	return blendfunc_STRAIGHT(rm,b,amount);
356 }
357 
358 } // synfig namespace
359 
360 
361 #endif // __SYNFIG_COLOR_COLORBLENDINGFUNCTIONS_H
362 
363