synfig-core  1.0.3
colorblendingfunctions.h
Go to the documentation of this file.
1 /* === S Y N F I G ========================================================= */
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>
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>
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>
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>
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>
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>
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>
151 C blendfunc_ADD(C &a,C &b,float amount)
152 {
153  const float alpha(a.get_a()*amount);
154 
155  b.set_r(b.get_r()+a.get_r()*alpha);
156  b.set_g(b.get_g()+a.get_g()*alpha);
157  b.set_b(b.get_b()+a.get_b()*alpha);
158 
159  return b;
160 }
161 
162 template <class C>
163 C blendfunc_SUBTRACT(C &a,C &b,float amount)
164 {
165  const float alpha(a.get_a()*amount);
166 
167  b.set_r(b.get_r()-a.get_r()*alpha);
168  b.set_g(b.get_g()-a.get_g()*alpha);
169  b.set_b(b.get_b()-a.get_b()*alpha);
170 
171  return b;
172 }
173 
174 template <class C>
175 C blendfunc_DIFFERENCE(C &a,C &b,float amount)
176 {
177  const float alpha(a.get_a()*amount);
178 
179  b.set_r(std::abs(b.get_r()-a.get_r()*alpha));
180  b.set_g(std::abs(b.get_g()-a.get_g()*alpha));
181  b.set_b(std::abs(b.get_b()-a.get_b()*alpha));
182 
183  return b;
184 }
185 
186 template <class C>
187 C blendfunc_MULTIPLY(C &a,C &b,float amount)
188 {
189  if(amount<0) a=~a, amount=-amount;
190 
191  amount*=a.get_a();
192  b.set_r(((b.get_r()*a.get_r())-b.get_r())*(amount)+b.get_r());
193  b.set_g(((b.get_g()*a.get_g())-b.get_g())*(amount)+b.get_g());
194  b.set_b(((b.get_b()*a.get_b())-b.get_b())*(amount)+b.get_b());
195  return b;
196 }
197 
198 template <class C>
199 C blendfunc_DIVIDE(C &a,C &b,float amount)
200 {
201  amount*=a.get_a();
202 
203  // We add COLOR_EPSILON in order to avoid a divide-by-zero condition.
204  // This causes DIVIDE to bias toward positive values, but the effect is
205  // really negligible. There is a reason why we use COLOR_EPSILON--we
206  // want the change to be imperceptible.
207 
208  b.set_r(((b.get_r()/(a.get_r()+COLOR_EPSILON))-b.get_r())*(amount)+b.get_r());
209  b.set_g(((b.get_g()/(a.get_g()+COLOR_EPSILON))-b.get_g())*(amount)+b.get_g());
210  b.set_b(((b.get_b()/(a.get_b()+COLOR_EPSILON))-b.get_b())*(amount)+b.get_b());
211 
212  return b;
213 }
214 
215 template <class C>
216 C blendfunc_COLOR(C &a,C &b,float amount)
217 {
218  C temp(b);
219  temp.set_uv(a.get_u(),a.get_v());
220  return (temp-b)*amount*a.get_a()+b;
221 }
222 
223 template <class C>
224 C blendfunc_HUE(C &a,C &b,float amount)
225 {
226  C temp(b);
227  temp.set_hue(a.get_hue());
228  return (temp-b)*amount*a.get_a()+b;
229 }
230 
231 template <class C>
232 C blendfunc_SATURATION(C &a,C &b,float amount)
233 {
234  C temp(b);
235  temp.set_s(a.get_s());
236  return (temp-b)*amount*a.get_a()+b;
237 }
238 
239 template <class C>
240 C blendfunc_LUMINANCE(C &a,C &b,float amount)
241 {
242  C temp(b);
243  temp.set_y(a.get_y());
244  return (temp-b)*amount*a.get_a()+b;
245 }
246 
247 template <class C>
248 C blendfunc_BEHIND(C &a,C &b,float amount)
249 {
250  if(a.get_a()==0)
251  a.set_a(COLOR_EPSILON*amount);
252  else
253  a.set_a(a.get_a()*amount);
254  return blendfunc_COMPOSITE(b,a,1.0);
255 }
256 
257 template <class C>
258 C blendfunc_ALPHA_BRIGHTEN(C &a,C &b,float amount)
259 {
260  // \todo can this be right, multiplying amount by *b*'s alpha?
261  // compare with blendfunc_BRIGHTEN where it is multiplied by *a*'s
262  if(a.get_a() < b.get_a()*amount)
263  return a.set_a(a.get_a()*amount);
264  return b;
265 }
266 
267 template <class C>
268 C blendfunc_ALPHA_DARKEN(C &a,C &b,float amount)
269 {
270  if(a.get_a()*amount > b.get_a())
271  return a.set_a(a.get_a()*amount);
272  return b;
273 }
274 
275 template <class C>
276 C blendfunc_SCREEN(C &a,C &b,float amount)
277 {
278  const float one(C::ceil);
279  if(amount<0) a=~a, amount=-amount;
280 
281  a.set_r(one-(one-a.get_r())*(one-b.get_r()));
282  a.set_g(one-(one-a.get_g())*(one-b.get_g()));
283  a.set_b(one-(one-a.get_b())*(one-b.get_b()));
284 
285  return blendfunc_ONTO(a,b,amount);
286 }
287 
288 template <class C>
289 C blendfunc_OVERLAY(C &a,C &b,float amount)
290 {
291  const float one(C::ceil);
292  if(amount<0) a=~a, amount=-amount;
293 
294  C rm;
295  rm.set_r(b.get_r()*a.get_r());
296  rm.set_g(b.get_g()*a.get_g());
297  rm.set_b(b.get_b()*a.get_b());
298 
299  C rs;
300  rs.set_r(one-(one-a.get_r())*(one-b.get_r()));
301  rs.set_g(one-(one-a.get_g())*(one-b.get_g()));
302  rs.set_b(one-(one-a.get_b())*(one-b.get_b()));
303 
304  C& ret(a);
305 
306  ret.set_r(a.get_r()*rs.get_r() + (one-a.get_r())*rm.get_r());
307  ret.set_g(a.get_g()*rs.get_g() + (one-a.get_g())*rm.get_g());
308  ret.set_b(a.get_b()*rs.get_b() + (one-a.get_b())*rm.get_b());
309 
310  return blendfunc_ONTO(ret,b,amount);
311 }
312 
313 
314 template <class C>
315 C blendfunc_HARD_LIGHT(C &a,C &b,float amount)
316 {
317  const float one(C::ceil);
318  const float half((one-C::floor)/2);
319  if(amount<0) a=~a, amount=-amount;
320 
321  if(a.get_r()>half) a.set_r(one-(one-(a.get_r()*2*one-one))*(one-b.get_r()));
322  else a.set_r(b.get_r()*(a.get_r()*2*one));
323  if(a.get_g()>half) a.set_g(one-(one-(a.get_g()*2*one-one))*(one-b.get_g()));
324  else a.set_g(b.get_g()*(a.get_g()*2*one));
325  if(a.get_b()>half) a.set_b(one-(one-(a.get_b()*2*one-one))*(one-b.get_b()));
326  else a.set_b(b.get_b()*(a.get_b()*2*one));
327 
328  return blendfunc_ONTO(a,b,amount);
329 }
330 
331 template <class C>
332 C blendfunc_ALPHA_OVER(C &a,C &b,float amount)
333 {
334  const float one(C::ceil);
335  C rm(b);
336 
337  //multiply the inverse alpha channel with the one below us
338  rm.set_a((one-a.get_a())*b.get_a());
339 
340  return blendfunc_STRAIGHT(rm,b,amount);
341 }
342 
343 } // synfig namespace
344 
345 
346 #endif // __SYNFIG_COLOR_COLORBLENDINGFUNCTIONS_H
347