From db41b6ac4e7d472672dca8efb8de77f8f1505329 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 29 Mar 2011 12:12:20 +0100 Subject: [PATCH] add VipsOperation abstract base class for all operations goes in --- ChangeLog | 3 + TODO | 6 +- libvips/include/vips/Makefile.am | 1 + libvips/include/vips/operation.h | 74 +++++++++ libvips/include/vips/vips.h | 2 +- libvips/iofuncs/Makefile.am | 1 + libvips/iofuncs/object.c | 4 + libvips/iofuncs/operation.c | 277 +++++++++++++++++++++++++++++++ 8 files changed, 363 insertions(+), 5 deletions(-) create mode 100644 libvips/include/vips/operation.h create mode 100644 libvips/iofuncs/operation.c diff --git a/ChangeLog b/ChangeLog index 0b551f27..c274ba14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,9 @@ - fits write - better fits metadata support - renamed all header fields, old names still supported, hopefully +- 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 30/11/10 started 7.24.0 - bump for new stable diff --git a/TODO b/TODO index d7bb65d2..1b13642c 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,6 @@ -- work though headers making sure old im_ stuff is gone - - especially internal.h / private.h seem to have a lot of junk in them - im__tile_width etc. need moving, see init.c +- we have VipsOperation ... now try VipsArithmetic and VipsBinary, then we can + do VipsAdd diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index f5ad29b1..02ac22d6 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -12,6 +12,7 @@ pkginclude_HEADERS = \ disp.h \ enumtypes.h \ error.h \ + operation.h \ format.h \ inplace.h \ generate.h \ diff --git a/libvips/include/vips/operation.h b/libvips/include/vips/operation.h new file mode 100644 index 00000000..f52ae952 --- /dev/null +++ b/libvips/include/vips/operation.h @@ -0,0 +1,74 @@ +/* base class for all vips operations + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU 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 + + */ + +#ifndef VIPS_OPERATION_H +#define VIPS_OPERATION_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#define VIPS_TYPE_OPERATION (vips_operation_get_type()) +#define VIPS_OPERATION( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_OPERATION, VipsOperation )) +#define VIPS_OPERATION_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_OPERATION, VipsOperationClass )) +#define VIPS_IS_OPERATION( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_OPERATION )) +#define VIPS_IS_OPERATION_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_OPERATION )) +#define VIPS_OPERATION_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_OPERATION, VipsOperationClass )) + +typedef gboolean (*VipsOperationBuildFn)( VipsObject * ); + +typedef struct _VipsOperation { + VipsObject parent_instance; + +} VipsOperation; + +typedef struct _VipsOperationClass { + VipsObjectClass parent_class; + +} VipsOperationClass; + +GType vips_operation_get_type( void ); + +int vips_operation_call_valist( VipsOperation *operation, va_list ap ); +VipsOperation *vips_operation_new( const char *name ); +int vips_call( const char *operation_name, ...) G_GNUC_NULL_TERMINATED; + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_OPERATION_H*/ diff --git a/libvips/include/vips/vips.h b/libvips/include/vips/vips.h index 547951c4..b62c2ad9 100644 --- a/libvips/include/vips/vips.h +++ b/libvips/include/vips/vips.h @@ -113,8 +113,8 @@ extern "C" { #include #include #include - #include +#include #include diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index b0971e74..9332a161 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -17,6 +17,7 @@ libiofuncs_la_SOURCES = \ sinkscreen.c \ memory.c \ header.c \ + operation.c \ region.c \ rect.c \ semaphore.c \ diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index a3311dac..a032608e 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -230,6 +230,10 @@ vips_argument_map( VipsObject *object, VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); GSList *p; + /* This loop also appears in vips_operation_call_valist() and needs + * to be kept in sync with that. + */ + for( p = class->argument_table_traverse; p; p = p->next ) { VipsArgumentClass *argument_class = (VipsArgumentClass *) p->data; diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c new file mode 100644 index 00000000..704921c4 --- /dev/null +++ b/libvips/iofuncs/operation.c @@ -0,0 +1,277 @@ +/* base class for all vips operations + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU 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 + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Abstract base class for operations. + */ + +G_DEFINE_ABSTRACT_TYPE( VipsOperation, vips_operation, VIPS_TYPE_OBJECT ); + +/* What to show about the argument. + */ +typedef struct { + gboolean required; /* show required args or optional */ + gboolean oftype; /* "is of type" message */ + int n; /* Arg number */ +} VipsOperationPrint; + +static void * +vips_operation_print_arg( VipsObject *object, GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + VipsBuf *buf, VipsOperationPrint *print ) +{ + /* Only show unassigned args ... assigned args are internal. + */ + + if( print->required == + ((argument_class->flags & VIPS_ARGUMENT_REQUIRED) != 0) && + !argument_instance->assigned ) { + if( print->oftype ) + vips_buf_appendf( buf, " %s :: %s\n", + pspec->name, + g_type_name( pspec->value_type ) ); + else { + if( print->n > 0 ) + vips_buf_appends( buf, ", " ); + vips_buf_appends( buf, pspec->name ); + } + + print->n += 1; + } + + return( NULL ); +} + +static void +vips_operation_print( VipsObject *object, VipsBuf *buf ) +{ + VipsOperation *operation = VIPS_OPERATION( object ); + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); + VipsOperationPrint print; + + /* First pass through args: show the required names. + */ + vips_buf_appendf( buf, "VipsOperation.%s (", object_class->nickname ); + print.required = TRUE; + print.oftype = FALSE; + print.n = 0; + vips_argument_map( VIPS_OBJECT( operation ), + (VipsArgumentMapFn) vips_operation_print_arg, buf, &print ); + vips_buf_appends( buf, ")\n" ); + + /* Show required types. + */ + vips_buf_appends( buf, "where:\n" ); + print.required = TRUE; + print.oftype = TRUE; + print.n = 0; + vips_argument_map( VIPS_OBJECT( operation ), + (VipsArgumentMapFn) vips_operation_print_arg, buf, &print ); + + /* Show optional args. + */ + vips_buf_appends( buf, "optional arguments:\n" ); + print.required = FALSE; + print.oftype = TRUE; + print.n = 0; + vips_argument_map( VIPS_OBJECT( operation ), + (VipsArgumentMapFn) vips_operation_print_arg, buf, &print ); +} + +static int +vips_operation_build( VipsObject *object ) +{ + if( VIPS_OBJECT_CLASS( vips_operation_parent_class )->build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_operation_class_init( VipsOperationClass *class ) +{ + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + vobject_class->print = vips_operation_print; + vobject_class->build = vips_operation_build; +} + +static void +vips_operation_init( VipsOperation *operation ) +{ + /* Init our instance fields. + */ +} + +#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 ) +{ + VipsObject *object = VIPS_OBJECT( operation ); + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + GSList *p; + char *first_property_name; + + /* First extract required arguments. Can't use vips_argument_map here + * :-( because passing va_list by reference is not portable. So we + * have to copy-paste the vips_argument_map() loop. Keep in sync with + * that. + */ + + for( p = class->argument_table_traverse; p; p = p->next ) { + VipsArgumentClass *argument_class = + (VipsArgumentClass *) p->data; + VipsArgument *argument = (VipsArgument *) argument_class; + GParamSpec *pspec = argument->pspec; + VipsArgumentInstance *argument_instance = + vips__argument_get_instance( argument_class, object ); + + /* We have many props on the arg table ... filter out the ones + * for this class. + */ + if( g_object_class_find_property( G_OBJECT_CLASS( class ), + pspec->name ) == pspec ) { + + /* End of stuff copy-pasted from vips_argument_map(). + */ + if( argument_class->flags & VIPS_ARGUMENT_REQUIRED && + !argument_instance->assigned ) { + GValue value = { 0 }; + char *msg = NULL; + + g_value_init( &value, + G_PARAM_SPEC_VALUE_TYPE( pspec ) ); + G_VALUE_COLLECT( &value, ap, 0, &msg ); + if( msg ) { + vips_error( class->description, + "%s", _( msg ) ); + g_value_unset( &value ); + g_free( msg ); + return( -1 ); + } + + g_object_set_property( G_OBJECT( operation ), + pspec->name, &value ); + g_value_unset( &value ); + } + } + } + + /* Now set optional args. + */ + first_property_name = va_arg( ap, char * ); + g_object_set_valist( G_OBJECT( operation ), first_property_name, ap ); + + if( vips_object_build( VIPS_OBJECT( operation ) ) ) + return( -1 ); + + return( 0 ); +} + +VipsOperation * +vips_operation_new( const char *name ) +{ + GType type; + + if( !(type = vips_type_find( "VipsOperation", name )) ) + return( NULL ); + + return( VIPS_OPERATION( vips_object_new( type, NULL, NULL, NULL ) ) ); +} + +int +vips_call( const char *operation_name, ... ) +{ + va_list ap; + VipsOperation *operation; + int result; + +#ifdef DEBUG + printf( "vips_call: starting for %s ...\n", operation_name ); +#endif /*DEBUG*/ + + if( !(operation = vips_operation_new( operation_name ) ) ) + return( -1 ); + + va_start( ap, operation_name ); + result = vips_operation_call_valist( operation, ap ); + va_end( ap ); + + /* The operation we have built should now be reffed by one of it's + * arguments ... or have finished it's work. + */ + g_object_unref( operation ); + + return( -1 ); +}