//| //| Color chooser for the Fast Light Tool Kit (FLTK). //| //| Copyright 1998-1999 by Bill Spitzak, Vivality llc., Venice, //| CA, U.S.A., Matthias Melcher and others. //| //| This library is free software; you can redistribute it and/or //| modify it under the terms of the GNU Library General Public //| License as published by the Free Software Foundation; either //| version 2 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 //| Library General Public License for more details. //| //| You should have received a copy of the GNU Library General Public //| License along with this library; if not, write to the Free Software //| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 //| USA. //| //| Please report all bugs and problems to "matthias@mediaone.net". //| #include #include "Fl_Color_Chooser_A.H" #include "Fl_Color_Button.H" #include #include #include // Besides being a useful object on it's own, the Fl_Color_Chooser_A was // an attempt to make a complex composite object that could be easily // imbedded into a user interface. If you wish to make complex objects // of your own, be sure to read this code. // The function Fl_Color_Chooser_A() creates a window containing a color // chooser and a few buttons and current-color indicators. It is an // easier interface for simple programs that just need a color. // The "hue box" can be a circle or rectilinear. // You get a circle by defining this: //#define CIRCLE 1 // And the "hue box" can auto-update when the value changes // you get this by defining this: #define UPDATE_HUE_BOX 1 void Fl_Color_Chooser_A::hsv2rgb( double H, double S, double V, double& r, double& g, double& b) { if (S < 5.0e-6) { r = g = b = V; } else { int i = (int)H; double f = H - (float)i; double p1 = V*(1.0-S); double p2 = V*(1.0-S*f); double p3 = V*(1.0-S*(1.0-f)); switch (i) { case 0: r = V; g = p3; b = p1; break; case 1: r = p2; g = V; b = p1; break; case 2: r = p1; g = V; b = p3; break; case 3: r = p1; g = p2; b = V; break; case 4: r = p3; g = p1; b = V; break; case 5: r = V; g = p1; b = p2; break; } } } void Fl_Color_Chooser_A::rgb2hsv( double r, double g, double b, double& H, double& S, double& V) { double maxv = r > g ? r : g; if (b > maxv) maxv = b; V = maxv; if (maxv>0) { double minv = r < g ? r : g; if (b < minv) minv = b; S = 1.0 - double(minv)/maxv; if (maxv > minv) { if (maxv == r) {H = (g-b)/double(maxv-minv); if (H<0) H += 6.0;} else if (maxv == g) H = 2.0+(b-r)/double(maxv-minv); else H = 4.0+(r-g)/double(maxv-minv); } } } enum {M_RGB, M_BYTE, M_HEX, M_HSV}; // modes static Fl_Menu_Item mode_menu[] = { {"rgb"}, {"byte"}, {"hex"}, {"hsv"}, {0}, {"rgba"}, {"byte"}, {"hex"}, {"hsva"}, {0} }; int Flcc_Value_Input_A::format(char* buf) { Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)parent(); if (c->mode() == M_HEX) return sprintf(buf,"0x%02X", int(value())); else return Fl_Valuator::format(buf); } void Fl_Color_Chooser_A::set_valuators() { switch (mode()) { case M_RGB: rvalue.range(0,1); rvalue.step(1,1000); rvalue.value(r_); gvalue.range(0,1); gvalue.step(1,1000); gvalue.value(g_); bvalue.range(0,1); bvalue.step(1,1000); bvalue.value(b_); avalue.range(0,1); avalue.step(1,1000); avalue.value(a_); break; case M_BYTE: case M_HEX: rvalue.range(0,255); rvalue.step(1); rvalue.value(int(255*r_+.5)); gvalue.range(0,255); gvalue.step(1); gvalue.value(int(255*g_+.5)); bvalue.range(0,255); bvalue.step(1); bvalue.value(int(255*b_+.5)); avalue.range(0,255); avalue.step(1); avalue.value(int(255*a_+.5)); break; case M_HSV: rvalue.range(0,6); rvalue.step(1,1000); rvalue.value(hue_); gvalue.range(0,1); gvalue.step(1,1000); gvalue.value(saturation_); bvalue.range(0,1); bvalue.step(1,1000); bvalue.value(value_); avalue.range(0,1); avalue.step(1,1000); avalue.value(a_); break; } } int Fl_Color_Chooser_A::rgb(double r, double g, double b, double a) { if (a == -1) a = a_; if (r == r_ && g == g_ && b == b_ && a == a_) return 0; double pa = a_; r_ = r; g_ = g; b_ = b; a_ = a; double ph = hue_; double ps = saturation_; double pv = value_; rgb2hsv(r,g,b,hue_,saturation_,value_); set_valuators(); if (value_ != pv) { #ifdef UPDATE_HUE_BOX huebox.damage(FL_DAMAGE_SCROLL); #endif valuebox.damage(FL_DAMAGE_EXPOSE);} if (hue_ != ph || saturation_ != ps) { huebox.damage(FL_DAMAGE_EXPOSE); valuebox.damage(FL_DAMAGE_SCROLL); } if (a_ != pa) alphabox.damage(FL_DAMAGE_EXPOSE); if (hue_ != ph || saturation_ != ps || value_ != pv) alphabox.damage(FL_DAMAGE_SCROLL); return 1; } int Fl_Color_Chooser_A::hsv(double h, double s, double v, double a) { if (a == -1) a = a_; double pa = a_; h = fmod(h,6.0); if (h < 0.0) h += 6.0; if (s < 0.0) s = 0.0; else if (s > 1.0) s = 1.0; if (v < 0.0) v = 0.0; else if (v > 1.0) v = 1.0; if (h == hue_ && s == saturation_ && v == value_ && a == a_) return 0; double ph = hue_; double ps = saturation_; double pv = value_; hue_ = h; saturation_ = s; value_ = v; a_ = a; if (value_ != pv) { #ifdef UPDATE_HUE_BOX huebox.damage(FL_DAMAGE_SCROLL); #endif valuebox.damage(FL_DAMAGE_EXPOSE);} if (hue_ != ph || saturation_ != ps) { huebox.damage(FL_DAMAGE_EXPOSE); valuebox.damage(FL_DAMAGE_SCROLL); } if (a_ != pa) alphabox.damage(FL_DAMAGE_EXPOSE); if (hue_ != ph || saturation_ != ps || value_ != pv) alphabox.damage(FL_DAMAGE_SCROLL); hsv2rgb(h,s,v,r_,g_,b_); set_valuators(); return 1; } //////////////////////////////////////////////////////////////// static void tohs(double x, double y, double& h, double& s) { #ifdef CIRCLE x = 2*x-1; y = 1-2*y; s = sqrt(x*x+y*y); if (s > 1.0) s = 1.0; h = (3.0/M_PI)*atan2(y,x); if (h<0) h += 6.0; #else h = fmod(6.0*x,6.0); if (h < 0.0) h += 6.0; s = 1.0-y; if (s < 0.0) s = 0.0; else if (s > 1.0) s = 1.0; #endif } int Flcc_HueBox_A::handle(int e) { static double ih, is; Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)parent(); switch (e) { case FL_PUSH: ih = c->hue(); is = c->saturation(); case FL_DRAG: { double Xf, Yf, H, S; Xf = (Fl::event_x()-x()-Fl::box_dx(box()))/double(w()-Fl::box_dw(box())); Yf = (Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box())); tohs(Xf, Yf, H, S); if (fabs(H-ih) < 3*6.0/w()) H = ih; if (fabs(S-is) < 3*1.0/h()) S = is; if (Fl::event_state(FL_CTRL)) H = ih; if (c->hsv(H, S, c->value())) c->do_callback(); } return 1; default: return 0; } } static void generate_image(void* vv, int X, int Y, int W, uchar* buf) { Flcc_HueBox_A* v = (Flcc_HueBox_A*)vv; int iw = v->w()-Fl::box_dw(v->box()); double Yf = double(Y)/(v->h()-Fl::box_dh(v->box())); #ifdef UPDATE_HUE_BOX const double V = ((Fl_Color_Chooser_A*)(v->parent()))->value(); #else const double V = 1.0; #endif for (int x = X; x < X+W; x++) { double Xf = double(x)/iw; double H,S; tohs(Xf,Yf,H,S); double r,g,b; Fl_Color_Chooser_A::hsv2rgb(H,S,V,r,g,b); *buf++ = uchar(255*r+.5); *buf++ = uchar(255*g+.5); *buf++ = uchar(255*b+.5); } } void Flcc_HueBox_A::draw() { if (damage()&FL_DAMAGE_ALL) draw_box(); int x1 = x()+Fl::box_dx(box()); int y1 = y()+Fl::box_dy(box()); int w1 = w()-Fl::box_dw(box()); int h1 = h()-Fl::box_dh(box()); if (damage() == FL_DAMAGE_EXPOSE) fl_clip(x1+px,y1+py,6,6); fl_draw_image(generate_image, this, x1, y1, w1, h1); if (damage() == FL_DAMAGE_EXPOSE) fl_pop_clip(); Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)parent(); #ifdef CIRCLE int X = int(.5*(cos(c->hue()*(M_PI/3.0))*c->saturation()+1) * (w1-6)); int Y = int(.5*(1-sin(c->hue()*(M_PI/3.0))*c->saturation()) * (h1-6)); #else int X = int(c->hue()/6.0*(w1-6)); int Y = int((1-c->saturation())*(h1-6)); #endif if (X < 0) X = 0; else if (X > w1-6) X = w1-6; if (Y < 0) Y = 0; else if (Y > h1-6) Y = h1-6; // fl_color(c->value()>.75 ? FL_BLACK : FL_WHITE); draw_box(FL_UP_BOX,x1+X,y1+Y,6,6,FL_GRAY); px = X; py = Y; } //////////////////////////////////////////////////////////////// int Flcc_ValueBox_A::handle(int e) { static double iv; Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)parent(); switch (e) { case FL_PUSH: iv = c->value(); case FL_DRAG: { double Yf; Yf = 1-(Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box())); if (fabs(Yf-iv)<(3*1.0/h())) Yf = iv; if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback(); } return 1; default: return 0; } } static double tr, tg, tb, ta; static void generate_vimage(void* vv, int X, int Y, int W, uchar* buf) { Flcc_ValueBox_A* v = (Flcc_ValueBox_A*)vv; double Yf = 255*(1.0-double(Y)/(v->h()-Fl::box_dh(v->box()))); uchar r = uchar(tr*Yf+.5); uchar g = uchar(tg*Yf+.5); uchar b = uchar(tb*Yf+.5); for (int x = X; x < X+W; x++) { *buf++ = r; *buf++ = g; *buf++ = b; } } void Flcc_ValueBox_A::draw() { if (damage()&FL_DAMAGE_ALL) draw_box(); Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)parent(); c->hsv2rgb(c->hue(),c->saturation(),1.0,tr,tg,tb); int x1 = x()+Fl::box_dx(box()); int y1 = y()+Fl::box_dy(box()); int w1 = w()-Fl::box_dw(box()); int h1 = h()-Fl::box_dh(box()); if (damage() == FL_DAMAGE_EXPOSE) fl_clip(x1,y1+py,w1,6); fl_draw_image(generate_vimage, this, x1, y1, w1, h1); if (damage() == FL_DAMAGE_EXPOSE) fl_pop_clip(); int Y = int((1-c->value()) * (h1-6)); if (Y < 0) Y = 0; else if (Y > h1-6) Y = h1-6; draw_box(FL_UP_BOX,x1,y1+Y,w1,6,FL_GRAY); py = Y; } //////////////////////////////////////////////////////////////// int Flcc_AlphaBox_A::handle(int e) { static double iv; Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)parent(); switch (e) { case FL_PUSH: iv = c->a(); case FL_DRAG: { double Yf; Yf = 1-(Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box())); if (Yf<0.0) Yf = 0.0; if (Yf>1.0) Yf = 1.0; if (c->rgb(c->r(),c->g(),c->b(),Yf)) c->do_callback(); } return 1; default: return 0; } } static void generate_aimage(void* vv, int X, int Y, int W, uchar* buf) { Flcc_ValueBox_A* v = (Flcc_ValueBox_A*)vv; double Yf = double(Y)/(v->h()-Fl::box_dh(v->box())), iYf = 1.0-Yf; uchar rg = (uchar)(255*(iYf*tr + Yf*0.8)); uchar gg = (uchar)(255*(iYf*tg + Yf*0.4)); uchar bg = (uchar)(255*(iYf*tb + Yf*0.4)); uchar r, g, b; if ((Y&0x07)==4) { r = rg; g = gg; b = bg; } else { r = (uchar)(255*(iYf*tr + Yf*0.8)); g = (uchar)(255*(iYf*tg + Yf*0.8)); b = (uchar)(255*(iYf*tb + Yf*0.8)); } for (int x = X; x < X+W; x++) { if ((x&0x07)==4) { *buf++ = rg; *buf++ = gg; *buf++ = bg; } else { *buf++ = r; *buf++ = g; *buf++ = b; } } } void Flcc_AlphaBox_A::draw() { if (damage()&FL_DAMAGE_ALL) draw_box(); Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)parent(); tr=c->r(); tg=c->g(); tb=c->b(); int x1 = x()+Fl::box_dx(box()); int y1 = y()+Fl::box_dy(box()); int w1 = w()-Fl::box_dw(box()); int h1 = h()-Fl::box_dh(box()); if (damage() == FL_DAMAGE_EXPOSE) fl_clip(x1,y1+py,w1,6); fl_draw_image(generate_aimage, this, x1, y1, w1, h1); if (damage() == FL_DAMAGE_EXPOSE) fl_pop_clip(); int Y = int((1-c->a()) * (h1-6)); if (Y < 0) Y = 0; else if (Y > h1-6) Y = h1-6; draw_box(FL_UP_BOX,x1,y1+Y,w1,6,FL_GRAY); py = Y; } //////////////////////////////////////////////////////////////// void Fl_Color_Chooser_A::rgb_cb(Fl_Widget* o, void*) { Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)(o->parent()); double r = c->rvalue.value(); double g = c->gvalue.value(); double b = c->bvalue.value(); double a = c->avalue.value(); if (c->mode() == M_HSV) { if (c->hsv(r,g,b,a)) c->do_callback(); return; } if (c->mode() != M_RGB) { r = r/255; g = g/255; b = b/255; a = a/255; } if (c->rgb(r,g,b,a)) c->do_callback(); } void Fl_Color_Chooser_A::mode_cb(Fl_Widget* o, void*) { Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)(o->parent()); // force them to redraw even if value is the same: c->rvalue.value(-1); c->gvalue.value(-1); c->bvalue.value(-1); c->avalue.value(-1); c->set_valuators(); } //////////////////////////////////////////////////////////////// Fl_Color_Chooser_A::Fl_Color_Chooser_A(int X, int Y, int W, int H, const char* L) : Fl_Group(0,0,200,125,L), huebox(0,0,100,125), valuebox(100,0,20,125), alphabox(120,0,20,125), choice(140,0,60,20), rvalue(140,20,60,25), gvalue(140,45,60,25), bvalue(140,70,60,25), avalue(140,95,60,25), resize_box(0,95+25,100,5) { end(); resizable(resize_box); resize(X,Y,W,H); r_ = g_ = b_ = 0.0; a_ = 1.0; hue_ = 0.0; saturation_ = 0.0; value_ = 0.0; huebox.box(FL_DOWN_FRAME); valuebox.box(FL_DOWN_FRAME); alphabox.box(FL_DOWN_FRAME); choice.menu(mode_menu); set_valuators(); rvalue.callback(rgb_cb); gvalue.callback(rgb_cb); bvalue.callback(rgb_cb); avalue.callback(rgb_cb); choice.callback(mode_cb); choice.box(FL_THIN_UP_BOX); choice.textfont(FL_HELVETICA_BOLD_ITALIC); } //////////////////////////////////////////////////////////////// // Fl_Color_Chooser_A(): #include #include #include static void chooser_cb(Fl_Object* o, void* vv) { Fl_Color_Chooser_A* c = (Fl_Color_Chooser_A*)o; Fl_Color_Button *v = (Fl_Color_Button*)vv; v->color((float)c->r(), (float)c->g(), (float)c->b(), (float)c->a()); v->damage(FL_DAMAGE_EXPOSE); } extern const char* fl_ok; extern const char* fl_cancel; int fl_color_chooser_a(const char* name, double& r, double& g, double& b, double& a) { Fl_Window window(210+20,167+25,name); Fl_Color_Chooser_A chooser(5, 5, 200+20, 95+25); Fl_Color_Button ok_color(5, 105+25, 95, 30); ok_color.box(FL_EMBOSSED_FRAME); ok_color.color((float)r, (float)g, (float)b, (float)a); Fl_Return_Button ok_button(5, 137+25, 95, 25, fl_ok); Fl_Color_Button cancel_color(110+20, 105+25, 95, 30); cancel_color.box(FL_EMBOSSED_FRAME); cancel_color.color((float)r, (float)g, (float)b, (float)a); Fl_Button cancel_button(110+20, 137+25, 95, 25, fl_cancel); Fl_Button restore_button(105, 110+25, 20, 20, "@<-"); restore_button.labeltype(FL_SYMBOL_LABEL); window.resizable(chooser); chooser.rgb(r,g,b,a); chooser.callback(chooser_cb, &ok_color); window.end(); window.set_modal(); window.hotspot(window); window.show(); while (window.shown()) { Fl::wait(); for (;;) { Fl_Widget* o = Fl::readqueue(); if (!o) break; if (o == &ok_button) { r = chooser.r(); g = chooser.g(); b = chooser.b(); a = chooser.a(); return 1; } if (o == &restore_button) { chooser.rgb(r,g,b,a); chooser.do_callback(); } if (o == &window || o == &cancel_button) return 0; } } return 0; } int fl_color_chooser_a(const char* name, uchar& r, uchar& g, uchar& b, uchar& a) { double dr = r/255.0; double dg = g/255.0; double db = b/255.0; double da = a/255.0; if (fl_color_chooser_a(name,dr,dg,db,da)) { r = uchar(255*dr+.5); g = uchar(255*dg+.5); b = uchar(255*db+.5); a = uchar(255*da+.5); return 1; } return 0; }