start on inplace
This commit is contained in:
parent
c8c532a068
commit
407f036a87
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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*/
|
||||
|
@ -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 *
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user