1
/* vpropagate.c -- This is a plug-in for GIMP (1.0's API)
2
* Author: Shuji Narazaki <narazaki@InetQ.or.jp>
3
* Time-stamp: <2000-01-09 15:50:46 yasuhiro>
6
* Copyright (C) 1996-1997 Shuji Narazaki <narazaki@InetQ.or.jp>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
the initial value of each pixel is the value of the pixel itself.
25
To determine whether it is an isolated local peak point, use:
26
(self == min && (! modified_flag)) ; modified_flag holds history of update
27
In other word, pixel itself is not a neighbor of it.
30
in response to bug #156545, after lengthy discussion, the meanings
31
of "dilate" and "erode" are being swapped -- 19 May 2006.
39
#include <libgimp/gimp.h>
40
#include <libgimp/gimpui.h>
42
#include "libgimp/stdplugins-intl.h"
45
#define VPROPAGATE_PROC "plug-in-vpropagate"
46
#define ERODE_PROC "plug-in-erode"
47
#define DILATE_PROC "plug-in-dilate"
48
#define PLUG_IN_BINARY "value-propagate"
49
#define PLUG_IN_IMAGE_TYPES "RGB*, GRAY*"
51
#define VP_RGB (1 << 0)
52
#define VP_GRAY (1 << 1)
53
#define VP_WITH_ALPHA (1 << 2)
54
#define VP_WO_ALPHA (1 << 3)
55
#define num_direction 4
61
static void query (void);
62
static void run (const gchar *name,
64
const GimpParam *param,
66
GimpParam **return_vals);
68
static GimpPDBStatusType value_propagate (GimpDrawable *drawable);
69
static void value_propagate_body (GimpDrawable *drawable,
70
GimpPreview *preview);
71
static gboolean vpropagate_dialog (GimpDrawable *drawable);
72
static void prepare_row (GimpPixelRgn *pixel_rgn,
78
static void vpropagate_toggle_button_update (GtkWidget *widget,
80
static GtkWidget * gtk_table_add_toggle (GtkWidget *table,
88
static int value_difference_check (guchar *, guchar *, int);
89
static void set_value (GimpImageBaseType,
90
int, guchar *, guchar *, guchar *,
92
static void initialize_white (GimpImageBaseType,
93
int, guchar *, guchar *,
95
static void propagate_white (GimpImageBaseType,
96
int, guchar *, guchar *, guchar *,
98
static void initialize_black (GimpImageBaseType,
99
int, guchar *, guchar *,
101
static void propagate_black (GimpImageBaseType,
102
int, guchar *, guchar *, guchar *,
104
static void initialize_middle (GimpImageBaseType,
105
int, guchar *, guchar *,
107
static void propagate_middle (GimpImageBaseType,
108
int, guchar *, guchar *, guchar *,
110
static void set_middle_to_peak (GimpImageBaseType,
111
int, guchar *, guchar *, guchar *,
113
static void set_foreground_to_peak (GimpImageBaseType,
114
int, guchar *, guchar *, guchar *,
116
static void initialize_foreground (GimpImageBaseType,
117
int, guchar *, guchar *,
119
static void initialize_background (GimpImageBaseType,
120
int, guchar *, guchar *,
122
static void propagate_a_color (GimpImageBaseType,
123
int, guchar *, guchar *, guchar *,
125
static void propagate_opaque (GimpImageBaseType,
126
int, guchar *, guchar *, guchar *,
128
static void propagate_transparent (GimpImageBaseType,
129
int, guchar *, guchar *, guchar *,
132
const GimpPlugInInfo PLUG_IN_INFO =
134
NULL, /* init_proc */
135
NULL, /* quit_proc */
136
query, /* query_proc */
140
#define SCALE_WIDTH 100
141
#define PROPAGATING_VALUE (1 << 0)
142
#define PROPAGATING_ALPHA (1 << 1)
148
gint propagating_channel;
149
gdouble propagating_rate;
155
static VPValueType vpvals =
157
0, /* propagate_mode */
158
3, /* PROPAGATING_VALUE + PROPAGATING_ALPHA */
160
15, /* propagate to all 4 directions */
162
255 /* upper_limit */
165
/* dialog variables */
166
static gint propagate_alpha;
167
static gint propagate_value;
168
static gint direction_mask_vec[4];
169
static gint channel_mask[] = { 1, 1, 1 };
170
static gint peak_max = 1;
171
static gint peak_min = 1;
172
static gint peak_includes_equals = 1;
173
static guchar fore[3];
174
static GtkWidget *preview;
178
gint applicable_image_type;
180
void (*initializer) (GimpImageBaseType, gint, guchar *, guchar *, gpointer *);
181
void (*updater) (GimpImageBaseType, gint, guchar *, guchar *, guchar *, gpointer);
182
void (*finalizer) (GimpImageBaseType, gint, guchar *, guchar *, guchar *, gpointer);
186
static ModeParam modes[num_mode] =
188
{ VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA,
189
N_("More _white (larger value)"),
190
initialize_white, propagate_white, set_value },
191
{ VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA,
192
N_("More blac_k (smaller value)"),
193
initialize_black, propagate_black, set_value },
194
{ VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA,
195
N_("_Middle value to peaks"),
196
initialize_middle, propagate_middle, set_middle_to_peak },
197
{ VP_RGB | VP_GRAY | VP_WITH_ALPHA | VP_WO_ALPHA,
198
N_("_Foreground to peaks"),
199
initialize_middle, propagate_middle, set_foreground_to_peak },
200
{ VP_RGB | VP_WITH_ALPHA | VP_WO_ALPHA,
201
N_("O_nly foreground"),
202
initialize_foreground, propagate_a_color, set_value },
203
{ VP_RGB | VP_WITH_ALPHA | VP_WO_ALPHA,
204
N_("Only b_ackground"),
205
initialize_background, propagate_a_color, set_value },
206
{ VP_RGB | VP_GRAY | VP_WITH_ALPHA,
208
NULL, propagate_opaque, set_value },
209
{ VP_RGB | VP_GRAY | VP_WITH_ALPHA,
210
N_("More t_ransparent"),
211
NULL, propagate_transparent, set_value },
219
static const GimpParamDef args[] =
221
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
222
{ GIMP_PDB_IMAGE, "image", "Input image (not used)" },
223
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
224
{ 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" },
225
{ GIMP_PDB_INT32, "propagating-channel", "channels which values are propagated" },
226
{ GIMP_PDB_FLOAT, "propagating-rate", "0.0 <= propagatating_rate <= 1.0" },
227
{ GIMP_PDB_INT32, "direction-mask", "0 <= direction-mask <= 15" },
228
{ GIMP_PDB_INT32, "lower-limit", "0 <= lower-limit <= 255" },
229
{ GIMP_PDB_INT32, "upper-limit", "0 <= upper-limit <= 255" }
232
gimp_install_procedure (VPROPAGATE_PROC,
233
N_("Propagate certain colors to neighboring pixels"),
234
"Propagate values of the layer",
235
"Shuji Narazaki (narazaki@InetQ.or.jp)",
238
N_("_Value Propagate..."),
241
G_N_ELEMENTS (args), 0,
244
gimp_install_procedure (ERODE_PROC,
245
N_("Shrink lighter areas of the image"),
247
"Shuji Narazaki (narazaki@InetQ.or.jp)",
253
G_N_ELEMENTS (args), 0,
256
gimp_install_procedure (DILATE_PROC,
257
N_("Grow lighter areas of the image"),
259
"Shuji Narazaki (narazaki@InetQ.or.jp)",
265
G_N_ELEMENTS (args), 0,
268
gimp_plugin_menu_register (VPROPAGATE_PROC, "<Image>/Filters/Distorts");
269
gimp_plugin_menu_register (ERODE_PROC, "<Image>/Filters/Generic");
270
gimp_plugin_menu_register (DILATE_PROC, "<Image>/Filters/Generic");
274
run (const gchar *name,
276
const GimpParam *param,
278
GimpParam **return_vals)
280
static GimpParam values[1];
281
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
282
GimpRunMode run_mode;
283
GimpDrawable *drawable;
285
run_mode = param[0].data.d_int32;
286
drawable = gimp_drawable_get (param[2].data.d_int32);
291
*return_vals = values;
293
values[0].type = GIMP_PDB_STATUS;
294
values[0].data.d_status = status;
298
case GIMP_RUN_INTERACTIVE:
299
if (strcmp (name, VPROPAGATE_PROC) == 0)
301
gimp_get_data (VPROPAGATE_PROC, &vpvals);
302
/* building the values of dialog variables from vpvals. */
304
(vpvals.propagating_channel & PROPAGATING_ALPHA) ? TRUE : FALSE;
306
(vpvals.propagating_channel & PROPAGATING_VALUE) ? TRUE : FALSE;
310
for (i = 0; i < 4; i++)
311
direction_mask_vec[i] =
312
(vpvals.direction_mask & (1 << i)) ? TRUE : FALSE;
315
if (! vpropagate_dialog (drawable))
318
else if (strcmp (name, ERODE_PROC) == 0 ||
319
strcmp (name, DILATE_PROC) == 0)
321
vpvals.propagating_channel = PROPAGATING_VALUE;
322
vpvals.propagating_rate = 1.0;
323
vpvals.direction_mask = 15;
324
vpvals.lower_limit = 0;
325
vpvals.upper_limit = 255;
327
if (strcmp (name, ERODE_PROC) == 0)
328
vpvals.propagate_mode = 1;
329
else if (strcmp (name, DILATE_PROC) == 0)
330
vpvals.propagate_mode = 0;
334
case GIMP_RUN_NONINTERACTIVE:
335
if (strcmp (name, VPROPAGATE_PROC) == 0)
337
vpvals.propagate_mode = param[3].data.d_int32;
338
vpvals.propagating_channel = param[4].data.d_int32;
339
vpvals.propagating_rate = param[5].data.d_float;
340
vpvals.direction_mask = param[6].data.d_int32;
341
vpvals.lower_limit = param[7].data.d_int32;
342
vpvals.upper_limit = param[8].data.d_int32;
344
else if (strcmp (name, ERODE_PROC) == 0 ||
345
strcmp (name, DILATE_PROC) == 0)
347
vpvals.propagating_channel = PROPAGATING_VALUE;
348
vpvals.propagating_rate = 1.0;
349
vpvals.direction_mask = 15;
350
vpvals.lower_limit = 0;
351
vpvals.upper_limit = 255;
353
if (strcmp (name, ERODE_PROC) == 0)
354
vpvals.propagate_mode = 1;
355
else if (strcmp (name, DILATE_PROC) == 0)
356
vpvals.propagate_mode = 0;
360
case GIMP_RUN_WITH_LAST_VALS:
361
gimp_get_data (name, &vpvals);
365
status = value_propagate (drawable);
367
if (status == GIMP_PDB_SUCCESS)
369
if (run_mode != GIMP_RUN_NONINTERACTIVE)
370
gimp_displays_flush ();
372
if (run_mode == GIMP_RUN_INTERACTIVE)
373
gimp_set_data (name, &vpvals, sizeof (VPValueType));
376
values[0].type = GIMP_PDB_STATUS;
377
values[0].data.d_status = status;
380
/* registered function entry */
381
static GimpPDBStatusType
382
value_propagate (GimpDrawable *drawable)
384
/* check the validness of parameters */
385
if (!(vpvals.propagating_channel & (PROPAGATING_VALUE | PROPAGATING_ALPHA)))
387
/* gimp_message ("No channel selected."); */
388
return GIMP_PDB_EXECUTION_ERROR;
390
if (vpvals.direction_mask == 0)
392
/* gimp_message ("No direction selected."); */
393
return GIMP_PDB_EXECUTION_ERROR;
395
if ((vpvals.lower_limit < 0) || (255 < vpvals.lower_limit) ||
396
(vpvals.upper_limit < 0) || (255 < vpvals.lower_limit) ||
397
(vpvals.upper_limit < vpvals.lower_limit))
399
/* gimp_message ("Limit values are not valid."); */
400
return GIMP_PDB_EXECUTION_ERROR;
402
value_propagate_body (drawable, NULL);
403
return GIMP_PDB_SUCCESS;
407
value_propagate_body (GimpDrawable *drawable,
408
GimpPreview *preview)
410
GimpImageBaseType dtype;
412
GimpPixelRgn srcRgn, destRgn;
413
guchar *here, *best, *dest;
414
guchar *dest_row, *prev_row, *cur_row, *next_row;
415
guchar *pr, *cr, *nr, *swap;
416
gint width, height, bytes, index;
417
gint begx, begy, endx, endy, x, y, dx;
418
gint left_index, right_index, up_index, down_index;
422
/* calculate neighbors' indexes */
423
left_index = (vpvals.direction_mask & (1 << Left2Right)) ? -1 : 0;
424
right_index = (vpvals.direction_mask & (1 << Right2Left)) ? 1 : 0;
425
up_index = (vpvals.direction_mask & (1 << Top2Bottom)) ? -1 : 0;
426
down_index = (vpvals.direction_mask & (1 << Bottom2Top)) ? 1 : 0;
427
operation = modes[vpvals.propagate_mode];
430
dtype = gimp_drawable_type (drawable->drawable_id);
431
bytes = drawable->bpp;
433
/* Here I use the algorithm of blur.c . */
436
gimp_preview_get_position (preview, &begx, &begy);
437
gimp_preview_get_size (preview, &width, &height);
439
endy = begy + height;
443
gimp_drawable_mask_bounds (drawable->drawable_id,
444
&begx, &begy, &endx, &endy);
446
height = endy - begy;
449
gimp_tile_cache_ntiles (2 * ((width) / gimp_tile_width () + 1));
451
prev_row = g_new (guchar, (width + 2) * bytes);
452
cur_row = g_new (guchar, (width + 2) * bytes);
453
next_row = g_new (guchar, (width + 2) * bytes);
454
dest_row = g_new (guchar, width * bytes);
456
gimp_pixel_rgn_init (&srcRgn, drawable,
457
begx, begy, width, height,
459
gimp_pixel_rgn_init (&destRgn, drawable,
460
begx, begy, width, height,
461
(preview == NULL), TRUE);
463
pr = prev_row + bytes;
464
cr = cur_row + bytes;
465
nr = next_row + bytes;
467
prepare_row (&srcRgn, pr, begx, (0 < begy) ? begy : begy - 1, endx-begx);
468
prepare_row (&srcRgn, cr, begx, begy, endx-begx);
470
best = g_new (guchar, bytes);
473
gimp_progress_init (_("Value Propagate"));
475
gimp_context_get_foreground (&foreground);
476
gimp_rgb_get_uchar (&foreground, fore+0, fore+1, fore+2);
479
for (y = begy ; y < endy ; y++)
481
prepare_row (&srcRgn, nr, begx, ((y+1) < endy) ? y+1 : endy, endx-begx);
483
for (index = 0; index < (endx - begx) * bytes; index++)
484
dest_row[index] = cr[index];
486
for (x = 0 ; x < endx - begx; x++)
488
dest = dest_row + (x * bytes);
489
here = cr + (x * bytes);
491
/* *** copy source value to best value holder *** */
492
memcpy (best, here, bytes);
494
if (operation.initializer)
495
(* operation.initializer)(dtype, bytes, best, here, &tmp);
497
/* *** gather neighbors' values: loop-unfolded version *** */
499
for (dx = left_index ; dx <= right_index ; dx++)
500
(* operation.updater)(dtype, bytes, here, pr+((x+dx)*bytes), best, tmp);
501
for (dx = left_index ; dx <= right_index ; dx++)
503
(* operation.updater)(dtype, bytes, here, cr+((x+dx)*bytes), best, tmp);
505
for (dx = left_index ; dx <= right_index ; dx++)
506
(* operation.updater)(dtype, bytes, here, nr+((x+dx)*bytes), best, tmp);
507
/* *** store it to dest_row*** */
508
(* operation.finalizer)(dtype, bytes, best, here, dest, tmp);
511
/* now store destline to destRgn */
512
gimp_pixel_rgn_set_row (&destRgn, dest_row, begx, y, endx - begx);
514
/* shift the row pointers */
521
if (((y % 16) == 0) && !preview)
522
gimp_progress_update ((gdouble) y / (gdouble) (endy - begy));
527
gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
532
/* update the region */
533
gimp_progress_update (1.0);
534
gimp_drawable_flush (drawable);
535
gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
536
gimp_drawable_update (drawable->drawable_id, begx, begy, endx-begx, endy-begy);
541
prepare_row (GimpPixelRgn *pixel_rgn,
550
gimp_pixel_rgn_get_row (pixel_rgn, data, x, (y + 1), w);
551
else if (y >= pixel_rgn->h)
552
gimp_pixel_rgn_get_row (pixel_rgn, data, x, (y - 1), w);
554
gimp_pixel_rgn_get_row (pixel_rgn, data, x, y, w);
556
/* Fill in edge pixels */
557
for (b = 0; b < pixel_rgn->bpp; b++)
559
data[-(gint)pixel_rgn->bpp + b] = data[b];
560
data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b];
565
set_value (GimpImageBaseType dtype,
581
case GIMP_RGBA_IMAGE:
585
case GIMP_GRAY_IMAGE:
588
case GIMP_GRAYA_IMAGE:
595
for (ch = 0; ch < value_chs; ch++)
597
if (vpvals.propagating_channel & PROPAGATING_VALUE) /* value channel */
598
*dest++ = (guchar)(vpvals.propagating_rate * best[ch]
599
+ (1.0 - vpvals.propagating_rate) * here[ch]);
605
if (vpvals.propagating_channel & PROPAGATING_ALPHA) /* alpha channel */
606
*dest++ = (guchar)(vpvals.propagating_rate * best[alpha]
607
+ (1.0 - vpvals.propagating_rate) * here[alpha]);
609
*dest++ = here[alpha];
614
value_difference_check (guchar *pos1,
621
for (index = 0 ; index < ch; index++)
622
if (channel_mask[index] != 0)
624
diff = abs(pos1[index] - pos2[index]);
625
if (! ((vpvals.lower_limit <= diff) && (diff <= vpvals.upper_limit)))
631
/* mothods for each mode */
633
initialize_white (GimpImageBaseType dtype,
643
case GIMP_RGBA_IMAGE:
645
*tmp = (void *) g_new (gfloat, 1);
646
**(float **)tmp = channel_mask[0] * here[0] * here[0]
647
+ channel_mask[1] * here[1] * here[1]
648
+ channel_mask[2] * here[2] * here[2];
650
case GIMP_GRAYA_IMAGE:
651
case GIMP_GRAY_IMAGE:
659
propagate_white (GimpImageBaseType dtype,
671
case GIMP_RGBA_IMAGE:
672
v_here = channel_mask[0] * here[0] * here[0]
673
+ channel_mask[1] * here[1] * here[1]
674
+ channel_mask[2] * here[2] * here[2];
675
if (*(float *)tmp < v_here && value_difference_check(orig, here, 3))
677
*(float *)tmp = v_here;
678
memcpy(best, here, 3 * sizeof(guchar)); /* alpha channel holds old value */
681
case GIMP_GRAYA_IMAGE:
682
case GIMP_GRAY_IMAGE:
683
if (*best < *here && value_difference_check(orig, here, 1))
692
initialize_black (GimpImageBaseType dtype,
701
case GIMP_RGBA_IMAGE:
703
*tmp = (void *) g_new (gfloat, 1);
704
**(float **)tmp = (channel_mask[0] * here[0] * here[0]
705
+ channel_mask[1] * here[1] * here[1]
706
+ channel_mask[2] * here[2] * here[2]);
708
case GIMP_GRAYA_IMAGE:
709
case GIMP_GRAY_IMAGE:
717
propagate_black (GimpImageBaseType image_type,
729
case GIMP_RGBA_IMAGE:
730
v_here = (channel_mask[0] * here[0] * here[0]
731
+ channel_mask[1] * here[1] * here[1]
732
+ channel_mask[2] * here[2] * here[2]);
733
if (v_here < *(float *)tmp && value_difference_check(orig, here, 3))
735
*(float *)tmp = v_here;
736
memcpy (best, here, 3 * sizeof(guchar)); /* alpha channel holds old value */
739
case GIMP_GRAYA_IMAGE:
740
case GIMP_GRAY_IMAGE:
741
if (*here < *best && value_difference_check(orig, here, 1))
753
glong original_value;
761
initialize_middle (GimpImageBaseType image_type,
771
*tmp = (void *) g_new (MiddlePacket, 1);
772
data = (MiddlePacket *)*tmp;
773
for (index = 0; index < channels; index++)
774
data->min[index] = data->max[index] = here[index];
778
case GIMP_RGBA_IMAGE:
779
data->original_value = (channel_mask[0] * here[0] * here[0]
780
+ channel_mask[1] * here[1] * here[1]
781
+ channel_mask[2] * here[2] * here[2]);
783
case GIMP_GRAYA_IMAGE:
784
case GIMP_GRAY_IMAGE:
785
data->original_value = *here;
790
data->minv = data->maxv = data->original_value;
791
data->min_modified = data->max_modified = 0;
795
propagate_middle (GimpImageBaseType image_type,
805
data = (MiddlePacket *)tmp;
810
case GIMP_RGBA_IMAGE:
811
v_here = (channel_mask[0] * here[0] * here[0]
812
+ channel_mask[1] * here[1] * here[1]
813
+ channel_mask[2] * here[2] * here[2]);
814
if ((v_here <= data->minv) && value_difference_check(orig, here, 3))
817
memcpy (data->min, here, 3*sizeof(guchar));
818
data->min_modified = 1;
820
if (data->maxv <= v_here && value_difference_check(orig, here, 3))
823
memcpy (data->max, here, 3*sizeof(guchar));
824
data->max_modified = 1;
827
case GIMP_GRAYA_IMAGE:
828
case GIMP_GRAY_IMAGE:
829
if ((*here <= data->min[0]) && value_difference_check(orig, here, 1))
831
data->min[0] = *here;
832
data->min_modified = 1;
834
if ((data->max[0] <= *here) && value_difference_check(orig, here, 1))
836
data->max[0] = *here;
837
data->max_modified = 1;
846
set_middle_to_peak (GimpImageBaseType image_type,
858
data = (MiddlePacket *)tmp;
859
if (! ((peak_min & (data->minv == data->original_value))
860
|| (peak_max & (data->maxv == data->original_value))))
862
if ((! peak_includes_equals)
863
&& ((peak_min & (! data->min_modified))
864
|| (peak_max & (! data->max_modified))))
872
case GIMP_RGBA_IMAGE:
876
case GIMP_GRAY_IMAGE:
879
case GIMP_GRAYA_IMAGE:
886
for (ch = 0; ch < value_chs; ch++)
888
if (vpvals.propagating_channel & PROPAGATING_VALUE) /* value channel */
889
*dest++ = (guchar)(vpvals.propagating_rate * 0.5 * (data->min[ch] + data->max[ch])
890
+ (1.0 - vpvals.propagating_rate) * here[ch]);
896
if (vpvals.propagating_channel & PROPAGATING_ALPHA) /* alpha channel */
897
*dest++ = (guchar)(vpvals.propagating_rate * best[alpha]
898
+ (1.0 - vpvals.propagating_rate) * here[alpha]);
900
*dest++ = here[alpha];
905
set_foreground_to_peak (GimpImageBaseType image_type,
917
data = (MiddlePacket *)tmp;
918
if (! ((peak_min & (data->minv == data->original_value))
919
|| (peak_max & (data->maxv == data->original_value))))
921
if (peak_includes_equals
922
&& ((peak_min & (! data->min_modified))
923
|| (peak_max & (! data->max_modified))))
931
case GIMP_RGBA_IMAGE:
935
case GIMP_GRAY_IMAGE:
938
case GIMP_GRAYA_IMAGE:
945
for (ch = 0; ch < value_chs; ch++)
947
if (vpvals.propagating_channel & PROPAGATING_VALUE) /* value channel */
948
*dest++ = (guchar)(vpvals.propagating_rate*fore[ch]
949
+ (1.0 - vpvals.propagating_rate)*here[ch]);
956
initialize_foreground (GimpImageBaseType image_type,
967
*tmp = (void *) g_new (guchar, 3);
969
gimp_context_get_foreground (&foreground);
970
gimp_rgb_get_uchar (&foreground, &ch[0], &ch[1], &ch[2]);
975
initialize_background (GimpImageBaseType image_type,
986
*tmp = (void *) g_new (guchar, 3);
988
gimp_context_get_background (&background);
989
gimp_rgb_get_uchar (&background, &ch[0], &ch[1], &ch[2]);
994
propagate_a_color (GimpImageBaseType image_type,
1001
guchar *fg = (guchar *)tmp;
1005
case GIMP_RGB_IMAGE:
1006
case GIMP_RGBA_IMAGE:
1007
if (here[0] == fg[0] && here[1] == fg[1] && here[2] == fg[2] &&
1008
value_difference_check(orig, here, 3))
1010
memcpy (best, here, 3 * sizeof(guchar)); /* alpha channel holds old value */
1013
case GIMP_GRAYA_IMAGE:
1014
case GIMP_GRAY_IMAGE:
1022
propagate_opaque (GimpImageBaseType image_type,
1031
case GIMP_RGBA_IMAGE:
1032
if (best[3] < here[3] && value_difference_check(orig, here, 3))
1033
memcpy(best, here, channels * sizeof(guchar));
1035
case GIMP_GRAYA_IMAGE:
1036
if (best[1] < here[1] && value_difference_check(orig, here, 1))
1037
memcpy(best, here, channels * sizeof(guchar));
1045
propagate_transparent (GimpImageBaseType image_type,
1054
case GIMP_RGBA_IMAGE:
1055
if (here[3] < best[3] && value_difference_check(orig, here, 3))
1056
memcpy(best, here, channels * sizeof(guchar));
1058
case GIMP_GRAYA_IMAGE:
1059
if (here[1] < best[1] && value_difference_check(orig, here, 1))
1060
memcpy(best, here, channels * sizeof(guchar));
1070
vpropagate_dialog (GimpDrawable *drawable)
1073
GtkWidget *main_vbox;
1077
GtkWidget *toggle_vbox;
1080
GSList *group = NULL;
1084
gimp_ui_init (PLUG_IN_BINARY, FALSE);
1086
dialog = gimp_dialog_new (_("Value Propagate"), PLUG_IN_BINARY,
1088
gimp_standard_help_func, VPROPAGATE_PROC,
1090
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1091
GTK_STOCK_OK, GTK_RESPONSE_OK,
1095
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1097
GTK_RESPONSE_CANCEL,
1100
gimp_window_set_transient (GTK_WINDOW (dialog));
1102
main_vbox = gtk_vbox_new (FALSE, 12);
1103
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
1104
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
1105
gtk_widget_show (main_vbox);
1107
preview = gimp_drawable_preview_new (drawable, NULL);
1108
gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
1109
gtk_widget_show (preview);
1111
g_signal_connect_swapped (preview, "invalidated",
1112
G_CALLBACK (value_propagate_body),
1115
hbox = gtk_hbox_new (FALSE, 12);
1116
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
1117
gtk_widget_show (hbox);
1119
/* Propagate Mode */
1120
frame = gimp_frame_new (_("Mode"));
1121
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
1122
gtk_widget_show (frame);
1124
toggle_vbox = gtk_vbox_new (FALSE, 2);
1125
gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);
1126
gtk_widget_show (toggle_vbox);
1128
for (index = 0; index < num_mode; index++)
1131
gtk_radio_button_new_with_mnemonic (group,
1132
gettext (modes[index].name));
1133
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
1134
gtk_box_pack_start (GTK_BOX (toggle_vbox), button, FALSE, FALSE, 0);
1135
gtk_widget_show (button);
1137
g_object_set_data (G_OBJECT (button), "gimp-item-data",
1138
GINT_TO_POINTER (index));
1140
g_signal_connect (button, "toggled",
1141
G_CALLBACK (gimp_radio_button_update),
1142
&vpvals.propagate_mode);
1143
g_signal_connect_swapped (button, "toggled",
1144
G_CALLBACK (gimp_preview_invalidate),
1147
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1148
index == vpvals.propagate_mode);
1151
/* Parameter settings */
1152
frame = gimp_frame_new (_("Propagate"));
1153
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
1154
gtk_widget_show (frame);
1156
table = gtk_table_new (8, 3, FALSE); /* 4 raw, 2 columns(name and value) */
1157
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1158
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1159
gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12);
1160
gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12);
1161
gtk_container_add (GTK_CONTAINER (frame), table);
1162
gtk_widget_show (table);
1164
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
1165
_("Lower t_hreshold:"), SCALE_WIDTH, 4,
1166
vpvals.lower_limit, 0, 255, 1, 8, 0,
1169
g_signal_connect (adj, "value-changed",
1170
G_CALLBACK (gimp_int_adjustment_update),
1171
&vpvals.lower_limit);
1172
g_signal_connect_swapped (adj, "value-changed",
1173
G_CALLBACK (gimp_preview_invalidate),
1176
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
1177
_("_Upper threshold:"), SCALE_WIDTH, 4,
1178
vpvals.upper_limit, 0, 255, 1, 8, 0,
1181
g_signal_connect (adj, "value-changed",
1182
G_CALLBACK (gimp_int_adjustment_update),
1183
&vpvals.upper_limit);
1184
g_signal_connect_swapped (adj, "value-changed",
1185
G_CALLBACK (gimp_preview_invalidate),
1188
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
1189
_("_Propagating rate:"), SCALE_WIDTH, 4,
1190
vpvals.propagating_rate, 0, 1, 0.01, 0.1, 2,
1193
g_signal_connect (adj, "value-changed",
1194
G_CALLBACK (gimp_double_adjustment_update),
1195
&vpvals.propagating_rate);
1196
g_signal_connect_swapped (adj, "value-changed",
1197
G_CALLBACK (gimp_preview_invalidate),
1200
gtk_table_add_toggle (table, _("To l_eft"), 0, 1, 4,
1201
G_CALLBACK (vpropagate_toggle_button_update),
1202
&direction_mask_vec[Right2Left]);
1203
gtk_table_add_toggle (table, _("To _right"), 2, 3, 4,
1204
G_CALLBACK (vpropagate_toggle_button_update),
1205
&direction_mask_vec[Left2Right]);
1206
gtk_table_add_toggle (table, _("To _top"), 1, 2, 3,
1207
G_CALLBACK (vpropagate_toggle_button_update),
1208
&direction_mask_vec[Bottom2Top]);
1209
gtk_table_add_toggle (table, _("To _bottom"), 1, 2, 5,
1210
G_CALLBACK (vpropagate_toggle_button_update),
1211
&direction_mask_vec[Top2Bottom]);
1213
if (gimp_drawable_has_alpha (drawable->drawable_id))
1218
gtk_table_add_toggle (table, _("Propagating _alpha channel"),
1220
G_CALLBACK (vpropagate_toggle_button_update),
1223
if (gimp_layer_get_lock_alpha (drawable->drawable_id))
1225
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), 0);
1226
gtk_widget_set_sensitive (toggle, FALSE);
1229
gtk_table_add_toggle (table, _("Propagating value channel"), 0, 3, 7,
1230
G_CALLBACK (vpropagate_toggle_button_update),
1234
gtk_widget_show (dialog);
1236
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
1242
for (i = result = 0; i < 4; i++)
1243
result |= (direction_mask_vec[i] ? 1 : 0) << i;
1244
vpvals.direction_mask = result;
1246
vpvals.propagating_channel = ((propagate_alpha ? PROPAGATING_ALPHA : 0) |
1247
(propagate_value ? PROPAGATING_VALUE : 0));
1250
gtk_widget_destroy (dialog);
1256
vpropagate_toggle_button_update (GtkWidget *widget,
1261
gimp_toggle_button_update (widget, data);
1263
for (i = result = 0; i < 4; i++)
1264
result |= (direction_mask_vec[i] ? 1 : 0) << i;
1265
vpvals.direction_mask = result;
1267
vpvals.propagating_channel = ((propagate_alpha ? PROPAGATING_ALPHA : 0) |
1268
(propagate_value ? PROPAGATING_VALUE : 0));
1269
gimp_preview_invalidate (GIMP_PREVIEW (preview));
1273
gtk_table_add_toggle (GtkWidget *table,
1283
toggle = gtk_check_button_new_with_mnemonic(name);
1284
gtk_table_attach (GTK_TABLE (table), toggle, x1, x2, y, y+1,
1285
GTK_FILL|GTK_EXPAND, 0, 0, 0);
1286
gtk_widget_show (toggle);
1288
g_signal_connect (toggle, "toggled",
1289
G_CALLBACK (update),
1292
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), *value);