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