81
82
#include "gimp-intl.h"
84
/* Other defines... */
85
86
#define MAX_GRADIENT 179.606 /* == sqrt (127^2 + 127^2) */
86
87
#define GRADIENT_SEARCH 32 /* how far to look when snapping to an edge */
87
#define TARGET_HEIGHT 25
88
#define TARGET_WIDTH 25
89
#define POINT_WIDTH 9 /* size (in pixels) of seed handles */
90
#define POINT_HALFWIDTH (POINT_WIDTH / 2)
88
#define TARGET_SIZE 25
89
#define POINT_WIDTH 12 /* size (in pixels) of seed handles */
91
90
#define EXTEND_BY 0.2 /* proportion to expand cost map by */
92
91
#define FIXED 5 /* additional fixed size to expand cost map */
93
92
#define MIN_GRADIENT 63 /* gradients < this are directionless */
95
#define MAX_POINTS 2048
97
#define COST_WIDTH 2 /* number of bytes for each pixel in cost map */
98
#define BLOCK_WIDTH 64
99
#define BLOCK_HEIGHT 64
100
#define CONV_WIDTH (BLOCK_WIDTH + 2)
101
#define CONV_HEIGHT (BLOCK_HEIGHT + 2)
94
#define COST_WIDTH 2 /* number of bytes for each pixel in cost map */
103
96
/* weight to give between gradient (_G) and direction (_D) */
104
97
#define OMEGA_D 0.2
123
116
/* local function prototypes */
125
static void gimp_iscissors_tool_class_init (GimpIscissorsToolClass *klass);
126
static void gimp_iscissors_tool_init (GimpIscissorsTool *iscissors);
128
static void gimp_iscissors_tool_finalize (GObject *object);
130
static void gimp_iscissors_tool_control (GimpTool *tool,
131
GimpToolAction action,
133
static void gimp_iscissors_tool_button_press (GimpTool *tool,
136
GdkModifierType state,
138
static void gimp_iscissors_tool_button_release (GimpTool *tool,
141
GdkModifierType state,
143
static void gimp_iscissors_tool_motion (GimpTool *tool,
146
GdkModifierType state,
148
static void gimp_iscissors_tool_oper_update (GimpTool *tool,
150
GdkModifierType state,
152
static void gimp_iscissors_tool_cursor_update (GimpTool *tool,
154
GdkModifierType state,
157
static void gimp_iscissors_tool_reset (GimpIscissorsTool *iscissors);
158
static void gimp_iscissors_tool_draw (GimpDrawTool *draw_tool);
118
static void gimp_iscissors_tool_finalize (GObject *object);
120
static void gimp_iscissors_tool_control (GimpTool *tool,
121
GimpToolAction action,
122
GimpDisplay *display);
123
static void gimp_iscissors_tool_button_press (GimpTool *tool,
126
GdkModifierType state,
127
GimpDisplay *display);
128
static void gimp_iscissors_tool_button_release (GimpTool *tool,
131
GdkModifierType state,
132
GimpButtonReleaseType release_type,
133
GimpDisplay *display);
134
static void gimp_iscissors_tool_motion (GimpTool *tool,
137
GdkModifierType state,
138
GimpDisplay *display);
139
static void gimp_iscissors_tool_oper_update (GimpTool *tool,
141
GdkModifierType state,
143
GimpDisplay *display);
144
static void gimp_iscissors_tool_cursor_update (GimpTool *tool,
146
GdkModifierType state,
147
GimpDisplay *display);
148
static gboolean gimp_iscissors_tool_key_press (GimpTool *tool,
150
GimpDisplay *display);
152
static void gimp_iscissors_tool_apply (GimpIscissorsTool *iscissors,
153
GimpDisplay *display);
154
static void gimp_iscissors_tool_reset (GimpIscissorsTool *iscissors);
155
static void gimp_iscissors_tool_draw (GimpDrawTool *draw_tool);
161
158
static void iscissors_convert (GimpIscissorsTool *iscissors,
163
static TileManager * gradient_map_new (GimpImage *gimage);
159
GimpDisplay *display);
160
static TileManager * gradient_map_new (GimpImage *image);
165
162
static void find_optimal_path (TileManager *gradient_map,
173
170
static void find_max_gradient (GimpIscissorsTool *iscissors,
177
174
static void calculate_curve (GimpTool *tool,
179
176
static void iscissors_draw_curve (GimpDrawTool *draw_tool,
181
178
static void iscissors_free_icurves (GSList *list);
182
static void iscissors_free_buffers (GimpIscissorsTool *iscissors);
184
180
static gint mouse_over_vertex (GimpIscissorsTool *iscissors,
187
static gboolean clicked_on_vertex (GimpTool *tool);
183
static gboolean clicked_on_vertex (GimpIscissorsTool *iscissors,
188
186
static GSList * mouse_over_curve (GimpIscissorsTool *iscissors,
191
static gboolean clicked_on_curve (GimpTool *tool);
189
static gboolean clicked_on_curve (GimpIscissorsTool *iscissors,
193
static void precalculate_arrays (void);
194
193
static GPtrArray * plot_pixels (GimpIscissorsTool *iscissors,
203
G_DEFINE_TYPE (GimpIscissorsTool, gimp_iscissors_tool,
204
GIMP_TYPE_SELECTION_TOOL)
206
#define parent_class gimp_iscissors_tool_parent_class
204
209
/* static variables */
206
211
/* where to move on a given link direction */
207
static gint move[8][2] =
212
static const gint move[8][2] =
233
238
static guchar maxgrad_conv2[TILE_WIDTH * TILE_HEIGHT * 4] = "";
236
static gfloat horz_deriv[9] =
241
static const gfloat horz_deriv[9] =
243
static gfloat vert_deriv[9] =
248
static const gfloat vert_deriv[9] =
250
static gfloat blur_32[9] =
255
static const gfloat blur_32[9] =
257
static gfloat distance_weights[GRADIENT_SEARCH * GRADIENT_SEARCH];
259
static gint diagonal_weight[256];
260
static gint direction_value[256][4];
261
static gboolean initialized = FALSE;
262
static Tile *cur_tile = NULL;
265
static GimpSelectionToolClass *parent_class = NULL;
262
static gfloat distance_weights[GRADIENT_SEARCH * GRADIENT_SEARCH];
264
static gint diagonal_weight[256];
265
static gint direction_value[256][4];
266
static Tile *cur_tile = NULL;
287
gimp_iscissors_tool_get_type (void)
289
static GType tool_type = 0;
293
static const GTypeInfo tool_info =
295
sizeof (GimpIscissorsToolClass),
296
(GBaseInitFunc) NULL,
297
(GBaseFinalizeFunc) NULL,
298
(GClassInitFunc) gimp_iscissors_tool_class_init,
299
NULL, /* class_finalize */
300
NULL, /* class_data */
301
sizeof (GimpIscissorsTool),
303
(GInstanceInitFunc) gimp_iscissors_tool_init,
306
tool_type = g_type_register_static (GIMP_TYPE_SELECTION_TOOL,
315
288
gimp_iscissors_tool_class_init (GimpIscissorsToolClass *klass)
317
290
GObjectClass *object_class = G_OBJECT_CLASS (klass);
318
291
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
319
292
GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
321
parent_class = g_type_class_peek_parent (klass);
323
295
object_class->finalize = gimp_iscissors_tool_finalize;
328
300
tool_class->motion = gimp_iscissors_tool_motion;
329
301
tool_class->oper_update = gimp_iscissors_tool_oper_update;
330
302
tool_class->cursor_update = gimp_iscissors_tool_cursor_update;
303
tool_class->key_press = gimp_iscissors_tool_key_press;
332
305
draw_tool_class->draw = gimp_iscissors_tool_draw;
307
for (i = 0; i < 256; i++)
309
/* The diagonal weight array */
310
diagonal_weight[i] = (int) (i * G_SQRT2);
312
/* The direction value array */
313
direction_value[i][0] = (127 - abs (127 - i)) * 2;
314
direction_value[i][1] = abs (127 - i) * 2;
315
direction_value[i][2] = abs (191 - i) * 2;
316
direction_value[i][3] = abs (63 - i) * 2;
319
/* set the 256th index of the direction_values to the hightest cost */
320
direction_value[255][0] = 255;
321
direction_value[255][1] = 255;
322
direction_value[255][2] = 255;
323
direction_value[255][3] = 255;
414
400
GimpCoords *coords,
416
402
GdkModifierType state,
403
GimpDisplay *display)
419
405
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
420
GimpSelectionOptions *options;
422
options = GIMP_SELECTION_OPTIONS (tool->tool_info->tool_options);
424
iscissors->x = coords->x;
425
iscissors->y = coords->y;
406
GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
408
iscissors->x = RINT (coords->x);
409
iscissors->y = RINT (coords->y);
427
411
/* If the tool was being used in another image...reset it */
429
413
if (gimp_tool_control_is_active (tool->control))
431
if (gdisp != tool->gdisp)
415
if (display != tool->display)
433
417
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
434
418
gimp_iscissors_tool_reset (iscissors);
449
433
iscissors->draw = DRAW_CURRENT_SEED;
451
435
if (! (state & GDK_SHIFT_MASK))
452
find_max_gradient (iscissors,
436
find_max_gradient (iscissors,
457
iscissors->x = CLAMP (iscissors->x, 0, gdisp->gimage->width - 1);
458
iscissors->y = CLAMP (iscissors->y, 0, gdisp->gimage->height - 1);
441
iscissors->x = CLAMP (iscissors->x, 0, display->image->width - 1);
442
iscissors->y = CLAMP (iscissors->y, 0, display->image->height - 1);
460
444
iscissors->ix = iscissors->x;
461
445
iscissors->iy = iscissors->y;
463
447
/* Initialize the selection core only on starting the tool */
464
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), gdisp);
448
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
468
/* Check if the mouse click occured on a vertex or the curve itself */
469
if (clicked_on_vertex (tool))
471
iscissors->nx = iscissors->x;
472
iscissors->ny = iscissors->y;
473
iscissors->state = SEED_ADJUSTMENT;
452
/* Check if the mouse click occurred on a vertex or the curve itself */
453
if (clicked_on_vertex (iscissors, coords->x, coords->y))
455
iscissors->nx = iscissors->x;
456
iscissors->ny = iscissors->y;
457
iscissors->state = SEED_ADJUSTMENT;
475
iscissors->draw = DRAW_CURVE | DRAW_ACTIVE_CURVE;
476
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
459
iscissors->draw = DRAW_CURVE | DRAW_ACTIVE_CURVE;
460
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
478
462
/* If the iscissors is connected, check if the click was inside */
479
463
else if (iscissors->connected && iscissors->mask &&
480
gimp_channel_value (iscissors->mask,
484
/* Undraw the curve */
485
gimp_tool_control_halt (tool->control);
487
iscissors->draw = DRAW_CURVE;
488
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
490
gimp_channel_select_channel (gimp_image_get_mask (gdisp->gimage),
491
tool->tool_info->blurb,
494
GIMP_SELECTION_TOOL (tool)->op,
496
options->feather_radius,
497
options->feather_radius);
499
gimp_iscissors_tool_reset (iscissors);
501
gimp_image_flush (gdisp->gimage);
464
gimp_pickable_get_opacity_at (GIMP_PICKABLE (iscissors->mask),
468
gimp_iscissors_tool_apply (iscissors, display);
503
470
else if (! iscissors->connected)
505
472
/* if we're not connected, we're adding a new point */
507
474
/* pause the tool, but undraw nothing */
508
475
iscissors->draw = DRAW_NOTHING;
509
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
476
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
511
iscissors->state = SEED_PLACEMENT;
512
iscissors->draw = DRAW_CURRENT_SEED;
478
iscissors->state = SEED_PLACEMENT;
479
iscissors->draw = DRAW_CURRENT_SEED;
514
481
if (options->interactive)
515
482
iscissors->draw |= DRAW_LIVEWIRE;
517
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
484
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
565
530
if (iscissors->mask)
566
531
g_object_unref (iscissors->mask);
568
iscissors->mask = gimp_channel_new_mask (gdisp->gimage,
569
gdisp->gimage->width,
570
gdisp->gimage->height);
533
iscissors->mask = gimp_channel_new_mask (display->image,
534
display->image->width,
535
display->image->height);
571
536
gimp_scan_convert_render (sc, GIMP_DRAWABLE (iscissors->mask)->tiles,
572
537
0, 0, options->antialias);
573
538
gimp_scan_convert_free (sc);
577
gimp_iscissors_tool_button_release (GimpTool *tool,
580
GdkModifierType state,
542
gimp_iscissors_tool_button_release (GimpTool *tool,
545
GdkModifierType state,
546
GimpButtonReleaseType release_type,
547
GimpDisplay *display)
583
549
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
584
GimpSelectionOptions *options;
550
GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
587
options = GIMP_SELECTION_OPTIONS (tool->tool_info->tool_options);
589
553
/* Make sure X didn't skip the button release event -- as it's known
610
574
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
612
/* First take care of the case where the user "cancels" the action */
613
if (! (state & GDK_BUTTON3_MASK))
576
if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
615
578
/* Progress to the next stage of intelligent selection */
616
579
switch (iscissors->state)
619
/* Add a new icurve */
620
if (!iscissors->first_point)
622
/* Determine if we're connecting to the first point */
623
if (iscissors->curves)
625
curve = (ICurve *) iscissors->curves->data;
582
/* Add a new icurve */
583
if (!iscissors->first_point)
585
/* Determine if we're connecting to the first point */
586
if (iscissors->curves)
588
curve = (ICurve *) iscissors->curves->data;
627
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), gdisp,
590
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
628
591
iscissors->x, iscissors->y,
629
592
GIMP_HANDLE_CIRCLE,
630
593
curve->x1, curve->y1,
631
594
POINT_WIDTH, POINT_WIDTH,
632
595
GTK_ANCHOR_CENTER,
635
iscissors->x = curve->x1;
636
iscissors->y = curve->y1;
637
iscissors->connected = TRUE;
598
iscissors->x = curve->x1;
599
iscissors->y = curve->y1;
600
iscissors->connected = TRUE;
641
/* Create the new curve segment */
642
if (iscissors->ix != iscissors->x ||
643
iscissors->iy != iscissors->y)
645
curve = g_new (ICurve, 1);
647
curve->x1 = iscissors->ix;
648
curve->y1 = iscissors->iy;
649
iscissors->ix = curve->x2 = iscissors->x;
650
iscissors->iy = curve->y2 = iscissors->y;
651
curve->points = NULL;
652
iscissors->curves = g_slist_append (iscissors->curves, curve);
653
calculate_curve (tool, curve);
656
else /* this was our first point */
658
iscissors->first_point = FALSE;
662
case SEED_ADJUSTMENT:
663
/* recalculate both curves */
664
if (iscissors->curve1)
666
iscissors->curve1->x1 = iscissors->nx;
667
iscissors->curve1->y1 = iscissors->ny;
668
calculate_curve (tool, iscissors->curve1);
670
if (iscissors->curve2)
672
iscissors->curve2->x2 = iscissors->nx;
673
iscissors->curve2->y2 = iscissors->ny;
674
calculate_curve (tool, iscissors->curve2);
604
/* Create the new curve segment */
605
if (iscissors->ix != iscissors->x ||
606
iscissors->iy != iscissors->y)
608
curve = g_new (ICurve, 1);
610
curve->x1 = iscissors->ix;
611
curve->y1 = iscissors->iy;
612
iscissors->ix = curve->x2 = iscissors->x;
613
iscissors->iy = curve->y2 = iscissors->y;
614
curve->points = NULL;
615
iscissors->curves = g_slist_append (iscissors->curves, curve);
616
calculate_curve (tool, curve);
619
else /* this was our first point */
621
iscissors->first_point = FALSE;
625
case SEED_ADJUSTMENT:
626
/* recalculate both curves */
627
if (iscissors->curve1)
629
iscissors->curve1->x1 = iscissors->nx;
630
iscissors->curve1->y1 = iscissors->ny;
631
calculate_curve (tool, iscissors->curve1);
633
if (iscissors->curve2)
635
iscissors->curve2->x2 = iscissors->nx;
636
iscissors->curve2->y2 = iscissors->ny;
637
calculate_curve (tool, iscissors->curve2);
683
646
iscissors->state = WAITING;
721
682
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
723
iscissors->x = coords->x;
724
iscissors->y = coords->y;
684
iscissors->x = RINT (coords->x);
685
iscissors->y = RINT (coords->y);
726
687
switch (iscissors->state)
728
689
case SEED_PLACEMENT:
729
690
/* Hold the shift key down to disable the auto-edge snap feature */
730
691
if (! (state & GDK_SHIFT_MASK))
731
find_max_gradient (iscissors, gdisp->gimage,
732
&iscissors->x, &iscissors->y);
692
find_max_gradient (iscissors, display->image,
693
&iscissors->x, &iscissors->y);
734
iscissors->x = CLAMP (iscissors->x, 0, gdisp->gimage->width - 1);
735
iscissors->y = CLAMP (iscissors->y, 0, gdisp->gimage->height - 1);
695
iscissors->x = CLAMP (iscissors->x, 0, display->image->width - 1);
696
iscissors->y = CLAMP (iscissors->y, 0, display->image->height - 1);
737
698
if (iscissors->first_point)
739
iscissors->ix = iscissors->x;
740
iscissors->iy = iscissors->y;
700
iscissors->ix = iscissors->x;
701
iscissors->iy = iscissors->y;
744
705
case SEED_ADJUSTMENT:
745
706
/* Move the current seed to the location of the cursor */
746
707
if (! (state & GDK_SHIFT_MASK))
747
find_max_gradient (iscissors, gdisp->gimage,
748
&iscissors->x, &iscissors->y);
708
find_max_gradient (iscissors, display->image,
709
&iscissors->x, &iscissors->y);
750
iscissors->x = CLAMP (iscissors->x, 0, gdisp->gimage->width - 1);
751
iscissors->y = CLAMP (iscissors->y, 0, gdisp->gimage->height - 1);
711
iscissors->x = CLAMP (iscissors->x, 0, display->image->width - 1);
712
iscissors->y = CLAMP (iscissors->y, 0, display->image->height - 1);
753
714
iscissors->nx = iscissors->x;
754
715
iscissors->ny = iscissors->y;
767
728
GimpTool *tool = GIMP_TOOL (draw_tool);
768
729
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (draw_tool);
730
GimpDisplay *display;
734
display = tool->display;
775
736
/* Draw the crosshairs target if we're placing a seed */
776
737
if (iscissors->draw & DRAW_CURRENT_SEED)
778
739
gimp_draw_tool_draw_handle (draw_tool,
779
740
GIMP_HANDLE_CROSS,
741
iscissors->x, iscissors->y,
742
TARGET_SIZE, TARGET_SIZE,
784
743
GTK_ANCHOR_CENTER,
787
746
/* Draw a line boundary */
788
747
if (! iscissors->first_point && ! (iscissors->draw & DRAW_LIVEWIRE))
790
749
gimp_draw_tool_draw_line (draw_tool,
791
750
iscissors->ix, iscissors->iy,
792
751
iscissors->x, iscissors->y,
797
756
/* Draw the livewire boundary */
918
877
iscissors_draw_curve (GimpDrawTool *draw_tool,
927
/* Uh, this shouldn't happen, but it does. So we ignore it.
928
* Quality code, baby.
930
884
if (! curve->points)
933
point = curve->points->pdata + 1;
934
len = curve->points->len - 1;
887
len = curve->points->len;
889
points = g_new (gdouble, 2 * len);
891
for (i = 0, point = curve->points->pdata; i < len; i++, point++)
938
coords = GPOINTER_TO_INT (*point);
939
coords_2 = GPOINTER_TO_INT (*(point - 1));
893
guint32 coords = GPOINTER_TO_INT (*point);
942
if (npts < MAX_POINTS)
944
gimp_draw_tool_draw_line (draw_tool,
945
(coords & 0x0000ffff),
947
(coords_2 & 0x0000ffff),
954
g_warning ("too many points in ICurve segment!");
895
points[i * 2] = (coords & 0x0000ffff);
896
points[i * 2 + 1] = (coords >> 16);
899
gimp_draw_tool_draw_lines (draw_tool, points, len, FALSE, FALSE);
961
904
gimp_iscissors_tool_oper_update (GimpTool *tool,
962
905
GimpCoords *coords,
963
906
GdkModifierType state,
908
GimpDisplay *display)
966
910
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
968
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, gdisp);
912
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
914
/* parent sets a message in the status bar, but it will be replaced here */
970
if (mouse_over_vertex (iscissors, coords->x, coords->y))
916
if (mouse_over_vertex (iscissors, coords->x, coords->y) > 1)
920
status = gimp_suggest_modifiers (_("Click-Drag to move this point"),
921
GDK_SHIFT_MASK & ~state,
922
_("%s: disable auto-snap"), NULL, NULL);
923
gimp_tool_replace_status (tool, display, status);
972
925
iscissors->op = ISCISSORS_OP_MOVE_POINT;
974
927
else if (mouse_over_curve (iscissors, coords->x, coords->y))
976
iscissors->op = ISCISSORS_OP_ADD_POINT;
931
curve = (ICurve *) iscissors->curves->data;
933
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
934
RINT (coords->x), RINT (coords->y),
936
curve->x1, curve->y1,
937
POINT_WIDTH, POINT_WIDTH,
941
gimp_tool_replace_status (tool, display, _("Click to close the"
943
iscissors->op = ISCISSORS_OP_CONNECT;
947
gimp_tool_replace_status (tool, display, _("Click to add a point"
948
" on this segment"));
949
iscissors->op = ISCISSORS_OP_ADD_POINT;
978
952
else if (iscissors->connected && iscissors->mask)
980
if (gimp_channel_value (iscissors->mask, coords->x, coords->y))
954
if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (iscissors->mask),
960
gimp_tool_replace_status (tool, display,
961
_("Click or press Enter to convert to"
982
964
iscissors->op = ISCISSORS_OP_SELECT;
970
gimp_tool_replace_status (tool, display,
971
_("Press Enter to convert to a"
986
974
iscissors->op = ISCISSORS_OP_IMPOSSIBLE;
991
979
switch (iscissors->state)
986
status = gimp_suggest_modifiers (_("Click or Click-Drag to add a"
988
GDK_SHIFT_MASK & ~state,
989
_("%s: disable auto-snap"),
991
gimp_tool_replace_status (tool, display, status);
994
994
iscissors->op = ISCISSORS_OP_ADD_POINT;
998
case SEED_ADJUSTMENT:
998
/* if NO_ACTION, keep parent's status bar message (selection tool) */
1000
999
iscissors->op = ISCISSORS_OP_NONE;
1007
1006
gimp_iscissors_tool_cursor_update (GimpTool *tool,
1008
1007
GimpCoords *coords,
1009
1008
GdkModifierType state,
1009
GimpDisplay *display)
1012
1011
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
1013
GimpCursorType cursor = GIMP_CURSOR_MOUSE;
1014
1012
GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
1016
1014
switch (iscissors->op)
1018
1016
case ISCISSORS_OP_SELECT:
1019
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, gdisp);
1017
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords,
1021
1021
case ISCISSORS_OP_MOVE_POINT:
1022
1022
modifier = GIMP_CURSOR_MODIFIER_MOVE;
1024
1025
case ISCISSORS_OP_ADD_POINT:
1025
1026
modifier = GIMP_CURSOR_MODIFIER_PLUS;
1029
case ISCISSORS_OP_CONNECT:
1030
modifier = GIMP_CURSOR_MODIFIER_JOIN;
1027
1033
case ISCISSORS_OP_IMPOSSIBLE:
1028
cursor = GIMP_CURSOR_BAD;
1034
gimp_tool_set_cursor (tool, gdisp,
1035
cursor, GIMP_TOOL_CURSOR_ISCISSORS, modifier);
1034
modifier = GIMP_CURSOR_MODIFIER_BAD;
1041
gimp_tool_set_cursor (tool, display,
1043
GIMP_TOOL_CURSOR_ISCISSORS,
1048
gimp_iscissors_tool_key_press (GimpTool *tool,
1049
GdkEventKey *kevent,
1050
GimpDisplay *display)
1052
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
1054
if (display != tool->display)
1057
switch (kevent->keyval)
1061
if (iscissors->connected && iscissors->mask)
1063
gimp_iscissors_tool_apply (iscissors, display);
1069
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
1078
gimp_iscissors_tool_apply (GimpIscissorsTool *iscissors,
1079
GimpDisplay *display)
1081
GimpTool *tool = GIMP_TOOL (iscissors);
1082
GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
1084
/* Undraw the curve */
1085
gimp_tool_control_halt (tool->control);
1087
iscissors->draw = DRAW_CURVE;
1088
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
1090
gimp_channel_select_channel (gimp_image_get_mask (display->image),
1091
tool->tool_info->blurb,
1096
options->feather_radius,
1097
options->feather_radius);
1099
gimp_iscissors_tool_reset (iscissors);
1101
gimp_image_flush (display->image);
1040
1105
gimp_iscissors_tool_reset (GimpIscissorsTool *iscissors)
1139
1188
while (list && curves_found < 2)
1141
curve = (ICurve *) list->data;
1190
ICurve *curve = list->data;
1143
1192
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (iscissors),
1144
GIMP_TOOL (iscissors)->gdisp,
1193
GIMP_TOOL (iscissors)->display,
1146
1195
GIMP_HANDLE_CIRCLE,
1147
1196
curve->x1, curve->y1,
1148
1197
POINT_WIDTH, POINT_WIDTH,
1149
1198
GTK_ANCHOR_CENTER,
1152
iscissors->curve1 = curve;
1201
iscissors->curve1 = curve;
1155
return curves_found;
1204
return curves_found;
1157
1206
else if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (iscissors),
1158
GIMP_TOOL (iscissors)->gdisp,
1207
GIMP_TOOL (iscissors)->display,
1160
1209
GIMP_HANDLE_CIRCLE,
1161
1210
curve->x2, curve->y2,
1162
1211
POINT_WIDTH, POINT_WIDTH,
1163
1212
GTK_ANCHOR_CENTER,
1166
iscissors->curve2 = curve;
1215
iscissors->curve2 = curve;
1169
return curves_found;
1218
return curves_found;
1172
1221
list = g_slist_next (list);
1199
1249
if (curves_found == 1)
1202
return clicked_on_curve (tool);
1252
return clicked_on_curve (iscissors, x, y);
1206
1256
static GSList *
1207
1257
mouse_over_curve (GimpIscissorsTool *iscissors,
1218
1263
/* traverse through the list, returning the curve segment's list element
1219
1264
* if the current cursor position is on a curve...
1222
1266
for (list = iscissors->curves; list; list = g_slist_next (list))
1224
curve = (ICurve *) list->data;
1268
ICurve *curve = list->data;
1226
1272
pt = curve->points->pdata;
1227
1273
len = curve->points->len;
1230
coords = GPOINTER_TO_INT (*pt);
1232
tx = coords & 0x0000ffff;
1235
/* Is the specified point close enough to the curve? */
1236
if (gimp_draw_tool_in_radius (GIMP_DRAW_TOOL (iscissors),
1237
GIMP_TOOL (iscissors)->gdisp,
1277
guint32 coords = GPOINTER_TO_INT (*pt);
1281
tx = coords & 0x0000ffff;
1284
/* Is the specified point close enough to the curve? */
1285
if (gimp_draw_tool_in_radius (GIMP_DRAW_TOOL (iscissors),
1286
GIMP_TOOL (iscissors)->display,
1250
1299
static gboolean
1251
clicked_on_curve (GimpTool *tool)
1300
clicked_on_curve (GimpIscissorsTool *iscissors,
1253
GimpIscissorsTool *iscissors;
1254
GimpSelectionOptions *options;
1255
GSList *list, *new_link;
1256
ICurve *curve, *new_curve;
1258
iscissors = GIMP_ISCISSORS_TOOL (tool);
1259
options = GIMP_SELECTION_OPTIONS (tool->tool_info->tool_options);
1261
1306
/* traverse through the list, getting back the curve segment's list
1262
1307
* element if the current cursor position is on a curve...
1263
1308
* If this occurs, replace the curve with two new curves,
1264
1309
* separated by a new vertex.
1266
list = mouse_over_curve (iscissors, iscissors->x, iscissors->y);
1311
list = mouse_over_curve (iscissors, x, y);
1270
curve = (ICurve *) list->data;
1315
ICurve *curve = list->data;
1272
1319
/* undraw the curve */
1273
1320
iscissors->draw = DRAW_CURVE;
1274
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
1321
gimp_draw_tool_pause (GIMP_DRAW_TOOL (iscissors));
1276
1323
/* Create the new curve */
1277
1324
new_curve = g_new (ICurve, 1);
1304
precalculate_arrays (void)
1308
for (i = 0; i < 256; i++)
1310
/* The diagonal weight array */
1311
diagonal_weight[i] = (int) (i * G_SQRT2);
1313
/* The direction value array */
1314
direction_value[i][0] = (127 - abs (127 - i)) * 2;
1315
direction_value[i][1] = abs (127 - i) * 2;
1316
direction_value[i][2] = abs (191 - i) * 2;
1317
direction_value[i][3] = abs (63 - i) * 2;
1320
/* set the 256th index of the direction_values to the hightest cost */
1321
direction_value[255][0] = 255;
1322
direction_value[255][1] = 255;
1323
direction_value[255][2] = 255;
1324
direction_value[255][3] = 255;
1329
1351
calculate_curve (GimpTool *tool,
1332
1354
GimpIscissorsTool *iscissors;
1355
GimpDisplay *display;
1334
1356
gint x, y, dir;
1335
1357
gint xs, ys, xe, ye;
1336
1358
gint x1, y1, x2, y2;
1350
1372
iscissors = GIMP_ISCISSORS_TOOL (tool);
1352
gdisp = tool->gdisp;
1374
display = tool->display;
1354
1376
/* Get the bounding box */
1355
xs = CLAMP (curve->x1, 0, gdisp->gimage->width - 1);
1356
ys = CLAMP (curve->y1, 0, gdisp->gimage->height - 1);
1357
xe = CLAMP (curve->x2, 0, gdisp->gimage->width - 1);
1358
ye = CLAMP (curve->y2, 0, gdisp->gimage->height - 1);
1377
xs = CLAMP (curve->x1, 0, display->image->width - 1);
1378
ys = CLAMP (curve->y1, 0, display->image->height - 1);
1379
xe = CLAMP (curve->x2, 0, display->image->width - 1);
1380
ye = CLAMP (curve->y2, 0, display->image->height - 1);
1359
1381
x1 = MIN (xs, xe);
1360
1382
y1 = MIN (ys, ye);
1361
1383
x2 = MAX (xs, xe) + 1; /* +1 because if xe = 199 & xs = 0, x2 - x1, width = 200 */
1396
1418
/* Initialise the gradient map tile manager for this image if we
1397
1419
* don't already have one. */
1398
1420
if (!iscissors->gradient_map)
1399
iscissors->gradient_map = gradient_map_new (gdisp->gimage);
1421
iscissors->gradient_map = gradient_map_new (display->image);
1401
1423
/* allocate the dynamic programming array */
1402
1424
iscissors->dp_buf =
1403
temp_buf_resize (iscissors->dp_buf, 4, x1, y1, width, height);
1425
temp_buf_resize (iscissors->dp_buf, 4, x1, y1, width, height);
1405
1427
/* find the optimal path of pixels from (x1, y1) to (x2, y2) */
1406
1428
find_optimal_path (iscissors->gradient_map, iscissors->dp_buf,
1407
x1, y1, x2, y2, xs, ys);
1429
x1, y1, x2, y2, xs, ys);
1409
1431
/* get a list of the pixels in the optimal path */
1410
1432
curve->points = plot_pixels (iscissors, iscissors->dp_buf,
1411
x1, y1, xs, ys, xe, ye);
1433
x1, y1, xs, ys, xe, ye);
1413
1435
/* If the bounding box has no width */
1414
1436
else if ((x2 - x1) == 0)
1605
1629
d = data + (y-y1) * dp_buf->width + (x-x1);
1607
1631
for (j = 0; j < dp_buf->width; j++)
1609
min_cost = G_MAXINT;
1611
/* pixel[] array encodes how to get to a neigbour, if possible.
1612
* 0 means no connection (eg edge).
1613
* Rest packed as bottom two bytes: y offset then x offset.
1614
* Initially, we assume we can't get anywhere. */
1615
for (k = 0; k < 8; k++)
1618
/* Find the valid neighboring pixels */
1619
/* the previous pixel */
1621
pixel[((dirx == 1) ? 4 : 0)] = PACK (-dirx, 0);
1623
/* the previous row of pixels */
1626
pixel[((diry == 1) ? 5 : 1)] = PACK (0, -diry);
1628
link = (linkdir == 1) ? 3 : 2;
1630
pixel[((diry == 1) ? (link + 4) : link)] = PACK(-dirx, -diry);
1632
link = (linkdir == 1) ? 2 : 3;
1633
if (j != dp_buf->width - 1)
1634
pixel[((diry == 1) ? (link + 4) : link)] = PACK (dirx, -diry);
1637
/* find the minimum cost of going through each neighbor to reach the
1641
for (k = 0; k < 8; k ++)
1644
link_cost[k] = calculate_link (gradient_map,
1633
min_cost = G_MAXINT;
1635
/* pixel[] array encodes how to get to a neigbour, if possible.
1636
* 0 means no connection (eg edge).
1637
* Rest packed as bottom two bytes: y offset then x offset.
1638
* Initially, we assume we can't get anywhere. */
1639
for (k = 0; k < 8; k++)
1642
/* Find the valid neighboring pixels */
1643
/* the previous pixel */
1645
pixel[((dirx == 1) ? 4 : 0)] = PACK (-dirx, 0);
1647
/* the previous row of pixels */
1650
pixel[((diry == 1) ? 5 : 1)] = PACK (0, -diry);
1652
link = (linkdir == 1) ? 3 : 2;
1654
pixel[((diry == 1) ? (link + 4) : link)] = PACK(-dirx, -diry);
1656
link = (linkdir == 1) ? 2 : 3;
1657
if (j != dp_buf->width - 1)
1658
pixel[((diry == 1) ? (link + 4) : link)] = PACK (dirx, -diry);
1661
/* find the minimum cost of going through each neighbor to reach the
1665
for (k = 0; k < 8; k ++)
1668
link_cost[k] = calculate_link (gradient_map,
1645
1669
xs + j*dirx, ys + i*diry,
1647
1671
((k > 3) ? k - 4 : k));
1648
offset = OFFSET (pixel [k]);
1649
pixel_cost[k] = PIXEL_COST (d[offset]);
1650
cum_cost[k] = pixel_cost[k] + link_cost[k];
1651
if (cum_cost[k] < min_cost)
1653
min_cost = cum_cost[k];
1658
/* If anything can be done... */
1661
/* set the cumulative cost of this pixel and the new direction */
1662
*d = (cum_cost[link] << 8) + link;
1664
/* possibly change the links from the other pixels to this pixel...
1665
* these changes occur if a neighboring pixel will receive a lower
1666
* cumulative cost by going through this pixel.
1668
for (k = 0; k < 8; k ++)
1669
if (pixel[k] && k != link)
1671
/* if the cumulative cost at the neighbor is greater than
1672
* the cost through the link to the current pixel, change the
1673
* neighbor's link to point to the current pixel.
1675
new_cost = link_cost[k] + cum_cost[link];
1676
if (pixel_cost[k] > new_cost)
1678
/* reverse the link direction /-----------------------\ */
1679
offset = OFFSET (pixel[k]);
1680
d[offset] = (new_cost << 8) + ((k > 3) ? k - 4 : k + 4);
1684
/* Set the seed point */
1688
/* increment the data pointer and the x counter */
1672
offset = OFFSET (pixel [k]);
1673
pixel_cost[k] = PIXEL_COST (d[offset]);
1674
cum_cost[k] = pixel_cost[k] + link_cost[k];
1675
if (cum_cost[k] < min_cost)
1677
min_cost = cum_cost[k];
1682
/* If anything can be done... */
1685
/* set the cumulative cost of this pixel and the new direction */
1686
*d = (cum_cost[link] << 8) + link;
1688
/* possibly change the links from the other pixels to this pixel...
1689
* these changes occur if a neighboring pixel will receive a lower
1690
* cumulative cost by going through this pixel.
1692
for (k = 0; k < 8; k ++)
1693
if (pixel[k] && k != link)
1695
/* if the cumulative cost at the neighbor is greater than
1696
* the cost through the link to the current pixel, change the
1697
* neighbor's link to point to the current pixel.
1699
new_cost = link_cost[k] + cum_cost[link];
1700
if (pixel_cost[k] > new_cost)
1702
/* reverse the link direction /-----------------------\ */
1703
offset = OFFSET (pixel[k]);
1704
d[offset] = (new_cost << 8) + ((k > 3) ? k - 4 : k + 4);
1708
/* Set the seed point */
1712
/* increment the data pointer and the x counter */
1693
1717
/* increment the y counter */
1736
1759
dw = tile_ewidth (tile);
1737
1760
dh = tile_eheight (tile);
1739
gimp_projection_finish_draw (gimage->projection);
1740
gimp_projection_flush_now (gimage->projection);
1742
/* get corresponding tile in the gimage */
1743
srctile = tile_manager_get_tile (gimp_projection_get_tiles (gimage->projection),
1762
pickable = GIMP_PICKABLE (image->projection);
1764
gimp_pickable_flush (pickable);
1766
/* get corresponding tile in the image */
1767
srctile = tile_manager_get_tile (gimp_pickable_get_tiles (pickable),
1748
1772
sw = tile_ewidth (srctile);
1749
1773
sh = tile_eheight (srctile);
1751
srcPR.w = MIN (dw, sw);
1752
srcPR.h = MIN (dh, sh);
1753
srcPR.bytes = gimp_projection_get_bytes (gimage->projection);
1754
srcPR.data = tile_data_pointer (srctile, 0, 0);
1755
srcPR.rowstride = srcPR.w * srcPR.bytes;
1775
pixel_region_init_data (&srcPR, tile_data_pointer (srctile, 0, 0),
1776
gimp_pickable_get_bytes (pickable),
1777
gimp_pickable_get_bytes (pickable) *
1779
0, 0, MIN (dw, sw), MIN (dh, sh));
1757
1781
/* XXX tile edges? */
1784
1808
gradmap = tiledata + tile_ewidth (tile) * COST_WIDTH * i;
1786
1810
for (j = 0; j < srcPR.w; j++)
1788
hmax = datah[0] - 128;
1789
vmax = datav[0] - 128;
1790
for (b = 1; b < srcPR.bytes; b++)
1792
if (abs (datah[b] - 128) > abs (hmax)) hmax = datah[b] - 128;
1793
if (abs (datav[b] - 128) > abs (vmax)) vmax = datav[b] - 128;
1796
if (i == 0 || j == 0 || i == srcPR.h-1 || j == srcPR.w-1)
1798
gradmap[j*COST_WIDTH] = 0;
1799
gradmap[j*COST_WIDTH + 1] = 255;
1803
/* 1 byte absolute magitude first */
1804
gradient = sqrt(SQR(hmax) + SQR(vmax));
1805
gradmap[j*COST_WIDTH] = gradient * 255 / MAX_GRADIENT;
1807
/* then 1 byte direction */
1808
if (gradient > MIN_GRADIENT)
1813
direction = (vmax > 0) ? G_PI_2 : -G_PI_2;
1815
direction = atan ((double) vmax / (double) hmax);
1817
/* Scale the direction from between 0 and 254,
1818
* corresponding to -PI/2, PI/2 255 is reserved for
1819
* directionless pixels */
1820
gradmap[j*COST_WIDTH + 1] =
1821
(guint8) (254 * (direction + G_PI_2) / G_PI);
1824
gradmap[j*COST_WIDTH + 1] = 255; /* reserved for weak gradient */
1827
datah += srcPR.bytes;
1828
datav += srcPR.bytes;
1812
hmax = datah[0] - 128;
1813
vmax = datav[0] - 128;
1814
for (b = 1; b < srcPR.bytes; b++)
1816
if (abs (datah[b] - 128) > abs (hmax)) hmax = datah[b] - 128;
1817
if (abs (datav[b] - 128) > abs (vmax)) vmax = datav[b] - 128;
1820
if (i == 0 || j == 0 || i == srcPR.h-1 || j == srcPR.w-1)
1822
gradmap[j*COST_WIDTH] = 0;
1823
gradmap[j*COST_WIDTH + 1] = 255;
1827
/* 1 byte absolute magitude first */
1828
gradient = sqrt(SQR(hmax) + SQR(vmax));
1829
gradmap[j*COST_WIDTH] = gradient * 255 / MAX_GRADIENT;
1831
/* then 1 byte direction */
1832
if (gradient > MIN_GRADIENT)
1837
direction = (vmax > 0) ? G_PI_2 : -G_PI_2;
1839
direction = atan ((double) vmax / (double) hmax);
1841
/* Scale the direction from between 0 and 254,
1842
* corresponding to -PI/2, PI/2 255 is reserved for
1843
* directionless pixels */
1844
gradmap[j*COST_WIDTH + 1] =
1845
(guint8) (254 * (direction + G_PI_2) / G_PI);
1848
gradmap[j*COST_WIDTH + 1] = 255; /* reserved for weak gradient */
1851
datah += srcPR.bytes;
1852
datav += srcPR.bytes;
1832
1856
tile_release (srctile, FALSE);
1835
1859
static TileManager *
1836
gradient_map_new (GimpImage *gimage)
1860
gradient_map_new (GimpImage *image)
1838
1862
TileManager *tm;
1840
tm = tile_manager_new (gimage->width, gimage->height,
1841
sizeof (guint8) * COST_WIDTH);
1842
tile_manager_set_user_data (tm, gimage);
1864
tm = tile_manager_new (image->width, image->height,
1865
sizeof (guint8) * COST_WIDTH);
1866
tile_manager_set_user_data (tm, image);
1843
1867
tile_manager_set_validate_proc (tm, gradmap_tile_validate);
1864
1888
/* Initialise the gradient map tile manager for this image if we
1865
1889
* don't already have one. */
1866
1890
if (!iscissors->gradient_map)
1867
iscissors->gradient_map = gradient_map_new (gimage);
1891
iscissors->gradient_map = gradient_map_new (image);
1869
1893
radius = GRADIENT_SEARCH >> 1;
1871
1895
/* calculate the extent of the search */
1872
cx = CLAMP (*x, 0, gimage->width);
1873
cy = CLAMP (*y, 0, gimage->height);
1896
cx = CLAMP (*x, 0, image->width);
1897
cy = CLAMP (*y, 0, image->height);
1874
1898
sx = cx - radius;
1875
1899
sy = cy - radius;
1876
x1 = CLAMP (cx - radius, 0, gimage->width);
1877
y1 = CLAMP (cy - radius, 0, gimage->height);
1878
x2 = CLAMP (cx + radius, 0, gimage->width);
1879
y2 = CLAMP (cy + radius, 0, gimage->height);
1900
x1 = CLAMP (cx - radius, 0, image->width);
1901
y1 = CLAMP (cy - radius, 0, image->height);
1902
x2 = CLAMP (cx + radius, 0, image->width);
1903
y2 = CLAMP (cy + radius, 0, image->height);
1880
1904
/* calculate the factor to multiply the distance from the cursor by */
1882
1906
max_gradient = 0;