summaryrefslogtreecommitdiff
path: root/src/Fl_Color_Chooser.cxx
diff options
context:
space:
mode:
authorMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
committerMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
commitf9039b2ae21988783feae9b362818e7923e82d14 (patch)
tree6d6fe3679d73448758f9794e7d4d4f6b22a4adad /src/Fl_Color_Chooser.cxx
parent67e89232f9ba067825a158734a09e0fa21aacbe3 (diff)
Initial revision
git-svn-id: file:///fltk/svn/fltk/trunk@2 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/Fl_Color_Chooser.cxx')
-rw-r--r--src/Fl_Color_Chooser.cxx397
1 files changed, 397 insertions, 0 deletions
diff --git a/src/Fl_Color_Chooser.cxx b/src/Fl_Color_Chooser.cxx
new file mode 100644
index 000000000..e0e2d926a
--- /dev/null
+++ b/src/Fl_Color_Chooser.cxx
@@ -0,0 +1,397 @@
+// Fl_Color_Chooser.C
+
+// Besides being a useful object on it's own, the Fl_Color_Chooser 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() 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.
+
+#include <FL/Fl.H>
+#include <FL/Fl_Color_Chooser.H>
+#include <FL/fl_draw.H>
+#include <FL/math.h>
+#include <stdio.h>
+
+// 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::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::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}
+};
+
+int Flcc_Value_Input::format(char* buf) {
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
+ if (c->mode() == M_HEX) return sprintf(buf,"0x%02X", int(value()));
+ else return Fl_Valuator::format(buf);
+}
+
+void Fl_Color_Chooser::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_);
+ 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));
+ 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_);
+ break;
+ }
+}
+
+int Fl_Color_Chooser::rgb(double r, double g, double b) {
+ if (r == r_ && g == g_ && b == b_) return 0;
+ r_ = r; g_ = g; b_ = b;
+ 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(6);
+#endif
+ valuebox.damage(2);}
+ if (hue_ != ph || saturation_ != ps) {huebox.damage(2); valuebox.damage(6);}
+ return 1;
+}
+
+int Fl_Color_Chooser::hsv(double h, double s, double v) {
+ 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_) return 0;
+ double ph = hue_;
+ double ps = saturation_;
+ double pv = value_;
+ hue_ = h; saturation_ = s; value_ = v;
+ if (value_ != pv) {
+#ifdef UPDATE_HUE_BOX
+ huebox.damage(6);
+#endif
+ valuebox.damage(2);}
+ if (hue_ != ph || saturation_ != ps) {huebox.damage(2); valuebox.damage(6);}
+ 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::handle(int e) {
+ static double ih, is;
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)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* v = (Flcc_HueBox*)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*)(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::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::draw() {
+ if (damage()&128) 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() == 2) fl_clip(x1+px,y1+py,6,6);
+ fl_draw_image(generate_image, this, x1, y1, w1, h1);
+ if (damage() == 2) fl_pop_clip();
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)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::handle(int e) {
+ static double iv;
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)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;
+static void generate_vimage(void* vv, int X, int Y, int W, uchar* buf) {
+ Flcc_ValueBox* v = (Flcc_ValueBox*)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::draw() {
+ if (damage()&128) draw_box();
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)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() == 2) fl_clip(x1,y1+py,w1,6);
+ fl_draw_image(generate_vimage, this, x1, y1, w1, h1);
+ if (damage() == 2) 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;
+}
+
+////////////////////////////////////////////////////////////////
+
+void Fl_Color_Chooser::rgb_cb(Fl_Widget* o, void*) {
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)(o->parent());
+ double r = c->rvalue.value();
+ double g = c->gvalue.value();
+ double b = c->bvalue.value();
+ if (c->mode() == M_HSV) {
+ if (c->hsv(r,g,b)) c->do_callback();
+ return;
+ }
+ if (c->mode() != M_RGB) {
+ r = r/255;
+ g = g/255;
+ b = b/255;
+ }
+ if (c->rgb(r,g,b)) c->do_callback();
+}
+
+void Fl_Color_Chooser::mode_cb(Fl_Widget* o, void*) {
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)(o->parent());
+ c->set_valuators();
+}
+
+////////////////////////////////////////////////////////////////
+
+Fl_Color_Chooser::Fl_Color_Chooser(int X, int Y, int W, int H, const char* L)
+ : Fl_Group(0,0,180,100,L),
+ huebox(0,0,100,100),
+ valuebox(100,0,20,100),
+ choice(120,0,60,20),
+ rvalue(120,20,60,25),
+ gvalue(120,45,60,25),
+ bvalue(120,70,60,25),
+ resize_box(0,95,100,5)
+{
+ end();
+ resizable(resize_box);
+ resize(X,Y,W,H);
+ r_ = g_ = b_ = 0;
+ hue_ = 0.0;
+ saturation_ = 0.0;
+ value_ = 0.0;
+ huebox.box(FL_DOWN_FRAME);
+ valuebox.box(FL_DOWN_FRAME);
+ choice.menu(mode_menu);
+ set_valuators();
+ rvalue.callback(rgb_cb);
+ gvalue.callback(rgb_cb);
+ bvalue.callback(rgb_cb);
+ choice.callback(mode_cb);
+ choice.box(FL_THIN_UP_BOX);
+ choice.textfont(FL_HELVETICA_BOLD_ITALIC);
+}
+
+////////////////////////////////////////////////////////////////
+// fl_color_chooser():
+
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Return_Button.H>
+
+class ColorChip : public Fl_Widget {
+ void draw();
+public:
+ uchar r,g,b;
+ ColorChip(int X, int Y, int W, int H) : Fl_Widget(X,Y,W,H) {
+ box(FL_ENGRAVED_FRAME);}
+};
+
+void ColorChip::draw() {
+ if (damage()&128) draw_box();
+ fl_rectf(x()+Fl::box_dx(box()),
+ y()+Fl::box_dy(box()),
+ w()-Fl::box_dw(box()),
+ h()-Fl::box_dh(box()),r,g,b);
+}
+
+static void chooser_cb(Fl_Object* o, void* vv) {
+ Fl_Color_Chooser* c = (Fl_Color_Chooser*)o;
+ ColorChip* v = (ColorChip*)vv;
+ v->r = uchar(255*c->r()+.5);
+ v->g = uchar(255*c->g()+.5);
+ v->b = uchar(255*c->b()+.5);
+ v->damage(2);
+}
+
+extern const char* fl_ok;
+extern const char* fl_cancel;
+
+int fl_color_chooser(const char* name, double& r, double& g, double& b) {
+ Fl_Window window(210,165,name);
+ Fl_Color_Chooser chooser(5, 5, 200, 95);
+ ColorChip ok_color(5, 105, 95, 30);
+ Fl_Return_Button ok_button(5, 135, 95, 25, fl_ok);
+ ColorChip cancel_color(110, 105, 95, 30);
+ cancel_color.r = uchar(255*r+.5); ok_color.r = cancel_color.r;
+ ok_color.g = cancel_color.g = uchar(255*g+.5);
+ ok_color.b = cancel_color.b = uchar(255*b+.5);
+ Fl_Button cancel_button(110, 135, 95, 25, fl_cancel);
+ window.resizable(chooser);
+ chooser.rgb(r,g,b);
+ chooser.callback(chooser_cb, &ok_color);
+ window.end();
+ window.set_modal();
+ window.hotspot(window);
+ window.show();
+ while (window.shown()) {
+ Fl::wait();
+ Fl_Widget* o;
+ while ((o = Fl::readqueue())) {
+ if (o == &ok_button) {
+ r = chooser.r();
+ g = chooser.g();
+ b = chooser.b();
+ return 1;
+ }
+ if (o == &window || o == &cancel_button) return 0;
+ }
+ }
+ return 0;
+}
+
+int fl_color_chooser(const char* name, uchar& r, uchar& g, uchar& b) {
+ double dr = r/255.0;
+ double dg = g/255.0;
+ double db = b/255.0;
+ if (fl_color_chooser(name,dr,dg,db)) {
+ r = uchar(255*dr+.5);
+ g = uchar(255*dg+.5);
+ b = uchar(255*db+.5);
+ return 1;
+ }
+ return 0;
+}