more updates
[synfig.git] / synfig-core / trunk / src / synfig / color.cpp
1 /* === S I N F G =========================================================== */
2 /*!     \file color.h
3 **      \brief Color Class
4 **
5 **      $Id: color.cpp,v 1.2 2005/01/23 04:03:21 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include <ETL/angle>
32 #include "color.h"
33 #include <cstdio>
34
35 #endif
36
37 using namespace sinfg;
38 using namespace etl;
39 using namespace std;
40
41 /* === M A C R O S ========================================================= */
42
43 #define COLOR_EPSILON   (0.000001f)
44
45 /* === G L O B A L S ======================================================= */
46
47 /* === P R O C E D U R E S ================================================= */
48
49 /* === M E T H O D S ======================================================= */
50
51
52
53 #if 0
54 Color&
55 Color::rotate_uv(const Angle& theta)const
56 {
57 /*/
58         Color ret(*this);
59         ret.set_hue(ret.get_hue()+theta);
60         return ret;
61 /*/
62         const float
63                 a(angle::sin(theta).get()),
64                 b(angle::cos(theta).get());
65         const float
66                 u(get_u()),
67                 v(get_v());
68         
69         return set_uv(b*u-a*v,a*u+b*v);
70         //return YUV(get_y(),b*u-a*v,a*u+b*v,get_a());
71 //*/
72 }
73 #endif
74
75 Color
76 Color::clamped_negative()const
77 {
78         Color ret=*this;
79                 
80         if(ret.a_==0)
81                 return alpha();
82
83         if(ret.a_<0)
84                 ret=-ret;
85
86         if(ret.r_<0)
87         {
88                 ret.g_-=ret.r_;
89                 ret.b_-=ret.r_;
90                 ret.r_=0.0f;
91         }
92         if(ret.g_<0)
93         {
94                 ret.r_-=ret.g_;
95                 ret.b_-=ret.g_;
96                 ret.g_=0.0f;
97         }
98         if(ret.b_<0)
99         {
100                 ret.r_-=ret.b_;
101                 ret.g_-=ret.b_;
102                 ret.b_=0.0f;
103         }
104
105         if(ret.r_>1) ret.r_=1;
106         if(ret.g_>1) ret.g_=1;
107         if(ret.b_>1) ret.b_=1;
108         if(ret.a_>1) ret.a_=1;
109
110         if(isnan(ret.get_r())) ret.r_=0.5;
111         if(isnan(ret.get_g())) ret.g_=0.5;
112         if(isnan(ret.get_b())) ret.b_=0.5;
113         if(isnan(ret.get_a())) ret.a_=1;
114
115 /*
116         if(ret.r_>1) { ret.g_/=ret.r_; ret.b_/=ret.r_; ret.r_=1; }
117         if(ret.g_>1) { ret.r_/=ret.g_; ret.b_/=ret.g_; ret.g_=1; }
118         if(ret.b_>1) { ret.g_/=ret.b_; ret.r_/=ret.b_; ret.b_=1; }
119         if(ret.a_>1) ret.a_=1;
120 */
121
122         return ret;
123 }
124
125 Color
126 Color::clamped()const
127 {
128         Color ret(*this);
129         if(ret.get_r()<0)
130                 ret.set_r(0);
131         if(ret.get_g()<0)
132                 ret.set_g(0);
133         if(ret.get_b()<0)
134                 ret.set_b(0);
135         if(ret.get_a()<0)
136                 ret.set_a(0);
137
138         if(ret.r_>1) ret.r_=1;
139         if(ret.g_>1) ret.g_=1;
140         if(ret.b_>1) ret.b_=1;
141         if(ret.a_>1) ret.a_=1;
142
143         if(isnan(ret.get_r())) ret.r_=0.5;
144         if(isnan(ret.get_g())) ret.g_=0.5;
145         if(isnan(ret.get_b())) ret.b_=0.5;
146         if(isnan(ret.get_a())) ret.a_=1;
147
148         return(ret);
149 }
150
151 typedef Color (*blendfunc)(Color &,Color &,float);
152
153 static Color
154 blendfunc_COMPOSITE(Color &src,Color &dest,float amount)
155 {
156         //c_dest'=c_src+(1.0-a_src)*c_dest
157         //a_dest'=a_src+(1.0-a_src)*a_dest
158
159         float a_src=src.get_a()*amount;
160         float a_dest=dest.get_a();
161
162         // if a_arc==0.0
163         //if(fabsf(a_src)<COLOR_EPSILON) return dest;
164         
165         // Scale the source and destination by their alpha values
166         src*=a_src;
167         dest*=a_dest;
168         
169         dest=src + dest*(1.0f-a_src);
170         
171         a_dest=a_src + a_dest*(1.0f-a_src);
172
173         // if a_dest!=0.0
174         if(fabsf(a_dest)>COLOR_EPSILON)
175         {
176                 dest/=a_dest;
177                 dest.set_a(a_dest);
178         }
179         else
180         {
181                 dest=Color::alpha();
182         }
183         assert(dest.is_valid());
184         return dest;
185 }
186
187 static Color
188 blendfunc_STRAIGHT(Color &src,Color &bg,float amount)
189 {
190         //a_out'=(a_src-a_bg)*amount+a_bg
191         //c_out'=(((c_src*a_src)-(c_bg*a_bg))*amount+(c_bg*a_bg))/a_out'
192
193         // ie: if(amount==1.0)
194         //if(fabsf(amount-1.0f)<COLOR_EPSILON)return src;
195
196         Color out;
197         
198         float a_out((src.get_a()-bg.get_a())*amount+bg.get_a());
199         
200         // if a_out!=0.0
201         if(fabsf(a_out)>COLOR_EPSILON)
202 //      if(a_out>COLOR_EPSILON || a_out<-COLOR_EPSILON)
203         {
204                 out=((src*src.get_a()-bg*bg.get_a())*amount+bg*bg.get_a())/a_out;
205                 out.set_a(a_out);
206         }
207         else
208                 out=Color::alpha();
209
210         assert(out.is_valid());
211         return out;
212 }
213
214 static Color
215 blendfunc_ONTO(Color &a,Color &b,float amount)
216 {
217         float alpha(b.get_a());
218         return blendfunc_COMPOSITE(a,b.set_a(1.0f),amount).set_a(alpha);
219 }
220
221 static Color
222 blendfunc_STRAIGHT_ONTO(Color &a,Color &b,float amount)
223 {
224         a.set_a(a.get_a()*b.get_a());
225         return blendfunc_STRAIGHT(a,b,amount);
226 }
227
228 static Color
229 blendfunc_BRIGHTEN(Color &a,Color &b,float amount)
230 {
231         const float alpha(a.get_a()*amount);
232         
233         if(b.get_r()<a.get_r()*alpha)
234                 b.set_r(a.get_r()*alpha);
235
236         if(b.get_g()<a.get_g()*alpha)
237                 b.set_g(a.get_g()*alpha);
238
239         if(b.get_b()<a.get_b()*alpha)
240                 b.set_b(a.get_b()*alpha);
241         
242         return b;
243 }
244
245 static Color
246 blendfunc_DARKEN(Color &a,Color &b,float amount)
247 {
248         const float alpha(a.get_a()*amount);
249         
250         if(b.get_r()>(a.get_r()-1.0f)*alpha+1.0f)
251                 b.set_r((a.get_r()-1.0f)*alpha+1.0f);
252
253         if(b.get_g()>(a.get_g()-1.0f)*alpha+1.0f)
254                 b.set_g((a.get_g()-1.0f)*alpha+1.0f);
255
256         if(b.get_b()>(a.get_b()-1.0f)*alpha+1.0f)
257                 b.set_b((a.get_b()-1.0f)*alpha+1.0f);
258
259         
260         return b;
261 }
262
263 static Color
264 blendfunc_ADD(Color &a,Color &b,float amount)
265 {
266         const float alpha(a.get_a()*amount);
267
268         b.set_r(b.get_r()+a.get_r()*alpha);
269         b.set_g(b.get_g()+a.get_g()*alpha);
270         b.set_b(b.get_b()+a.get_b()*alpha);
271
272         return b;
273 }
274
275 static Color
276 blendfunc_SUBTRACT(Color &a,Color &b,float amount)
277 {
278         const float alpha(a.get_a()*amount);
279
280         b.set_r(b.get_r()-a.get_r()*alpha);
281         b.set_g(b.get_g()-a.get_g()*alpha);
282         b.set_b(b.get_b()-a.get_b()*alpha);
283
284         return b;
285 }
286
287 static Color
288 blendfunc_DIFFERENCE(Color &a,Color &b,float amount)
289 {
290         const float alpha(a.get_a()*amount);
291
292         b.set_r(abs(b.get_r()-a.get_r()*alpha));
293         b.set_g(abs(b.get_g()-a.get_g()*alpha));
294         b.set_b(abs(b.get_b()-a.get_b()*alpha));
295
296         return b;
297 }
298
299 static Color
300 blendfunc_MULTIPLY(Color &a,Color &b,float amount)
301 {
302         if(amount<0) a=~a, amount=-amount;
303
304         amount*=a.get_a();
305         b.set_r(((b.get_r()*a.get_r())-b.get_r())*(amount)+b.get_r());
306         b.set_g(((b.get_g()*a.get_g())-b.get_g())*(amount)+b.get_g());
307         b.set_b(((b.get_b()*a.get_b())-b.get_b())*(amount)+b.get_b());
308         return b;
309 }
310
311 static Color
312 blendfunc_DIVIDE(Color &a,Color &b,float amount)
313 {
314         amount*=a.get_a();
315
316         // We add COLOR_EPSILON in order to avoid a divide-by-zero condition.
317         // This causes DIVIDE to bias toward positive values, but the effect is
318         // really neglegable. There is a reason why we use COLOR_EPSILON--we
319         // want the change to be imperceptable.
320         
321         b.set_r(((b.get_r()/(a.get_r()+COLOR_EPSILON))-b.get_r())*(amount)+b.get_r());
322         b.set_g(((b.get_g()/(a.get_g()+COLOR_EPSILON))-b.get_g())*(amount)+b.get_g());
323         b.set_b(((b.get_b()/(a.get_b()+COLOR_EPSILON))-b.get_b())*(amount)+b.get_b());
324
325         return b;
326 }
327
328 static Color
329 blendfunc_COLOR(Color &a,Color &b,float amount)
330 {
331         Color temp(b);
332         temp.set_uv(a.get_u(),a.get_v());
333         return (temp-b)*amount*a.get_a()+b;                     
334 }
335
336 static Color
337 blendfunc_HUE(Color &a,Color &b,float amount)
338 {
339         Color temp(b);
340         temp.set_hue(a.get_hue());
341         return (temp-b)*amount*a.get_a()+b;
342 }
343
344 static Color
345 blendfunc_SATURATION(Color &a,Color &b,float amount)
346 {
347         Color temp(b);
348         temp.set_s(a.get_s());
349         return (temp-b)*amount*a.get_a()+b;
350 }
351
352 static Color
353 blendfunc_LUMINANCE(Color &a,Color &b,float amount)
354 {
355         Color temp(b);
356         temp.set_y(a.get_y());
357         return (temp-b)*amount*a.get_a()+b;
358 }
359
360 static Color
361 blendfunc_BEHIND(Color &a,Color &b,float amount)
362 {
363         if(a.get_a()==0)a.set_a(COLOR_EPSILON);         //!< \hack
364         a.set_a(a.get_a()*amount);
365         return blendfunc_COMPOSITE(b,a,1.0);
366 }
367
368 static Color
369 blendfunc_ALPHA_BRIGHTEN(Color &a,Color &b,float amount)
370 {
371         if(a.get_a()<b.get_a()*amount)
372                 return a.set_a(a.get_a()*amount);
373         return b;
374 }
375
376 static Color
377 blendfunc_ALPHA_DARKEN(Color &a,Color &b,float amount)
378 {
379         if(a.get_a()*amount>b.get_a())
380                 return a.set_a(a.get_a()*amount);
381         return b;
382 }
383
384 static Color
385 blendfunc_SCREEN(Color &a,Color &b,float amount)
386 {
387         if(amount<0) a=~a, amount=-amount;
388
389         a.set_r(1.0-(1.0f-a.get_r())*(1.0f-b.get_r()));
390         a.set_g(1.0-(1.0f-a.get_g())*(1.0f-b.get_g()));
391         a.set_b(1.0-(1.0f-a.get_b())*(1.0f-b.get_b()));
392         
393         return blendfunc_ONTO(a,b,amount);
394 }
395
396 static Color
397 blendfunc_OVERLAY(Color &a,Color &b,float amount)
398 {       
399         if(amount<0) a=~a, amount=-amount;
400
401         Color rm;
402         rm.set_r(b.get_r()*a.get_r());
403         rm.set_g(b.get_g()*a.get_g());
404         rm.set_b(b.get_b()*a.get_b());
405
406         Color rs;
407         rs.set_r(1.0-(1.0f-a.get_r())*(1.0f-b.get_r()));
408         rs.set_g(1.0-(1.0f-a.get_g())*(1.0f-b.get_g()));
409         rs.set_b(1.0-(1.0f-a.get_b())*(1.0f-b.get_b()));
410
411         Color& ret(a);
412         
413         ret.set_r(a.get_r()*rs.get_r() + (1.0-a.get_r())*rm.get_r());
414         ret.set_g(a.get_g()*rs.get_g() + (1.0-a.get_g())*rm.get_g());
415         ret.set_b(a.get_b()*rs.get_b() + (1.0-a.get_b())*rm.get_b());
416
417         return blendfunc_ONTO(ret,b,amount);
418 }
419
420 static Color
421 blendfunc_HARD_LIGHT(Color &a,Color &b,float amount)
422 {
423         if(amount<0) a=~a, amount=-amount;
424
425         if(a.get_r()>0.5f)      a.set_r(1.0-(1.0f-(a.get_r()*2.0f-1.0f))*(1.0f-b.get_r()));
426         else                            a.set_r(b.get_r()*(a.get_r()*2.0f));
427         if(a.get_g()>0.5f)      a.set_g(1.0-(1.0f-(a.get_g()*2.0f-1.0f))*(1.0f-b.get_g()));
428         else                            a.set_g(b.get_g()*(a.get_g()*2.0f));
429         if(a.get_b()>0.5f)      a.set_b(1.0-(1.0f-(a.get_b()*2.0f-1.0f))*(1.0f-b.get_b()));
430         else                            a.set_b(b.get_b()*(a.get_b()*2.0f));
431
432         return blendfunc_ONTO(a,b,amount);
433 }
434
435 static Color
436 blendfunc_ALPHA_OVER(Color &a,Color &b,float amount)
437 {
438         Color rm(b);
439
440         //multiply the inverse alpha channel with the one below us
441         rm.set_a((1-a.get_a())*b.get_a());
442
443         return blendfunc_STRAIGHT(rm,b,amount);
444 }
445
446
447 Color
448 Color::blend(Color a, Color b,float amount, Color::BlendMethod type)
449 {
450 #if 0
451         if(isnan(a.get_r()) || isnan(a.get_g()) || isnan(a.get_b()))
452         {
453 #ifdef _DEBUG
454                 a=magenta().set_a(a.get_a());
455 #else
456                 a=black().set_a(a.get_a());
457 #endif
458         }
459
460         if(isnan(b.get_r()) || isnan(b.get_g()) || isnan(b.get_b()))
461         {
462 #ifdef _DEBUG
463                 b=magenta().set_a(b.get_a());
464 #else
465                 b=black().set_a(b.get_a());
466 #endif
467         }
468 #endif
469         
470 /*
471         if(!a.is_valid()&&b.is_valid())
472                 return b;
473
474         if(a.is_valid()&&!b.is_valid())
475                 return a;
476
477         if(!a.is_valid()||!b.is_valid())
478         {
479 #ifdef _DEBUG
480                 return magenta();
481 #else
482                 return black();
483 #endif
484         }
485 */
486         
487         // No matter what blend method is being used,
488         // if the amount is equal to zero, then only B
489         // will shine through
490         if(fabsf(amount)<=COLOR_EPSILON)return b;
491         
492         assert(type<BLEND_END);
493         
494         const static blendfunc vtable[BLEND_END]=
495         {
496                 blendfunc_COMPOSITE, 
497                 blendfunc_STRAIGHT,
498                 blendfunc_BRIGHTEN,
499                 blendfunc_DARKEN,
500                 blendfunc_ADD,
501                 blendfunc_SUBTRACT,
502                 blendfunc_MULTIPLY,
503                 blendfunc_DIVIDE,
504                 blendfunc_COLOR,
505                 blendfunc_HUE,
506                 blendfunc_SATURATION,
507                 blendfunc_LUMINANCE,
508                 blendfunc_BEHIND,
509                 blendfunc_ONTO,
510                 blendfunc_ALPHA_BRIGHTEN,
511                 blendfunc_ALPHA_DARKEN,
512                 blendfunc_SCREEN,
513                 blendfunc_HARD_LIGHT,
514                 blendfunc_DIFFERENCE,
515                 blendfunc_ALPHA_OVER,
516                 blendfunc_OVERLAY,
517                 blendfunc_STRAIGHT_ONTO,
518         };
519         
520         return vtable[type](a,b,amount);
521 }