diff --git a/modules/gfx/pymod/__init__.py b/modules/gfx/pymod/__init__.py
index be887c4ab071e3c4f2da12a771e7e1edad6b8b30..bd5750d32178c50d2ce7758af2a053dd2ea4c0f1 100644
--- a/modules/gfx/pymod/__init__.py
+++ b/modules/gfx/pymod/__init__.py
@@ -19,34 +19,33 @@
 from _ost_gfx import *
 from py_gfx_obj import PyGfxObj
 
-WHITE=Color(1.0,1.0,1.0)
-BLACK=Color(0.0,0.0,0.0)
-GREY=Color(0.5,0.5,0.5)
-RED=Color(1.0,0.0,0.0)
-DARKRED=Color(0.5,0.0,0.0)
-LIGHTRED=Color(1.0,0.5,0.5)
-GREEN=Color(0.0,1.0,0.0)
-DARKGREEN=Color(0.0,0.5,0.0)
-LIGHTGREEN=Color(0.5,1.0,0.5)
-BLUE=Color(0.0,0.0,1.0)
-DARKBLUE=Color(0.0,0.0,0.5)
-LIGHTBLUE=Color(0.5,0.5,1.0)
-YELLOW=Color(1.0,1.0,0.0)
-DARKYELLOW=Color(0.5,0.5,0.0)
-LIGHTYELLOW=Color(1.0,1.0,0.5)
-CYAN=Color(0.0,1.0,1.0)
-DARKCYAN=Color(0.0,0.5,0.5)
-LIGHTCYAN=Color(0.5,1.0,1.0)
-MAGENTA=Color(1.0,0.0,1.0)
-DARKMAGENTA=Color(0.5,0.0,0.5)
-LIGHTMAGENTA=Color(1.0,0.5,1.0)
+WHITE=RGB(1.0,1.0,1.0)
+BLACK=RGB(0.0,0.0,0.0)
+GREY=RGB(0.5,0.5,0.5)
+RED=RGB(1.0,0.0,0.0)
+DARKRED=RGB(0.5,0.0,0.0)
+LIGHTRED=RGB(1.0,0.5,0.5)
+GREEN=RGB(0.0,1.0,0.0)
+DARKGREEN=RGB(0.0,0.5,0.0)
+LIGHTGREEN=RGB(0.5,1.0,0.5)
+BLUE=RGB(0.0,0.0,1.0)
+DARKBLUE=RGB(0.0,0.0,0.5)
+LIGHTBLUE=RGB(0.5,0.5,1.0)
+YELLOW=RGB(1.0,1.0,0.0)
+DARKYELLOW=RGB(0.5,0.5,0.0)
+LIGHTYELLOW=RGB(1.0,1.0,0.5)
+CYAN=RGB(0.0,1.0,1.0)
+DARKCYAN=RGB(0.0,0.5,0.5)
+LIGHTCYAN=RGB(0.5,1.0,1.0)
+MAGENTA=RGB(1.0,0.0,1.0)
+DARKMAGENTA=RGB(0.5,0.0,0.5)
+LIGHTMAGENTA=RGB(1.0,0.5,1.0)
 PURPLE=MAGENTA
 DARKPURPLE=DARKMAGENTA
 LIGHTPURPLE=LIGHTMAGENTA
