new flood
This commit is contained in:
parent
94ec87fda8
commit
cb888b9765
@ -9,7 +9,7 @@
|
||||
- im_disp2Lab() was broken
|
||||
- deprecated.h is now defined in terms of current functionality, rather than
|
||||
repeating stuff
|
||||
- im_flood() uses inline
|
||||
- im_flood() and friends rewritten, typically 4x faster
|
||||
|
||||
26/11/09 started 7.20.3
|
||||
- updated en_GB.po translation
|
||||
|
2
TODO
2
TODO
@ -2,8 +2,6 @@
|
||||
|
||||
- rename vipsCC in SWIG as pyvips?
|
||||
|
||||
- im_flood*() should use inline rather than #define
|
||||
|
||||
- look into G_GNUC_DEPRECATED for back compat in vips8
|
||||
|
||||
- use
|
||||
|
@ -48,16 +48,28 @@ int im_fastlineuser( IMAGE *im,
|
||||
int x1, int y1, int x2, int y2,
|
||||
int (*fn)(), void *client1, void *client2, void *client3 );
|
||||
int im_readpoint( IMAGE *im, int x, int y, PEL *pel );
|
||||
|
||||
int im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout );
|
||||
int im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout );
|
||||
int im_flood_other( IMAGE *test, IMAGE *mark,
|
||||
int x, int y, int serial, Rect *dout );
|
||||
|
||||
int im_flood_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink );
|
||||
int im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink );
|
||||
int im_flood_other( IMAGE *mask, IMAGE *test, int x, int y, int serial );
|
||||
int im_flood_other_copy( IMAGE *mask, IMAGE *test, IMAGE *out,
|
||||
int im_flood_other_copy( IMAGE *test, IMAGE *mark, IMAGE *out,
|
||||
int x, int y, int serial );
|
||||
|
||||
int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink,
|
||||
int n, int *x1v, int *y1v, int *x2v, int *y2v );
|
||||
|
||||
int im_flood_old( IMAGE *im, int x, int y, PEL *ink, Rect *dout );
|
||||
int im_flood_blob_old( IMAGE *im, int x, int y, PEL *ink, Rect *dout );
|
||||
int im_flood_other_old( IMAGE *mask, IMAGE *test, int x, int y, int serial );
|
||||
int im_flood_copy_old( IMAGE *in, IMAGE *out, int x, int y, PEL *ink );
|
||||
int im_flood_blob_copy_old( IMAGE *in, IMAGE *out, int x, int y, PEL *ink );
|
||||
int im_flood_other_copy_old( IMAGE *mask, IMAGE *test, IMAGE *out,
|
||||
int x, int y, int serial );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
@ -4,7 +4,7 @@ libinplace_la_SOURCES = \
|
||||
im_circle.c \
|
||||
im_flood.c \
|
||||
im_flood_other.c \
|
||||
im_flood_new.c \
|
||||
flood.c \
|
||||
im_insertplace.c \
|
||||
im_paintrect.c \
|
||||
im_plotmask.c \
|
||||
|
572
libvips/inplace/flood.c
Normal file
572
libvips/inplace/flood.c
Normal file
@ -0,0 +1,572 @@
|
||||
/* 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
|
||||
* large images.
|
||||
*
|
||||
* JC 30/8/97
|
||||
* - VIPSified, cleaned up, from "John Robinson's prog to fill
|
||||
* enclosed areas"
|
||||
* - something Kirk gave me, so thanks John
|
||||
* JC 1/10/97
|
||||
* - swapped inner memcmp/cpy for a loop ... faster for small pixels
|
||||
* 13/7/02 JC
|
||||
* - im_flood_blob() added
|
||||
* 5/12/06
|
||||
* - im_invalidate() after paint
|
||||
* 24/3/09
|
||||
* - 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
|
||||
* 21/12/09
|
||||
* - rewrite for a scanline based fill, 4x faster!
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser 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
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
#define SWAP( TYPE, A, B ) { \
|
||||
TYPE t = (A); \
|
||||
(A) = (B); \
|
||||
(B) = t; \
|
||||
}
|
||||
|
||||
/* Size of a scanline buffer. We allocate a list of these to hold scanlines
|
||||
* we need to visit.
|
||||
*/
|
||||
#define PBUFSIZE (1000)
|
||||
|
||||
/* A scanline we know could contain pixels connected to us.
|
||||
*
|
||||
* Dir is the direction of connection: +1 means y is increasing, ie. the line
|
||||
* above this one is filled. -1 if y is decreasing.
|
||||
*/
|
||||
typedef struct {
|
||||
int x1, x2;
|
||||
int y;
|
||||
int dir;
|
||||
} Scan;
|
||||
|
||||
/* A buffer of scanlines, and how many of them have been used. If ->next is
|
||||
* non-NULL, the next block could contain more of them.
|
||||
*
|
||||
* We keep a pair of these, then loop over one and write to the other.
|
||||
*/
|
||||
typedef struct _Buffer {
|
||||
struct _Buffer *next;
|
||||
int n;
|
||||
Scan scan[PBUFSIZE];
|
||||
} Buffer;
|
||||
|
||||
/* Our state.
|
||||
*/
|
||||
typedef struct {
|
||||
/* Parameters.
|
||||
*/
|
||||
IMAGE *test; /* Test this image */
|
||||
IMAGE *mark; /* Mark this image */
|
||||
int x, y;
|
||||
PEL *ink; /* Copy of ink param */
|
||||
Rect *dout; /* Write dirty here at end */
|
||||
|
||||
/* Derived stuff.
|
||||
*/
|
||||
PEL *edge; /* Boundary colour */
|
||||
int equal; /* Fill to == edge, or != edge */
|
||||
int tsize; /* sizeof( one pel in test ) */
|
||||
int msize; /* sizeof( one pel in mark ) */
|
||||
int left, right; /* Record bounding box of modified pixels */
|
||||
int top, bottom;
|
||||
|
||||
/* Read from in, add new possibilities to out.
|
||||
*/
|
||||
Buffer *in;
|
||||
Buffer *out;
|
||||
} Flood;
|
||||
|
||||
/* Alloc a new buffer.
|
||||
*/
|
||||
static Buffer *
|
||||
buffer_build( void )
|
||||
{
|
||||
Buffer *buf = IM_NEW( NULL, Buffer );
|
||||
|
||||
if( !buf )
|
||||
return( NULL );
|
||||
buf->next = NULL;
|
||||
buf->n = 0;
|
||||
|
||||
return( buf );
|
||||
}
|
||||
|
||||
/* Free a chain of buffers.
|
||||
*/
|
||||
static void
|
||||
buffer_free( Buffer *buf )
|
||||
{
|
||||
while( buf ) {
|
||||
Buffer *p;
|
||||
|
||||
p = buf->next;
|
||||
im_free( buf );
|
||||
buf = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a scanline to a buffer, prepending a new buffer if necessary. Return
|
||||
* the new head buffer.
|
||||
*/
|
||||
static inline Buffer *
|
||||
buffer_add( Buffer *buf, Flood *flood, int x1, int x2, int y, int dir )
|
||||
{
|
||||
/* Clip against image size.
|
||||
*/
|
||||
if( y < 0 || y >= flood->test->Ysize )
|
||||
return( buf );
|
||||
x1 = IM_CLIP( 0, x1, flood->test->Xsize - 1 );
|
||||
x2 = IM_CLIP( 0, x2, flood->test->Xsize - 1 );
|
||||
if( x2 - x1 < 0 )
|
||||
return( buf );
|
||||
|
||||
buf->scan[buf->n].x1 = x1;
|
||||
buf->scan[buf->n].x2 = x2;
|
||||
buf->scan[buf->n].y = y;
|
||||
buf->scan[buf->n].dir = dir;
|
||||
buf->n += 1;
|
||||
|
||||
if( buf->n == PBUFSIZE ) {
|
||||
Buffer *new;
|
||||
|
||||
if( !(new = buffer_build()) )
|
||||
return( NULL );
|
||||
new->next = buf;
|
||||
buf = new;
|
||||
}
|
||||
|
||||
return( buf );
|
||||
}
|
||||
|
||||
/* Is p "connected"? ie. is equal to or not equal to flood->edge, depending on
|
||||
* whether we are flooding to the edge boundary or flooding edge-coloured
|
||||
* pixels.
|
||||
*/
|
||||
static inline gboolean
|
||||
flood_connected( Flood *flood, PEL *p )
|
||||
{
|
||||
int j;
|
||||
|
||||
for( j = 0; j < flood->tsize; j++ )
|
||||
if( p[j] != flood->edge[j] )
|
||||
break;
|
||||
|
||||
return( flood->equal ^ (j < flood->tsize) );
|
||||
}
|
||||
|
||||
/* Faster than memcpy for n < about 20.
|
||||
*/
|
||||
static inline void
|
||||
flood_paint( Flood *flood, PEL *q )
|
||||
{
|
||||
int j;
|
||||
|
||||
for( j = 0; j < flood->msize; j++ )
|
||||
q[j] = flood->ink[j];
|
||||
}
|
||||
|
||||
/* Fill left and right, return the endpoints. The start point (x, y) must be
|
||||
* connected.
|
||||
*/
|
||||
static void
|
||||
flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 )
|
||||
{
|
||||
const int width = flood->mark->Xsize;
|
||||
|
||||
PEL *tp;
|
||||
PEL *mp;
|
||||
int i;
|
||||
|
||||
g_assert( flood_connected( flood,
|
||||
(PEL *) IM_IMAGE_ADDR( flood->test, x, y ) ) );
|
||||
|
||||
/* Fill this pixel and to the right.
|
||||
*/
|
||||
tp = (PEL *) IM_IMAGE_ADDR( flood->test, x, y );
|
||||
mp = (PEL *) IM_IMAGE_ADDR( flood->mark, x, y );
|
||||
for( i = x; i < width && flood_connected( flood, tp ); i++ ) {
|
||||
flood_paint( flood, mp );
|
||||
tp += flood->tsize;
|
||||
mp += flood->msize;
|
||||
}
|
||||
*x2 = i - 1;
|
||||
|
||||
/* Fill to the left.
|
||||
*/
|
||||
tp = (PEL *) IM_IMAGE_ADDR( flood->test, x - 1, y );
|
||||
mp = (PEL *) IM_IMAGE_ADDR( flood->mark, x - 1, y );
|
||||
for( i = x - 1; i > 0 && flood_connected( flood, tp ); i-- ) {
|
||||
flood_paint( flood, mp );
|
||||
tp -= flood->tsize;
|
||||
mp -= flood->msize;
|
||||
}
|
||||
*x1 = i + 1;
|
||||
|
||||
if( flood->dout ) {
|
||||
flood->left = IM_MIN( flood->left, *x1 );
|
||||
flood->right = IM_MAX( flood->right, *x2 );
|
||||
flood->top = IM_MIN( flood->top, y );
|
||||
flood->bottom = IM_MAX( flood->bottom, y );
|
||||
}
|
||||
}
|
||||
|
||||
/* We know the line below or above us is filled between x1 and x2. Search our
|
||||
* line in this range looking for an edge pixel we can flood from.
|
||||
*/
|
||||
static void
|
||||
flood_around( Flood *flood, Scan *scan )
|
||||
{
|
||||
int x;
|
||||
|
||||
g_assert( scan->dir == 1 || scan->dir == -1 );
|
||||
|
||||
for( x = scan->x1; x <= scan->x2; x++ ) {
|
||||
PEL *p = (PEL *) IM_IMAGE_ADDR( flood->test, x, scan->y );
|
||||
|
||||
if( flood_connected( flood, p ) ) {
|
||||
int x1a;
|
||||
int x2a;
|
||||
|
||||
flood_scanline( flood, x, scan->y, &x1a, &x2a );
|
||||
|
||||
/* Our new scanline can have up to three more
|
||||
* scanlines connected to it: above, below left, below
|
||||
* right.
|
||||
*/
|
||||
if( x1a < scan->x1 - 1 )
|
||||
flood->out = buffer_add( flood->out, flood,
|
||||
x1a, scan->x1 - 1,
|
||||
scan->y - scan->dir, -scan->dir );
|
||||
if( x2a > scan->x2 + 1 )
|
||||
flood->out = buffer_add( flood->out, flood,
|
||||
scan->x2 + 1, x2a,
|
||||
scan->y - scan->dir, -scan->dir );
|
||||
flood->out = buffer_add( flood->out, flood,
|
||||
x1a, x2a, scan->y + scan->dir,
|
||||
scan->dir );
|
||||
|
||||
x = x2a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flood_buffer( Flood *flood, Buffer *buf )
|
||||
{
|
||||
Buffer *p;
|
||||
|
||||
for( p = buf; p; p = p->next ) {
|
||||
int i;
|
||||
|
||||
for( i = 0; i < p->n; i++ )
|
||||
flood_around( flood, &buf->scan[i] );
|
||||
|
||||
p->n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flood_all( Flood *flood, int x, int y )
|
||||
{
|
||||
/* Test start pixel ... nothing to do?
|
||||
*/
|
||||
if( flood_connected( flood,
|
||||
(PEL *) IM_IMAGE_ADDR( flood->test, x, y ) ) ) {
|
||||
int x1, x2;
|
||||
|
||||
flood_scanline( flood, x, y, &x1, &x2 );
|
||||
flood->in = buffer_add( flood->in, flood, x1, x2, y + 1, 1 );
|
||||
flood->in = buffer_add( flood->in, flood, x1, x2, y - 1, -1 );
|
||||
|
||||
while( flood->in->n ) {
|
||||
flood_buffer( flood, flood->in );
|
||||
|
||||
SWAP( Buffer *, flood->in, flood->out );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flood_free( Flood *flood )
|
||||
{
|
||||
/* Write dirty back to caller.
|
||||
*/
|
||||
if( flood->dout ) {
|
||||
flood->dout->left = flood->left;
|
||||
flood->dout->top = flood->top;
|
||||
flood->dout->width = flood->right - flood->left + 1;
|
||||
flood->dout->height = flood->bottom - flood->top + 1;
|
||||
}
|
||||
|
||||
IM_FREE( flood->ink );
|
||||
IM_FREE( flood->edge );
|
||||
IM_FREEF( buffer_free, flood->in );
|
||||
IM_FREEF( buffer_free, flood->out );
|
||||
im_free( flood );
|
||||
}
|
||||
|
||||
static Flood *
|
||||
flood_build( IMAGE *test, IMAGE *mark, int x, int y, PEL *ink, Rect *dout )
|
||||
{
|
||||
Flood *flood = IM_NEW( NULL, Flood );
|
||||
|
||||
if( !flood )
|
||||
return( NULL );
|
||||
flood->test = test;
|
||||
flood->mark = mark;
|
||||
flood->x = x;
|
||||
flood->y = y;
|
||||
flood->ink = NULL;
|
||||
flood->dout = dout;
|
||||
flood->edge = NULL;
|
||||
flood->tsize = IM_IMAGE_SIZEOF_PEL( test );
|
||||
flood->msize = IM_IMAGE_SIZEOF_PEL( mark );
|
||||
flood->left = x;
|
||||
flood->top = y;
|
||||
flood->right = x;
|
||||
flood->bottom = y;
|
||||
|
||||
flood->in = NULL;
|
||||
flood->out = NULL;
|
||||
|
||||
if( !(flood->ink = (PEL *) im_malloc( NULL, flood->msize )) ||
|
||||
!(flood->edge = (PEL *) im_malloc( NULL, flood->tsize )) ||
|
||||
!(flood->in = buffer_build()) ||
|
||||
!(flood->out = buffer_build()) ) {
|
||||
flood_free( flood );
|
||||
return( NULL );
|
||||
}
|
||||
memcpy( flood->ink, ink, flood->msize );
|
||||
|
||||
return( flood );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
{
|
||||
Flood *flood;
|
||||
|
||||
if( im_rwcheck( im ) ||
|
||||
im_check_known_coded( "im_flood", im ) )
|
||||
return( -1 );
|
||||
if( !(flood = flood_build( im, im, x, y, ink, dout )) )
|
||||
return( -1 );
|
||||
|
||||
/* Flood to != ink.
|
||||
*/
|
||||
memcpy( flood->edge, ink, flood->tsize );
|
||||
flood->equal = 0;
|
||||
|
||||
flood_all( flood, x, y );
|
||||
|
||||
flood_free( flood );
|
||||
|
||||
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 equal to the start pixel, in other words, it
|
||||
* searches for a blob of same-coloured pixels.
|
||||
*
|
||||
* 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 )
|
||||
{
|
||||
Flood *flood;
|
||||
|
||||
if( im_rwcheck( im ) ||
|
||||
im_check_known_coded( "im_flood", im ) )
|
||||
return( -1 );
|
||||
if( !(flood = flood_build( im, im, x, y, ink, dout )) )
|
||||
return( -1 );
|
||||
|
||||
/* Edge is set by colour of start pixel.
|
||||
*/
|
||||
memcpy( flood->edge, IM_IMAGE_ADDR( im, x, y ), flood->tsize );
|
||||
flood->equal = 1;
|
||||
|
||||
flood_all( flood, x, y );
|
||||
|
||||
flood_free( flood );
|
||||
|
||||
im_invalidate( im );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_flood_other( IMAGE *test, IMAGE *mark, int x, int y, int serial, Rect *dout )
|
||||
{
|
||||
int *m;
|
||||
Flood *flood;
|
||||
|
||||
if( im_incheck( test ) ||
|
||||
im_rwcheck( mark ) ||
|
||||
im_check_known_coded( "im_flood_other", test ) ||
|
||||
im_check_uncoded( "im_flood_other", mark ) ||
|
||||
im_check_mono( "im_flood_other", mark ) ||
|
||||
im_check_format( "im_flood_other", mark, IM_BANDFMT_INT ) ||
|
||||
im_check_same_size( "im_flood_other", test, mark ) )
|
||||
return( -1 );
|
||||
|
||||
/* Have we done this point already?
|
||||
*/
|
||||
m = (int *) IM_IMAGE_ADDR( mark, x, y );
|
||||
if( *m == serial )
|
||||
return( 0 );
|
||||
|
||||
if( !(flood = flood_build( test, mark, x, y, (PEL *) &serial, dout )) )
|
||||
return( -1 );
|
||||
|
||||
/* Edge is set by colour of start pixel.
|
||||
*/
|
||||
memcpy( flood->edge, IM_IMAGE_ADDR( test, x, y ), flood->tsize );
|
||||
flood->equal = 1;
|
||||
|
||||
flood_all( flood, x, y );
|
||||
|
||||
flood_free( flood );
|
||||
|
||||
im_invalidate( mark );
|
||||
|
||||
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?
|
||||
*/
|
||||
|
||||
int
|
||||
im_flood_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open_local( out, "im_flood_blob_copy", "t" )) ||
|
||||
im_copy( in, t ) ||
|
||||
im_flood( t, x, y, ink, NULL ) ||
|
||||
im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open_local( out, "im_flood_blob_copy", "t" )) ||
|
||||
im_copy( in, t ) ||
|
||||
im_flood_blob( t, x, y, ink, NULL ) ||
|
||||
im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_flood_other_copy( IMAGE *test, IMAGE *mark, IMAGE *out,
|
||||
int x, int y, int serial )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open_local( out, "im_flood_other_copy", "t" )) ||
|
||||
im_copy( mark, t ) ||
|
||||
im_flood_other( test, t, x, y, serial, NULL ) ||
|
||||
im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ dofill( State *st, Buffer *in, Buffer *out )
|
||||
}
|
||||
|
||||
int
|
||||
im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
im_flood_old( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
{
|
||||
State *st;
|
||||
Buffer *in, *out, *t;
|
||||
@ -372,7 +372,7 @@ im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
}
|
||||
|
||||
int
|
||||
im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
im_flood_blob_old( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
{
|
||||
State *st;
|
||||
Buffer *in, *out, *t;
|
||||
@ -417,13 +417,30 @@ im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
* automatically. Maybe nip could do it if it sees a RW image argument?
|
||||
*/
|
||||
int
|
||||
im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
|
||||
im_flood_blob_copy_old( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open_local( out, "im_flood_blob_copy", "t" )) ||
|
||||
im_copy( in, t ) ||
|
||||
im_flood_blob( t, x, y, ink, NULL ) ||
|
||||
im_flood_blob_old( t, x, y, ink, NULL ) ||
|
||||
im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
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?
|
||||
*/
|
||||
int
|
||||
im_flood_copy_old( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open_local( out, "im_flood_copy", "t" )) ||
|
||||
im_copy( in, t ) ||
|
||||
im_flood_old( t, x, y, ink, NULL ) ||
|
||||
im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
@ -431,3 +448,5 @@ im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,451 +0,0 @@
|
||||
/* 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
|
||||
* large images.
|
||||
*
|
||||
* JC 30/8/97
|
||||
* - VIPSified, cleaned up, from "John Robinson's prog to fill
|
||||
* enclosed areas"
|
||||
* - something Kirk gave me, so thanks John
|
||||
* JC 1/10/97
|
||||
* - swapped inner memcmp/cpy for a loop ... faster for small pixels
|
||||
* 13/7/02 JC
|
||||
* - im_flood_blob() added
|
||||
* 5/12/06
|
||||
* - im_invalidate() after paint
|
||||
* 24/3/09
|
||||
* - 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
|
||||
* 21/12/09
|
||||
* - rewrite for a scanline based fill
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser 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
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef WITH_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif /*WITH_DMALLOC*/
|
||||
|
||||
/* Size of a scanline buffer. We allocate a list of these to hold scanlines
|
||||
* we need to visit.
|
||||
*/
|
||||
#define PBUFSIZE (1000)
|
||||
|
||||
/* A scanline we know could contain pixels connected to us.
|
||||
*/
|
||||
typedef struct {
|
||||
int x1, x2;
|
||||
int y;
|
||||
} Scan;
|
||||
|
||||
/* A buffer of scanlines, and how many of them have been used. If ->next is
|
||||
* non-NULL, the next block could contain more of them.
|
||||
*
|
||||
* We keep a pair of these, then loop over one and write to the other.
|
||||
*/
|
||||
typedef struct _Buffer {
|
||||
struct _Buffer *next;
|
||||
int n;
|
||||
Scan scan[PBUFSIZE];
|
||||
} Buffer;
|
||||
|
||||
/* Our state.
|
||||
*/
|
||||
typedef struct {
|
||||
/* Parameters.
|
||||
*/
|
||||
IMAGE *im;
|
||||
int x, y;
|
||||
PEL *ink; /* Copy of ink param */
|
||||
Rect *dout; /* Write dirty here at end */
|
||||
|
||||
/* Derived stuff.
|
||||
*/
|
||||
PEL *edge; /* Boundary colour */
|
||||
int equal; /* Fill to == edge, or != edge */
|
||||
int ps; /* sizeof( one pel ) */
|
||||
int ls; /* sizeof( one line ) */
|
||||
int left, right; /* Area will fill within */
|
||||
int top, bottom;
|
||||
Rect dirty; /* Bounding box of pixels we have changed */
|
||||
|
||||
/* We need to flood above and below these scanlines.
|
||||
*/
|
||||
Buffer *up;
|
||||
Buffer *down;
|
||||
Buffer *read_up;
|
||||
Buffer *read_down;
|
||||
} State;
|
||||
|
||||
/* Alloc a new buffer.
|
||||
*/
|
||||
static Buffer *
|
||||
buffer_build( void )
|
||||
{
|
||||
Buffer *buf = IM_NEW( NULL, Buffer );
|
||||
|
||||
if( !buf )
|
||||
return( NULL );
|
||||
buf->next = NULL;
|
||||
buf->n = 0;
|
||||
|
||||
return( buf );
|
||||
}
|
||||
|
||||
/* Free a chain of buffers.
|
||||
*/
|
||||
static void
|
||||
buffer_free( Buffer *buf )
|
||||
{
|
||||
IM_FREE( buf->next );
|
||||
im_free( buf );
|
||||
}
|
||||
|
||||
/* Add a scanline to a buffer, prepending a new buffer if necessary. Return
|
||||
* the new head buffer.
|
||||
*/
|
||||
static inline Buffer *
|
||||
buffer_add( State *st, Buffer *buf, int x1, int x2, int y )
|
||||
{
|
||||
/* Clip against image size.
|
||||
*/
|
||||
if( y < 0 || y > st->im->Ysize )
|
||||
return( buf );
|
||||
x1 = IM_CLIP( 0, x1, st->im->Xsize );
|
||||
x2 = IM_CLIP( 0, x2, st->im->Xsize );
|
||||
if( x2 - x1 <= 0 )
|
||||
return( buf );
|
||||
|
||||
buf->scan[buf->n].x1 = x1;
|
||||
buf->scan[buf->n].x2 = x2;
|
||||
buf->scan[buf->n].y = y;
|
||||
buf->n++;
|
||||
|
||||
if( buf->n == PBUFSIZE ) {
|
||||
Buffer *new;
|
||||
|
||||
if( !(new = buffer_build()) )
|
||||
return( NULL );
|
||||
new->next = buf;
|
||||
buf = new;
|
||||
}
|
||||
|
||||
return( buf );
|
||||
}
|
||||
|
||||
/* Is p "connected"? ie. is equal to or not equal to st->edge, depending on
|
||||
* whether we are flooding to the edge boundary, or flooding edge-coloured
|
||||
* pixels.
|
||||
*/
|
||||
static inline gboolean
|
||||
pixel_connected( State *st, PEL *p )
|
||||
{
|
||||
int j;
|
||||
|
||||
for( j = 0; j < st->ps; j++ )
|
||||
if( p[j] != st->edge[j] )
|
||||
break;
|
||||
|
||||
return( st->equal ^ (j == st->ps) );
|
||||
}
|
||||
|
||||
/* Faster than memcpy for n < about 20.
|
||||
*/
|
||||
static inline void
|
||||
pixel_paint( State *st, PEL *q )
|
||||
{
|
||||
int j;
|
||||
|
||||
for( j = 0; j < st->ps; j++ )
|
||||
q[j] = st->ink[j];
|
||||
}
|
||||
|
||||
/* Fill a left and right, return the endpoints. The start point (x, y) must be
|
||||
* connected.
|
||||
*/
|
||||
static void
|
||||
fill_scanline( State *st, int x, int y, int *x1, int *x2 )
|
||||
{
|
||||
PEL *p = (PEL *) IM_IMAGE_ADDR( st->im, x, y );
|
||||
|
||||
int i;
|
||||
PEL *q;
|
||||
|
||||
g_assert( pixel_connected( st, p ) );
|
||||
|
||||
/* Fill this pixel and to the right.
|
||||
*/
|
||||
for( q = p, i = 0;
|
||||
i < st->im->Xsize - x && pixel_connected( st, q );
|
||||
q += st->ps, i++ )
|
||||
pixel_paint( st, q );
|
||||
*x2 = x + i - 1;
|
||||
|
||||
/* Fill to the left.
|
||||
*/
|
||||
for( q = p - st->ps, i = 1;
|
||||
i > x && pixel_connected( st, q );
|
||||
q -= st->ps, i++ )
|
||||
pixel_paint( st, q );
|
||||
*x1 = x - (i - 1);
|
||||
}
|
||||
|
||||
/* We know the line below or above us is filled between x1 and x2. Search our
|
||||
* line in this range looking for an edge pixel we can flood from.
|
||||
*
|
||||
* "direction" is -1 if we're going up, +1 if we're filling down.
|
||||
*/
|
||||
static void
|
||||
fill_around( State *st, Scan *scan, int dir )
|
||||
{
|
||||
int x;
|
||||
|
||||
g_assert( dir == 1 || dir == -1 );
|
||||
|
||||
for( x = scan->x1; x <= scan->x2; x++ ) {
|
||||
PEL *p = (PEL *) IM_IMAGE_ADDR( st->im, x, scan->y );
|
||||
|
||||
if( pixel_connected( st, p ) ) {
|
||||
int x1a;
|
||||
int x2a;
|
||||
|
||||
fill_scanline( st, x, scan->y, &x1a, &x2a );
|
||||
|
||||
/* Our new scanline can have up to three more
|
||||
* scanlines connected to it: above, below left, below
|
||||
* right.
|
||||
*/
|
||||
if( x1a < scan->x1 - 1 )
|
||||
st->down = buffer_add( st, st->down,
|
||||
x1a, scan->x1 - 1, scan->y - dir );
|
||||
if( x2a > scan->x2 + 1 )
|
||||
st->down = buffer_add( st, st->down,
|
||||
scan->x2 + 1, x2a, scan->y - dir );
|
||||
st->up = buffer_add( st, st->up,
|
||||
x1a, x2a, scan->y + dir );
|
||||
|
||||
x = x2a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fill_buffer( State *st, Buffer *buf, int dir )
|
||||
{
|
||||
Buffer *p;
|
||||
|
||||
for( p = buf; p; p = p->next ) {
|
||||
int i;
|
||||
|
||||
for( i = 0; i < p->n; i++ )
|
||||
fill_around( st, &buf->scan[i], dir );
|
||||
|
||||
p->n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fill_all( State *st )
|
||||
{
|
||||
while( st->read_up || st->read_down ) {
|
||||
Buffer *p;
|
||||
|
||||
fill_buffer( st, st->read_up, -1 );
|
||||
fill_buffer( st, st->read_down, -1 );
|
||||
|
||||
p = st->read_up;
|
||||
st->read_up = st->up;
|
||||
st->up = p;
|
||||
|
||||
p = st->read_down;
|
||||
st->read_down = st->down;
|
||||
st->down = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free a state.
|
||||
*/
|
||||
static void
|
||||
state_free( State *st )
|
||||
{
|
||||
/* Write dirty back to caller.
|
||||
*/
|
||||
if( st->dout )
|
||||
*st->dout = st->dirty;
|
||||
|
||||
/* Free our stuff.
|
||||
*/
|
||||
IM_FREE( st->ink );
|
||||
IM_FREE( st->edge );
|
||||
IM_FREEF( buffer_free, st->up );
|
||||
IM_FREEF( buffer_free, st->down );
|
||||
IM_FREEF( buffer_free, st->read_up );
|
||||
IM_FREEF( buffer_free, st->read_down );
|
||||
im_free( st );
|
||||
}
|
||||
|
||||
/* Build a state.
|
||||
*/
|
||||
static State *
|
||||
state_build( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
{
|
||||
State *st = IM_NEW( NULL, State );
|
||||
|
||||
if( !st )
|
||||
return( NULL );
|
||||
st->im = im;
|
||||
st->x = x;
|
||||
st->y = y;
|
||||
st->ink = NULL;
|
||||
st->dout = dout;
|
||||
st->edge = NULL;
|
||||
st->ps = IM_IMAGE_SIZEOF_PEL( im );
|
||||
st->ls = IM_IMAGE_SIZEOF_LINE( im );
|
||||
st->left = 0;
|
||||
st->top = 0;
|
||||
st->right = im->Xsize;
|
||||
st->bottom = im->Ysize;
|
||||
st->dirty.left = x;
|
||||
st->dirty.top = y;
|
||||
st->dirty.width = 0;
|
||||
st->dirty.height = 0;
|
||||
|
||||
st->up = NULL;
|
||||
st->down = NULL;
|
||||
st->read_up = NULL;
|
||||
st->read_down = NULL;
|
||||
|
||||
if( !(st->ink = (PEL *) im_malloc( NULL, st->ps )) ||
|
||||
!(st->edge = (PEL *) im_malloc( NULL, st->ps )) ||
|
||||
!(st->up = buffer_build()) ||
|
||||
!(st->down = buffer_build()) ||
|
||||
!(st->read_up = buffer_build()) ||
|
||||
!(st->read_down = buffer_build()) ) {
|
||||
state_free( st );
|
||||
return( NULL );
|
||||
}
|
||||
memcpy( st->ink, ink, st->ps );
|
||||
|
||||
return( st );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_new( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
|
||||
{
|
||||
State *st;
|
||||
Buffer *in, *out, *t;
|
||||
PEL *p;
|
||||
|
||||
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 ) {
|
||||
state_free( st );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Flood to != ink.
|
||||
*/
|
||||
memcpy( st->edge, ink, st->ps );
|
||||
st->equal = 0;
|
||||
|
||||
/* Add start pixel to the work buffer, and loop.
|
||||
st->buf1 = buffer_add( st, 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 ) ) {
|
||||
state_free( st );
|
||||
return( -1 );
|
||||
}
|
||||
*/
|
||||
|
||||
state_free( st );
|
||||
|
||||
im_invalidate( im );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_flood_new_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open_local( out, "im_flood_blob_copy", "t" )) ||
|
||||
im_copy( in, t ) ||
|
||||
im_flood_new( t, x, y, ink, NULL ) ||
|
||||
im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ dofill( State *st, Buffer *in, Buffer *out )
|
||||
}
|
||||
|
||||
int
|
||||
im_flood_other( IMAGE *mask, IMAGE *test, int x, int y, int serial )
|
||||
im_flood_other_old( IMAGE *mask, IMAGE *test, int x, int y, int serial )
|
||||
{
|
||||
State *st;
|
||||
Buffer *in, *out, *t;
|
||||
@ -315,14 +315,14 @@ im_flood_other( IMAGE *mask, IMAGE *test, int x, int y, int serial )
|
||||
* automatically. Maybe nip could do it if it sees a RW image argument?
|
||||
*/
|
||||
int
|
||||
im_flood_other_copy( IMAGE *mask, IMAGE *test, IMAGE *out,
|
||||
im_flood_other_copy_old( IMAGE *mask, IMAGE *test, IMAGE *out,
|
||||
int x, int y, int serial )
|
||||
{
|
||||
IMAGE *t;
|
||||
|
||||
if( !(t = im_open_local( out, "im_flood_other_copy", "t" )) ||
|
||||
im_copy( mask, t ) ||
|
||||
im_flood_other( t, test, x, y, serial ) ||
|
||||
im_flood_other_old( t, test, x, y, serial ) ||
|
||||
im_copy( t, out ) )
|
||||
return( -1 );
|
||||
|
||||
|
@ -188,9 +188,9 @@ vector_to_ink( IMAGE *im, double *vec )
|
||||
return( (PEL *) t[2]->data );
|
||||
}
|
||||
|
||||
/* Args for im_flood_new().
|
||||
/* Args for im_flood_old().
|
||||
*/
|
||||
static im_arg_desc flood_new_args[] = {
|
||||
static im_arg_desc flood_old_args[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" ),
|
||||
IM_INPUT_INT( "start_x" ),
|
||||
@ -198,10 +198,10 @@ static im_arg_desc flood_new_args[] = {
|
||||
IM_INPUT_DOUBLEVEC( "ink" )
|
||||
};
|
||||
|
||||
/* Call im_flood_new() via arg vector.
|
||||
/* Call im_flood_old() via arg vector.
|
||||
*/
|
||||
static int
|
||||
flood_new_vec( im_object *argv )
|
||||
flood_old_vec( im_object *argv )
|
||||
{
|
||||
IMAGE *in = argv[0];
|
||||
IMAGE *out = argv[1];
|
||||
@ -209,30 +209,30 @@ flood_new_vec( im_object *argv )
|
||||
int start_y = *((int *) argv[3]);
|
||||
im_doublevec_object *dv = (im_doublevec_object *) argv[4];
|
||||
|
||||
extern int im_flood_new_copy( IMAGE *, IMAGE *, int, int, PEL * );
|
||||
extern int im_flood_copy_old( IMAGE *, IMAGE *, int, int, PEL * );
|
||||
|
||||
PEL *ink;
|
||||
|
||||
if( dv->n != in->Bands ) {
|
||||
im_error( "im_flood_new",
|
||||
im_error( "im_flood_old",
|
||||
"%s", _( "bad vector length" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( !(ink = vector_to_ink( in, dv->vec )) )
|
||||
return( -1 );
|
||||
|
||||
return( im_flood_new_copy( in, out, start_x, start_y, ink ) );
|
||||
return( im_flood_copy_old( in, out, start_x, start_y, ink ) );
|
||||
}
|
||||
|
||||
/* Description of im_flood_new().
|
||||
/* Description of im_flood_old().
|
||||
*/
|
||||
static im_function flood_new_desc = {
|
||||
"im_flood_new", /* Name */
|
||||
static im_function flood_old_desc = {
|
||||
"im_flood_old", /* Name */
|
||||
"flood with ink from start_x, start_y while pixel == start pixel",
|
||||
0, /* Flags */
|
||||
flood_new_vec, /* Dispatch function */
|
||||
IM_NUMBER( flood_new_args ),/* Size of arg list */
|
||||
flood_new_args /* Arg list */
|
||||
flood_old_vec, /* Dispatch function */
|
||||
IM_NUMBER( flood_old_args ),/* Size of arg list */
|
||||
flood_old_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for im_flood_blob_copy().
|
||||
@ -280,6 +280,51 @@ static im_function flood_blob_copy_desc = {
|
||||
flood_blob_copy_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for im_flood_copy().
|
||||
*/
|
||||
static im_arg_desc flood_copy_args[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" ),
|
||||
IM_INPUT_INT( "start_x" ),
|
||||
IM_INPUT_INT( "start_y" ),
|
||||
IM_INPUT_DOUBLEVEC( "ink" )
|
||||
};
|
||||
|
||||
/* Call im_flood_copy() via arg vector.
|
||||
*/
|
||||
static int
|
||||
flood_copy_vec( im_object *argv )
|
||||
{
|
||||
IMAGE *in = argv[0];
|
||||
IMAGE *out = argv[1];
|
||||
int start_x = *((int *) argv[2]);
|
||||
int start_y = *((int *) argv[3]);
|
||||
im_doublevec_object *dv = (im_doublevec_object *) argv[4];
|
||||
|
||||
PEL *ink;
|
||||
|
||||
if( dv->n != in->Bands ) {
|
||||
im_error( "im_flood_copy",
|
||||
"%s", _( "bad vector length" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( !(ink = vector_to_ink( in, dv->vec )) )
|
||||
return( -1 );
|
||||
|
||||
return( im_flood_copy( in, out, start_x, start_y, ink ) );
|
||||
}
|
||||
|
||||
/* Description of im_flood_copy().
|
||||
*/
|
||||
static im_function flood_copy_desc = {
|
||||
"im_flood_copy", /* Name */
|
||||
"flood with ink from start_x, start_y while pixel == start pixel",
|
||||
0, /* Flags */
|
||||
flood_copy_vec, /* Dispatch function */
|
||||
IM_NUMBER( flood_copy_args ),/* Size of arg list */
|
||||
flood_copy_args /* Arg list */
|
||||
};
|
||||
|
||||
/* Args for im_flood_other_copy().
|
||||
*/
|
||||
static im_arg_desc flood_other_copy_args[] = {
|
||||
@ -335,7 +380,8 @@ static im_function flood_other_copy_desc = {
|
||||
static im_function *inplace_list[] = {
|
||||
&circle_desc,
|
||||
&flood_blob_copy_desc,
|
||||
&flood_new_desc,
|
||||
&flood_copy_desc,
|
||||
&flood_old_desc,
|
||||
&flood_other_copy_desc,
|
||||
&insertplace_desc,
|
||||
&lineset_desc
|
||||
|
@ -92,7 +92,8 @@ im_label_regions( IMAGE *test, IMAGE *mask, int *segments )
|
||||
for( y = 0; y < test->Ysize; y++ ) {
|
||||
for( x = 0; x < test->Xsize; x++ ) {
|
||||
if( !m[x] ) {
|
||||
if( im_flood_other( t[1], test, x, y, serial ) )
|
||||
if( im_flood_other( test, t[1],
|
||||
x, y, serial, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
serial += 1;
|
||||
|
Loading…
Reference in New Issue
Block a user