diff --git a/ChangeLog b/ChangeLog index e05bd87d40..74eda8e60d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Fri Mar 24 17:13:41 CET 2000 Sven Neumann + + plug-ins/common/mapcolor.c: applied gimp-kirchgessner-000320-0. + Fixes problem with divide by zero when selecting same colors, + adds preview and makes the GUI more intuitive. + + Additionally, I've added another check for the drawable being RGB + in the function that does the actual work. Remember that the + image_type may change while the dialog is open! + Fri Mar 24 15:48:40 CET 2000 Sven Neumann applied gimp-quinet-000310-0 by Raphael Quinet diff --git a/plug-ins/common/mapcolor.c b/plug-ins/common/mapcolor.c index d0d5bf21a0..c2531d27e8 100644 --- a/plug-ins/common/mapcolor.c +++ b/plug-ins/common/mapcolor.c @@ -24,10 +24,13 @@ * V 1.00, PK, 26-Oct-98: Creation. * V 1.01, PK, 21-Nov-99: Fix problem with working on layered images * Internationalization + * V 1.02, PK, 19-Mar-00: Better explaining text/dialogue + * Preview added + * Fix problem with divide by zero */ -#define VERSIO 1.01 -static char dversio[] = "v1.01 21-Nov-99"; -static char ident[] = "@(#) GIMP mapcolor plug-in v1.01 21-Nov-99"; +#define VERSIO 1.02 +static char dversio[] = "v1.02 19-Mar-00"; +static char ident[] = "@(#) GIMP mapcolor plug-in v1.02 19-Mar-00"; #include "config.h" @@ -57,7 +60,7 @@ typedef struct gint32 map_mode; } PluginValues; -PluginValues plvals = +PluginValues plvals = { { { 0, 0, 0}, @@ -68,6 +71,26 @@ PluginValues plvals = 0 }; + +/* Preview handling stuff */ + +#define IMG_PRV_SIZE 128 + +typedef struct +{ + guint width, height; + guchar *img; +} IMG_PREVIEW; + + +typedef struct +{ + GtkWidget *preview; + IMG_PREVIEW *img_preview; + IMG_PREVIEW *map_preview; +} PLInterface; + + gint run_flag = FALSE; /* Declare some local functions. @@ -87,9 +110,22 @@ GPlugInInfo PLUG_IN_INFO = run, /* run_proc */ }; -static gint dialog (void); +static IMG_PREVIEW *img_preview_alloc (guint width, guint height); +static void img_preview_free (IMG_PREVIEW *ip); +static void img_preview_copy (IMG_PREVIEW *src, IMG_PREVIEW **dst); +static IMG_PREVIEW *img_preview_create_from_drawable (guint maxsize, + gint32 drawable_ID); + +static void update_img_preview (void); + +static gint dialog (gint32 drawable_ID); static void mapcolor_ok_callback (GtkWidget *widget, gpointer data); +static void get_mapping (guchar *src_col1, guchar *src_col2, + guchar *dst_col1, guchar *dst_col2, gint32 map_mode, + guchar *redmap, guchar *greenmap, guchar *bluemap); +static void color_button_color_changed_callback (GtkWidget *widget, + gpointer data); static void add_color_button (gint csel_index, gint left, gint top, @@ -101,14 +137,174 @@ static void color_mapping (GDrawable *drawable); /* The run mode */ static GRunModeType l_run_mode; -static gchar *csel_title[4] = -{ - N_("First Source Color"), +static gchar *csel_title[4] = +{ + N_("First Source Color"), N_("Second Source Color"), - N_("First Destination Color"), - N_("Second Destination Color") + N_("First Destination Color"), + N_("Second Destination Color") }; +static PLInterface plinterface; + + +/* Allocate image preview structure and preview memory */ +static IMG_PREVIEW * +img_preview_alloc (guint width, guint height) + +{IMG_PREVIEW *ip; + + ip = (IMG_PREVIEW *)g_malloc (sizeof (IMG_PREVIEW)); + ip->img = (guchar *)g_malloc (width*height*3); + if (ip->img == NULL) + { + g_free (ip); + return NULL; + } + ip->width = width; + ip->height = height; + + return ip; +} + + +/* Free image preview */ +static void +img_preview_free (IMG_PREVIEW *ip) + +{ + if (ip) + { + if (ip->img) + { + g_free (ip->img); + ip->img = NULL; + } + ip->width = ip->height = 0; + g_free (ip); + } +} + + +/* Copy image preview. Create/modify destinataion preview */ +static void +img_preview_copy (IMG_PREVIEW *src, IMG_PREVIEW **dst) + +{int numbytes; + IMG_PREVIEW *dst_p; + + if ((src == NULL) || (src->img == NULL) || (dst == NULL)) return; + + numbytes = src->width * src->height * 3; /* 1 byte spare */ + if (numbytes <= 0) return; + + if (*dst == NULL) /* Create new preview ? */ + { + *dst = img_preview_alloc (src->width, src->height); + if (*dst == NULL) return; + memcpy ((*dst)->img, src->img, numbytes); + return; + } + + /* destination preview already exists */ + dst_p = *dst; + + /* Did not already allocate enough memory ? */ + if ((dst_p->img != NULL) && (dst_p->width*dst_p->height*3 < numbytes)) + { + g_free (dst_p->img); + dst_p->width = dst_p->height = 0; + dst_p->img = NULL; + } + if (dst_p->img == NULL) + { + dst_p->img = (guchar *)g_malloc (numbytes); + if (dst_p->img == NULL) return; + } + dst_p->width = src->width; + dst_p->height = src->height; + memcpy (dst_p->img, src->img, numbytes); +} + + +static IMG_PREVIEW * +img_preview_create_from_drawable (guint maxsize, gint32 drawable_ID) + +{GDrawable *drw; + GPixelRgn pixel_rgn; + guint drw_width, drw_height; + guint prv_width, prv_height; + gint src_x, src_y, x, y; + guchar *prv_data, *img_data, *cu_row; + double xfactor, yfactor; + gint tile_height, row_start, row_end; + gint bpp; + IMG_PREVIEW *ip; + + drw_width = gimp_drawable_width (drawable_ID); + drw_height = gimp_drawable_height (drawable_ID); + tile_height = (gint)gimp_tile_height(); + bpp = gimp_drawable_bpp(drawable_ID); + + img_data = g_malloc (drw_width * tile_height * bpp); + if (img_data == NULL) return NULL; + + /* Calculate preview size */ + if ((drw_width <= maxsize) && (drw_height <= maxsize)) + { + prv_width = drw_width; + prv_height = drw_height; + } + else + { + xfactor = ((double)maxsize) / ((double)drw_width); + yfactor = ((double)maxsize) / ((double)drw_height); + if (xfactor < yfactor) + { + prv_width = maxsize; + prv_height = (guint)(drw_height * xfactor); + } + else + { + prv_width = (guint)(drw_width * yfactor); + prv_height = maxsize; + } + } + ip = img_preview_alloc (prv_width, prv_height); + if (ip == NULL) return NULL; + + drw = gimp_drawable_get (drawable_ID); + prv_data = ip->img; + gimp_pixel_rgn_init (&pixel_rgn, drw, 0, 0, drw_width, drw_height, + FALSE, FALSE); + row_start = row_end = -1; + + /* Get the pixels for the preview from the drawable */ + for (y = 0; y < prv_height; y++) + { + src_y = ((drw_height-1) * y) / (prv_height-1); + if (src_y > row_end) /* Need new row ? */ + { + row_start = (src_y / tile_height) * tile_height; + row_end = row_start+tile_height-1; + if (row_end > drw_height-1) row_end = drw_height-1; + gimp_pixel_rgn_get_rect (&pixel_rgn, img_data, 0, row_start, drw_width, + row_end-row_start+1); + } + cu_row = img_data + (src_y-row_start)*drw_width*bpp; + for (x = 0; x < prv_width; x++) + { + src_x = ((drw_width-1) * x) / (prv_width-1); + memcpy (prv_data, cu_row+bpp*src_x, 3); + prv_data += 3; + } + } + + gimp_drawable_detach (drw); + g_free (img_data); + return ip; +} + MAIN () @@ -140,10 +336,12 @@ query (void) INIT_I18N (); gimp_install_procedure ("plug_in_color_adjust", - "Adjust current foreground/background color in the " - "drawable to black/white", - "The current foreground color is mapped to black, " - "the current background color is mapped to white.", + "Adjust color range given by foreground/background " + "color to black/white", + "The current foreground color is mapped to black " + "(black point), the current background color is " + "mapped to white (white point). Intermediate " + "colors are interpolated", "Peter Kirchgessner", "Peter Kirchgessner", dversio, @@ -154,14 +352,15 @@ query (void) adjust_args, NULL); gimp_install_procedure ("plug_in_color_map", - "Map two source colors to two destination colors. " - "Other colors are mapped by interpolation.", - "Map two source colors to two destination colors. " - "Other colors are mapped by interpolation.", + "Map color range specified by two colors" + "to color range specified by two other color.", + "Map color range specified by two colors" + "to color range specified by two other color." + "Intermediate colors are interpolated.", "Peter Kirchgessner", "Peter Kirchgessner", dversio, - N_("/Filters/Colors/Map/Color Mapping..."), + N_("/Filters/Colors/Map/Color Range Mapping..."), "RGB*", PROC_PLUG_IN, nmap_args, 0, @@ -268,7 +467,7 @@ run (gchar *name, c = &(plvals.colors[1][0]); /* Second source color */ gimp_palette_get_background (c, c+1, c+2); - if (!dialog ()) + if (!dialog (param[2].data.d_drawable)) break; } else if (run_mode == RUN_WITH_LAST_VALS) @@ -302,15 +501,54 @@ run (gchar *name, values[0].data.d_status = status; } +static void +update_img_preview (void) + +{IMG_PREVIEW *dst_ip = plinterface.map_preview; + IMG_PREVIEW *src_ip = plinterface.img_preview; + guchar *src, *dst; + GtkWidget *preview = plinterface.preview; + int j; + unsigned char redmap[256], greenmap[256], bluemap[256]; + unsigned char *src_col1 = plvals.colors[0]; + unsigned char *src_col2 = plvals.colors[1]; + unsigned char *dst_col1 = plvals.colors[2]; + unsigned char *dst_col2 = plvals.colors[3]; + + if ((dst_ip == NULL) || (src_ip == NULL)) return; + + get_mapping (src_col1, src_col2, dst_col1, dst_col2, plvals.map_mode, + redmap, greenmap, bluemap); + + j = dst_ip->width*dst_ip->height; + src = src_ip->img; + dst = dst_ip->img; + while (j-- > 0) + { + *(dst++) = redmap[*(src++)]; + *(dst++) = greenmap[*(src++)]; + *(dst++) = bluemap[*(src++)]; + } + for (j = 0; j < dst_ip->height; j++) + gtk_preview_draw_row (GTK_PREVIEW (preview), dst_ip->img + dst_ip->width*3*j, + 0, j, dst_ip->width); + gtk_widget_draw (preview, NULL); + gdk_flush (); +} + + static gint -dialog (void) +dialog (gint32 drawable_ID) { GtkWidget *dlg; - GtkWidget *frame; + GtkWidget *frame, *pframe; + GtkWidget *abox; GtkWidget *table; + GtkWidget *preview; + IMG_PREVIEW *ip; guchar *color_cube; gchar **argv; - gint argc; + gint argc, j; argc = 1; argv = g_new (gchar *, 1); @@ -330,7 +568,9 @@ dialog (void) gtk_widget_set_default_visual (gtk_preview_get_visual ()); gtk_widget_set_default_colormap (gtk_preview_get_cmap ()); - dlg = gimp_dialog_new (_("Map Colors"), "mapcolor", + memset (&plinterface, 0, sizeof (plinterface)); + + dlg = gimp_dialog_new (_("Map Color Range"), "mapcolor", gimp_plugin_help_func, "filters/mapcolor.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, @@ -346,24 +586,59 @@ dialog (void) GTK_SIGNAL_FUNC (gtk_main_quit), NULL); - frame = gtk_frame_new (_("Colors")); - gtk_container_set_border_width (GTK_CONTAINER (frame), 6); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, FALSE, 0); - gtk_widget_show (frame); + /* Preview */ + ip = img_preview_create_from_drawable (IMG_PRV_SIZE, drawable_ID); + if (ip) + { + plinterface.img_preview = ip; + img_preview_copy (plinterface.img_preview, &(plinterface.map_preview)); - /* The table keeps the color selections */ - table = gtk_table_new (2, 4, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacing (GTK_TABLE (table), 1, 6); - gtk_container_set_border_width (GTK_CONTAINER (table), 4); - gtk_container_add (GTK_CONTAINER (frame), table); - gtk_widget_show (table); + frame = gtk_frame_new (_("Preview")); + gtk_container_set_border_width (GTK_CONTAINER (frame), 6); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox),frame,FALSE,FALSE,0); - add_color_button (0, 0, 0, table); - add_color_button (1, 0, 1, table); - add_color_button (2, 2, 0, table); - add_color_button (3, 2, 1, table); + abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_container_set_border_width (GTK_CONTAINER (abox), 4); + gtk_container_add (GTK_CONTAINER (frame), abox); + + pframe = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (pframe), GTK_SHADOW_IN); + gtk_container_set_border_width (GTK_CONTAINER (pframe), 4); + gtk_container_add (GTK_CONTAINER (abox), pframe); + + preview = gtk_preview_new (GTK_PREVIEW_COLOR); + plinterface.preview = preview; + gtk_preview_size (GTK_PREVIEW (preview), ip->width, ip->height); + gtk_container_add (GTK_CONTAINER (pframe), preview); + + gtk_widget_show (preview); + gtk_widget_show (pframe); + gtk_widget_show (abox); + gtk_widget_show (frame); + } + + for (j = 0; j < 2; j++) + { + frame = gtk_frame_new ((j==0) ? _("Source color range") + : _("Destination color range")); + gtk_container_set_border_width (GTK_CONTAINER (frame), 6); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox),frame,FALSE,FALSE,0); + gtk_widget_show (frame); + + /* The table keeps the color selections */ + table = gtk_table_new (1, 4, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacing (GTK_TABLE (table), 1, 6); + gtk_container_set_border_width (GTK_CONTAINER (table), 4); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + add_color_button (j*2, 0, 0, table); + add_color_button (j*2 + 1, 2, 0, table); + } + + update_img_preview (); gtk_widget_show (dlg); @@ -373,6 +648,14 @@ dialog (void) return run_flag; } +static void +color_button_color_changed_callback (GtkWidget *widget, + gpointer data) +{ + update_img_preview (); +} + + static void add_color_button (gint csel_index, gint left, @@ -392,11 +675,15 @@ add_color_button (gint csel_index, button = gimp_color_button_new (gettext (csel_title[csel_index]), PRV_WIDTH, PRV_HEIGHT, plvals.colors[csel_index], 3); + gtk_signal_connect (GTK_OBJECT (button), "color_changed", + GTK_SIGNAL_FUNC (color_button_color_changed_callback), + NULL); gtk_table_attach (GTK_TABLE (table), button, left+1, left+2, top, top+1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (button); } + static void mapcolor_ok_callback (GtkWidget *widget, gpointer data) @@ -406,6 +693,9 @@ mapcolor_ok_callback (GtkWidget *widget, run_flag = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); + + img_preview_free (plinterface.img_preview); + img_preview_free (plinterface.map_preview); } static void @@ -436,6 +726,7 @@ get_mapping (guchar *src_col1, { a = src_col1[rgb]; as = dst_col1[rgb]; b = src_col2[rgb]; bs = dst_col2[rgb]; + if (b == a) b = a+1; for (i = 0; i < 256; i++) { j = ((i - a) * (bs - as)) / (b - a) + as; @@ -467,6 +758,12 @@ color_mapping (GDrawable *drawable) || (src_col1[1] == src_col2[1]) || (src_col1[2] == src_col2[2])) return; + if (!gimp_drawable_is_rgb (drawable->id)) + { + g_message (_("Color Mapping / Adjust FG/BG:\nCannot operate on gray/indexed images")); + return; + } + gimp_drawable_mask_bounds (drawable->id, &xmin, &ymin, &xmax, &ymax); if ((ymin == ymax) || (xmin == xmax)) return; total = (xmax - xmin) * (ymax - ymin);