merge class-params branch
This commit is contained in:
parent
9ac76a9e58
commit
51d2235b07
@ -20,11 +20,13 @@
|
||||
- interpolators use type introspection
|
||||
- added vips --list classes, does formats too
|
||||
- include sys/param.h to get PATH_MAX in more places
|
||||
- VIPS_INTERPOLATE_SHIFT bumped to 12, we need the extra precision for u16 gel
|
||||
data
|
||||
- added vips_format_get_flags()
|
||||
- oop, forgot to check for cancel during tiled tiff write
|
||||
- don't use mmap for tiff read: no performance advantage, chews up VM
|
||||
- VIPS_INTERPOLATE_SHIFT bumped to 12, we need the extra precision for u16 gel
|
||||
data
|
||||
- added string->double transform for cmdline args
|
||||
- merged class-params branch back into trunk
|
||||
|
||||
11/9/08 started 7.16.3
|
||||
- oop typo in manpage for im_project()
|
||||
|
79
TODO
79
TODO
@ -1,62 +1,15 @@
|
||||
- try making vips_add(), an operator as a class
|
||||
|
||||
- nickname, description etc need to be properties so nip2 can read them
|
||||
|
||||
arg, they are class properties, argh, can we support these?
|
||||
|
||||
- add params
|
||||
|
||||
use property system
|
||||
|
||||
use construtors, see docs for g_object_new(), rather than vips8 build funcs
|
||||
|
||||
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?
|
||||
|
||||
|
||||
|
||||
- started adding
|
||||
|
||||
#define IM_TYPE_GOBJECT "gobject" /* A GObject of a specified class */
|
||||
- make a new package for "resample"? im_shrink & friends could go in there too
|
||||
|
||||
- make a "deprecated" package too
|
||||
|
||||
- im_affinei and im_affinei_all need docs
|
||||
|
||||
- all the interpolators
|
||||
|
||||
want bicubic args to be
|
||||
|
||||
IM_GOBJECT( "VipsInterpolate", "interpolate" );
|
||||
|
||||
ie. pass a gobject which is a sub-type of VipsInterpolate in an arg called
|
||||
"interpolate"
|
||||
|
||||
then we need to create an object of that class, get the param list, set from
|
||||
any args, call object_init, and pass
|
||||
|
||||
on return, unref the object
|
||||
|
||||
so
|
||||
|
||||
im_affinei_all in.v out.v "yafrsmooth(2.4)" 0.9 0 0 0.9 0 0
|
||||
|
||||
creates a yafrsmooth, sets the param,
|
||||
|
||||
|
||||
in nip2,
|
||||
|
||||
inter = g_object_new "VipsInterpolateBicubic" [$sharpness => 2.4];
|
||||
|
||||
also need something to return a list of GTypes below a name etc., find class
|
||||
description:w
|
||||
|
||||
|
||||
need k
|
||||
|
||||
|
||||
|
||||
- make a new package for "resample"? im_shrink & friends could go in there too
|
||||
|
||||
- how to expose things like yafrsmooth's "sharpening" parameter to
|
||||
nip2/C++/Python?
|
||||
can we write a find-by-nickname function? eg.
|
||||
@ -69,6 +22,18 @@
|
||||
|
||||
would get us the GType for VipsInterpolatorBicubic
|
||||
|
||||
add a g_get_children( "classname" ) -> ["child1-name", ..] to nip2, see old
|
||||
vips8.c code?
|
||||
|
||||
in nip2,
|
||||
|
||||
inter = g_object_new "yafrsmooth" [$sharpness => 2.4];
|
||||
|
||||
also need something to return a list of GTypes below a name etc., find class
|
||||
description:w
|
||||
|
||||
need k
|
||||
|
||||
- redo the format system in this way too?
|
||||
|
||||
need a way to init a package: the init would register all the types that
|
||||
@ -87,13 +52,19 @@
|
||||
|
||||
now always included in vips.h
|
||||
|
||||
- build nip2
|
||||
|
||||
- check mosaic1, global_balance, similarity etc. use of im__affine
|
||||
|
||||
how can we move them to im_affinei ?
|
||||
|
||||
- rename Transformation -> Transform
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- update the Portfiles on the mac and on the website from the macports ones
|
||||
|
||||
|
@ -55,7 +55,7 @@ extern "C" {
|
||||
#define IM_TYPE_IMAGE "image" /* IMAGE descriptor */
|
||||
#define IM_TYPE_DISPLAY "display" /* Display descriptor */
|
||||
#define IM_TYPE_GVALUE "gvalue" /* GValue wrapper */
|
||||
#define IM_TYPE_GOBJECT "gobject" /* A GObject of a specified class */
|
||||
#define IM_TYPE_INTERPOLATE "interpolate"/* A subclass of VipsInterpolate */
|
||||
typedef char *im_arg_type; /* Type of argument id */
|
||||
|
||||
/* Internal representation of an argument to an image processing function.
|
||||
@ -210,6 +210,8 @@ extern im_type_desc im__output_display;
|
||||
extern im_type_desc im__input_gvalue;
|
||||
extern im_type_desc im__output_gvalue;
|
||||
|
||||
extern im_type_desc im__input_interpolate;
|
||||
|
||||
/* VIPS print functions.
|
||||
*/
|
||||
int im__iprint( im_object obj ); /* int */
|
||||
@ -255,6 +257,8 @@ int im__gprint( im_object obj ); /* GValue */
|
||||
#define IM_INPUT_GVALUE( S ) { S, &im__input_gvalue, NULL }
|
||||
#define IM_OUTPUT_GVALUE( S ) { S, &im__output_gvalue, im__gprint }
|
||||
|
||||
#define IM_INPUT_INTERPOLATE( S ) { S, &im__input_interpolate, NULL }
|
||||
|
||||
/* Add a plug-in package.
|
||||
*/
|
||||
im_package *im_load_plugin( const char *name );
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Abstract base type for supported image formats. Subclass this to add a new
|
||||
/* Base type for supported image formats. Subclass this to add a new
|
||||
* format.
|
||||
*/
|
||||
|
||||
@ -50,15 +50,14 @@ extern "C" {
|
||||
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
||||
VIPS_TYPE_FORMAT, VipsFormatClass ))
|
||||
|
||||
/* Image file properties. OR these together to get the result of
|
||||
* im_format_flags_fn(). 0 is default.
|
||||
/* Image file properties.
|
||||
*/
|
||||
typedef enum {
|
||||
VIPS_FORMAT_NONE = 0, /* No flags set */
|
||||
VIPS_FORMAT_PARTIAL = 1 /* Lazy read OK (eg. tiled tiff) */
|
||||
} VipsFormatFlags;
|
||||
|
||||
/* Function protos for formats.
|
||||
/* Don't instantiate these things, just use the class stuff.
|
||||
*/
|
||||
|
||||
typedef struct _VipsFormat {
|
||||
|
@ -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 in the constructor.
|
||||
*/
|
||||
VIPS_ARGUMENT_REQUIRED = 1,
|
||||
|
||||
/* Can only be set in the constructor.
|
||||
*/
|
||||
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,17 +170,25 @@ 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 */
|
||||
gboolean constructed; /* Construct done and checked */
|
||||
|
||||
typedef struct _VipsObjectClass {
|
||||
/* Table of argument instances for this class and any derived classes.
|
||||
*/
|
||||
VipsArgumentTable *argument_table;
|
||||
};
|
||||
|
||||
struct _VipsObjectClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* Build the object ... all argument properties have been set,
|
||||
* now build the thing.
|
||||
*/
|
||||
int (*build)( VipsObject *object );
|
||||
|
||||
/* Something about the object has changed. Should use glib's properties
|
||||
* but fix this later.
|
||||
*/
|
||||
@ -78,14 +210,33 @@ 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_set_property( GObject *gobject,
|
||||
guint property_id, const GValue *value, GParamSpec *pspec );
|
||||
void vips_object_get_property( GObject *gobject,
|
||||
guint property_id, GValue *value, GParamSpec *pspec );
|
||||
|
||||
int vips_object_build( VipsObject *object );
|
||||
void *vips_object_changed( VipsObject *object );
|
||||
void vips_object_print_class( VipsObjectClass *klass );
|
||||
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 );
|
||||
|
||||
VipsObject *vips_object_new_from_string( const char *base, const char *str );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
@ -338,6 +338,21 @@ char *im__file_read( FILE *fp, const char *name, unsigned int *length_out );
|
||||
char *im__file_read_name( const char *name, unsigned int *length_out );
|
||||
int im__file_write( void *data, size_t size, size_t nmemb, FILE *stream );
|
||||
|
||||
typedef enum {
|
||||
VIPS_TOKEN_LEFT = 1, /* ({[ */
|
||||
VIPS_TOKEN_RIGHT, /* ]}) */
|
||||
VIPS_TOKEN_STRING, /* string or "str\"ing" */
|
||||
VIPS_TOKEN_EQUALS, /* = */
|
||||
VIPS_TOKEN_COMMA /* , */
|
||||
} VipsToken;
|
||||
|
||||
const char *vips__token_get( const char *buffer,
|
||||
VipsToken *token, char *string, int size );
|
||||
const char *vips__token_must( const char *buffer, VipsToken *token,
|
||||
char *string, int size );
|
||||
const char *vips__token_need( const char *buffer, VipsToken need_token,
|
||||
char *string, int size );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
@ -874,9 +874,31 @@ output_gvalue_init( im_object *obj )
|
||||
|
||||
im_type_desc im__output_gvalue = {
|
||||
IM_TYPE_GVALUE,
|
||||
sizeof( GValue ), /* Need some storage */
|
||||
IM_TYPE_OUTPUT, /* No arg needed (just print) */
|
||||
sizeof( GValue ), /* Need some storage */
|
||||
IM_TYPE_OUTPUT, /* No arg needed (just print) */
|
||||
(im_init_obj_fn) output_gvalue_init, /* Init function */
|
||||
(im_dest_obj_fn) gvalue_free /* Destroy function */
|
||||
};
|
||||
|
||||
/* Init function for input interpolate.
|
||||
*/
|
||||
static int
|
||||
input_interpolate_init( im_object *obj, char *str )
|
||||
{
|
||||
VipsObject *object;
|
||||
|
||||
if( !(object = vips_object_new_from_string( "VipsInterpolate", str )) )
|
||||
return( -1 );
|
||||
*obj = object;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
im_type_desc im__input_interpolate = {
|
||||
IM_TYPE_INTERPOLATE,
|
||||
0, /* No storage required */
|
||||
IM_TYPE_ARG, /* It requires a command-line arg */
|
||||
(im_init_obj_fn) input_interpolate_init,/* Init function */
|
||||
(im_dest_obj_fn) g_object_unref /* Destroy function */
|
||||
};
|
||||
|
||||
|
@ -76,6 +76,19 @@ vips_object_changed( VipsObject *object )
|
||||
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 )
|
||||
{
|
||||
@ -100,16 +113,244 @@ 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 );
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
#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,17 +364,315 @@ 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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, 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.
|
||||
*/
|
||||
if( argument_class->flags & VIPS_ARGUMENT_CONSTRUCT &&
|
||||
object->constructed ) {
|
||||
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_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' has unimplemented type", G_STRLOC,
|
||||
((VipsArgument *) argument_class)->pspec->name );
|
||||
}
|
||||
|
||||
/* 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_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_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" ),
|
||||
pspec->name,
|
||||
G_OBJECT_TYPE_NAME( object ),
|
||||
object->name );
|
||||
*result = -1;
|
||||
}
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_object_real_build( VipsObject *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;
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_object_real_changed( VipsObject *object )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
VipsObject *object = VIPS_OBJECT( gobject );
|
||||
|
||||
printf( "vips_object_real_changed: " );
|
||||
vips_object_print( object );
|
||||
#endif /*DEBUG*/
|
||||
@ -156,6 +695,13 @@ vips_object_real_print( VipsObject *object, im_buf_t *buf )
|
||||
im_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 *class )
|
||||
{
|
||||
@ -163,13 +709,22 @@ 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->build = vips_object_real_build;
|
||||
class->changed = vips_object_real_changed;
|
||||
class->print_class = vips_object_real_print_class;
|
||||
class->print = vips_object_real_print;
|
||||
class->nickname = "object";
|
||||
class->description = _( "VIPS base class" );
|
||||
|
||||
/* Table of VipsArgumentClass ... we can just g_free() them.
|
||||
*/
|
||||
class->argument_table = g_hash_table_new_full(
|
||||
g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_free );
|
||||
class->argument_table_traverse = NULL;
|
||||
|
||||
vips_object_signals[SIG_CHANGED] = g_signal_new( "changed",
|
||||
G_OBJECT_CLASS_TYPE( gobject_class ),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
@ -177,6 +732,11 @@ vips_object_class_init( VipsObjectClass *class )
|
||||
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 );
|
||||
}
|
||||
|
||||
static void
|
||||
@ -195,3 +755,172 @@ 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 );
|
||||
|
||||
#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 arguents" ) );
|
||||
g_object_unref( object );
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
if( vips_object_build( object ) ) {
|
||||
g_object_unref( object );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( object );
|
||||
}
|
||||
|
||||
|
@ -362,7 +362,7 @@ vips_type_find( const char *basename, const char *nickname )
|
||||
/* FIXME ... we've not supposed to use G_TYPE_FROM_CLASS(), I think.
|
||||
* I'm not * sure what the alternative is.
|
||||
*/
|
||||
return( G_TYPE_FROM_CLASS( class ) );
|
||||
return( G_TYPE_FROM_CLASS( class ) );
|
||||
}
|
||||
|
||||
/* Like strncpy(), but always NULL-terminate, and don't pad with NULLs.
|
||||
@ -1159,13 +1159,19 @@ im__file_write( void *data, size_t size, size_t nmemb, FILE *stream )
|
||||
gboolean
|
||||
im_isnative( im_arch_type arch )
|
||||
{
|
||||
switch ( arch ) {
|
||||
case IM_ARCH_NATIVE: return( TRUE );
|
||||
case IM_ARCH_BYTE_SWAPPED: return( FALSE );
|
||||
case IM_ARCH_LSB_FIRST: return( !im_amiMSBfirst() );
|
||||
case IM_ARCH_MSB_FIRST: return( im_amiMSBfirst() );
|
||||
}
|
||||
abort();
|
||||
switch( arch ) {
|
||||
case IM_ARCH_NATIVE:
|
||||
return( TRUE );
|
||||
case IM_ARCH_BYTE_SWAPPED:
|
||||
return( FALSE );
|
||||
case IM_ARCH_LSB_FIRST:
|
||||
return( !im_amiMSBfirst() );
|
||||
case IM_ARCH_MSB_FIRST:
|
||||
return( im_amiMSBfirst() );
|
||||
|
||||
default:
|
||||
g_assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a few bytes from the start of a file. For sniffing file types.
|
||||
@ -1193,3 +1199,164 @@ im__get_bytes( const char *filename, unsigned char buf[], int len )
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
/* Break a command-line argument into tokens separated by whitespace. Strings
|
||||
* can't be adjacent, so "hello world" (without quotes) is a single string.
|
||||
* Strings are written (with \" escaped) into string, which must be size
|
||||
* characters large.
|
||||
*/
|
||||
const char *
|
||||
vips__token_get( const char *p, VipsToken *token, char *string, int size )
|
||||
{
|
||||
const char *q;
|
||||
int ch;
|
||||
int n;
|
||||
|
||||
/* Parse this token with p.
|
||||
*/
|
||||
if( !p )
|
||||
return( NULL );
|
||||
|
||||
/* Skip initial whitespace.
|
||||
*/
|
||||
p += strspn( p, " \t\n\r" );
|
||||
if( !p[0] )
|
||||
return( NULL );
|
||||
|
||||
switch( (ch = p[0]) ) {
|
||||
case '{':
|
||||
case '[':
|
||||
case '(':
|
||||
*token = VIPS_TOKEN_LEFT;
|
||||
p += 1;
|
||||
break;
|
||||
|
||||
case ')':
|
||||
case ']':
|
||||
case '}':
|
||||
*token = VIPS_TOKEN_RIGHT;
|
||||
p += 1;
|
||||
break;
|
||||
|
||||
case '=':
|
||||
*token = VIPS_TOKEN_EQUALS;
|
||||
p += 1;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
*token = VIPS_TOKEN_COMMA;
|
||||
p += 1;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
/* Parse a quoted string. Copy up to ", interpret any \",
|
||||
* error if no closing ".
|
||||
*/
|
||||
*token = VIPS_TOKEN_STRING;
|
||||
|
||||
do {
|
||||
/* Number of characters until the next quote
|
||||
* character or end of string.
|
||||
*/
|
||||
if( (q = strchr( p + 1, ch )) )
|
||||
n = q - p + 1;
|
||||
else
|
||||
n = strlen( p + 1 );
|
||||
|
||||
g_assert( size > n + 1 );
|
||||
memcpy( string, p + 1, n );
|
||||
string[n] = '\0';
|
||||
|
||||
/* p[n + 1] might not be " if there's no closing ".
|
||||
*/
|
||||
if( p[n + 1] == ch && p[n] == '\\' )
|
||||
/* An escaped ": overwrite the '\' with '"'
|
||||
*/
|
||||
string[n - 1] = ch;
|
||||
|
||||
string += n;
|
||||
size -= n;
|
||||
p += n + 1;
|
||||
} while( p[0] && p[-1] == '\\' );
|
||||
|
||||
p += 1;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* It's an unquoted string: read up to the next non-string
|
||||
* character. We don't allow two strings next to each other,
|
||||
* so the next break must be bracket, equals, comma.
|
||||
*/
|
||||
*token = VIPS_TOKEN_STRING;
|
||||
n = strcspn( p, "[{()}]=," );
|
||||
g_assert( size > n + 1 );
|
||||
memcpy( string, p, n );
|
||||
string[n] = '\0';
|
||||
p += n;
|
||||
|
||||
/* We remove leading whitespace, so we trim trailing
|
||||
* whitespace from unquoted strings too.
|
||||
*/
|
||||
while( isspace( string[n - 1] ) ) {
|
||||
string[n - 1] = '\0';
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return( p );
|
||||
}
|
||||
|
||||
/* We expect a token.
|
||||
*/
|
||||
const char *
|
||||
vips__token_must( const char *p, VipsToken *token,
|
||||
char *string, int size )
|
||||
{
|
||||
if( !(p = vips__token_get( p, token, string, size )) ) {
|
||||
im_error( "get_token", "%s", _( "unexpected end of string" ) );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( p );
|
||||
}
|
||||
|
||||
/* Turn a VipsToken into a string.
|
||||
*/
|
||||
static const char *
|
||||
vips__token_string( VipsToken token )
|
||||
{
|
||||
switch( token ) {
|
||||
case VIPS_TOKEN_LEFT: return( _( "opening brace" ) );
|
||||
case VIPS_TOKEN_RIGHT: return( _( "closing brace" ) );
|
||||
case VIPS_TOKEN_STRING: return( _( "string" ) );
|
||||
case VIPS_TOKEN_EQUALS: return( "=" );
|
||||
case VIPS_TOKEN_COMMA: return( "," );
|
||||
|
||||
default:
|
||||
g_assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/* We expect a certain token.
|
||||
*/
|
||||
const char *
|
||||
vips__token_need( const char *p, VipsToken need_token,
|
||||
char *string, int size )
|
||||
{
|
||||
VipsToken token;
|
||||
|
||||
if( !(p = vips__token_must( p, &token, string, size )) )
|
||||
return( NULL );
|
||||
if( token != need_token ) {
|
||||
im_error( "get_token", _( "expected %s, saw %s" ),
|
||||
vips__token_string( need_token ),
|
||||
vips__token_string( token ) );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( p );
|
||||
}
|
||||
|
@ -465,5 +465,6 @@ vips_interpolate_new( const char *nickname )
|
||||
if( !(type = vips_type_find( "VipsInterpolate", nickname )) )
|
||||
return( NULL );
|
||||
|
||||
return( VIPS_INTERPOLATE( g_object_new( type, NULL ) ) );
|
||||
return( VIPS_INTERPOLATE( g_object_new( type,
|
||||
"sharpening", 2.0, NULL ) ) );
|
||||
}
|
||||
|
@ -579,7 +579,7 @@ static im_function affinei_desc = {
|
||||
static im_arg_desc affinei_all_args[] = {
|
||||
IM_INPUT_IMAGE( "in" ),
|
||||
IM_OUTPUT_IMAGE( "out" ),
|
||||
IM_INPUT_STRING( "interpolate" ),
|
||||
IM_INPUT_INTERPOLATE( "interpolate" ),
|
||||
IM_INPUT_DOUBLE( "a" ),
|
||||
IM_INPUT_DOUBLE( "b" ),
|
||||
IM_INPUT_DOUBLE( "c" ),
|
||||
@ -593,23 +593,16 @@ static im_arg_desc affinei_all_args[] = {
|
||||
static int
|
||||
affinei_all_vec( im_object *argv )
|
||||
{
|
||||
const char *interpol = argv[2];
|
||||
VipsInterpolate *interpolate = VIPS_INTERPOLATE( argv[2] );
|
||||
double a = *((double *) argv[3]);
|
||||
double b = *((double *) argv[4]);
|
||||
double c = *((double *) argv[5]);
|
||||
double d = *((double *) argv[6]);
|
||||
double dx = *((double *) argv[7]);
|
||||
double dy = *((double *) argv[8]);
|
||||
VipsInterpolate *interpolate;
|
||||
int result;
|
||||
|
||||
if( !(interpolate = vips_interpolate_new( interpol )) )
|
||||
return( -1 );
|
||||
result = im_affinei_all( argv[0], argv[1], interpolate,
|
||||
a, b, c, d, dx, dy );
|
||||
g_object_unref( interpolate );
|
||||
|
||||
return( result );
|
||||
return( im_affinei_all( argv[0], argv[1], interpolate,
|
||||
a, b, c, d, dx, dy ) );
|
||||
}
|
||||
|
||||
/* Description of im_affinei.
|
||||
|
@ -740,10 +740,14 @@ 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 );
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "yafrsmooth";
|
||||
object_class->description = _( "YAFR smooth interpolation" );
|
||||
|
||||
|
@ -56,6 +56,10 @@
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG_FATAL
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
@ -861,6 +865,18 @@ main( int argc, char **argv )
|
||||
|
||||
if( im_init_world( argv[0] ) )
|
||||
error_exit( "unable to start VIPS" );
|
||||
#ifdef DEBUG_FATAL
|
||||
/* Set masks for debugging ... stop on any problem.
|
||||
*/
|
||||
g_log_set_always_fatal(
|
||||
G_LOG_FLAG_RECURSION |
|
||||
G_LOG_FLAG_FATAL |
|
||||
G_LOG_LEVEL_ERROR |
|
||||
G_LOG_LEVEL_CRITICAL |
|
||||
G_LOG_LEVEL_WARNING );
|
||||
|
||||
fprintf( stderr, "*** DEBUG_FATAL: will abort() on first warning\n" );
|
||||
#endif /*!DEBUG_FATAL*/
|
||||
|
||||
context = g_option_context_new( _( "- VIPS driver program" ) );
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user