From 4384ba7c55edc820cf4c93529bfa95b0da6c0843 Mon Sep 17 00:00:00 2001 From: Sven Neumann Date: Sat, 8 May 1999 16:34:29 +0000 Subject: [PATCH] Waterselect as a colorselect module. --Sven --- ChangeLog | 8 + modules/Makefile.am | 7 +- modules/colorsel_water.c | 638 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 652 insertions(+), 1 deletion(-) create mode 100644 modules/colorsel_water.c diff --git a/ChangeLog b/ChangeLog index 43bba3536c..a9dd5e51b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Sat May 8 18:31:04 MEST 1999 Sven Neumann + + * modules/Makefile.am + * modules/colorsel_water.c: Implemented the waterselect plug-in + as a colorselect module. There is a strange bug left with the update + of the previews in the color history. Any help is appreciated! + I'll let the plug-in in the distribution until this is fixed. + 1999-05-07 Michael Natterer * app/app_procs.c diff --git a/modules/Makefile.am b/modules/Makefile.am index 1a6f853285..e2ca753e98 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -17,7 +17,8 @@ EXTRA_DIST = \ module_LTLIBRARIES = \ libcolorsel_gtk.la \ - libcolorsel_triangle.la + libcolorsel_triangle.la \ + libcolorsel_water.la libcolorsel_gtk_la_SOURCES = colorsel_gtk.c libcolorsel_gtk_la_LDFLAGS = -avoid-version -module @@ -26,3 +27,7 @@ libcolorsel_gtk_la_LIBADD = @GTK_LIBS@ libcolorsel_triangle_la_SOURCES = colorsel_triangle.c libcolorsel_triangle_la_LDFLAGS = -avoid-version -module libcolorsel_triangle_la_LIBADD = @GTK_LIBS@ + +libcolorsel_water_la_SOURCES = colorsel_water.c +libcolorsel_water_la_LDFLAGS = -avoid-version -module +libcolorsel_water_la_LIBADD = @GTK_LIBS@ diff --git a/modules/colorsel_water.c b/modules/colorsel_water.c new file mode 100644 index 0000000000..f2641af0d9 --- /dev/null +++ b/modules/colorsel_water.c @@ -0,0 +1,638 @@ +/* Watercolor color_select_module, Raph Levien , February 1998 + * + * Ported to loadable color-selector interface, May 1999 + * by Sven Neumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* #define VERBOSE 1 */ + +#include +#include +#include +#include +#include +#include + + +/* prototypes */ +static GtkWidget * colorsel_water_new (int, int, int, + GimpColorSelector_Callback, + void *, + void **); +static void colorsel_water_free (void *); +static void colorsel_water_setcolor (void *, int, int, int, int); +static void colorsel_water_update (); + + +/* local methods */ +static GimpColorSelectorMethods methods = +{ + colorsel_water_new, + colorsel_water_free, + colorsel_water_setcolor +}; + + +static GimpModuleInfo info = { + NULL, + "Watercolor style color selector as a pluggable colour selector", + "Raph Levien , Sven Neumann ", + "v0.2", + "(c) 1998-1999, released under the GPL", + "May, 08 1999" +}; + + +/*************************************************************/ + +/* globaly exported init function */ +G_MODULE_EXPORT GimpModuleStatus +module_init (GimpModuleInfo **inforet) +{ + GimpColorSelectorID id; + + id = gimp_color_selector_register ("Watercolor", &methods); + + if (id) + { + info.shutdown_data = id; + *inforet = &info; + return GIMP_MODULE_OK; + } + else + { + return GIMP_MODULE_UNLOAD; + } +} + + +G_MODULE_EXPORT void +module_unload (void *shutdown_data, + void (*completed_cb)(void *), + void *completed_data) +{ + gimp_color_selector_unregister (shutdown_data, completed_cb, completed_data); +} + + +/* definitions and variables */ + +#define N_BUCKETS 10 +#define LAST_BUCKET (N_BUCKETS) /* bucket number 0 is current color */ +#define IMAGE_SIZE 200 +#define BUCKET_SIZE 20 +#define PREVIEW_SIZE 40 + +typedef struct { + GimpColorSelector_Callback callback; + void *data; +} ColorselWater; + +static gdouble bucket[N_BUCKETS + 1][3]; +static GtkWidget *color_preview[N_BUCKETS + 1]; +static gdouble last_x, last_y, last_pressure; +static guint32 motion_time; +static gint button_state; +static ColorselWater *coldata; + + +static void +set_bucket (gint i, gdouble r, gdouble g, gdouble b) +{ + if (i >= 0 && i <= N_BUCKETS) + { + bucket[i][0] = r; + bucket[i][1] = g; + bucket[i][2] = b; + } +} + +static gdouble +calc (gdouble x, gdouble y, gdouble angle) +{ + gdouble s, c; + + s = 1.6 * sin (angle * M_PI / 180) * 256.0 / IMAGE_SIZE; + c = 1.6 * cos (angle * M_PI / 180) * 256.0 / IMAGE_SIZE; + return 128 + (x - (IMAGE_SIZE >> 1)) * c - (y - (IMAGE_SIZE >> 1)) * s; +} + +static guchar +bucket_to_byte (gdouble val) +{ + return CLAMP ((gint)(val * 280 - 25), 0, 255); +} + +static void +draw_bucket (gint i) +{ + guchar *buf; + gint x, y; + gint width; + gint height; + guchar r, g, b; + + g_return_if_fail (i >= 0 && i <= N_BUCKETS); + +#ifdef VERBOSE + g_print ("draw_bucket %d\n", i); +#endif + + width = GTK_WIDGET(color_preview[i])->allocation.width; + height = GTK_WIDGET(color_preview[i])->allocation.height; + buf = g_new (guchar, 3*width); + + r = bucket_to_byte (bucket[i][0]); + g = bucket_to_byte (bucket[i][1]); + b = bucket_to_byte (bucket[i][2]); + for (x = 0; x < width; x++) + { + buf[x * 3] = r; + buf[x * 3 + 1] = g; + buf[x * 3 + 2] = b; + } + for (y = 0; y < height; y++) + gtk_preview_draw_row (GTK_PREVIEW (color_preview[i]), buf, 0, y, width); + + gtk_widget_draw (color_preview[i], NULL); + g_free (buf); +} + + +static void +draw_all_buckets () +{ + gint i; + +#ifdef VERBOSE + g_print ("drawing all buckets\n"); +#endif + + for (i = 1; i <= N_BUCKETS; i++) + draw_bucket (i); +} + +static void +pick_up_bucket_callback (GtkWidget *widget, + gpointer data) +{ + guint i = GPOINTER_TO_UINT (data); + +#ifdef VERBOSE + g_print ("pick up bucket %d\n", i); +#endif + + if (i > 0 && i <= N_BUCKETS) + { + bucket[0][0] = bucket[i][0]; + bucket[0][1] = bucket[i][1]; + bucket[0][2] = bucket[i][2]; + colorsel_water_update (); + } +} + +static void +shift_buckets () +{ + gint i; + +#ifdef VERBOSE + g_print ("shift\n"); +#endif + + /* to avoid duplication, do nothing if cuurent bucket is already present */ + for (i = 1; i <= N_BUCKETS; i++) + { + if (bucket[i][0] == bucket[0][0] && + bucket[i][1] == bucket[0][1] && + bucket[i][2] == bucket[0][2]) + return; + } + + /* don't bother storing pure white in buckets */ + if (bucket[0][0] == 1.0 && + bucket[0][1] == 1.0 && + bucket[0][2] == 1.0) + return; + + /* shift all buckets one to the right */ + for (i = N_BUCKETS; i > 0; i--) + { + bucket[i][0] = bucket[i-1][0]; + bucket[i][1] = bucket[i-1][1]; + bucket[i][2] = bucket[i-1][2]; + } +} + +/* Initialize the preview */ +static void +select_area_draw (GtkWidget *preview) +{ + guchar buf[3 * IMAGE_SIZE]; + gint x, y; + gdouble r, g, b; + gdouble dr, dg, db; + + for (y = 0; y < IMAGE_SIZE; y++) + { + r = calc (0, y, 0); + g = calc (0, y, 120); + b = calc (0, y, 240); + + dr = calc (1, y, 0) - r; + dg = calc (1, y, 120) - g; + db = calc (1, y, 240) - b; + + for (x = 0; x < IMAGE_SIZE; x++) + { + buf[x * 3] = CLAMP ((gint)r, 0, 255); + buf[x * 3 + 1] = CLAMP ((gint)g, 0, 255); + buf[x * 3 + 2] = CLAMP ((gint)b, 0, 255); + r += dr; + g += dg; + b += db; + } + + gtk_preview_draw_row (GTK_PREVIEW (preview), buf, 0, y, IMAGE_SIZE); + } +} + + +static void +add_pigment (gboolean erase, gdouble x, gdouble y, gdouble much) +{ + gdouble r, g, b; + +#ifdef VERBOSE + g_print ("x: %g, y: %g, much: %g\n", + x, y, much); +#endif + if (erase) + { + bucket[0][0] = 1 - (1 - bucket[0][0]) * (1 - much); + bucket[0][1] = 1 - (1 - bucket[0][1]) * (1 - much); + bucket[0][2] = 1 - (1 - bucket[0][2]) * (1 - much); + } + else + { + r = calc (x, y, 0) / 255.0; + if (r < 0) r = 0; + if (r > 1) r = 1; + + g = calc (x, y, 120) / 255.0; + if (g < 0) g = 0; + if (g > 1) g = 1; + + b = calc (x, y, 240) / 255.0; + if (b < 0) b = 0; + if (b > 1) b = 1; + + bucket[0][0] *= (1 - (1 - r) * much); + bucket[0][1] *= (1 - (1 - g) * much); + bucket[0][2] *= (1 - (1 - b) * much); + } + + colorsel_water_update (); +} + +static void +draw_brush (GtkWidget *widget, gboolean erase, + gdouble x, gdouble y, gdouble pressure) +{ + gdouble much; /* how much pigment to mix in */ + + if (pressure < last_pressure) + last_pressure = pressure; + + much = sqrt ((x - last_x) * (x - last_x) + + (y - last_y) * (y - last_y) + + 1000 * (pressure - last_pressure) * (pressure - last_pressure)); + + much *= pressure * 0.05; + + add_pigment (erase, x, y, much); + + last_x = x; + last_y = y; + last_pressure = pressure; +} + + +static gint +button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + gboolean erase; + +#ifdef VERBOSE + g_print ("button press\n"); +#endif + last_x = event->x; + last_y = event->y; + last_pressure = event->pressure; + + button_state |= 1 << event->button; + + erase = (event->button != 1) || + (event->source == GDK_SOURCE_ERASER); + + add_pigment (erase, event->x, event->y, 0.05); + motion_time = event->time; + + return FALSE; +} + +static gint +button_release_event (GtkWidget *widget, GdkEventButton *event) +{ + button_state &= ~(1 << event->button); + + return TRUE; +} + +static gint +motion_notify_event (GtkWidget *widget, GdkEventMotion *event) +{ + GdkTimeCoord *coords; + int nevents; + int i; + gboolean erase; + + if (event->state & (GDK_BUTTON1_MASK | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK)) + { + coords = gdk_input_motion_events (event->window, event->deviceid, + motion_time, event->time, + &nevents); + erase = (event->state & (GDK_BUTTON3_MASK | GDK_BUTTON4_MASK)) || + (event->source == GDK_SOURCE_ERASER); + motion_time = event->time; + if (coords) + { + for (i=0; iis_hint) + gdk_input_window_get_pointer (event->window, event->deviceid, + NULL, NULL, NULL, NULL, NULL, NULL); + draw_brush (widget, erase, event->x, event->y, + event->pressure); + } + } + else + { + gdk_input_window_get_pointer (event->window, event->deviceid, + &event->x, &event->y, + NULL, NULL, NULL, NULL); + } + + + return TRUE; +} + +static gint +proximity_out_event (GtkWidget *widget, GdkEventProximity *event) +{ +#ifdef VERBOSE + g_print ("proximity out\n"); +#endif /* VERBOSE */ + return TRUE; +} + +static void +new_color_callback (GtkWidget *widget, gpointer *data) +{ +#ifdef VERBOSE + g_print ("new color\n"); +#endif + shift_buckets (); + + /* current bucket becomes white */ + bucket[0][0] = 1.0; + bucket[0][1] = 1.0; + bucket[0][2] = 1.0; + + draw_all_buckets (); + colorsel_water_update (); + + return; +} + +static void +reset_color_callback (GtkWidget *widget, gpointer *data) +{ +#ifdef VERBOSE + g_print ("reset color\n"); +#endif + + /* current bucket becomes white */ + bucket[0][0] = 1.0; + bucket[0][1] = 1.0; + bucket[0][2] = 1.0; + + colorsel_water_update (); + + return; +} + + +/*************************************************************/ +/* methods */ + + +static GtkWidget* +colorsel_water_new (int r, int g, int b, + GimpColorSelector_Callback callback, + void *callback_data, + /* RETURNS: */ + void **selector_data) +{ + GtkWidget *preview; + GtkWidget *event_box; + GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *hbox2; + GtkWidget *hbox3; + GtkWidget *vbox; + GtkWidget *vbox2; + GtkWidget *vbox3; + GtkWidget *table; + GtkWidget *button; + GtkWidget *bbox; + guint i; + + coldata = g_malloc (sizeof (ColorselWater)); + + coldata->callback = callback; + coldata->data = callback_data; + + *selector_data = coldata; + + vbox = gtk_vbox_new (FALSE, 0); + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 8); + + /* the event box */ + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, FALSE, 0); + + event_box = gtk_event_box_new (); + gtk_container_add (GTK_CONTAINER (frame), event_box); + + preview = gtk_preview_new (GTK_PREVIEW_COLOR); + gtk_preview_size (GTK_PREVIEW (preview), IMAGE_SIZE, IMAGE_SIZE); + gtk_container_add (GTK_CONTAINER (event_box), preview); + select_area_draw (preview); + + /* Event signals */ + gtk_signal_connect (GTK_OBJECT (event_box), "motion_notify_event", + (GtkSignalFunc) motion_notify_event, NULL); + gtk_signal_connect (GTK_OBJECT (event_box), "button_press_event", + (GtkSignalFunc) button_press_event, NULL); + gtk_signal_connect (GTK_OBJECT (event_box), "button_release_event", + (GtkSignalFunc) button_release_event, NULL); + gtk_signal_connect (GTK_OBJECT (event_box), "proximity_out_event", + (GtkSignalFunc) proximity_out_event, NULL); + + gtk_widget_set_events (event_box, GDK_EXPOSURE_MASK + | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_KEY_PRESS_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK + | GDK_PROXIMITY_OUT_MASK); + + /* The following call enables tracking and processing of extension + events for the drawing area */ + gtk_widget_set_extension_events (event_box, GDK_EXTENSION_EVENTS_ALL); + gtk_widget_grab_focus (event_box); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_box_pack_end (GTK_BOX (hbox), vbox2, TRUE, FALSE, 0); + + hbox2 = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox2), hbox2, TRUE, FALSE, 8); + + vbox3 = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox2), vbox3, FALSE, FALSE, 8); + hbox3 = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox3), hbox3, FALSE, FALSE, 8); + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (hbox3), frame, TRUE, FALSE, 0); + color_preview[0] = gtk_preview_new (GTK_PREVIEW_COLOR); + gtk_preview_size (GTK_PREVIEW (color_preview[0]), PREVIEW_SIZE, PREVIEW_SIZE); + gtk_container_add (GTK_CONTAINER (frame), color_preview[0]); + gtk_widget_show_all (vbox3); + + bbox = gtk_vbutton_box_new (); + gtk_box_pack_end (GTK_BOX (hbox2), bbox, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("New"); + gtk_container_add (GTK_CONTAINER (bbox), button); + gtk_signal_connect (GTK_OBJECT (button), "pressed", + (GtkSignalFunc) new_color_callback, + NULL); + button = gtk_button_new_with_label ("Reset"); + gtk_container_add (GTK_CONTAINER (bbox), button); + gtk_signal_connect (GTK_OBJECT (button), "pressed", + (GtkSignalFunc) reset_color_callback, + NULL); + + gtk_widget_show_all (hbox2); + + frame = gtk_frame_new ("Color history"); + gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, FALSE, 0); + + table = gtk_table_new (2, 5, TRUE); + gtk_container_set_border_width (GTK_CONTAINER (table), 4); + gtk_container_add (GTK_CONTAINER (frame), table); + + for (i = 0; i < N_BUCKETS; i++) + { + button = gtk_button_new (); + gtk_signal_connect (GTK_OBJECT (button), "pressed", + GTK_SIGNAL_FUNC (pick_up_bucket_callback), + (gpointer) GUINT_TO_POINTER (i+1)); + + gtk_table_attach_defaults (GTK_TABLE (table), + button, + i % 5, (i % 5) + 1, + i / 5, (i/ 5) + 1); + color_preview[i+1] = gtk_preview_new (GTK_PREVIEW_COLOR); + gtk_preview_size (GTK_PREVIEW (color_preview[i+1]), BUCKET_SIZE, BUCKET_SIZE); + gtk_container_add (GTK_CONTAINER (button), color_preview[i+1]); + gtk_widget_show (preview); + gtk_widget_show (button); + set_bucket (i+1, 1.0, 1.0, 1.0); + } + + gtk_widget_show_all (vbox2); + gtk_widget_show_all (hbox); + + colorsel_water_setcolor (coldata, r, g, b, 0); + draw_all_buckets (); + + return (vbox); +} + + +static void +colorsel_water_free (void *selector_data) +{ + g_free (selector_data); +} + +static void +colorsel_water_setcolor (void *data, int r, int g, int b, + int set_current) +{ + set_bucket (0, + ((gdouble) r) / 255.999, + ((gdouble) g) / 255.999, + ((gdouble) b) / 255.999); + draw_bucket (0); +} + +static void +colorsel_water_update () +{ + int r; + int g; + int b; + + r = (int) (bucket[0][0] * 255.999); + g = (int) (bucket[0][1] * 255.999); + b = (int) (bucket[0][2] * 255.999); + + draw_bucket (0); + + coldata->callback (coldata->data, r, g, b); +} + +/* End of colorsel_gtk.c */ + + + + + + + +