1
/* GIMP - The GNU Image Manipulation Program
2
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
/* This tool is based on a paper from SIGGRAPH '95:
20
* "Intelligent Scissors for Image Composition", Eric N. Mortensen and
21
* William A. Barrett, Brigham Young University.
23
* thanks to Professor D. Forsyth for prompting us to implement this tool. */
25
/* Personal note: Dr. Barrett, one of the authors of the paper written above
26
* is not only one of the most brilliant people I have ever met, he is an
27
* incredible professor who genuinely cares about his students and wants them
28
* to learn as much as they can about the topic.
30
* I didn't even notice I was taking a class from the person who wrote the
31
* paper until halfway through the semester.
35
/* The history of this implementation is lonog and varied. It was
36
* orignally done by Spencer and Peter, and worked fine in the 0.54
37
* (motif only) release of GIMP. Later revisions (0.99.something
38
* until about 1.1.4) completely changed the algorithm used, until it
39
* bore little resemblance to the one described in the paper above.
40
* The 0.54 version of the algorithm was then forwards ported to 1.1.4
44
/* Livewire boundary implementation done by Laramie Leavitt */
52
#include <gdk/gdkkeysyms.h>
54
#include "libgimpmath/gimpmath.h"
55
#include "libgimpwidgets/gimpwidgets.h"
57
#include "tools-types.h"
59
#include "base/pixel-region.h"
60
#include "base/temp-buf.h"
61
#include "base/tile-manager.h"
62
#include "base/tile.h"
64
#include "paint-funcs/paint-funcs.h"
66
#include "core/gimpchannel.h"
67
#include "core/gimpchannel-select.h"
68
#include "core/gimpimage.h"
69
#include "core/gimppickable.h"
70
#include "core/gimpscanconvert.h"
71
#include "core/gimptoolinfo.h"
73
#include "widgets/gimphelp-ids.h"
74
#include "widgets/gimpwidgets-utils.h"
76
#include "display/gimpdisplay.h"
78
#include "gimpiscissorsoptions.h"
79
#include "gimpiscissorstool.h"
80
#include "gimptoolcontrol.h"
82
#include "gimp-intl.h"
86
#define MAX_GRADIENT 179.606 /* == sqrt (127^2 + 127^2) */
87
#define GRADIENT_SEARCH 32 /* how far to look when snapping to an edge */
88
#define TARGET_SIZE 25
89
#define POINT_WIDTH 12 /* size (in pixels) of seed handles */
90
#define EXTEND_BY 0.2 /* proportion to expand cost map by */
91
#define FIXED 5 /* additional fixed size to expand cost map */
92
#define MIN_GRADIENT 63 /* gradients < this are directionless */
94
#define COST_WIDTH 2 /* number of bytes for each pixel in cost map */
96
/* weight to give between gradient (_G) and direction (_D) */
100
/* sentinel to mark seed point in ?cost? map */
103
/* Functional defines */
104
#define PIXEL_COST(x) ((x) >> 8)
105
#define PIXEL_DIR(x) ((x) & 0x000000ff)
116
/* local function prototypes */
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);
158
static void iscissors_convert (GimpIscissorsTool *iscissors,
159
GimpDisplay *display);
160
static TileManager * gradient_map_new (GimpImage *image);
162
static void find_optimal_path (TileManager *gradient_map,
170
static void find_max_gradient (GimpIscissorsTool *iscissors,
174
static void calculate_curve (GimpTool *tool,
176
static void iscissors_draw_curve (GimpDrawTool *draw_tool,
178
static void iscissors_free_icurves (GQueue *curves);
180
static gint mouse_over_vertex (GimpIscissorsTool *iscissors,
183
static gboolean clicked_on_vertex (GimpIscissorsTool *iscissors,
186
static GList * mouse_over_curve (GimpIscissorsTool *iscissors,
189
static gboolean clicked_on_curve (GimpIscissorsTool *iscissors,
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
209
/* static variables */
211
/* where to move on a given link direction */
212
static const gint move[8][2] =
235
/* temporary convolution buffers -- */
236
static guchar maxgrad_conv0[TILE_WIDTH * TILE_HEIGHT * 4] = "";
237
static guchar maxgrad_conv1[TILE_WIDTH * TILE_HEIGHT * 4] = "";
238
static guchar maxgrad_conv2[TILE_WIDTH * TILE_HEIGHT * 4] = "";
241
static const gfloat horz_deriv[9] =
248
static const gfloat vert_deriv[9] =
255
static const gfloat blur_32[9] =
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;
270
gimp_iscissors_tool_register (GimpToolRegisterCallback callback,
273
(* callback) (GIMP_TYPE_ISCISSORS_TOOL,
274
GIMP_TYPE_ISCISSORS_OPTIONS,
275
gimp_iscissors_options_gui,
277
"gimp-iscissors-tool",
279
_("Scissors Select Tool: Select shapes using intelligent edge-fitting"),
280
N_("Intelligent _Scissors"),
282
NULL, GIMP_HELP_TOOL_ISCISSORS,
283
GIMP_STOCK_TOOL_ISCISSORS,
288
gimp_iscissors_tool_class_init (GimpIscissorsToolClass *klass)
290
GObjectClass *object_class = G_OBJECT_CLASS (klass);
291
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
292
GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
295
object_class->finalize = gimp_iscissors_tool_finalize;
297
tool_class->control = gimp_iscissors_tool_control;
298
tool_class->button_press = gimp_iscissors_tool_button_press;
299
tool_class->button_release = gimp_iscissors_tool_button_release;
300
tool_class->motion = gimp_iscissors_tool_motion;
301
tool_class->oper_update = gimp_iscissors_tool_oper_update;
302
tool_class->cursor_update = gimp_iscissors_tool_cursor_update;
303
tool_class->key_press = gimp_iscissors_tool_key_press;
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;
327
gimp_iscissors_tool_init (GimpIscissorsTool *iscissors)
329
GimpTool *tool = GIMP_TOOL (iscissors);
331
iscissors->op = ISCISSORS_OP_NONE;
332
iscissors->dp_buf = NULL;
333
iscissors->curves = g_queue_new ();
334
iscissors->draw = DRAW_NOTHING;
335
iscissors->state = NO_ACTION;
336
iscissors->mask = NULL;
337
iscissors->gradient_map = NULL;
338
iscissors->livewire = NULL;
340
gimp_tool_control_set_scroll_lock (tool->control, TRUE);
341
gimp_tool_control_set_snap_to (tool->control, FALSE);
342
gimp_tool_control_set_preserve (tool->control, FALSE);
343
gimp_tool_control_set_dirty_mask (tool->control, GIMP_DIRTY_IMAGE_SIZE);
344
gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_ISCISSORS);
346
gimp_iscissors_tool_reset (iscissors);
350
gimp_iscissors_tool_finalize (GObject *object)
352
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (object);
354
gimp_iscissors_tool_reset (iscissors);
356
g_queue_free (iscissors->curves);
358
G_OBJECT_CLASS (parent_class)->finalize (object);
362
gimp_iscissors_tool_control (GimpTool *tool,
363
GimpToolAction action,
364
GimpDisplay *display)
366
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
369
switch (iscissors->state)
372
draw = DRAW_CURVE | DRAW_CURRENT_SEED;
375
case SEED_ADJUSTMENT:
376
draw = DRAW_CURVE | DRAW_ACTIVE_CURVE;
384
iscissors->draw = draw;
386
GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
390
case GIMP_TOOL_ACTION_PAUSE:
391
case GIMP_TOOL_ACTION_RESUME:
394
case GIMP_TOOL_ACTION_HALT:
395
gimp_iscissors_tool_reset (iscissors);
401
gimp_iscissors_tool_button_press (GimpTool *tool,
404
GdkModifierType state,
405
GimpDisplay *display)
407
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
408
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
410
iscissors->x = RINT (coords->x);
411
iscissors->y = RINT (coords->y);
413
/* If the tool was being used in another image...reset it */
415
if (gimp_tool_control_is_active (tool->control))
417
if (display != tool->display)
419
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
420
gimp_iscissors_tool_reset (iscissors);
421
gimp_tool_control_activate (tool->control);
426
gimp_tool_control_activate (tool->control);
429
tool->display = display;
431
switch (iscissors->state)
434
iscissors->state = SEED_PLACEMENT;
435
iscissors->draw = DRAW_CURRENT_SEED;
437
if (! (state & GDK_SHIFT_MASK))
438
find_max_gradient (iscissors,
443
iscissors->x = CLAMP (iscissors->x,
444
0, gimp_image_get_width (display->image) - 1);
445
iscissors->y = CLAMP (iscissors->y,
446
0, gimp_image_get_height (display->image) - 1);
448
iscissors->ix = iscissors->x;
449
iscissors->iy = iscissors->y;
451
/* Initialize the selection core only on starting the tool */
452
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
456
/* Check if the mouse click occurred on a vertex or the curve itself */
457
if (clicked_on_vertex (iscissors, coords->x, coords->y))
459
iscissors->nx = iscissors->x;
460
iscissors->ny = iscissors->y;
461
iscissors->state = SEED_ADJUSTMENT;
463
iscissors->draw = DRAW_CURVE | DRAW_ACTIVE_CURVE;
464
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
466
/* If the iscissors is connected, check if the click was inside */
467
else if (iscissors->connected && iscissors->mask &&
468
gimp_pickable_get_opacity_at (GIMP_PICKABLE (iscissors->mask),
472
gimp_iscissors_tool_apply (iscissors, display);
474
else if (! iscissors->connected)
476
/* if we're not connected, we're adding a new point */
478
/* pause the tool, but undraw nothing */
479
iscissors->draw = DRAW_NOTHING;
480
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
482
iscissors->state = SEED_PLACEMENT;
483
iscissors->draw = DRAW_CURRENT_SEED;
485
if (options->interactive)
486
iscissors->draw |= DRAW_LIVEWIRE;
488
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
496
iscissors_convert (GimpIscissorsTool *iscissors,
497
GimpDisplay *display)
499
GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (iscissors);
502
GimpVector2 *points = NULL;
503
guint n_total_points = 0;
505
sc = gimp_scan_convert_new ();
507
for (list = g_queue_peek_tail_link (iscissors->curves);
509
list = g_list_previous (list))
511
ICurve *icurve = list->data;
513
n_total_points += icurve->points->len;
516
points = g_new (GimpVector2, n_total_points);
519
/* go over the curves in reverse order, adding the points we have */
520
for (list = g_queue_peek_tail_link (iscissors->curves);
522
list = g_list_previous (list))
524
ICurve *icurve = list->data;
528
n_points = icurve->points->len;
530
for (i = 0; i < n_points; i++)
532
guint32 packed = GPOINTER_TO_INT (g_ptr_array_index (icurve->points,
535
points[n_total_points+i].x = packed & 0x0000ffff;
536
points[n_total_points+i].y = packed >> 16;
539
n_total_points += n_points;
542
gimp_scan_convert_add_polyline (sc, n_total_points, points, TRUE);
546
g_object_unref (iscissors->mask);
548
iscissors->mask = gimp_channel_new_mask (display->image,
549
gimp_image_get_width (display->image),
550
gimp_image_get_height (display->image));
551
gimp_scan_convert_render (sc,
552
gimp_drawable_get_tiles (GIMP_DRAWABLE (iscissors->mask)),
553
0, 0, options->antialias);
554
gimp_scan_convert_free (sc);
558
gimp_iscissors_tool_button_release (GimpTool *tool,
561
GdkModifierType state,
562
GimpButtonReleaseType release_type,
563
GimpDisplay *display)
565
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
566
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
568
/* Make sure X didn't skip the button release event -- as it's known
571
if (iscissors->state == WAITING)
574
/* Undraw everything */
575
switch (iscissors->state)
578
iscissors->draw = DRAW_CURVE | DRAW_CURRENT_SEED;
579
if (options->interactive)
580
iscissors->draw |= DRAW_LIVEWIRE;
582
case SEED_ADJUSTMENT:
583
iscissors->draw = DRAW_CURVE | DRAW_ACTIVE_CURVE;
589
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
591
if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
593
/* Progress to the next stage of intelligent selection */
594
switch (iscissors->state)
597
/* Add a new icurve */
598
if (!iscissors->first_point)
600
/* Determine if we're connecting to the first point */
601
if (! g_queue_is_empty (iscissors->curves))
603
ICurve *curve = g_queue_peek_head (iscissors->curves);
605
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
606
iscissors->x, iscissors->y,
608
curve->x1, curve->y1,
609
POINT_WIDTH, POINT_WIDTH,
613
iscissors->x = curve->x1;
614
iscissors->y = curve->y1;
615
iscissors->connected = TRUE;
619
/* Create the new curve segment */
620
if (iscissors->ix != iscissors->x ||
621
iscissors->iy != iscissors->y)
623
ICurve *curve = g_slice_new (ICurve);
625
curve->x1 = iscissors->ix;
626
curve->y1 = iscissors->iy;
627
iscissors->ix = curve->x2 = iscissors->x;
628
iscissors->iy = curve->y2 = iscissors->y;
629
curve->points = NULL;
631
g_queue_push_tail (iscissors->curves, curve);
633
calculate_curve (tool, curve);
636
else /* this was our first point */
638
iscissors->first_point = FALSE;
642
case SEED_ADJUSTMENT:
643
/* recalculate both curves */
644
if (iscissors->curve1)
646
iscissors->curve1->x1 = iscissors->nx;
647
iscissors->curve1->y1 = iscissors->ny;
648
calculate_curve (tool, iscissors->curve1);
650
if (iscissors->curve2)
652
iscissors->curve2->x2 = iscissors->nx;
653
iscissors->curve2->y2 = iscissors->ny;
654
calculate_curve (tool, iscissors->curve2);
663
iscissors->state = WAITING;
665
/* Draw only the boundary */
666
iscissors->draw = DRAW_CURVE;
667
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
669
/* convert the curves into a region */
670
if (iscissors->connected)
671
iscissors_convert (iscissors, display);
675
gimp_iscissors_tool_motion (GimpTool *tool,
678
GdkModifierType state,
679
GimpDisplay *display)
681
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
682
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
684
if (iscissors->state == NO_ACTION)
687
if (iscissors->state == SEED_PLACEMENT)
689
iscissors->draw = DRAW_CURRENT_SEED;
691
if (options->interactive)
692
iscissors->draw |= DRAW_LIVEWIRE;
694
else if (iscissors->state == SEED_ADJUSTMENT)
696
iscissors->draw = DRAW_ACTIVE_CURVE;
699
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
701
iscissors->x = RINT (coords->x);
702
iscissors->y = RINT (coords->y);
704
switch (iscissors->state)
707
/* Hold the shift key down to disable the auto-edge snap feature */
708
if (! (state & GDK_SHIFT_MASK))
709
find_max_gradient (iscissors, display->image,
710
&iscissors->x, &iscissors->y);
712
iscissors->x = CLAMP (iscissors->x,
713
0, gimp_image_get_width (display->image) - 1);
714
iscissors->y = CLAMP (iscissors->y,
715
0, gimp_image_get_height (display->image) - 1);
717
if (iscissors->first_point)
719
iscissors->ix = iscissors->x;
720
iscissors->iy = iscissors->y;
724
case SEED_ADJUSTMENT:
725
/* Move the current seed to the location of the cursor */
726
if (! (state & GDK_SHIFT_MASK))
727
find_max_gradient (iscissors, display->image,
728
&iscissors->x, &iscissors->y);
730
iscissors->x = CLAMP (iscissors->x,
731
0, gimp_image_get_width (display->image) - 1);
732
iscissors->y = CLAMP (iscissors->y,
733
0, gimp_image_get_height (display->image) - 1);
735
iscissors->nx = iscissors->x;
736
iscissors->ny = iscissors->y;
743
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
747
gimp_iscissors_tool_draw (GimpDrawTool *draw_tool)
749
GimpTool *tool = GIMP_TOOL (draw_tool);
750
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (draw_tool);
752
/* Draw the crosshairs target if we're placing a seed */
753
if (iscissors->draw & DRAW_CURRENT_SEED)
755
gimp_draw_tool_draw_handle (draw_tool,
757
iscissors->x, iscissors->y,
758
TARGET_SIZE, TARGET_SIZE,
762
/* Draw a line boundary */
763
if (! iscissors->first_point && ! (iscissors->draw & DRAW_LIVEWIRE))
765
gimp_draw_tool_draw_line (draw_tool,
766
iscissors->ix, iscissors->iy,
767
iscissors->x, iscissors->y,
772
/* Draw the livewire boundary */
773
if ((iscissors->draw & DRAW_LIVEWIRE) && ! iscissors->first_point)
775
/* See if the mouse has moved. If so, create a new segment... */
776
if (! iscissors->livewire ||
777
(iscissors->livewire &&
778
(iscissors->ix != iscissors->livewire->x1 ||
779
iscissors->x != iscissors->livewire->x2 ||
780
iscissors->iy != iscissors->livewire->y1 ||
781
iscissors->y != iscissors->livewire->y2)))
783
ICurve *curve = g_slice_new (ICurve);
785
curve->x1 = iscissors->ix;
786
curve->y1 = iscissors->iy;
787
curve->x2 = iscissors->x;
788
curve->y2 = iscissors->y;
789
curve->points = NULL;
791
if (iscissors->livewire)
793
if (iscissors->livewire->points)
794
g_ptr_array_free (iscissors->livewire->points, TRUE);
796
g_slice_free (ICurve, iscissors->livewire);
798
iscissors->livewire = NULL;
801
iscissors->livewire = curve;
802
calculate_curve (tool, curve);
806
iscissors_draw_curve (draw_tool, iscissors->livewire);
809
if ((iscissors->draw & DRAW_CURVE) && ! iscissors->first_point)
813
/* Draw a point at the init point coordinates */
814
if (! iscissors->connected)
816
gimp_draw_tool_draw_handle (draw_tool,
817
GIMP_HANDLE_FILLED_CIRCLE,
826
/* Go through the list of icurves, and render each one... */
827
for (list = g_queue_peek_head_link (iscissors->curves);
829
list = g_list_next (list))
831
ICurve *curve = list->data;
833
if (iscissors->draw & DRAW_ACTIVE_CURVE)
835
/* don't draw curve1 at all */
836
if (curve == iscissors->curve1)
840
gimp_draw_tool_draw_handle (draw_tool,
841
GIMP_HANDLE_FILLED_CIRCLE,
849
if (iscissors->draw & DRAW_ACTIVE_CURVE)
851
/* draw only the start handle of curve2 */
852
if (curve == iscissors->curve2)
857
iscissors_draw_curve (draw_tool, curve);
861
if (iscissors->draw & DRAW_ACTIVE_CURVE)
863
/* plot both curves, and the control point between them */
864
if (iscissors->curve1)
866
gimp_draw_tool_draw_line (draw_tool,
867
iscissors->curve1->x2,
868
iscissors->curve1->y2,
873
if (iscissors->curve2)
875
gimp_draw_tool_draw_line (draw_tool,
876
iscissors->curve2->x1,
877
iscissors->curve2->y1,
883
gimp_draw_tool_draw_handle (draw_tool,
884
GIMP_HANDLE_FILLED_CIRCLE,
896
iscissors_draw_curve (GimpDrawTool *draw_tool,
906
len = curve->points->len;
908
points = g_new (GimpVector2, len);
910
for (i = 0, point = curve->points->pdata; i < len; i++, point++)
912
guint32 coords = GPOINTER_TO_INT (*point);
914
points[i].x = (coords & 0x0000ffff);
915
points[i].y = (coords >> 16);
918
gimp_draw_tool_draw_lines (draw_tool, points, len, FALSE, FALSE);
924
gimp_iscissors_tool_oper_update (GimpTool *tool,
926
GdkModifierType state,
928
GimpDisplay *display)
930
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
932
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
934
/* parent sets a message in the status bar, but it will be replaced here */
936
if (mouse_over_vertex (iscissors, coords->x, coords->y) > 1)
940
status = gimp_suggest_modifiers (_("Click-Drag to move this point"),
941
GDK_SHIFT_MASK & ~state,
942
_("%s: disable auto-snap"), NULL, NULL);
943
gimp_tool_replace_status (tool, display, status);
945
iscissors->op = ISCISSORS_OP_MOVE_POINT;
947
else if (mouse_over_curve (iscissors, coords->x, coords->y))
949
ICurve *curve = g_queue_peek_head (iscissors->curves);
951
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
952
RINT (coords->x), RINT (coords->y),
954
curve->x1, curve->y1,
955
POINT_WIDTH, POINT_WIDTH,
959
gimp_tool_replace_status (tool, display, _("Click to close the"
961
iscissors->op = ISCISSORS_OP_CONNECT;
965
gimp_tool_replace_status (tool, display, _("Click to add a point"
966
" on this segment"));
967
iscissors->op = ISCISSORS_OP_ADD_POINT;
970
else if (iscissors->connected && iscissors->mask)
972
if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (iscissors->mask),
978
gimp_tool_replace_status (tool, display,
979
_("Click or press Enter to convert to"
982
iscissors->op = ISCISSORS_OP_SELECT;
988
gimp_tool_replace_status (tool, display,
989
_("Press Enter to convert to a"
992
iscissors->op = ISCISSORS_OP_IMPOSSIBLE;
997
switch (iscissors->state)
1004
status = gimp_suggest_modifiers (_("Click or Click-Drag to add a"
1006
GDK_SHIFT_MASK & ~state,
1007
_("%s: disable auto-snap"),
1009
gimp_tool_replace_status (tool, display, status);
1012
iscissors->op = ISCISSORS_OP_ADD_POINT;
1016
/* if NO_ACTION, keep parent's status bar message (selection tool) */
1017
iscissors->op = ISCISSORS_OP_NONE;
1024
gimp_iscissors_tool_cursor_update (GimpTool *tool,
1026
GdkModifierType state,
1027
GimpDisplay *display)
1029
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
1030
GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
1032
switch (iscissors->op)
1034
case ISCISSORS_OP_SELECT:
1036
GimpSelectionOptions *options;
1038
options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
1040
/* Do not overwrite the modifiers for add, subtract, intersect */
1041
if (options->operation == GIMP_CHANNEL_OP_REPLACE)
1043
modifier = GIMP_CURSOR_MODIFIER_SELECT;
1048
case ISCISSORS_OP_MOVE_POINT:
1049
modifier = GIMP_CURSOR_MODIFIER_MOVE;
1052
case ISCISSORS_OP_ADD_POINT:
1053
modifier = GIMP_CURSOR_MODIFIER_PLUS;
1056
case ISCISSORS_OP_CONNECT:
1057
modifier = GIMP_CURSOR_MODIFIER_JOIN;
1060
case ISCISSORS_OP_IMPOSSIBLE:
1061
modifier = GIMP_CURSOR_MODIFIER_BAD;
1068
if (modifier != GIMP_CURSOR_MODIFIER_NONE)
1070
gimp_tool_set_cursor (tool, display,
1072
GIMP_TOOL_CURSOR_ISCISSORS,
1077
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords,
1083
gimp_iscissors_tool_key_press (GimpTool *tool,
1084
GdkEventKey *kevent,
1085
GimpDisplay *display)
1087
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
1089
if (display != tool->display)
1092
switch (kevent->keyval)
1097
if (iscissors->connected && iscissors->mask)
1099
gimp_iscissors_tool_apply (iscissors, display);
1105
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
1114
gimp_iscissors_tool_apply (GimpIscissorsTool *iscissors,
1115
GimpDisplay *display)
1117
GimpTool *tool = GIMP_TOOL (iscissors);
1118
GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
1120
/* Undraw the curve */
1121
gimp_tool_control_halt (tool->control);
1123
iscissors->draw = DRAW_CURVE;
1124
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
1126
gimp_channel_select_channel (gimp_image_get_mask (display->image),
1127
tool->tool_info->blurb,
1132
options->feather_radius,
1133
options->feather_radius);
1135
gimp_iscissors_tool_reset (iscissors);
1137
gimp_image_flush (display->image);
1141
gimp_iscissors_tool_reset (GimpIscissorsTool *iscissors)
1143
/* Free and reset the curve list */
1144
iscissors_free_icurves (iscissors->curves);
1147
if (iscissors->mask)
1149
g_object_unref (iscissors->mask);
1150
iscissors->mask = NULL;
1153
/* free the gradient map */
1154
if (iscissors->gradient_map)
1156
/* release any tile we were using */
1159
tile_release (cur_tile, FALSE);
1163
tile_manager_unref (iscissors->gradient_map);
1164
iscissors->gradient_map = NULL;
1167
iscissors->curve1 = NULL;
1168
iscissors->curve2 = NULL;
1169
iscissors->first_point = TRUE;
1170
iscissors->connected = FALSE;
1171
iscissors->draw = DRAW_NOTHING;
1172
iscissors->state = NO_ACTION;
1174
/* Reset the dp buffers */
1175
if (iscissors->dp_buf)
1177
temp_buf_free (iscissors->dp_buf);
1178
iscissors->dp_buf = NULL;
1184
iscissors_free_icurves (GQueue *curves)
1186
while (! g_queue_is_empty (curves))
1188
ICurve *curve = g_queue_pop_head (curves);
1191
g_ptr_array_free (curve->points, TRUE);
1193
g_slice_free (ICurve, curve);
1198
/* XXX need some scan-conversion routines from somewhere. maybe. ? */
1201
mouse_over_vertex (GimpIscissorsTool *iscissors,
1206
gint curves_found = 0;
1208
/* traverse through the list, returning non-zero if the current cursor
1209
* position is on an existing curve vertex. Set the curve1 and curve2
1210
* variables to the two curves containing the vertex in question
1213
iscissors->curve1 = iscissors->curve2 = NULL;
1215
for (list = g_queue_peek_head_link (iscissors->curves);
1217
list = g_list_next (list))
1219
ICurve *curve = list->data;
1221
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (iscissors),
1222
GIMP_TOOL (iscissors)->display,
1225
curve->x1, curve->y1,
1226
POINT_WIDTH, POINT_WIDTH,
1230
iscissors->curve1 = curve;
1233
return curves_found;
1235
else if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (iscissors),
1236
GIMP_TOOL (iscissors)->display,
1239
curve->x2, curve->y2,
1240
POINT_WIDTH, POINT_WIDTH,
1244
iscissors->curve2 = curve;
1247
return curves_found;
1251
return curves_found;
1255
clicked_on_vertex (GimpIscissorsTool *iscissors,
1259
gint curves_found = 0;
1261
curves_found = mouse_over_vertex (iscissors, x, y);
1263
if (curves_found > 1)
1265
/* undraw the curve */
1266
iscissors->draw = DRAW_CURVE;
1267
gimp_draw_tool_pause (GIMP_DRAW_TOOL (iscissors));
1272
/* if only one curve was found, the curves are unconnected, and
1273
* the user only wants to move either the first or last point
1274
* disallow this for now.
1276
if (curves_found == 1)
1279
return clicked_on_curve (iscissors, x, y);
1284
mouse_over_curve (GimpIscissorsTool *iscissors,
1290
/* traverse through the list, returning the curve segment's list element
1291
* if the current cursor position is on a curve...
1293
for (list = g_queue_peek_head_link (iscissors->curves);
1295
list = g_list_next (list))
1297
ICurve *curve = list->data;
1301
pt = curve->points->pdata;
1302
len = curve->points->len;
1306
guint32 coords = GPOINTER_TO_INT (*pt);
1310
tx = coords & 0x0000ffff;
1313
/* Is the specified point close enough to the curve? */
1314
if (gimp_draw_tool_in_radius (GIMP_DRAW_TOOL (iscissors),
1315
GIMP_TOOL (iscissors)->display,
1329
clicked_on_curve (GimpIscissorsTool *iscissors,
1333
GList *list = mouse_over_curve (iscissors, x, y);
1335
/* traverse through the list, getting back the curve segment's list
1336
* element if the current cursor position is on a curve...
1337
* If this occurs, replace the curve with two new curves,
1338
* separated by a new vertex.
1343
ICurve *curve = list->data;
1346
/* undraw the curve */
1347
iscissors->draw = DRAW_CURVE;
1348
gimp_draw_tool_pause (GIMP_DRAW_TOOL (iscissors));
1350
/* Create the new curve */
1351
new_curve = g_slice_new (ICurve);
1353
new_curve->x2 = curve->x2;
1354
new_curve->y2 = curve->y2;
1355
new_curve->x1 = curve->x2 = iscissors->x;
1356
new_curve->y1 = curve->y2 = iscissors->y;
1357
new_curve->points = NULL;
1359
/* Create the new link and supply the new curve as data */
1360
g_queue_insert_after (iscissors->curves, list, new_curve);
1362
iscissors->curve1 = new_curve;
1363
iscissors->curve2 = curve;
1373
calculate_curve (GimpTool *tool,
1376
GimpIscissorsTool *iscissors;
1377
GimpDisplay *display;
1379
gint xs, ys, xe, ye;
1380
gint x1, y1, x2, y2;
1382
gint ewidth, eheight;
1384
/* Calculate the lowest cost path from one vertex to the next as specified
1385
* by the parameter "curve".
1386
* Here are the steps:
1387
* 1) Calculate the appropriate working area for this operation
1388
* 2) Allocate a temp buf for the dynamic programming array
1389
* 3) Run the dynamic programming algorithm to find the optimal path
1390
* 4) Translate the optimal path into pixels in the icurve data
1394
iscissors = GIMP_ISCISSORS_TOOL (tool);
1396
display = tool->display;
1398
/* Get the bounding box */
1399
xs = CLAMP (curve->x1, 0, gimp_image_get_width (display->image) - 1);
1400
ys = CLAMP (curve->y1, 0, gimp_image_get_height (display->image) - 1);
1401
xe = CLAMP (curve->x2, 0, gimp_image_get_width (display->image) - 1);
1402
ye = CLAMP (curve->y2, 0, gimp_image_get_height (display->image) - 1);
1405
x2 = MAX (xs, xe) + 1; /* +1 because if xe = 199 & xs = 0, x2 - x1, width = 200 */
1406
y2 = MAX (ys, ye) + 1;
1408
/* expand the boundaries past the ending points by
1409
* some percentage of width and height. This serves the following purpose:
1410
* It gives the algorithm more area to search so better solutions
1411
* are found. This is particularly helpful in finding "bumps" which
1412
* fall outside the bounding box represented by the start and end
1413
* coordinates of the "curve".
1415
ewidth = (x2 - x1) * EXTEND_BY + FIXED;
1416
eheight = (y2 - y1) * EXTEND_BY + FIXED;
1419
x2 += CLAMP (ewidth, 0, gimp_image_get_width (display->image) - x2);
1421
x1 -= CLAMP (ewidth, 0, x1);
1424
y2 += CLAMP (eheight, 0, gimp_image_get_height (display->image) - y2);
1426
y1 -= CLAMP (eheight, 0, y1);
1428
/* blow away any previous points list we might have */
1431
g_ptr_array_free (curve->points, TRUE);
1432
curve->points = NULL;
1435
/* If the bounding box has width and height... */
1436
if ((x2 - x1) && (y2 - y1))
1441
/* Initialise the gradient map tile manager for this image if we
1442
* don't already have one. */
1443
if (!iscissors->gradient_map)
1444
iscissors->gradient_map = gradient_map_new (display->image);
1446
/* allocate the dynamic programming array */
1448
temp_buf_resize (iscissors->dp_buf, 4, x1, y1, width, height);
1450
/* find the optimal path of pixels from (x1, y1) to (x2, y2) */
1451
find_optimal_path (iscissors->gradient_map, iscissors->dp_buf,
1452
x1, y1, x2, y2, xs, ys);
1454
/* get a list of the pixels in the optimal path */
1455
curve->points = plot_pixels (iscissors, iscissors->dp_buf,
1456
x1, y1, xs, ys, xe, ye);
1458
/* If the bounding box has no width */
1459
else if ((x2 - x1) == 0)
1461
/* plot a vertical line */
1463
dir = (ys > ye) ? -1 : 1;
1464
curve->points = g_ptr_array_new ();
1467
g_ptr_array_add (curve->points, GINT_TO_POINTER ((y << 16) + xs));
1471
/* If the bounding box has no height */
1472
else if ((y2 - y1) == 0)
1474
/* plot a horizontal line */
1476
dir = (xs > xe) ? -1 : 1;
1477
curve->points = g_ptr_array_new ();
1480
g_ptr_array_add (curve->points, GINT_TO_POINTER ((ys << 16) + x));
1487
/* badly need to get a replacement - this is _way_ too expensive */
1489
gradient_map_value (TileManager *map,
1495
static gint cur_tilex;
1496
static gint cur_tiley;
1500
x / TILE_WIDTH != cur_tilex ||
1501
y / TILE_HEIGHT != cur_tiley)
1504
tile_release (cur_tile, FALSE);
1506
cur_tile = tile_manager_get_tile (map, x, y, TRUE, FALSE);
1511
cur_tilex = x / TILE_WIDTH;
1512
cur_tiley = y / TILE_HEIGHT;
1515
p = tile_data_pointer (cur_tile, x, y);
1524
calculate_link (TileManager *gradient_map,
1531
guint8 grad1, dir1, grad2, dir2;
1533
if (!gradient_map_value (gradient_map, x, y, &grad1, &dir1))
1539
/* Convert the gradient into a cost: large gradients are good, and
1540
* so have low cost. */
1541
grad1 = 255 - grad1;
1543
/* calculate the contribution of the gradient magnitude */
1545
value += diagonal_weight[grad1] * OMEGA_G;
1547
value += grad1 * OMEGA_G;
1549
/* calculate the contribution of the gradient direction */
1550
x += (gint8)(pixel & 0xff);
1551
y += (gint8)((pixel & 0xff00) >> 8);
1553
if (!gradient_map_value (gradient_map, x, y, &grad2, &dir2))
1560
(direction_value[dir1][link] + direction_value[dir2][link]) * OMEGA_D;
1567
plot_pixels (GimpIscissorsTool *iscissors,
1583
width = dp_buf->width;
1585
/* Start the data pointer at the correct location */
1586
data = (guint *) temp_buf_data (dp_buf) + (ye - y1) * width + (xe - x1);
1591
list = g_ptr_array_new ();
1595
coords = (y << 16) + x;
1596
g_ptr_array_add (list, GINT_TO_POINTER (coords));
1598
link = PIXEL_DIR (*data);
1599
if (link == SEED_POINT)
1604
data += move[link][1] * width + move[link][0];
1607
/* won't get here */
1612
#define PACK(x, y) ((((y) & 0xff) << 8) | ((x) & 0xff))
1613
#define OFFSET(pixel) ((gint8)((pixel) & 0xff) + \
1614
((gint8)(((pixel) & 0xff00) >> 8)) * dp_buf->width)
1618
find_optimal_path (TileManager *gradient_map,
1642
/* initialize the dynamic programming buffer */
1643
data = (guint32 *) temp_buf_data_clear (dp_buf);
1645
/* what directions are we filling the array in according to? */
1646
dirx = (xs - x1 == 0) ? 1 : -1;
1647
diry = (ys - y1 == 0) ? 1 : -1;
1648
linkdir = (dirx * diry);
1652
for (i = 0; i < dp_buf->height; i++)
1656
d = data + (y-y1) * dp_buf->width + (x-x1);
1658
for (j = 0; j < dp_buf->width; j++)
1660
min_cost = G_MAXINT;
1662
/* pixel[] array encodes how to get to a neigbour, if possible.
1663
* 0 means no connection (eg edge).
1664
* Rest packed as bottom two bytes: y offset then x offset.
1665
* Initially, we assume we can't get anywhere. */
1666
for (k = 0; k < 8; k++)
1669
/* Find the valid neighboring pixels */
1670
/* the previous pixel */
1672
pixel[((dirx == 1) ? 4 : 0)] = PACK (-dirx, 0);
1674
/* the previous row of pixels */
1677
pixel[((diry == 1) ? 5 : 1)] = PACK (0, -diry);
1679
link = (linkdir == 1) ? 3 : 2;
1681
pixel[((diry == 1) ? (link + 4) : link)] = PACK(-dirx, -diry);
1683
link = (linkdir == 1) ? 2 : 3;
1684
if (j != dp_buf->width - 1)
1685
pixel[((diry == 1) ? (link + 4) : link)] = PACK (dirx, -diry);
1688
/* find the minimum cost of going through each neighbor to reach the
1692
for (k = 0; k < 8; k ++)
1695
link_cost[k] = calculate_link (gradient_map,
1696
xs + j*dirx, ys + i*diry,
1698
((k > 3) ? k - 4 : k));
1699
offset = OFFSET (pixel [k]);
1700
pixel_cost[k] = PIXEL_COST (d[offset]);
1701
cum_cost[k] = pixel_cost[k] + link_cost[k];
1702
if (cum_cost[k] < min_cost)
1704
min_cost = cum_cost[k];
1709
/* If anything can be done... */
1712
/* set the cumulative cost of this pixel and the new direction */
1713
*d = (cum_cost[link] << 8) + link;
1715
/* possibly change the links from the other pixels to this pixel...
1716
* these changes occur if a neighboring pixel will receive a lower
1717
* cumulative cost by going through this pixel.
1719
for (k = 0; k < 8; k ++)
1720
if (pixel[k] && k != link)
1722
/* if the cumulative cost at the neighbor is greater than
1723
* the cost through the link to the current pixel, change the
1724
* neighbor's link to point to the current pixel.
1726
new_cost = link_cost[k] + cum_cost[link];
1727
if (pixel_cost[k] > new_cost)
1729
/* reverse the link direction /-----------------------\ */
1730
offset = OFFSET (pixel[k]);
1731
d[offset] = (new_cost << 8) + ((k > 3) ? k - 4 : k + 4);
1735
/* Set the seed point */
1739
/* increment the data pointer and the x counter */
1744
/* increment the y counter */
1750
/* Called to fill in a newly referenced tile in the gradient map */
1752
gradmap_tile_validate (TileManager *tm,
1756
static gboolean first_gradient = TRUE;
1758
GimpPickable *pickable;
1773
gint radius = GRADIENT_SEARCH >> 1;
1775
/* compute the distance weights */
1776
for (i = 0; i < GRADIENT_SEARCH; i++)
1777
for (j = 0; j < GRADIENT_SEARCH; j++)
1778
distance_weights[i * GRADIENT_SEARCH + j] =
1779
1.0 / (1 + sqrt (SQR (i - radius) + SQR (j - radius)));
1781
first_gradient = FALSE;
1784
tile_manager_get_tile_coordinates (tm, tile, &x, &y);
1786
dw = tile_ewidth (tile);
1787
dh = tile_eheight (tile);
1789
pickable = GIMP_PICKABLE (gimp_image_get_projection (image));
1791
gimp_pickable_flush (pickable);
1793
/* get corresponding tile in the image */
1794
srctile = tile_manager_get_tile (gimp_pickable_get_tiles (pickable),
1799
sw = tile_ewidth (srctile);
1800
sh = tile_eheight (srctile);
1802
pixel_region_init_data (&srcPR,
1803
tile_data_pointer (srctile, 0, 0),
1804
gimp_pickable_get_bytes (pickable),
1805
gimp_pickable_get_bytes (pickable) *
1807
0, 0, MIN (dw, sw), MIN (dh, sh));
1810
/* XXX tile edges? */
1812
/* Blur the source to get rid of noise */
1813
pixel_region_init_data (&destPR, maxgrad_conv0, 4, TILE_WIDTH * 4,
1814
0, 0, srcPR.w, srcPR.h);
1815
convolve_region (&srcPR, &destPR, blur_32, 3, 32, GIMP_NORMAL_CONVOL, FALSE);
1817
/* Use the blurred region as the new source pixel region */
1818
pixel_region_init_data (&srcPR, maxgrad_conv0, 4, TILE_WIDTH * 4,
1819
0, 0, srcPR.w, srcPR.h);
1821
/* Get the horizontal derivative */
1822
pixel_region_init_data (&destPR, maxgrad_conv1, 4, TILE_WIDTH * 4,
1823
0, 0, srcPR.w, srcPR.h);
1824
convolve_region (&srcPR, &destPR, horz_deriv, 3, 1, GIMP_NEGATIVE_CONVOL,
1827
/* Get the vertical derivative */
1828
pixel_region_init_data (&destPR, maxgrad_conv2, 4, TILE_WIDTH * 4,
1829
0, 0, srcPR.w, srcPR.h);
1830
convolve_region (&srcPR, &destPR, vert_deriv, 3, 1, GIMP_NEGATIVE_CONVOL,
1833
/* calculate overall gradient */
1834
tiledata = tile_data_pointer (tile, 0, 0);
1836
for (i = 0; i < srcPR.h; i++)
1838
const guint8 *datah = maxgrad_conv1 + srcPR.rowstride * i;
1839
const guint8 *datav = maxgrad_conv2 + srcPR.rowstride * i;
1841
gradmap = tiledata + tile_ewidth (tile) * COST_WIDTH * i;
1843
for (j = 0; j < srcPR.w; j++)
1845
gint8 hmax = datah[0] - 128;
1846
gint8 vmax = datav[0] - 128;
1848
for (b = 1; b < srcPR.bytes; b++)
1850
if (abs (datah[b] - 128) > abs (hmax))
1851
hmax = datah[b] - 128;
1853
if (abs (datav[b] - 128) > abs (vmax))
1854
vmax = datav[b] - 128;
1857
if (i == 0 || j == 0 || i == srcPR.h-1 || j == srcPR.w-1)
1859
gradmap[j * COST_WIDTH + 0] = 0;
1860
gradmap[j * COST_WIDTH + 1] = 255;
1864
/* 1 byte absolute magnitude first */
1865
gradient = sqrt (SQR (hmax) + SQR (vmax));
1866
gradmap[j * COST_WIDTH] = gradient * 255 / MAX_GRADIENT;
1868
/* then 1 byte direction */
1869
if (gradient > MIN_GRADIENT)
1874
direction = (vmax > 0) ? G_PI_2 : -G_PI_2;
1876
direction = atan ((gdouble) vmax / (gdouble) hmax);
1878
/* Scale the direction from between 0 and 254,
1879
* corresponding to -PI/2, PI/2 255 is reserved for
1880
* d9irectionless pixels */
1881
gradmap[j * COST_WIDTH + 1] =
1882
(guint8) (254 * (direction + G_PI_2) / G_PI);
1886
gradmap[j * COST_WIDTH + 1] = 255; /* reserved for weak gradient */
1890
datah += srcPR.bytes;
1891
datav += srcPR.bytes;
1895
tile_release (srctile, FALSE);
1898
static TileManager *
1899
gradient_map_new (GimpImage *image)
1903
tm = tile_manager_new (gimp_image_get_width (image),
1904
gimp_image_get_height (image),
1905
sizeof (guint8) * COST_WIDTH);
1907
tile_manager_set_validate_proc (tm,
1908
(TileValidateProc) gradmap_tile_validate,
1915
find_max_gradient (GimpIscissorsTool *iscissors,
1924
gint sx, sy, cx, cy;
1925
gint x1, y1, x2, y2;
1927
gfloat max_gradient;
1929
/* Initialise the gradient map tile manager for this image if we
1930
* don't already have one. */
1931
if (! iscissors->gradient_map)
1932
iscissors->gradient_map = gradient_map_new (image);
1934
radius = GRADIENT_SEARCH >> 1;
1936
/* calculate the extent of the search */
1937
cx = CLAMP (*x, 0, gimp_image_get_width (image));
1938
cy = CLAMP (*y, 0, gimp_image_get_height (image));
1941
x1 = CLAMP (cx - radius, 0, gimp_image_get_width (image));
1942
y1 = CLAMP (cy - radius, 0, gimp_image_get_height (image));
1943
x2 = CLAMP (cx + radius, 0, gimp_image_get_width (image));
1944
y2 = CLAMP (cy + radius, 0, gimp_image_get_height (image));
1945
/* calculate the factor to multiply the distance from the cursor by */
1951
/* Find the point of max gradient */
1952
pixel_region_init (&srcPR, iscissors->gradient_map,
1953
x1, y1, x2 - x1, y2 - y1, FALSE);
1955
/* this iterates over 1, 2 or 4 tiles only */
1956
for (pr = pixel_regions_register (1, &srcPR);
1958
pr = pixel_regions_process (pr))
1960
endx = srcPR.x + srcPR.w;
1961
endy = srcPR.y + srcPR.h;
1963
for (i = srcPR.y; i < endy; i++)
1965
const guint8 *gradient = srcPR.data + srcPR.rowstride * (i - srcPR.y);
1967
for (j = srcPR.x; j < endx; j++)
1969
gfloat g = *gradient;
1971
gradient += COST_WIDTH;
1973
g *= distance_weights [(i-y1) * GRADIENT_SEARCH + (j-x1)];
1975
if (g > max_gradient)