start on inplace

This commit is contained in:
John Cupitt 2014-01-21 19:35:13 +00:00
parent c8c532a068
commit 407f036a87
6 changed files with 203 additions and 106 deletions

View File

@ -289,6 +289,7 @@ void vips_freqfilt_operation_init( void );
void vips_create_operation_init( void ); void vips_create_operation_init( void );
void vips_morphology_operation_init( void ); void vips_morphology_operation_init( void );
void vips_convolution_operation_init( void ); void vips_convolution_operation_init( void );
void vips_draw_operation_init( void );
guint64 vips__parse_size( const char *size_string ); guint64 vips__parse_size( const char *size_string );

View File

@ -44,10 +44,101 @@
#include "draw.h" #include "draw.h"
/**
* SECTION: inplace
* @short_description: in-place paintbox operations: flood, paste, line,
* circle
* @stability: Stable
* @include: vips/vips.h
*
* These operations directly modify the image. They do not thread, on 32-bit
* machines they will be limited to 2GB images, and a little care needs to be
* taken if you use them as part of an image pipeline.
*
* They are mostly supposed to be useful
* for paintbox-style programs.
*
*/
G_DEFINE_ABSTRACT_TYPE( VipsDraw, vips_draw, VIPS_TYPE_OPERATION );
static int
vips_draw_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsDraw *draw = VIPS_DRAW( object );
#ifdef DEBUG
printf( "vips_draw_build: " );
vips_object_print_name( object );
printf( "\n" );
#endif /*DEBUG*/
if( VIPS_OBJECT_CLASS( vips_draw_parent_class )->build( object ) )
return( -1 );
if( vips_image_inplace( draw->im ) )
return( NULL );
draw->lsize = VIPS_IMAGE_SIZEOF_LINE( draw->im );
draw->psize = VIPS_IMAGE_SIZEOF_PEL( draw->im );
draw->noclip = FALSE;
if( !(draw->pixel_ink = vips__vector_to_ink(
class->nickname, draw->im,
draw->ink->data, draw->ink->n )) )
return( -1 );
return( 0 );
}
static void
vips_draw_class_init( VipsDrawClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "draw";
vobject_class->description = _( "Draw operations" );
vobject_class->build = vips_draw_build;
VIPS_ARG_IMAGE( class, "im", 1,
_( "Image" ),
_( "Image to draw on" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsDraw, im ) );
VIPS_ARG_BOXED( class, "ink", 12,
_( "Ink" ),
_( "Colour for pixels" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsDraw, ink ),
VIPS_TYPE_ARRAY_DOUBLE );
}
static void
vips_draw_init( VipsDraw *draw )
{
draw->ink = vips_area_new_array( G_TYPE_DOUBLE, sizeof( double ), 1 );
((double *) (draw->ink->data))[0] = 0;
}
void
vips_draw_operation_init( void )
{
extern GType vips_copy_get_type( void );
vips_copy_get_type();
}
/* Fill a scanline between points x1 and x2 inclusive. x1 < x2. /* Fill a scanline between points x1 and x2 inclusive. x1 < x2.
*/ */
void void
im__draw_scanline( Draw *draw, int y, int x1, int x2 ) vips__draw_scanline( VipsDraw *draw, int y, int x1, int x2 )
{ {
VipsPel *mp; VipsPel *mp;
int i; int i;
@ -55,48 +146,24 @@ im__draw_scanline( Draw *draw, int y, int x1, int x2 )
g_assert( x1 <= x2 ); g_assert( x1 <= x2 );
if( y < 0 || y >= draw->im->Ysize ) if( y < 0 ||
y >= draw->im->Ysize )
return; return;
if( x1 < 0 && x2 < 0 ) if( x1 < 0 &&
x2 < 0 )
return; return;
if( x1 >= draw->im->Xsize && x2 >= draw->im->Xsize ) if( x1 >= draw->im->Xsize &&
x2 >= draw->im->Xsize )
return; return;
x1 = IM_CLIP( 0, x1, draw->im->Xsize - 1 ); x1 = VIPS_CLIP( 0, x1, draw->im->Xsize - 1 );
x2 = IM_CLIP( 0, x2, draw->im->Xsize - 1 ); x2 = VIPS_CLIP( 0, x2, draw->im->Xsize - 1 );
mp = IM_IMAGE_ADDR( draw->im, x1, y ); mp = VIPS_IMAGE_ADDR( draw->im, x1, y );
len = x2 - x1 + 1; len = x2 - x1 + 1;
for( i = 0; i < len; i++ ) { for( i = 0; i < len; i++ ) {
im__draw_pel( draw, mp ); vips__draw_pel( draw, mp );
mp += draw->psize; mp += draw->psize;
} }
} }
void
im__draw_free( Draw *draw )
{
IM_FREE( draw->ink );
}
Draw *
im__draw_init( Draw *draw, IMAGE *im, VipsPel *ink )
{
if( im_rwcheck( im ) )
return( NULL );
draw->im = im;
draw->ink = NULL;
draw->lsize = IM_IMAGE_SIZEOF_LINE( im );
draw->psize = IM_IMAGE_SIZEOF_PEL( im );
draw->noclip = FALSE;
if( ink ) {
if( !(draw->ink = (VipsPel *) im_malloc( NULL, draw->psize )) )
return( NULL );
memcpy( draw->ink, ink, draw->psize );
}
return( draw );
}

