fix up flood

This commit is contained in:
John Cupitt 2009-12-17 11:44:28 +00:00
parent 5a05101a0a
commit d71b6b754a
3 changed files with 167 additions and 101 deletions

View File

@ -9,6 +9,7 @@
- im_disp2Lab() was broken - im_disp2Lab() was broken
- deprecated.h is now defined in terms of current functionality, rather than - deprecated.h is now defined in terms of current functionality, rather than
repeating stuff repeating stuff
- im_flood() uses inline
26/11/09 started 7.20.3 26/11/09 started 7.20.3
- updated en_GB.po translation - updated en_GB.po translation

9
TODO
View File

@ -1,16 +1,9 @@
- some of deprecated.h repeats stuff we do better in image.h, eg. addr() - add a --without-cpp option?
should just call IM_REGION_ADDR()
- rename vipsCC in SWIG as pyvips? - rename vipsCC in SWIG as pyvips?
- im_flood*() should use inline rather than #define - im_flood*() should use inline rather than #define
- try the new liboil thing:
http://code.entropywave.com/projects/orc/
pick something like abs and time it
- look into G_GNUC_DEPRECATED for back compat in vips8 - look into G_GNUC_DEPRECATED for back compat in vips8
- use - use

View File

@ -1,8 +1,4 @@
/* int im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) /* flood-fill
*
* Flood fill from point (x,y) with colour ink. Flood up to boundary == ink.
* Any type, any number of bands, IM_CODING_LABQ too. Returns the bounding box
* of the modified pixels in dout, whether it succeeds or not.
* *
* Currently a rather inefficient pixel-based algorithm, should put something * Currently a rather inefficient pixel-based algorithm, should put something
* better in, really. Speed isn't likely to be a problem, except for very * better in, really. Speed isn't likely to be a problem, except for very
@ -22,6 +18,10 @@
* - added IM_CODING_RAD support * - added IM_CODING_RAD support
* 28/9/09 * 28/9/09
* - ooops, tiny memleak * - ooops, tiny memleak
* 17/12/09
* - use inline rather than defines, so we can add scanline fills more
* easily
* - gtk-doc comments
*/ */
/* /*
@ -114,7 +114,7 @@ typedef struct {
/* Alloc a new buffer. /* Alloc a new buffer.
*/ */
static Buffer * static Buffer *
build_buffer( void ) buffer_build( void )
{ {
Buffer *buf = IM_NEW( NULL, Buffer ); Buffer *buf = IM_NEW( NULL, Buffer );
@ -129,16 +129,71 @@ build_buffer( void )
/* Free a chain of buffers. /* Free a chain of buffers.
*/ */
static void static void
free_buffer( Buffer *buf ) buffer_free( Buffer *buf )
{ {
IM_FREE( buf->next ); IM_FREE( buf->next );
im_free( buf ); im_free( buf );
} }
/* Add an xy point to a buffer, appending a new buffer if necessary. Return
* the new tail buffer.
*/
static inline Buffer *
buffer_add( Buffer *buf, int x, int y )
{
/* buf can be NULL if we've had an error.
*/
if( !buf )
return( NULL );
buf->points[buf->n].x = x;
buf->points[buf->n].y = y;
buf->n++;
if( buf->n == PBUFSIZE ) {
if( !buf->next ) {
if( !(buf->next = buffer_build()) )
return( NULL );
}
buf = buf->next;
buf->n = 0;
}
return( buf );
}
static inline Buffer *
buffer_add_ifnotedge( Buffer *buf, State *st, PEL *p, int x, int y )
{
int j;
for( j = 0; j < st->ps; j++ )
if( p[j] != st->edge[j] ) {
buf = buffer_add( buf, x, y );
break;
}
return( buf );
}
static inline Buffer *
buffer_add_ifedge( Buffer *buf, State *st, PEL *p, int x, int y )
{
int j;
for( j = 0; j < st->ps; j++ )
if( p[j] != st->edge[j] )
break;
if( j == st->ps )
buf = buffer_add( buf, x, y );
return( buf );
}
/* Free a state. /* Free a state.
*/ */
static void static void
free_state( State *st ) state_free( State *st )
{ {
/* Write dirty back to caller. /* Write dirty back to caller.
*/ */
@ -149,15 +204,15 @@ free_state( State *st )
*/ */
IM_FREE( st->ink ); IM_FREE( st->ink );
IM_FREE( st->edge ); IM_FREE( st->edge );
IM_FREEF( free_buffer, st->buf1 ); IM_FREEF( buffer_free, st->buf1 );
IM_FREEF( free_buffer, st->buf2 ); IM_FREEF( buffer_free, st->buf2 );
im_free( st ); im_free( st );
} }
/* Build a state. /* Build a state.
*/ */
static State * static State *
build_state( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) state_build( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
{ {
State *st = IM_NEW( NULL, State ); State *st = IM_NEW( NULL, State );
@ -184,9 +239,9 @@ build_state( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
if( !(st->ink = (PEL *) im_malloc( NULL, st->ps )) || if( !(st->ink = (PEL *) im_malloc( NULL, st->ps )) ||
!(st->edge = (PEL *) im_malloc( NULL, st->ps )) || !(st->edge = (PEL *) im_malloc( NULL, st->ps )) ||
!(st->buf1 = build_buffer()) || !(st->buf1 = buffer_build()) ||
!(st->buf2 = build_buffer()) ) { !(st->buf2 = buffer_build()) ) {
free_state( st ); state_free( st );
return( NULL ); return( NULL );
} }
memcpy( st->ink, ink, st->ps ); memcpy( st->ink, ink, st->ps );
@ -194,46 +249,6 @@ build_state( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
return( st ); return( st );
} }
/* Add xy to buffer, move buffer on on overflow.
*/
#define ADD( BUF, X, Y ) { \
BUF->points[BUF->n].x = X; \
BUF->points[BUF->n].y = Y; \
BUF->n++; \
if( BUF->n == PBUFSIZE ) { \
if( !BUF->next ) { \
if( !(BUF->next = build_buffer()) ) \
return( -1 ); \
} \
BUF = BUF->next; \
BUF->n = 0; \
} \
}
/* If point != edge, add it to out.
*/
#define ADDIFNOTEDGE( P, X, Y ) { \
PEL *p1 = (P); \
\
for( j = 0; j < st->ps; j++ ) \
if( p1[j] != st->edge[j] ) { \
ADD( out, X, Y ); \
break; \
} \
}
/* If point == edge, add it to out.
*/
#define ADDIFEDGE( P, X, Y ) { \
PEL *p1 = (P); \
\
for( j = 0; j < st->ps; j++ ) \
if( p1[j] != st->edge[j] ) \
break; \
if( j == st->ps ) \
ADD( out, X, Y ); \
}
/* Read points to fill from in, write new points to out. /* Read points to fill from in, write new points to out.
*/ */
static int static int
@ -291,24 +306,37 @@ dofill( State *st, Buffer *in, Buffer *out )
*/ */
if( st->equal ) { if( st->equal ) {
if( x < st->right - 1 ) if( x < st->right - 1 )
ADDIFEDGE( p + st->ps, x + 1, y ); out = buffer_add_ifedge( out, st,
p + st->ps, x + 1, y );
if( x > st->left ) if( x > st->left )
ADDIFEDGE( p - st->ps, x - 1, y ); out = buffer_add_ifedge( out, st,
p - st->ps, x - 1, y );
if( y < st->bottom - 1 ) if( y < st->bottom - 1 )
ADDIFEDGE( p + st->ls, x, y + 1 ); out = buffer_add_ifedge( out, st,
p + st->ls, x, y + 1 );
if( y > st->top ) if( y > st->top )
ADDIFEDGE( p - st->ls, x, y - 1 ); out = buffer_add_ifedge( out, st,
p - st->ls, x, y - 1 );
} }
else { else {
if( x < st->right - 1 ) if( x < st->right - 1 )
ADDIFNOTEDGE( p + st->ps, x + 1, y ); out = buffer_add_ifnotedge( out, st,
p + st->ps, x + 1, y );
if( x > st->left ) if( x > st->left )
ADDIFNOTEDGE( p - st->ps, x - 1, y ); out = buffer_add_ifnotedge( out, st,
p - st->ps, x - 1, y );
if( y < st->bottom - 1 ) if( y < st->bottom - 1 )
ADDIFNOTEDGE( p + st->ls, x, y + 1 ); out = buffer_add_ifnotedge( out, st,
p + st->ls, x, y + 1 );
if( y > st->top ) if( y > st->top )
ADDIFNOTEDGE( p - st->ls, x, y - 1 ); out = buffer_add_ifnotedge( out, st,
p - st->ls, x, y - 1 );
} }
/* There was an error in one of the adds.
*/
if( !out )
return( -1 );
} }
if( in->n == PBUFSIZE ) if( in->n == PBUFSIZE )
@ -322,6 +350,28 @@ dofill( State *st, Buffer *in, Buffer *out )
return( 0 ); return( 0 );
} }
/**
* im_flood:
* @im: image to fill
* @x: position to start fill
* @y: position to start fill
* @ink: colour to fill with
* @dout: output the bounding box of the filled area
*
* Flood-fill @im with @ink, starting at position @x, @y. The filled area is
* bounded by pixels that are equal to the ink colour, in other words, it
* searches for pixels enclosed by a line of @ink.
*
* The bounding box of the modified pixels is returned in @dout.
*
* This an inplace operation, so @im is changed. It does not thread and will
* not work well as part of a pipeline. On 32-bit machines, it will be limited
* to 2GB images.
*
* See also: im_flood_blob(), im_flood_other(), im_flood_blob_copy().
*
* Returns: 0 on success, or -1 on error.
*/
int int
im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
{ {
@ -329,23 +379,16 @@ im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
Buffer *in, *out, *t; Buffer *in, *out, *t;
PEL *p; PEL *p;
if( im_rwcheck( im ) ) if( im_rwcheck( im ) ||
return( -1 ); im_check_known_coded( "im_flood", im ) )
if( im->Coding != IM_CODING_NONE && if( !(st = state_build( im, x, y, ink, dout )) )
im->Coding != IM_CODING_LABQ &&
im->Coding != IM_CODING_RAD ) {
im_error( "im_flood", "%s",
_( "Coding should be NONE, LABQ or RAD" ) );
return( -1 );
}
if( !(st = build_state( im, x, y, ink, dout )) )
return( -1 ); return( -1 );
/* Test start pixel ... nothing to do? /* Test start pixel ... nothing to do?
*/ */
p = (PEL *) im->data + x*st->ps + y*st->ls; p = (PEL *) im->data + x*st->ps + y*st->ls;
if( memcmp( p, ink, st->ps ) == 0 ) { if( memcmp( p, ink, st->ps ) == 0 ) {
free_state( st ); state_free( st );
return( 0 ); return( 0 );
} }
@ -356,21 +399,43 @@ im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
/* Add start pixel to the work buffer, and loop. /* Add start pixel to the work buffer, and loop.
*/ */
ADD( st->buf1, x, y ) st->buf1 = buffer_add( st->buf1, x, y );
for( in = st->buf1, out = st->buf2; for( in = st->buf1, out = st->buf2;
in->n > 0; t = in, in = out, out = t ) in->n > 0; t = in, in = out, out = t )
if( dofill( st, in, out ) ) { if( dofill( st, in, out ) ) {
free_state( st ); state_free( st );
return( -1 ); return( -1 );
} }
free_state( st ); state_free( st );
im_invalidate( im ); im_invalidate( im );
return( 0 ); return( 0 );
} }
/**
* im_flood_blob:
* @im: image to fill
* @x: position to start fill
* @y: position to start fill
* @ink: colour to fill with
* @dout: output the bounding box of the filled area
*
* Flood-fill @im with @ink, starting at position @x, @y. The filled area is
* bounded by pixels that are not equal to the colour of the start pixel, in
* other words, it searches for a blob of connected pixels of the same colour.
*
* The bounding box of the modified pixels is returned in @dout.
*
* This an inplace operation, so @im is changed. It does not thread and will
* not work well as part of a pipeline. On 32-bit machines, it will be limited
* to 2GB images.
*
* See also: im_flood(), im_flood_other(), im_flood_blob_copy().
*
* Returns: 0 on success, or -1 on error.
*/
int int
im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
{ {
@ -378,16 +443,10 @@ im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
Buffer *in, *out, *t; Buffer *in, *out, *t;
PEL *p; PEL *p;
if( im_rwcheck( im ) ) if( im_rwcheck( im ) ||
im_check_known_coded( "im_flood", im ) )
return( -1 ); return( -1 );
if( im->Coding != IM_CODING_NONE && if( !(st = state_build( im, x, y, ink, dout )) )
im->Coding != IM_CODING_LABQ &&
im->Coding != IM_CODING_RAD ) {
im_error( "im_flood", "%s",
_( "Coding should be NONE, LABQ or RAD" ) );
return( -1 );
}
if( !(st = build_state( im, x, y, ink, dout )) )
return( -1 ); return( -1 );
/* Edge is set by colour of start pixel. /* Edge is set by colour of start pixel.
@ -398,23 +457,38 @@ im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
/* Add start pixel to the work buffer, and loop. /* Add start pixel to the work buffer, and loop.
*/ */
ADD( st->buf1, x, y ) st->buf1 = buffer_add( st->buf1, x, y );
for( in = st->buf1, out = st->buf2; for( in = st->buf1, out = st->buf2;
in->n > 0; t = in, in = out, out = t ) in->n > 0; t = in, in = out, out = t )
if( dofill( st, in, out ) ) { if( dofill( st, in, out ) ) {
free_state( st ); state_free( st );
return( -1 ); return( -1 );
} }
free_state( st ); state_free( st );
im_invalidate( im ); im_invalidate( im );
return( 0 ); return( 0 );
} }
/* A Flood blob we can call from nip. Grr! Should be a way to wrap these /**
* automatically. Maybe nip could do it if it sees a RW image argument? * im_flood_blob_copy:
* @in: input image
* @out: output image
* @x: position to start fill
* @y: position to start fill
* @ink: colour to fill with
*
* Copy @in to a otemporary memory buffer, then flood-fill with @ink,
* starting at position @x, @y. The filled area is
* bounded by pixels that are not equal to the colour of the start pixel, in
* other words, it searches for a blob of connected pixels of the same colour.
* The temporary image is then copied to @out.
*
* See also: im_flood(), im_flood_other(), im_flood_blob_copy().
*
* Returns: 0 on success, or -1 on error.
*/ */
int int
im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink ) im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
@ -429,5 +503,3 @@ im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
return( 0 ); return( 0 );
} }