/* vpropagate.c -- This is a plug-in for the GIMP (1.0's API) * Author: Shuji Narazaki * Time-stamp: <2000-01-09 15:50:46 yasuhiro> * Version: 0.89a * * Copyright (C) 1996-1997 Shuji Narazaki * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* memo the initial value of each pixel is the value of the pixel itself. To determine whether it is an isolated local peak point, use: (self == min && (! modified_flag)) ; modified_flag holds history of update In other word, pixel itself is not a neighbor of it. */ #include "config.h" #include #include #include #include #include #include #include "libgimp/stdplugins-intl.h" #define PLUG_IN_NAME "plug_in_vpropagate" #define SHORT_NAME "vpropagate" typedef guchar CH; #define VP_RGB (1 << 0) #define VP_GRAY (1 << 1) #define VP_WITH_ALPHA (1 << 2) #define VP_WO_ALPHA (1 << 3) #define num_direction 4 #define Right2Left 0 #define Bottom2Top 1 #define Left2Right 2 #define Top2Bottom 3 static void query (void); static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); static GimpPDBStatusType value_propagate (gint drawable_id); static void value_propagate_body (gint drawable_id); static int vpropagate_dialog (GimpImageBaseType image_type); static void prepare_row (GimpPixelRgn *pixel_rgn, guchar *data, int x, int y, int w); static void vpropagate_ok_callback (GtkWidget *widget, gpointer data); static GtkWidget * gtk_table_add_toggle (GtkWidget *table, gchar *name, gint x1, gint x2, gint y, GtkSignalFunc update, gint *value); static int value_difference_check (CH *, CH *, int); static void set_value (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void initialize_white (GimpImageBaseType, int, CH *, CH *, void **); static void propagate_white (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void initialize_black (GimpImageBaseType, int, CH *, CH *, void **); static void propagate_black (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void initialize_middle (GimpImageBaseType, int, CH *, CH *, void **); static void propagate_middle (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void set_middle_to_peak (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void set_foreground_to_peak (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void initialize_foreground (GimpImageBaseType, int, CH *, CH *, void **); static void initialize_background (GimpImageBaseType, int, CH *, CH *, void **); static void propagate_a_color (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void propagate_opaque (GimpImageBaseType, int, CH *, CH *, CH *, void *); static void propagate_transparent (GimpImageBaseType, int, CH *, CH *, CH *, void *); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; #define UPDATE_STEP 20 #define SCALE_WIDTH 100 #define PROPAGATING_VALUE 1<<0 #define PROPAGATING_ALPHA 1<<1 /* parameters */ typedef struct { gint propagate_mode; gint propagating_channel; gdouble propagating_rate; gint direction_mask; gint lower_limit; gint upper_limit; /* gint channel_mask[3]; */ /* gint peak_includes_equals; */ /* gint overwrite; */ } VPValueType; static VPValueType vpvals = { 0, 3, /* PROPAGATING_VALUE + PROPAGATING_ALPHA */ 1.0, /* [0.0:1.0] */ 15, /* propagate to all 4 directions */ 0, 255 }; /* dialog vairables */ static gint propagate_alpha; static gint propagate_value; static gint direction_mask_vec[4]; static gint channel_mask[] = { 1, 1, 1 }; static gint peak_max = 1; static gint peak_min = 1; static gint peak_includes_equals = 1; static guchar fore[3]; typedef struct { gint applicable_image_type; gchar *name; void (*initializer) (); void (*updater) (); void (*finalizer) (); } ModeParam; #define num_mode 8 static ModeParam modes[num_mode] = { { VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA, N_("More White (Larger Value)"), initialize_white, propagate_white, set_value }, { VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA, N_("More Black (Smaller Value)"), initialize_black, propagate_black, set_value }, { VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA, N_("Middle Value to Peaks"), initialize_middle, propagate_middle, set_middle_to_peak }, { VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA, N_("Foreground to Peaks"), initialize_middle, propagate_middle, set_foreground_to_peak }, { VP_RGB | VP_WITH_ALPHA | VP_WO_ALPHA, N_("Only Foreground"), initialize_foreground, propagate_a_color, set_value }, { VP_RGB | VP_WITH_ALPHA | VP_WO_ALPHA, N_("Only Background"), initialize_background, propagate_a_color, set_value }, { VP_RGB | VP_GRAY | VP_WITH_ALPHA, N_("More Opaque"), NULL, propagate_opaque, set_value }, { VP_RGB | VP_GRAY | VP_WITH_ALPHA, N_("More Transparent"), NULL, propagate_transparent, set_value }, }; typedef struct { gint run; } VPInterface; static VPInterface vpropagate_interface = { FALSE }; static gint drawable_id; MAIN () static void query (void) { static GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image (not used)" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }, { GIMP_PDB_INT32, "propagate-mode", "propagate 0:white, 1:black, 2:middle value 3:foreground to peak, 4:foreground, 5:background, 6:opaque, 7:transparent" }, { GIMP_PDB_INT32, "propagating-channel", "channels which values are propagated" }, { GIMP_PDB_FLOAT, "propagating-rate", "0.0 <= propagatating_rate <= 1.0" }, { GIMP_PDB_INT32, "direction-mask", "0 <= direction-mask <= 15" }, { GIMP_PDB_INT32, "lower-limit", "0 <= lower-limit <= 255" }, { GIMP_PDB_INT32, "upper-limit", "0 <= upper-limit <= 255" } }; static gint nargs = sizeof (args) / sizeof (args[0]); gimp_install_procedure (PLUG_IN_NAME, "Propagate values of the layer", "Propagate values of the layer", "Shuji Narazaki (narazaki@InetQ.or.jp)", "Shuji Narazaki", "1996-1997", N_("/Filters/Distorts/Value Propagate..."), "RGB*,GRAY*", GIMP_PLUGIN, nargs, 0, args, NULL); } static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpRunModeType run_mode; run_mode = param[0].data.d_int32; drawable_id = param[2].data.d_int32; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; switch (run_mode) { case GIMP_RUN_INTERACTIVE: INIT_I18N_UI(); gimp_get_data (PLUG_IN_NAME, &vpvals); /* building the values of dialog variables from vpvals. */ propagate_alpha = (vpvals.propagating_channel & PROPAGATING_ALPHA) ? TRUE : FALSE; propagate_value = (vpvals.propagating_channel & PROPAGATING_VALUE) ? TRUE : FALSE; { int i; for (i = 0; i < 4; i++) direction_mask_vec[i] = (vpvals.direction_mask & (1 << i)) ? TRUE : FALSE; } if (! vpropagate_dialog (gimp_drawable_type(drawable_id))) return; break; case GIMP_RUN_NONINTERACTIVE: INIT_I18N(); vpvals.propagate_mode = param[3].data.d_int32; vpvals.propagating_channel = param[4].data.d_int32; vpvals.propagating_rate = param[5].data.d_float; vpvals.direction_mask = param[6].data.d_int32; vpvals.lower_limit = param[7].data.d_int32; vpvals.upper_limit = param[8].data.d_int32; break; case GIMP_RUN_WITH_LAST_VALS: INIT_I18N(); gimp_get_data (PLUG_IN_NAME, &vpvals); break; } status = value_propagate (drawable_id); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush(); if (run_mode == GIMP_RUN_INTERACTIVE && status == GIMP_PDB_SUCCESS) gimp_set_data (PLUG_IN_NAME, &vpvals, sizeof (VPValueType)); values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; } /* registered function entry */ static GimpPDBStatusType value_propagate (gint drawable_id) { /* check the validness of parameters */ if (!(vpvals.propagating_channel & (PROPAGATING_VALUE | PROPAGATING_ALPHA))) { /* gimp_message ("No channel selected."); */ return GIMP_PDB_EXECUTION_ERROR; } if (vpvals.direction_mask == 0) { /* gimp_message ("No direction selected."); */ return GIMP_PDB_EXECUTION_ERROR; } if ((vpvals.lower_limit < 0) || (255 < vpvals.lower_limit) || (vpvals.upper_limit < 0) || (255 < vpvals.lower_limit) || (vpvals.upper_limit < vpvals.lower_limit)) { /* gimp_message ("Limit values are not valid."); */ return GIMP_PDB_EXECUTION_ERROR; } value_propagate_body (drawable_id); return GIMP_PDB_SUCCESS; } static void value_propagate_body (gint drawable_id) { GimpDrawable *drawable; GimpImageBaseType dtype; ModeParam operation; GimpPixelRgn srcRgn, destRgn; guchar *here, *best, *dest; guchar *dest_row, *prev_row, *cur_row, *next_row, *pr, *cr, *nr, *swap; gint width, height, bytes, index; gint begx, begy, endx, endy, x, y, dx; gint left_index, right_index, up_index, down_index; void *tmp; /* calculate neighbors' indexes */ left_index = (vpvals.direction_mask & (1 << Left2Right)) ? -1 : 0; right_index = (vpvals.direction_mask & (1 << Right2Left)) ? 1 : 0; up_index = (vpvals.direction_mask & (1 << Top2Bottom)) ? -1 : 0; down_index = (vpvals.direction_mask & (1 << Bottom2Top)) ? 1 : 0; operation = modes[vpvals.propagate_mode]; tmp = NULL; drawable = gimp_drawable_get (drawable_id); dtype = gimp_drawable_type (drawable_id); /* Here I use the algorithm of blur.c . */ gimp_drawable_mask_bounds (drawable->id, &begx, &begy, &endx, &endy); width = drawable->width; height = drawable->height; bytes = drawable->bpp; prev_row = g_new (guchar, (endx - begx + 2) * bytes); cur_row = g_new (guchar, (endx - begx + 2) * bytes); next_row = g_new (guchar, (endx - begx + 2) * bytes); dest_row = g_new (guchar, (endx - begx) * bytes); gimp_pixel_rgn_init (&srcRgn, drawable, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&destRgn, drawable, 0, 0, width, height, TRUE, TRUE); pr = prev_row + bytes; cr = cur_row + bytes; nr = next_row + bytes; prepare_row (&srcRgn, pr, begx, (0 < begy) ? begy : begy - 1, endx-begx); prepare_row (&srcRgn, cr, begx, begy, endx-begx); best = g_new (guchar, bytes); gimp_progress_init (_("Value propagating...")); gimp_palette_get_foreground (fore+0, fore+1, fore+2); /* start real job */ for (y = begy ; y < endy ; y++) { prepare_row (&srcRgn, nr, begx, ((y+1) < endy) ? y+1 : endy, endx-begx); for (index = 0; index < (endx - begx) * bytes; index++) dest_row[index] = cr[index]; for (x = 0 ; x < endx - begx; x++) { dest = dest_row + (x * bytes); here = cr + (x * bytes); /* *** copy source value to best value holder *** */ memcpy ((void *)best, (void *)here, bytes); if (operation.initializer) (* operation.initializer)(dtype, bytes, best, here, &tmp); /* *** gather neighbors' values: loop-unfolded version *** */ if (up_index == -1) for (dx = left_index ; dx <= right_index ; dx++) (* operation.updater)(dtype, bytes, here, pr+((x+dx)*bytes), best, tmp); for (dx = left_index ; dx <= right_index ; dx++) if (dx != 0) (* operation.updater)(dtype, bytes, here, cr+((x+dx)*bytes), best, tmp); if (down_index == 1) for (dx = left_index ; dx <= right_index ; dx++) (* operation.updater)(dtype, bytes, here, nr+((x+dx)*bytes), best, tmp); /* *** store it to dest_row*** */ (* operation.finalizer)(dtype, bytes, best, here, dest, tmp); } /* now store destline to destRgn */ gimp_pixel_rgn_set_row (&destRgn, dest_row, begx, y, endx - begx); /* shift the row pointers */ swap = pr; pr = cr; cr = nr; nr = swap; /* update each UPDATE_STEP (optimizer must generate cheap code) */ if ((y % 5) == 0) /*(y % (int) ((endy - begy) / UPDATE_STEP)) == 0 */ gimp_progress_update ((double)y / (double)(endy - begy)); } /* update the region */ gimp_progress_update(1.0); gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update (drawable->id, begx, begy, endx-begx, endy-begy); } static void prepare_row (GimpPixelRgn *pixel_rgn, guchar *data, gint x, gint y, gint w) { gint b; if (y == 0) gimp_pixel_rgn_get_row (pixel_rgn, data, x, (y + 1), w); else if (y == pixel_rgn->h) gimp_pixel_rgn_get_row (pixel_rgn, data, x, (y - 1), w); else gimp_pixel_rgn_get_row (pixel_rgn, data, x, y, w); /* Fill in edge pixels */ for (b = 0; b < pixel_rgn->bpp; b++) { data[-(gint)pixel_rgn->bpp + b] = data[b]; data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b]; } } static void set_value (GimpImageBaseType dtype, gint bytes, guchar *best, guchar *here, guchar *dest, void *tmp) { gint value_chs = 0; gint alpha = 0; gint ch; switch (dtype) { case GIMP_RGB_IMAGE: value_chs = 3; break; case GIMP_RGBA_IMAGE: value_chs = 3; alpha = 3; break; case GIMP_GRAY_IMAGE: value_chs = 1; break; case GIMP_GRAYA_IMAGE: value_chs = 1; alpha = 1; break; default: break; } for (ch = 0; ch < value_chs; ch++) { if (vpvals.propagating_channel & PROPAGATING_VALUE) /* value channel */ *dest++ = (CH)(vpvals.propagating_rate * best[ch] + (1.0 - vpvals.propagating_rate) * here[ch]); else *dest++ = here[ch]; } if (alpha) { if (vpvals.propagating_channel & PROPAGATING_ALPHA) /* alpha channel */ *dest++ = (CH)(vpvals.propagating_rate * best[alpha] + (1.0 - vpvals.propagating_rate) * here[alpha]); else *dest++ = here[alpha]; } } static int value_difference_check (CH *pos1, CH *pos2, gint ch) { gint index; int diff; for (index = 0 ; index < ch; index++) if (channel_mask[index] != 0) { diff = abs(pos1[index] - pos2[index]); if (! ((vpvals.lower_limit <= diff) && (diff <= vpvals.upper_limit))) return 0; } return 1; } /* mothods for each mode */ static void initialize_white (GimpImageBaseType dtype, gint bytes, CH *best, CH *here, void **tmp) { if (*tmp == NULL) *tmp = (void *) g_new (gfloat, 1); *(float *)*tmp = sqrt (channel_mask[0] * here[0] * here[0] + channel_mask[1] * here[1] * here[1] + channel_mask[2] * here[2] * here[2]); } static void propagate_white (GimpImageBaseType dtype, gint bytes, CH *orig, CH *here, CH *best, void *tmp) { float v_here; switch (dtype) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: v_here = sqrt (channel_mask[0] * here[0] * here[0] + channel_mask[1] * here[1] * here[1] + channel_mask[2] * here[2] * here[2]); if (*(float *)tmp < v_here && value_difference_check(orig, here, 3)) { *(float *)tmp = v_here; memcpy(best, here, 3 * sizeof(CH)); /* alpha channel holds old value */ } break; case GIMP_GRAYA_IMAGE: case GIMP_GRAY_IMAGE: if (*best < *here && value_difference_check(orig, here, 1)) *best = *here; break; default: break; } } static void initialize_black (GimpImageBaseType dtype, gint channels, CH *best, CH *here, void **tmp) { if (*tmp == NULL) *tmp = (void *) g_new (gfloat, 1); *(float *)*tmp = sqrt (channel_mask[0] * here[0] * here[0] + channel_mask[1] * here[1] * here[1] + channel_mask[2] * here[2] * here[2]); } static void propagate_black (GimpImageBaseType image_type, gint channels, CH *orig, CH *here, CH *best, void *tmp) { float v_here; switch (image_type) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: v_here = sqrt (channel_mask[0] * here[0] * here[0] + channel_mask[1] * here[1] * here[1] + channel_mask[2] * here[2] * here[2]); if (v_here < *(float *)tmp && value_difference_check(orig, here, 3)) { *(float *)tmp = v_here; memcpy (best, here, 3 * sizeof(CH)); /* alpha channel holds old value */ } break; case GIMP_GRAYA_IMAGE: case GIMP_GRAY_IMAGE: if (*here < *best && value_difference_check(orig, here, 1)) *best = *here; break; default: break; } } typedef struct { gshort min_modified; gshort max_modified; glong original_value; glong minv; CH min[3]; glong maxv; CH max[3]; } MiddlePacket; static void initialize_middle (GimpImageBaseType image_type, gint channels, CH *best, CH *here, void **tmp) { int index; MiddlePacket *data; if (*tmp == NULL) *tmp = (void *) g_new (MiddlePacket, 1); data = (MiddlePacket *)*tmp; for (index = 0; index < channels; index++) data->min[index] = data->max[index] = here[index]; switch (image_type) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: data->original_value = sqrt (channel_mask[0] * here[0] * here[0] + channel_mask[1] * here[1] * here[1] + channel_mask[2] * here[2] * here[2]); break; case GIMP_GRAYA_IMAGE: case GIMP_GRAY_IMAGE: data->original_value = *here; break; default: break; } data->minv = data->maxv = data->original_value; data->min_modified = data->max_modified = 0; } static void propagate_middle (GimpImageBaseType image_type, gint channels, CH *orig, CH *here, CH *best, void *tmp) { float v_here; MiddlePacket *data; data = (MiddlePacket *)tmp; switch (image_type) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: v_here = sqrt (channel_mask[0] * here[0] * here[0] + channel_mask[1] * here[1] * here[1] + channel_mask[2] * here[2] * here[2]); if ((v_here <= data->minv) && value_difference_check(orig, here, 3)) { data->minv = v_here; memcpy (data->min, here, 3*sizeof(CH)); data->min_modified = 1; } if (data->maxv <= v_here && value_difference_check(orig, here, 3)) { data->maxv = v_here; memcpy (data->max, here, 3*sizeof(CH)); data->max_modified = 1; } break; case GIMP_GRAYA_IMAGE: case GIMP_GRAY_IMAGE: if ((*here <= data->min[0]) && value_difference_check(orig, here, 1)) { data->min[0] = *here; data->min_modified = 1; } if ((data->max[0] <= *here) && value_difference_check(orig, here, 1)) { data->max[0] = *here; data->max_modified = 1; } break; default: break; } } static void set_middle_to_peak (GimpImageBaseType image_type, gint channels, CH *here, CH *best, CH *dest, void *tmp) { gint value_chs = 0; gint alpha = 0; gint ch; MiddlePacket *data; data = (MiddlePacket *)tmp; if (! ((peak_min & (data->minv == data->original_value)) || (peak_max & (data->maxv == data->original_value)))) return; if ((! peak_includes_equals) && ((peak_min & (! data->min_modified)) || (peak_max & (! data->max_modified)))) return; switch (image_type) { case GIMP_RGB_IMAGE: value_chs = 3; break; case GIMP_RGBA_IMAGE: value_chs = 3; alpha = 3; break; case GIMP_GRAY_IMAGE: value_chs = 1; break; case GIMP_GRAYA_IMAGE: value_chs = 1; alpha = 1; break; default: break; } for (ch = 0; ch < value_chs; ch++) { if (vpvals.propagating_channel & PROPAGATING_VALUE) /* value channel */ *dest++ = (CH)(vpvals.propagating_rate * 0.5 * (data->min[ch] + data->max[ch]) + (1.0 - vpvals.propagating_rate) * here[ch]); else *dest++ = here[ch]; } if (alpha) { if (vpvals.propagating_channel & PROPAGATING_ALPHA) /* alpha channel */ *dest++ = (CH)(vpvals.propagating_rate * best[alpha] + (1.0 - vpvals.propagating_rate) * here[alpha]); else *dest++ = here[alpha]; } } static void set_foreground_to_peak (GimpImageBaseType image_type, gint channels, CH *here, CH *best, CH *dest, void *tmp) { gint value_chs = 0; gint alpha = 0; gint ch; MiddlePacket *data; data = (MiddlePacket *)tmp; if (! ((peak_min & (data->minv == data->original_value)) || (peak_max & (data->maxv == data->original_value)))) return; if (peak_includes_equals && ((peak_min & (! data->min_modified)) || (peak_max & (! data->max_modified)))) return; switch (image_type) { case GIMP_RGB_IMAGE: value_chs = 3; break; case GIMP_RGBA_IMAGE: value_chs = 3; alpha = 3; break; case GIMP_GRAY_IMAGE: value_chs = 1; break; case GIMP_GRAYA_IMAGE: value_chs = 1; alpha = 1; break; default: break; } for (ch = 0; ch < value_chs; ch++) { if (vpvals.propagating_channel & PROPAGATING_VALUE) /* value channel */ *dest++ = (CH)(vpvals.propagating_rate*fore[ch] + (1.0 - vpvals.propagating_rate)*here[ch]); else *dest++ = here[ch]; } } static void initialize_foreground (GimpImageBaseType image_type, gint channels, CH *here, CH *best, void **tmp) { CH *ch; if (*tmp == NULL) { *tmp = (void *) g_new (CH, 3); ch = (CH *)*tmp; gimp_palette_get_foreground (&ch[0], &ch[1], &ch[2]); } } static void initialize_background (GimpImageBaseType image_type, gint channels, CH *here, CH *best, void **tmp) { CH *ch; if (*tmp == NULL) { *tmp = (void *) g_new (CH, 3); ch = (CH *)*tmp; gimp_palette_get_background (&ch[0], &ch[1], &ch[2]); } } static void propagate_a_color (GimpImageBaseType image_type, gint channels, CH *orig, CH *here, CH *best, void *tmp) { CH *fg = (CH *)tmp; switch (image_type) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: if (here[0] == fg[0] && here[1] == fg[1] && here[2] == fg[2] && value_difference_check(orig, here, 3)) { memcpy (best, here, 3 * sizeof(CH)); /* alpha channel holds old value */ } break; case GIMP_GRAYA_IMAGE: case GIMP_GRAY_IMAGE: break; default: break; } } static void propagate_opaque (GimpImageBaseType image_type, gint channels, CH *orig, CH *here, CH *best, void *tmp) { switch (image_type) { case GIMP_RGBA_IMAGE: if (best[3] < here[3] && value_difference_check(orig, here, 3)) memcpy(best, here, channels * sizeof(CH)); break; case GIMP_GRAYA_IMAGE: if (best[1] < here[1] && value_difference_check(orig, here, 1)) memcpy(best, here, channels * sizeof(CH)); break; default: break; } } static void propagate_transparent (GimpImageBaseType image_type, gint channels, CH *orig, CH *here, CH *best, void *tmp) { switch (image_type) { case GIMP_RGBA_IMAGE: if (here[3] < best[3] && value_difference_check(orig, here, 3)) memcpy(best, here, channels * sizeof(CH)); break; case GIMP_GRAYA_IMAGE: if (here[1] < best[1] && value_difference_check(orig, here, 1)) memcpy(best, here, channels * sizeof(CH)); break; default: break; } } /* dialog stuff */ static int vpropagate_dialog (GimpImageBaseType image_type) { GtkWidget *dlg; GtkWidget *hbox; GtkWidget *frame; GtkWidget *table; GtkWidget *toggle_vbox; GtkWidget *button; GtkWidget *sep; GtkObject *adj; GSList *group = NULL; gint index = 0; gimp_ui_init ("vpropagate", FALSE); dlg = gimp_dialog_new (_("Value Propagate"), "vpropagate", gimp_standard_help_func, "filters/vpropagate.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, _("OK"), vpropagate_ok_callback, NULL, NULL, NULL, TRUE, FALSE, _("Cancel"), gtk_widget_destroy, NULL, 1, NULL, FALSE, TRUE, NULL); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); hbox = gtk_hbox_new (FALSE, 4); gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); /* Propagate Mode */ frame = gtk_frame_new (_("Propagate Mode")); gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0); toggle_vbox = gtk_vbox_new (FALSE, 1); gtk_container_set_border_width (GTK_CONTAINER (toggle_vbox), 2); gtk_container_add (GTK_CONTAINER (frame), toggle_vbox); for (index = 0; index < num_mode; index++) { button = gtk_radio_button_new_with_label (group, gettext (modes[index].name)); group = gtk_radio_button_group (GTK_RADIO_BUTTON (button)); gtk_box_pack_start (GTK_BOX (toggle_vbox), button, FALSE, FALSE, 0); gtk_object_set_user_data (GTK_OBJECT (button), (gpointer) index); gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (gimp_radio_button_update), &vpvals.propagate_mode); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), index == vpvals.propagate_mode); gtk_widget_show (button); } gtk_widget_show (toggle_vbox); gtk_widget_show (frame); /* Parameter settings */ frame = gtk_frame_new (_("Parameter Settings")); gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0); table = gtk_table_new (10, 3, FALSE); /* 4 raw, 2 columns(name and value) */ gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_container_set_border_width (GTK_CONTAINER (table), 4); gtk_container_add (GTK_CONTAINER (frame), table); adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, _("Lower Threshold:"), SCALE_WIDTH, 0, vpvals.lower_limit, 0, 255, 1, 8, 0, TRUE, 0, 0, NULL, NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (gimp_int_adjustment_update), &vpvals.lower_limit); adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1, _("Upper Threshold:"), SCALE_WIDTH, 0, vpvals.upper_limit, 0, 255, 1, 8, 0, TRUE, 0, 0, NULL, NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (gimp_int_adjustment_update), &vpvals.upper_limit); adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, _("Propagating Rate:"), SCALE_WIDTH, 0, vpvals.propagating_rate, 0, 1, 0.01, 0.1, 2, TRUE, 0, 0, NULL, NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &vpvals.propagating_rate); sep = gtk_hseparator_new (); gtk_table_attach (GTK_TABLE (table), sep, 0, 3, 3, 4, GTK_FILL, 0, 0, 0); gtk_widget_show (sep); gtk_table_add_toggle (table, _("To Left"), 0, 1, 5, GTK_SIGNAL_FUNC (gimp_toggle_button_update), &direction_mask_vec[Right2Left]); gtk_table_add_toggle (table, _("To Right"), 2, 3, 5, GTK_SIGNAL_FUNC (gimp_toggle_button_update), &direction_mask_vec[Left2Right]); gtk_table_add_toggle (table, _("To Top"), 1, 2, 4, GTK_SIGNAL_FUNC (gimp_toggle_button_update), &direction_mask_vec[Bottom2Top]); gtk_table_add_toggle (table, _("To Bottom"), 1, 2, 6, GTK_SIGNAL_FUNC (gimp_toggle_button_update), &direction_mask_vec[Top2Bottom]); if ((image_type == GIMP_RGBA_IMAGE) | (image_type == GIMP_GRAYA_IMAGE)) { sep = gtk_hseparator_new (); gtk_table_attach (GTK_TABLE (table), sep, 0, 3, 7, 8, GTK_FILL, 0, 0, 0); gtk_widget_show (sep); { GtkWidget *toggle; toggle = gtk_table_add_toggle (table, _("Propagating Alpha Channel"), 0, 3, 8, (GtkSignalFunc) gimp_toggle_button_update, &propagate_alpha); if (gimp_layer_get_preserve_transparency (drawable_id)) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), 0); gtk_widget_set_sensitive (toggle, FALSE); } } gtk_table_add_toggle (table, _("Propagating Value Channel"), 0, 3, 9, (GtkSignalFunc) gimp_toggle_button_update, &propagate_value); } gtk_widget_show (table); gtk_widget_show (frame); gtk_widget_show (hbox); gtk_widget_show (dlg); gtk_main (); gdk_flush (); return vpropagate_interface.run; } static void vpropagate_ok_callback (GtkWidget *widget, gpointer data) { gint i, result; for (i = result = 0; i < 4; i++) result |= (direction_mask_vec[i] ? 1 : 0) << i; vpvals.direction_mask = result; vpvals.propagating_channel = ((propagate_alpha ? PROPAGATING_ALPHA : 0) | (propagate_value ? PROPAGATING_VALUE : 0)); vpropagate_interface.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } static GtkWidget * gtk_table_add_toggle (GtkWidget *table, gchar *name, gint x1, gint x2, gint y, GtkSignalFunc update, gint *value) { GtkWidget *toggle; toggle = gtk_check_button_new_with_label(name); gtk_table_attach (GTK_TABLE (table), toggle, x1, x2, y, y+1, GTK_FILL|GTK_EXPAND, 0, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) update, value); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), *value); gtk_widget_show (toggle); return toggle; }