/* === S Y N F I G ========================================================= */
-/*! \file color.h
+/*! \file color.cpp
** \brief Color Class
**
-** $Id: color.cpp,v 1.2 2005/01/23 04:03:21 darco Exp $
+** $Id$
**
** \legal
** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+** Copyright (c) 2007 Chris Moore
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
#include <ETL/angle>
#include "color.h"
#include <cstdio>
+#include <sstream>
+#include <iostream>
+#include <iomanip>
#endif
+ColorReal
+Color::hex2real(String s)
+{
+ std::istringstream i(s);
+ int n;
+ i.fill('0');
+ if (!(i >> hex >> n))
+ throw String("bad conversion from hex string \"") + s + String("\"");
+ return n / 255.0f;
+}
+
+const String
+Color::real2hex(ColorReal c)
+{
+ std::ostringstream o;
+ o.width(2);
+ o.fill('0');
+ if (c<0) c = 0;
+ if (c>1) c = 1;
+ o << hex << int(c*255.0f);
+ return o.str();
+}
+
+void
+Color::set_hex(String& str)
+{
+ value_type r, g, b;
+ String hex;
+
+ // use just the hex characters
+ for (String::const_iterator iter = str.begin(); iter != str.end(); iter++)
+ if (isxdigit(*iter))
+ hex.push_back(*iter);
+
+ try
+ {
+ if (hex.size() == 1)
+ {
+ r = hex2real(hex.substr(0,1)+hex.substr(0,1));
+ r_ = g_ = b_ = r;
+ }
+ else if (hex.size() == 3)
+ {
+ r = hex2real(hex.substr(0,1)+hex.substr(0,1));
+ g = hex2real(hex.substr(1,1)+hex.substr(1,1));
+ b = hex2real(hex.substr(2,1)+hex.substr(2,1));
+ r_ = r; g_ = g; b_ = b;
+ }
+ else if (hex.size() == 6)
+ {
+ r = hex2real(hex.substr(0,2));
+ g = hex2real(hex.substr(2,2));
+ b = hex2real(hex.substr(4,2));
+ r_ = r; g_ = g; b_ = b;
+ }
+ }
+ catch (string s)
+ {
+ printf("caught <%s>\n", s.c_str());
+ return;
+ }
+}
+
+const String
+Color::get_string(void)const
+{
+ std::ostringstream o;
+ o << std::fixed << std::setprecision(3) << "#" << get_hex() << " : " << std::setw(6) << a_;
+ return String(o.str().c_str());
+}
+
#if 0
Color&
Color::rotate_uv(const Angle& theta)const
const float
u(get_u()),
v(get_v());
-
+
return set_uv(b*u-a*v,a*u+b*v);
//return YUV(get_y(),b*u-a*v,a*u+b*v,get_a());
//*/
Color::clamped_negative()const
{
Color ret=*this;
-
+
if(ret.a_==0)
return alpha();
// if a_arc==0.0
//if(fabsf(a_src)<COLOR_EPSILON) return dest;
-
+
// Scale the source and destination by their alpha values
src*=a_src;
dest*=a_dest;
-
+
dest=src + dest*(1.0f-a_src);
-
+
a_dest=a_src + a_dest*(1.0f-a_src);
// if a_dest!=0.0
//if(fabsf(amount-1.0f)<COLOR_EPSILON)return src;
Color out;
-
+
float a_out((src.get_a()-bg.get_a())*amount+bg.get_a());
-
+
// if a_out!=0.0
if(fabsf(a_out)>COLOR_EPSILON)
// if(a_out>COLOR_EPSILON || a_out<-COLOR_EPSILON)
blendfunc_BRIGHTEN(Color &a,Color &b,float amount)
{
const float alpha(a.get_a()*amount);
-
+
if(b.get_r()<a.get_r()*alpha)
b.set_r(a.get_r()*alpha);
if(b.get_b()<a.get_b()*alpha)
b.set_b(a.get_b()*alpha);
-
+
return b;
}
blendfunc_DARKEN(Color &a,Color &b,float amount)
{
const float alpha(a.get_a()*amount);
-
+
if(b.get_r()>(a.get_r()-1.0f)*alpha+1.0f)
b.set_r((a.get_r()-1.0f)*alpha+1.0f);
if(b.get_b()>(a.get_b()-1.0f)*alpha+1.0f)
b.set_b((a.get_b()-1.0f)*alpha+1.0f);
-
+
return b;
}
// We add COLOR_EPSILON in order to avoid a divide-by-zero condition.
// This causes DIVIDE to bias toward positive values, but the effect is
- // really neglegable. There is a reason why we use COLOR_EPSILON--we
- // want the change to be imperceptable.
-
+ // really negligible. There is a reason why we use COLOR_EPSILON--we
+ // want the change to be imperceptible.
+
b.set_r(((b.get_r()/(a.get_r()+COLOR_EPSILON))-b.get_r())*(amount)+b.get_r());
b.set_g(((b.get_g()/(a.get_g()+COLOR_EPSILON))-b.get_g())*(amount)+b.get_g());
b.set_b(((b.get_b()/(a.get_b()+COLOR_EPSILON))-b.get_b())*(amount)+b.get_b());
{
Color temp(b);
temp.set_uv(a.get_u(),a.get_v());
- return (temp-b)*amount*a.get_a()+b;
+ return (temp-b)*amount*a.get_a()+b;
}
static Color
static Color
blendfunc_BEHIND(Color &a,Color &b,float amount)
{
- if(a.get_a()==0)a.set_a(COLOR_EPSILON); //!< \hack
+ if(a.get_a()==0)a.set_a(COLOR_EPSILON); //!< \todo this is a hack
a.set_a(a.get_a()*amount);
return blendfunc_COMPOSITE(b,a,1.0);
}
static Color
blendfunc_ALPHA_BRIGHTEN(Color &a,Color &b,float amount)
{
- if(a.get_a()<b.get_a()*amount)
+ // \todo can this be right, multiplying amount by *b*'s alpha?
+ // compare with blendfunc_BRIGHTEN where it is multiplied by *a*'s
+ if(a.get_a() < b.get_a()*amount)
return a.set_a(a.get_a()*amount);
return b;
}
static Color
blendfunc_ALPHA_DARKEN(Color &a,Color &b,float amount)
{
- if(a.get_a()*amount>b.get_a())
+ if(a.get_a()*amount > b.get_a())
return a.set_a(a.get_a()*amount);
return b;
}
a.set_r(1.0-(1.0f-a.get_r())*(1.0f-b.get_r()));
a.set_g(1.0-(1.0f-a.get_g())*(1.0f-b.get_g()));
a.set_b(1.0-(1.0f-a.get_b())*(1.0f-b.get_b()));
-
+
return blendfunc_ONTO(a,b,amount);
}
static Color
blendfunc_OVERLAY(Color &a,Color &b,float amount)
-{
+{
if(amount<0) a=~a, amount=-amount;
Color rm;
rs.set_b(1.0-(1.0f-a.get_b())*(1.0f-b.get_b()));
Color& ret(a);
-
+
ret.set_r(a.get_r()*rs.get_r() + (1.0-a.get_r())*rm.get_r());
ret.set_g(a.get_g()*rs.get_g() + (1.0-a.get_g())*rm.get_g());
ret.set_b(a.get_b()*rs.get_b() + (1.0-a.get_b())*rm.get_b());
#endif
}
#endif
-
+
/*
if(!a.is_valid()&&b.is_valid())
return b;
#endif
}
*/
-
+
// No matter what blend method is being used,
// if the amount is equal to zero, then only B
// will shine through
if(fabsf(amount)<=COLOR_EPSILON)return b;
-
+
assert(type<BLEND_END);
-
+
const static blendfunc vtable[BLEND_END]=
{
- blendfunc_COMPOSITE,
+ blendfunc_COMPOSITE, // 0
blendfunc_STRAIGHT,
blendfunc_BRIGHTEN,
blendfunc_DARKEN,
blendfunc_ADD,
- blendfunc_SUBTRACT,
+ blendfunc_SUBTRACT, // 5
blendfunc_MULTIPLY,
blendfunc_DIVIDE,
blendfunc_COLOR,
blendfunc_HUE,
- blendfunc_SATURATION,
+ blendfunc_SATURATION, // 10
blendfunc_LUMINANCE,
blendfunc_BEHIND,
blendfunc_ONTO,
blendfunc_ALPHA_BRIGHTEN,
- blendfunc_ALPHA_DARKEN,
+ blendfunc_ALPHA_DARKEN, // 15
blendfunc_SCREEN,
blendfunc_HARD_LIGHT,
blendfunc_DIFFERENCE,
blendfunc_ALPHA_OVER,
- blendfunc_OVERLAY,
+ blendfunc_OVERLAY, // 20
blendfunc_STRAIGHT_ONTO,
};
-
+
return vtable[type](a,b,amount);
}