Does anyone find iscissors useful? Does it do what you would
like or expect? If not, then you may want to test this patch for me.
Since ISCISSORS is currently a bug, help fix it.
This patch adds the livewire path to iscissors. Now you can
see where the actual line is going to end up. There are still a few
bugs and some memory leaks that should be worked out soon, but I am
interested in a complete rewrite.
There are a few issues with a rewrite of iscissors. In order
to be effective I need to know how to do the following:
1. Allocate a buffer the same size as the (x,y) of the visible portion
of the image. This buffer will be destroyed when the tool is closed
or the image changes.
2. Allocate a task to compute values in the background so that the
mouse can still be moved while the computation takes place.
#2 is the hard part. I can do #1 with the current tile system.
Are there any capabilities in the gimp to allow #2? If not, then
iscissors probably won't get much better than this patch.
(ok, a little, but it can't keep up when running on a i586 233 MMX over a
10 MBit network connection to the xserver--my test setup ).
Anyway, try it out, tell me if you like it. I may become motivated to
finish it then...
Laramie
--- iscissors.old Fri Sep 15 18:51:04 2000
+++ iscissors.c Fri Sep 15 19:59:43 2000
@@ -30,6 +30,10 @@
* The 0.54 version of the algorithm was then forwards ported to 1.1.4
* by Austin Donnelly. */
+/* Modifications to implement the livewire boundary were done in version
+ * gimp 1.1.25 by Laramie Leavitt */
+
+
#include "config.h"
#include <stdlib.h>
@@ -89,8 +93,10 @@
DRAW_NOTHING = 0x0,
DRAW_CURRENT_SEED = 0x1,
DRAW_CURVE = 0x2,
- DRAW_ACTIVE_CURVE = 0x4
+ DRAW_ACTIVE_CURVE = 0x4,
+ DRAW_LIVEWIRE = 0x8
} Iscissors_draw;
+
#define DRAW_ALL (DRAW_CURRENT_SEED | DRAW_CURVE)
typedef struct _iscissors Iscissors;
@@ -107,6 +113,8 @@
TempBuf *dp_buf; /* dynamic programming buffer */
+ ICurve *livewire;
+
ICurve *curve1; /* 1st curve connected to current point */
ICurve *curve2; /* 2nd curve connected to current point */
@@ -121,6 +129,8 @@
/* XXX might be useful */
Channel *mask; /* selection mask */
TileManager *gradient_map; /* lazily filled gradient map */
+ TileManager *cost_map; /* lazily filled in cost map */
+
};
typedef struct _IScissorsOptions IScissorsOptions;
@@ -136,22 +146,35 @@
/* Other defines... */
#define MAX_GRADIENT 179.606 /* == sqrt(127^2 + 127^2) */
+
#define GRADIENT_SEARCH 32 /* how far to look when snapping to an edge */
+#define GRADIENT_RADIUS (GRADIENT_SEARCH >> 1)
+
#define TARGET_HEIGHT 25
#define TARGET_WIDTH 25
+
#define POINT_WIDTH 8 /* size (in pixels) of seed handles */
#define POINT_HALFWIDTH (POINT_WIDTH / 2)
+
#define EXTEND_BY 0.2 /* proportion to expand cost map by */
#define FIXED 5 /* additional fixed size to expand cost map */
#define MIN_GRADIENT 63 /* gradients < this are directionless */
#define MAX_POINTS 2048
+#define POS_GRADIENT 0
+#define POS_COST 1
+#define POS_DIR 2
+#define POS_LAPLACE 3
+
#ifdef USE_LAPLACIAN
-# define COST_WIDTH 3 /* number of bytes for each pixel in cost map */
+# define TOTAL_COST_WIDTH 4
#else
-# define COST_WIDTH 2 /* number of bytes for each pixel in cost map */
+# define TOTAL_COST_WIDTH 3
#endif
+
+#define TILE_LAYER( tile, layer ) ((tile_ewidth(tile)*tile_eheight(tile))*layer)
+
#define BLOCK_WIDTH 64
#define BLOCK_HEIGHT 64
#define CONV_WIDTH (BLOCK_WIDTH + 2)
@@ -174,7 +197,6 @@
#define PIXEL_COST(x) (x >> 8)
#define PIXEL_DIR(x) (x & 0x000000ff)
-
/* static variables */
/* where to move on a given link direction */
@@ -212,7 +234,15 @@
D(static guint sent2 = 0xd2d2d2d2);
static guchar maxgrad_conv2 [TILE_WIDTH * TILE_HEIGHT * 4] = "";
D(static guint sent3 = 0xd3d3d3d3);
+static guchar costgrad_conv0 [TILE_WIDTH * TILE_HEIGHT * 4] = "";
+D(static guint sent1 = 0xd4d4d4d4);
+static guchar costgrad_conv1 [TILE_WIDTH * TILE_HEIGHT * 4] = "";
+D(static guint sent2 = 0xd5d5d5d5);
+#ifdef USE_LAPLACIAN
+static guchar costgrad_conv2 [TILE_WIDTH * TILE_HEIGHT * 4] = "";
+D(static guint sent2 = 0xd6d6d6d6);
+#endif
static gint horz_deriv [9] =
{
@@ -228,6 +258,12 @@
-1, -2, -1,
};
+static gint blur_32 [9] =
+{
+ 1, 1, 1,
+ 1, 24, 1,
+ 1, 1, 1,
+};
#ifdef USE_LAPLACIAN
static gint laplacian [9] =
@@ -238,13 +274,7 @@
};
#endif
-static gint blur_32 [9] =
-{
- 1, 1, 1,
- 1, 24, 1,
- 1, 1, 1,
-};
-
+static gboolean first_gradient = TRUE;
static gfloat distance_weights [GRADIENT_SEARCH * GRADIENT_SEARCH];
static gint diagonal_weight [256];
@@ -354,6 +384,7 @@
private->core = draw_core_new (iscissors_draw);
private->op = -1;
private->curves = NULL;
+ private->livewire = NULL;
private->dp_buf = NULL;
private->state = NO_ACTION;
private->mask = NULL;
@@ -470,7 +501,7 @@
iscissors->nx = iscissors->x;
iscissors->ny = iscissors->y;
iscissors->state = SEED_ADJUSTMENT;
- iscissors->draw = DRAW_ACTIVE_CURVE;
+ iscissors->draw = DRAW_ACTIVE_CURVE | DRAW_LIVEWIRE;
draw_core_resume (iscissors->core, tool);
grab_pointer = TRUE;
}
@@ -506,7 +537,7 @@
else if (!iscissors->connected)
{
iscissors->state = SEED_PLACEMENT;
- iscissors->draw = DRAW_CURRENT_SEED;
+ iscissors->draw = DRAW_CURRENT_SEED | DRAW_LIVEWIRE;
grab_pointer = TRUE;
draw_core_resume (iscissors->core, tool);
@@ -631,6 +662,7 @@
}
}
+ /* TODO: Use the livewire segment instead of recomputing */
/* Create the new curve segment */
if (iscissors->ix != iscissors->x ||
iscissors->iy != iscissors->y)
@@ -703,7 +735,7 @@
return;
if (iscissors->state == SEED_PLACEMENT)
- iscissors->draw = DRAW_CURRENT_SEED;
+ iscissors->draw = DRAW_CURRENT_SEED | DRAW_LIVEWIRE;
else if (iscissors->state == SEED_ADJUSTMENT)
iscissors->draw = DRAW_ACTIVE_CURVE;
@@ -778,13 +810,58 @@
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
tx2 - (TARGET_WIDTH >> 1), ty2,
tx2 + (TARGET_WIDTH >> 1), ty2);
+
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
tx2, ty2 - (TARGET_HEIGHT >> 1),
tx2, ty2 + (TARGET_HEIGHT >> 1));
+ }
- if (!iscissors->first_point)
+// Calculate livewire.
+ if ((iscissors->draw & DRAW_LIVEWIRE) && !iscissors->first_point )
+ {
+ if( !iscissors->livewire ||
+ (iscissors->livewire
+ && ( iscissors->ix != iscissors->livewire->x1
+ || iscissors->x != iscissors->livewire->x2
+ || iscissors->iy != iscissors->livewire->y1
+ || iscissors->y != iscissors->livewire->y2 )
+ )
+ )
+ {
+
+ curve = g_new (ICurve, 1);
+
+ curve->x1 = iscissors->ix;
+ curve->y1 = iscissors->iy;
+ curve->x2 = iscissors->x;
+ curve->y2 = iscissors->y;
+ curve->points = NULL;
+ TRC (("create new livewire segment\n"));
+ if ( iscissors->livewire )
+ {
+ if (iscissors->livewire->points)
+ {
+ TRC (("g_ptr_array_free
+(iscissors->livewire->points);\n"));
+ g_ptr_array_free (iscissors->livewire->points, TRUE);
+ }
+ TRC (("g_free (iscissors->livewire);\n"));
+ g_free (iscissors->livewire);
+
+ iscissors->livewire = NULL;
+ }
+
+ iscissors->livewire = curve;
+ TRC (("calculate curve\n"));
+ calculate_curve (tool, curve);
+ curve = NULL;
+ }
+
+ /* plot the curve */
+ iscissors_draw_curve (gdisp, iscissors, iscissors->livewire );
+#if 0
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
tx1, ty1, tx2, ty2);
+#endif
}
if ((iscissors->draw & DRAW_CURVE) && !iscissors->first_point)
@@ -1127,9 +1204,6 @@
channel_delete (iscissors->mask);
iscissors->mask = NULL;
- /* free the gradient map */
- if (iscissors->gradient_map)
- {
/* release any tile we were using */
if (cur_tile)
{
@@ -1138,13 +1212,53 @@
}
cur_tile = NULL;
+ /* free the gradient map */
+ if (iscissors->gradient_map)
+ {
+
TRC (("tile_manager_destroy (iscissors->gradient_map);\n"));
tile_manager_destroy (iscissors->gradient_map);
iscissors->gradient_map = NULL;
}
+ /* free the cost map */
+ if( iscissors->cost_map )
+ {
+ TRC (("tile_manager_destroy (iscissors->cost_map);\n"));
+ tile_manager_destroy (iscissors->cost_map);
+ iscissors->cost_map = NULL;
+ }
+
+ if ( iscissors->livewire )
+ {
+ if (iscissors->livewire->points)
+ {
+ TRC (("g_ptr_array_free (iscissors->livewire->points);\n"));
+ g_ptr_array_free (iscissors->livewire->points, TRUE);
+ }
+ TRC (("g_free (iscissors->livewire);\n"));
+ g_free (iscissors->livewire);
+
+ iscissors->livewire = NULL;
+ }
+
+ if ( iscissors->livewire )
+ {
+ if (iscissors->livewire->points)
+ {
+ TRC (("g_ptr_array_free (iscissors->livewire->points);\n"));
+ g_ptr_array_free (iscissors->livewire->points, TRUE);
+ }
+ TRC (("g_free (iscissors->livewire);\n"));
+ g_free (iscissors->livewire);
+
+ iscissors->livewire = NULL;
+ }
+
iscissors->curve1 = NULL;
iscissors->curve2 = NULL;
+
+
iscissors->first_point = TRUE;
iscissors->connected = FALSE;
iscissors->state = NO_ACTION;
@@ -1518,9 +1632,12 @@
gradient_map_value (TileManager *map,
gint x,
gint y,
- guint8 *grad,
- guint8 *dir)
+ guint8 *cost,
+ guint8 *dir,
+ guint8 *lap
+ )
{
+
static int cur_tilex;
static int cur_tiley;
guint8 *p;
@@ -1539,8 +1656,22 @@
}
p = tile_data_pointer (cur_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
- *grad = p[0];
- *dir = p[1];
+
+/* For the old behavior, prior to this patch, use the following instead
+ of POS_COST. The main difference is that POS_COST is somoothed before
+ the edge detection algorithm, and POS_GRADIENT is not. Mostly. There
+ is also a little math difference in the rounding and subtractions.
+*/
+
+/* *cost = p[ POS_GRADIENT ]; */
+
+ *cost = p[ POS_COST ];
+ *dir = p[ POS_DIR ];
+#ifdef USE_LAPLACIAN
+ *lap = p[ POS_LAPLACE ];
+#else
+ *lap = 0;
+#endif
return TRUE;
}
@@ -1553,9 +1684,9 @@
gint link)
{
gint value = 0;
- guint8 grad1, dir1, grad2, dir2;
+ guint8 grad1, dir1, lap1, grad2, dir2, lap2;
- if (!gradient_map_value (gradient_map, x, y, &grad1, &dir1))
+ if (!gradient_map_value (gradient_map, x, y, &grad1, &dir1, &lap1))
{
grad1 = 0;
dir1 = 255;
@@ -1565,16 +1696,19 @@
* so have low cost. */
grad1 = 255 - grad1;
+
+
/* calculate the contribution of the gradient magnitude */
if (link > 1)
value += diagonal_weight [grad1] * OMEGA_G;
else
value += grad1 * OMEGA_G;
+
/* calculate the contribution of the gradient direction */
x += (gint8)(pixel & 0xff);
y += (gint8)((pixel & 0xff00) >> 8);
- if (!gradient_map_value (gradient_map, x, y, &grad2, &dir2))
+ if (!gradient_map_value (gradient_map, x, y, &grad2, &dir2, &lap2))
{
grad2 = 0;
dir2 = 255;
@@ -1582,6 +1716,9 @@
value += (direction_value [dir1][link] + direction_value [dir2][link]) *
OMEGA_D;
+
+ /* calculate the contribution of the laplacian */
+
return value;
}
@@ -1637,6 +1774,7 @@
((gint8)(((pixel) & 0xff00) >> 8)) * dp_buf->width)
+
static void
find_optimal_path (TileManager *gradient_map,
TempBuf *dp_buf,
@@ -1782,22 +1920,41 @@
}
+/*
+// find_max_gradient.
+//
+// This method searches the local area around the (x,y)
+// Point to find the point with the maximum gradient.
+//
+*/
+
/* Called to fill in a newly referenced tile in the gradient map */
static void
gradmap_tile_validate (TileManager *tm,
Tile *tile)
{
- static gboolean first_gradient = TRUE;
gint x, y;
gint dw, dh;
gint sw, sh;
gint i, j;
gint b;
- gfloat gradient;
- guint8 *gradmap;
+
guint8 *tiledata;
- guint8 *datah, *datav;
- gint8 hmax, vmax;
+
+#ifdef USE_LAPLACIAN
+ guint8 *laplv;
+ guint8 lmax;
+#endif
+ guint8 chmax, cvmax;
+ guint8 ghmax, gvmax;
+
+ gfloat gradient, cost;
+
+ guint8 *gradmap;
+ guint8 *gradh, *gradv;
+
+ guint8 *costh, *costv;
+
Tile *srctile;
PixelRegion srcPR, destPR;
GImage *gimage;
@@ -1843,8 +2000,26 @@
/* XXX tile edges? */
- /* Blur the source to get rid of noise */
+
+ /* First, calculate the cost map. The cost map is DIFFERENT from the gradient
+map. */
+ /* Get the horizontal derivative */
+ /* This requires doing the convolve operations twice-- once with a blurred input,
+and once without. */
+
destPR.rowstride = TILE_WIDTH * 4;
+ destPR.data = costgrad_conv0;
+ convolve_region (&srcPR, &destPR, horz_deriv, 3, 1, NEGATIVE_CONVOL);
+
+ /* Get the vertical derivative */
+ destPR.data = costgrad_conv1;
+ convolve_region (&srcPR, &destPR, vert_deriv, 3, 1, NEGATIVE_CONVOL);
+
+#ifdef USE_LAPLACIAN
+ /* Get the laplacian */
+ destPR.data = costgrad_conv2;
+ convolve_region (&srcPR, &destPR, laplacian, 3, 1, NEGATIVE_CONVOL);
+#endif
+
+ /* Blur the source to get rid of noise */
destPR.data = maxgrad_conv0;
convolve_region (&srcPR, &destPR, blur_32, 3, 32, NORMAL_CONVOL);
@@ -1862,61 +2037,87 @@
/* calculate overall gradient */
tiledata = tile_data_pointer (tile, 0, 0);
+
+ /* fill in the cost map */
+
for (i = 0; i < srcPR.h; i++)
{
- datah = maxgrad_conv1 + srcPR.rowstride*i;
- datav = maxgrad_conv2 + srcPR.rowstride*i;
- gradmap = tiledata + tile_ewidth (tile) * COST_WIDTH * i;
+ gradh = maxgrad_conv1 + (srcPR.rowstride * i);
+ gradv = maxgrad_conv2 + (srcPR.rowstride * i);
+ costh = costgrad_conv0 + (srcPR.rowstride * i);
+ costv = costgrad_conv1 + (srcPR.rowstride * i);
+
+#ifdef USE_LAPLACIAN
+ laplv = costgrad_conv2 + (srcPR.rowstride * i);
+#endif
+
+ // The gradient is always the first one.
+ gradmap = tiledata + ( tile_ewidth ( tile ) * i * TOTAL_COST_WIDTH );
+ /* find the maximums */
for (j = 0; j < srcPR.w; j++)
{
- hmax = datah[0] - 128;
- vmax = datav[0] - 128;
+ chmax = costh[0] - 128;
+ cvmax = costv[0] - 128;
+#ifdef USE_LAPLACIAN
+ lmax = laplv[0] - 128;
+#endif
+ ghmax = gradh[0];
+ gvmax = gradv[0];
+
for (b = 1; b < srcPR.bytes; b++)
{
- if (abs (datah[b] - 128) > abs (hmax)) hmax = datah[b] - 128;
- if (abs (datav[b] - 128) > abs (vmax)) vmax = datav[b] - 128;
+#ifdef USE_LAPLACIAN
+ if (abs (laplv[b] - 128) > abs (lmax)) lmax = laplv[b] - 128;
+#endif
+ if (abs (costh[b] - 128) > abs (chmax)) chmax = costh[b] - 128;
+ if (abs (costv[b] - 128) > abs (cvmax)) cvmax = costv[b] - 128;
+ if (gradh[b] > ghmax) ghmax = gradh[b];
+ if (gradv[b] > gvmax) gvmax = gradv[b];
}
+ gradient = sqrt (ghmax * ghmax + gvmax * gvmax);
+ cost = sqrt (chmax * chmax + cvmax * cvmax );
+
+ gradmap[ POS_COST ] = (guint8 ) CLAMP( (gint) cost, 0, 255 );
+ gradmap[ POS_GRADIENT ] = gradient * 255 / MAX_GRADIENT;
+ gradmap[ POS_DIR ] = 255;
+#ifdef USE_LAPLACIAN
+ gradmap[ POS_LAPLACE ] = lmax;
+#endif
+ /* 1 byte absolute magitude first */
+
if (i == 0 || j == 0 || i == srcPR.h-1 || j == srcPR.w-1)
{
- gradmap[j*COST_WIDTH] = 0;
- gradmap[j*COST_WIDTH + 1] = 255;
+ gradmap[ POS_GRADIENT ] = 0;
+ /* Do I need to set POS_COST to 0 here as well? */
goto contin;
}
- /* 1 byte absolute magitude first */
- gradient = sqrt(SQR(hmax) + SQR(vmax));
- gradmap[j*COST_WIDTH] = gradient * 255 / MAX_GRADIENT;
-
/* then 1 byte direction */
- if (gradient > MIN_GRADIENT)
+ if (cost > MIN_GRADIENT)
{
gfloat direction;
- if (!hmax)
- direction = (vmax > 0) ? G_PI_2 : -G_PI_2;
+ if (!chmax)
+ direction = (cvmax > 0) ? G_PI_2 : -G_PI_2;
else
- direction = atan ((double) vmax / (double) hmax);
+ direction = atan ((double) cvmax / (double) chmax);
/* Scale the direction from between 0 and 254,
* corresponding to -PI/2, PI/2 255 is reserved for
* directionless pixels */
- gradmap[j*COST_WIDTH + 1] =
- (guint8) (254 * (direction + G_PI_2) / G_PI);
+ gradmap[ POS_DIR ] = (guint8) (254 * (direction + G_PI_2) / G_PI);
}
- else
- gradmap[j*COST_WIDTH + 1] = 255; /* reserved for weak gradient */
+contin:
- contin:
- {
-#ifdef DEBUG
- int g = gradmap[j*COST_WIDTH];
- int d = gradmap[j*COST_WIDTH + 1];
- TRC (("%c%c", 'a' + (g * 25 / 255), '0' + (d / 25)));
-#endif /* DEBUG */
- }
+ costh += srcPR.bytes;
+ costv += srcPR.bytes;
+#ifdef USE_LAPLACIAN
+ laplv += srcPR.bytes;
+#endif
+ gradh += srcPR.bytes;
+ gradv += srcPR.bytes;
- datah += srcPR.bytes;
- datav += srcPR.bytes;
+ gradmap += TOTAL_COST_WIDTH;
}
TRC (("\n"));
}
@@ -1925,19 +2126,21 @@
tile_release (srctile, FALSE);
}
+
static TileManager *
gradient_map_new (GImage *gimage)
{
TileManager *tm;
tm = tile_manager_new (gimage->width, gimage->height,
- sizeof (guint8) * COST_WIDTH);
+ sizeof (guint8) * TOTAL_COST_WIDTH );
tile_manager_set_user_data (tm, gimage);
tile_manager_set_validate_proc (tm, gradmap_tile_validate);
return tm;
}
+
static void
find_max_gradient (Iscissors *iscissors,
GImage *gimage,
@@ -1945,7 +2148,6 @@
gint *y)
{
PixelRegion srcPR;
- gint radius;
gint i, j;
gint endx, endy;
gint sx, sy, cx, cy;
@@ -1961,17 +2163,16 @@
if (!iscissors->gradient_map)
iscissors->gradient_map = gradient_map_new (gimage);
- radius = GRADIENT_SEARCH >> 1;
-
/* calculate the extent of the search */
cx = CLAMP (*x, 0, gimage->width);
cy = CLAMP (*y, 0, gimage->height);
- sx = cx - radius;
- sy = cy - radius;
- x1 = CLAMP (cx - radius, 0, gimage->width);
- y1 = CLAMP (cy - radius, 0, gimage->height);
- x2 = CLAMP (cx + radius, 0, gimage->width);
- y2 = CLAMP (cy + radius, 0, gimage->height);
+ sx = cx - GRADIENT_RADIUS;
+ sy = cy - GRADIENT_RADIUS;
+
+ x1 = CLAMP (cx - GRADIENT_RADIUS, 0, gimage->width);
+ y1 = CLAMP (cy - GRADIENT_RADIUS, 0, gimage->height);
+ x2 = CLAMP (cx + GRADIENT_RADIUS, 0, gimage->width);
+ y2 = CLAMP (cy + GRADIENT_RADIUS, 0, gimage->height);
/* calculate the factor to multiply the distance from the cursor by */
max_gradient = 0;
@@ -1994,8 +2195,8 @@
gradient = srcPR.data + srcPR.rowstride * (i - srcPR.y);
for (j = srcPR.x; j < endx; j++)
{
- g = *gradient;
- gradient += COST_WIDTH;
+ g = gradient[ POS_GRADIENT ] ;
+ gradient += TOTAL_COST_WIDTH;
g *= distance_weights [(i-y1) * GRADIENT_SEARCH + (j-x1)];
if (g > max_gradient)
{
@@ -2011,3 +2212,4 @@
}
/* End of iscissors.c */
+