From 8d367998c6326eda3fb852cf0fb461ec675edf4b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 4 Apr 2011 15:46:57 +0100 Subject: [PATCH] VipsAdd works done VipsAdd class and made an im_add() stub for compat --- ChangeLog | 1 + TODO | 4 - libvips/arithmetic/Makefile.am | 1 - libvips/arithmetic/add.c | 137 +++++++- libvips/arithmetic/binary.c | 22 +- libvips/arithmetic/im_add.c | 545 ------------------------------- libvips/deprecated/vips7compat.c | 254 ++++++++++++++ libvips/include/vips/object.h | 4 +- libvips/include/vips/private.h | 4 + libvips/iofuncs/init.c | 4 + libvips/iofuncs/object.c | 32 +- libvips/iofuncs/operation.c | 79 +++-- 12 files changed, 463 insertions(+), 624 deletions(-) delete mode 100644 libvips/arithmetic/im_add.c diff --git a/ChangeLog b/ChangeLog index c274ba14..602791cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -45,6 +45,7 @@ - all of iofuncs moved to vips_ namespace - lots of old iofuncs API moved to deprecated - added VipsOperation, an abstract base class for all vips operations +- added VipsAdd, the first operation object 30/11/10 started 7.24.0 - bump for new stable diff --git a/TODO b/TODO index 1b13642c..847583ee 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,4 @@ -- we have VipsOperation ... now try VipsArithmetic and VipsBinary, then we can - do VipsAdd - - - add MATLAB write diff --git a/libvips/arithmetic/Makefile.am b/libvips/arithmetic/Makefile.am index a98ae8e2..feb436f3 100644 --- a/libvips/arithmetic/Makefile.am +++ b/libvips/arithmetic/Makefile.am @@ -3,7 +3,6 @@ noinst_LTLIBRARIES = libarithmetic.la libarithmetic_la_SOURCES = \ arith_dispatch.c \ im_abs.c \ - im_add.c \ im_avg.c \ im_bandmean.c \ im_cross_phase.c \ diff --git a/libvips/arithmetic/add.c b/libvips/arithmetic/add.c index 6805181d..dc7e43ac 100644 --- a/libvips/arithmetic/add.c +++ b/libvips/arithmetic/add.c @@ -1,4 +1,41 @@ /* add operation + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 J.Cupitt + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * 9/5/95 JC + * - simplified: now just handles 10 cases (instead of 50), using + * im_clip2*() to help + * - now uses im_wrapmany() rather than im_generate() + * 31/5/96 JC + * - SWAP() removed, *p++ removed + * 27/9/04 + * - im__cast_and_call() now matches bands as well + * - ... so 1 band + 4 band image -> 4 band image + * 8/12/06 + * - add liboil support + * 18/8/08 + * - revise upcasting system + * - im__cast_and_call() no longer sets bbits for you + * - add gtkdoc comments + * - remove separate complex case, just double size + * 11/9/09 + * - im__cast_and_call() becomes im__arith_binary() + * - more of operation scaffold moved inside + * 25/7/10 + * - remove oil support again ... we'll try Orc instead + * 29/10/10 + * - move to VipsVector for Orc support + * 28/2/11 + * - argh vector int/uint was broken + * 4/4/11 + * - rewrite as a class */ /* @@ -49,24 +86,88 @@ #include #endif /*WITH_DMALLOC*/ -/* VipsAdd class +/** + * VipsAdd: + * @in1: input image + * @in2: input image + * @out: output image + * + * This operation calculates @in1 + @in2 and writes the result to @out. + * The images must be the same size. They may have any format. + * + * If the number of bands differs, one of the images + * must have one band. In this case, an n-band image is formed from the + * one-band image by joining n copies of the one-band image together, and then + * the two n-band images are operated upon. + * + * The two input images are cast up to the smallest common type (see table + * Smallest common format in + * arithmetic), then the + * following table is used to determine the output type: + * + * + * im_add() type promotion + * + * + * + * input type + * output type + * + * + * + * + * uchar + * ushort + * + * + * char + * short + * + * + * ushort + * uint + * + * + * short + * int + * + * + * uint + * uint + * + * + * int + * int + * + * + * float + * float + * + * + * double + * double + * + * + * complex + * complex + * + * + * double complex + * double complex + * + * + * + *
+ * + * In other words, the output type is just large enough to hold the whole + * range of possible values. + * + * Operations on integer images are performed using the processor's vector unit, + * if possible. Disable this with --vips-novector or IM_NOVECTOR. + * + * See also: im_subtract(), im_lintra(). */ -#define VIPS_TYPE_ADD (vips_add_get_type()) -#define VIPS_ADD( obj ) \ - (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_ADD, VipsAdd )) -#define VIPS_ADD_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_ADD, VipsAddClass)) -#define VIPS_IS_ADD( obj ) \ - (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_ADD )) -#define VIPS_IS_ADD_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_ADD )) -#define VIPS_ADD_GET_CLASS( obj ) \ - (G_TYPE_INSTANCE_GET_CLASS( (obj), \ - VIPS_TYPE_ADD, VipsAddClass )) - typedef VipsBinary VipsAdd; typedef VipsBinaryClass VipsAddClass; @@ -162,10 +263,14 @@ static int bandfmt_add[10] = { static void vips_add_class_init( VipsAddClass *class ) { + VipsObjectClass *object_class = (VipsObjectClass *) class; VipsArithmeticClass *aclass = VIPS_ARITHMETIC_CLASS( class ); VipsBinaryClass *bclass = VIPS_BINARY_CLASS( class ); VipsVector *v; + object_class->nickname = "add"; + object_class->description = _( "add two images" ); + vips_arithmetic_set_format_table( aclass, bandfmt_add ); v = vips_arithmetic_get_program( aclass, VIPS_FORMAT_UCHAR ); diff --git a/libvips/arithmetic/binary.c b/libvips/arithmetic/binary.c index c65f3e34..3086fa26 100644 --- a/libvips/arithmetic/binary.c +++ b/libvips/arithmetic/binary.c @@ -252,7 +252,7 @@ static int vips_binary_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - const char *domain = class->description; + const char *domain = class->nickname; VipsArithmetic *arithmetic = VIPS_ARITHMETIC( object ); VipsArithmeticClass *aclass = VIPS_ARITHMETIC_GET_CLASS( arithmetic ); VipsBinary *binary = VIPS_BINARY( object ); @@ -318,16 +318,6 @@ vips_binary_class_init( VipsBinaryClass *class ) /* Create properties. */ - pspec = g_param_spec_object( "left-image", - "Left", "Left-hand image argument", - VIPS_TYPE_IMAGE, - G_PARAM_READWRITE ); - g_object_class_install_property( gobject_class, - PROP_LEFT, pspec ); - vips_object_class_install_argument( vobject_class, pspec, - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsBinary, left ) ); - pspec = g_param_spec_object( "right-image", "Right", "Right-hand image argument", VIPS_TYPE_IMAGE, @@ -337,6 +327,16 @@ vips_binary_class_init( VipsBinaryClass *class ) vips_object_class_install_argument( vobject_class, pspec, VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsBinary, right ) ); + + pspec = g_param_spec_object( "left-image", + "Left", "Left-hand image argument", + VIPS_TYPE_IMAGE, + G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, + PROP_LEFT, pspec ); + vips_object_class_install_argument( vobject_class, pspec, + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsBinary, left ) ); } static void diff --git a/libvips/arithmetic/im_add.c b/libvips/arithmetic/im_add.c deleted file mode 100644 index 70bdc888..00000000 --- a/libvips/arithmetic/im_add.c +++ /dev/null @@ -1,545 +0,0 @@ -/* im_add.c - * - * Copyright: 1990, N. Dessipris. - * - * Author: Nicos Dessipris - * Written on: 02/05/1990 - * Modified on: - * 29/4/93 J.Cupitt - * - now works for partial images - * 1/7/93 JC - * - adapted for partial v2 - * 9/5/95 JC - * - simplified: now just handles 10 cases (instead of 50), using - * im_clip2*() to help - * - now uses im_wrapmany() rather than im_generate() - * 31/5/96 JC - * - SWAP() removed, *p++ removed - * 27/9/04 - * - im__cast_and_call() now matches bands as well - * - ... so 1 band + 4 band image -> 4 band image - * 8/12/06 - * - add liboil support - * 18/8/08 - * - revise upcasting system - * - im__cast_and_call() no longer sets bbits for you - * - add gtkdoc comments - * - remove separate complex case, just double size - * 11/9/09 - * - im__cast_and_call() becomes im__arith_binary() - * - more of operation scaffold moved inside - * 25/7/10 - * - remove oil support again ... we'll try Orc instead - * 29/10/10 - * - move to VipsVector for Orc support - * 28/2/11 - * - argh vector int/uint was broken - */ - -/* - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include -#include -#include - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -#define LOOP( IN, OUT ) { \ - IN *p1 = (IN *) in[0]; \ - IN *p2 = (IN *) in[1]; \ - OUT *q = (OUT *) out; \ - \ - for( x = 0; x < sz; x++ ) \ - q[x] = p1[x] + p2[x]; \ -} - -static VipsVector *add_vectors[IM_BANDFMT_LAST] = { NULL }; - -static void -add_buffer( PEL **in, PEL *out, int width, IMAGE *im ) -{ - /* Complex just doubles the size. - */ - const int sz = width * im->Bands * - (vips_bandfmt_iscomplex( im->BandFmt ) ? 2 : 1); - - if( vips_vector_get_enabled() && - add_vectors[im->BandFmt] ) { - VipsVector *vector = add_vectors[im->BandFmt]; - - VipsExecutor ex; - - vips_executor_set_program( &ex, vector, sz ); - vips_executor_set_array( &ex, vector->s[0], in[0] ); - vips_executor_set_array( &ex, vector->s[1], in[1] ); - vips_executor_set_destination( &ex, out ); - - vips_executor_run( &ex ); - } - else { - int x; - - /* Add all input types. Keep types here in sync with - * bandfmt_add[] below. - */ - switch( im->BandFmt ) { - case IM_BANDFMT_UCHAR: - LOOP( unsigned char, unsigned short ); break; - case IM_BANDFMT_CHAR: - LOOP( signed char, signed short ); break; - case IM_BANDFMT_USHORT: - LOOP( unsigned short, unsigned int ); break; - case IM_BANDFMT_SHORT: - LOOP( signed short, signed int ); break; - case IM_BANDFMT_UINT: - LOOP( unsigned int, unsigned int ); break; - case IM_BANDFMT_INT: - LOOP( signed int, signed int ); break; - - case IM_BANDFMT_FLOAT: - case IM_BANDFMT_COMPLEX: - LOOP( float, float ); break; - - case IM_BANDFMT_DOUBLE: - case IM_BANDFMT_DPCOMPLEX: - LOOP( double, double ); break; - - default: - g_assert( 0 ); - } - } -} - -/* Save a bit of typing. - */ -#define UC IM_BANDFMT_UCHAR -#define C IM_BANDFMT_CHAR -#define US IM_BANDFMT_USHORT -#define S IM_BANDFMT_SHORT -#define UI IM_BANDFMT_UINT -#define I IM_BANDFMT_INT -#define F IM_BANDFMT_FLOAT -#define X IM_BANDFMT_COMPLEX -#define D IM_BANDFMT_DOUBLE -#define DX IM_BANDFMT_DPCOMPLEX - -/* For two integer types, the "largest", ie. one which can represent the - * full range of both. - */ -static int bandfmt_largest[6][6] = { - /* UC C US S UI I */ -/* UC */ { UC, S, US, S, UI, I }, -/* C */ { S, C, I, S, I, I }, -/* US */ { US, I, US, I, UI, I }, -/* S */ { S, S, I, S, I, I }, -/* UI */ { UI, I, UI, I, UI, I }, -/* I */ { I, I, I, I, I, I } -}; - -/* For two formats, find one which can represent the full range of both. - */ -static VipsBandFmt -im__format_common( VipsBandFmt in1, VipsBandFmt in2 ) -{ - if( vips_bandfmt_iscomplex( in1 ) || - vips_bandfmt_iscomplex( in2 ) ) { - /* What kind of complex? - */ - if( in1 == IM_BANDFMT_DPCOMPLEX || in2 == IM_BANDFMT_DPCOMPLEX ) - /* Output will be DPCOMPLEX. - */ - return( IM_BANDFMT_DPCOMPLEX ); - else - return( IM_BANDFMT_COMPLEX ); - - } - else if( vips_bandfmt_isfloat( in1 ) || - vips_bandfmt_isfloat( in2 ) ) { - /* What kind of float? - */ - if( in1 == IM_BANDFMT_DOUBLE || in2 == IM_BANDFMT_DOUBLE ) - return( IM_BANDFMT_DOUBLE ); - else - return( IM_BANDFMT_FLOAT ); - } - else - /* Must be int+int -> int. - */ - return( bandfmt_largest[in1][in2] ); -} - -int -im__formatalike_vec( IMAGE **in, IMAGE **out, int n ) -{ - int i; - VipsBandFmt fmt; - - g_assert( n >= 1 ); - - fmt = in[0]->BandFmt; - for( i = 1; i < n; i++ ) - fmt = im__format_common( fmt, in[i]->BandFmt ); - - for( i = 0; i < n; i++ ) - if( im_clip2fmt( in[i], out[i], fmt ) ) - return( -1 ); - - return( 0 ); -} - -int -im__formatalike( IMAGE *in1, IMAGE *in2, IMAGE *out1, IMAGE *out2 ) -{ - IMAGE *in[2]; - IMAGE *out[2]; - - in[0] = in1; - in[1] = in2; - out[0] = out1; - out[1] = out2; - - return( im__formatalike_vec( in, out, 2 ) ); -} - -/* Make an n-band image. Input 1 or n bands. - */ -int -im__bandup( const char *domain, IMAGE *in, IMAGE *out, int n ) -{ - IMAGE *bands[256]; - int i; - - if( in->Bands == n ) - return( im_copy( in, out ) ); - if( in->Bands != 1 ) { - im_error( domain, _( "not one band or %d bands" ), n ); - return( -1 ); - } - if( n > 256 || n < 1 ) { - im_error( domain, "%s", _( "bad bands" ) ); - return( -1 ); - } - - for( i = 0; i < n; i++ ) - bands[i] = in; - - return( im_gbandjoin( bands, out, n ) ); -} - -int -im__bandalike_vec( const char *domain, IMAGE **in, IMAGE **out, int n ) -{ - int i; - int max_bands; - - g_assert( n >= 1 ); - - max_bands = in[0]->Bands; - for( i = 1; i < n; i++ ) - max_bands = IM_MAX( max_bands, in[i]->Bands ); - for( i = 0; i < n; i++ ) - if( im__bandup( domain, in[i], out[i], max_bands ) ) - return( -1 ); - - return( 0 ); -} - -int -im__bandalike( const char *domain, - IMAGE *in1, IMAGE *in2, IMAGE *out1, IMAGE *out2 ) -{ - IMAGE *in[2]; - IMAGE *out[2]; - - in[0] = in1; - in[1] = in2; - out[0] = out1; - out[1] = out2; - if( im__bandalike_vec( domain, in, out, 2 ) ) - return( -1 ); - - return( 0 ); -} - -/* The common part of most binary arithmetic, relational and boolean - * operators. We: - * - * - check in and out - * - cast in1 and in2 up to a common format - * - cast the common format to the output format with the supplied table - * - equalise bands - * - run the supplied buffer operation passing one of the up-banded, - * up-casted and up-sized inputs as the first param - */ -int -im__arith_binary( const char *domain, - IMAGE *in1, IMAGE *in2, IMAGE *out, - int format_table[10], - im_wrapmany_fn fn, void *b ) -{ - IMAGE *t[5]; - - if( im_piocheck( in1, out ) || - im_pincheck( in2 ) || - im_check_bands_1orn( domain, in1, in2 ) || - im_check_size_same( domain, in1, in2 ) || - im_check_uncoded( domain, in1 ) || - im_check_uncoded( domain, in2 ) ) - return( -1 ); - - /* Cast our input images up to a common format and bands. - */ - if( im_open_local_array( out, t, 4, domain, "p" ) || - im__formatalike( in1, in2, t[0], t[1] ) || - im__bandalike( domain, t[0], t[1], t[2], t[3] ) ) - return( -1 ); - - /* Generate the output. - */ - if( im_cp_descv( out, t[2], t[3], NULL ) ) - return( -1 ); - - /* What number of bands will we write? Same as up-banded input. - */ - out->Bands = t[2]->Bands; - - /* What output type will we write? - */ - out->BandFmt = format_table[t[2]->BandFmt]; - - /* And process! The buffer function gets one of the input images as a - * sample. - */ - t[4] = NULL; - if( im_wrapmany( t + 2, out, fn, t[2], b ) ) - return( -1 ); - - return( 0 ); -} - -VipsVector * -im__init_program( VipsVector *vectors[IM_BANDFMT_LAST], - VipsBandFmt format_table[IM_BANDFMT_LAST], VipsBandFmt fmt ) -{ - int isize = im__sizeof_bandfmt[fmt]; - int osize = im__sizeof_bandfmt[format_table[fmt]]; - - VipsVector *v; - - v = vips_vector_new( "binary arith", osize ); - - vips_vector_source_name( v, "s1", isize ); - vips_vector_source_name( v, "s2", isize ); - vips_vector_temporary( v, "t1", osize ); - vips_vector_temporary( v, "t2", osize ); - - vectors[fmt] = v; - - return( v ); -} - -void -im__compile_programs( VipsVector *vectors[IM_BANDFMT_LAST] ) -{ - int fmt; - - for( fmt = 0; fmt < IM_BANDFMT_LAST; fmt++ ) { - if( vectors[fmt] && - !vips_vector_compile( vectors[fmt] ) ) - IM_FREEF( vips_vector_free, vectors[fmt] ); - } - -#ifdef DEBUG - printf( "im__compile_programs: " ); - for( fmt = 0; fmt < IM_BANDFMT_LAST; fmt++ ) - if( vectors[fmt] ) - printf( "%s ", im_BandFmt2char( fmt ) ); - printf( "\n" ); -#endif /*DEBUG*/ -} - -/* Type promotion for addition. Sign and value preserving. Make sure these - * match the case statement in add_buffer() above. - */ -static int bandfmt_add[10] = { -/* UC C US S UI I F X D DX */ - US, S, UI, I, UI, I, F, X, D, DX -}; - -static void -build_programs( void ) -{ - static gboolean done = FALSE; - - VipsVector *v; - - if( done ) - return; - done = TRUE; - - v = im__init_program( add_vectors, bandfmt_add, IM_BANDFMT_UCHAR ); - vips_vector_asm2( v, "convubw", "t1", "s1" ); - vips_vector_asm2( v, "convubw", "t2", "s2" ); - vips_vector_asm3( v, "addw", "d1", "t1", "t2" ); - - v = im__init_program( add_vectors, bandfmt_add, IM_BANDFMT_CHAR ); - vips_vector_asm2( v, "convsbw", "t1", "s1" ); - vips_vector_asm2( v, "convsbw", "t2", "s2" ); - vips_vector_asm3( v, "addw", "d1", "t1", "t2" ); - - v = im__init_program( add_vectors, bandfmt_add, IM_BANDFMT_USHORT ); - vips_vector_asm2( v, "convuwl", "t1", "s1" ); - vips_vector_asm2( v, "convuwl", "t2", "s2" ); - vips_vector_asm3( v, "addl", "d1", "t1", "t2" ); - - v = im__init_program( add_vectors, bandfmt_add, IM_BANDFMT_SHORT ); - vips_vector_asm2( v, "convswl", "t1", "s1" ); - vips_vector_asm2( v, "convswl", "t2", "s2" ); - vips_vector_asm3( v, "addl", "d1", "t1", "t2" ); - - /* - - uint/int are a little slower than C, on a c2d anyway - - float/double/complex are not handled well - - v = im__init_program( add_vectors, IM_BANDFMT_UINT ); - vips_vector_asm3( v, "addl", "d1", "s1", "s2" ); - - v = im__init_program( add_vectors, IM_BANDFMT_INT ); - vips_vector_asm3( v, "addl", "d1", "s1", "s2" ); - - */ - - im__compile_programs( add_vectors ); -} - -/** - * im_add: - * @in1: input image - * @in2: input image - * @out: output image - * - * This operation calculates @in1 + @in2 and writes the result to @out. - * The images must be the same size. They may have any format. - * - * If the number of bands differs, one of the images - * must have one band. In this case, an n-band image is formed from the - * one-band image by joining n copies of the one-band image together, and then - * the two n-band images are operated upon. - * - * The two input images are cast up to the smallest common type (see table - * Smallest common format in - * arithmetic), then the - * following table is used to determine the output type: - * - * - * im_add() type promotion - * - * - * - * input type - * output type - * - * - * - * - * uchar - * ushort - * - * - * char - * short - * - * - * ushort - * uint - * - * - * short - * int - * - * - * uint - * uint - * - * - * int - * int - * - * - * float - * float - * - * - * double - * double - * - * - * complex - * complex - * - * - * double complex - * double complex - * - * - * - *
- * - * In other words, the output type is just large enough to hold the whole - * range of possible values. - * - * Operations on integer images are performed using the processor's vector unit, - * if possible. Disable this with --vips-novector or IM_NOVECTOR. - * - * See also: im_subtract(), im_lintra(). - * - * Returns: 0 on success, -1 on error - */ -int -im_add( IMAGE *in1, IMAGE *in2, IMAGE *out ) -{ - if( vips_vector_get_enabled() ) - build_programs(); - - return( im__arith_binary( "im_add", - in1, in2, out, - bandfmt_add, - (im_wrapmany_fn) add_buffer, NULL ) ); -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 3a651d67..dd70d894 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -46,6 +46,7 @@ #include #include #include +#include #ifdef WITH_DMALLOC #include @@ -505,3 +506,256 @@ im_wraptwo( IMAGE *in1, IMAGE *in2, IMAGE *out, (im_wrapmany_fn) wraptwo_gen, bun, NULL ) ); } +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define X IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DX IM_BANDFMT_DPCOMPLEX + +/* For two integer types, the "largest", ie. one which can represent the + * full range of both. + */ +static int bandfmt_largest[6][6] = { + /* UC C US S UI I */ +/* UC */ { UC, S, US, S, UI, I }, +/* C */ { S, C, I, S, I, I }, +/* US */ { US, I, US, I, UI, I }, +/* S */ { S, S, I, S, I, I }, +/* UI */ { UI, I, UI, I, UI, I }, +/* I */ { I, I, I, I, I, I } +}; + +/* For two formats, find one which can represent the full range of both. + */ +static VipsBandFmt +im__format_common( VipsBandFmt in1, VipsBandFmt in2 ) +{ + if( vips_bandfmt_iscomplex( in1 ) || + vips_bandfmt_iscomplex( in2 ) ) { + /* What kind of complex? + */ + if( in1 == IM_BANDFMT_DPCOMPLEX || in2 == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + return( IM_BANDFMT_DPCOMPLEX ); + else + return( IM_BANDFMT_COMPLEX ); + + } + else if( vips_bandfmt_isfloat( in1 ) || + vips_bandfmt_isfloat( in2 ) ) { + /* What kind of float? + */ + if( in1 == IM_BANDFMT_DOUBLE || in2 == IM_BANDFMT_DOUBLE ) + return( IM_BANDFMT_DOUBLE ); + else + return( IM_BANDFMT_FLOAT ); + } + else + /* Must be int+int -> int. + */ + return( bandfmt_largest[in1][in2] ); +} + +int +im__formatalike_vec( IMAGE **in, IMAGE **out, int n ) +{ + int i; + VipsBandFmt fmt; + + g_assert( n >= 1 ); + + fmt = in[0]->BandFmt; + for( i = 1; i < n; i++ ) + fmt = im__format_common( fmt, in[i]->BandFmt ); + + for( i = 0; i < n; i++ ) + if( im_clip2fmt( in[i], out[i], fmt ) ) + return( -1 ); + + return( 0 ); +} + +int +im__formatalike( IMAGE *in1, IMAGE *in2, IMAGE *out1, IMAGE *out2 ) +{ + IMAGE *in[2]; + IMAGE *out[2]; + + in[0] = in1; + in[1] = in2; + out[0] = out1; + out[1] = out2; + + return( im__formatalike_vec( in, out, 2 ) ); +} + +/* Make an n-band image. Input 1 or n bands. + */ +int +im__bandup( const char *domain, IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *bands[256]; + int i; + + if( in->Bands == n ) + return( im_copy( in, out ) ); + if( in->Bands != 1 ) { + im_error( domain, _( "not one band or %d bands" ), n ); + return( -1 ); + } + if( n > 256 || n < 1 ) { + im_error( domain, "%s", _( "bad bands" ) ); + return( -1 ); + } + + for( i = 0; i < n; i++ ) + bands[i] = in; + + return( im_gbandjoin( bands, out, n ) ); +} + +int +im__bandalike_vec( const char *domain, IMAGE **in, IMAGE **out, int n ) +{ + int i; + int max_bands; + + g_assert( n >= 1 ); + + max_bands = in[0]->Bands; + for( i = 1; i < n; i++ ) + max_bands = IM_MAX( max_bands, in[i]->Bands ); + for( i = 0; i < n; i++ ) + if( im__bandup( domain, in[i], out[i], max_bands ) ) + return( -1 ); + + return( 0 ); +} + +int +im__bandalike( const char *domain, + IMAGE *in1, IMAGE *in2, IMAGE *out1, IMAGE *out2 ) +{ + IMAGE *in[2]; + IMAGE *out[2]; + + in[0] = in1; + in[1] = in2; + out[0] = out1; + out[1] = out2; + if( im__bandalike_vec( domain, in, out, 2 ) ) + return( -1 ); + + return( 0 ); +} + +/* The common part of most binary arithmetic, relational and boolean + * operators. We: + * + * - check in and out + * - cast in1 and in2 up to a common format + * - cast the common format to the output format with the supplied table + * - equalise bands + * - run the supplied buffer operation passing one of the up-banded, + * up-casted and up-sized inputs as the first param + */ +int +im__arith_binary( const char *domain, + IMAGE *in1, IMAGE *in2, IMAGE *out, + int format_table[10], + im_wrapmany_fn fn, void *b ) +{ + IMAGE *t[5]; + + if( im_piocheck( in1, out ) || + im_pincheck( in2 ) || + im_check_bands_1orn( domain, in1, in2 ) || + im_check_size_same( domain, in1, in2 ) || + im_check_uncoded( domain, in1 ) || + im_check_uncoded( domain, in2 ) ) + return( -1 ); + + /* Cast our input images up to a common format and bands. + */ + if( im_open_local_array( out, t, 4, domain, "p" ) || + im__formatalike( in1, in2, t[0], t[1] ) || + im__bandalike( domain, t[0], t[1], t[2], t[3] ) ) + return( -1 ); + + /* Generate the output. + */ + if( im_cp_descv( out, t[2], t[3], NULL ) ) + return( -1 ); + + /* What number of bands will we write? Same as up-banded input. + */ + out->Bands = t[2]->Bands; + + /* What output type will we write? + */ + out->BandFmt = format_table[t[2]->BandFmt]; + + /* And process! The buffer function gets one of the input images as a + * sample. + */ + t[4] = NULL; + if( im_wrapmany( t + 2, out, fn, t[2], b ) ) + return( -1 ); + + return( 0 ); +} + +VipsVector * +im__init_program( VipsVector *vectors[IM_BANDFMT_LAST], + VipsBandFmt format_table[IM_BANDFMT_LAST], VipsBandFmt fmt ) +{ + int isize = im__sizeof_bandfmt[fmt]; + int osize = im__sizeof_bandfmt[format_table[fmt]]; + + VipsVector *v; + + v = vips_vector_new( "binary arith", osize ); + + vips_vector_source_name( v, "s1", isize ); + vips_vector_source_name( v, "s2", isize ); + vips_vector_temporary( v, "t1", osize ); + vips_vector_temporary( v, "t2", osize ); + + vectors[fmt] = v; + + return( v ); +} + +void +im__compile_programs( VipsVector *vectors[IM_BANDFMT_LAST] ) +{ + int fmt; + + for( fmt = 0; fmt < IM_BANDFMT_LAST; fmt++ ) { + if( vectors[fmt] && + !vips_vector_compile( vectors[fmt] ) ) + IM_FREEF( vips_vector_free, vectors[fmt] ); + } + +#ifdef DEBUG + printf( "im__compile_programs: " ); + for( fmt = 0; fmt < IM_BANDFMT_LAST; fmt++ ) + if( vectors[fmt] ) + printf( "%s ", im_BandFmt2char( fmt ) ); + printf( "\n" ); +#endif /*DEBUG*/ +} + +int +im_add( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + return( vips_call( "add", in1, in2, out, NULL ) ); +} diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 73dd9d0c..9b2dcfd0 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -142,10 +142,10 @@ typedef struct _VipsArgumentInstance { */ gboolean assigned; - /* If this is an output argument, keep the id of our "destroy" handler + /* If this is an output argument, keep the id of our "close" handler * here. */ - gulong destroy_id; + gulong close_id; } VipsArgumentInstance; /* Need to look up our VipsArgument structs from a pspec. Just hash the diff --git a/libvips/include/vips/private.h b/libvips/include/vips/private.h index ef8b0649..ed718629 100644 --- a/libvips/include/vips/private.h +++ b/libvips/include/vips/private.h @@ -162,6 +162,10 @@ int vips_region_fill( struct _VipsRegion *reg, */ int vips__image_write_prepare( struct _VipsImage *image ); +/* Start up various packages. + */ +void vips_arithmetic_operation_init( void ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index c749318e..521d8fe8 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -217,6 +217,10 @@ vips_init( const char *argv0 ) vips__interpolate_init(); im__format_init(); + /* Start up packages. + */ + vips_arithmetic_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 * plugin. diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index a032608e..280b06a9 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -195,10 +195,10 @@ vips_object_rewind( VipsObject *object ) static void vips_argument_instance_free( VipsArgumentInstance *argument_instance ) { - if( argument_instance->destroy_id ) { + if( argument_instance->close_id ) { g_signal_handler_disconnect( argument_instance->object, - argument_instance->destroy_id ); - argument_instance->destroy_id = 0; + argument_instance->close_id ); + argument_instance->close_id = 0; } g_free( argument_instance ); } @@ -281,7 +281,7 @@ vips_argument_init2( VipsObject *object, GParamSpec *pspec, argument->pspec = ((VipsArgument *) argument_class)->pspec; argument_instance->object = object; argument_instance->assigned = FALSE; - argument_instance->destroy_id = 0; + argument_instance->close_id = 0; vips_argument_table_replace( object->argument_table, argument ); @@ -360,15 +360,15 @@ vips_object_clear_object( VipsObject *object, GParamSpec *pspec ) #endif /*DEBUG_REF*/ /* The object reffed us. Stop listening link to the - * object's "destroy" signal. We can come here from - * object being destroyed, in which case the handler + * object's "close" signal. We can come here from + * object being closed, in which case the handler * will already have been disconnected for us. */ if( g_signal_handler_is_connected( object, - argument_instance->destroy_id ) ) + argument_instance->close_id ) ) g_signal_handler_disconnect( object, - argument_instance->destroy_id ); - argument_instance->destroy_id = 0; + argument_instance->close_id ); + argument_instance->close_id = 0; *member = NULL; g_object_unref( object ); @@ -469,13 +469,13 @@ vips_object_finalize( GObject *gobject ) } static void -vips_object_arg_destroy( GObject *argument, +vips_object_arg_close( GObject *argument, VipsArgumentInstance *argument_instance ) { VipsObject *object = argument_instance->object; GParamSpec *pspec = ((VipsArgument *) argument_instance)->pspec; - /* Argument had reffed us ... now it's being destroyed, so we unref. + /* Argument had reffed us ... now it's being closed, so we unref. */ vips_object_clear_object( object, pspec ); } @@ -524,9 +524,9 @@ vips_object_set_object( VipsObject *object, GParamSpec *pspec, /* The argument reffs us. */ g_object_ref( object ); - argument_instance->destroy_id = - g_signal_connect( *member, "destroy", - G_CALLBACK( vips_object_arg_destroy ), + argument_instance->close_id = + g_signal_connect( *member, "close", + G_CALLBACK( vips_object_arg_close ), argument_instance ); } } @@ -964,7 +964,7 @@ vips_object_class_install_argument( VipsObjectClass *object_class, vips_argument_table_replace( object_class->argument_table, (VipsArgument *) argument_class ); - object_class->argument_table_traverse = g_slist_append( + object_class->argument_table_traverse = g_slist_prepend( object_class->argument_table_traverse, argument_class ); } @@ -1361,7 +1361,7 @@ vips_type_find( const char *basename, const char *nickname ) { VipsObjectClass *class; - if( !(class = vips_class_find( "VipsInterpolate", nickname )) ) + if( !(class = vips_class_find( "VipsObject", nickname )) ) return( 0 ); /* FIXME ... we've not supposed to use G_TYPE_FROM_CLASS(), I think. diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index 33608d65..c81bc19c 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -28,8 +28,8 @@ */ /* -#define DEBUG */ +#define VIPS_DEBUG #ifdef HAVE_CONFIG_H #include @@ -42,6 +42,7 @@ #include #include +#include #include @@ -90,6 +91,30 @@ vips_operation_print_arg( VipsObject *object, GParamSpec *pspec, return( NULL ); } +#ifdef VIPS_DEBUG +static void * +vips_operation_call_argument( VipsObject *object, GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance ) +{ + VipsArgument *argument = (VipsArgument *) argument_class; + + printf( " %s: offset = %d ", + argument->pspec->name, argument_class->offset ); + if( argument_class->flags & VIPS_ARGUMENT_REQUIRED ) + printf ("required " ); + if( argument_class->flags & VIPS_ARGUMENT_CONSTRUCT ) + printf ("construct " ); + if( argument_class->flags & VIPS_ARGUMENT_SET_ONCE ) + printf ("set-once " ); + if( argument_instance->assigned ) + printf ("assigned " ); + printf( "\n" ); + + return( NULL ); +} +#endif /*VIPS_DEBUG*/ + static void vips_operation_print( VipsObject *object, VipsBuf *buf ) { @@ -97,6 +122,12 @@ vips_operation_print( VipsObject *object, VipsBuf *buf ) VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); VipsOperationPrint print; +#ifdef VIPS_DEBUG + printf( "%s args:\n", object_class->nickname ); + vips_argument_map( VIPS_OBJECT( operation ), + (VipsArgumentMapFn) vips_operation_call_argument, NULL, NULL ); +#endif /*VIPS_DEBUG*/ + /* First pass through args: show the required names. */ vips_buf_appendf( buf, "VipsOperation.%s (", object_class->nickname ); @@ -151,30 +182,6 @@ vips_operation_init( VipsOperation *operation ) */ } -#ifdef DEBUG -static void * -vips_operation_call_argument( VipsObject *object, GParamSpec *pspec, - VipsArgumentClass *argument_class, - VipsArgumentInstance *argument_instance ) -{ - VipsArgument *argument = (VipsArgument *) argument_class; - - printf( " %s: offset=%d ", - argument->pspec->name, argument_class->offset ); - if( argument_class->flags & VIPS_ARGUMENT_REQUIRED ) - printf ("required " ); - if( argument_class->flags & VIPS_ARGUMENT_CONSTRUCT ) - printf ("construct " ); - if( argument_class->flags & VIPS_ARGUMENT_SET_ONCE ) - printf ("set-once " ); - if( argument_instance->assigned ) - printf ("assigned " ); - printf( "\n" ); - - return( NULL ); -} -#endif /*DEBUG*/ - int vips_operation_call_valist( VipsOperation *operation, va_list ap ) { @@ -221,6 +228,17 @@ vips_operation_call_valist( VipsOperation *operation, va_list ap ) return( -1 ); } +#ifdef VIPS_DEBUG +{ + char *str; + + str = g_strdup_value_contents( &value ); + VIPS_DEBUG_MSG( "\t%s = %s\n", + pspec->name, str ); + g_free( str ); +} +#endif /*VIPS_DEBUG*/ + g_object_set_property( G_OBJECT( operation ), pspec->name, &value ); g_value_unset( &value ); @@ -247,7 +265,7 @@ vips_operation_new( const char *name ) if( !(type = vips_type_find( "VipsOperation", name )) ) return( NULL ); - return( VIPS_OPERATION( vips_object_new( type, NULL, NULL, NULL ) ) ); + return( VIPS_OPERATION( g_object_new( type, NULL ) ) ); } int @@ -257,13 +275,16 @@ vips_call( const char *operation_name, ... ) VipsOperation *operation; int result; -#ifdef DEBUG - printf( "vips_call: starting for %s ...\n", operation_name ); -#endif /*DEBUG*/ + VIPS_DEBUG_MSG( "vips_call: starting for %s ...\n", operation_name ); if( !(operation = vips_operation_new( operation_name ) ) ) return( -1 ); +#ifdef VIPS_DEBUG + VIPS_DEBUG_MSG( "where:\n" ); + vips_object_print( VIPS_OBJECT( operation ) ); +#endif /*VIPS_DEBUG*/ + va_start( ap, operation_name ); result = vips_operation_call_valist( operation, ap ); va_end( ap );