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
- deprecated.h is now defined in terms of current functionality, rather than
repeating stuff
- im_flood() uses inline
26/11/09 started 7.20.3
- 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()
should just call IM_REGION_ADDR()
- add a --without-cpp option?
- rename vipsCC in SWIG as pyvips?
- 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
- use

View File

@ -1,8 +1,4 @@
/* int im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
*
* 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.
/* flood-fill
*
* Currently a rather inefficient pixel-based algorithm, should put something
* better in, really. Speed isn't likely to be a problem, except for very
@ -22,6 +18,10 @@
* - added IM_CODING_RAD support
* 28/9/09
* - 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.
*/
static Buffer *
build_buffer( void )
buffer_build( void )
{
Buffer *buf = IM_NEW( NULL, Buffer );
@ -129,16 +129,71 @@ build_buffer( void )
/* Free a chain of buffers.
*/
static void
free_buffer( Buffer *buf )
buffer_free( Buffer *buf )
{
IM_FREE( buf->next );
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.
*/
static void
free_state( State *st )
state_free( State *st )
{
/* Write dirty back to caller.
*/
@ -149,15 +204,15 @@ free_state( State *st )
*/
IM_FREE( st->ink );
IM_FREE( st->edge );
IM_FREEF( free_buffer, st->buf1 );
IM_FREEF( free_buffer, st->buf2 );
IM_FREEF( buffer_free, st->buf1 );
IM_FREEF( buffer_free, st->buf2 );
im_free( st );
}
/* Build a 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 );
@ -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 )) ||
!(st->edge = (PEL *) im_malloc( NULL, st->ps )) ||
!(st->buf1 = build_buffer()) ||
!(st->buf2 = build_buffer()) ) {
free_state( st );
!(st->buf1 = buffer_build()) ||
!(st->buf2 = buffer_build()) ) {
state_free( st );
return( NULL );
}
memcpy( st->ink, ink, st->ps );
@ -194,46 +249,6 @@ build_state( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
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.
*/
static int
@ -291,24 +306,37 @@ dofill( State *st, Buffer *in, Buffer *out )
*/
if( st->equal ) {
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 )
ADDIFEDGE( p - st->ps, x - 1, y );
out = buffer_add_ifedge( out, st,
p - st->ps, x - 1, y );
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 )
ADDIFEDGE( p - st->ls, x, y - 1 );
out = buffer_add_ifedge( out, st,
p - st->ls, x, y - 1 );
}
else {
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 )
ADDIFNOTEDGE( p - st->ps, x - 1, y );
out = buffer_add_ifnotedge( out, st,
p - st->ps, x - 1, y );
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 )
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 )
@ -322,6 +350,28 @@ dofill( State *st, Buffer *in, Buffer *out )
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
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;
PEL *p;
if( im_rwcheck( im ) )
return( -1 );
if( im->Coding != IM_CODING_NONE &&
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 )) )
if( im_rwcheck( im ) ||
im_check_known_coded( "im_flood", im ) )
if( !(st = state_build( im, x, y, ink, dout )) )
return( -1 );
/* Test start pixel ... nothing to do?
*/
p = (PEL *) im->data + x*st->ps + y*st->ls;
if( memcmp( p, ink, st->ps ) == 0 ) {
free_state( st );
state_free( st );
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( st->buf1, x, y )
st->buf1 = buffer_add( st->buf1, x, y );
for( in = st->buf1, out = st->buf2;
in->n > 0; t = in, in = out, out = t )
if( dofill( st, in, out ) ) {
free_state( st );
state_free( st );
return( -1 );
}
free_state( st );
state_free( st );
im_invalidate( im );
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
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;
PEL *p;
if( im_rwcheck( im ) )
if( im_rwcheck( im ) ||
im_check_known_coded( "im_flood", im ) )
return( -1 );
if( im->Coding != IM_CODING_NONE &&
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 )) )
if( !(st = state_build( im, x, y, ink, dout )) )
return( -1 );
/* 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( st->buf1, x, y )
st->buf1 = buffer_add( st->buf1, x, y );
for( in = st->buf1, out = st->buf2;
in->n > 0; t = in, in = out, out = t )
if( dofill( st, in, out ) ) {
free_state( st );
state_free( st );
return( -1 );
}
free_state( st );
state_free( st );
im_invalidate( im );
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
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 );
}