diff --git a/TODO b/TODO index 14f589fb..465958a9 100644 --- a/TODO +++ b/TODO @@ -2,6 +2,10 @@ - can we call vips7 funcs from the vips8 interface? we already have vips8 from vips7 + we'd have to make fake classes to wrap each operation I guesso + + try wrapping for fun + - revisit orc conv diff --git a/libvips/arithmetic/arithmetic.c b/libvips/arithmetic/arithmetic.c index f0359323..db6741e2 100644 --- a/libvips/arithmetic/arithmetic.c +++ b/libvips/arithmetic/arithmetic.c @@ -108,9 +108,6 @@ vips_arithmetic_class_init( VipsArithmeticClass *class ) GParamSpec *pspec; - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - vobject_class->build = vips_arithmetic_build; pspec = g_param_spec_object( "out", "Output", diff --git a/libvips/deprecated/wrapvips7.c b/libvips/deprecated/wrapvips7.c index f2ea270b..cfaa3732 100644 --- a/libvips/deprecated/wrapvips7.c +++ b/libvips/deprecated/wrapvips7.c @@ -31,7 +31,9 @@ */ /* +#define DEBUG #define VIPS_DEBUG +#define DEBUG_REF */ #ifdef HAVE_CONFIG_H @@ -56,22 +58,357 @@ static GHashTable *vips7_types = NULL; -typedef VipsOperation VipsVips7; -typedef VipsOperationClass VipsVips7Class; +typedef struct _Vips7 { + VipsOperation parent_object; + + /* vips7 dispatch spine we build. + */ + im_object *vargv; + + /* Set if we get an error during construct. + */ + gboolean error; + +} Vips7; + +typedef struct _Vips7Class { + VipsOperationClass parent_class; + + /* Look this up from the class name. + */ + im_function *fn; + + /* Set if we can't wrap this im_function for some reason. + */ + gboolean not_supported; + +} Vips7Class; + +typedef enum { + VIPS7_NONE = -1, + VIPS7_DOUBLE = 0, + VIPS7_INT, + VIPS7_COMPLEX, + VIPS7_STRING, + VIPS7_IMAGE, + VIPS7_DOUBLEVEC, + VIPS7_DMASK, + VIPS7_IMASK, + VIPS7_IMAGEVEC, + VIPS7_INTVEC, + VIPS7_GVALUE, + VIPS7_INTERPOLATE +} Vips7Type; + +static char *vips7_supported[] = { + IM_TYPE_DOUBLE, + IM_TYPE_INT, + IM_TYPE_COMPLEX, + IM_TYPE_STRING, + IM_TYPE_IMAGE, + IM_TYPE_DOUBLEVEC, + IM_TYPE_DMASK, + IM_TYPE_IMASK, + IM_TYPE_IMAGEVEC, + IM_TYPE_INTVEC, + IM_TYPE_GVALUE, + IM_TYPE_INTERPOLATE +}; + +/* Turn a vips7 type name to an enum. + */ +static Vips7Type +vips7_lookup_type( im_arg_type type ) +{ + int i; + + for( i = 0; i < IM_NUMBER( vips7_supported ); i++ ) + if( strcmp( type, vips7_supported[i] ) == 0 ) + return( (Vips7Type) i ); + + return( VIPS7_NONE ); +} + +static void +vips7_dispose( GObject *gobject ) +{ + Vips7 *vips7 = VIPS7( gobject ); + +#ifdef DEBUG + printf( "vips7_dispose: " ); + vips_object_print_name( object ); + printf( "\n" ); +#endif /*DEBUG*/ + + G_OBJECT_CLASS( parent_class )->dispose( gobject ); +} + +/* Junk stuff we may have attached to vargv. + */ +static void +vips7_vargv_free( im_function *fn, im_object *vargv ) +{ + int i; + + for( i = 0; i < fn->argc; i++ ) { + im_arg_desc *arg = &fn->argv[i]; + im_type_desc *type = arg->desc; + im_arg_type vt = type->type; + + switch( vips7_lookup_type( vt ) ) { + case CALL_NONE: /* IM_TYPE_DISPLAY */ + case CALL_DOUBLE: + case CALL_INT: + case CALL_COMPLEX: + case CALL_GVALUE: + case CALL_INTERPOLATE: + case CALL_IMAGE: + /* Do nothing. + */ + break; + + case CALL_STRING: + VIPS_FREE( obj ); + break; + + case CALL_IMAGEVEC: + VIPS_FREE( ((im_imagevec_object *) obj)->vec ); + break; + + case CALL_DOUBLEVEC: + VIPS_FREE( ((im_doublevec_object *) obj)->vec ); + break; + + case CALL_INTVEC: + VIPS_FREE( ((im_intvec_object *) obj)->vec ); + break; + + case CALL_DMASK: + VIPS_FREE( ((im_mask_object *) obj)->name ); + VIPS_FREEF( im_free_dmask, + ((im_mask_object *) obj)->mask ); + break; + + case CALL_IMASK: + VIPS_FREE( ((im_mask_object *) obj)->name ); + VIPS_FREEF( im_free_imask, + ((im_mask_object *) obj)->mask ); + break; + + default: + g_assert( FALSE ); + } + } +} + +static void +vips7_finalize( GObject *gobject ) +{ + Vips7 *vips7 = VIPS7( gobject ); + Vips7Class *class = VIPS7_GET_CLASS( vips7 ); + +#ifdef DEBUG + printf( "vips7_finalize: " ); + vips_object_print_name( object ); + printf( "\n" ); +#endif /*DEBUG*/ + + if( vips7->vargv ) { + vips7_vargv_free( class->fn, vips7->vargv ) + im_free_vargv( class->fn, vips7->vargv ); + VIPS_FREE( vips7->vargv ); + } + + G_OBJECT_CLASS( parent_class )->finalize( gobject ); +} + +static void +vips7_object_set_property( GObject *gobject, + guint property_id, const GValue *value, GParamSpec *pspec ) +{ + VipsObject *object = VIPS_OBJECT( gobject ); + VipsObjectClass *oclass = VIPS_OBJECT_GET_CLASS( gobject ); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup( oclass->argument_table, pspec ); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance( argument_class, object ); + + Vips7 *vips7 = VIPS7( gobject ); + Vips7Class *class = VIPS7_GET_CLASS( vips7 ); + int i = argument_class->offset; + im_arg_desc *arg = &class->fn->argv[i]; + im_type_desc *type = arg->desc; + im_arg_type vt = type->type; + + g_assert( argument_instance ); + + if( !argument_class ) { + G_OBJECT_WARN_INVALID_PROPERTY_ID( gobject, + property_id, pspec ); + return; + } + +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "vips_object_set_property: " ); + vips_object_print_name( object ); + printf( ".%s = %s\n", g_param_spec_get_name( pspec ), str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + g_assert( ((VipsArgument *) argument_class)->pspec == pspec ); + g_assert( ((VipsArgument *) argument_instance)->pspec == pspec ); + + /* If this is a construct-only argument, we can only set before we've + * built. + */ + if( argument_class->flags & VIPS_ARGUMENT_CONSTRUCT && + object->constructed ) { + g_warning( "%s: %s can't assign '%s' after construct", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_param_spec_get_name( pspec ) ); + return; + } + + /* If this is a set-once argument, check we've not set it before. + */ + if( argument_class->flags & VIPS_ARGUMENT_SET_ONCE && + argument_instance->assigned ) { + g_warning( "%s: %s can only assign '%s' once", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_param_spec_get_name( pspec ) ); + return; + } + + switch( vips7_lookup_type( vt ) ) { + case VIPS7_DOUBLE: + *((double*)vi->vargv[i]) = g_value_get_double( value ); + break; + + case VIPS7_INT: + *((int*)vi->vargv[i]) = g_value_get_int( value ); + break; + + case VIPS7_STRING: + VIPS_SETSTR( vi->vargv[i], g_value_get_string( value ) ); + break; + + case VIPS7_GVALUE: + vi->vargv[i] = value; + break; + + case VIPS7_IMAGE: + case VIPS7_INTERPOLATE: + vi->vargv[i] = g_value_get_object( value ); + break; + + default: + vips7->error = TRUE; + break; + } + + /* Note that it's now been set. + */ + argument_instance->assigned = TRUE; +} + +static void +vips7_object_get_property( GObject *gobject, + guint property_id, GValue *value, GParamSpec *pspec ) +{ + VipsObject *object = VIPS_OBJECT( gobject ); + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gobject ); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup( class->argument_table, pspec ); + + Vips7 *vips7 = VIPS7( gobject ); + Vips7Class *class = VIPS7_GET_CLASS( vips7 ); + int i = argument_class->offset; + im_arg_desc *arg = &class->fn->argv[i]; + im_type_desc *type = arg->desc; + im_arg_type vt = type->type; + + if( !argument_class ) { + G_OBJECT_WARN_INVALID_PROPERTY_ID( gobject, + property_id, pspec ); + return; + } + + g_assert( ((VipsArgument *) argument_class)->pspec == pspec ); + + if( !argument_instance->assigned ) { + g_warning( "%s: %s attempt to read unset property %s", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) ) ); + return; + } + + switch( vips7_lookup_type( vt ) ) { + case VIPS7_DOUBLE: + g_value_set_double( value, *((double*)vi->vargv[i]) ); + break; + + case VIPS7_INT: + g_value_set_int( value, *((int*)vi->vargv[i]) ); + break; + + case VIPS7_STRING: + g_value_set_string( value, vi->vargv[i] ); + break; + + case VIPS7_IMAGE: + case VIPS7_INTERPOLATE: + case VIPS7_GVALUE: + g_value_set_object( value, vi->vargv[i] ); + break; + + default: + g_warning( "%s: %s unimplemented property type %s", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) ) ); + break; + } +} static int -vips_vips7_build( VipsObject *object ) +vips7_build( VipsObject *object ) { - VipsVips7 *vips7 = VIPS_VIPS7( object ); + Vips7 *vips7 = VIPS_VIPS7( object ); + Vips7Class *class = VIPS7_GET_CLASS( vips7 ); + im_function *fn = class->fn; + + if( vips7->error ) { + vips_error( "vips7", + _( "error constructing vips7 operation %s" ), + class->nickname ); + return( -1 ); + } + + if( class->not_supported ) { + vips_error( "vips7", _( "unable to call vips7 operation " + "%s from vips8" ), class->nickname ); + return( -1 ); + } if( VIPS_OBJECT_CLASS( parent_class )->build( object ) ) return( -1 ); + if( fn->disp( vips7->vargv ) ) + return( -1 ); + return( 0 ); } static void -vips_vips7_class_init( VipsVips7Class *class ) +vips7_class_init( VipsVips7Class *class ) { /* The name of the vips operation we wrap is hidden in our class name. */ @@ -87,36 +424,184 @@ vips_vips7_class_init( VipsVips7Class *class ) g_assert( fn ); - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - - vobject_class->build = vips_vips7_build; + gobject_class->dispose = vips7_dispose; + gobject_class->finalize = vips7_finalize; + gobject_class->set_property = vips7_object_set_property; + gobject_class->get_property = vips7_object_get_property; + object_class->build = vips7_build; object_class->nickname = name; object_class->description = fn->desc; + class->fn = fn; + for( i = 0; i < fn->argc; i++ ) { + im_arg_desc *arg = &fn->argv[i]; + im_type_desc *type = arg->desc; + im_arg_type vt = type->type; + GParamSpec *pspec; - pspec = g_param_spec_object( "out", "Output", - _( "Output image" ), - VIPS_TYPE_IMAGE, - G_PARAM_READWRITE ); - g_object_class_install_property( gobject_class, - PROP_OUTPUT, pspec ); - vips_object_class_install_argument( vobject_class, pspec, - VIPS_ARGUMENT_REQUIRED_OUTPUT, - G_STRUCT_OFFSET( VipsArithmetic, output ) ); + switch( vips7_lookup_type( vt ) ) { + case VIPS7_DOUBLEVEC: + case VIPS7_DMASK: + case VIPS7_IMASK: + case VIPS7_IMAGEVEC: + case VIPS7_INTVEC: + case VIPS7_GVALUE: + case VIPS7_INTERPOLATE: + case VIPS7_DOUBLE: + case VIPS7_INT: + case VIPS7_COMPLEX: + case VIPS7_STRING: + case VIPS7_NONE: + /* Can't wrap this function. class_init can't fail, so + * set a flag to block _build(). + */ + class->not_supported = TRUE; + break; + + case VIPS7_IMAGE: + pspec = g_param_spec_object( arg->name, + arg->name, + arg->name, + VIPS_TYPE_IMAGE, + G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, + i, pspec ); + vips_object_class_install_argument( vobject_class, + pspec, + (type->flags & IM_TYPE_OUTPUT) ? + VIPS_ARGUMENT_REQUIRED_OUTPUT : + VIPS_ARGUMENT_REQUIRED_INPUT, + i ); + break; + + default: + g_assert( 0 ); + } } } static void -vips_vips7_init( VipsVips7 *vips7 ) +vips7_arg_close( GObject *argument, + VipsArgumentInstance *argument_instance ) { + VipsObject *object = argument_instance->object; + GParamSpec *pspec = ((VipsArgument *) argument_instance)->pspec; + + g_object_unref( object ); +} + +/* Init an output slot in vargv. + */ +static void * +vips7_build_output( VipsObject *object, + GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b ) +{ + Vips7 *vips7 = VIPS7( object ); + Vips7Class *class = VIPS7_GET_CLASS( vips7 ); + int i = argument_class->offset; + im_arg_desc *arg = &class->fn->argv[i]; + im_type_desc *type = arg->desc; + im_arg_type vt = type->type; + + /* We want required, construct-time, unassigned output args. + */ + if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) || + !(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) || + argument_instance->assigned || + !(argument_class->flags & VIPS_ARGUMENT_OUTPUT) ) + return( NULL ); + + /* Provide output objects for the operation to write to. + */ + switch( vips7_lookup_type( vt ) ) { + case VIPS7_DOUBLE: + case VIPS7_INT: + case VIPS7_COMPLEX: + case VIPS7_STRING: + break; + + case VIPS7_IMAGE: + /* Output objects ref this operation. + */ + vips7->vargv[i] = vips_image_new(); + g_object_ref( vips7 ); + + /* vipsobject will handle close_id disconnect for us. + */ + argument_instance->close_id = + g_signal_connect( *member, "close", + G_CALLBACK( vips7_arg_close ), + argument_instance ); + break; + + case VIPS7_DMASK: + case VIPS7_IMASK: + { + im_mask_object *mo = vips7->vargv[i]; + + mo->mask = NULL; + mo->name = im_strdup( NULL, "" ); + + break; + } + + case VIPS7_GVALUE: + { + GValue *value = vips7->vargv[i]; + + memset( value, 0, sizeof( GValue ) ); + + break; + } + + case VIPS7_DOUBLEVEC: + case VIPS7_INTVEC: + { + /* intvec is also int + pointer. + */ + im_doublevec_object *dv = vips7->vargv[i]; + + dv->n = 0; + dv->vec = NULL; + + break; + } + + default: + vips7->error = TRUE; + break; + } + + return( NULL ); +} + +static void +vips7_init( VipsVips7 *vips7 ) +{ + Vips7Class *class = VIPS7_GET_CLASS( vips7 ); + im_function *fn = class->fn; + + if( !(vips7->vargv = IM_ARRAY( NULL, fn->argc + 1, im_object )) || + im_allocate_vargv( vi->fn, vi->vargv ) ) { + vips7->error = TRUE; + return; + } + + /* Init all the output args. + */ + (void) vips_argument_map( VIPS_OBJECT( vips7 ), + vips_call_char_option, + (void *) name, (void *) value ); } static GType -vips_get_type( im_function *fn ) +vips7_get_type( im_function *fn ) { static const GTypeInfo info = { sizeof( VipsVips7Class ), @@ -146,3 +631,11 @@ vips_get_type( im_function *fn ) return( type ); } + + +/* Walk the whole of the vips7 operation table building classes. + */ +void +vips__init_vips7_classes( void ) +{ +} diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 2697021d..cd31fc45 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -743,6 +743,8 @@ vips_object_get_property( GObject *gobject, VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gobject ); VipsArgumentClass *argument_class = (VipsArgumentClass *) vips__argument_table_lookup( class->argument_table, pspec ); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance( argument_class, object ); if( !argument_class ) { G_OBJECT_WARN_INVALID_PROPERTY_ID( gobject, @@ -752,6 +754,14 @@ vips_object_get_property( GObject *gobject, g_assert( ((VipsArgument *) argument_class)->pspec == pspec ); + if( !argument_instance->assigned ) { + g_warning( "%s: %s attempt to read unset property %s", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) ) ); + return; + } + if( G_IS_PARAM_SPEC_STRING( pspec ) ) { char *member = G_STRUCT_MEMBER( char *, object, argument_class->offset );