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_morphology_operation_init( void );
void vips_convolution_operation_init( void );
void vips_draw_operation_init( void );
guint64 vips__parse_size( const char *size_string );

View File

@ -44,10 +44,101 @@
#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.
*/
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;
int i;
@ -55,48 +146,24 @@ im__draw_scanline( Draw *draw, int y, int x1, int x2 )
g_assert( x1 <= x2 );
if( y < 0 || y >= draw->im->Ysize )
if( y < 0 ||
y >= draw->im->Ysize )
return;
if( x1 < 0 && x2 < 0 )
if( x1 < 0 &&
x2 < 0 )
return;
if( x1 >= draw->im->Xsize && x2 >= draw->im->Xsize )
if( x1 >= draw->im->Xsize &&
x2 >= draw->im->Xsize )
return;
x1 = IM_CLIP( 0, x1, draw->im->Xsize - 1 );
x2 = IM_CLIP( 0, x2, draw->im->Xsize - 1 );
x1 = VIPS_CLIP( 0, x1, 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;
for( i = 0; i < len; i++ ) {
im__draw_pel( draw, mp );
vips__draw_pel( draw, mp );
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.
*/
typedef struct _Draw {
#ifndef VIPS_DRAW_H
#define VIPS_DRAW_H
#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.
*/
IMAGE *im; /* Draw here */
VipsPel *ink; /* Copy of ink param */
VipsImage *im; /* Draw here */
VipsArea *ink; /* With this */
/* Derived stuff.
*/
size_t lsize;
size_t psize;
/* Ink cast to pixel type.
*/
VipsPel *pixel_ink;
/* If the object to draw is entirely within the image, we have a
* faster noclip path.
*/
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
im__draw_pel( Draw *draw, VipsPel *q )
vips__draw_pel( VipsDraw *draw, VipsPel *q )
{
int j;
/* Faster than memcopy() for n < about 20.
*/
for( j = 0; j < draw->psize; j++ )
q[j] = draw->ink[j];
q[j] = draw->pixel_ink[j];
}
/* Paint, with clip.
*/
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;
if( y < 0 || y >= draw->im->Ysize )
if( y < 0 ||
y >= draw->im->Ysize )
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?
*/
static inline gboolean
im__draw_painted( Draw *draw, VipsPel *p )
vips__draw_painted( VipsDraw *draw, VipsPel *p )
{
int j;
for( j = 0; j < draw->psize; j++ )
if( p[j] != draw->ink[j] )
if( p[j] != draw->pixel_ink[j] )
break;
return( j == draw->psize );
}
void im__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 );
void vips__draw_scanline( Draw *draw, int y, int x1, int x2 );
#ifdef __cplusplus
}
#endif /*__cplusplus*/
#endif /*VIPS_DRAW_H*/

View File

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

View File

@ -39,22 +39,6 @@
#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
* valid.
*/

View File

@ -272,6 +272,7 @@ vips__init( const char *argv0 )
vips_convolution_operation_init();
vips_freqfilt_operation_init();
vips_morphology_operation_init();
vips_draw_operation_init();
/* 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