vips_circle() works
flood next
This commit is contained in:
parent
7ef49c2f2d
commit
e3dbbab570
@ -3,6 +3,7 @@
|
|||||||
- background render thread cleans up and quits neatly
|
- background render thread cleans up and quits neatly
|
||||||
- colourspace has a source_space option
|
- colourspace has a source_space option
|
||||||
- operations can be tagged as "deprecated"
|
- operations can be tagged as "deprecated"
|
||||||
|
- redo im_circle() as a class
|
||||||
|
|
||||||
22/1/14 started 7.38.3
|
22/1/14 started 7.38.3
|
||||||
- undeprecate VIPS_MASK_IDEAL_HIGHPASS and friends, ruby-vips was using them,
|
- undeprecate VIPS_MASK_IDEAL_HIGHPASS and friends, ruby-vips was using them,
|
||||||
|
@ -3,7 +3,7 @@ noinst_LTLIBRARIES = libdraw.la
|
|||||||
libdraw_la_SOURCES = \
|
libdraw_la_SOURCES = \
|
||||||
circle.c \
|
circle.c \
|
||||||
draw.c \
|
draw.c \
|
||||||
flood.c \
|
im_flood.c \
|
||||||
im_draw_circle.c \
|
im_draw_circle.c \
|
||||||
im_draw_image.c \
|
im_draw_image.c \
|
||||||
im_draw_line.c \
|
im_draw_line.c \
|
||||||
|
@ -121,8 +121,7 @@ vips_circle_build( VipsObject *object )
|
|||||||
|
|
||||||
int x, y, d;
|
int x, y, d;
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_circle_parent_class )->
|
if( VIPS_OBJECT_CLASS( vips_circle_parent_class )->build( object ) )
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
circle->centre = VIPS_IMAGE_ADDR( draw->image, circle->cx, circle->cy );
|
circle->centre = VIPS_IMAGE_ADDR( draw->image, circle->cx, circle->cy );
|
||||||
|
@ -194,7 +194,7 @@ buffer_add( Buffer *buf, VipsFlood *flood, int x1, int x2, int y, int dir )
|
|||||||
* pixels.
|
* pixels.
|
||||||
*/
|
*/
|
||||||
static inline gboolean
|
static inline gboolean
|
||||||
flood_connected( VipsFlood *flood, VipsPel *tp )
|
vips_flood_connected( VipsFlood *flood, VipsPel *tp )
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ flood_connected( VipsFlood *flood, VipsPel *tp )
|
|||||||
* connected and unpainted.
|
* connected and unpainted.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
flood_scanline( VipsFlood *flood, int x, int y, int *x1, int *x2 )
|
vips_flood_scanline( VipsFlood *flood, int x, int y, int *x1, int *x2 )
|
||||||
{
|
{
|
||||||
VipsDraw *draw = VIPS_DRAW( flood );
|
VipsDraw *draw = VIPS_DRAW( flood );
|
||||||
const int width = flood->test->Xsize;
|
const int width = flood->test->Xsize;
|
||||||
@ -219,7 +219,7 @@ flood_scanline( VipsFlood *flood, int x, int y, int *x1, int *x2 )
|
|||||||
VipsPel *tp;
|
VipsPel *tp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
g_assert( flood_connected( flood,
|
g_assert( vips_flood_connected( flood,
|
||||||
VIPS_IMAGE_ADDR( flood->test, x, y ) ) );
|
VIPS_IMAGE_ADDR( flood->test, x, y ) ) );
|
||||||
g_assert( !vips__draw_painted( draw,
|
g_assert( !vips__draw_painted( draw,
|
||||||
VIPS_IMAGE_ADDR( draw->im, x, y ) ) );
|
VIPS_IMAGE_ADDR( draw->im, x, y ) ) );
|
||||||
@ -230,7 +230,7 @@ flood_scanline( VipsFlood *flood, int x, int y, int *x1, int *x2 )
|
|||||||
*/
|
*/
|
||||||
tp = VIPS_IMAGE_ADDR( flood->test, x + 1, y );
|
tp = VIPS_IMAGE_ADDR( flood->test, x + 1, y );
|
||||||
for( i = x + 1; i < width; i++ ) {
|
for( i = x + 1; i < width; i++ ) {
|
||||||
if( !flood_connected( flood, tp ) )
|
if( !vips_flood_connected( flood, tp ) )
|
||||||
break;
|
break;
|
||||||
tp += flood->tsize;
|
tp += flood->tsize;
|
||||||
}
|
}
|
||||||
@ -240,7 +240,7 @@ flood_scanline( VipsFlood *flood, int x, int y, int *x1, int *x2 )
|
|||||||
*/
|
*/
|
||||||
tp = VIPS_IMAGE_ADDR( flood->test, x - 1, y );
|
tp = VIPS_IMAGE_ADDR( flood->test, x - 1, y );
|
||||||
for( i = x - 1; i >= 0; i-- ) {
|
for( i = x - 1; i >= 0; i-- ) {
|
||||||
if( !flood_connected( flood, tp ) )
|
if( !vips_flood_connected( flood, tp ) )
|
||||||
break;
|
break;
|
||||||
tp -= flood->tsize;
|
tp -= flood->tsize;
|
||||||
}
|
}
|
||||||
@ -262,7 +262,7 @@ flood_scanline( VipsFlood *flood, int x, int y, int *x1, int *x2 )
|
|||||||
* line in this range looking for an edge pixel we can flood from.
|
* line in this range looking for an edge pixel we can flood from.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
flood_around( VipsFlood *flood, Scan *scan )
|
vips_flood_around( VipsFlood *flood, Scan *scan )
|
||||||
{
|
{
|
||||||
VipsDraw *draw = VIPS_DRAW( flood );
|
VipsDraw *draw = VIPS_DRAW( flood );
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ flood_around( VipsFlood *flood, Scan *scan )
|
|||||||
x = scan->x1;
|
x = scan->x1;
|
||||||
x <= scan->x2;
|
x <= scan->x2;
|
||||||
tp += flood->tsize, x++ ) {
|
tp += flood->tsize, x++ ) {
|
||||||
if( flood_connected( flood, tp ) ) {
|
if( vips_flood_connected( flood, tp ) ) {
|
||||||
int x1a;
|
int x1a;
|
||||||
int x2a;
|
int x2a;
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ flood_around( VipsFlood *flood, Scan *scan )
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
flood_scanline( flood, x, scan->y, &x1a, &x2a );
|
vips_flood_scanline( flood, x, scan->y, &x1a, &x2a );
|
||||||
|
|
||||||
/* Our new scanline can have up to three more
|
/* Our new scanline can have up to three more
|
||||||
* scanlines connected to it: above, below left, below
|
* scanlines connected to it: above, below left, below
|
||||||
@ -317,17 +317,17 @@ flood_around( VipsFlood *flood, Scan *scan )
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
flood_all( Flood *flood, int x, int y )
|
vips_flood_all( VipsFlood *flood, int x, int y )
|
||||||
{
|
{
|
||||||
int x1, x2;
|
int x1, x2;
|
||||||
|
|
||||||
/* Test start pixel ... nothing to do?
|
/* Test start pixel ... nothing to do?
|
||||||
*/
|
*/
|
||||||
if( !flood_connected( flood,
|
if( !vips_flood_connected( flood,
|
||||||
VIPS_IMAGE_ADDR( flood->test, x, y ) ) )
|
VIPS_IMAGE_ADDR( flood->test, x, y ) ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
flood_scanline( flood, x, y, &x1, &x2 );
|
vips_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 );
|
||||||
flood->in = buffer_add( flood->in, flood, x1, x2, y - 1, -1 );
|
flood->in = buffer_add( flood->in, flood, x1, x2, y - 1, -1 );
|
||||||
|
|
||||||
@ -338,7 +338,7 @@ flood_all( Flood *flood, int x, int y )
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for( i = 0; i < p->n; i++ )
|
for( i = 0; i < p->n; i++ )
|
||||||
flood_around( flood, &p->scan[i] );
|
vips_flood_around( flood, &p->scan[i] );
|
||||||
|
|
||||||
p->n = 0;
|
p->n = 0;
|
||||||
}
|
}
|
||||||
@ -347,6 +347,20 @@ flood_all( Flood *flood, int x, int y )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_flood_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsDraw *draw = VIPS_DRAW( object );
|
||||||
|
VipsFlood *flood = (VipsFlood *) object;
|
||||||
|
|
||||||
|
int x, y, d;
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_flood_parent_class )->build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
flood_free( VipsFlood *flood )
|
flood_free( VipsFlood *flood )
|
||||||
{
|
{
|
||||||
@ -365,12 +379,12 @@ flood_free( VipsFlood *flood )
|
|||||||
vips_free( flood );
|
vips_free( flood );
|
||||||
}
|
}
|
||||||
|
|
||||||
static Flood *
|
static VipsFlood *
|
||||||
flood_new( IMAGE *image, IMAGE *test, int x, int y, VipsPel *ink, Rect *dout )
|
vips_flood_new( VipsImage *image, VipsImage *test, int x, int y, VipsPel *ink, Rect *dout )
|
||||||
{
|
{
|
||||||
Flood *flood;
|
VipsFlood *flood;
|
||||||
|
|
||||||
if( !(flood = IM_NEW( NULL, Flood )) )
|
if( !(flood = VIPS_NEW( NULL, VipsFlood )) )
|
||||||
return( NULL );
|
return( NULL );
|
||||||
if( !im__draw_init( DRAW( flood ), image, ink ) ) {
|
if( !im__draw_init( DRAW( flood ), image, ink ) ) {
|
||||||
flood_free( flood );
|
flood_free( flood );
|
||||||
@ -382,7 +396,7 @@ flood_new( IMAGE *image, IMAGE *test, int x, int y, VipsPel *ink, Rect *dout )
|
|||||||
flood->y = y;
|
flood->y = y;
|
||||||
flood->dout = dout;
|
flood->dout = dout;
|
||||||
flood->edge = NULL;
|
flood->edge = NULL;
|
||||||
flood->tsize = IM_IMAGE_SIZEOF_PEL( test );
|
flood->tsize = VIPS_IMAGE_SIZEOF_PEL( test );
|
||||||
flood->left = x;
|
flood->left = x;
|
||||||
flood->top = y;
|
flood->top = y;
|
||||||
flood->right = x;
|
flood->right = x;
|
||||||
@ -418,12 +432,12 @@ flood_new( IMAGE *image, IMAGE *test, int x, int y, VipsPel *ink, Rect *dout )
|
|||||||
* Returns: 0 on success, or -1 on error.
|
* Returns: 0 on success, or -1 on error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
im_draw_flood( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
im_draw_flood( VipsImage *image, int x, int y, VipsPel *ink, Rect *dout )
|
||||||
{
|
{
|
||||||
Flood *flood;
|
VipsFlood *flood;
|
||||||
|
|
||||||
if( im_check_coding_known( "im_draw_flood", image ) ||
|
if( im_check_coding_known( "im_draw_flood", image ) ||
|
||||||
!(flood = flood_new( image, image, x, y, ink, dout )) )
|
!(flood = vips_flood_new( image, image, x, y, ink, dout )) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* Flood to != ink.
|
/* Flood to != ink.
|
||||||
@ -431,7 +445,7 @@ im_draw_flood( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
|||||||
memcpy( flood->edge, ink, flood->tsize );
|
memcpy( flood->edge, ink, flood->tsize );
|
||||||
flood->equal = 0;
|
flood->equal = 0;
|
||||||
|
|
||||||
flood_all( flood, x, y );
|
vips_flood_all( flood, x, y );
|
||||||
|
|
||||||
flood_free( flood );
|
flood_free( flood );
|
||||||
|
|
||||||
@ -458,18 +472,18 @@ im_draw_flood( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
|||||||
* Returns: 0 on success, or -1 on error.
|
* Returns: 0 on success, or -1 on error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
im_draw_flood_blob( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
im_draw_flood_blob( VipsImage *image, int x, int y, VipsPel *ink, Rect *dout )
|
||||||
{
|
{
|
||||||
Flood *flood;
|
VipsFlood *flood;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
if( im_check_coding_known( "im_draw_flood_blob", image ) ||
|
if( im_check_coding_known( "im_draw_flood_blob", image ) ||
|
||||||
!(flood = flood_new( image, image, x, y, ink, dout )) )
|
!(flood = vips_flood_new( image, image, x, y, ink, dout )) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* Edge is set by colour of start pixel.
|
/* Edge is set by colour of start pixel.
|
||||||
*/
|
*/
|
||||||
memcpy( flood->edge, IM_IMAGE_ADDR( image, x, y ), flood->tsize );
|
memcpy( flood->edge, VIPS_IMAGE_ADDR( image, x, y ), flood->tsize );
|
||||||
flood->equal = 1;
|
flood->equal = 1;
|
||||||
|
|
||||||
/* If edge == ink, we'll never stop :-( or rather, there's nothing to
|
/* If edge == ink, we'll never stop :-( or rather, there's nothing to
|
||||||
@ -481,7 +495,7 @@ im_draw_flood_blob( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
|||||||
if( j == flood->tsize )
|
if( j == flood->tsize )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
flood_all( flood, x, y );
|
vips_flood_all( flood, x, y );
|
||||||
|
|
||||||
flood_free( flood );
|
flood_free( flood );
|
||||||
|
|
||||||
@ -510,36 +524,36 @@ im_draw_flood_blob( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
|||||||
* Returns: 0 on success, or -1 on error.
|
* Returns: 0 on success, or -1 on error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
im_draw_flood_other( IMAGE *image,
|
im_draw_flood_other( VipsImage *image,
|
||||||
IMAGE *test, int x, int y, int serial, Rect *dout )
|
VipsImage *test, int x, int y, int serial, Rect *dout )
|
||||||
{
|
{
|
||||||
int *m;
|
int *m;
|
||||||
Flood *flood;
|
VipsFlood *flood;
|
||||||
|
|
||||||
if( im_incheck( test ) ||
|
if( im_incheck( test ) ||
|
||||||
im_check_coding_known( "im_draw_flood_other", test ) ||
|
im_check_coding_known( "im_draw_flood_other", test ) ||
|
||||||
im_check_uncoded( "im_draw_flood_other", image ) ||
|
im_check_uncoded( "im_draw_flood_other", image ) ||
|
||||||
im_check_mono( "im_draw_flood_other", image ) ||
|
im_check_mono( "im_draw_flood_other", image ) ||
|
||||||
im_check_format( "im_draw_flood_other", image,
|
im_check_format( "im_draw_flood_other", image,
|
||||||
IM_BANDFMT_INT ) ||
|
VIPS_BANDFMT_INT ) ||
|
||||||
im_check_size_same( "im_draw_flood_other", test, image ) )
|
im_check_size_same( "im_draw_flood_other", test, image ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* Have we done this point already?
|
/* Have we done this point already?
|
||||||
*/
|
*/
|
||||||
m = (int *) IM_IMAGE_ADDR( image, x, y );
|
m = (int *) VIPS_IMAGE_ADDR( image, x, y );
|
||||||
if( *m == serial )
|
if( *m == serial )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
if( !(flood = flood_new( image, test, x, y, (VipsPel *) &serial, dout )) )
|
if( !(flood = vips_flood_new( image, test, x, y, (VipsPel *) &serial, dout )) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* Edge is set by colour of start pixel.
|
/* Edge is set by colour of start pixel.
|
||||||
*/
|
*/
|
||||||
memcpy( flood->edge, IM_IMAGE_ADDR( test, x, y ), flood->tsize );
|
memcpy( flood->edge, VIPS_IMAGE_ADDR( test, x, y ), flood->tsize );
|
||||||
flood->equal = 1;
|
flood->equal = 1;
|
||||||
|
|
||||||
flood_all( flood, x, y );
|
vips_flood_all( flood, x, y );
|
||||||
|
|
||||||
flood_free( flood );
|
flood_free( flood );
|
||||||
|
|
||||||
|
540
libvips/draw/im_flood.c
Normal file
540
libvips/draw/im_flood.c
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
/* flood-fill
|
||||||
|
*
|
||||||
|
* 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, about 4x faster!
|
||||||
|
* - allow separate test and mark images
|
||||||
|
* 22/1/10
|
||||||
|
* - flood_blob could loop if start point == ink
|
||||||
|
* 6/3/10
|
||||||
|
* - don't im_invalidate() after paint, this now needs to be at a higher
|
||||||
|
* level
|
||||||
|
* 27/9/10
|
||||||
|
* - use Draw base class
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301 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>
|
||||||
|
|
||||||
|
#include "old_draw.h"
|
||||||
|
|
||||||
|
/* 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 {
|
||||||
|
Draw draw;
|
||||||
|
|
||||||
|
/* Parameters.
|
||||||
|
*/
|
||||||
|
IMAGE *test; /* Test this image */
|
||||||
|
int x, y;
|
||||||
|
Rect *dout; /* Write dirty here at end */
|
||||||
|
|
||||||
|
/* Derived stuff.
|
||||||
|
*/
|
||||||
|
VipsPel *edge; /* Boundary colour */
|
||||||
|
int equal; /* Fill to == edge, or != edge */
|
||||||
|
int tsize; /* sizeof( one pel in test ) */
|
||||||
|
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;
|
||||||
|
|
||||||
|
buf = g_new( Buffer, 1 );
|
||||||
|
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;
|
||||||
|
g_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;
|
||||||
|
|
||||||
|
new = buffer_build();
|
||||||
|
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, VipsPel *tp )
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for( j = 0; j < flood->tsize; j++ )
|
||||||
|
if( tp[j] != flood->edge[j] )
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* If flood->equal, true if point == edge.
|
||||||
|
*/
|
||||||
|
return( flood->equal ^ (j < flood->tsize) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill left and right, return the endpoints. The start point (x, y) must be
|
||||||
|
* connected and unpainted.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 )
|
||||||
|
{
|
||||||
|
Draw *draw = DRAW( flood );
|
||||||
|
const int width = flood->test->Xsize;
|
||||||
|
|
||||||
|
VipsPel *tp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
g_assert( flood_connected( flood,
|
||||||
|
IM_IMAGE_ADDR( flood->test, x, y ) ) );
|
||||||
|
g_assert( !im__draw_painted( draw,
|
||||||
|
IM_IMAGE_ADDR( draw->im, x, y ) ) );
|
||||||
|
|
||||||
|
/* Search to the right for the first non-connected pixel. If the start
|
||||||
|
* pixel is unpainted, we know all the intervening pixels must be
|
||||||
|
* unpainted too.
|
||||||
|
*/
|
||||||
|
tp = IM_IMAGE_ADDR( flood->test, x + 1, y );
|
||||||
|
for( i = x + 1; i < width; i++ ) {
|
||||||
|
if( !flood_connected( flood, tp ) )
|
||||||
|
break;
|
||||||
|
tp += flood->tsize;
|
||||||
|
}
|
||||||
|
*x2 = i - 1;
|
||||||
|
|
||||||
|
/* Search left.
|
||||||
|
*/
|
||||||
|
tp = IM_IMAGE_ADDR( flood->test, x - 1, y );
|
||||||
|
for( i = x - 1; i >= 0; i-- ) {
|
||||||
|
if( !flood_connected( flood, tp ) )
|
||||||
|
break;
|
||||||
|
tp -= flood->tsize;
|
||||||
|
}
|
||||||
|
*x1 = i + 1;
|
||||||
|
|
||||||
|
/* Paint the range we discovered.
|
||||||
|
*/
|
||||||
|
im__draw_scanline( draw, y, *x1, *x2 );
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
Draw *draw = DRAW( flood );
|
||||||
|
|
||||||
|
VipsPel *tp;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
g_assert( scan->dir == 1 || scan->dir == -1 );
|
||||||
|
|
||||||
|
for( tp = IM_IMAGE_ADDR( flood->test, scan->x1, scan->y ),
|
||||||
|
x = scan->x1;
|
||||||
|
x <= scan->x2;
|
||||||
|
tp += flood->tsize, x++ ) {
|
||||||
|
if( flood_connected( flood, tp ) ) {
|
||||||
|
int x1a;
|
||||||
|
int x2a;
|
||||||
|
|
||||||
|
/* If mark and test are different images, we also need
|
||||||
|
* to check for painted. Otherwise we can get stuck in
|
||||||
|
* connected loops.
|
||||||
|
*/
|
||||||
|
if( draw->im != flood->test ) {
|
||||||
|
VipsPel *mp = IM_IMAGE_ADDR(
|
||||||
|
draw->im, x, scan->y );
|
||||||
|
|
||||||
|
if( im__draw_painted( draw, mp ) )
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 - 2,
|
||||||
|
scan->y - scan->dir, -scan->dir );
|
||||||
|
if( x2a > scan->x2 + 1 )
|
||||||
|
flood->out = buffer_add( flood->out, flood,
|
||||||
|
scan->x2 + 2, x2a,
|
||||||
|
scan->y - scan->dir, -scan->dir );
|
||||||
|
flood->out = buffer_add( flood->out, flood,
|
||||||
|
x1a, x2a, scan->y + scan->dir,
|
||||||
|
scan->dir );
|
||||||
|
|
||||||
|
x = x2a + 1;
|
||||||
|
tp = IM_IMAGE_ADDR( flood->test, x, scan->y );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
flood_all( Flood *flood, int x, int y )
|
||||||
|
{
|
||||||
|
int x1, x2;
|
||||||
|
|
||||||
|
/* Test start pixel ... nothing to do?
|
||||||
|
*/
|
||||||
|
if( !flood_connected( flood,
|
||||||
|
IM_IMAGE_ADDR( flood->test, x, y ) ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
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 ) {
|
||||||
|
Buffer *p;
|
||||||
|
|
||||||
|
for( p = flood->in; p; p = p->next ) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0; i < p->n; i++ )
|
||||||
|
flood_around( flood, &p->scan[i] );
|
||||||
|
|
||||||
|
p->n = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IM_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__draw_free( DRAW( flood ) );
|
||||||
|
IM_FREE( flood->edge );
|
||||||
|
IM_FREEF( buffer_free, flood->in );
|
||||||
|
IM_FREEF( buffer_free, flood->out );
|
||||||
|
im_free( flood );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Flood *
|
||||||
|
flood_new( IMAGE *image, IMAGE *test, int x, int y, VipsPel *ink, Rect *dout )
|
||||||
|
{
|
||||||
|
Flood *flood;
|
||||||
|
|
||||||
|
if( !(flood = IM_NEW( NULL, Flood )) )
|
||||||
|
return( NULL );
|
||||||
|
if( !im__draw_init( DRAW( flood ), image, ink ) ) {
|
||||||
|
flood_free( flood );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
flood->test = test;
|
||||||
|
flood->x = x;
|
||||||
|
flood->y = y;
|
||||||
|
flood->dout = dout;
|
||||||
|
flood->edge = NULL;
|
||||||
|
flood->tsize = IM_IMAGE_SIZEOF_PEL( test );
|
||||||
|
flood->left = x;
|
||||||
|
flood->top = y;
|
||||||
|
flood->right = x;
|
||||||
|
flood->bottom = y;
|
||||||
|
flood->in = buffer_build();
|
||||||
|
flood->out = buffer_build();
|
||||||
|
|
||||||
|
if( !(flood->edge = (VipsPel *) im_malloc( NULL, flood->tsize )) ) {
|
||||||
|
flood_free( flood );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( flood );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* im_draw_flood:
|
||||||
|
* @image: 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 @image 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. @dout may be
|
||||||
|
* NULL.
|
||||||
|
*
|
||||||
|
* See also: im_draw_flood_blob(), im_draw_flood_other().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, or -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
im_draw_flood( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
||||||
|
{
|
||||||
|
Flood *flood;
|
||||||
|
|
||||||
|
if( im_check_coding_known( "im_draw_flood", image ) ||
|
||||||
|
!(flood = flood_new( image, image, 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 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* im_draw_flood_blob:
|
||||||
|
* @image: 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 @image 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. @dout may be
|
||||||
|
* NULL.
|
||||||
|
*
|
||||||
|
* See also: im_draw_flood(), im_draw_flood_other(), im_draw_flood_blob().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, or -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
im_draw_flood_blob( IMAGE *image, int x, int y, VipsPel *ink, Rect *dout )
|
||||||
|
{
|
||||||
|
Flood *flood;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if( im_check_coding_known( "im_draw_flood_blob", image ) ||
|
||||||
|
!(flood = flood_new( image, image, x, y, ink, dout )) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Edge is set by colour of start pixel.
|
||||||
|
*/
|
||||||
|
memcpy( flood->edge, IM_IMAGE_ADDR( image, x, y ), flood->tsize );
|
||||||
|
flood->equal = 1;
|
||||||
|
|
||||||
|
/* If edge == ink, we'll never stop :-( or rather, there's nothing to
|
||||||
|
* do.
|
||||||
|
*/
|
||||||
|
for( j = 0; j < flood->tsize; j++ )
|
||||||
|
if( flood->edge[j] != DRAW( flood )->ink[j] )
|
||||||
|
break;
|
||||||
|
if( j == flood->tsize )
|
||||||
|
return( 0 );
|
||||||
|
|
||||||
|
flood_all( flood, x, y );
|
||||||
|
|
||||||
|
flood_free( flood );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* im_draw_flood_other:
|
||||||
|
* @image: image to mark
|
||||||
|
* @test: image to test
|
||||||
|
* @x: position to start fill
|
||||||
|
* @y: position to start fill
|
||||||
|
* @serial: mark pixels with this number
|
||||||
|
* @dout: output the bounding box of the filled area
|
||||||
|
*
|
||||||
|
* Flood-fill @image with @serial, starting at position @x, @y. The filled
|
||||||
|
* area is bounded by pixels in @test that are equal to the start pixel, in
|
||||||
|
* other words, it searches @test for a blob of same-coloured pixels, marking
|
||||||
|
* those pixels in @image with @serial.
|
||||||
|
*
|
||||||
|
* The bounding box of the modified pixels is returned in @dout. @dout may be
|
||||||
|
* NULL.
|
||||||
|
*
|
||||||
|
* See also: im_draw_flood(), im_label_regions(), im_draw_flood_blob().
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, or -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
im_draw_flood_other( IMAGE *image,
|
||||||
|
IMAGE *test, int x, int y, int serial, Rect *dout )
|
||||||
|
{
|
||||||
|
int *m;
|
||||||
|
Flood *flood;
|
||||||
|
|
||||||
|
if( im_incheck( test ) ||
|
||||||
|
im_check_coding_known( "im_draw_flood_other", test ) ||
|
||||||
|
im_check_uncoded( "im_draw_flood_other", image ) ||
|
||||||
|
im_check_mono( "im_draw_flood_other", image ) ||
|
||||||
|
im_check_format( "im_draw_flood_other", image,
|
||||||
|
IM_BANDFMT_INT ) ||
|
||||||
|
im_check_size_same( "im_draw_flood_other", test, image ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Have we done this point already?
|
||||||
|
*/
|
||||||
|
m = (int *) IM_IMAGE_ADDR( image, x, y );
|
||||||
|
if( *m == serial )
|
||||||
|
return( 0 );
|
||||||
|
|
||||||
|
if( !(flood = flood_new( image, test, x, y, (VipsPel *) &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 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
|
|
||||||
#include "draw.h"
|
#include "old_draw.h"
|
||||||
|
|
||||||
/* Fill a scanline between points x1 and x2 inclusive. x1 < x2.
|
/* Fill a scanline between points x1 and x2 inclusive. x1 < x2.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user