1 /* === S Y N F I G ========================================================= */
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
42 using namespace synfig;
46 /* === M A C R O S ========================================================= */
48 #define COLOR_EPSILON (0.000001f)
50 /* === G L O B A L S ======================================================= */
52 /* === P R O C E D U R E S ================================================= */
54 /* === M E T H O D S ======================================================= */
59 Color::hex2real(String s)
61 std::istringstream i(s);
65 throw String("bad conversion from hex string \"") + s + String("\"");
70 Color::real2hex(ColorReal c)
77 o << hex << int(c*255.0f);
82 Color::set_hex(String& str)
87 // use just the hex characters
88 for (String::const_iterator iter = str.begin(); iter != str.end(); iter++)
96 r = hex2real(hex.substr(0,1)+hex.substr(0,1));
99 else if (hex.size() == 3)
101 r = hex2real(hex.substr(0,1)+hex.substr(0,1));
102 g = hex2real(hex.substr(1,1)+hex.substr(1,1));
103 b = hex2real(hex.substr(2,1)+hex.substr(2,1));
104 r_ = r; g_ = g; b_ = b;
106 else if (hex.size() == 6)
108 r = hex2real(hex.substr(0,2));
109 g = hex2real(hex.substr(2,2));
110 b = hex2real(hex.substr(4,2));
111 r_ = r; g_ = g; b_ = b;
116 printf("caught <%s>\n", s.c_str());
122 Color::get_string(void)const
124 std::ostringstream o;
125 o << std::fixed << std::setprecision(3) << "#" << get_hex() << " : " << std::setw(6) << a_;
126 return String(o.str().c_str());
131 Color::rotate_uv(const Angle& theta)const
135 ret.set_hue(ret.get_hue()+theta);
139 a(angle::sin(theta).get()),
140 b(angle::cos(theta).get());
145 return set_uv(b*u-a*v,a*u+b*v);
146 //return YUV(get_y(),b*u-a*v,a*u+b*v,get_a());
152 Color::clamped_negative()const
181 if(ret.r_>1) ret.r_=1;
182 if(ret.g_>1) ret.g_=1;
183 if(ret.b_>1) ret.b_=1;
184 if(ret.a_>1) ret.a_=1;
186 if(isnan(ret.get_r())) ret.r_=0.5;
187 if(isnan(ret.get_g())) ret.g_=0.5;
188 if(isnan(ret.get_b())) ret.b_=0.5;
189 if(isnan(ret.get_a())) ret.a_=1;
192 if(ret.r_>1) { ret.g_/=ret.r_; ret.b_/=ret.r_; ret.r_=1; }
193 if(ret.g_>1) { ret.r_/=ret.g_; ret.b_/=ret.g_; ret.g_=1; }
194 if(ret.b_>1) { ret.g_/=ret.b_; ret.r_/=ret.b_; ret.b_=1; }
195 if(ret.a_>1) ret.a_=1;
202 Color::clamped()const
214 if(ret.r_>1) ret.r_=1;
215 if(ret.g_>1) ret.g_=1;
216 if(ret.b_>1) ret.b_=1;
217 if(ret.a_>1) ret.a_=1;
219 if(isnan(ret.get_r())) ret.r_=0.5;
220 if(isnan(ret.get_g())) ret.g_=0.5;
221 if(isnan(ret.get_b())) ret.b_=0.5;
222 if(isnan(ret.get_a())) ret.a_=1;
227 typedef Color (*blendfunc)(Color &,Color &,float);
230 blendfunc_COMPOSITE(Color &src,Color &dest,float amount)
232 //c_dest'=c_src+(1.0-a_src)*c_dest
233 //a_dest'=a_src+(1.0-a_src)*a_dest
235 float a_src=src.get_a()*amount;
236 float a_dest=dest.get_a();
239 //if(fabsf(a_src)<COLOR_EPSILON) return dest;
241 // Scale the source and destination by their alpha values
245 dest=src + dest*(1.0f-a_src);
247 a_dest=a_src + a_dest*(1.0f-a_src);
250 if(fabsf(a_dest)>COLOR_EPSILON)
259 assert(dest.is_valid());
264 blendfunc_STRAIGHT(Color &src,Color &bg,float amount)
266 //a_out'=(a_src-a_bg)*amount+a_bg
267 //c_out'=(((c_src*a_src)-(c_bg*a_bg))*amount+(c_bg*a_bg))/a_out'
269 // ie: if(amount==1.0)
270 //if(fabsf(amount-1.0f)<COLOR_EPSILON)return src;
274 float a_out((src.get_a()-bg.get_a())*amount+bg.get_a());
277 if(fabsf(a_out)>COLOR_EPSILON)
278 // if(a_out>COLOR_EPSILON || a_out<-COLOR_EPSILON)
280 out=((src*src.get_a()-bg*bg.get_a())*amount+bg*bg.get_a())/a_out;
286 assert(out.is_valid());
291 blendfunc_ONTO(Color &a,Color &b,float amount)
293 float alpha(b.get_a());
294 return blendfunc_COMPOSITE(a,b.set_a(1.0f),amount).set_a(alpha);
298 blendfunc_STRAIGHT_ONTO(Color &a,Color &b,float amount)
300 a.set_a(a.get_a()*b.get_a());
301 return blendfunc_STRAIGHT(a,b,amount);
305 blendfunc_BRIGHTEN(Color &a,Color &b,float amount)
307 const float alpha(a.get_a()*amount);
309 if(b.get_r()<a.get_r()*alpha)
310 b.set_r(a.get_r()*alpha);
312 if(b.get_g()<a.get_g()*alpha)
313 b.set_g(a.get_g()*alpha);
315 if(b.get_b()<a.get_b()*alpha)
316 b.set_b(a.get_b()*alpha);
322 blendfunc_DARKEN(Color &a,Color &b,float amount)
324 const float alpha(a.get_a()*amount);
326 if(b.get_r()>(a.get_r()-1.0f)*alpha+1.0f)
327 b.set_r((a.get_r()-1.0f)*alpha+1.0f);
329 if(b.get_g()>(a.get_g()-1.0f)*alpha+1.0f)
330 b.set_g((a.get_g()-1.0f)*alpha+1.0f);
332 if(b.get_b()>(a.get_b()-1.0f)*alpha+1.0f)
333 b.set_b((a.get_b()-1.0f)*alpha+1.0f);
340 blendfunc_ADD(Color &a,Color &b,float amount)
342 const float alpha(a.get_a()*amount);
344 b.set_r(b.get_r()+a.get_r()*alpha);
345 b.set_g(b.get_g()+a.get_g()*alpha);
346 b.set_b(b.get_b()+a.get_b()*alpha);
352 blendfunc_SUBTRACT(Color &a,Color &b,float amount)
354 const float alpha(a.get_a()*amount);
356 b.set_r(b.get_r()-a.get_r()*alpha);
357 b.set_g(b.get_g()-a.get_g()*alpha);
358 b.set_b(b.get_b()-a.get_b()*alpha);
364 blendfunc_DIFFERENCE(Color &a,Color &b,float amount)
366 const float alpha(a.get_a()*amount);
368 b.set_r(abs(b.get_r()-a.get_r()*alpha));
369 b.set_g(abs(b.get_g()-a.get_g()*alpha));
370 b.set_b(abs(b.get_b()-a.get_b()*alpha));
376 blendfunc_MULTIPLY(Color &a,Color &b,float amount)
378 if(amount<0) a=~a, amount=-amount;
381 b.set_r(((b.get_r()*a.get_r())-b.get_r())*(amount)+b.get_r());
382 b.set_g(((b.get_g()*a.get_g())-b.get_g())*(amount)+b.get_g());
383 b.set_b(((b.get_b()*a.get_b())-b.get_b())*(amount)+b.get_b());
388 blendfunc_DIVIDE(Color &a,Color &b,float amount)
392 // We add COLOR_EPSILON in order to avoid a divide-by-zero condition.
393 // This causes DIVIDE to bias toward positive values, but the effect is
394 // really negligible. There is a reason why we use COLOR_EPSILON--we
395 // want the change to be imperceptible.
397 b.set_r(((b.get_r()/(a.get_r()+COLOR_EPSILON))-b.get_r())*(amount)+b.get_r());
398 b.set_g(((b.get_g()/(a.get_g()+COLOR_EPSILON))-b.get_g())*(amount)+b.get_g());
399 b.set_b(((b.get_b()/(a.get_b()+COLOR_EPSILON))-b.get_b())*(amount)+b.get_b());
405 blendfunc_COLOR(Color &a,Color &b,float amount)
408 temp.set_uv(a.get_u(),a.get_v());
409 return (temp-b)*amount*a.get_a()+b;
413 blendfunc_HUE(Color &a,Color &b,float amount)
416 temp.set_hue(a.get_hue());
417 return (temp-b)*amount*a.get_a()+b;
421 blendfunc_SATURATION(Color &a,Color &b,float amount)
424 temp.set_s(a.get_s());
425 return (temp-b)*amount*a.get_a()+b;
429 blendfunc_LUMINANCE(Color &a,Color &b,float amount)
432 temp.set_y(a.get_y());
433 return (temp-b)*amount*a.get_a()+b;
437 blendfunc_BEHIND(Color &a,Color &b,float amount)
439 if(a.get_a()==0)a.set_a(COLOR_EPSILON); //!< \todo this is a hack
440 a.set_a(a.get_a()*amount);
441 return blendfunc_COMPOSITE(b,a,1.0);
445 blendfunc_ALPHA_BRIGHTEN(Color &a,Color &b,float amount)
447 if(a.get_a()<b.get_a()*amount)
448 return a.set_a(a.get_a()*amount);
453 blendfunc_ALPHA_DARKEN(Color &a,Color &b,float amount)
455 if(a.get_a()*amount>b.get_a())
456 return a.set_a(a.get_a()*amount);
461 blendfunc_SCREEN(Color &a,Color &b,float amount)
463 if(amount<0) a=~a, amount=-amount;
465 a.set_r(1.0-(1.0f-a.get_r())*(1.0f-b.get_r()));
466 a.set_g(1.0-(1.0f-a.get_g())*(1.0f-b.get_g()));
467 a.set_b(1.0-(1.0f-a.get_b())*(1.0f-b.get_b()));
469 return blendfunc_ONTO(a,b,amount);
473 blendfunc_OVERLAY(Color &a,Color &b,float amount)
475 if(amount<0) a=~a, amount=-amount;
478 rm.set_r(b.get_r()*a.get_r());
479 rm.set_g(b.get_g()*a.get_g());
480 rm.set_b(b.get_b()*a.get_b());
483 rs.set_r(1.0-(1.0f-a.get_r())*(1.0f-b.get_r()));
484 rs.set_g(1.0-(1.0f-a.get_g())*(1.0f-b.get_g()));
485 rs.set_b(1.0-(1.0f-a.get_b())*(1.0f-b.get_b()));
489 ret.set_r(a.get_r()*rs.get_r() + (1.0-a.get_r())*rm.get_r());
490 ret.set_g(a.get_g()*rs.get_g() + (1.0-a.get_g())*rm.get_g());
491 ret.set_b(a.get_b()*rs.get_b() + (1.0-a.get_b())*rm.get_b());
493 return blendfunc_ONTO(ret,b,amount);
497 blendfunc_HARD_LIGHT(Color &a,Color &b,float amount)
499 if(amount<0) a=~a, amount=-amount;
501 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()));
502 else a.set_r(b.get_r()*(a.get_r()*2.0f));
503 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()));
504 else a.set_g(b.get_g()*(a.get_g()*2.0f));
505 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()));
506 else a.set_b(b.get_b()*(a.get_b()*2.0f));
508 return blendfunc_ONTO(a,b,amount);
512 blendfunc_ALPHA_OVER(Color &a,Color &b,float amount)
516 //multiply the inverse alpha channel with the one below us
517 rm.set_a((1-a.get_a())*b.get_a());
519 return blendfunc_STRAIGHT(rm,b,amount);
524 Color::blend(Color a, Color b,float amount, Color::BlendMethod type)
527 if(isnan(a.get_r()) || isnan(a.get_g()) || isnan(a.get_b()))
530 a=magenta().set_a(a.get_a());
532 a=black().set_a(a.get_a());
536 if(isnan(b.get_r()) || isnan(b.get_g()) || isnan(b.get_b()))
539 b=magenta().set_a(b.get_a());
541 b=black().set_a(b.get_a());
547 if(!a.is_valid()&&b.is_valid())
550 if(a.is_valid()&&!b.is_valid())
553 if(!a.is_valid()||!b.is_valid())
563 // No matter what blend method is being used,
564 // if the amount is equal to zero, then only B
565 // will shine through
566 if(fabsf(amount)<=COLOR_EPSILON)return b;
568 assert(type<BLEND_END);
570 const static blendfunc vtable[BLEND_END]=
582 blendfunc_SATURATION,
586 blendfunc_ALPHA_BRIGHTEN,
587 blendfunc_ALPHA_DARKEN,
589 blendfunc_HARD_LIGHT,
590 blendfunc_DIFFERENCE,
591 blendfunc_ALPHA_OVER,
593 blendfunc_STRAIGHT_ONTO,
596 return vtable[type](a,b,amount);