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()