-ORANGE=Color(1.0,0.5,0.0)
-DARKORANGE=Color(0.5,0.25,0.0)
-LIGHTORANGE=Color(1.0,0.75,0.5)
-
+ORANGE=RGB(1.0,0.5,0.0)
+DARKORANGE=RGB(0.5,0.25,0.0)
+LIGHTORANGE=RGB(1.0,0.75,0.5)
 
 def Stereo(mode,flip=None,alg=None):
   """
diff --git a/modules/gfx/pymod/export_color.cc b/modules/gfx/pymod/export_color.cc
index 022f7669f0fae7326d58fba48d06887bf3c55c49..006331174f56347499be724dd7f2dcdb9f797cd2 100644
--- a/modules/gfx/pymod/export_color.cc
+++ b/modules/gfx/pymod/export_color.cc
@@ -25,38 +25,6 @@ using namespace ost;
 using namespace ost::gfx;
 
 namespace {
-  float get_red(const Color& c) {
-    return c[0];
-  }
-
-  void set_red(Color& c, float v) {
-    c[0]=v;
-  }
-  
-  float get_green(const Color& c) {
-    return c[1];
-  }
-  
-  void set_green(Color& c, float v) {
-    c[1]=v;
-  }
-  
-  float get_blue(const Color& c) {
-    return c[2];
-  }
-  
-  void set_blue(Color& c, float v) {
-    c[2]=v;
-  }
-  
-  float get_alpha(const Color& c) {
-    return c[3];
-  }
-
-  void set_alpha(Color& c, float v) {
-    c[3]=v;
-  }
-
   float get(const Color& c, int i) {
     if(i<0 || i>3) {
       throw Error("Color: index out of bounds");
@@ -73,36 +41,124 @@ namespace {
 
   std::string repr(const Color& c) {
     std::ostringstream m;
-    m << "gfx.Color(" << c[0] << "," << c[1] << "," << c[2] << "," << c[3] << ")";
+    m << "gfx.RGBA(" << c.GetRed() << "," << c.GetGreen() << "," << c.GetBlue() << "," << c.GetAlpha() << ")";
+    m << " gfx.HSV(" << c.GetHue() << "," << c.GetSat() << "," << c.GetVal() << ")";
     return m.str();
   }
 
+  Color rgb1(float r, float g, float b) {return RGB(r,g,b);}
+  Color rgb2(uchar r, uchar g, uchar b) {return RGB(r,g,b);}
+  Color rgb3(uint rgb) {return RGB(static_cast<uchar>((rgb>>16)&0xff),
+                                   static_cast<uchar>((rgb>>8)&0xff),
+                                   static_cast<uchar>((rgb)&0xff));}
+  Color rgba1(float r, float g, float b, float a) {return RGBA(r,g,b,a);}
+  Color rgba2(uchar r, uchar g, uchar b, uchar a) {return RGBA(r,g,b,a);}
+  Color rgba3(uint rgba) {return RGBA(static_cast<uchar>((rgba>>24)&0xff),
+                                      static_cast<uchar>((rgba>>16)&0xff),
+                                      static_cast<uchar>((rgba>>8)&0xff),
+                                      static_cast<uchar>((rgba)&0xff));}
+
+  tuple get_rgb(const Color& c) {return make_tuple(c.GetRed(),c.GetGreen(),c.GetBlue());}
+  void set_rgb(Color& c, object rgb) {
+    extract<geom::Vec3&> vec3(rgb);
+    if(vec3.check()) {
+      c.SetRGB(vec3()[0],vec3()[1],vec3()[2]);
+    } else {
+      // assume sequence
+      c.SetRGB(extract<float>(rgb[0]),
+               extract<float>(rgb[1]),
+               extract<float>(rgb[2]));
+
+    }
+  }
+
+  tuple get_rgba(const Color& c) {return make_tuple(c.GetRed(),c.GetGreen(),c.GetBlue(),c.GetAlpha());}
+  void set_rgba(Color& c, object rgba) {
+    extract<geom::Vec4&> vec4(rgba);
+    if(vec4.check()) {
+      c.SetRGB(vec4()[0],vec4()[1],vec4()[2]);
+      c.SetAlpha(vec4()[3]);
+    } else {
+      // assume sequence
+      c.SetRGB(extract<float>(rgba[0]),
+               extract<float>(rgba[1]),
+               extract<float>(rgba[2]));
+      c.SetAlpha(extract<float>(rgba[3]));
+    }
+  }
+
+  tuple get_hsv(const Color& c) {return make_tuple(c.GetHue(),c.GetSat(),c.GetVal());}
+  void set_hsv(Color& c, object hsv) {
+    extract<geom::Vec3&> vec3(hsv);
+    if(vec3.check()) {
+      c.SetHSV(vec3()[0],vec3()[1],vec3()[2]);
+    } else {
+      // assume sequence
+      c.SetHSV(extract<float>(hsv[0]),
+               extract<float>(hsv[1]),
+               extract<float>(hsv[2]));
+    }
+  }
+
+  tuple get_hsva(const Color& c) {return make_tuple(c.GetHue(),c.GetSat(),c.GetVal(),c.GetAlpha());}
+  void set_hsva(Color& c, object hsva) {
+    extract<geom::Vec4&> vec4(hsva);
+    if(vec4.check()) {
+      c.SetHSV(vec4()[0],vec4()[1],vec4()[2]);
+      c.SetAlpha(vec4()[3]);
+    } else {
+      // assume sequence
+      c.SetHSV(extract<float>(hsva[0]),
+               extract<float>(hsva[1]),
+               extract<float>(hsva[2]));
+      c.SetAlpha(extract<float>(hsva[3]));
+    }
+  }
+
 }
 
 void export_color()
 {
   class_<Color>("Color",init<>())
-    .def(init<float, float, float, optional<float> >())
     .def(self_ns::str(self))
     .def("__repr__",repr)
-    .def("Red",get_red)
-    .def("Green",get_green)
-    .def("Blue",get_blue)
-    .def("Alpha",get_alpha)
+    .add_property("r",&Color::GetRed,&Color::SetRed)
+    .add_property("g",&Color::GetGreen,&Color::SetGreen)
+    .add_property("b",&Color::GetBlue,&Color::SetBlue)
+    .add_property("a",&Color::GetAlpha,&Color::SetAlpha)
+    .add_property("h",&Color::GetHue,&Color::SetHue)
+    .add_property("s",&Color::GetSat,&Color::SetSat)
+    .add_property("v",&Color::GetVal,&Color::SetVal)
+    .add_property("red",&Color::GetRed,&Color::SetRed)
+    .add_property("green",&Color::GetGreen,&Color::SetGreen)
+    .add_property("blue",&Color::GetBlue,&Color::SetBlue)
+    .add_property("alpha",&Color::GetAlpha,&Color::SetAlpha)
+    .add_property("hue",&Color::GetHue,&Color::SetHue)
+    .add_property("sat",&Color::GetSat,&Color::SetSat)
+    .add_property("val",&Color::GetVal,&Color::SetVal)
+    .add_property("rgb",get_rgb,set_rgb)
+    .add_property("rgba",get_rgba,set_rgba)
+    .add_property("hsv",get_hsv,set_hsv)
+    .add_property("hsva",get_hsva,set_hsva)
+    .def("__getitem__",get)
+    .def("__setitem__",set)
+    // DEPRECATED
+    .def(init<float, float, float, optional<float> >())
+    .def("Red",&Color::GetRed)
+    .def("Green",&Color::GetGreen)
+    .def("Blue",&Color::GetBlue)
+    .def("Alpha",&Color::GetAlpha)
     .def("ToHSV",&Color::ToHSV)
     .def("FromRGBA",&Color::FromRGB)
-    .add_property("r",get_red,set_red)
-    .add_property("g",get_green,set_green)
-    .add_property("b",get_blue,set_blue)
-    .add_property("a",get_alpha,set_alpha)
-    .add_property("red",get_red,set_red)
-    .add_property("green",get_green,set_green)
-    .add_property("blue",get_blue,set_blue)
-    .add_property("alpha",get_alpha,set_alpha)
-    .def("__getitem__",get)
-    .def("__setitem__",get)
     ;
 
+  def("RGB",rgb3);
+  def("RGB",rgb2);
+  def("RGB",rgb1);
+  def("RGBA",rgba3);
+  def("RGBA",rgba2);
+  def("RGBA",rgba1);
   def("HSV",HSV);
+  def("HSVA",HSVA);
   
 }
diff --git a/modules/gfx/src/color.cc b/modules/gfx/src/color.cc
index f40e69c53629e136a661b380424d4e075419ae50..124d7aabcef3894d4a8fe1b155b77bb6e88bef9e 100644
--- a/modules/gfx/src/color.cc
+++ b/modules/gfx/src/color.cc
@@ -26,179 +26,385 @@
 
 namespace ost { namespace gfx {
 
-namespace {
-// maps hsv to rgb (0-1)
-geom::Vec3 HSVtoRGB(const geom::Vec3& hsv)
-{
-  geom::Vec3 rgb;
-  if (hsv[1]<1e-9){
-    rgb[0]=hsv[2];
-    rgb[1]=hsv[2];
-    rgb[2]=hsv[2];
+Color::Color()
+{
+  rgba_[0]=1.0;
+  rgba_[1]=1.0;
+  rgba_[2]=1.0;
+  rgba_[3]=1.0;
+  rgb_dirty_=false;
+  hsv_dirty_=true;
+}
+
+Color::Color(float r, float g, float b, float a) 
+{
+  rgba_[0]=r;
+  rgba_[1]=g;
+  rgba_[2]=b;
+  rgba_[3]=a;
+  rgb_dirty_=false;
+  hsv_dirty_=true;
+}
+  
+void Color::SetRGB(float r, float g, float b)
+{
+  rgba_[0]=r;
+  rgba_[1]=g;
+  rgba_[2]=b;
+  rgb_dirty_=false;
+  hsv_dirty_=true;
+}
+
+geom::Vec3 Color::GetRGB() const
+{
+  if(rgb_dirty_) to_rgb();
+  return geom::Vec3(rgba_);
+}
+
+geom::Vec4 Color::GetRGBA() const
+{
+  if(rgb_dirty_) to_rgb();
+  return geom::Vec3(rgba_);
+}
+
+float Color::GetRed() const
+{
+  if(rgb_dirty_) to_rgb();
+  return rgba_[0];
+}
+
+void Color::SetRed(float x)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[0]=x;
+  hsv_dirty_=true;
+}
+
+float Color::GetGreen() const
+{
+  if(rgb_dirty_) to_rgb();
+  return rgba_[1];
+}
+
+void Color::SetGreen(float x)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[1]=x;
+  hsv_dirty_=true;
+}
+
+float Color::GetBlue() const
+{
+  if(rgb_dirty_) to_rgb();
+  return rgba_[2];
+}
+
+void Color::SetBlue(float x)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[2]=x;
+  hsv_dirty_=true;
+}
+
+void Color::SetHSV(float h, float s, float v)
+{
+  hsv_[0]=h;
+  hsv_[1]=s;
+  hsv_[2]=v;
+  hsv_dirty_=false;
+  rgb_dirty_=true;
+}
+
+geom::Vec3 Color::GetHSV() const
+{
+  if(hsv_dirty_) to_hsv();
+  return geom::Vec3(hsv_);
+}
+
+geom::Vec4 Color::GetHSVA() const
+{
+  if(hsv_dirty_) to_hsv();
+  return geom::Vec4(hsv_[0],hsv_[1],hsv_[2],rgba_[3]);
+}
+
+float Color::GetHue() const
+{
+  if(hsv_dirty_) to_hsv();
+  return hsv_[0];
+}
+
+void Color::SetHue(float x)
+{
+  if(hsv_dirty_) to_hsv();
+  hsv_[0]=x;
+  rgb_dirty_=true;
+}
+
+float Color::GetSat() const
+{
+  if(hsv_dirty_) to_hsv();
+  return hsv_[1];
+}
+
+void Color::SetSat(float x)
+{
+  if(hsv_dirty_) to_hsv();
+  hsv_[1]=x;
+  rgb_dirty_=true;
+}
+
+float Color::GetVal() const
+{
+  if(hsv_dirty_) to_hsv();
+  return hsv_[2];
+}
+
+void Color::SetVal(float x)
+{
+  if(hsv_dirty_) to_hsv();
+  hsv_[2]=x;
+  rgb_dirty_=true;
+}
+
+float Color::GetAlpha() const
+{
+  return rgba_[3];
+}
+
+void Color::SetAlpha(float x)
+{
+  rgba_[3]=x;
+}
+
+////////////////////////////////////
+// operators
+
+Color::operator const float* () const 
+{
+  if(rgb_dirty_) to_rgb();
+  return rgba_;
+}
+
+Color::operator float* () 
+{
+  if(rgb_dirty_) to_rgb();
+  hsv_dirty_=true; 
+  return rgba_;
+}
+
+Color& Color::operator*=(float rhs)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[0]*=rhs;
+  rgba_[1]*=rhs;
+  rgba_[2]*=rhs;
+  rgba_[3]*=rhs; 
+  hsv_dirty_=true; 
+  return *this;
+}
+
+Color& Color::operator+=(float rhs)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[0]+=rhs;
+  rgba_[1]+=rhs;
+  rgba_[2]+=rhs;
+  rgba_[3]+=rhs;
+  hsv_dirty_=true; 
+  return *this;
+}
+  
+Color& Color::operator+=(const Color& rhs)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[0]+=rhs[0];
+  rgba_[1]+=rhs[1];
+  rgba_[2]+=rhs[2];
+  rgba_[3]+=rhs[3];  
+  hsv_dirty_=true; 
+  return *this;  
+}
+
+Color& Color::operator-=(const Color& rhs)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[0]-=rhs[0];
+  rgba_[1]-=rhs[1];
+  rgba_[2]-=rhs[2];
+  rgba_[3]-=rhs[3];  
+  hsv_dirty_=true; 
+  return *this;
+}
+
+Color& Color::operator-=(float rhs)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[0]-=rhs;
+  rgba_[1]-=rhs;
+  rgba_[2]-=rhs;
+  rgba_[3]-=rhs;
+  hsv_dirty_=true; 
+  return *this;
+}
+
+Color& Color::operator/=(float rhs)
+{
+  if(rgb_dirty_) to_rgb();
+  rgba_[0]/=rhs;
+  rgba_[1]/=rhs;
+  rgba_[2]/=rhs;
+  rgba_[3]/=rhs;  
+  hsv_dirty_=true; 
+  return *this;
+}
+
+//////////////////////////////////////
+// private methods
+
+void Color::to_rgb() const
+{
+  float hh=fmod(hsv_[0],1.0);
+  if(hh<0.0) hh+=1.0;
+  float ss=std::min(1.0f,std::max(0.0f,hsv_[1]));
+  float vv=std::min(1.0f,std::max(0.0f,hsv_[2]));
+  if (ss<1e-9){
+    rgba_[0]=vv;
+    rgba_[1]=vv;
+    rgba_[2]=vv;
   } else {
-    double var_h=hsv[0]*6.0<6.0?hsv[0]*6.0:0.0;
+    float var_h=hh*6.0;
     int var_i =static_cast<int>(var_h);
-    double var_1 = hsv[2]*(1-hsv[1]);
-    double var_2 = hsv[2]*(1-hsv[1]*( var_h -var_i));
-    double var_3 = hsv[2]*(1-hsv[1]*(1-(var_h-var_i)));
+    float var_1 = vv*(1-ss);
+    float var_2 = vv*(1-ss*( var_h - static_cast<float>(var_i)));
+    float var_3 = vv*(1-ss*(1-(var_h-static_cast<float>(var_i))));
     switch(var_i){
     case 0:
-      rgb[0]=hsv[2];
-      rgb[1]=var_3;
-      rgb[2]=var_1;
+      rgba_[0]=vv;
+      rgba_[1]=var_3;
+      rgba_[2]=var_1;
       break;
     case 1:
-      rgb[0] = var_2;
-      rgb[1] = hsv[2];
-      rgb[2] = var_1;
+      rgba_[0] = var_2;
+      rgba_[1] = vv;
+      rgba_[2] = var_1;
       break;
     case 2:
-      rgb[0] = var_1;
-      rgb[1] = hsv[2];
-      rgb[2] = var_3;
+      rgba_[0] = var_1;
+      rgba_[1] = vv;
+      rgba_[2] = var_3;
       break;
     case 3:
-      rgb[0] = var_1 ; 
-      rgb[1] = var_2 ; 
-      rgb[2] = hsv[2];
+      rgba_[0] = var_1 ; 
+      rgba_[1] = var_2 ; 
+      rgba_[2] = vv;
       break;
     case 4:
-      rgb[0] = var_3 ;
-      rgb[1] = var_1 ;
-      rgb[2] = hsv[2];
+      rgba_[0] = var_3 ;
+      rgba_[1] = var_1 ;
+      rgba_[2] = vv;
       break;
     case 5:
-      rgb[0] = hsv[2];
-      rgb[1] = var_1 ;
-      rgb[2] = var_2;
+      rgba_[0] = vv;
+      rgba_[1] = var_1 ;
+      rgba_[2] = var_2;
       break;
     }
   }
-  return rgb;
+  rgb_dirty_=false;
 }
 
-// maps rgb (0-1) to hsv
-geom::Vec3 RGBtoHSV(const geom::Vec3& rgb)
+void Color::to_hsv() const
 {
-  geom::Vec3 hsv;
-  double var_R = ( rgb[0] );
-  double var_G = ( rgb[1] );
-  double var_B = ( rgb[2] );
+  float rr = std::min(1.0f,std::max(0.0f,rgba_[0]));
+  float gg = std::min(1.0f,std::max(0.0f,rgba_[1]));
+  float bb = std::min(1.0f,std::max(0.0f,rgba_[2]));
 
-  double var_Min = std::min(std::min( var_R, var_G), var_B );
-  double var_Max = std::max(std::max( var_R, var_G), var_B );
-  double del_Max = var_Max - var_Min;
+  float vmin = std::min(std::min(rr, gg), bb);
+  float vmax = std::max(std::max(rr, gg), bb);
+  float vdelta = vmax - vmin;
 
-  hsv[2] = var_Max;
+  hsv_[2] = vmax;
 
-  if ( del_Max < 1.0e-9 ){
-    hsv[0] = 0.0;
-    hsv[1] = 0.0;
+  if (vdelta < 1.0e-9) {
+    hsv_[0] = 0.0;
+    hsv_[1] = 0.0;
   } else {
-    hsv[1] = del_Max / var_Max;
-    double del_R = ( ( ( var_Max - var_R ) / 6.0 ) + ( del_Max / 2.0 ) ) / del_Max;
-    double del_G = ( ( ( var_Max - var_G ) / 6.0 ) + ( del_Max / 2.0 ) ) / del_Max;
-    double del_B = ( ( ( var_Max - var_B ) / 6.0 ) + ( del_Max / 2.0 ) ) / del_Max;
+    hsv_[1] = vdelta / vmax;
+    float dr = (((vmax - rr) / 6.0) + (vdelta / 2.0)) / vdelta;
+    float dg = (((vmax - gg) / 6.0) + (vdelta / 2.0)) / vdelta;
+    float db = (((vmax - bb) / 6.0) + (vdelta / 2.0)) / vdelta;
     
-    if ( var_R == var_Max ){
-      hsv[0] = del_B - del_G;
-    } else if ( var_G == var_Max ){
-      hsv[0] = ( 1.0 / 3.0 ) + del_R - del_B;
-    } else if ( var_B == var_Max ){
-      hsv[0] = ( 2.0 / 3.0 ) + del_G - del_R;
+    if (rr == vmax) {
+      hsv_[0] = db - dg;
+    } else if (gg == vmax) {
+      hsv_[0] = (1.0 / 3.0) + dr - db;
+    } else if (bb == vmax) {
+      hsv_[0] = (2.0 / 3.0) + dg - dr;
     }
-    if ( hsv[0] < 0.0 ){
-      hsv[0] += 1.0;
+    if (hsv_[0] < 0.0) {
+      hsv_[0] += 1.0;
     }
-    if ( hsv[0] > 1.0 ){
-      hsv[0] -= 1.0;
+    if (hsv_[0] > 1.0) {
+      hsv_[0] -= 1.0;
     }
   }
-
-  hsv[0]=hsv[0]*360.0;
-  hsv[1]=hsv[1]*100.0;
-  hsv[2]=hsv[2]*100.0;
-
-  return hsv;
+  hsv_dirty_=false;
 }
-} // anon ns
 
-geom::Vec3 Color::ToHSV()
-{
-  return RGBtoHSV(geom::Vec3(rgba[0],rgba[1],rgba[2]));
-}
+////////////////////////////////////
+// stand alone functions
 
-
-Color HSV(double h, double s, double v)
+Color RGB(float r, float g, float b)
 {
-  if(h>1.0 || s>1.0 || v>1.0) {
-    h=h/360.0;
-    s=s/100.0;
-    v=v/100.0;
-  }
-  geom::Vec3 rgb=HSVtoRGB(geom::Vec3(h,s,v));
-  return Color(rgb[0],rgb[1],rgb[2]);
+  Color nrvo;
+  nrvo.SetRGB(r,g,b);
+  return nrvo;
 }
 
-std::ostream& operator<<(std::ostream& s, const Color& c)
+Color RGB(uchar r, uchar g, uchar b)
 {
-  s << "{" << c.Red() << "," << c.Green() << "," << c.Blue() << "," << c.Alpha() << "}";
-  return s;
+  static float f=1.0/255.0;
+  Color nrvo;
+  nrvo.SetRGB(static_cast<float>(r)*f,static_cast<float>(g)*f,static_cast<float>(b)*f);
+  return nrvo;
 }
 
-
-Color& Color::operator*=(float rhs)
+Color RGBA(float r, float g, float b, float a)
 {
-  rgba[0]*=rhs;
-  rgba[1]*=rhs;
-  rgba[2]*=rhs;
-  rgba[3]*=rhs; 
-  return *this;
+  Color nrvo;
+  nrvo.SetRGB(r,g,b);
+  nrvo.SetAlpha(a);
+  return nrvo;
 }
 
-Color& Color::operator+=(float rhs)
-{
-  rgba[0]+=rhs;
-  rgba[1]+=rhs;
-  rgba[2]+=rhs;
-  rgba[3]+=rhs;
-  return *this;
-}
-  
-Color& Color::operator+=(const Color& rhs)
+Color RGBA(uchar r, uchar g, uchar b, uchar a)
 {
-  rgba[0]+=rhs[0];
-  rgba[1]+=rhs[1];
-  rgba[2]+=rhs[2];
-  rgba[3]+=rhs[3];  
-  return *this;  
+  static float f=1.0/255.0;
+  Color nrvo;
+  nrvo.SetRGB(static_cast<float>(r)*f,static_cast<float>(g)*f,static_cast<float>(b)*f);
+  nrvo.SetAlpha(static_cast<float>(a)*f);
+  return nrvo;
 }
 
-Color& Color::operator-=(const Color& rhs)
+Color HSV(float h, float s, float v)
 {
-  rgba[0]-=rhs[0];
-  rgba[1]-=rhs[1];
-  rgba[2]-=rhs[2];
-  rgba[3]-=rhs[3];  
-  return *this;
+  Color nrvo;
+  nrvo.SetHSV(h,s,v);
+  return nrvo;
 }
 
-Color& Color::operator-=(float rhs)
+  Color HSVA(float h, float s, float v, float a)
 {
-  rgba[0]-=rhs;
-  rgba[1]-=rhs;
-  rgba[2]-=rhs;
-  rgba[3]-=rhs;
-  return *this;
+  Color nrvo;
+  nrvo.SetHSV(h,s,v);
+  nrvo.SetAlpha(a);
+  return nrvo;
 }
 
-Color& Color::operator/=(float rhs)
+std::ostream& operator<<(std::ostream& s, const Color& c)
 {
-  rgba[0]/=rhs;
-  rgba[1]/=rhs;
-  rgba[2]/=rhs;
-  rgba[3]/=rhs;  
-  return *this;
+  s << "Color(r=" << c.GetRed() << ",g=" <<c.GetGreen() << ",b=" << c.GetBlue() << ",h=" << c.GetHue() << ",s=" << c.GetSat() << ",v=" << c.GetVal() << ",a=" << c.GetAlpha() << ")";
+  return s;
 }
 
 }} // ns
diff --git a/modules/gfx/src/color.hh b/modules/gfx/src/color.hh
index 533381dbc07d06880f2f2b3ab00a2b1a3e72c0f3..e97b085dc8968bf9178bed508c8b8e7585ad339b 100644
--- a/modules/gfx/src/color.hh
+++ b/modules/gfx/src/color.hh
@@ -32,35 +32,114 @@
 
 namespace ost { namespace gfx {
 
-/// \brief color with red, green, blue and alpha component
+/*!
+  A color is defined in both RGB as well as HSV space, with an 
+  additional A component. To initialize a color use the factory
+  functions RGB(r,g,b), RGBA(r,g,b,a), HSV(h,s,v) and HSVA(h,s,v,a)
+
+  All values are defined in the range of 0.0 to 1.0, but are not clamped
+  on reading or writing, except when updating cross-domain, i.e. changes 
+  to R,G or B cause HSV to be updated accordingly, and vice versa.
+  This may cause previously out-of-bounds values to become clamped or wrapped
+  to the defined domain. An out-of-bounds H will be wrapped to be between 0 and 1, 
+  all others will be clamped.
+
+  For interaction with OpenGL, an operator float* method exists that
+  return a pointer to a float[4], in the order RGBA
+
+  Operator overloading works on the RGB components, i.e. C1+C2, C+s, C*s
+*/
 class DLLEXPORT_OST_GFX Color :   
   private boost::additive<Color>,
   private boost::additive<Color, float>,
   private boost::multiplicative<Color, float>{
+
 public:
-  Color() {
-    rgba[0]=1.0;
-    rgba[1]=1.0;
-    rgba[2]=1.0;
-    rgba[3]=1.0;
-  }
+  //! \brief initialize to white
+  Color();
+
+  //! convenience to set RGB triplet (ranges 0-1)
+  void SetRGB(float r, float g, float b);
+  //! convenience to get RGB triplet as Vec3
+  geom::Vec3 GetRGB() const;
+  //! convenience to get RGBA as Vec4
+  geom::Vec4 GetRGBA() const;
+  //! retrieve red
+  float GetRed() const;
+  //! set red
+  void SetRed(float);
+  //! retrieve green
+  float GetGreen() const;
+  //! set green
+  void SetGreen(float);
+  //! retrieve blue
+  float GetBlue() const;
+  //! set blue
+  void SetBlue(float);
+  //! convenience to set HSV triplet (ranges 0-1)
+  void SetHSV(float h, float s, float v);
+  //! convenience to get HSV triplet as Vec3
+  geom::Vec3 GetHSV() const;
+  //! convenience to get HSVA as Vec4
+  geom::Vec4 GetHSVA() const;
+  //! retrieve hue
+  float GetHue() const;
+  //! set hue
+  void SetHue(float);
+  //! retrieve saturatuin
+  float GetSat() const;
+  //! set saturation
+  void SetSat(float);
+  //! retrieve value
+  float GetVal() const;
+  //! set value
+  void SetVal(float);
+  //! retrieve alpha
+  float GetAlpha() const;
+  //! set alpha
+  void SetAlpha(float);
+
   
-  Color(float r, float g, float b, float a=1.0) {
-    rgba[0]=r;
-    rgba[1]=g;
-    rgba[2]=b;
-    rgba[3]=a;
-  }
+  /*!
+    \brief direct access to RGBA components
+
+    In the context of a OpenGL call that requires a pointer
+    to an RGB or RGBA float triplet or quadruplet, this will
+    do the automatic casting from a Color object, i.e. 
+    glColor3fv(my_color);
+
+    This will also allow access to the RGBA values
+    via the array notation, i.e. my_color[0]
+  */
+  operator const float* () const;
+  operator float* ();
+  
+  Color& operator*=(float rhs);
+  Color& operator+=(float rhs);
+    
+  Color& operator+=(const Color& rhs);  
+  Color& operator-=(const Color& rhs);    
+  Color& operator-=(float rhs);
+  Color& operator/=(float rhs);  
 
-  float& Red() {return rgba[0];}
-  const float& Red() const {return rgba[0];}
-  float& Green() {return rgba[1];}
-  const float& Green() const {return rgba[1];}
-  float& Blue() {return rgba[2];}
-  const float& Blue() const {return rgba[2];}
-  float& Alpha() {return rgba[3];}
-  const float& Alpha() const {return rgba[3];}
+  ////////////////////////////////////////////////////////////////
 
+  //! DEPRECATED
+  geom::Vec3 ToHSV() const {return GetHSV();}
+
+  //! DEPRECATED
+  Color(float r, float g, float b, float a=1.0);
+
+  //! DEPRECATED
+  float Red() const {return GetRed();}
+  //! DEPRECATED
+  float Green() const {return GetGreen();}
+  //! DEPRECATED
+  float Blue() const {return GetBlue();}
+  //! DEPRECATED
+  float Alpha() const {return GetAlpha();}
+
+  //! DEPRECATED
   static Color FromRGB(unsigned char r, unsigned char g, 
                        unsigned char b, unsigned char a = 0xff) {
     static float f=1.0/255.0;
@@ -68,32 +147,53 @@ public:
                  f*static_cast<float>(b),f*static_cast<float>(a));
   }
 
-  geom::Vec3 ToHSV();
+  ////////////////////////////////////////////////////////////////
 
-  // these also take care of operator[](uint i) !
-  operator float* () {return rgba;}
-  operator const float* () const {return rgba;}
-  
-  Color& operator*=(float rhs);
-  Color& operator+=(float rhs);
-    
-  Color& operator+=(const Color& rhs);  
-  Color& operator-=(const Color& rhs);    
-  Color& operator-=(float rhs);
-  Color& operator/=(float rhs);  
 private:
-  float rgba[4];
+  void to_hsv() const;
+  void to_rgb() const;
+
+  mutable float rgba_[4];
+  mutable float hsv_[3];
+  mutable bool rgb_dirty_;
+  mutable bool hsv_dirty_;
 };
 
+/*
+  \brief RGB color spec from floats (0.0-1.0)
+*/
+Color DLLEXPORT_OST_GFX RGB(float r, float g, float b);
+
+/*
+  \brief RGB color spec from bytes (0-255)
+*/
+Color DLLEXPORT_OST_GFX RGB(uchar r, uchar g, uchar b);
+
+/*
+  \brief RGBA color spec from floats (0.0-1.0)
+*/
+Color DLLEXPORT_OST_GFX RGBA(float r, float g, float b, float a);
+
+/*
+  \brief RGBA color spec from bytes (0-255)
+*/
+Color DLLEXPORT_OST_GFX RGBA(uchar r, uchar g, uchar b, uchar a);
+
 /*!
   \brief HSV color spec
 
-  h: Hue from 0 to 360 (0=red, 120=green, 240=blue)
-  s: Saturation from 0 (no color) to 100 (full color)
-  v: Value from 0 (no light, black) to 100 (full light)
+  h: Hue from 0 to 1 (0=red, 2/6=green, 4/6=blue)
+  s: Saturation from 0 (no color) to 1 (full color)
+  v: Value from 0 (no light, black) to 1 (full light)
+*/
+Color DLLEXPORT_OST_GFX HSV(float h, float s, float v);
+
+/*!
+  \brief HSVA color spec
 */
-Color DLLEXPORT_OST_GFX HSV(double h, double s, double v);
+Color DLLEXPORT_OST_GFX HSVA(float h, float s, float v, float a);
 
+//! \brief string form
 DLLEXPORT_OST_GFX std::ostream& operator<<(std::ostream&, const Color& c);
 
 }}
diff --git a/modules/gfx/src/impl/cartoon_renderer.cc b/modules/gfx/src/impl/cartoon_renderer.cc
index 9af2bdf80e4b5e837d6ac8d0be849d700a87f365..f9eee510679686554c1f7bfac9fd6550f7e3f837 100644
--- a/modules/gfx/src/impl/cartoon_renderer.cc
+++ b/modules/gfx/src/impl/cartoon_renderer.cc
@@ -362,8 +362,9 @@ void CartoonRenderer::rebuild_spline_obj(IndexedVertexArray& va,
     if(slist.size()==2 && slist[0].type==6) {
       // make a cylinder
       va.AddCylinder(CylinderPrim(slist[0].position,slist[1].position,
-				  options_->GetHelixWidth(),
-				  slist[0].color1,slist[1].color1),
+                                  options_->GetHelixWidth(),
+                                  slist[0].color1,
+                                  slist[1].color1),
 		     options_->GetArcDetail(),true);
       continue;
     }
@@ -458,7 +459,7 @@ TraceProfile CartoonRenderer::transform_and_add_profile(const std::vector<TraceP
       norm=Normalize(norm);
     }
     tf_prof[c]=TraceProfileEntry(vec,norm);
-    Color col = se.color1;
+    RGBAColor col = se.color1;
     if(se.type==1) {
       if(se.nflip) {
         col = c<half ? se.color1 : se.color2;
@@ -523,7 +524,7 @@ void CartoonRenderer::cap_profile(const impl::TraceProfile& p,
                                   bool flipn, IndexedVertexArray& va)
 {
   geom::Vec3 norm=flipn ? -se.direction : se.direction;
-  VertexID pi0 = va.Add(se.position,norm, se.color1,geom::Vec2(0.5,0.5));
+  VertexID pi0 = va.Add(se.position,norm, se.color1, geom::Vec2(0.5,0.5));
   std::vector<VertexID> vertices(p.size());
   float fac=2.0*M_PI/static_cast<float>(p.size()-1);
   for(unsigned int i=0;i<p.size();++i) {
diff --git a/modules/gfx/src/impl/cpk_renderer.cc b/modules/gfx/src/impl/cpk_renderer.cc
index b0aff5ec60cb74a21625b25ad267f401fad74549..f780647b745c51658d12fd3e97dbc05359159baf 100644
--- a/modules/gfx/src/impl/cpk_renderer.cc
+++ b/modules/gfx/src/impl/cpk_renderer.cc
@@ -56,7 +56,7 @@ void CPKRenderer::PrepareRendering()
 
 void CPKRenderer::PrepareRendering(GfxView& view, IndexedVertexArray& va, bool is_sel)
 {
-  const Color& sel_clr=this->GetSelectionColor();
+  RGBAColor sel_clr=this->GetSelectionColor();
   float factor=is_sel ? 1.2 : 1.0;
   if(options_!=NULL){
     factor *= options_->GetRadiusMult();
diff --git a/modules/gfx/src/impl/entity_detail.cc b/modules/gfx/src/impl/entity_detail.cc
index 3069077f4ea17e615aaf112742d993f1fc7a1bfa..02e40b2138edee1e989c80a6680fee04fedbc897 100644
--- a/modules/gfx/src/impl/entity_detail.cc
+++ b/modules/gfx/src/impl/entity_detail.cc
@@ -39,7 +39,7 @@ struct BlurQuadEntry
 {
   float zdist;
   geom::Vec3 p1,p2,p3,p4;
-  gfx::Color c1,c2,c3,c4;
+  RGBAColor c1,c2,c3,c4;
 };
 
 struct BlurQuadEntryLess
diff --git a/modules/gfx/src/impl/entity_detail.hh b/modules/gfx/src/impl/entity_detail.hh
index 5905d6b870ebeda937cde84a7eb9c4e85dd481bb..12412f680236afc25f67556971b76f8eac8f643d 100644
--- a/modules/gfx/src/impl/entity_detail.hh
+++ b/modules/gfx/src/impl/entity_detail.hh
@@ -39,13 +39,38 @@
 
 namespace ost { namespace gfx { namespace impl {
 
+struct RGBAColor {
+  RGBAColor() {
+    rgba[0]=1.0; rgba[1]=1.0; rgba[2]=1.0; rgba[3]=1.0;
+  }
+  RGBAColor(float r, float g, float b, float a=1.0) {
+    rgba[0]=r; rgba[1]=g; rgba[2]=b; rgba[3]=a;
+  }
+  RGBAColor(const Color& c) {
+    rgba[0]=c.GetRed(); rgba[1]=c.GetGreen(); rgba[2]=c.GetBlue(); rgba[3]=c.GetAlpha();
+  }
+
+  RGBAColor& operator=(const Color& c) {
+    rgba[0]=c.GetRed(); rgba[1]=c.GetGreen(); rgba[2]=c.GetBlue(); rgba[3]=c.GetAlpha();
+    return *this;
+  }
+
+  operator Color () const {return RGBA(rgba[0],rgba[1],rgba[2],rgba[3]);}
+
+  operator float* () {return rgba;}
+  operator const float* () const {return rgba;}
+  float rgba[4];
+};
+
 struct DLLEXPORT_OST_GFX AtomEntry 
 {
   AtomEntry() {}
   AtomEntry(const mol::AtomHandle& a, float r, float v, const Color& c):
-    atom(a),color(c),radius(r), vdwr(v) {}
+    atom(a), color(c), radius(r), vdwr(v) 
+  {
+  }
   mol::AtomHandle atom;
-  Color color;
+  RGBAColor color;
   float radius;
   float vdwr;
 };
@@ -88,7 +113,7 @@ typedef boost::shared_ptr<GfxView> GfxViewPtr;
 
 struct DLLEXPORT_OST_GFX NodeEntry {
   mol::AtomHandle atom;
-  Color color1, color2;
+  RGBAColor color1, color2;
   geom::Vec3 direction,normal;
   float rad;
   geom::Vec3 v0,v1,v2; // helper vectors
@@ -131,6 +156,16 @@ struct DLLEXPORT_OST_GFX SplineEntry {
     id(-1)
   {
   }
+  SplineEntry(const geom::Vec3& p, 
+              const geom::Vec3& d,
+              const geom::Vec3& n,
+              float r,
+              const RGBAColor& c1, const RGBAColor& c2,
+              unsigned int t, int i):
+    position(p),direction(d),normal(n),color1(c1),color2(c2),rad(r),type(t),
+    type1(t),type2(t),frac(0.0),running_length(0.0),v0(),v1(),v2(),nflip(false),id(i)
+  {
+  }
   SplineEntry(const geom::Vec3& p, 
               const geom::Vec3& d,
               const geom::Vec3& n,
@@ -143,7 +178,7 @@ struct DLLEXPORT_OST_GFX SplineEntry {
   }
 
   geom::Vec3 position,direction,normal;
-  Color color1, color2;
+  RGBAColor color1, color2;
   float rad;
   unsigned int type;
   unsigned int type1, type2;
diff --git a/modules/gfx/src/texture.cc b/modules/gfx/src/texture.cc
index 56ea7555360c05de3d4b2d0e36c5c446275f62b4..d708daa1a19629a5b4229d047c0fc6ed58b95d0b 100644
--- a/modules/gfx/src/texture.cc
+++ b/modules/gfx/src/texture.cc
@@ -1,4 +1,5 @@
 #include "texture.hh"
+#include "bitmap_io.hh"
 
 namespace ost { namespace gfx {
 
@@ -7,12 +8,12 @@ namespace ost { namespace gfx {
     d_()
   {
     if(!bm.data) return;
-    d_=boost::shared_array<Color>(new Color[w_*h_]);
+    d_=boost::shared_array<float>(new float[4*w_*h_]);
     static float f=1.0/255.0;
     for(GLint v=0;v<h_;++v) {
       for(GLint u=0;u<w_;++u) {
         int p=v*w_+u;
-        Color& c = d_[p];
+        float* c = &d_[p*4];
         if(bm.channels==1) {
           c[0]=f*static_cast<float>(bm.data[p]);
           c[1]=c[0];
diff --git a/modules/gfx/src/texture.hh b/modules/gfx/src/texture.hh
index b3a53dd2718786a747d3bd7fdf8f8fabf3c8c822..dd71404feb5f946a0965416546e7f57b42467fc3 100644
--- a/modules/gfx/src/texture.hh
+++ b/modules/gfx/src/texture.hh
@@ -29,11 +29,11 @@
 
 #include "module_config.hh"
 #include "glext_include.hh"
-#include "color.hh"
-#include "bitmap_io.hh"
 
 namespace ost { namespace gfx {
 
+class Bitmap;
+
 class Texture
 {
 public:
@@ -46,17 +46,14 @@ public:
   Texture(GLint w, GLint h):
     w_(w),
     h_(h),
-    d_(new Color[w*h])
+    d_(new float[4*w*h])
   {}
 
   Texture(const Bitmap& b);
 
   bool IsValid() const {return d_;}
 
-  Color& operator()(uint u, uint v) {return d_[v*w_+u];}
-  const Color& operator()(uint u, uint v) const {return d_[v*w_+u];}
-
-  float* data() {return d_[0];}
+  float* data() {return &d_[0];}
   
   GLint width() const {return w_;}
   GLint height() const {return h_;}
@@ -66,7 +63,7 @@ public:
 
 private:
   GLint w_,h_;
-  boost::shared_array<Color> d_;
+  boost::shared_array<float> d_;
 };
 
 }} // ns
diff --git a/modules/gfx/tests/CMakeLists.txt b/modules/gfx/tests/CMakeLists.txt
index b5bb03b71e8eb2dbf90fc8886f1cf34157e52670..273ab6809780c1c9cc2a7083b0c4beaac9ec7a29 100644
--- a/modules/gfx/tests/CMakeLists.txt
+++ b/modules/gfx/tests/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(OST_GFX_UNIT_TESTS
   tests.cc
   test_gfx_node.cc
+  test_color.cc
   test_gfx.py
 )
 if (ENABLE_IMG)
diff --git a/modules/gfx/tests/test_color.cc b/modules/gfx/tests/test_color.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0f827802318f2779cb6c5da4afb95d132b79787e
--- /dev/null
+++ b/modules/gfx/tests/test_color.cc
@@ -0,0 +1,184 @@
+//------------------------------------------------------------------------------
+// This file is part of the OpenStructure project <www.openstructure.org>
+//
+// Copyright (C) 2008-2011 by the OpenStructure authors
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation; either version 3.0 of the License, or (at your option)
+// any later version.
+// This library is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+//------------------------------------------------------------------------------
+
+/*
+  Author: Ansgar Philippsen
+*/
+
+#include <ost/gfx/color.hh>
+
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+#include <boost/test/floating_point_comparison.hpp>
+
+using boost::unit_test_framework::test_suite;
+
+using namespace ost;
+using namespace ost::gfx;
+
+namespace {
+  bool compare_colors(const Color& c1, const Color& c2, float tol=1e-6) {
+    return std::fabs(c1.GetRed()-c2.GetRed()<tol) &&
+      std::fabs(c1.GetGreen()-c2.GetGreen()<tol) &&
+      std::fabs(c1.GetBlue()-c2.GetBlue()<tol) &&
+      std::fabs(c1.GetAlpha()-c2.GetAlpha()<tol) &&
+      std::fabs(c1.GetHue()-c2.GetHue()<tol) &&
+      std::fabs(c1.GetSat()-c2.GetSat()<tol) &&
+      std::fabs(c1.GetVal()-c2.GetVal()<tol);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE(gfx)
+
+BOOST_AUTO_TEST_CASE(default_color)
+{
+  Color c;
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetAlpha(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+}
+
+BOOST_AUTO_TEST_CASE(set_rgb)
+{
+  Color c;
+  c = RGB(1.0,0.0,0.0); // red
+  BOOST_CHECK_CLOSE(c.GetAlpha(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),0.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = RGB(1.0,1.0,0.0); // yellow
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),1.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = RGB(0.0,1.0,0.0); // green
+  BOOST_CHECK_CLOSE(c.GetRed(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),2.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = RGB(0.0,1.0,1.0); // cyan
+  BOOST_CHECK_CLOSE(c.GetRed(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),3.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = RGB(0.0,0.0,1.0); // blue
+  BOOST_CHECK_CLOSE(c.GetRed(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),4.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = RGB(1.0,0.0,1.0); // purple
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),5.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+}
+
+BOOST_AUTO_TEST_CASE(set_hsv)
+{
+  Color c;
+  c = HSV(0.0/6.0,1.0,1.0); // red
+  BOOST_CHECK_CLOSE(c.GetAlpha(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),0.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(0.0/6.0,0.5,1.0); // light red
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),0.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(1.0/6.0,1.0,1.0); // yellow
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),1.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(2.0/6.0,1.0,1.0); // green
+  BOOST_CHECK_CLOSE(c.GetRed(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),2.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(2.0/6.0,0.5,1.0); // light green
+  BOOST_CHECK_CLOSE(c.GetRed(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),2.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(3.0/6.0,1.0,1.0); // cyan
+  BOOST_CHECK_CLOSE(c.GetRed(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),3.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(4.0/6.0,1.0,1.0); // blue
+  BOOST_CHECK_CLOSE(c.GetRed(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),4.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(4.0/6.0,0.5,1.0); // light blue
+  BOOST_CHECK_CLOSE(c.GetRed(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),4.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),0.5,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+  c = HSV(5.0/6.0,1.0,1.0); // purple
+  BOOST_CHECK_CLOSE(c.GetRed(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetGreen(),0.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetBlue(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetHue(),5.0/6.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetSat(),1.0,1e-6);
+  BOOST_CHECK_CLOSE(c.GetVal(),1.0,1e-6);
+}
+
+BOOST_AUTO_TEST_CASE(set_char)
+{
+  BOOST_CHECK(compare_colors(RGB(255,0,0),RGB(1.0,0.0,0.0)));
+  BOOST_CHECK(compare_colors(RGBA(0,127,0,127),RGBA(0.0,0.5,0.0,0.5)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()