hi :) some time ago i said i would like to work on the iscissors tool. well, i started rewriting it, as i didn't really liked the current code. my implementation is far from being ready, but i don't have that much time to work on it for the next weeks, and i don't want my code to just rod on my discs. i haven't really had the time to learn all of the gimp stuff i would need to build a good tool, so my code is not really integrated into the gimp, i just added enough function calls to be able to do quick tests. my code is not useable in its current form! but i think it's a good basis for a future iscissors tool well, have a look at it... my implementation goes to gimpiscissorstool-livewire.[ch], i changed gimpiscissorstool.[ch] just enough to disable the old code and to enable some features using my code -- CU, / Friedrich-Alexander University Erlangen, Germany Martin Waitz // [Tali on IRCnet] [tali.home.pages.de] _________ ______________/// - - - - - - - - - - - - - - - - - - - - /// dies ist eine manuell generierte mail, sie beinhaltet // tippfehler und ist auch ohne grossbuchstaben gueltig. / - Wer bereit ist, grundlegende Freiheiten aufzugeben, um sich kurzfristige Sicherheit zu verschaffen, der hat weder Freiheit noch Sicherheit verdient. Benjamin Franklin (1706 - 1790)
/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This code was written by Martin Waitz <tali@xxxxxxxxxxxxxxxxxxxx>, * based on a paper by Mortensen and Barrett: * http://www.cs.orst.edu/~enm/publications/GMIP_98/seg_scissors.html * it really needs some work, especially integration into the GIMP. */ #include "config.h" #include <string.h> #include <unistd.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> #include "tools-types.h" #include "core/gimpimage.h" #include "core/gimpimage-projection.h" #include "base/tile.h" #include "base/tile-manager.h" #include "gimpiscissorstool-livewire.h" /***************************************************************************/ /* XXX static const gint MAX_COST = (255);*/ #define MAX_COST (255) /***************************************************************************/ /* DEBUG stuff */ /*#define DEBUG*/ #ifdef DEBUG struct my_debug_header { guint32 magic; guint32 size; gchar* filename; gchar* function; guint32 linenum; guint8 data[0]; }; #define MY_DEBUG_MAGIC_HEAD (0xdeadbeaf) #define MY_DEBUG_MAGIC_TAIL (0xfacefeed) static gpointer my_malloc0(gulong n_bytes, gchar* filename, gchar* function, guint linenum) { struct my_debug_header *p; g_assert( n_bytes > 0 ); p = (struct my_debug_header*) g_malloc0( n_bytes + sizeof(struct my_debug_header)+4 ); /* insert info before data */ p->magic = MY_DEBUG_MAGIC_HEAD; p->size = n_bytes; p->filename = filename; p->function = function; p->linenum = linenum; /* insert info after data */ * (guint32*) (p->data+n_bytes) = MY_DEBUG_MAGIC_TAIL; /*g_printerr( "my_malloc: %p (%d)\n", p->data, n_bytes );*/ return p->data; } static void my_check_pointer(gpointer mem) { struct my_debug_header *p; g_assert( mem ); p = (struct my_debug_header*) ( ((guint8*) mem) - sizeof(struct my_debug_header) ); g_assert( (gpointer) p > (gpointer) 0x8000000); /* g_assert( (gpointer) p < sbrk(0) ); */ g_assert( p->data == mem ); g_assert( p->magic == MY_DEBUG_MAGIC_HEAD ); g_assert( p->size > 0 ); g_assert( p->filename ); g_assert( p->function ); g_assert( p->linenum>0 ); g_assert( * (guint32*) (p->data+p->size) == MY_DEBUG_MAGIC_TAIL ); } static void my_free(gpointer mem) { struct my_debug_header *p; if( mem==NULL ) return; /*g_printerr( "my_free: %p\n", mem );*/ my_check_pointer( mem ); p = (struct my_debug_header*) ( ((guint8*) mem) - sizeof(struct my_debug_header) ); memset( p, 0, p->size + sizeof(struct my_debug_header)+4 ); g_free( p ); } #undef g_new #undef g_new0 #undef g_renew #define g_new(struct_type, n_structs) \ ((struct_type *) my_malloc0 (((gsize) sizeof (struct_type)) * ((gsize) (n_structs)), \ __FILE__, __PRETTY_FUNCTION__, __LINE__)) #define g_new0(struct_type, n_structs) \ ((struct_type *) my_malloc0 (((gsize) sizeof (struct_type)) * ((gsize) (n_structs)), \ __FILE__, __PRETTY_FUNCTION__, __LINE__)) #define g_renew(struct_type, mem, n_structs) \ ((struct_type *) my_realloc ((mem), ((gsize) sizeof (struct_type)) * ((gsize) (n_structs)), \ __FILE__, __PRETTY_FUNCTION__, __LINE__)) #define g_free(p) my_free(p) #define check_pointer(p) my_check_pointer(p) #else #define check_pointer g_assert #endif /***************************************************************************/ /* Curve */ /* curve from free point to seedpoint */ /* okay, that seems like the wrong direction, but it's easier that way */ /* the curve is stored as an array of directions between the pixels. * there are two types of curves: with and without aging information: * curves are built without aging information. * aging information is the information about the similarity of several * curves. it is generated by curve_replace, which merges the aging * info from one curve with the shape of a new curve */ /* store the aging information for one link of the live wire */ struct _AgeEntry { gint paths; GTimeVal time; }; /* construct a new curve */ Curve * curve_new() { Curve *curve; curve = g_new0(Curve, 1); curve->curve = g_byte_array_new(); return( curve ); } /* destruct a curve */ void curve_free(Curve *curve) { g_assert( curve ); g_byte_array_free( curve->curve, TRUE ); if( curve_has_age(curve) ) { g_array_free( curve->age, TRUE ); } g_free( curve ); } /* modify a coordianate according to a given direction */ inline void curve_proceed(guint8 dir, gint *x, gint *y) { static const gint move_x[] = { +1, +1, 0, -1, -1, -1, 0, +1 }; static const gint move_y[] = { 0, +1, +1, +1, 0, -1, -1, -1 }; g_assert( dir < sizeof(move_x)/sizeof(move_x[0]) ); *x += move_x[dir]; *y += move_y[dir]; } /* return the reverse of the given direction */ inline guint8 curve_reverse(guint8 dir) { return (dir+4) % 8; } /* set the free point of an new curve */ /* only works on a freshly created curve */ void curve_set_free(Curve *curve, gint x, gint y) { check_pointer( curve ); g_assert( curve->curve->len == 0 ); curve->free_x = curve->seed_x = x; curve->free_y = curve->seed_y = y; curve->bounds_x1 = curve->bounds_x2 = x; curve->bounds_y1 = curve->bounds_y2 = y; } /* add point to curve (moving seedpoint) */ void curve_move_seed(Curve *curve, guint8 dir) { check_pointer( curve ); g_assert( !curve_has_age(curve) ); /* add coord */ g_byte_array_append( curve->curve, &dir, 1 ); curve_proceed( dir, &curve->seed_x, &curve->seed_y ); /* update bounding box */ if( curve->seed_x<curve->bounds_x1 ) curve->bounds_x1 = curve->seed_x; if( curve->seed_x>curve->bounds_x2 ) curve->bounds_x2 = curve->seed_x; if( curve->seed_y<curve->bounds_y1 ) curve->bounds_y1 = curve->seed_y; if( curve->seed_y>curve->bounds_y2 ) curve->bounds_y2 = curve->seed_y; } /* traverse the curve */ /* last point traversed is the one just before the seed point */ /* stops if the callback returns FALSE */ void curve_traverse(Curve *curve, CurveTraversalCallback callback, gpointer data) { CurveLink link; gint i; check_pointer( curve ); g_assert( callback ); link.x = curve->free_x; link.y = curve->free_y; for(i=0; i<curve->curve->len; i++) { link.dir = curve->curve->data[i]; if( !callback( curve, &link, data ) ) break; curve_proceed( link.dir, &link.x, &link.y ); } } /* is aging information available for this path? */ gboolean curve_has_age(Curve *curve) { return curve->age!=NULL; } /* add aging information for this path */ /* only for empty path */ void curve_add_age(Curve *curve) { check_pointer( curve ); g_assert( curve->curve->len == 0 ); g_assert( curve->age==NULL ); curve->age = g_array_new( FALSE, FALSE, sizeof(AgeEntry) ); } /* free aging information of this curve */ void curve_drop_age(Curve *curve) { check_pointer( curve ); if( curve_has_age( curve ) ) { g_array_free( curve->age, TRUE ); curve->age = NULL; } } /* update the counters in the target curve, freeing source */ /* something like { target.copyFrom(source); free(source); } */ /* points that haven't moved between both curves are aged */ void curve_replace(Curve *target, Curve *source) { check_pointer( source ); check_pointer( target ); if( curve_has_age( target ) ) { guint8 *s, *t; gint slen, tlen; gint overlap, i; GTimeVal current_time; /* although curve is saved from free to seedpoint, it actually * is rooted at the seedpoint, start there for comparision */ slen = source->curve->len; tlen = target->curve->len; if( target->seed_x==source->seed_x && target->seed_y==source->seed_y ) { s = &source->curve->data[slen-1]; t = &target->curve->data[tlen-1]; for( overlap=0; overlap<MIN(slen,tlen) && *s==*t; overlap++, s--, t-- ) { /* increase number of paths for that point */ g_array_index(target->age, AgeEntry, tlen-overlap-1).paths++; } /* now we have: * o=free points | overlapping part->age it | x=seed points * source: o----------\ (length overlap, from (slen-overlap) to (slen-1) * ==========================x * ==========================x * target: o------/ (length overlap, from (tlen-overlap) to (tlen-1) */ /* move that part of the ageing info to the new position in the curve */ if( slen > tlen ) { target->age = g_array_set_size(target->age, slen); memmove( &g_array_index(target->age, AgeEntry, (slen-overlap)), &g_array_index(target->age, AgeEntry, (tlen-overlap)), overlap * sizeof(AgeEntry) ); } else if( slen < tlen ) { memmove( &g_array_index(target->age, AgeEntry, (slen-overlap)), &g_array_index(target->age, AgeEntry, (tlen-overlap)), overlap * sizeof(AgeEntry) ); target->age = g_array_set_size(target->age, slen); } } else { overlap = 0; target->age = g_array_set_size(target->age, slen); } g_get_current_time( ¤t_time ); for( i=0; i<slen-overlap; i++ ) { g_array_index( target->age, AgeEntry, i ).paths = 0; g_array_index( target->age, AgeEntry, i ).time = current_time; } } /* move over curve */ target->seed_x = source->seed_x; target->seed_y = source->seed_y; target->free_x = source->free_x; target->free_y = source->free_y; g_byte_array_free( target->curve, TRUE ); target->curve = source->curve; target->bounds_x1 = source->bounds_x1; target->bounds_x2 = source->bounds_x2; target->bounds_y1 = source->bounds_y1; target->bounds_y2 = source->bounds_y2; curve_drop_age( source ); g_free( source ); } /* check aging information */ /* if age is reached, curve gets truncated and TRUE is returned */ gboolean curve_check_age(Curve *curve, GradientInput *gi) { AgeEntry *age; GTimeVal current_time; gint x, y; guint8 dir; gint i; glong msec; gint path_max, path_thresh; check_pointer( curve ); if( !curve->age ) return FALSE; g_assert( curve->curve->len == curve->age->len ); if( curve->age->len<20 ) return FALSE; g_get_current_time( ¤t_time ); x = curve->free_x; y = curve->free_y; path_max = g_array_index( curve->age, AgeEntry, curve->age->len-1 ).paths; if( path_max < 10 ) return FALSE; /* is there a bug with path_max? */ path_thresh = path_max / 5 + 10; /* only check the first half of the wire */ for(i=0; i<curve_length(curve)/2; i++) { age = &g_array_index( curve->age, AgeEntry, i); msec = (current_time.tv_sec - age->time.tv_sec) * 1000 + (current_time.tv_usec - age->time.tv_usec) / 1000; g_assert( age->paths <= path_max ); g_assert( msec < 1000000 ); /*g_printerr( "curve_check_age: age=%p msec=%d paths=%d\n", age, msec, age->paths );*/ /* check for age */ if( (msec > 500+4*gradientinput_get_magnitude(gi, x, y)) && (age->paths > path_thresh) ) { g_printerr( "curve_check_age: [aged] i=%d/%d msec=%ld paths=%d/%d\n", i, curve_length(curve), msec, age->paths, path_thresh ); curve_truncate_at_free( curve, i, x, y ); return TRUE; } /* get next coords */ dir = curve->curve->data[i]; curve_proceed( dir, &x, &y ); } return FALSE; } /* truncate curve to only contain the part between the free point and the truncation point */ /* -> remove seed point */ void curve_truncate_at_seed(Curve *curve, gint index, gint x, gint y) { gint newlen; check_pointer( curve ); g_assert( index>0 ); newlen = curve->curve->len - index; g_assert( newlen>0 ); g_byte_array_set_size( curve->curve, newlen ); if( curve_has_age(curve) ) { g_array_set_size( curve->age, newlen ); } curve->seed_x = x; curve->seed_y = y; /* TODO: fix bounding box? */ } /* truncate curve to only contain the part between the seed point and the truncation point */ /* -> remove free point */ void curve_truncate_at_free(Curve *curve, gint index, gint x, gint y) { gint newlen; check_pointer( curve ); g_assert( index>=0 ); if( index==0 ) return; newlen = curve->curve->len - index; g_assert( newlen>0 ); memmove( curve->curve->data, curve->curve->data+index, newlen ); g_byte_array_set_size( curve->curve, newlen ); if( curve_has_age(curve) ) { memmove( &g_array_index(curve->age, AgeEntry, 0), &g_array_index(curve->age, AgeEntry, index), newlen * sizeof(AgeEntry) ); g_array_set_size( curve->age, newlen ); } curve->free_x = x; curve->free_y = y; /* TODO: fix bounding box? */ } /* not needed, remove? */ gboolean curve_contains_callback(Curve *curve, CurveLink *link, gpointer data); gboolean curve_contains(Curve *curve, GimpIscissorsTool *iscissors, gdouble x, gdouble y, gint tolerance) { struct CurveContainsParam { GimpIscissorsTool *iscissors; gdouble x, y; gint tolerance; gboolean result; } param; gint px, py, cx, cy; check_pointer( curve ); g_assert( iscissors ); /* check bonding box */ gdisplay_transform_coords(GIMP_TOOL(iscissors)->gdisp, x, y, &px, &py, FALSE); gdisplay_transform_coords(GIMP_TOOL(iscissors)->gdisp, curve->bounds_x1, curve->bounds_y1, &cx, &cy, FALSE); if( px <= cx-tolerance ) return FALSE; if( py <= cy-tolerance ) return FALSE; gdisplay_transform_coords(GIMP_TOOL(iscissors)->gdisp, curve->bounds_x1, curve->bounds_y1, &cx, &cy, FALSE); if( px >= cx+tolerance ) return FALSE; if( py >= cy+tolerance ) return FALSE; /* check curve */ param.iscissors = iscissors; param.x = x; param.y = y; param.tolerance = tolerance; param.result = FALSE; curve_traverse( curve, curve_contains_callback, ¶m ); return param.result; } gboolean curve_contains_callback(Curve *curve, CurveLink *link, gpointer data) { struct CurveContainsParam { GimpIscissorsTool *iscissors; gdouble x, y; gint tolerance; gboolean result; } *param; param = (struct CurveContainsParam*) data; if( gimp_draw_tool_in_radius( GIMP_DRAW_TOOL(param->iscissors), GIMP_TOOL(param->iscissors)->gdisp, link->x, link->y, param->x, param->y, param->tolerance ) ) param->result = TRUE; return !param->result; } /* if both curves have a point in common, both curves will be truncated at that point */ /* (c1-seed and c2-free is moved to intersection) */ /* returns TRUE if curves are modified */ struct CurveFindIntersectCallbackParam { Curve *c1; gint index1, index2; gint x, y; gboolean found; }; gboolean curve_find_intersect_callback1(Curve *c1, CurveLink *, gpointer data); gboolean curve_find_intersect_callback2(Curve *c2, CurveLink *, gpointer data); gboolean curve_find_intersect(Curve *c1, Curve *c2) { struct CurveFindIntersectCallbackParam param; g_assert( c1 ); g_assert( c2 ); /* first check bounding boxes */ if( c1->bounds_x1 > c2->bounds_x2 || c1->bounds_x2 < c2->bounds_x1 || c1->bounds_y1 > c2->bounds_y2 || c1->bounds_y2 < c2->bounds_y1 ) return FALSE; /* start searching with the free point of c2 */ param.c1 = c1; param.index2 = -1; param.found = FALSE; curve_traverse( c2, curve_find_intersect_callback2, ¶m ); if( !param.found ) return FALSE; /* FIXME: have a look at corner cases */ curve_truncate_at_seed( c1, param.index1, param.x, param.y ); curve_truncate_at_free( c2, param.index2, param.x, param.y ); return TRUE; } gboolean curve_find_intersect_callback2(Curve *c2, CurveLink *link, gpointer data) { /* outer loop */ struct CurveFindIntersectCallbackParam *param = (struct CurveFindIntersectCallbackParam *) data; param->index2++; param->index1 = -1; if( link->x < param->c1->bounds_x1 || link->y < param->c1->bounds_y1 || link->x > param->c1->bounds_x2 || link->y > param->c1->bounds_y2 ) return TRUE; /* this point of c2 is within c1's bounding box */ param->x = link->x; param->y = link->y; curve_traverse( param->c1, curve_find_intersect_callback1, param ); return !param->found; } gboolean curve_find_intersect_callback1(Curve *c1, CurveLink *link, gpointer data) { /* inner loop */ struct CurveFindIntersectCallbackParam *param = (struct CurveFindIntersectCallbackParam *) data; param->index1++; if( link->x==param->x && link->y==param->y ) param->found = TRUE; return !param->found; } /* draw one curve segment to the screen */ static gboolean curve_draw_callback( Curve *curve, CurveLink *, gpointer data ); inline void curve_draw(Curve *curve, GimpDrawTool *draw_tool) { curve_traverse( curve, curve_draw_callback, draw_tool ); } static gboolean curve_draw_callback( Curve *curve, CurveLink *link, gpointer data ) { gint x2, y2; GimpDrawTool *draw_tool = (GimpDrawTool*) data; g_assert( draw_tool ); x2 = link->x; y2 = link->y; curve_proceed( link->dir, &x2, &y2 ); gimp_draw_tool_draw_line( draw_tool, link->x, link->y, x2, y2, FALSE); return TRUE; } /* get a list of all the points on that curve */ gboolean curve_get_points_callback(Curve *curve, CurveLink *link, gpointer data); void curve_get_points(Curve *curve, gint *len, GimpVector2 **points) { GimpVector2 *p; check_pointer( curve ); *len = curve->curve->len; p = g_new( GimpVector2, *len ); *points = p; curve_traverse( curve, curve_get_points_callback, &p ); p->x = curve->seed_x; p->y = curve->seed_y; } gboolean curve_get_points_callback(Curve *curve, CurveLink *link, gpointer data) { GimpVector2 **param = (GimpVector2**) data; (*param)->x = link->x; (*param)->y = link->y; (*param)++; return TRUE; } /***************************************************************************/ /* build up a 2 dimensional array */ gpointer* array2d_new(gint w, gint h, gsize el_size) { int i; guint8 *pp; gpointer *p; /* alloc one big matrix holding all the data */ pp = g_new( guint8, w * h * el_size ); check_pointer( pp ); /* build a pointer array pointing into that matrix */ p = g_new( gpointer, h ); check_pointer( p ); for( i=0; i<h; i++ ) p[i] = pp + i * w*el_size; return p; } /* free such an array */ void array2d_free(gpointer *p) { g_free( *p ); g_free( p ); } /***************************************************************************/ /* GradientInput: store all the information needed for gradient based * cost function */ GradientInput * gradientinput_new() { GradientInput *gi; gi = g_new0( GradientInput, 1 ); gi->width = gi->height = -1; return gi; } void gradientinput_free( GradientInput *gi ) { if( gi->magnitude ) array2d_free( (gpointer*) gi->magnitude ); if( gi->direction ) array2d_free( (gpointer*) gi->direction ); g_free( gi ); } typedef struct _TmpGradient TmpGradient; struct _TmpGradient { GimpImage *image; GimpImageType type; gint **smooth_x, **smooth_y; gint **gradient_x, **gradient_y; gint width, height; }; static /*inline*/ void /* performance critical */ _convolve_smooth_tile(TmpGradient *tg, Tile *tile, gint tile_x, gint tile_y) { gint x, y; gint x0, y0; gint *smooth_x; gint *smooth_y1, *smooth_y2, *smooth_y3; guchar col[3]; gpointer src; /* leave out one pixel at the border */ x0 = tile_x? 0 : 1; y0 = tile_y? 0 : 1; for( y=y0; y<tile_eheight(tile)-1; y++ ) { smooth_x = &tg->smooth_x[tile_y+y][(tile_x+x0-1)*3]; smooth_y1 = &tg->smooth_y[tile_y+y-1][(tile_x+x0)*3]; smooth_y2 = &tg->smooth_y[tile_y+y+0][(tile_x+x0)*3]; smooth_y3 = &tg->smooth_y[tile_y+y+1][(tile_x+x0)*3]; for( x=x0; x<tile_ewidth(tile)-1; x++ ) { /* push one pixel into the convolved image */ src = tile_data_pointer( tile, x, y ); gimp_image_get_color( tg->image, tg->type, col, src ); /* smooth convolution filter: {+1, +2, +1} */ *smooth_x++ += col[0]; /* (tile_x+x-1, tile_y+y) */ *smooth_x++ += col[1]; *smooth_x++ += col[2]; *smooth_x++ += col[0]<<1; /* (tile_x+x, tile_y+y) */ *smooth_x++ += col[1]<<1; *smooth_x++ += col[2]<<1; *smooth_x++ += col[0]; /* (tile_x+x+1, tile_y+y) */ *smooth_x++ += col[1]; *smooth_x++ += col[2]; smooth_x -= 6; /* go to the pixel to the left of the next one */ *smooth_y1++ += col[0]; /* (tile_x+x, tile_y+y-1) */ *smooth_y1++ += col[1]; *smooth_y1++ += col[2]; *smooth_y2++ += col[0]<<1; /* (tile_x+x, tile_y+y) */ *smooth_y2++ += col[1]<<1; *smooth_y2++ += col[2]<<1; *smooth_y3++ += col[0]; /* (tile_x+x, tile_y+y+1) */ *smooth_y3++ += col[1]; *smooth_y3++ += col[2]; } } } static /*inline*/ void /* performance critical */ _convolve_smooth_tile_fast(TmpGradient *tg, Tile *tile, gint tile_x, gint tile_y) { gint x, y; gint *smooth_x; gint *smooth_y1, *smooth_y2, *smooth_y3; guchar col[3]; gpointer src; for( y=0; y<TILE_HEIGHT; y++ ) { smooth_x = &tg->smooth_x[tile_y+y][(tile_x-1)*3]; smooth_y1 = &tg->smooth_y[tile_y+y-1][tile_x*3]; smooth_y2 = &tg->smooth_y[tile_y+y+0][tile_x*3]; smooth_y3 = &tg->smooth_y[tile_y+y+1][tile_x*3]; for( x=0; x<TILE_WIDTH; x++ ) { /* push one pixel into the convolved image */ src = tile_data_pointer( tile, x, y ); gimp_image_get_color( tg->image, tg->type, col, src ); /* smooth convolution filter: {+1, +2, +1} */ *smooth_x++ += col[0]; *smooth_x++ += col[1]; *smooth_x++ += col[2]; *smooth_x++ += col[0]<<1; *smooth_x++ += col[1]<<1; *smooth_x++ += col[2]<<1; *smooth_x++ += col[0]; *smooth_x++ += col[1]; *smooth_x++ += col[2]; smooth_x -= 6; /* go to the pixel to the left of the next one */ *smooth_y1++ += col[0]; *smooth_y1++ += col[1]; *smooth_y1++ += col[2]; *smooth_y2++ += col[0]<<1; *smooth_y2++ += col[1]<<1; *smooth_y2++ += col[2]<<1; *smooth_y3++ += col[0]; *smooth_y3++ += col[1]; *smooth_y3++ += col[2]; } } } static /*inline*/ void /* performance critical */ _convolve_gradient(TmpGradient *tg) { gint i; gint *gradient; gint *src_1, *src_2; /* calculate gradient in x-direction */ gradient = &tg->gradient_x[1][1*3]; src_1 = &tg->smooth_y[1][0*3]; src_2 = &tg->smooth_y[1][2*3]; for( i=(tg->height-2)*tg->width -2; i; i-- ) { /* gradient convolution filter: {+1, 0, -1} */ *gradient++ = *src_1++ - *src_2++; *gradient++ = *src_1++ - *src_2++; *gradient++ = *src_1++ - *src_2++; /* (3 color channels) */ } /* calculate gradient in y-direction */ gradient = &tg->gradient_y[1][1*3]; src_1 = &tg->smooth_x[0][1*3]; src_2 = &tg->smooth_x[2][1*3]; for( i=(tg->height-2)*tg->width -2; i; i-- ) { /* gradient convolution filter: {+1, 0, -1} */ *gradient++ = *src_1++ - *src_2++; *gradient++ = *src_1++ - *src_2++; *gradient++ = *src_1++ - *src_2++; /* (3 color channels) */ } /* the borders contain crap now */ } /* fast integer square root */ #define integer_sqrt_iter(N) \ try = root + (1 << (N)); \ if (n >= try << (N)) { \ n -= try << (N); \ root |= 2 << (N); \ } guint32 /* performance critical */ integer_sqrt(guint32 n) { guint32 root = 0, try; integer_sqrt_iter(15); integer_sqrt_iter(14); integer_sqrt_iter(13); integer_sqrt_iter(12); integer_sqrt_iter(11); integer_sqrt_iter(10); integer_sqrt_iter( 9); integer_sqrt_iter( 8); integer_sqrt_iter( 7); integer_sqrt_iter( 6); integer_sqrt_iter( 5); integer_sqrt_iter( 4); integer_sqrt_iter( 3); integer_sqrt_iter( 2); integer_sqrt_iter( 1); integer_sqrt_iter( 0); return root >> 1; } /* update all the gradients with data from the image */ void gradientinput_set_image(GradientInput *gi, GimpImage *image) { TmpGradient tg; Tile *tile; gint x, y; gint margin = 1; /* XXX */ check_pointer( gi ); g_assert( image ); gi->image = image; if( gi->width!=gimp_image_get_width(image) || gi->height!=gimp_image_get_height(image) ) { /* realloc memory for cache */ if( gi->magnitude ) array2d_free( (gpointer*) gi->magnitude ); if( gi->direction ) array2d_free( (gpointer*) gi->direction ); gi->width = gimp_image_get_width(image); gi->height = gimp_image_get_height(image); g_assert( gi->width >= 4 ); g_assert( gi->height >= 4 ); gi->magnitude = (gint**) array2d_new( gi->width, gi->height, sizeof(**gi->magnitude) ); gi->direction = (gint**) array2d_new( gi->width, gi->height, sizeof(**gi->direction) ); } /* temp. memory for storage of gradients */ tg.image = gi->image; tg.type = gimp_image_projection_type(gi->image); tg.width = gi->width; tg.height = gi->height; tg.smooth_x = (gint**) array2d_new( tg.width, tg.height, sizeof(gint)*3 ); tg.smooth_y = (gint**) array2d_new( tg.width, tg.height, sizeof(gint)*3 ); tg.gradient_x = (gint**) array2d_new( tg.width, tg.height, sizeof(gint)*3 ); tg.gradient_y = (gint**) array2d_new( tg.width, tg.height, sizeof(gint)*3 ); /* calculate horizontal and vertical gradients */ for( y=0; y<gi->height; y+=TILE_HEIGHT ) { for( x=0; x<gi->width; x+=TILE_WIDTH ) { /* walk all tiles and add to convolution */ tile = tile_manager_get_tile( gimp_image_projection(image), x, y, TRUE, FALSE); if( x==0 || y==0 || x+TILE_WIDTH+margin>=gi->width || y+TILE_HEIGHT+margin>=gi->height ) { /* on the borders, we use the checking version */ _convolve_smooth_tile(&tg, tile, x, y ); } else { /* in the middle, we use the fast one */ _convolve_smooth_tile_fast(&tg, tile, x, y ); } } } _convolve_gradient(&tg); gi->max_magnitude = 0; /* get image data */ for( y=1; y<gi->height-1; y++ ) for( x=1; x<gi->width-1; x++ ) { gint mag, dir, c, g_x, g_y; mag = g_x = g_y = 0; /* search for the color channel with the strongest magnitude */ for( c=0; c<3; c++ ) { gint g_cx, g_cy, cmag; g_cx = tg.gradient_x[y][3*x+c]; g_cy = tg.gradient_y[y][3*x+c]; /* calculate gradient magnitude */ cmag = integer_sqrt( g_cx*g_cx + g_cy*g_cy ); if( cmag > mag ) { g_x = g_cx; g_y = g_cy; mag = cmag; } } gi->magnitude[y][x] = mag; if( mag > gi->max_magnitude ) gi->max_magnitude = mag; /* calculate gradient direction */ /* FIXME: test it! integer version?! */ dir = (MAX_COST/G_PI/2.0) * atan2( g_y, g_x ); if( dir<0 ) dir+= MAX_COST; gi->direction[y][x] = dir; } g_printerr( "gradientinput_set_image: max_magnitude=%d\n", gi->max_magnitude ); /* normalize gradient magnitude */ for( y=0; y<gi->height; y++ ) for( x=0; x<gi->width; x++ ) { gi->magnitude[y][x] = gi->magnitude[y][x] * MAX_COST / gi->max_magnitude; } array2d_free( (gpointer*) tg.smooth_x ); array2d_free( (gpointer*) tg.smooth_y ); array2d_free( (gpointer*) tg.gradient_x ); array2d_free( (gpointer*) tg.gradient_y ); } inline gint /* performance critical */ gradientinput_get_magnitude(GradientInput *gi, gint x, gint y) { return( gi->magnitude[y][x] ); } inline gint /* performance critical */ gradientinput_get_direction(GradientInput *gi, gint x, gint y) { return( gi->direction[y][x] ); } /***************************************************************************/ /* abstract class for cost functions (one component of the local link costs) */ void costfunction_free(CostFunction *map) { check_pointer( map ); /* leave input, has to be deleted explicitly */ g_free( map->data ); g_free( map ); } /* if cost function is dependent on the last selected curve, update it */ inline void costfunction_new_livewire(CostFunction *map, Curve *last) { if( map->new_livewire ) map->new_livewire(map, last); } inline gint /* performance critical */ costfunction_get_cost(CostFunction *map, CurveLink *link) { /* has to be always defined, no check (no branch->fast) */ return( map->get_cost(map, link) ); } /***************************************************************************/ gint gradientmag_get_cost(CostFunction *cost, CurveLink *link); gint gradientdir_get_cost(CostFunction *cost, CurveLink *link); /* GradientMag: derived from CostFunction */ /* strong gradients have low costs */ CostFunction * gradientmag_new(GradientInput *gi) { CostFunction *cost; cost = g_new0( CostFunction, 1 ); cost->get_cost = gradientmag_get_cost; cost->input = gi; return cost; } gint /* performance critical */ gradientmag_get_cost(CostFunction *cost, CurveLink *link) { GradientInput *gi = (GradientInput*) cost->input; return MAX_COST - gradientinput_get_magnitude( gi, link->x, link->y ); } /* GradientDir: derived from CostFunction */ /* gradients orthogonal to link directions have low costs */ /* TODO: implement! */ CostFunction * gradientdir_new(GradientInput *gi) { CostFunction *cost; cost = g_new0( CostFunction, 1 ); cost->get_cost = gradientdir_get_cost; cost->input = gi; return cost; } gint /* performance critical */ gradientdir_get_cost(CostFunction *cost, CurveLink *link) { gint x1, y1, x2, y2; guint8 dir; guint8 alpha1, alpha2; GradientInput *gi = (GradientInput*) cost->input; dir = link->dir; x1 = x2 = link->x; y1 = y2 = link->y; curve_proceed( dir, &x2, &y2 ); g_assert( x2>=0 && y2>=0 && x2<gi->width && y2<gi->height ); dir += 2; dir %= 8; /* orthogonal */ dir *= 32; /* scale to 0..255 */ /* calculate angle between directions */ if( gradientinput_get_magnitude( gi, x1, y1 ) < MAX_COST/8 ) alpha1 = 63; else alpha1 = gradientinput_get_direction( gi, x1, y1 ) - dir; if( gradientinput_get_magnitude( gi, x2, y2 ) < MAX_COST/8 ) alpha2 = 127; else alpha2 = gradientinput_get_direction( gi, x2, y2 ) - dir; #ifdef SLOW if( alpha1<0 ) alpha1 += 256; if( alpha1 >= 128 ) alpha1 = 255-alpha1; if( alpha1 >= 64 ) { /* angle too big, flip dir */ alpha1 -= 128; alpha2 -= 128; } if( alpha1<0 ) alpha1 += 256; while(alpha2<0) alpha2 += 256; if( alpha2 >= 128 ) alpha2 = 255-alpha2; /* maximum alpha1 is 63, max. alpha2 is 128 */ return (alpha1 + alpha2) * 4 / 3; #else /* same as above */ if( alpha1 & 128 ) alpha1 ^= 255; if( alpha1 & 192 ) { alpha1 ^= 128; alpha2 ^= 128; } if( alpha2 & 128 ) alpha2 ^= 255; return alpha1 + alpha2; #endif } /***************************************************************************/ /* DynamicMap derived from CostFunction: */ /* gets trained to previous values, which get low costs */ /* one really needs to be able to disable this function on the fly, * as it has problems with various images */ gint dynamicmap_get_cost_none(CostFunction *map, CurveLink *link); gint dynamicmap_get_cost(CostFunction *map, CurveLink *link); void dynamicmap_new_livewire(CostFunction *map, Curve *last); gint dynamicmap_build_histogram(CostFunction *map, Curve *curve); CostFunction * dynamicmap_new(CostFunction *input) { CostFunction *cost; cost = g_new0( CostFunction, 1 ); /* at first, don't return any costs unless we got trained */ cost->get_cost = dynamicmap_get_cost_none; cost->new_livewire = dynamicmap_new_livewire; cost->input = input; return cost; } /* this function gets called whenever we don't have trained costs available */ gint /* performance critical */ dynamicmap_get_cost_none(CostFunction *cost, CurveLink *link) { return costfunction_get_cost( (CostFunction*)cost->input, link ); } /* calculate trained costs */ gint /* performance critical */ dynamicmap_get_cost(CostFunction *cost, CurveLink *link) { guint *map; map = (guint*) cost->data; return map[ costfunction_get_cost( (CostFunction*)cost->input, link ) ]; } /* train based on the last curve selected */ void dynamicmap_new_livewire(CostFunction *cost, Curve *last) { gint histmax, i; guint *map; check_pointer( cost ); g_assert( cost->input ); g_free( cost->data ); cost->data = NULL; if( !last ) { /* reset training */ cost->get_cost = dynamicmap_get_cost_none; return; } /* train the cost mapping */ cost->data = map = g_new0( guint, MAX_COST+1 ); histmax = dynamicmap_build_histogram(cost, last); /* normize and invert histogram */ for( i=0; i<MAX_COST; i++ ) { map[i] = MAX_COST - MAX_COST*map[i]/histmax; g_printerr( "%d ", map[i] ); } g_printerr( "\n" ); /* activate trained costs */ cost->get_cost = dynamicmap_get_cost; } /* build histogram of the function along the curve in map, return maximal count */ struct _DynamicMapBuildHistogramParam { CostFunction *cost; gint weight; gint max; }; gboolean dynamicmap_build_histogram_callback(Curve *curve, CurveLink *link, gpointer data); gint dynamicmap_build_histogram(CostFunction *cost, Curve *curve) { struct _DynamicMapBuildHistogramParam param; check_pointer( cost ); check_pointer( curve ); param.cost = cost; param.weight = 32; /* use the last 32 pixels */ param.max = 0; curve_traverse( curve, dynamicmap_build_histogram_callback, ¶m ); return param.max; } gboolean dynamicmap_build_histogram_callback(Curve *curve, CurveLink *link, gpointer data) { static const gint smooth[] = { 1, 2, 5, 7, 9, 10, 9, 7, 5, 2, 1 }; static const gint smooth_size = (sizeof(smooth) / sizeof(smooth[0])); int value; struct _DynamicMapBuildHistogramParam *param = (struct _DynamicMapBuildHistogramParam*) data; guint *map; gint i, v; value = costfunction_get_cost( (CostFunction*) param->cost->input, link ); g_assert( value>=0 && value<=MAX_COST ); /* add the current weight to the histogram */ map = (guint*) param->cost->data; for( v=value-smooth_size/2, i=0; v<=value+smooth_size/2; v++, i++ ) { if( v<0 || v>MAX_COST ) continue; map[v] += smooth[i] * param->weight; if( map[v]>param->max ) param->max=map[v]; } /* adjust weight */ param->weight--; return param->weight>0; } /***************************************************************************/ /* NULL is considered an empty list */ inline CostList * costlist_new() { return NULL; } void costlist_free(CostList *costs) { CostList *next; while( costs ) { next = costs->next; costfunction_free( costs->cost ); g_free( costs ); costs = next; } } /* add a new weighted cost component */ CostList * costlist_add(CostList *costs, CostFunction *cf, gint weight) { CostList *c; c = g_new(CostList, 1); c->cost = cf; c->weight = weight; c->next = costs; return c; } /* update all cost components with data from the last curve */ void costlist_new_livewire(CostList *costs, Curve *last) { while( costs ) { costfunction_new_livewire( costs->cost, last ); costs = costs->next; } } /* diagonal links are sqrt(2) more expensive */ static const gint scale_diag[2] ={ 1414, 1000 }; /* get the overall weighted costs */ gint costlist_get(CostList *costs, CurveLink *link) { gint cost; gint sum, sum_weight; sum = sum_weight = 0; while( costs!=NULL ) { cost = costfunction_get_cost( costs->cost, link ); if( cost>=0 ) { sum += cost * costs->weight; sum_weight += costs->weight; } costs = costs->next; } return sum * 1000 / scale_diag[link->dir%2] / sum_weight; } /***************************************************************************/ static const gint BUCKET_SORT_SIZE = (MAX_COST + 1); /* a sorted list of coordinates * they are sorted according to their cumulative costs to the current seedpoint. * as elements with lowest costs are removed, and new elements have a cost difference * of less than MAX_COST, they can be sorted in O(1) via MAX_COST buckets, * each holding all coordinates with the exact same cost */ typedef struct _CoordStack CoordStack; struct _CoordStack { gint x, y; CoordStack *next; }; struct _BucketSort { GPtrArray *buckets; gint pos, size; }; /* create new buckets for sorting */ BucketSort * bucketsort_new() { BucketSort *bsort = g_new( BucketSort, 1 ); bsort->buckets = g_ptr_array_sized_new( BUCKET_SORT_SIZE ); g_ptr_array_set_size( bsort->buckets, BUCKET_SORT_SIZE ); bsort->pos = 0; bsort->size = 0; return bsort; } void bucketsort_free(BucketSort *bsort) { check_pointer( bsort ); bucketsort_remove_all( bsort ); g_ptr_array_free( bsort->buckets, TRUE ); g_free( bsort ); } /* flush the entire list */ void bucketsort_remove_all(BucketSort *bsort) { int i; CoordStack *stack, *next; check_pointer( bsort ); g_assert( bsort->buckets ); for( i=0; i<BUCKET_SORT_SIZE; i++ ) { stack = (CoordStack*) g_ptr_array_index( bsort->buckets, i ); g_ptr_array_index( bsort->buckets, i ) = NULL; while( stack!=NULL ) { next = stack->next; g_free( stack ); stack = next; } } bsort->pos = 0; bsort->size = 0; } /* test if there are elements in the list */ gboolean bucketsort_is_empty(BucketSort *bsort) { check_pointer( bsort ); return( bsort->size==0 ); } /* add a new element, value MUST NOT be greater than * the lowest value in the list plus MAX_COST */ void bucketsort_push(BucketSort *bsort, gint x, gint y, gint value) { CoordStack *coord; CoordStack **stack; check_pointer( bsort ); stack = (CoordStack**) &g_ptr_array_index( bsort->buckets, value % BUCKET_SORT_SIZE ); coord = g_new( CoordStack, 1 ); coord->x = x; coord->y = y; coord->next = *stack; *stack = coord; bsort->size++; } /* get the coordinate with the lowest costs and removes it from list */ void bucketsort_pop(BucketSort *bsort, gint *x, gint *y) { CoordStack *coord; CoordStack **stack; check_pointer( bsort ); g_assert( bsort->size > 0 ); stack = (CoordStack**) &g_ptr_array_index( bsort->buckets, bsort->pos ); while( *stack==NULL ) { bsort->pos = (bsort->pos+1) % BUCKET_SORT_SIZE; stack = (CoordStack**) &g_ptr_array_index( bsort->buckets, bsort->pos ); } coord = *stack; *stack = coord->next; *x = coord->x; *y = coord->y; g_free( coord ); bsort->size--; } /***************************************************************************/ /* PathSearch: calculate spanning tree and curves */ static gboolean pathsearch_dowork(gpointer data); static void pathsearch_startwork(PathSearch *psearch); static void pathsearch_stopwork(PathSearch *psearch); /* allocate new PathSearch */ PathSearch* pathsearch_new(CostList *costlist) { PathSearch *psearch; psearch = g_new0( PathSearch, 1 ); psearch->costs = costlist; return( psearch ); } /* start with background work */ inline static void pathsearch_startwork(PathSearch *psearch) { check_pointer( psearch ); check_pointer( psearch->tree_cum_cost ); check_pointer( psearch->tree_dir ); check_pointer( psearch->active ); g_assert( !bucketsort_is_empty(psearch->active) ); if( psearch->working ) return; psearch->work_tag = g_idle_add( pathsearch_dowork, psearch ); psearch->working = TRUE; } /* stop background work */ inline static void pathsearch_stopwork(PathSearch *psearch) { check_pointer( psearch ); if( !psearch->working ) return; psearch->working = FALSE; g_source_remove( psearch->work_tag ); } void pathsearch_free(PathSearch *psearch) { g_assert( psearch ); pathsearch_stop( psearch ); costlist_free( psearch->costs ); bucketsort_free( psearch->active ); g_free( psearch ); } /* stop with path generation and abandon all cached data */ void pathsearch_stop(PathSearch *psearch) { check_pointer( psearch ); pathsearch_stopwork( psearch ); if( psearch->tree_cum_cost ) array2d_free( (gpointer*) psearch->tree_cum_cost ); if( psearch->tree_dir ) array2d_free( (gpointer*) psearch->tree_dir ); if( psearch->active ) bucketsort_free( psearch->active ); psearch->tree_cum_cost = NULL; psearch->tree_dir = NULL; psearch->active = NULL; costlist_new_livewire( psearch->costs, NULL ); } /* use new image for path calculation */ /* must be called before trying to use the path generator */ void pathsearch_set_image(PathSearch *psearch, GimpImage *image) { check_pointer( psearch ); pathsearch_stop( psearch ); psearch->width = gimp_image_get_width(image); psearch->height = gimp_image_get_height(image); } /* set the seed point of all generated paths */ /* must be called before pathsearch_generate and after pathsearch_set_image */ void pathsearch_set_seed(PathSearch *psearch, gint x, gint y, Curve *last) { check_pointer( psearch ); g_assert( x>=0 && x<psearch->width ); g_assert( y>=0 && y<psearch->height ); pathsearch_stopwork( psearch ); psearch->seed_x = x; psearch->seed_y = y; costlist_new_livewire( psearch->costs, last ); /* create new structures for the spanning tree */ if( !psearch->tree_cum_cost ) psearch->tree_cum_cost = (gint**) array2d_new( psearch->width+1, psearch->height+1, sizeof(gint) ); if( !psearch->tree_dir ) psearch->tree_dir = (guint8**) array2d_new( psearch->width+1, psearch->height+1, sizeof(guint8) ); for( y=0; y<psearch->height; y++ ) for( x=0; x<psearch->width; x++ ) { psearch->tree_cum_cost[y][x] = G_MAXINT; psearch->tree_dir[y][x] = DIR_NONE; } psearch->tree_cum_cost[psearch->seed_y][psearch->seed_x] = 0; /* mark new seed point as the only active one */ if( psearch->active ) bucketsort_remove_all( psearch->active ); else psearch->active = bucketsort_new(); bucketsort_push( psearch->active, psearch->seed_x, psearch->seed_y, 0 ); pathsearch_startwork( psearch ); } /* calculate a path to the seed point */ Curve * pathsearch_generate(PathSearch *psearch, gint x, gint y) { Curve *curve; guint8 dir; check_pointer( psearch ); check_pointer( psearch->tree_dir ); g_assert( x>=0 && x<psearch->width ); g_assert( y>=0 && y<psearch->height ); curve = curve_new(); curve_set_free( curve, x, y ); if( x==psearch->seed_x && y==psearch->seed_y ) return curve; dir = psearch->tree_dir[y][x]; if( dir == DIR_NONE ) return NULL; while( x!=psearch->seed_x || y!=psearch->seed_y ) { curve_move_seed( curve, dir ); curve_proceed( dir, &x, &y ); dir = psearch->tree_dir[y][x]; } #ifdef DEBUG if( psearch->tree_cum_cost ) { CurveLink link; link.x = curve->free_x; link.y = curve->free_y; link.dir = 1; g_printerr("pathsearch_generate: x=%d y=%d len=%d lcost=%d cost=%d\n", curve->free_x, curve->free_y, curve_length(curve), costlist_get(psearch->costs, &link), psearch->tree_cum_cost[curve->free_y][curve->free_x] ); } else { CurveLink link; link.x = curve->free_x; link.y = curve->free_y; link.dir = 1; g_printerr("pathsearch_generate: x=%d y=%d len=%d lcost=%d\n", curve->free_x, curve->free_y, curve_length(curve), costlist_get(psearch->costs, &link) ); } #endif return curve; } /* work on the spanning tree * every time this function is called, a bit of the tree * is generated */ static gboolean pathsearch_dowork(gpointer data) { CurveLink link; gint count; gint x, y, cumcost; PathSearch *psearch = (PathSearch*) data; check_pointer( psearch ); check_pointer( psearch->tree_cum_cost ); check_pointer( psearch->tree_dir ); g_assert( psearch->working ); count = 0; while( !bucketsort_is_empty(psearch->active) ) { /* get a newly evaluated pixel */ bucketsort_pop( psearch->active, &x, &y ); cumcost = psearch->tree_cum_cost[y][x]; /* and work with it's neighbors */ for( link.dir=0; link.dir<8; link.dir++ ) { gint nc, cost; link.x = x; link.y = y; curve_proceed( curve_reverse( link.dir ), &link.x, &link.y ); if( link.x<0 || link.y<0 || link.x>=psearch->width || link.y>=psearch->height ) continue; nc = psearch->tree_cum_cost[link.y][link.x]; cost = cumcost + costlist_get(psearch->costs, &link); if( cost >= nc ) continue; /* we found a better path */ psearch->tree_cum_cost[link.y][link.x] = cost; psearch->tree_dir[link.y][link.x] = link.dir; /* mark that pixel for future work */ bucketsort_push( psearch->active, link.x, link.y, cost ); } /* only do some iterations here, come back after processing other events */ if( ++count > 1000 ) return TRUE; } /* done, free the now obsolete data */ costlist_new_livewire( psearch->costs, NULL ); /* XXX: needed? */ array2d_free( (gpointer*) psearch->tree_cum_cost ); bucketsort_free( psearch->active ); psearch->tree_cum_cost = NULL; psearch->active = NULL; /* don't call this function again */ psearch->working = FALSE; return FALSE; } /***************************************************************************/ /* LiveWire: high level stuff: maintain a list of curves */ LiveWire * livewire_new() { LiveWire *lw; CostList *costs; CostFunction *cost; lw = g_new0( LiveWire, 1 ); costs = costlist_new(); /* initialize cost functions */ lw->grad_input = gradientinput_new(); cost = gradientmag_new( lw->grad_input ); costs = costlist_add( costs, cost, 2 ); cost = dynamicmap_new( cost ); costs = costlist_add( costs, cost, 1 ); cost = gradientdir_new( lw->grad_input ); costs = costlist_add( costs, cost, 2 ); lw->psearch = pathsearch_new(costs); return lw; } void livewire_free(LiveWire *lw) { g_assert( lw ); livewire_reset( lw ); g_free( lw ); } /* use this image for live wire */ void livewire_set_image(LiveWire *lw, GimpImage *image) { check_pointer( lw ); pathsearch_set_image( lw->psearch, image ); gradientinput_set_image( lw->grad_input, image ); } /* start selecting an object */ void livewire_start(LiveWire *lw, gint x, gint y) { check_pointer( lw ); g_assert( lw->wire==NULL ); livewire_reset( lw ); pathsearch_set_seed( lw->psearch, x, y, NULL ); /* lw->wire will always hold the current wire, with aging info */ lw->wire = curve_new(); curve_add_age( lw->wire ); } /* move the end of the live wire */ void livewire_move(LiveWire *lw, gint x, gint y) { Curve *curve; check_pointer( lw ); check_pointer( lw->wire ); if( x==lw->wire->free_x && y==lw->wire->free_y ) return; curve = pathsearch_generate( lw->psearch, x, y ); if( curve == NULL ) return; curve_replace( lw->wire, curve ); /* path cooling: auto-generate new seed points? */ if( curve_check_age( lw->wire, lw->grad_input ) ) { /* curve already got truncated */ livewire_confirm( lw ); } } /* the current live wire is ok, freeze it and start a new one from here */ void livewire_confirm(LiveWire *lw) { Curve *first; check_pointer( lw ); check_pointer( lw->wire ); if( curve_length(lw->wire) == 0 ) return; g_printerr( "livewire_confirm()\n" ); curve_drop_age( lw->wire ); pathsearch_set_seed( lw->psearch, lw->wire->free_x, lw->wire->free_y, lw->wire ); /* lw->curves is in oposite order */ lw->curves = g_list_prepend( lw->curves, lw->wire ); first = (Curve*) g_list_last(lw->curves)->data; if( lw->wire!=first && curve_find_intersect( lw->wire, first ) ) { lw->wire = NULL; livewire_done( lw ); } else { /* new live wire, with new age information */ lw->wire = curve_new(); curve_add_age( lw->wire ); } } /* last confirm was wrong, undo it */ void livewire_undo(LiveWire *lw) { check_pointer( lw ); g_assert( lw->curves ); g_printerr( "livewire_undo()\n" ); curve_free( lw->wire ); lw->wire = (Curve*) lw->curves->data; lw->curves = g_list_remove( lw->curves, lw->wire ); pathsearch_set_seed( lw->psearch, lw->wire->seed_x, lw->wire->seed_y, lw->curves->data ); } /* curve segments are closed now */ void livewire_done(LiveWire *lw) { check_pointer( lw ); g_printerr( "livewire_done()\n" ); lw->closed = TRUE; pathsearch_stop( lw->psearch ); } /* restart */ void livewire_reset(LiveWire *lw) { check_pointer( lw ); pathsearch_stop( lw->psearch ); g_free( lw->wire ); lw->wire = NULL; while( lw->curves ) { curve_free( (Curve*) lw->curves->data ); lw->curves = g_list_delete_link( lw->curves, lw->curves ); } lw->curves = NULL; } /* return TRUE if it is already closed */ gboolean livewire_closed(LiveWire *lw) { check_pointer( lw ); return lw->closed; }
/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __GIMP_ISISSORSTOOL_LIVEWIRE_H__ #define __GIMP_ISISSORSTOOL_LIVEWIRE_H__ #include <glib.h> #include <glib-object.h> #include <gtk/gtk.h> #include "core/core-types.h" #include "tools-types.h" #include "core/gimpimage.h" #include "display/gimpdisplay.h" #include "gimpiscissorstool.h" /* definition of classes */ typedef struct _Curve Curve; typedef struct _CurveLink CurveLink; typedef struct _AgeEntry AgeEntry; typedef struct _GradientInput GradientInput; typedef struct _CostFunction CostFunction; typedef struct _CostList CostList; typedef struct _BucketSort BucketSort; typedef struct _PathSearch PathSearch; /*typedef struct _LiveWire LiveWire;*/ /***************************************************************************/ /* CurveLink: position and direction of a link between two pixels */ struct _CurveLink { gint x, y; guint8 dir; }; /***************************************************************************/ /* Curves: store one path segment between two seed points */ struct _Curve { /* start and end point */ gint free_x, free_y; gint seed_x, seed_y; /* the actual data */ GByteArray *curve; GArray *age; /* optional */ /* bounding box of the curve */ gint bounds_x1, bounds_x2; gint bounds_y1, bounds_y2; }; static const guint8 DIR_NONE = 8; typedef gboolean (*CurveTraversalCallback)(Curve*, CurveLink *, gpointer); Curve* curve_new(); void curve_free(Curve *curve); void curve_set_free(Curve *curve, gint x, gint y); void curve_move_seed(Curve *curve, guint8 dir); static inline gint curve_length(Curve *curve) {return curve->curve->len;} void curve_traverse(Curve *curve, CurveTraversalCallback callback, gpointer data); gboolean curve_has_age(Curve *curve); void curve_add_age(Curve *curve); void curve_drop_age(Curve *curve); void curve_replace(Curve *target, Curve *source); gboolean curve_check_age(Curve *curve, GradientInput *gi); void curve_truncate_at_seed(Curve *curve, gint index, gint x, gint y); void curve_truncate_at_free(Curve *curve, gint index, gint x, gint y); gboolean curve_contains(Curve *curve, GimpIscissorsTool *iscissors, gdouble x, gdouble y, gint tolerance); gboolean curve_find_intersect(Curve *c1, Curve *c2); void curve_get_points(Curve *curve, gint *len, GimpVector2 **points); void curve_draw(Curve *curve, GimpDrawTool *draw_tool); void curve_proceed(guint8 dir, gint *x, gint *y); /***************************************************************************/ struct _GradientInput { GimpImage *image; gint width, height; gint max_magnitude; gint **magnitude; gint **direction; }; GradientInput * gradientinput_new(); void gradientinput_set_image(GradientInput *gi, GimpImage *image); inline gint gradientinput_get_magnitude(GradientInput *gi, gint x, gint y); inline gint gradientinput_get_direction(GradientInput *gi, gint x, gint y); /***************************************************************************/ /* CostFunction: abstract class to calculate local costs */ struct _CostFunction { /* overrideable functions: */ gint (*get_cost)(CostFunction *map, CurveLink *link); void (*new_livewire)(CostFunction *map, Curve *last); /* pointer to our input object */ gpointer input; gpointer data; }; void costfunction_free(CostFunction *cfunc); void costfunction_new_livewire(CostFunction *map, Curve *last); gint costfunction_get_cost(CostFunction *cfunc, CurveLink *link); /***************************************************************************/ /* CostList: weighted costs */ struct _CostList { CostFunction *cost; gint weight; CostList *next; }; CostList * costlist_new(); void costlist_free(CostList *costs); CostList * costlist_add(CostList *costs, CostFunction *map, gint weight); void costlist_set_image(CostList *costs, GimpImage *image); void costlist_new_livewire(CostList *costs, Curve *last); gint costlist_get(CostList *costs, CurveLink *link); /***************************************************************************/ /* BucketSort: sorted list of active points */ BucketSort * bucketsort_new(); void bucketsort_free(BucketSort *bsort); void bucketsort_remove_all(BucketSort *bsort); gboolean bucketsort_is_empty(BucketSort *bsort); void bucketsort_push(BucketSort *bsort, gint x, gint y, gint value); void bucketsort_pop(BucketSort *bsort, gint *x, gint *y); /***************************************************************************/ /* PathSearch: maintain a spanning tree of optimal paths */ struct _PathSearch { CostList *costs; /* list of costs */ gint seed_x, seed_y; /* seed point for new curves */ Curve *last_curve; /* last curve ending at that seed point, if any */ gint width, height; /* the spanning tree */ gint **tree_cum_cost; guint8 **tree_dir; /* for background work: */ BucketSort *active; gboolean working; guint work_tag; }; PathSearch* pathsearch_new(); void pathsearch_free(PathSearch *psearch); void pathsearch_stop(PathSearch *psearch); void pathsearch_set_image(PathSearch *psearch, GimpImage *image); void pathsearch_set_seed(PathSearch *psearch, gint x, gint y, Curve *last); Curve * pathsearch_generate(PathSearch *psearch, gint x, gint y); /***************************************************************************/ struct _LiveWire { PathSearch *psearch; GradientInput *grad_input; CostList *costs; /* list of costs */ GList *curves; Curve *wire; gboolean closed; }; LiveWire * livewire_new(); void livewire_free(LiveWire *lw); void livewire_set_image(LiveWire *lw, GimpImage *image); void livewire_start(LiveWire *lw, gint x, gint y); void livewire_move(LiveWire *lw, gint x, gint y); void livewire_confirm(LiveWire *lw); void livewire_undo(LiveWire *lw); void livewire_done(LiveWire *lw); void livewire_reset(LiveWire *lw); gboolean livewire_closed(LiveWire *lw); #endif
? .gimpiscissorstool.c.swp ? gimpiscissorstool-livewire.c ? gimpiscissorstool-livewire.h Index: Makefile.am =================================================================== RCS file: /cvs/gnome/gimp/app/tools/Makefile.am,v retrieving revision 1.52 diff -u -p -r1.52 Makefile.am --- Makefile.am 2002/02/08 07:51:16 1.52 +++ Makefile.am 2002/02/21 23:10:06 @@ -60,6 +60,8 @@ libapptools_a_SOURCES = @STRIP_BEGIN@ \ gimpinktool-blob.h \ gimpiscissorstool.c \ gimpiscissorstool.h \ + gimpiscissorstool-livewire.c \ + gimpiscissorstool-livewire.h \ gimplevelstool.c \ gimplevelstool.h \ gimpmagnifytool.c \ Index: gimpiscissorstool.c =================================================================== RCS file: /cvs/gnome/gimp/app/tools/gimpiscissorstool.c,v retrieving revision 1.108 diff -u -p -r1.108 gimpiscissorstool.c --- gimpiscissorstool.c 2002/02/05 11:35:02 1.108 +++ gimpiscissorstool.c 2002/02/21 23:10:08 @@ -29,11 +29,14 @@ * bore little resemblance to the one described in the paper above. * The 0.54 version of the algorithm was then forwards ported to 1.1.4 * by Austin Donnelly. + * first Livewire boundary implementation done by Laramie Leavitt. + * + * The code was completely redesigned by Martin Waitz for 1.3, + * based on another paper by Mortensen and Barrett: + * http://www.cs.orst.edu/~enm/publications/GMIP_98/seg_scissors.html */ -/* Livewire boundary implementation done by Laramie Leavitt */ - #include "config.h" #include <stdlib.h> @@ -65,9 +68,10 @@ #include "display/gimpdisplay-foreach.h" #include "gimpbezierselecttool.h" -#include "gimpiscissorstool.h" #include "gimpeditselectiontool.h" #include "selection_options.h" +#include "gimpiscissorstool.h" +#include "gimpiscissorstool-livewire.h" #include "libgimp/gimpintl.h" @@ -83,8 +87,6 @@ #define FIXED 5 /* additional fixed size to expand cost map */ #define MIN_GRADIENT 63 /* gradients < this are directionless */ -#define MAX_POINTS 2048 - #define COST_WIDTH 2 /* number of bytes for each pixel in cost map */ #define BLOCK_WIDTH 64 #define BLOCK_HEIGHT 64 @@ -102,14 +104,9 @@ #define PIXEL_COST(x) (x >> 8) #define PIXEL_DIR(x) (x & 0x000000ff) - -struct _ICurve -{ - gint x1, y1; - gint x2, y2; - GPtrArray *points; -}; +/***************************************************************************/ +/* OLD */ /* local function prototypes */ @@ -166,9 +163,7 @@ static void find_max_gradient gint *x, gint *y); static void calculate_curve (GimpTool *tool, - ICurve *curve); -static void iscissors_draw_curve (GimpDrawTool *draw_tool, - ICurve *curve); + Curve *curve); static void iscissors_free_icurves (GSList *list); static void iscissors_free_buffers (GimpIscissorsTool *iscissors); @@ -194,7 +189,8 @@ static GPtrArray * plot_pixels /* static variables */ -/* where to move on a given link direction */ +/* where to move on a given link direction */ +#if 0 static gint move[8][2] = { { 1, 0 }, @@ -206,6 +202,7 @@ static gint move[8][2] = { 1, -1 }, { -1, -1 }, }; +#endif /* IE: * '---+---+---` @@ -333,12 +330,16 @@ gimp_iscissors_tool_init (GimpIscissorsT tool = GIMP_TOOL (iscissors); iscissors->op = -1; +#if 0 iscissors->curves = NULL; iscissors->dp_buf = NULL; - iscissors->state = NO_ACTION; - iscissors->mask = NULL; iscissors->gradient_map = NULL; iscissors->livewire = NULL; +#else + iscissors->livewire = livewire_new(); +#endif + iscissors->state = NO_ACTION; + iscissors->mask = NULL; tool->tool_cursor = GIMP_SCISSORS_TOOL_CURSOR; @@ -355,6 +356,7 @@ gimp_iscissors_tool_finalize (GObject *o iscissors = GIMP_ISCISSORS_TOOL (object); gimp_iscissors_tool_reset (iscissors); + livewire_free( iscissors->livewire ); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -427,11 +429,13 @@ gimp_iscissors_tool_button_press (GimpTo /* If the tool was being used in another image...reset it */ - if (tool->state == ACTIVE && gdisp != tool->gdisp) - { - gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool)); - gimp_iscissors_tool_reset (iscissors); - } + if (gdisp != tool->gdisp) { + if ( tool->state == ACTIVE ) { + gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool)); + gimp_iscissors_tool_reset (iscissors); + } + livewire_set_image( iscissors->livewire, gdisp->gimage ); + } tool->state = ACTIVE; tool->gdisp = gdisp; @@ -451,14 +455,19 @@ gimp_iscissors_tool_button_press (GimpTo iscissors->x = CLAMP (iscissors->x, 0, gdisp->gimage->width - 1); iscissors->y = CLAMP (iscissors->y, 0, gdisp->gimage->height - 1); +#if 0 iscissors->ix = iscissors->x; iscissors->iy = iscissors->y; +#endif + + livewire_start( iscissors->livewire, iscissors->x, iscissors->y ); /* Initialize the selection core only on starting the tool */ gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), gdisp); break; default: +#if 0 /* Check if the mouse click occured on a vertex or the curve itself */ if (clicked_on_vertex (tool)) { @@ -498,6 +507,7 @@ gimp_iscissors_tool_button_press (GimpTo } /* if we're not connected, we're adding a new point */ else if (! iscissors->connected) +#endif { iscissors->state = SEED_PLACEMENT; iscissors->draw = DRAW_CURRENT_SEED; @@ -512,50 +522,33 @@ gimp_iscissors_tool_button_press (GimpTo } +// create a new channel mask using our curves static void iscissors_convert (GimpIscissorsTool *iscissors, GimpDisplay *gdisp) { - GimpScanConvert *sc; - GimpVector2 *points; - guint n_points; - GSList *list; - ICurve *icurve; - guint packed; - gint i; - gint index; - - sc = gimp_scan_convert_new (gdisp->gimage->width, gdisp->gimage->height, 1); + GList *list; + GimpScanConvert *sc; + GimpVector2 *points; + guint n_points; - /* go over the curves in reverse order, adding the points we have */ - list = iscissors->curves; - index = g_slist_length (list); - while (index) - { - index--; - icurve = (ICurve *) g_slist_nth_data (list, index); - - n_points = icurve->points->len; - points = g_new (GimpVector2, n_points); + sc = gimp_scan_convert_new (gdisp->gimage->width, gdisp->gimage->height, 1); - for (i = 0; i < n_points; i ++) - { - packed = GPOINTER_TO_INT (g_ptr_array_index (icurve->points, i)); - points[i].x = packed & 0x0000ffff; - points[i].y = packed >> 16; + /* FIXME: move to livewire_ */ + list = iscissors->livewire->curves; + while( list ) { + curve_get_points( (Curve*) list->data, &n_points, &points ); + gimp_scan_convert_add_points( sc, n_points, points ); + g_free( points ); } - - gimp_scan_convert_add_points (sc, n_points, points); - g_free (points); - } - if (iscissors->mask) - g_object_unref (G_OBJECT (iscissors->mask)); + if (iscissors->mask) + g_object_unref (G_OBJECT (iscissors->mask)); - iscissors->mask = gimp_scan_convert_to_channel (sc, gdisp->gimage); - gimp_scan_convert_free (sc); + iscissors->mask = gimp_scan_convert_to_channel (sc, gdisp->gimage); + gimp_scan_convert_free (sc); - gimp_channel_invalidate_bounds (iscissors->mask); + gimp_channel_invalidate_bounds (iscissors->mask); } static void @@ -567,12 +560,12 @@ gimp_iscissors_tool_button_release (Gimp { GimpIscissorsTool *iscissors; SelectionOptions *sel_options; - ICurve *curve; iscissors = GIMP_ISCISSORS_TOOL (tool); sel_options = (SelectionOptions *) tool->tool_info->tool_options; + g_printerr( "gimp_iscissors_tool_button_release: state=%d\n", iscissors->state); /* Make sure X didn't skip the button release event -- as it's known * to do */ @@ -596,7 +589,7 @@ gimp_iscissors_tool_button_release (Gimp break; } - gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool)); + //gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool)); /* First take care of the case where the user "cancels" the action */ if (! (state & GDK_BUTTON3_MASK)) @@ -605,6 +598,7 @@ gimp_iscissors_tool_button_release (Gimp switch (iscissors->state) { case SEED_PLACEMENT: +#if 0 /* Add a new icurve */ if (!iscissors->first_point) { @@ -640,8 +634,11 @@ gimp_iscissors_tool_button_release (Gimp { iscissors->first_point = FALSE; } +#else + livewire_confirm( iscissors->livewire ); +#endif break; - +#if 0 case SEED_ADJUSTMENT: /* recalculate both curves */ if (iscissors->curve1) @@ -657,7 +654,7 @@ gimp_iscissors_tool_button_release (Gimp calculate_curve (tool, iscissors->curve2); } break; - +#endif default: break; } @@ -667,11 +664,13 @@ gimp_iscissors_tool_button_release (Gimp iscissors->state = WAITING; iscissors->draw = DRAW_CURVE; - gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); + gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); - /* convert the curves into a region */ - if (iscissors->connected) - iscissors_convert (iscissors, gdisp); + /* convert the curves into a region */ + if( livewire_closed( iscissors->livewire ) ) { + iscissors_convert( iscissors, gdisp ); + iscissors->state = NO_ACTION; + } } static void @@ -708,6 +707,7 @@ gimp_iscissors_tool_motion (GimpTool iscissors->x = coords->x; iscissors->y = coords->y; +#if 0 switch (iscissors->state) { case SEED_PLACEMENT: @@ -742,6 +742,7 @@ gimp_iscissors_tool_motion (GimpTool default: break; } +#endif gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); } @@ -752,8 +753,8 @@ gimp_iscissors_tool_draw (GimpDrawTool * GimpTool *tool; GimpIscissorsTool *iscissors; GimpDisplay *gdisp; - ICurve *curve; - GSList *list; + Curve *curve; + GList *list; tool = GIMP_TOOL (draw_tool); iscissors = GIMP_ISCISSORS_TOOL (draw_tool); @@ -773,6 +774,7 @@ gimp_iscissors_tool_draw (GimpDrawTool * FALSE); /* Draw a line boundary */ +#if 0 if (! iscissors->first_point && ! (iscissors->draw & DRAW_LIVEWIRE)) { gimp_draw_tool_draw_line (draw_tool, @@ -780,8 +782,12 @@ gimp_iscissors_tool_draw (GimpDrawTool * iscissors->x, iscissors->y, FALSE); } +#endif } + livewire_move( iscissors->livewire, iscissors->x, iscissors->y ); + +#if 0 /* Draw the livewire boundary */ if ((iscissors->draw & DRAW_LIVEWIRE) && ! iscissors->first_point) { @@ -793,6 +799,7 @@ gimp_iscissors_tool_draw (GimpDrawTool * iscissors->iy != iscissors->livewire->y1 || iscissors->y != iscissors->livewire->y2))) { + curve = g_new (ICurve, 1); curve->x1 = iscissors->ix; @@ -815,12 +822,17 @@ gimp_iscissors_tool_draw (GimpDrawTool * calculate_curve (tool, curve); curve = NULL; } - - /* plot the curve */ - iscissors_draw_curve (draw_tool, iscissors->livewire); - } - - if ((iscissors->draw & DRAW_CURVE) && ! iscissors->first_point) +#endif + /* plot the curve */ + if( iscissors->livewire->wire ) + curve_draw( iscissors->livewire->wire, draw_tool ); +#if 0 + } +#endif + + if( iscissors->livewire->curves ) { +#if 0 + if ((iscissors->draw & DRAW_CURVE) && iscissors->livewire->curves ) { /* Draw a point at the init point coordinates */ if (! iscissors->connected) @@ -834,26 +846,27 @@ gimp_iscissors_tool_draw (GimpDrawTool * GTK_ANCHOR_CENTER, FALSE); } +#endif /* Go through the list of icurves, and render each one... */ - for (list = iscissors->curves; list; list = g_slist_next (list)) + for (list = iscissors->livewire->curves; list; list = g_list_next (list)) { - curve = (ICurve *) list->data; + curve = (Curve *) list->data; - /* plot the curve */ - iscissors_draw_curve (draw_tool, curve); + /* plot the curve_ */ + curve_draw( curve, draw_tool ); gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_CIRCLE, - curve->x1, - curve->y1, + curve->seed_x, + curve->seed_y, POINT_WIDTH, POINT_WIDTH, GTK_ANCHOR_CENTER, FALSE); } } - +#if 0 if (iscissors->draw & DRAW_ACTIVE_CURVE) { /* plot both curves, and the control point between them */ @@ -885,23 +898,26 @@ gimp_iscissors_tool_draw (GimpDrawTool * GTK_ANCHOR_CENTER, FALSE); } +#endif } +#if 0 +// draw one curve segment to the screen static void iscissors_draw_curve (GimpDrawTool *draw_tool, ICurve *curve) { gpointer *point; guint len; - gint npts = 0; + gint n_pts = 0; guint32 coords; guint32 coords_2; /* Uh, this shouldn't happen, but it does. So we ignore it. * Quality code, baby. */ - if (! curve->points) + if (! curve->coords ) return; point = curve->points->pdata + 1; @@ -930,6 +946,7 @@ iscissors_draw_curve (GimpDrawTool *draw } } } +#endif static void gimp_iscissors_tool_oper_update (GimpTool *tool, @@ -949,7 +966,7 @@ gimp_iscissors_tool_oper_update (GimpToo { iscissors->op = SELECTION_MOVE; /* abused */ } - else if (iscissors->connected && iscissors->mask && + else if (livewire_closed(iscissors->livewire) && iscissors->mask && gimp_channel_value (iscissors->mask, coords->x, coords->y)) { if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK)) @@ -969,7 +986,7 @@ gimp_iscissors_tool_oper_update (GimpToo iscissors->op = SELECTION_REPLACE; } } - else if (iscissors->connected && iscissors->mask) + else if (livewire_closed(iscissors->livewire) && iscissors->mask) { iscissors->op = -1; } @@ -1039,9 +1056,12 @@ gimp_iscissors_tool_cursor_update (GimpT } +/***************************************************************************/ + static void gimp_iscissors_tool_reset (GimpIscissorsTool *iscissors) { +#if 0 /* Free and reset the curve list */ if (iscissors->curves) { @@ -1049,6 +1069,9 @@ gimp_iscissors_tool_reset (GimpIscissors g_slist_free (iscissors->curves); iscissors->curves = NULL; } +#endif + + livewire_reset( iscissors->livewire ); /* free mask */ if (iscissors->mask) @@ -1056,7 +1079,7 @@ gimp_iscissors_tool_reset (GimpIscissors g_object_unref (G_OBJECT (iscissors->mask)); iscissors->mask = NULL; } - +#if 0 /* free the gradient map */ if (iscissors->gradient_map) { @@ -1075,10 +1098,12 @@ gimp_iscissors_tool_reset (GimpIscissors iscissors->curve2 = NULL; iscissors->first_point = TRUE; iscissors->connected = FALSE; +#endif iscissors->state = NO_ACTION; - +#if 0 /* Reset the dp buffers */ iscissors_free_buffers (iscissors); +#endif /* If they haven't already been initialized, precalculate the diagonal * weight and direction value arrays @@ -1094,15 +1119,12 @@ gimp_iscissors_tool_reset (GimpIscissors static void iscissors_free_icurves (GSList *list) { - ICurve * curve; + Curve * curve; while (list) { - curve = (ICurve *) list->data; - if (curve->points) - g_ptr_array_free (curve->points, TRUE); - - g_free (curve); + curve = (Curve *) list->data; + curve_free( curve ); list = g_slist_next (list); } } @@ -1111,10 +1133,12 @@ iscissors_free_icurves (GSList *list) static void iscissors_free_buffers (GimpIscissorsTool *iscissors) { +#if 0 if (iscissors->dp_buf) temp_buf_free (iscissors->dp_buf); iscissors->dp_buf = NULL; +#endif } @@ -1125,6 +1149,7 @@ mouse_over_vertex (GimpIscissorsTool *is gdouble x, gdouble y) { +#if 0 GSList *list; ICurve *curve; gint curves_found = 0; @@ -1133,7 +1158,6 @@ mouse_over_vertex (GimpIscissorsTool *is * position is on an existing curve vertex. Set the curve1 and curve2 * variables to the two curves containing the vertex in question */ - iscissors->curve1 = iscissors->curve2 = NULL; list = iscissors->curves; @@ -1160,7 +1184,7 @@ mouse_over_vertex (GimpIscissorsTool *is GIMP_TOOL (iscissors)->gdisp, x, y, GIMP_HANDLE_CIRCLE, - curve->x2, curve->y2, + curve->x1, curve->y1, POINT_WIDTH, POINT_WIDTH, GTK_ANCHOR_CENTER, FALSE)) @@ -1173,8 +1197,9 @@ mouse_over_vertex (GimpIscissorsTool *is list = g_slist_next (list); } - return curves_found; +#endif + return 0; } static gboolean @@ -1210,6 +1235,7 @@ mouse_over_curve (GimpIscissorsTool *isc gdouble x, gdouble y) { +#if 0 GSList *list; gpointer *pt; gint len; @@ -1220,7 +1246,6 @@ mouse_over_curve (GimpIscissorsTool *isc /* traverse through the list, returning the curve segment's list element * if the current cursor position is on a curve... */ - for (list = iscissors->curves; list; list = g_slist_next (list)) { curve = (ICurve *) list->data; @@ -1245,7 +1270,7 @@ mouse_over_curve (GimpIscissorsTool *isc } } } - +#endif return NULL; } @@ -1254,7 +1279,7 @@ clicked_on_curve (GimpTool *tool) { GimpIscissorsTool *iscissors; GSList *list, *new_link; - ICurve *curve, *new_curve; + Curve *curve, *new_curve; iscissors = GIMP_ISCISSORS_TOOL (tool); @@ -1267,6 +1292,7 @@ clicked_on_curve (GimpTool *tool) if (list) { +#if 0 /* temporarily disabled for live wire redesign */ curve = (ICurve *) list->data; /* Since we're modifying the curve, undraw the existing one */ @@ -1297,6 +1323,7 @@ clicked_on_curve (GimpTool *tool) gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); return TRUE; +#endif } return FALSE; @@ -1326,8 +1353,8 @@ precalculate_arrays (void) direction_value[255][2] = 255; direction_value[255][3] = 255; } - +#if 0 static void calculate_curve (GimpTool *tool, ICurve *curve) @@ -1440,7 +1467,7 @@ calculate_curve (GimpTool *tool, } } } - +#endif /* badly need to get a replacement - this is _way_ too expensive */ static gboolean @@ -1515,6 +1542,7 @@ calculate_link (TileManager *gradient_ma } +#if 0 static GPtrArray * plot_pixels (GimpIscissorsTool *iscissors, TempBuf *dp_buf, @@ -1559,6 +1587,7 @@ plot_pixels (GimpIscissorsTool *iscissor /* won't get here */ return NULL; } +#endif #define PACK(x, y) ((((y) & 0xff) << 8) | ((x) & 0xff)) Index: gimpiscissorstool.h =================================================================== RCS file: /cvs/gnome/gimp/app/tools/gimpiscissorstool.h,v retrieving revision 1.12 diff -u -p -r1.12 gimpiscissorstool.h --- gimpiscissorstool.h 2001/12/03 13:44:56 1.12 +++ gimpiscissorstool.h 2002/02/21 23:10:08 @@ -44,9 +44,7 @@ typedef enum DRAW_ALL = (DRAW_CURRENT_SEED | DRAW_CURVE) } Iscissors_draw; -typedef struct _ICurve ICurve; - #define GIMP_TYPE_ISCISSORS_TOOL (gimp_iscissors_tool_get_type ()) #define GIMP_ISCISSORS_TOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ISCISSORS_TOOL, GimpIscissorsTool)) #define GIMP_ISCISSORS_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ISCISSORS_TOOL, GimpIscissorsToolClass)) @@ -55,6 +53,7 @@ typedef struct _ICurve ICurve; #define GIMP_ISCISSORS_TOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ISCISSORS_TOOL, GimpIscissorsToolClass)) +typedef struct _LiveWire LiveWire; typedef struct _GimpIscissorsTool GimpIscissorsTool; typedef struct _GimpIscissorsToolClass GimpIscissorsToolClass; @@ -63,7 +62,7 @@ struct _GimpIscissorsTool GimpDrawTool parent_instance; SelectOps op; - +#if 0 gint x, y; /* upper left hand coordinate */ gint ix, iy; /* initial coordinates */ gint nx, ny; /* new coordinates */ @@ -79,7 +78,10 @@ struct _GimpIscissorsTool gboolean first_point; /* is this the first point? */ gboolean connected; /* is the region closed? */ - +#else + gint x, y; /* mouse position: move the wire there on next redraw */ + LiveWire *livewire; +#endif Iscissors_state state; /* state of iscissors */ Iscissors_draw draw; /* items to draw on a draw request */
Attachment:
pgpWPM1E695B8.pgp
Description: PGP signature