View File

@ -28,65 +28,102 @@
*/ */
/* Our state. #ifndef VIPS_DRAW_H
*/ #define VIPS_DRAW_H
typedef struct _Draw {
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus*/
#define VIPS_TYPE_DRAW (vips_draw_get_type())
#define VIPS_DRAW( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
VIPS_TYPE_DRAW, VipsDraw ))
#define VIPS_DRAW_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_CAST( (klass), \
VIPS_TYPE_DRAW, VipsDrawClass))
#define VIPS_IS_DRAW( obj ) \
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_DRAW ))
#define VIPS_IS_DRAW_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_DRAW ))
#define VIPS_DRAW_GET_CLASS( obj ) \
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_DRAW, VipsDrawClass ))
typedef struct _VipsDraw {
VipsOperation parent_instance;
/* Parameters. /* Parameters.
*/ */
IMAGE *im; /* Draw here */ VipsImage *im; /* Draw here */
VipsPel *ink; /* Copy of ink param */ VipsArea *ink; /* With this */
/* Derived stuff. /* Derived stuff.
*/ */
size_t lsize; size_t lsize;
size_t psize; size_t psize;
/* Ink cast to pixel type.
*/
VipsPel *pixel_ink;
/* If the object to draw is entirely within the image, we have a /* If the object to draw is entirely within the image, we have a
* faster noclip path. * faster noclip path.
*/ */
gboolean noclip; gboolean noclip;
} Draw; } VipsDraw;
#define DRAW(X) ((Draw *)(X)) typedef struct _VipsDrawClass {
VipsOperationClass parent_class;
} VipsDrawClass;
GType vips_draw_get_type( void );
static inline void static inline void
im__draw_pel( Draw *draw, VipsPel *q ) vips__draw_pel( VipsDraw *draw, VipsPel *q )
{ {
int j; int j;
/* Faster than memcopy() for n < about 20. /* Faster than memcopy() for n < about 20.
*/ */
for( j = 0; j < draw->psize; j++ ) for( j = 0; j < draw->psize; j++ )
q[j] = draw->ink[j]; q[j] = draw->pixel_ink[j];
} }
/* Paint, with clip. /* Paint, with clip.
*/ */
static inline void static inline void
im__draw_pel_clip( Draw *draw, int x, int y ) vips__draw_pel_clip( VipsDraw *draw, int x, int y )
{ {
if( x < 0 || x >= draw->im->Xsize ) if( x < 0 ||
x >= draw->im->Xsize )
return; return;
if( y < 0 || y >= draw->im->Ysize ) if( y < 0 ||
y >= draw->im->Ysize )
return; return;
im__draw_pel( draw, IM_IMAGE_ADDR( draw->im, x, y ) ); vips__draw_pel( draw, VIPS_IMAGE_ADDR( draw->im, x, y ) );
} }
/* Is p painted? /* Is p painted?
*/ */
static inline gboolean static inline gboolean
im__draw_painted( Draw *draw, VipsPel *p ) vips__draw_painted( VipsDraw *draw, VipsPel *p )
{ {
int j; int j;
for( j = 0; j < draw->psize; j++ ) for( j = 0; j < draw->psize; j++ )
if( p[j] != draw->ink[j] ) if( p[j] != draw->pixel_ink[j] )
break; break;
return( j == draw->psize ); return( j == draw->psize );
} }
void im__draw_scanline( Draw *draw, int y, int x1, int x2 ); void vips__draw_scanline( Draw *draw, int y, int x1, int x2 );
void im__draw_free( Draw *draw );
Draw *im__draw_init( Draw *draw, IMAGE *im, VipsPel *ink ); #ifdef __cplusplus
}
#endif /*__cplusplus*/
#endif /*VIPS_DRAW_H*/

