diff --git a/ChangeLog b/ChangeLog index f34edb53..a0758e3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,7 @@ - old code still there as vips_shrink2() for testing - only allow [] for filename options - add memory.h to Python API .. makes tracked highwater visible +- added bandjoin_const to add constant bands to an image 7/5/15 started 8.1.1 - oop, vips-8.0 wrapper script should be vips-8.1, thanks Danilo diff --git a/TODO b/TODO index b67d0a3e..17144f20 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,8 @@ -- started bandjoinconst +- pyvips should use bandjoin_const when possible - see also unaryconst.c + benchmark, maybe not worthwhile + +- test suite is broken, float->int mask I guess - looks like we have a race in tiled threadcache? see diff --git a/libvips/conversion/bandjoin.c b/libvips/conversion/bandjoin.c index 5e65bed2..86a4bc45 100644 --- a/libvips/conversion/bandjoin.c +++ b/libvips/conversion/bandjoin.c @@ -24,6 +24,8 @@ * - sizealike inputs * 27/10/11 * - rewrite as a class + * 7/11/15 + * - added bandjoin_const */ /* @@ -278,5 +280,197 @@ typedef struct _VipsBandjoinConst { typedef VipsBandaryClass VipsBandjoinConstClass; -//G_DEFINE_TYPE( VipsBandjoinConst, vips_bandjoin_const, VIPS_TYPE_BANDARY ); +G_DEFINE_TYPE( VipsBandjoinConst, vips_bandjoin_const, VIPS_TYPE_BANDARY ); +static void +vips_bandjoin_const_finalize( GObject *object ) +{ + VipsBandjoinConst *bandjoin = (VipsBandjoinConst *) object; + + VIPS_FREE( bandjoin->c_ready ); + + G_OBJECT_CLASS( vips_bandjoin_const_parent_class )->finalize( object ); +} + +static void +vips_bandjoin_const_buffer( VipsBandary *bandary, + VipsPel *q, VipsPel **p, int width ) +{ + VipsConversion *conversion = (VipsConversion *) bandary; + VipsBandjoinConst *bandjoin = (VipsBandjoinConst *) bandary; + VipsImage *in = bandary->ready[0]; + + /* Output pel size. + */ + const int ops = VIPS_IMAGE_SIZEOF_PEL( conversion->out ); + + /* Input pel size. + */ + const int ips = VIPS_IMAGE_SIZEOF_PEL( in ); + + /* Extra bands size. + */ + const int ebs = ops - ips; + + VipsPel * restrict p1; + VipsPel * restrict q1; + int x, z; + + q1 = q; + p1 = p[0]; + + for( x = 0; x < width; x++ ) { + for( z = 0; z < ips; z++ ) + q1[z] = p1[z]; + + p1 += ips; + q1 += ips; + + for( z = 0; z < ebs; z++ ) + q1[z] = bandjoin->c_ready[z]; + + q1 += ebs; + } +} + +static int +vips_bandjoin_const_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsBandary *bandary = (VipsBandary *) object; + VipsBandjoinConst *bandjoin = (VipsBandjoinConst *) object; + + if( bandjoin->c && + bandjoin->in ) { + double *c; + int n; + + c = vips_array_double_get( bandjoin->c, &n ); + + if( n == 0 ) + return( vips_bandary_copy( bandary ) ); + else + bandary->out_bands = bandjoin->in->Bands + n; + + bandary->n = 1; + bandary->in = &bandjoin->in; + + if( !(bandjoin->c_ready = vips__vector_to_pels( class->nickname, + n, bandjoin->in->BandFmt, bandjoin->in->Coding, + c, NULL, n )) ) + return( -1 ); + + } + + if( VIPS_OBJECT_CLASS( vips_bandjoin_const_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_bandjoin_const_class_init( VipsBandjoinConstClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsBandaryClass *bandary_class = VIPS_BANDARY_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_bandjoin_const_class_init\n" ); + + gobject_class->finalize = vips_bandjoin_const_finalize; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "bandjoin_const"; + vobject_class->description = _( "append a constant band to an image" ); + vobject_class->build = vips_bandjoin_const_build; + + bandary_class->process_line = vips_bandjoin_const_buffer; + + VIPS_ARG_IMAGE( class, "in", 0, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsBandjoinConst, in ) ); + + VIPS_ARG_BOXED( class, "c", 12, + _( "Constants" ), + _( "Array of constants to add" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsBandjoinConst, c ), + VIPS_TYPE_ARRAY_DOUBLE ); + +} + +static void +vips_bandjoin_const_init( VipsBandjoinConst *bandjoin ) +{ + /* Init our instance fields. + */ +} + +static int +vips_bandjoin_constv( VipsImage *in, VipsImage **out, + double *c, int n, va_list ap ) +{ + VipsArrayDouble *array; + int result; + + array = vips_array_double_new( c, n ); + result = vips_call_split( "bandjoin_const", ap, in, out, array ); + vips_area_unref( VIPS_AREA( array ) ); + + return( result ); +} + +/** + * vips_bandjoin_const: + * @in: (array length=n) (transfer none): array of input images + * @out: output image + * @c: (array length=n): array of constants to append + * @n: number of constants + * @...: %NULL-terminated list of optional named arguments + * + * Append a set of constant bands to an image. + * + * See also: vips_bandjoin(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_bandjoin_const( VipsImage *in, VipsImage **out, double *c, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_bandjoin_constv( in, out, c, n, ap ); + va_end( ap ); + + return( result ); +} + +/** + * vips_bandjoin_const1: + * @in: input image + * @out: output image + * @c: constant to append + * @...: %NULL-terminated list of optional named arguments + * + * Append a single constant band to an image. + * + * Returns: 0 on success, -1 on error + */ +int +vips_bandjoin_const1( VipsImage *in, VipsImage **out, double c, ... ) +{ + va_list ap; + int result; + + va_start( ap, c ); + result = vips_bandjoin_constv( in, out, &c, 1, ap ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index 947425bb..9ec4f420 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -229,6 +229,7 @@ vips_conversion_operation_init( void ) extern GType vips_replicate_get_type( void ); extern GType vips_cast_get_type( void ); extern GType vips_bandjoin_get_type( void ); + extern GType vips_bandjoin_const_get_type( void ); extern GType vips_bandrank_get_type( void ); extern GType vips_black_get_type( void ); extern GType vips_rot_get_type( void ); @@ -273,6 +274,7 @@ vips_conversion_operation_init( void ) vips_replicate_get_type(); vips_cast_get_type(); vips_bandjoin_get_type(); + vips_bandjoin_const_get_type(); vips_bandrank_get_type(); vips_black_get_type(); vips_rot_get_type(); diff --git a/libvips/conversion/insert.c b/libvips/conversion/insert.c index d01ecf6f..534df5c7 100644 --- a/libvips/conversion/insert.c +++ b/libvips/conversion/insert.c @@ -208,65 +208,79 @@ vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) return( 0 ); } -/* Calculate a pixel for an image from a vec of double. Valid while im is - * valid. imag can be NULL, meaning all zero for the imaginary component. +/* Make a pair of vector constants into a set of formatted pixels. bands can + * be 3 while n is 1, meaning expand the constant to the number of bands. + * imag can be NULL, meaning all zero for the imaginary component. */ VipsPel * -vips__vector_to_ink( const char *domain, - VipsImage *im, double *real, double *imag, int n ) +vips__vector_to_pels( const char *domain, + int bands, VipsBandFormat format, VipsCoding coding, + double *real, double *imag, int n ) { /* Run our pipeline relative to this. */ VipsImage *context = vips_image_new(); VipsImage **t = (VipsImage **) - vips_object_local_array( VIPS_OBJECT( context ), 6 ); + vips_object_local_array( VIPS_OBJECT( context ), 8 ); - VipsBandFormat format; - int bands; + VipsImage *in; double *ones; VipsPel *result; int i; #ifdef VIPS_DEBUG - printf( "vips__vector_to_ink: starting\n" ); + printf( "vips__vector_to_pels: starting\n" ); #endif /*VIPS_DEBUG*/ - vips_image_decode_predict( im, &bands, &format ); - ones = VIPS_ARRAY( im, n, double ); + ones = VIPS_ARRAY( context, n, double ); for( i = 0; i < n; i++ ) ones[i] = 1.0; - /* Cast vec to match the decoded image. + /* Make the real and imaginary parts. */ - if( vips_black( &t[1], 1, 1, "bands", bands, NULL ) || - vips_linear( t[1], &t[2], ones, real, n, NULL ) || - vips_cast( t[2], &t[3], format, NULL ) ) { + if( vips_black( &t[0], 1, 1, "bands", bands, NULL ) || + vips_linear( t[0], &t[1], ones, real, n, NULL ) ) { g_object_unref( context ); return( NULL ); } + in = t[1]; + + if( imag ) { + if( vips_black( &t[2], 1, 1, "bands", bands, NULL ) || + vips_linear( t[2], &t[3], ones, imag, n, NULL ) || + vips_complexform( in, t[3], &t[4], NULL ) ) { + g_object_unref( context ); + return( NULL ); + } + in = t[4]; + } - /* And now recode the vec to match the original im. + /* Cast to the output type and coding. */ - if( vips_image_encode( t[3], &t[4], im->Coding ) || - !(t[5] = vips_image_new_memory()) || - vips_image_write( t[4], t[5] ) ) { + if( vips_cast( in, &t[5], format, NULL ) || + vips_image_encode( t[5], &t[6], coding ) ) { g_object_unref( context ); return( NULL ); } + in = t[6]; + + /* Write to memory, copy to output buffer. + */ + if( !(t[7] = vips_image_new_memory()) || + vips_image_write( in, t[7] ) ) { + g_object_unref( context ); + return( NULL ); + } + in = t[7]; if( !(result = - VIPS_ARRAY( im, VIPS_IMAGE_SIZEOF_PEL( t[5] ), VipsPel )) ) { + VIPS_ARRAY( NULL, VIPS_IMAGE_SIZEOF_PEL( in ), VipsPel )) ) { g_object_unref( context ); return( NULL ); } - g_assert( VIPS_IMAGE_SIZEOF_PEL( t[5] ) == - VIPS_IMAGE_SIZEOF_PEL( im ) ); - - memcpy( result, t[5]->data, VIPS_IMAGE_SIZEOF_PEL( im ) ); - - g_object_unref( context ); + memcpy( result, in->data, VIPS_IMAGE_SIZEOF_PEL( in ) ); #ifdef VIPS_DEBUG { @@ -278,12 +292,43 @@ vips__vector_to_ink( const char *domain, printf( "(%g, %g) ", real[i], imag ? imag[i] : 0 ); printf( "\n" ); printf( "\tink = " ); - for( i = 0; i < VIPS_IMAGE_SIZEOF_PEL( im ); i++ ) + for( i = 0; i < VIPS_IMAGE_SIZEOF_PEL( in ); i++ ) printf( "%d ", result[i] ); printf( "\n" ); } #endif /*VIPS_DEBUG*/ + g_object_unref( context ); + + return( result ); +} + +static void +vips__vector_to_ink_cb( VipsObject *object, char *buf ) +{ + g_free( buf ); +} + +/* Calculate a pixel for an image from a vec of double. Valid while im is + * valid. + */ +VipsPel * +vips__vector_to_ink( const char *domain, + VipsImage *im, double *real, double *imag, int n ) +{ + int bands; + VipsBandFormat format; + VipsPel *result; + + vips_image_decode_predict( im, &bands, &format ); + + if( !(result = vips__vector_to_pels( domain, + bands, format, im->Coding, real, imag, n )) ) + return( NULL ); + + g_signal_connect( im, "postclose", + G_CALLBACK( vips__vector_to_ink_cb ), result ); + return( result ); } diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index bd36c0af..979eed81 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -171,6 +171,9 @@ int vips__bandalike( const char *domain, /* draw */ +VipsPel *vips__vector_to_pels( const char *domain, + int bands, VipsBandFormat format, VipsCoding coding, + double *real, double *imag, int n ); VipsPel *vips__vector_to_ink( const char *domain, VipsImage *im, double *real, double *imag, int n ); double *vips__ink_to_vector( const char *domain,