1 /* === S I N F G =========================================================== */
5 ** $Id: color.cpp,v 1.2 2005/01/23 04:03:21 darco Exp $
8 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
10 ** This software and associated documentation
11 ** are CONFIDENTIAL and PROPRIETARY property of
12 ** the above-mentioned copyright holder.
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.
20 /* ========================================================================= */
22 /* === H E A D E R S ======================================================= */
37 using namespace sinfg;
41 /* === M A C R O S ========================================================= */
43 #define COLOR_EPSILON (0.000001f)
45 /* === G L O B A L S ======================================================= */
47 /* === P R O C E D U R E S ================================================= */
49 /* === M E T H O D S ======================================================= */
55 Color::rotate_uv(const Angle& theta)const
59 ret.set_hue(ret.get_hue()+theta);
63 a(angle::sin(theta).get()),
64 b(angle::cos(theta).get());
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());
76 Color::clamped_negative()const
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;
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;
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;
126 Color::clamped()const
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;
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;
151 typedef Color (*blendfunc)(Color &,Color &,float);
154 blendfunc_COMPOSITE(Color &src,Color &dest,float amount)
156 //c_dest'=c_src+(1.0-a_src)*c_dest
157 //a_dest'=a_src+(1.0-a_src)*a_dest
159 float a_src=src.get_a()*amount;
160 float a_dest=dest.get_a();
163 //if(fabsf(a_src)<COLOR_EPSILON) return dest;
165 // Scale the source and destination by their alpha values
169 dest=src + dest*(1.0f-a_src);
171 a_dest=a_src + a_dest*(1.0f-a_src);
174 if(fabsf(a_dest)>COLOR_EPSILON)
183 assert(dest.is_valid());
188 blendfunc_STRAIGHT(Color &src,Color &bg,float amount)
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'
193 // ie: if(amount==1.0)
194 //if(fabsf(amount-1.0f)<COLOR_EPSILON)return src;
198 float a_out((src.get_a()-bg.get_a())*amount+bg.get_a());
201 if(fabsf(a_out)>COLOR_EPSILON)
202 // if(a_out>COLOR_EPSILON || a_out<-COLOR_EPSILON)
204 out=((src*src.get_a()-bg*bg.get_a())*amount+bg*bg.get_a())/a_out;
210 assert(out.is_valid());
215 blendfunc_ONTO(Color &a,Color &b,float amount)
217 float alpha(b.get_a());
218 return blendfunc_COMPOSITE(a,b.set_a(1.0f),amount).set_a(alpha);
222 blendfunc_STRAIGHT_ONTO(Color &a,Color &b,float amount)
224 a.set_a(a.get_a()*b.get_a());
225 return blendfunc_STRAIGHT(a,b,amount);
229 blendfunc_BRIGHTEN(Color &a,Color &b,float amount)
231 const float alpha(a.get_a()*amount);
233 if(b.get_r()<a.get_r()*alpha)
234 b.set_r(a.get_r()*alpha);
236 if(b.get_g()<a.get_g()*alpha)
237 b.set_g(a.get_g()*alpha);
239 if(b.get_b()<a.get_b()*alpha)
240 b.set_b(a.get_b()*alpha);
246 blendfunc_DARKEN(Color &a,Color &b,float amount)
248 const float alpha(a.get_a()*amount);
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);
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);
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);
264 blendfunc_ADD(Color &a,Color &b,float amount)
266 const float alpha(a.get_a()*amount);
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);
276 blendfunc_SUBTRACT(Color &a,Color &b,float amount)
278 const float alpha(a.get_a()*amount);
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);
288 blendfunc_DIFFERENCE(Color &a,Color &b,float amount)
290 const float alpha(a.get_a()*amount);
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));
300 blendfunc_MULTIPLY(Color &a,Color &b,float amount)
302 if(amount<0) a=~a, amount=-amount;
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());
312 blendfunc_DIVIDE(Color &a,Color &b,float amount)
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.
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());
329 blendfunc_COLOR(Color &a,Color &b,float amount)
332 temp.set_uv(a.get_u(),a.get_v());
333 return (temp-b)*amount*a.get_a()+b;
337 blendfunc_HUE(Color &a,Color &b,float amount)
340 temp.set_hue(a.get_hue());
341 return (temp-b)*amount*a.get_a()+b;
345 blendfunc_SATURATION(Color &a,Color &b,float amount)
348 temp.set_s(a.get_s());
349 return (temp-b)*amount*a.get_a()+b;
353 blendfunc_LUMINANCE(Color &a,Color &b,float amount)
356 temp.set_y(a.get_y());
357 return (temp-b)*amount*a.get_a()+b;
361 blendfunc_BEHIND(Color &a,Color &b,float amount)
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);
369 blendfunc_ALPHA_BRIGHTEN(Color &a,Color &b,float amount)
371 if(a.get_a()<b.get_a()*amount)
372 return a.set_a(a.get_a()*amount);
377 blendfunc_ALPHA_DARKEN(Color &a,Color &b,float amount)
379 if(a.get_a()*amount>b.get_a())
380 return a.set_a(a.get_a()*amount);
385 blendfunc_SCREEN(Color &a,Color &b,float amount)
387 if(amount<0) a=~a, amount=-amount;
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()));
393 return blendfunc_ONTO(a,b,amount);
397 blendfunc_OVERLAY(Color &a,Color &b,float amount)
399 if(amount<0) a=~a, amount=-amount;
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());
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()));
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());
417 return blendfunc_ONTO(ret,b,amount);
421 blendfunc_HARD_LIGHT(Color &a,Color &b,float amount)
423 if(amount<0) a=~a, amount=-amount;
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));
432 return blendfunc_ONTO(a,b,amount);
436 blendfunc_ALPHA_OVER(Color &a,Color &b,float amount)
440 //multiply the inverse alpha channel with the one below us
441 rm.set_a((1-a.get_a())*b.get_a());
443 return blendfunc_STRAIGHT(rm,b,amount);
448 Color::blend(Color a, Color b,float amount, Color::BlendMethod type)
451 if(isnan(a.get_r()) || isnan(a.get_g()) || isnan(a.get_b()))
454 a=magenta().set_a(a.get_a());
456 a=black().set_a(a.get_a());
460 if(isnan(b.get_r()) || isnan(b.get_g()) || isnan(b.get_b()))
463 b=magenta().set_a(b.get_a());
465 b=black().set_a(b.get_a());
471 if(!a.is_valid()&&b.is_valid())
474 if(a.is_valid()&&!b.is_valid())
477 if(!a.is_valid()||!b.is_valid())
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;
492 assert(type<BLEND_END);
494 const static blendfunc vtable[BLEND_END]=
506 blendfunc_SATURATION,
510 blendfunc_ALPHA_BRIGHTEN,
511 blendfunc_ALPHA_DARKEN,
513 blendfunc_HARD_LIGHT,
514 blendfunc_DIFFERENCE,
515 blendfunc_ALPHA_OVER,
517 blendfunc_STRAIGHT_ONTO,
520 return vtable[type](a,b,amount);