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