/* * colorsel.c - concept of a color-selector. * * (C) 1998, Simon Budig * * 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. */ #include #include #include #include #include #define COLORWHEELRADIUS 100 #define COLORTRIANGLERADIUS 80 #define PREVIEWSIZE (2*COLORWHEELRADIUS+1) #define INTENSITY(r,g,b) (r * 0.30 + g * 0.59 + b * 0.11 + 0.001) #define BGCOLOR 180 #define PREVIEW_MASK GDK_EXPOSURE_MASK | \ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | \ GDK_BUTTON_MOTION_MASK typedef enum { HUE = 0, SATURATION, VALUE, RED, GREEN, BLUE, HUE_SATURATION, HUE_VALUE, SATURATION_VALUE, RED_GREEN, RED_BLUE, GREEN_BLUE } ColorSelectFillType; typedef struct _ColorSelect _ColorSelect, *ColorSelectP; struct _ColorSelect { gint values[6]; }; static ColorSelectP coldata; /* Conversion hsv->rgb */ static void color_hsv_to_rgb (gfloat hue, gfloat sat, gfloat val, guchar* red, guchar *green, guchar *blue) { gfloat f, p, q, t; if (sat == 0) { *red = val * 255; *green = val * 255; *blue = val * 255; } else { while (hue < 0) hue += 360; while (hue >= 360) hue -= 360; hue /= 60; f = hue - (int) hue; p = val * (1 - sat); q = val * (1 - (sat * f)); t = val * (1 - (sat * (1 - f))); switch ((int) hue) { case 0: *red = val * 255; *green = t * 255; *blue = p * 255; break; case 1: *red = q * 255; *green = val * 255; *blue = p * 255; break; case 2: *red = p * 255; *green = val * 255; *blue = t * 255; break; case 3: *red = p * 255; *green = q * 255; *blue = val * 255; break; case 4: *red = t * 255; *green = p * 255; *blue = val * 255; break; case 5: *red = val * 255; *green = p * 255; *blue = q * 255; break; default: break; } } } static void color_select_update_rgb_values (ColorSelectP csp) { gfloat h, s, v; gfloat f, p, q, t; if (csp) { h = csp->values[HUE]; s = csp->values[SATURATION] / 100.0; v = csp->values[VALUE] / 100.0; if (s == 0) { csp->values[RED] = v * 255; csp->values[GREEN] = v * 255; csp->values[BLUE] = v * 255; } else { if (h == 360) h = 0; h /= 60; f = h - (int) h; p = v * (1 - s); q = v * (1 - (s * f)); t = v * (1 - (s * (1 - f))); switch ((int) h) { case 0: csp->values[RED] = v * 255; csp->values[GREEN] = t * 255; csp->values[BLUE] = p * 255; break; case 1: csp->values[RED] = q * 255; csp->values[GREEN] = v * 255; csp->values[BLUE] = p * 255; break; case 2: csp->values[RED] = p * 255; csp->values[GREEN] = v * 255; csp->values[BLUE] = t * 255; break; case 3: csp->values[RED] = p * 255; csp->values[GREEN] = q * 255; csp->values[BLUE] = v * 255; break; case 4: csp->values[RED] = t * 255; csp->values[GREEN] = p * 255; csp->values[BLUE] = v * 255; break; case 5: csp->values[RED] = v * 255; csp->values[GREEN] = p * 255; csp->values[BLUE] = q * 255; break; } } } } void update_color_preview (GtkWidget *preview, gint hue_changed) { guchar buf[3*PREVIEWSIZE]; gint x, y, k, r2, dx, col; gint x0, y0; gfloat hue, sat, val, s, v; static gfloat oldsat=0, oldval=0; gint hx,hy, sx,sy, vx,vy; hue = (float) coldata->values[HUE] * M_PI / 180; hx = sin(hue) * COLORTRIANGLERADIUS; hy = cos(hue) * COLORTRIANGLERADIUS; sx = sin(hue - 2*M_PI/3) * COLORTRIANGLERADIUS; sy = cos(hue - 2*M_PI/3) * COLORTRIANGLERADIUS; vx = sin(hue + 2*M_PI/3) * COLORTRIANGLERADIUS; vy = cos(hue + 2*M_PI/3) * COLORTRIANGLERADIUS; hue = (float) coldata->values[HUE]; if (hue_changed) { for (y = COLORWHEELRADIUS; y > -COLORWHEELRADIUS; y--) { dx = (int) sqrt((float) abs((COLORWHEELRADIUS)*(COLORWHEELRADIUS)-y*y)); for (x = -dx, k=0; x <= dx; x++) { buf[k]=buf[k+1]=buf[k+2]=BGCOLOR; r2 = (x*x)+(y*y); if ( r2 <= COLORWHEELRADIUS * COLORWHEELRADIUS) { if (r2 > COLORTRIANGLERADIUS * COLORTRIANGLERADIUS) { color_hsv_to_rgb (atan2 (x,y) / M_PI * 180, 1, 1, &buf[k], &buf[k+1], &buf[k+2]); } else { val = (float) ((x-sx)*(hy-vy)-(y-sy)*(hx-vx)) / (float) ((vx-sx)*(hy-vy)-(vy-sy)*(hx-vx)); if (val>0 && val<=1) { // eigentlich val>=0, aber dann Grafikfehler... sat = (val==0?0: ((float) (y-sy-val*(vy-sy)) / (val * (float) (hy-vy)))); if (sat >= 0 && sat <= 1) color_hsv_to_rgb (hue, sat, val, &buf[k], &buf[k+1], &buf[k+2]); } } } k += 3; } /* gtk_preview_draw_row (GTK_PREVIEW (preview), buf, 0, COLORWHEELRADIUS - y - 1, PREVIEWSIZE); */ gtk_preview_draw_row (GTK_PREVIEW (preview), buf, COLORWHEELRADIUS - dx, COLORWHEELRADIUS - y - 1, 2*dx+1); } // Marker im aeusseren Ring x0 = (gint) (sin(hue*M_PI/180) * ((float) (COLORWHEELRADIUS - COLORTRIANGLERADIUS + 1)/2 + COLORTRIANGLERADIUS) + 0.5); y0 = (gint) (cos(hue*M_PI/180) * ((float) (COLORWHEELRADIUS - COLORTRIANGLERADIUS + 1)/2 + COLORTRIANGLERADIUS) + 0.5); color_hsv_to_rgb (atan2 (x0,y0) / M_PI * 180, 1, 1, &buf[0], &buf[1], &buf[2]); col = INTENSITY(buf[0], buf[1], buf[2]) > 127 ? 0 : 255 ; for (y = y0 - 4 ; y <= y0 + 4 ; y++) { for (x = x0 - 4, k=0 ; x <= x0 + 4 ; x++) { r2 = (x-x0)*(x-x0)+(y-y0)*(y-y0); if (r2 <= 20 && r2 >= 6) buf[k]=buf[k+1]=buf[k+2]=col; else color_hsv_to_rgb (atan2 (x,y) / M_PI * 180, 1, 1, &buf[k], &buf[k+1], &buf[k+2]); k += 3; } gtk_preview_draw_row (GTK_PREVIEW (preview), buf, COLORWHEELRADIUS + x0-4, COLORWHEELRADIUS - 1 - y, 9); } } else { // Marker im Dreieck loeschen s = oldsat; v = oldval; x0 = (gint) (sx + (vx - sx)*v + (hx - vx) * s * v); y0 = (gint) (sy + (vy - sy)*v + (hy - vy) * s * v); for (y = y0 - 4 ; y <= y0 + 4 ; y++) { for (x = x0 - 4, k=0 ; x <= x0 + 4 ; x++) { buf[k]=buf[k+1]=buf[k+2]=BGCOLOR; r2 = (x-x0)*(x-x0)+(y-y0)*(y-y0); if (x*x+y*y > COLORTRIANGLERADIUS * COLORTRIANGLERADIUS) { color_hsv_to_rgb (atan2 (x,y) / M_PI * 180, 1, 1, &buf[k], &buf[k+1], &buf[k+2]); } else { val = (float) ((x-sx)*(hy-vy)-(y-sy)*(hx-vx)) / (float) ((vx-sx)*(hy-vy)-(vy-sy)*(hx-vx)); if (val>0 && val<=1) { // eigentlich val>=0, aber dann Grafikfehler... sat = (val==0?0: ((float) (y-sy-val*(vy-sy)) / (val * (float) (hy-vy)))); if (sat >= 0 && sat <= 1) color_hsv_to_rgb (hue, sat, val, &buf[k], &buf[k+1], &buf[k+2]); } } k += 3; } gtk_preview_draw_row (GTK_PREVIEW (preview), buf, COLORWHEELRADIUS + x0-4, COLORWHEELRADIUS - 1 - y, 9); } oldsat = coldata->values[SATURATION] / 100.0; oldval = coldata->values[VALUE] / 100.0; } // Marker im Dreieck col = INTENSITY(coldata->values[RED], coldata->values[GREEN], coldata->values[BLUE]) > 127 ? 0 : 255 ; s = coldata->values[SATURATION] / 100.0; v = coldata->values[VALUE] / 100.0; oldsat=s; oldval=v; x0 = (gint) (sx + (vx - sx)*v + (hx - vx) * s * v); y0 = (gint) (sy + (vy - sy)*v + (hy - vy) * s * v); for (y = y0 - 4 ; y <= y0 + 4 ; y++) { for (x = x0 - 4, k=0 ; x <= x0 + 4 ; x++) { buf[k]=buf[k+1]=buf[k+2]=BGCOLOR; r2 = (x-x0)*(x-x0)+(y-y0)*(y-y0); if (r2 <= 20 && r2 >= 6) buf[k]=buf[k+1]=buf[k+2]=col; else { if (x*x+y*y > COLORTRIANGLERADIUS * COLORTRIANGLERADIUS) { color_hsv_to_rgb (atan2 (x,y) / M_PI * 180, 1, 1, &buf[k], &buf[k+1], &buf[k+2]); } else { val = (float) ((x-sx)*(hy-vy)-(y-sy)*(hx-vx)) / (float) ((vx-sx)*(hy-vy)-(vy-sy)*(hx-vx)); if (val>0 && val<=1) { // eigentlich val>=0, aber dann Grafikfehler... sat = (val==0?0: ((float) (y-sy-val*(vy-sy)) / (val * (float) (hy-vy)))); if (sat >= 0 && sat <= 1) color_hsv_to_rgb (hue, sat, val, &buf[k], &buf[k+1], &buf[k+2]); } } } k += 3; } gtk_preview_draw_row (GTK_PREVIEW (preview), buf, COLORWHEELRADIUS + x0-4, COLORWHEELRADIUS - 1 - y, 9); } for (k=0; k < (PREVIEWSIZE * 3); k+=3) { buf[k]=coldata->values[RED]; buf[k+1]=coldata->values[GREEN]; buf[k+2]=coldata->values[BLUE]; } for (y=0; y < 30; y++) { gtk_preview_draw_row (GTK_PREVIEW (preview), buf, 0, y+PREVIEWSIZE, PREVIEWSIZE); } gtk_widget_draw (preview, NULL); } void init_color_preview (GtkWidget *preview) { guchar buf[3*PREVIEWSIZE]; gint i; for (i=0; i < 3*PREVIEWSIZE; i+=3) buf[i]=buf[i+1]=buf[i+2]=BGCOLOR; for (i=0; i < PREVIEWSIZE; i++) gtk_preview_draw_row (GTK_PREVIEW (preview), buf, 0, i, PREVIEWSIZE); gtk_widget_draw (preview, NULL); } /* * Color Preview */ gint color_selection_callback(GtkWidget *widget, GdkEvent *event) { static gint mode=0; gint x,y, mousex, mousey; gfloat r; gfloat hue, sat, val; gint hx,hy, sx,sy, vx,vy; switch (event->type) { case GDK_BUTTON_PRESS: x = event->button.x - COLORWHEELRADIUS - 1; y = - event->button.y + COLORWHEELRADIUS + 1; r = sqrt((float) (x*x+y*y)); if ( /* r <= COLORWHEELRADIUS && */ r > COLORTRIANGLERADIUS) mode = 1; // Dragging in the Ring else mode = 2; // Dragging in the Triangle break; case GDK_MOTION_NOTIFY: x = event->motion.x - COLORWHEELRADIUS - 1; y = - event->motion.y + COLORWHEELRADIUS + 1; break; case GDK_BUTTON_RELEASE: mode = 0; break; default: gtk_widget_get_pointer(widget, &x, &y); x = x - COLORWHEELRADIUS - 1; y = - y + COLORWHEELRADIUS + 1; break; } gtk_widget_get_pointer(widget, &mousex, &mousey); if ((event->type == GDK_MOTION_NOTIFY && (mousex != event->motion.x || mousey != event->motion.y))) return FALSE; if (mode == 1) { coldata->values[HUE] = ( (int) (atan2 (x, y) / M_PI * 180) + 360 ) %360; color_select_update_rgb_values(coldata); update_color_preview(widget, 1); } if (mode == 2) { hue = (float) coldata->values[HUE] * M_PI / 180; hx = sin(hue) * COLORTRIANGLERADIUS; hy = cos(hue) * COLORTRIANGLERADIUS; sx = sin(hue - 2*M_PI/3) * COLORTRIANGLERADIUS; sy = cos(hue - 2*M_PI/3) * COLORTRIANGLERADIUS; vx = sin(hue + 2*M_PI/3) * COLORTRIANGLERADIUS; vy = cos(hue + 2*M_PI/3) * COLORTRIANGLERADIUS; hue = (float) coldata->values[HUE]; if ((x-sx)*vx+(y-sy)*vy < 0) { sat = 1; val = ((float) ((x-sx)*(hx-sx)+(y-sy)*(hy-sy)))/((hx-sx)*(hx-sx)+(hy-sy)*(hy-sy)); if (val<0) val=0; else if (val>1) val=1; } else if ((x-sx)*hx+(y-sy)*hy < 0) { sat = 0; val = ((float) ((x-sx)*(vx-sx)+(y-sy)*(vy-sy)))/((vx-sx)*(vx-sx)+(vy-sy)*(vy-sy)); if (val<0) val=0; else if (val>1) val=1; } else if ((x-hx)*sx+(y-hy)*sy < 0) { val = 1; sat = ((float) ((x-vx)*(hx-vx)+(y-vy)*(hy-vy)))/((hx-vx)*(hx-vx)+(hy-vy)*(hy-vy)); if (sat<0) sat=0; else if (sat>1) sat=1; } else { val = (float) ((x-sx)*(hy-vy)-(y-sy)*(hx-vx)) / (float) ((vx-sx)*(hy-vy)-(vy-sy)*(hx-vx)); if (val<=0) { val=0; sat=0; } else { if (val>1) val=1; sat = (float) (y-sy-val*(vy-sy)) / (val * (float) (hy-vy)); if (sat<0) sat=0; else if (sat>1) sat=1; } } coldata->values[SATURATION]=100*sat+0.5; coldata->values[VALUE]=100*val+0.5; color_select_update_rgb_values(coldata); update_color_preview(widget, 0); } return FALSE; } static void color_preview_destroy (GtkWidget *widget, GtkWidget **window) { *window = NULL; exit (0); } void create_color_preview () { static GtkWidget *window = NULL; GtkWidget *preview; if (!window) { window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(color_preview_destroy), &window); gtk_window_set_title (GTK_WINDOW (window), "Select Color"); gtk_container_border_width (GTK_CONTAINER (window), 10); preview = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_widget_set_events( GTK_WIDGET(preview), PREVIEW_MASK ); gtk_preview_size (GTK_PREVIEW (preview), PREVIEWSIZE, PREVIEWSIZE + 30 /* BAD! */); gtk_container_add (GTK_CONTAINER (window), preview); gtk_signal_connect (GTK_OBJECT(preview), "motion_notify_event", GTK_SIGNAL_FUNC(color_selection_callback), NULL); gtk_signal_connect (GTK_OBJECT(preview), "button_press_event", GTK_SIGNAL_FUNC(color_selection_callback), NULL); gtk_signal_connect (GTK_OBJECT(preview), "button_release_event", GTK_SIGNAL_FUNC(color_selection_callback), NULL); gtk_widget_show (preview); init_color_preview (preview); update_color_preview (preview, 1); } if (!GTK_WIDGET_VISIBLE (window)) gtk_widget_show (window); else gtk_widget_destroy (window); } int main (int argc, char *argv[]) { coldata = g_malloc (sizeof (_ColorSelect)); coldata->values[HUE]=0; coldata->values[SATURATION]=100; coldata->values[VALUE]=100; color_select_update_rgb_values(coldata); gtk_set_locale (); gtk_init (&argc, &argv); gtk_rc_parse ("colorselrc"); create_color_preview (); gtk_main (); return 0; }