1077 lines
28 KiB
C
1077 lines
28 KiB
C
/* abstract base class for all vips objects
|
|
*
|
|
* Edited from nip's base class, 15/10/08
|
|
*/
|
|
|
|
/*
|
|
|
|
Copyright (C) 1991-2003 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 <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <vips/vips.h>
|
|
#include <vips/internal.h>
|
|
|
|
#ifdef WITH_DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif /*WITH_DMALLOC*/
|
|
|
|
/* Our signals.
|
|
*/
|
|
enum {
|
|
SIG_CHANGED, /* VipsObject has changed somehow */
|
|
SIG_LAST
|
|
};
|
|
|
|
/* Properties.
|
|
*/
|
|
enum {
|
|
PROP_NICKNAME = 1,/* Class properties as object props */
|
|
PROP_DESCRIPTION,
|
|
PROP_LAST
|
|
};
|
|
|
|
static guint vips_object_signals[SIG_LAST] = { 0 };
|
|
|
|
G_DEFINE_ABSTRACT_TYPE( VipsObject, vips_object, G_TYPE_OBJECT );
|
|
|
|
void *
|
|
vips_object_changed( VipsObject *object )
|
|
{
|
|
g_return_val_if_fail( object != NULL, NULL );
|
|
g_return_val_if_fail( VIPS_IS_OBJECT( object ), NULL );
|
|
|
|
#ifdef DEBUG
|
|
printf( "vips_object_changed: " );
|
|
vips_object_print( object );
|
|
#endif /*DEBUG*/
|
|
|
|
g_signal_emit( G_OBJECT( object ),
|
|
vips_object_signals[SIG_CHANGED], 0 );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
int
|
|
vips_object_build( VipsObject *object )
|
|
{
|
|
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
|
|
|
|
#ifdef DEBUG
|
|
printf( "vips_object_build: " );
|
|
vips_object_print( object );
|
|
#endif /*DEBUG*/
|
|
|
|
return( object_class->build( object ) );
|
|
}
|
|
|
|
void
|
|
vips_object_print_class( VipsObjectClass *class )
|
|
{
|
|
VipsBuf buf;
|
|
char str[1000];
|
|
|
|
vips_buf_init_static( &buf, str, 1000 );
|
|
class->print_class( class, &buf );
|
|
printf( "%s\n", vips_buf_all( &buf ) );
|
|
}
|
|
|
|
void
|
|
vips_object_print( VipsObject *object )
|
|
{
|
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
|
VipsBuf buf;
|
|
char str[1000];
|
|
|
|
vips_object_print_class( class );
|
|
vips_buf_init_static( &buf, str, 1000 );
|
|
class->print( object, &buf );
|
|
printf( "\n%s (%p)\n", vips_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 );
|
|
}
|
|
|
|
/* Loop over the vips_arguments to an object.
|
|
*/
|
|
void *
|
|
vips_argument_map( VipsObject *object,
|
|
VipsArgumentMapFn fn, void *a, void *b )
|
|
{
|
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
|
GSList *p;
|
|
|
|
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 ) {
|
|
void *result;
|
|
|
|
if( (result = fn( object, pspec,
|
|
argument_class, argument_instance, a, b )) )
|
|
return( result );
|
|
}
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static void *
|
|
vips_argument_init2( VipsObject *object, GParamSpec *pspec,
|
|
VipsArgumentClass *argument_class,
|
|
VipsArgumentInstance *argument_instance,
|
|
void *a, void *b )
|
|
{
|
|
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, vips_argument_init2, 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 ) );
|
|
}
|
|
|
|
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 )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
static void
|
|
vips_object_finalize( GObject *gobject )
|
|
{
|
|
VipsObject *object = VIPS_OBJECT( gobject );
|
|
|
|
#ifdef DEBUG
|
|
printf( "vips_object_finalize: " );
|
|
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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Also used by subclasses, so not 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, 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;
|
|
}
|
|
|
|
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_DOUBLE( pspec ) ) {
|
|
double *member = &G_STRUCT_MEMBER( double, object,
|
|
argument_class->offset );
|
|
|
|
*member = g_value_get_double( 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 unimplemented property type %s",
|
|
G_STRLOC,
|
|
G_OBJECT_TYPE_NAME( gobject ),
|
|
g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) ) );
|
|
}
|
|
|
|
/* Note that it's now been set.
|
|
*/
|
|
argument_instance->assigned = TRUE;
|
|
}
|
|
|
|
/* Also used by subclasses, so not 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_DOUBLE( pspec ) ) {
|
|
double *member = &G_STRUCT_MEMBER( double, object,
|
|
argument_class->offset );
|
|
|
|
g_value_set_double( 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: %s unimplemented property type %s",
|
|
G_STRLOC,
|
|
G_OBJECT_TYPE_NAME( gobject ),
|
|
g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) ) );
|
|
}
|
|
}
|
|
|
|
static void *
|
|
vips_object_check_required( VipsObject *object, GParamSpec *pspec,
|
|
VipsArgumentClass *argument_class,
|
|
VipsArgumentInstance *argument_instance,
|
|
void *a, void *b )
|
|
{
|
|
int *result = (int *) a;
|
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
|
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
|
!argument_instance->assigned ) {
|
|
im_error( "check_required",
|
|
_( "required construct param %s to %s %s not set" ),
|
|
g_param_spec_get_name( pspec ),
|
|
G_OBJECT_TYPE_NAME( object ),
|
|
object->name );
|
|
*result = -1;
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static int
|
|
vips_object_real_build( VipsObject *object )
|
|
{
|
|
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
|
|
|
|
int result;
|
|
|
|
g_assert( !object->constructed );
|
|
|
|
/* Check all required arguments have been supplied, don't stop on 1st
|
|
* error.
|
|
*/
|
|
result = 0;
|
|
(void) vips_argument_map( object,
|
|
vips_object_check_required, &result, NULL );
|
|
|
|
/* ... more checks go here.
|
|
*/
|
|
object->constructed = TRUE;
|
|
|
|
/* It'd be nice if this just copied a pointer rather than did a
|
|
* strdup(). Set these here rather than in object_init, so that the
|
|
* class gets a chance to set them.
|
|
*/
|
|
g_object_set( object,
|
|
"nickname", object_class->nickname,
|
|
"description", object_class->description, NULL );
|
|
|
|
return( result );
|
|
}
|
|
|
|
static void
|
|
vips_object_real_changed( VipsObject *object )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "vips_object_real_changed: " );
|
|
vips_object_print( object );
|
|
#endif /*DEBUG*/
|
|
}
|
|
|
|
static void
|
|
vips_object_real_print_class( VipsObjectClass *class, VipsBuf *buf )
|
|
{
|
|
vips_buf_appendf( buf, "%s", G_OBJECT_CLASS_NAME( class ) );
|
|
if( class->nickname )
|
|
vips_buf_appendf( buf, " (%s)", class->nickname );
|
|
if( class->description )
|
|
vips_buf_appendf( buf, ", %s", class->description );
|
|
}
|
|
|
|
static void
|
|
vips_object_real_print( VipsObject *object, VipsBuf *buf )
|
|
{
|
|
if( object->name )
|
|
vips_buf_appendf( buf, "\"%s\"", object->name );
|
|
}
|
|
|
|
static void
|
|
transform_string_double( const GValue *src_value, GValue *dest_value )
|
|
{
|
|
g_value_set_double( dest_value,
|
|
g_ascii_strtod( g_value_get_string( src_value ), NULL ) );
|
|
}
|
|
|
|
static void
|
|
vips_object_class_init( VipsObjectClass *object_class )
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS( object_class );
|
|
|
|
GParamSpec *pspec;
|
|
|
|
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;
|
|
|
|
object_class->build = vips_object_real_build;
|
|
object_class->changed = vips_object_real_changed;
|
|
object_class->print_class = vips_object_real_print_class;
|
|
object_class->print = vips_object_real_print;
|
|
object_class->nickname = "object";
|
|
object_class->description = _( "VIPS base class" );
|
|
|
|
/* Table of VipsArgumentClass ... we can just g_free() them.
|
|
*/
|
|
object_class->argument_table = g_hash_table_new_full(
|
|
g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_free );
|
|
object_class->argument_table_traverse = NULL;
|
|
|
|
vips_object_signals[SIG_CHANGED] = g_signal_new( "changed",
|
|
G_OBJECT_CLASS_TYPE( gobject_class ),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET( VipsObjectClass, changed ),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0 );
|
|
|
|
/* For setting double arguments from the command-line.
|
|
*/
|
|
g_value_register_transform_func( G_TYPE_STRING, G_TYPE_DOUBLE,
|
|
transform_string_double );
|
|
|
|
/* Create properties.
|
|
*/
|
|
pspec = g_param_spec_string( "nickname",
|
|
_( "Nickname" ),
|
|
_( "Class nickname" ),
|
|
"",
|
|
(GParamFlags) G_PARAM_READWRITE );
|
|
g_object_class_install_property( gobject_class,
|
|
PROP_NICKNAME, pspec );
|
|
vips_object_class_install_argument( object_class, pspec,
|
|
VIPS_ARGUMENT_SET_ONCE,
|
|
G_STRUCT_OFFSET( VipsObject, nickname ) );
|
|
|
|
pspec = g_param_spec_string( "description",
|
|
_( "Description" ),
|
|
_( "Class description" ),
|
|
"",
|
|
(GParamFlags) G_PARAM_READWRITE );
|
|
g_object_class_install_property( gobject_class,
|
|
PROP_DESCRIPTION, pspec );
|
|
vips_object_class_install_argument( object_class, pspec,
|
|
VIPS_ARGUMENT_SET_ONCE,
|
|
G_STRUCT_OFFSET( VipsObject, description ) );
|
|
|
|
}
|
|
|
|
static void
|
|
vips_object_init( VipsObject *object )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "vips_object_init: " );
|
|
vips_object_print( object );
|
|
#endif /*DEBUG*/
|
|
}
|
|
|
|
void
|
|
vips_object_set_name( VipsObject *object, const char *name )
|
|
{
|
|
IM_SETSTR( object->name, 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 );
|
|
|
|
#ifdef DEBUG
|
|
printf( "vips_object_class_install_argument: %s\n", pspec->name );
|
|
#endif /*DEBUG*/
|
|
|
|
/* 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) &&
|
|
(flags & 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 );
|
|
}
|
|
|
|
/* Set a named arg from a string.
|
|
*/
|
|
static int
|
|
vips_object_set_arg( VipsObject *object, const char *name, const char *value )
|
|
{
|
|
GValue gvalue = { 0, };
|
|
|
|
g_value_init( &gvalue, G_TYPE_STRING );
|
|
g_value_set_string( &gvalue, value );
|
|
g_object_set_property( G_OBJECT( object ), name, &gvalue );
|
|
g_value_unset( &gvalue );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static void *
|
|
vips_object_set_required_test( VipsObject *object,
|
|
GParamSpec *pspec,
|
|
VipsArgumentClass *argument_class,
|
|
VipsArgumentInstance *argument_instance,
|
|
void *a, void *b )
|
|
{
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
|
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
|
!argument_instance->assigned )
|
|
return( pspec );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* Set the first unassigned required arg to the string.
|
|
*/
|
|
static int
|
|
vips_object_set_required( VipsObject *object, const char *value )
|
|
{
|
|
GParamSpec *pspec;
|
|
|
|
if( !(pspec = vips_argument_map( object,
|
|
vips_object_set_required_test, NULL, NULL )) ) {
|
|
im_error( "vips_object_set_required",
|
|
_( "no unset required arguments for %s" ), value );
|
|
return( -1 );
|
|
}
|
|
|
|
if( vips_object_set_arg( object, pspec->name, value ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Set object args from a string. We've seen the '(', we need to check for the
|
|
* closing ')' and make sure there's no extra stuff.
|
|
*/
|
|
static int
|
|
vips_object_set_args( VipsObject *object, const char *p )
|
|
{
|
|
VipsToken token;
|
|
char string[PATH_MAX];
|
|
char string2[PATH_MAX];
|
|
|
|
do {
|
|
if( !(p = vips__token_need( p, VIPS_TOKEN_STRING,
|
|
string, PATH_MAX )) )
|
|
return( -1 );
|
|
|
|
/* We have to look for a '=', ')' or a ',' to see if string is
|
|
* a param name or a value.
|
|
*/
|
|
if( !(p = vips__token_must( p, &token, string2, PATH_MAX )) )
|
|
return( -1 );
|
|
if( token == VIPS_TOKEN_EQUALS ) {
|
|
if( !(p = vips__token_need( p, VIPS_TOKEN_STRING,
|
|
string2, PATH_MAX )) )
|
|
return( -1 );
|
|
if( vips_object_set_arg( object, string, string2 ) )
|
|
return( -1 );
|
|
if( !(p = vips__token_must( p, &token,
|
|
string2, PATH_MAX )) )
|
|
return( -1 );
|
|
}
|
|
else {
|
|
if( vips_object_set_required( object, string ) )
|
|
return( -1 );
|
|
}
|
|
|
|
/* Now must be a , or a ).
|
|
*/
|
|
if( token != VIPS_TOKEN_RIGHT && token != VIPS_TOKEN_COMMA ) {
|
|
im_error( "set_args", "%s",
|
|
_( "not , or ) after parameter" ) );
|
|
return( -1 );
|
|
}
|
|
} while( token != VIPS_TOKEN_RIGHT );
|
|
|
|
if( (p = vips__token_get( p, &token, string, PATH_MAX )) ) {
|
|
im_error( "set_args", "%s",
|
|
_( "extra tokens after ')'" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
VipsObject *
|
|
vips_object_new_from_string( const char *basename, const char *p )
|
|
{
|
|
VipsToken token;
|
|
char string[PATH_MAX];
|
|
GType type;
|
|
VipsObject *object;
|
|
|
|
if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, string, PATH_MAX )) ||
|
|
!(type = vips_type_find( basename, string )) )
|
|
return( NULL );
|
|
|
|
object = VIPS_OBJECT( g_object_new( type, NULL ) );
|
|
|
|
if( (p = vips__token_get( p, &token, string, PATH_MAX )) ) {
|
|
if( token == VIPS_TOKEN_LEFT &&
|
|
vips_object_set_args( object, p ) ) {
|
|
im_error( "object_new", "%s",
|
|
_( "bad object arguments" ) );
|
|
g_object_unref( object );
|
|
return( NULL );
|
|
}
|
|
}
|
|
|
|
if( vips_object_build( object ) ) {
|
|
g_object_unref( object );
|
|
return( NULL );
|
|
}
|
|
|
|
return( object );
|
|
}
|
|
|
|
static void
|
|
vips_object_print_arg( VipsObject *object, GParamSpec *pspec, VipsBuf *buf )
|
|
{
|
|
GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
|
|
const char *name = g_param_spec_get_name( pspec );
|
|
GValue value = { 0 };
|
|
char *str_value;
|
|
|
|
g_value_init( &value, type );
|
|
g_object_get_property( G_OBJECT( object ), name, &value );
|
|
str_value = g_strdup_value_contents( &value );
|
|
vips_buf_appends( buf, str_value );
|
|
g_free( str_value );
|
|
g_value_unset( &value );
|
|
|
|
}
|
|
|
|
static void *
|
|
vips_object_to_string_required( VipsObject *object,
|
|
GParamSpec *pspec,
|
|
VipsArgumentClass *argument_class,
|
|
VipsArgumentInstance *argument_instance,
|
|
void *a, void *b )
|
|
{
|
|
VipsBuf *buf = (VipsBuf *) a;
|
|
gboolean *first = (gboolean *) b;
|
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) {
|
|
if( *first ) {
|
|
vips_buf_appends( buf, "(" );
|
|
*first = FALSE;
|
|
}
|
|
else {
|
|
vips_buf_appends( buf, "," );
|
|
}
|
|
|
|
vips_object_print_arg( object, pspec, buf );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static void *
|
|
vips_object_to_string_optional( VipsObject *object,
|
|
GParamSpec *pspec,
|
|
VipsArgumentClass *argument_class,
|
|
VipsArgumentInstance *argument_instance,
|
|
void *a, void *b )
|
|
{
|
|
VipsBuf *buf = (VipsBuf *) a;
|
|
gboolean *first = (gboolean *) b;
|
|
|
|
if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
|
argument_instance->assigned ) {
|
|
if( *first ) {
|
|
vips_buf_appends( buf, "(" );
|
|
*first = FALSE;
|
|
}
|
|
else {
|
|
vips_buf_appends( buf, "," );
|
|
}
|
|
|
|
vips_buf_appends( buf, g_param_spec_get_name( pspec ) );
|
|
vips_buf_appends( buf, "=" );
|
|
vips_object_print_arg( object, pspec, buf );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* The inverse of vips_object_new_from_string(): turn an object into eg.
|
|
* "VipsInterpolateYafrsmooth(sharpening=12)".
|
|
*/
|
|
void
|
|
vips_object_to_string( VipsObject *object, VipsBuf *buf )
|
|
{
|
|
gboolean first;
|
|
|
|
/* Nicknames are not guaranteed to be unique, so use the full type
|
|
* name.
|
|
*/
|
|
vips_buf_appends( buf, G_OBJECT_TYPE_NAME( object ) );
|
|
first = TRUE;
|
|
(void) vips_argument_map( object,
|
|
vips_object_to_string_required, buf, &first );
|
|
(void) vips_argument_map( object,
|
|
vips_object_to_string_optional, buf, &first );
|
|
if( !first )
|
|
vips_buf_appends( buf, ")" );
|
|
}
|