fix up flood
This commit is contained in:
parent
5a05101a0a
commit
d71b6b754a
@ -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
9
TODO
@ -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
|
||||||
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user