View File

@ -28,6 +28,8 @@
* level * level
* 27/9/10 * 27/9/10
* - use Draw base class * - use Draw base class
* 21/1/14
* - redo as a class
*/ */
/* /*
@ -97,16 +99,16 @@ typedef struct _Buffer {
Scan scan[PBUFSIZE]; Scan scan[PBUFSIZE];
} Buffer; } Buffer;
/* Our state. /* Base class.
*/ */
typedef struct { typedef struct _VipsFlood {
Draw draw; VipsDraw draw;
/* Parameters. /* Parameters.
*/ */
IMAGE *test; /* Test this image */ VipsImage *test; /* Test this image */
int x, y; int x, y;
Rect *dout; /* Write dirty here at end */ VipsRect *dout; /* Write dirty here at end */
/* Derived stuff. /* Derived stuff.
*/ */
@ -120,7 +122,11 @@ typedef struct {
*/ */
Buffer *in; Buffer *in;
Buffer *out; Buffer *out;
} Flood; } VipsFlood;
typedef VipsDrawClass VipsFloodClass;
G_DEFINE_ABSTRACT_TYPE( VipsFlood, vips_flood, VIPS_TYPE_DRAW );
/* Alloc a new buffer. /* Alloc a new buffer.
*/ */
@ -154,14 +160,15 @@ buffer_free( Buffer *buf )
* the new head buffer. * the new head buffer.
*/ */
static inline Buffer * static inline Buffer *
buffer_add( Buffer *buf, Flood *flood, int x1, int x2, int y, int dir ) buffer_add( Buffer *buf, VipsFlood *flood, int x1, int x2, int y, int dir )
{ {
/* Clip against image size. /* Clip against image size.
*/ */
if( y < 0 || y >= flood->test->Ysize ) if( y < 0 ||
y >= flood->test->Ysize )
return( buf ); return( buf );
x1 = IM_CLIP( 0, x1, flood->test->Xsize - 1 ); x1 = VIPS_CLIP( 0, x1, flood->test->Xsize - 1 );
x2 = IM_CLIP( 0, x2, flood->test->Xsize - 1 ); x2 = VIPS_CLIP( 0, x2, flood->test->Xsize - 1 );
if( x2 - x1 < 0 ) if( x2 - x1 < 0 )
return( buf ); return( buf );
@ -187,7 +194,7 @@ buffer_add( Buffer *buf, Flood *flood, int x1, int x2, int y, int dir )
* pixels. * pixels.
*/ */
static inline gboolean static inline gboolean
flood_connected( Flood *flood, VipsPel *tp ) flood_connected( VipsFlood *flood, VipsPel *tp )
{ {
int j; int j;
@ -204,24 +211,24 @@ flood_connected( Flood *flood, VipsPel *tp )
* connected and unpainted. * connected and unpainted.
*/ */
static void static void
flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 ) flood_scanline( VipsFlood *flood, int x, int y, int *x1, int *x2 )
{ {
Draw *draw = DRAW( flood ); VipsDraw *draw = VIPS_DRAW( flood );
const int width = flood->test->Xsize; const int width = flood->test->Xsize;
VipsPel *tp; VipsPel *tp;
int i; int i;
g_assert( flood_connected( flood, g_assert( flood_connected( flood,
IM_IMAGE_ADDR( flood->test, x, y ) ) ); VIPS_IMAGE_ADDR( flood->test, x, y ) ) );
g_assert( !im__draw_painted( draw, g_assert( !vips__draw_painted( draw,
IM_IMAGE_ADDR( draw->im, x, y ) ) ); VIPS_IMAGE_ADDR( draw->im, x, y ) ) );
/* Search to the right for the first non-connected pixel. If the start /* Search to the right for the first non-connected pixel. If the start
* pixel is unpainted, we know all the intervening pixels must be * pixel is unpainted, we know all the intervening pixels must be
* unpainted too. * unpainted too.
*/ */
tp = IM_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( !flood_connected( flood, tp ) )
break; break;
@ -231,7 +238,7 @@ flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 )
/* Search left. /* Search left.
*/ */
tp = IM_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( !flood_connected( flood, tp ) )
break; break;
@ -241,13 +248,13 @@ flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 )
/* Paint the range we discovered. /* Paint the range we discovered.
*/ */
im__draw_scanline( draw, y, *x1, *x2 ); vips__draw_scanline( draw, y, *x1, *x2 );
if( flood->dout ) { if( flood->dout ) {
flood->left = IM_MIN( flood->left, *x1 ); flood->left = VIPS_MIN( flood->left, *x1 );
flood->right = IM_MAX( flood->right, *x2 ); flood->right = VIPS_MAX( flood->right, *x2 );
flood->top = IM_MIN( flood->top, y ); flood->top = VIPS_MIN( flood->top, y );
flood->bottom = IM_MAX( flood->bottom, y ); flood->bottom = VIPS_MAX( flood->bottom, y );
} }
} }
@ -255,16 +262,17 @@ flood_scanline( Flood *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( Flood *flood, Scan *scan ) flood_around( VipsFlood *flood, Scan *scan )
{ {
Draw *draw = DRAW( flood ); VipsDraw *draw = VIPS_DRAW( flood );
VipsPel *tp; VipsPel *tp;
int x; int x;
g_assert( scan->dir == 1 || scan->dir == -1 ); g_assert( scan->dir == 1 ||
scan->dir == -1 );
for( tp = IM_IMAGE_ADDR( flood->test, scan->x1, scan->y ), for( tp = VIPS_IMAGE_ADDR( flood->test, scan->x1, scan->y ),
x = scan->x1; x = scan->x1;
x <= scan->x2; x <= scan->x2;
tp += flood->tsize, x++ ) { tp += flood->tsize, x++ ) {
@ -277,10 +285,10 @@ flood_around( Flood *flood, Scan *scan )
* connected loops. * connected loops.
*/ */
if( draw->im != flood->test ) { if( draw->im != flood->test ) {
VipsPel *mp = IM_IMAGE_ADDR( VipsPel *mp = VIPS_IMAGE_ADDR(
draw->im, x, scan->y ); draw->im, x, scan->y );
if( im__draw_painted( draw, mp ) ) if( vips__draw_painted( draw, mp ) )
continue; continue;
} }
@ -303,7 +311,7 @@ flood_around( Flood *flood, Scan *scan )
scan->dir ); scan->dir );
x = x2a + 1; x = x2a + 1;
tp = IM_IMAGE_ADDR( flood->test, x, scan->y ); tp = VIPS_IMAGE_ADDR( flood->test, x, scan->y );
} }
} }
} }
@ -335,12 +343,12 @@ flood_all( Flood *flood, int x, int y )
p->n = 0; p->n = 0;
} }
IM_SWAP( Buffer *, flood->in, flood->out ); VIPS_SWAP( Buffer *, flood->in, flood->out );
} }
} }
static void static void
flood_free( Flood *flood ) flood_free( VipsFlood *flood )
{ {
/* Write dirty back to caller. /* Write dirty back to caller.
*/ */
@ -351,11 +359,10 @@ flood_free( Flood *flood )
flood->dout->height = flood->bottom - flood->top + 1; flood->dout->height = flood->bottom - flood->top + 1;
} }
im__draw_free( DRAW( flood ) ); VIPS_FREE( flood->edge );
IM_FREE( flood->edge ); VIPS_FREEF( buffer_free, flood->in );
IM_FREEF( buffer_free, flood->in ); VIPS_FREEF( buffer_free, flood->out );
IM_FREEF( buffer_free, flood->out ); vips_free( flood );
im_free( flood );
} }
static Flood * static Flood *

View File

@ -39,22 +39,6 @@
#include <vips/vips.h> #include <vips/vips.h>
/**
* SECTION: inplace
* @short_description: in-place paintbox operations: flood, paste, line,
* circle
* @stability: Stable
* @include: vips/vips.h
*
* These operations directly modify the image. They do not thread, on 32-bit
* machines they will be limited to 2GB images, and a little care needs to be
* taken if you use them as part of an image pipeline.
*
* They are mostly supposed to be useful
* for paintbox-style programs.
*
*/
/* Calculate a pixel for an image from a vec of double. Valid while im is /* Calculate a pixel for an image from a vec of double. Valid while im is
* valid. * valid.
*/ */

View File

@ -272,6 +272,7 @@ vips__init( const char *argv0 )
vips_convolution_operation_init(); vips_convolution_operation_init();
vips_freqfilt_operation_init(); vips_freqfilt_operation_init();
vips_morphology_operation_init(); vips_morphology_operation_init();
vips_draw_operation_init();
/* Load up any plugins in the vips libdir. We don't error on failure, /* Load up any plugins in the vips libdir. We don't error on failure,
* it's too annoying to have VIPS refuse to start because of a broken * it's too annoying to have VIPS refuse to start because of a broken