From 534c05c56743d15a7f300c41adfbb642b5b13b84 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 2 Dec 2008 17:11:30 +0000 Subject: [PATCH] add vips8 arg stuff --- ChangeLog | 1 + TODO | 6 +- include/vips/object.h | 151 ++++++++- libsrc/iofuncs/meta.c | 1 + libsrc/iofuncs/object.c | 545 +++++++++++++++++++++++++++++++- libsrc/mosaicing/yafrsmooth.cpp | 24 ++ 6 files changed, 719 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 604eb7fb..058a1d56 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,6 +19,7 @@ - updated docs - interpolators use type introspection - added vips --list classes, does formats too +- copy/pasted some vips8 arg stuff, needs much hacking 11/9/08 started 7.16.3 - oop typo in manpage for im_project() diff --git a/TODO b/TODO index dbdfb062..938e05a7 100644 --- a/TODO +++ b/TODO @@ -6,8 +6,12 @@ nickname, description etc need to be properties so nip2 can read them + + + + add a g_get_children( "classname" ) -> ["child1-name", ..] to nip2, see old - vips8.c cooe? + vips8.c code? diff --git a/include/vips/object.h b/include/vips/object.h index 097c7317..0046649c 100644 --- a/include/vips/object.h +++ b/include/vips/object.h @@ -34,6 +34,130 @@ extern "C" { #endif /*__cplusplus*/ +typedef struct _VipsObject VipsObject; +typedef struct _VipsObjectClass VipsObjectClass; + +/* Track extra stuff for arguments to objects + */ + +/* Flags we associate with each argument. + */ +typedef enum _VipsArgumentFlags { + VIPS_ARGUMENT_NONE = 0, + + /* Must be set before _build(). + */ + VIPS_ARGUMENT_REQUIRED = 1, + + /* Can only be set before _build(). + */ + VIPS_ARGUMENT_CONSTRUCT = 2, + + /* Can only be set once. + */ + VIPS_ARGUMENT_SET_ONCE = 4, + + /* Have input & output flags. Both set is an error; neither set is OK. + */ + + /* Is an input argument (one we depend on) ... if it's a gobject, we + * should ref it. In our _dispose(), we should unref it. + */ + VIPS_ARGUMENT_INPUT = 8, + + /* Is an output argument (one that depends on us) ... if it's a + * gobject, we should ref ourselves. We watch "destroy" on the + * argument: if it goes, we unref ourselves. If we dispose, we + * disconnect the signal. + */ + VIPS_ARGUMENT_OUTPUT = 16 +} VipsArgumentFlags; + +/* Useful flag combinations. User-visible ones are: + +VIPS_ARGUMENT_REQURED_INPUT Eg. the "left" argument for an add operation + +VIPS_ARGUMENT_OPTIONAL_INPUT Eg. the "caption" for an object + +VIPS_ARGUMENT_REQURED_OUTPUT Eg. the "result" of an add operation + +VIPS_ARGUMENT_OPTIONAL_OUTPUT Eg. the "width" of an image + + Other combinations are used internally, eg. supplying the cast-table for an + arithmetic operation + + */ + +#define VIPS_ARGUMENT_REQUIRED_INPUT \ + (VIPS_ARGUMENT_INPUT | VIPS_ARGUMENT_REQUIRED | \ + VIPS_ARGUMENT_CONSTRUCT | VIPS_ARGUMENT_SET_ONCE) + +#define VIPS_ARGUMENT_OPTIONAL_INPUT \ + (VIPS_ARGUMENT_INPUT | \ + VIPS_ARGUMENT_CONSTRUCT | VIPS_ARGUMENT_SET_ONCE) + +#define VIPS_ARGUMENT_REQUIRED_OUTPUT \ + (VIPS_ARGUMENT_OUTPUT | VIPS_ARGUMENT_REQUIRED | \ + VIPS_ARGUMENT_SET_ONCE) + +#define VIPS_ARGUMENT_OPTIONAL_OUTPUT \ + (VIPS_ARGUMENT_OUTPUT | \ + VIPS_ARGUMENT_SET_ONCE) + +/* Keep one of these for every argument. + */ +typedef struct _VipsArgument { + GParamSpec *pspec; /* pspec for this argument */ + + /* More stuff, see below */ +} VipsArgument; + +/* Keep one of these in the class struct for every argument. + */ +typedef struct _VipsArgumentClass { + VipsArgument parent; + + /* The class of the object we are an arg for. + */ + VipsObjectClass *object_class; + + VipsArgumentFlags flags; + guint offset; /* G_STRUCT_OFFSET of member in object */ +} VipsArgumentClass; + +/* Keep one of these in the object struct for every argument instance. + */ +typedef struct _VipsArgumentInstance { + VipsArgument parent; + + /* The object we are attached to. + */ + VipsObject *object; + + /* Has been set. + */ + gboolean assigned; + + /* If this is an output argument, keep the id of our "destroy" handler + * here. + */ + gulong destroy_id; +} VipsArgumentInstance; + +/* Need to look up our VipsArgument structs from a pspec. Just hash the + * pointer (ie. we assume pspecs are never shared, is this correct?) + */ +typedef GHashTable VipsArgumentTable; + +VipsArgumentInstance *vips__argument_get_instance( VipsArgumentClass *, + VipsObject *); +VipsArgument *vips__argument_table_lookup( VipsArgumentTable *, + GParamSpec *); +typedef void *(*VipsArgumentMapFn)( VipsObject *, GParamSpec *, + VipsArgumentClass *, VipsArgumentInstance *, void *a, void *b ); +void *vips__argument_map( VipsObject *object, + VipsArgumentMapFn fn, void *a, void *b ); + #define VIPS_TYPE_OBJECT (vips_object_get_type()) #define VIPS_OBJECT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), VIPS_TYPE_OBJECT, VipsObject )) @@ -46,15 +170,17 @@ extern "C" { #define VIPS_OBJECT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), VIPS_TYPE_OBJECT, VipsObjectClass )) -typedef struct _VipsObject { +struct _VipsObject { GObject parent_object; - /* Optional instance name. - */ - char *name; -} VipsObject; + char *name; /* Optional instance name */ -typedef struct _VipsObjectClass { + /* Table of argument instances for this class and any derived classes. + */ + VipsArgumentTable *argument_table; +}; + +struct _VipsObjectClass { GObjectClass parent_class; /* Something about the object has changed. Should use glib's properties @@ -78,7 +204,15 @@ typedef struct _VipsObjectClass { /* Class description. Used for help messages, so internationalised. */ const char *description; -} VipsObjectClass; + + /* Table of arguments for this class and any derived classes. Order + * is important, so keep a traverse list too. We can't rely on the + * ordering given by g_object_class_list_properties() since it comes + * from a hash :-( + */ + VipsArgumentTable *argument_table; + GSList *argument_table_traverse; +}; void *vips_object_changed( VipsObject *object ); void vips_object_print_class( VipsObjectClass *klass ); @@ -86,6 +220,9 @@ void vips_object_print( VipsObject *object ); GType vips_object_get_type( void ); +void vips_object_class_install_argument( VipsObjectClass *, + GParamSpec *pspec, VipsArgumentFlags flags, guint offset ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libsrc/iofuncs/meta.c b/libsrc/iofuncs/meta.c index 696ec263..1ab06652 100644 --- a/libsrc/iofuncs/meta.c +++ b/libsrc/iofuncs/meta.c @@ -937,3 +937,4 @@ im__meta_init_types( void ) g_value_register_transform_func( IM_TYPE_SAVE_STRING, G_TYPE_DOUBLE, transform_save_string_double ); } + diff --git a/libsrc/iofuncs/object.c b/libsrc/iofuncs/object.c index 590e13a0..cc3e82e0 100644 --- a/libsrc/iofuncs/object.c +++ b/libsrc/iofuncs/object.c @@ -100,16 +100,247 @@ vips_object_print( VipsObject *object ) printf( "\n%s (%p)\n", im_buf_all( &buf ), object ); } +/* Extra stuff we track for properties to do our argument handling. + */ + +/* Free a VipsArgumentInstance ... VipsArgumentClass can just be g_free()d. + */ +static void +vips_argument_instance_free (VipsArgumentInstance *argument_instance) +{ + if (argument_instance->destroy_id) { + g_signal_handler_disconnect (argument_instance->object, + argument_instance->destroy_id); + argument_instance->destroy_id = 0; + } + g_free (argument_instance); +} + +VipsArgument * +vips__argument_table_lookup (VipsArgumentTable *table, + GParamSpec *pspec) +{ + return (g_hash_table_lookup (table, pspec)); +} + +static void +vips_argument_table_replace (VipsArgumentTable *table, VipsArgument *argument) +{ + g_hash_table_replace (table, argument->pspec, argument); +} + +static void +vips_argument_table_destroy (VipsArgumentTable *table) +{ + g_hash_table_destroy (table); +} + +static void * +vips_argument_init_sub (VipsObject *object, GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance) +{ + VipsArgument *argument; + +#ifdef DEBUG + printf ("vips_argument_init_sub: adding instance argument for %s\n", + pspec->name); +#endif /*DEBUG*/ + + /* argument_instance should be NULL since we've not set it yet. + */ + g_assert (argument_instance == NULL); + + argument_instance = g_new (VipsArgumentInstance, 1); + argument = (VipsArgument *) argument_instance; + + argument->pspec = ((VipsArgument *) argument_class)->pspec; + argument_instance->object = object; + argument_instance->assigned = FALSE; + argument_instance->destroy_id = 0; + + vips_argument_table_replace (object->argument_table, argument); + + return (NULL); +} + +/* Create a VipsArgumentInstance for each installed argument property. Ideally + * we'd do this during _init() but g_object_class_find_property() does not seem + * to work then :-( so we have to delay it until first access. + */ +static void +vips_argument_init (VipsObject *object) +{ + if( !object->argument_table ) { + object->argument_table = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, + (GDestroyNotify) vips_argument_instance_free); + + /* Make a VipsArgumentInstance for each installed argument + * property. + */ + vips__argument_map (object, + (VipsArgumentMapFn) vips_argument_init_sub, NULL, NULL); + } +} + +/* Convenience ... given the VipsArgumentClass, get the VipsArgumentInstance. + */ +VipsArgumentInstance * +vips__argument_get_instance (VipsArgumentClass *argument_class, + VipsObject *object) +{ + /* Make sure the instance args are built. + */ + vips_argument_init (object); + + return ((VipsArgumentInstance *) + vips__argument_table_lookup (object->argument_table, + ((VipsArgument *) argument_class)->pspec)); +} + +/* Loop over the vips_arguments to an object. + * + * Note: this code copy-pasted into vips_operation_call_valist(), keep in sync. + */ +void * +vips__argument_map (VipsObject *object, VipsArgumentMapFn fn, void *a, void *b) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS (object); + GSList *p; + + for (p = object_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 (object_class), + pspec->name) == pspec) { + void *result; + + if ((result = fn (object, pspec, + argument_class, argument_instance, a, b))) + return (result); + } + } + + return (NULL); +} + +static void +vips_object_clear_object (VipsObject *object, GParamSpec *pspec) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS (object); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup (class->argument_table, pspec); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance (argument_class, object); + GObject **member = &G_STRUCT_MEMBER (GObject *, object, + argument_class->offset); + + if (*member) { + if (argument_class->flags & VIPS_ARGUMENT_INPUT) { +#ifdef DEBUG_REF + printf ("vips_object_clear_object: vips object: "); + vips_object_print (object); + printf (" no longer refers to gobject %s (%p)\n", + G_OBJECT_TYPE_NAME (*member), *member); + printf (" count down to %d\n", + G_OBJECT (*member)->ref_count - 1); +#endif /*DEBUG_REF*/ + + /* We reffed the object. + */ + g_object_unref (*member); + } + else if (argument_class->flags & VIPS_ARGUMENT_OUTPUT) { +#ifdef DEBUG_REF + printf ("vips_object_clear_object: gobject %s (%p)\n", + G_OBJECT_TYPE_NAME (*member), *member); + printf (" no longer refers to vips object: "); + vips_object_print (object); + printf (" count down to %d\n", + G_OBJECT (object)->ref_count - 1); +#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 + * will already have been disconnected for us. + */ + if( g_signal_handler_is_connected (object, + argument_instance->destroy_id)) + g_signal_handler_disconnect (object, + argument_instance->destroy_id); + argument_instance->destroy_id = 0; + *member = NULL; + + g_object_unref (object); + } + + *member = NULL; + } +} + +/* Free any args which are holding resources. + */ +static void * +vips_object_dispose_argument (VipsObject *object, GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b) +{ +#ifdef DEBUG + printf ("vips_object_dispose_argument: %s.%s\n", + object->name, pspec->name); +#endif /*DEBUG*/ + + g_assert (((VipsArgument *) argument_class)->pspec == pspec); + g_assert (((VipsArgument *) argument_instance)->pspec == pspec); + + if (G_IS_PARAM_SPEC_STRING (pspec)) { + char **member = &G_STRUCT_MEMBER (char *, object, + argument_class->offset); + + IM_FREE (*member); + } + else if (G_IS_PARAM_SPEC_OBJECT (pspec)) + vips_object_clear_object (object, pspec); + else if (G_IS_PARAM_SPEC_BOXED (pspec)) { + gpointer *member = &G_STRUCT_MEMBER (gpointer, object, + argument_class->offset); + + if (*member) { + g_boxed_free (G_PARAM_SPEC_VALUE_TYPE (pspec), *member); + *member = NULL; + } + } + + return (NULL); +} + static void vips_object_dispose( GObject *gobject ) { -#ifdef DEBUG VipsObject *object = VIPS_OBJECT( gobject ); +#ifdef DEBUG printf( "vips_object_dispose: " ); vips_object_print( object ); #endif /*DEBUG*/ + /* Clear all our arguments: they may be holding refs we should drop. + */ + vips__argument_map (object, + vips_object_dispose_argument, NULL, NULL); + G_OBJECT_CLASS( vips_object_parent_class )->dispose( gobject ); } @@ -123,11 +354,260 @@ vips_object_finalize( GObject *gobject ) vips_object_print( object ); #endif /*DEBUG*/ + IM_FREEF (vips_argument_table_destroy, object->argument_table); IM_FREE( object->name ); G_OBJECT_CLASS( vips_object_parent_class )->finalize( gobject ); } +static void +vips_object_arg_destroy (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. + */ + vips_object_clear_object (object, pspec); +} + +static void +vips_object_set_object (VipsObject *object, GParamSpec *pspec, + GObject *argument) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS (object); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup (class->argument_table, pspec); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance (argument_class, object); + GObject **member = &G_STRUCT_MEMBER (GObject *, object, + argument_class->offset); + + g_assert (!*member); + + *member = argument; + + if (*member) { + if (argument_class->flags & VIPS_ARGUMENT_INPUT) { +#ifdef DEBUG_REF + printf ("vips_object_set_object: vips object: "); + vips_object_print (object); + printf (" refers to gobject %s (%p)\n", + G_OBJECT_TYPE_NAME (*member), *member); + printf (" count up to %d\n", + G_OBJECT (*member)->ref_count); +#endif /*DEBUG_REF*/ + + /* Ref the argument. + */ + g_object_ref (*member); + } + else if (argument_class->flags & VIPS_ARGUMENT_OUTPUT) { +#ifdef DEBUG_REF + printf ("vips_object_set_object: gobject %s (%p)\n", + G_OBJECT_TYPE_NAME (*member), *member); + printf (" refers to vips object: "); + vips_object_print (object); + printf (" count up to %d\n", + G_OBJECT (object)->ref_count); +#endif /*DEBUG_REF*/ + + /* 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); + } + } +} + +static void +vips_object_set_property (GObject *gobject, + guint property_id, const 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); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance (argument_class, object); + + 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: %s.%s = %s\n", + object->name, pspec->name, 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. + + FIXME ... how do we spot end of construct? put this back + + if (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT && + object->done_build) { + g_warning ("%s: can't assign '%s' after construct", G_STRLOC, + ((VipsArgument *)argument_class)->pspec->name); + 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: can only assign '%s' once", G_STRLOC, + ((VipsArgument *)argument_class)->pspec->name); + return; + } + + if (G_IS_PARAM_SPEC_STRING (pspec)) { + char **member = &G_STRUCT_MEMBER (char *, object, + argument_class->offset); + + IM_SETSTR (*member, g_value_get_string (value)); + } + else if (G_IS_PARAM_SPEC_OBJECT (pspec)) { + /* Remove any old object. + */ + vips_object_clear_object (object, pspec); + + /* Install the new object. + */ + vips_object_set_object (object, pspec, + g_value_get_object (value)); + } + else if (G_IS_PARAM_SPEC_INT (pspec)) { + int *member = &G_STRUCT_MEMBER (int, object, + argument_class->offset); + + *member = g_value_get_int (value); + } + else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) { + gboolean *member = &G_STRUCT_MEMBER (gboolean, object, + argument_class->offset); + + *member = g_value_get_boolean (value); + } + else if (G_IS_PARAM_SPEC_ENUM (pspec)) { + int *member = &G_STRUCT_MEMBER (int, object, + argument_class->offset); + + *member = g_value_get_enum (value); + } + else if (G_IS_PARAM_SPEC_POINTER (pspec)) { + gpointer *member = &G_STRUCT_MEMBER (gpointer, object, + argument_class->offset); + + *member = g_value_get_pointer (value); + } + else if (G_IS_PARAM_SPEC_BOXED (pspec)) { + gpointer *member = &G_STRUCT_MEMBER (gpointer, object, + argument_class->offset); + + if (*member) { + g_boxed_free (G_PARAM_SPEC_VALUE_TYPE (pspec), *member); + *member = NULL; + } + + /* Copy the boxed into our pointer (will use eg. + * vips__object_vector_dup ()). + */ + *member = g_value_dup_boxed (value); + } + else { + g_warning ("%s: '%s' has unimplemented type", G_STRLOC, + ((VipsArgument *)argument_class)->pspec->name); + } + + /* Note that it's now been set. + */ + argument_instance->assigned = TRUE; +} + +static void +vips_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); + + if (!argument_class) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, + property_id, pspec); + return; + } + + g_assert (((VipsArgument *) argument_class)->pspec == pspec); + + if (G_IS_PARAM_SPEC_STRING (pspec)) { + char *member = G_STRUCT_MEMBER (char *, object, + argument_class->offset); + + g_value_set_string (value, member); + } + else if (G_IS_PARAM_SPEC_OBJECT (pspec)) { + GObject **member = &G_STRUCT_MEMBER (GObject *, object, + argument_class->offset); + + g_value_set_object (value, *member); + } + else if (G_IS_PARAM_SPEC_INT (pspec)) { + int *member = &G_STRUCT_MEMBER (int, object, + argument_class->offset); + + g_value_set_int (value, *member); + } + else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) { + gboolean *member = &G_STRUCT_MEMBER (gboolean, object, + argument_class->offset); + + g_value_set_boolean (value, *member); + } + else if (G_IS_PARAM_SPEC_ENUM (pspec)) { + int *member = &G_STRUCT_MEMBER (int, object, + argument_class->offset); + + g_value_set_enum (value, *member); + } + else if (G_IS_PARAM_SPEC_POINTER (pspec)) { + gpointer *member = &G_STRUCT_MEMBER (gpointer, object, + argument_class->offset); + + g_value_set_pointer (value, *member); + } + else if (G_IS_PARAM_SPEC_BOXED (pspec)) { + gpointer *member = &G_STRUCT_MEMBER (gpointer, object, + argument_class->offset); + + /* Copy the boxed into our pointer (will use eg. + * vips__object_vector_dup ()). + */ + g_value_set_boxed (value, *member); + } + else { + g_warning ("%s: unimplemented property type", G_STRLOC); + } +} + static void vips_object_real_changed( VipsObject *object ) { @@ -163,6 +643,8 @@ vips_object_class_init( VipsObjectClass *class ) gobject_class->dispose = vips_object_dispose; gobject_class->finalize = vips_object_finalize; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; class->changed = vips_object_real_changed; class->print_class = vips_object_real_print_class; @@ -195,3 +677,64 @@ vips_object_set_name( VipsObject *object, const char *name ) vips_object_changed( object ); } +/* Add a vipsargument ... automate some stuff with this. + */ +void +vips_object_class_install_argument (VipsObjectClass *object_class, + GParamSpec *pspec, VipsArgumentFlags flags, guint offset) +{ + VipsArgumentClass *argument_class = g_new (VipsArgumentClass, 1); + + /* Must be a new one. + */ + g_assert (!vips__argument_table_lookup (object_class->argument_table, + pspec)); + + /* Mustn't have INPUT and OUTPUT both set. + */ + g_assert ((flags & (VIPS_ARGUMENT_INPUT | VIPS_ARGUMENT_OUTPUT)) != + (VIPS_ARGUMENT_INPUT | VIPS_ARGUMENT_OUTPUT)); + + ((VipsArgument *) argument_class)->pspec = pspec; + argument_class->object_class = object_class; + argument_class->flags = flags; + argument_class->offset = offset; + + vips_argument_table_replace (object_class->argument_table, + (VipsArgument *) argument_class); + object_class->argument_table_traverse = g_slist_append ( + object_class->argument_table_traverse, argument_class); +} + +/* Has a property been set? + */ +gboolean +vips_object_argument_assigned (VipsObject *object, const char *property_name) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS (object); + GParamSpec *pspec; + VipsArgumentClass *argument_class; + VipsArgumentInstance *argument_instance; + + if (!(pspec = g_object_class_find_property ( + G_OBJECT_CLASS (object_class), property_name))) { + g_warning ("property %s not found", property_name); + return (FALSE); + } + if (!(argument_class = (VipsArgumentClass *) + vips__argument_table_lookup (object_class->argument_table, + pspec))) { + g_warning ("vips argument for property %s not found", + property_name); + return (FALSE); + } + if (!(argument_instance = vips__argument_get_instance ( + argument_class, object))) { + g_warning ("properties for vips argument %s not found", + property_name); + return (FALSE); + } + + return (argument_instance->assigned); +} + diff --git a/libsrc/mosaicing/yafrsmooth.cpp b/libsrc/mosaicing/yafrsmooth.cpp index 188c9f2b..0accb310 100644 --- a/libsrc/mosaicing/yafrsmooth.cpp +++ b/libsrc/mosaicing/yafrsmooth.cpp @@ -128,6 +128,14 @@ */ #define SMOOTH_SHARPENING_SCALE (0.453125f) +/* Properties. + */ +enum { + PROP_SHARPENING = 1, + PROP_LAST +}; + + #define VIPS_TYPE_INTERPOLATE_YAFRSMOOTH \ (vips_interpolate_yafrsmooth_get_type()) #define VIPS_INTERPOLATE_YAFRSMOOTH( obj ) \ @@ -740,10 +748,13 @@ vips_interpolate_yafrsmooth_interpolate( VipsInterpolate *interpolate, static void vips_interpolate_yafrsmooth_class_init( VipsInterpolateYafrsmoothClass *iclass ) { + GObjectClass *gobject_class = G_OBJECT_CLASS( iclass ); VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); VipsInterpolateClass *interpolate_class = VIPS_INTERPOLATE_CLASS( iclass ); + GParamSpec *pspec; + object_class->nickname = "yafrsmooth"; object_class->description = _( "YAFR smooth interpolation" ); @@ -762,6 +773,19 @@ vips_interpolate_yafrsmooth_class_init( VipsInterpolateYafrsmoothClass *iclass ) iclass->matrixi[x][i] = iclass->matrixf[x][i] * VIPS_INTERPOLATE_SCALE; } + + /* Create properties. + */ + pspec = g_param_spec_double( "sharpening", + _( "Sharpening" ), + _( "Degree of extra edge enhancement" ), + 0, 4, 1, + (GParamFlags) G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, + PROP_SHARPENING, pspec ); + vips_object_class_install_argument( object_class, pspec, + VIPS_ARGUMENT_SET_ONCE, + G_STRUCT_OFFSET( VipsInterpolateYafrsmooth, sharpening ) ); } static void