2011-03-29 13:12:20 +02:00
|
|
|
/* base class for all vips operations
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (C) 1991-2005 The National Gallery
|
|
|
|
|
2012-09-17 12:52:32 +02:00
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
2011-03-29 13:12:20 +02:00
|
|
|
|
2012-09-17 12:52:32 +02:00
|
|
|
This library is distributed in the hope that it will be useful,
|
2011-03-29 13:12:20 +02:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2012-09-17 12:52:32 +02:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
2011-03-29 13:12:20 +02:00
|
|
|
|
2012-09-17 12:52:32 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; if not, write to the Free Software
|
2013-03-07 06:40:19 +01:00
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
02110-1301 USA
|
2011-03-29 13:12:20 +02:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2011-12-11 23:01:39 +01:00
|
|
|
#define VIPS_DEBUG
|
2011-12-12 12:34:28 +01:00
|
|
|
*/
|
2011-03-29 13:12:20 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif /*HAVE_CONFIG_H*/
|
|
|
|
#include <vips/intl.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include <vips/vips.h>
|
2011-04-04 16:46:57 +02:00
|
|
|
#include <vips/debug.h>
|
2011-03-29 13:12:20 +02:00
|
|
|
|
|
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
|
2013-06-17 15:54:11 +02:00
|
|
|
/**
|
|
|
|
* SECTION: operation
|
|
|
|
* @short_description: the VIPS operation base object class
|
|
|
|
* @stability: Stable
|
|
|
|
* @see_also: <link linkend="libvips-object">object</link>
|
|
|
|
* @include: vips/vips.h
|
|
|
|
*
|
|
|
|
* The #VipsOperation class and associated types and macros.
|
|
|
|
*
|
|
|
|
* #VipsOperation is the base class for all operations in libvips. It builds
|
|
|
|
* on #VipsObject to provide the introspection and command-line interface to
|
|
|
|
* libvips.
|
|
|
|
*
|
|
|
|
* It also maintains a cache of recent operations. You can tune the cache
|
|
|
|
* behaviour in various ways.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2013-09-29 12:04:11 +02:00
|
|
|
/**
|
|
|
|
* VipsOperationFlags:
|
|
|
|
* @VIPS_OPERATION_NONE: no flags
|
|
|
|
* @VIPS_OPERATION_SEQUENTIAL: can work sequentially
|
|
|
|
* @VIPS_OPERATION_NOCACHE: must not be cached
|
2014-02-03 14:23:58 +01:00
|
|
|
* @VIPS_OPERATION_DEPRECATED: a compatibility thing
|
2013-09-29 12:04:11 +02:00
|
|
|
*
|
|
|
|
* Flags we associate with an operation.
|
|
|
|
*
|
|
|
|
* @VIPS_OPERATION_SEQUENTIAL means that the operation works like vips_conv():
|
|
|
|
* it can happily process images top-to-bottom with only small non-local
|
|
|
|
* references.
|
|
|
|
*
|
|
|
|
* @VIPS_OPERATION_SEQUENTIAL_UNBUFFERED means that the operation works like
|
|
|
|
* vips_copy(): it can happily process images top-to-bottom and makes no
|
|
|
|
* non-local references.
|
|
|
|
*
|
|
|
|
* @VIPS_OPERATION_NOCACHE means that the operation must not be cached by
|
|
|
|
* vips.
|
2014-02-03 14:23:58 +01:00
|
|
|
*
|
|
|
|
* @VIPS_OPERATION_DEPRECATED means this is an old operation kept in vips for
|
|
|
|
* compatibility only and should be hidden from users.
|
2013-09-29 12:04:11 +02:00
|
|
|
*/
|
|
|
|
|
2011-03-29 13:12:20 +02:00
|
|
|
/* Abstract base class for operations.
|
|
|
|
*/
|
|
|
|
|
|
|
|
G_DEFINE_ABSTRACT_TYPE( VipsOperation, vips_operation, VIPS_TYPE_OBJECT );
|
|
|
|
|
2011-07-18 18:10:41 +02:00
|
|
|
static void
|
|
|
|
vips_operation_finalize( GObject *gobject )
|
|
|
|
{
|
2013-11-18 19:31:49 +01:00
|
|
|
VipsOperation *operation = VIPS_OPERATION( gobject );
|
|
|
|
|
2011-07-18 18:10:41 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_operation_finalize: %p\n", gobject );
|
|
|
|
|
2013-11-18 19:31:49 +01:00
|
|
|
if( operation->pixels ) {
|
|
|
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gobject );
|
|
|
|
|
|
|
|
vips_info( class->nickname,
|
|
|
|
_( "%d pixels calculated" ), operation->pixels );
|
|
|
|
}
|
|
|
|
|
2011-07-18 18:10:41 +02:00
|
|
|
G_OBJECT_CLASS( vips_operation_parent_class )->finalize( gobject );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vips_operation_dispose( GObject *gobject )
|
|
|
|
{
|
|
|
|
VIPS_DEBUG_MSG( "vips_operation_dispose: %p\n", gobject );
|
|
|
|
|
|
|
|
G_OBJECT_CLASS( vips_operation_parent_class )->dispose( gobject );
|
|
|
|
}
|
|
|
|
|
2014-02-20 20:52:36 +01:00
|
|
|
/* Three basic types of command-line argument.
|
|
|
|
*
|
|
|
|
* INPUTS: things like an input image, there is a filename argument on the
|
|
|
|
* command-line which is used to construct the operation argument.
|
|
|
|
*
|
|
|
|
* NOARG_OUTPUT: things like the result of VipsMax, there's no correspondiong
|
|
|
|
* command-line argument, we just print the value.
|
|
|
|
*
|
|
|
|
* OPTIONS: optional arguments.
|
|
|
|
*
|
|
|
|
* NONE: hide this thing.
|
2011-03-29 13:12:20 +02:00
|
|
|
*/
|
2014-02-20 20:52:36 +01:00
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
USAGE_INPUTS,
|
|
|
|
USAGE_NOARG_OUTPUT,
|
|
|
|
USAGE_OPTIONS,
|
|
|
|
USAGE_NONE
|
|
|
|
} UsageType;
|
|
|
|
|
2011-03-29 13:12:20 +02:00
|
|
|
typedef struct {
|
2011-05-13 11:08:53 +02:00
|
|
|
char *message; /* header message on first print */
|
2014-02-20 20:52:36 +01:00
|
|
|
UsageType type; /* Type of arg to select */
|
|
|
|
gboolean oftype; /* Show as "of type" */
|
2011-03-29 13:12:20 +02:00
|
|
|
int n; /* Arg number */
|
2012-01-16 15:54:29 +01:00
|
|
|
} VipsOperationClassUsage;
|
2011-03-29 13:12:20 +02:00
|
|
|
|
2014-02-20 20:52:36 +01:00
|
|
|
/* Put an arg into one the categories above.
|
|
|
|
*/
|
|
|
|
static UsageType
|
|
|
|
vips_operation_class_usage_classify( VipsArgumentClass *argument_class )
|
|
|
|
{
|
|
|
|
if( !(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) ||
|
|
|
|
(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) )
|
|
|
|
return( USAGE_NONE );
|
|
|
|
|
|
|
|
if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) )
|
|
|
|
return( USAGE_OPTIONS );
|
|
|
|
|
|
|
|
if( vips_argument_class_needsstring( argument_class ) )
|
|
|
|
return( USAGE_INPUTS );
|
|
|
|
|
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_OUTPUT) &&
|
|
|
|
!vips_argument_class_needsstring( argument_class ) )
|
|
|
|
return( USAGE_NOARG_OUTPUT );
|
|
|
|
|
|
|
|
return( USAGE_NONE );
|
|
|
|
}
|
|
|
|
|
2011-03-29 13:12:20 +02:00
|
|
|
static void *
|
2012-01-16 15:54:29 +01:00
|
|
|
vips_operation_class_usage_arg( VipsObjectClass *object_class,
|
2011-12-02 14:15:05 +01:00
|
|
|
GParamSpec *pspec, VipsArgumentClass *argument_class,
|
2012-01-16 15:54:29 +01:00
|
|
|
VipsBuf *buf, VipsOperationClassUsage *usage )
|
2011-03-29 13:12:20 +02:00
|
|
|
{
|
2014-02-20 20:52:36 +01:00
|
|
|
if( usage->type ==
|
|
|
|
vips_operation_class_usage_classify( argument_class ) ) {
|
|
|
|
if( usage->message &&
|
|
|
|
usage->n == 0 )
|
2012-01-16 15:54:29 +01:00
|
|
|
vips_buf_appendf( buf, "%s\n", usage->message );
|
2011-05-13 11:08:53 +02:00
|
|
|
|
2014-02-20 20:52:36 +01:00
|
|
|
if( usage->oftype )
|
2011-12-03 11:59:25 +01:00
|
|
|
vips_buf_appendf( buf, " %-12s - %s, %s %s\n",
|
2011-05-16 18:10:08 +02:00
|
|
|
g_param_spec_get_name( pspec ),
|
2011-12-02 14:30:37 +01:00
|
|
|
g_param_spec_get_blurb( pspec ),
|
2011-07-22 18:28:08 +02:00
|
|
|
(argument_class->flags & VIPS_ARGUMENT_INPUT) ?
|
2011-12-03 11:59:25 +01:00
|
|
|
_( "input" ) : _( "output" ),
|
|
|
|
g_type_name(
|
|
|
|
G_PARAM_SPEC_VALUE_TYPE( pspec ) ) );
|
2011-03-29 13:12:20 +02:00
|
|
|
else {
|
2014-02-20 20:52:36 +01:00
|
|
|
if( usage->n > 0 )
|
|
|
|
vips_buf_appends( buf, " " );
|
|
|
|
vips_buf_appends( buf,
|
|
|
|
g_param_spec_get_name( pspec ) );
|
2011-03-29 13:12:20 +02:00
|
|
|
}
|
|
|
|
|
2012-01-16 15:54:29 +01:00
|
|
|
usage->n += 1;
|
2011-03-29 13:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
2011-12-02 14:15:05 +01:00
|
|
|
static void
|
2012-01-16 15:54:29 +01:00
|
|
|
vips_operation_usage( VipsOperationClass *class, VipsBuf *buf )
|
2011-12-02 14:15:05 +01:00
|
|
|
{
|
|
|
|
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
|
|
|
|
|
2012-01-16 15:54:29 +01:00
|
|
|
VipsOperationClassUsage usage;
|
2011-12-02 14:15:05 +01:00
|
|
|
|
2014-02-20 20:52:36 +01:00
|
|
|
vips_buf_appendf( buf, "%s\n", object_class->description );
|
|
|
|
vips_buf_appendf( buf, "usage:\n" );
|
|
|
|
|
2011-12-02 14:15:05 +01:00
|
|
|
/* First pass through args: show the required names.
|
|
|
|
*/
|
|
|
|
vips_buf_appendf( buf, " %s ", object_class->nickname );
|
2012-01-16 15:54:29 +01:00
|
|
|
usage.message = NULL;
|
2014-02-20 20:52:36 +01:00
|
|
|
usage.type = USAGE_INPUTS;
|
2012-01-16 15:54:29 +01:00
|
|
|
usage.oftype = FALSE;
|
|
|
|
usage.n = 0;
|
2011-12-02 14:15:05 +01:00
|
|
|
vips_argument_class_map( object_class,
|
2012-01-16 15:54:29 +01:00
|
|
|
(VipsArgumentClassMapFn) vips_operation_class_usage_arg,
|
|
|
|
buf, &usage );
|
2011-12-02 14:15:05 +01:00
|
|
|
vips_buf_appends( buf, "\n" );
|
|
|
|
|
|
|
|
/* Show required types.
|
|
|
|
*/
|
2012-01-16 15:54:29 +01:00
|
|
|
usage.message = "where:";
|
2014-02-20 20:52:36 +01:00
|
|
|
usage.type = USAGE_INPUTS;
|
|
|
|
usage.oftype = TRUE;
|
|
|
|
usage.n = 0;
|
|
|
|
vips_argument_class_map( object_class,
|
|
|
|
(VipsArgumentClassMapFn) vips_operation_class_usage_arg,
|
|
|
|
buf, &usage );
|
|
|
|
|
|
|
|
/* Show outputs with no input arg (eg. output maximum value for
|
|
|
|
* vips_max()).
|
|
|
|
*/
|
|
|
|
usage.message = "outputs:";
|
|
|
|
usage.type = USAGE_NOARG_OUTPUT;
|
2012-01-16 15:54:29 +01:00
|
|
|
usage.oftype = TRUE;
|
|
|
|
usage.n = 0;
|
2011-12-02 14:15:05 +01:00
|
|
|
vips_argument_class_map( object_class,
|
2012-01-16 15:54:29 +01:00
|
|
|
(VipsArgumentClassMapFn) vips_operation_class_usage_arg,
|
|
|
|
buf, &usage );
|
2011-12-02 14:15:05 +01:00
|
|
|
|
|
|
|
/* Show optional args.
|
|
|
|
*/
|
2012-01-16 15:54:29 +01:00
|
|
|
usage.message = "optional arguments:";
|
2014-02-20 20:52:36 +01:00
|
|
|
usage.type = USAGE_OPTIONS;
|
2012-01-16 15:54:29 +01:00
|
|
|
usage.oftype = TRUE;
|
|
|
|
usage.n = 0;
|
2011-12-02 14:15:05 +01:00
|
|
|
vips_argument_class_map( object_class,
|
2012-01-16 15:54:29 +01:00
|
|
|
(VipsArgumentClassMapFn) vips_operation_class_usage_arg,
|
|
|
|
buf, &usage );
|
2012-07-10 11:51:40 +02:00
|
|
|
|
|
|
|
/* Show flags.
|
|
|
|
*/
|
|
|
|
if( class->flags ) {
|
|
|
|
GFlagsValue *value;
|
|
|
|
VipsOperationFlags flags;
|
|
|
|
GFlagsClass *flags_class =
|
|
|
|
g_type_class_ref( VIPS_TYPE_OPERATION_FLAGS );
|
|
|
|
|
|
|
|
vips_buf_appendf( buf, "operation flags: " );
|
|
|
|
flags = class->flags;
|
|
|
|
while( flags && (value =
|
|
|
|
g_flags_get_first_value( flags_class, flags )) ) {
|
|
|
|
vips_buf_appendf( buf, "%s ", value->value_nick );
|
|
|
|
flags &= ~value->value;
|
|
|
|
}
|
|
|
|
vips_buf_appends( buf, "\n" );
|
|
|
|
}
|
2011-12-02 14:15:05 +01:00
|
|
|
}
|
|
|
|
|
2011-04-04 16:46:57 +02:00
|
|
|
static void *
|
|
|
|
vips_operation_call_argument( VipsObject *object, GParamSpec *pspec,
|
|
|
|
VipsArgumentClass *argument_class,
|
2012-01-04 14:50:10 +01:00
|
|
|
VipsArgumentInstance *argument_instance,
|
|
|
|
void *a, void *b )
|
2011-04-04 16:46:57 +02:00
|
|
|
{
|
|
|
|
VipsArgument *argument = (VipsArgument *) argument_class;
|
|
|
|
|
|
|
|
printf( " %s: offset = %d ",
|
2011-05-16 18:10:08 +02:00
|
|
|
g_param_spec_get_name( argument->pspec ),
|
|
|
|
argument_class->offset );
|
2011-04-04 16:46:57 +02:00
|
|
|
if( argument_class->flags & VIPS_ARGUMENT_REQUIRED )
|
|
|
|
printf ("required " );
|
|
|
|
if( argument_class->flags & VIPS_ARGUMENT_CONSTRUCT )
|
|
|
|
printf ("construct " );
|
|
|
|
if( argument_class->flags & VIPS_ARGUMENT_SET_ONCE )
|
|
|
|
printf ("set-once " );
|
|
|
|
if( argument_instance->assigned )
|
|
|
|
printf ("assigned " );
|
|
|
|
printf( "\n" );
|
|
|
|
|
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
2011-03-29 13:12:20 +02:00
|
|
|
static void
|
2012-01-16 15:54:29 +01:00
|
|
|
vips_operation_dump( VipsObject *object, VipsBuf *buf )
|
2011-03-29 13:12:20 +02:00
|
|
|
{
|
|
|
|
VipsOperation *operation = VIPS_OPERATION( object );
|
|
|
|
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
|
|
|
|
|
2011-04-04 16:46:57 +02:00
|
|
|
printf( "%s args:\n", object_class->nickname );
|
|
|
|
vips_argument_map( VIPS_OBJECT( operation ),
|
2012-01-04 14:50:10 +01:00
|
|
|
vips_operation_call_argument, NULL, NULL );
|
2011-04-04 16:46:57 +02:00
|
|
|
|
2012-01-16 15:54:29 +01:00
|
|
|
VIPS_OBJECT_CLASS( vips_operation_parent_class )->dump( object, buf );
|
2011-03-29 13:12:20 +02:00
|
|
|
}
|
|
|
|
|
2012-01-04 14:50:10 +01:00
|
|
|
static void *
|
|
|
|
vips_operation_vips_operation_print_summary_arg( VipsObject *object,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
VipsArgumentClass *argument_class,
|
|
|
|
VipsArgumentInstance *argument_instance,
|
|
|
|
void *a, void *b )
|
|
|
|
{
|
|
|
|
VipsBuf *buf = (VipsBuf *) a;
|
|
|
|
|
|
|
|
/* Just assigned required input construct args
|
|
|
|
*/
|
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
|
|
|
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
|
|
|
argument_instance->assigned ) {
|
|
|
|
const char *name = g_param_spec_get_name( pspec );
|
|
|
|
GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
|
|
|
|
|
|
|
|
GValue gvalue = { 0, };
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
g_value_init( &gvalue, type );
|
|
|
|
g_object_get_property( G_OBJECT( object ), name, &gvalue );
|
|
|
|
str = g_strdup_value_contents( &gvalue );
|
2012-10-29 18:19:15 +01:00
|
|
|
vips_buf_appendf( buf, " %s=%s", name, str );
|
2012-01-04 14:50:10 +01:00
|
|
|
g_free( str );
|
|
|
|
g_value_unset( &gvalue );
|
|
|
|
}
|
|
|
|
|
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-01-16 15:54:29 +01:00
|
|
|
vips_operation_summary( VipsObject *object, VipsBuf *buf )
|
2012-01-04 14:50:10 +01:00
|
|
|
{
|
|
|
|
VipsOperation *operation = VIPS_OPERATION( object );
|
|
|
|
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
|
|
|
|
|
2012-10-29 18:19:15 +01:00
|
|
|
vips_buf_appendf( buf, "%s", object_class->nickname );
|
2012-01-04 14:50:10 +01:00
|
|
|
vips_argument_map( VIPS_OBJECT( operation ),
|
|
|
|
vips_operation_vips_operation_print_summary_arg, buf, NULL );
|
|
|
|
|
2012-10-29 18:19:15 +01:00
|
|
|
vips_buf_appends( buf, " -" );
|
|
|
|
|
2012-01-04 14:50:10 +01:00
|
|
|
VIPS_OBJECT_CLASS( vips_operation_parent_class )->
|
2012-01-16 15:54:29 +01:00
|
|
|
summary( object, buf );
|
2012-01-04 14:50:10 +01:00
|
|
|
}
|
|
|
|
|
2012-07-10 11:51:40 +02:00
|
|
|
static VipsOperationFlags
|
|
|
|
vips_operation_real_get_flags( VipsOperation *operation )
|
|
|
|
{
|
|
|
|
VipsOperationClass *class = VIPS_OPERATION_GET_CLASS( operation );
|
|
|
|
|
|
|
|
return( class->flags );
|
|
|
|
}
|
|
|
|
|
2011-03-29 13:12:20 +02:00
|
|
|
static void
|
|
|
|
vips_operation_class_init( VipsOperationClass *class )
|
|
|
|
{
|
2011-07-18 18:10:41 +02:00
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
2011-03-29 13:12:20 +02:00
|
|
|
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
|
|
|
|
2011-07-18 18:10:41 +02:00
|
|
|
gobject_class->finalize = vips_operation_finalize;
|
|
|
|
gobject_class->dispose = vips_operation_dispose;
|
|
|
|
|
2011-07-16 12:26:24 +02:00
|
|
|
vobject_class->nickname = "operation";
|
2011-12-02 14:15:05 +01:00
|
|
|
vobject_class->description = _( "operations" );
|
2012-01-16 15:54:29 +01:00
|
|
|
vobject_class->summary = vips_operation_summary;
|
|
|
|
vobject_class->dump = vips_operation_dump;
|
2011-12-02 14:15:05 +01:00
|
|
|
|
2012-01-16 15:54:29 +01:00
|
|
|
class->usage = vips_operation_usage;
|
2012-07-10 11:51:40 +02:00
|
|
|
class->get_flags = vips_operation_real_get_flags;
|
2011-03-29 13:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vips_operation_init( VipsOperation *operation )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-07-10 11:51:40 +02:00
|
|
|
/**
|
|
|
|
* vips_operation_get_flags:
|
|
|
|
* @operation: operation to fetch flags from
|
|
|
|
*
|
|
|
|
* Returns the set of flags for this operation.
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, or -1 on error.
|
|
|
|
*/
|
|
|
|
VipsOperationFlags
|
|
|
|
vips_operation_get_flags( VipsOperation *operation )
|
|
|
|
{
|
|
|
|
VipsOperationClass *class = VIPS_OPERATION_GET_CLASS( operation );
|
|
|
|
|
|
|
|
return( class->get_flags( operation ) );
|
|
|
|
}
|
|
|
|
|
2012-01-01 13:04:46 +01:00
|
|
|
/**
|
|
|
|
* vips_operation_class_print_usage: (skip)
|
|
|
|
* @operation_class: class to print usage for
|
|
|
|
*
|
|
|
|
* Print a usage message for the operation to stdout.
|
|
|
|
*/
|
2011-12-02 14:15:05 +01:00
|
|
|
void
|
|
|
|
vips_operation_class_print_usage( VipsOperationClass *operation_class )
|
|
|
|
{
|
|
|
|
char str[2048];
|
|
|
|
VipsBuf buf = VIPS_BUF_STATIC( str );
|
|
|
|
|
2012-01-16 15:54:29 +01:00
|
|
|
operation_class->usage( operation_class, &buf );
|
2014-02-20 20:52:36 +01:00
|
|
|
printf( "%s", vips_buf_all( &buf ) );
|
2011-12-02 14:15:05 +01:00
|
|
|
}
|
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
VipsOperation *
|
|
|
|
vips_operation_new( const char *name )
|
|
|
|
{
|
|
|
|
GType type;
|
2011-05-22 18:32:57 +02:00
|
|
|
VipsOperation *operation;
|
2011-05-19 14:48:09 +02:00
|
|
|
|
2011-09-12 13:28:01 +02:00
|
|
|
vips_check_init();
|
|
|
|
|
2011-12-13 14:19:20 +01:00
|
|
|
if( !(type = vips_type_find( "VipsOperation", name )) ) {
|
|
|
|
vips_error( "VipsOperation",
|
|
|
|
_( "class \"%s\" not found" ), name );
|
2011-05-19 14:48:09 +02:00
|
|
|
return( NULL );
|
2011-12-13 14:19:20 +01:00
|
|
|
}
|
2013-11-29 10:40:54 +01:00
|
|
|
|
2011-05-22 18:32:57 +02:00
|
|
|
operation = VIPS_OPERATION( g_object_new( type, NULL ) );
|
2011-05-19 14:48:09 +02:00
|
|
|
|
2011-07-18 18:10:41 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_operation_new: %s (%p)\n", name, operation );
|
|
|
|
|
2011-05-22 18:32:57 +02:00
|
|
|
return( operation );
|
|
|
|
}
|
2011-03-29 13:12:20 +02:00
|
|
|
|
2011-09-05 19:16:32 +02:00
|
|
|
/* Some systems do not have va_copy() ... this might work (it does on MSVC),
|
|
|
|
* apparently.
|
|
|
|
*
|
|
|
|
* FIXME ... this should be in configure.in
|
|
|
|
*/
|
|
|
|
#ifndef va_copy
|
|
|
|
#define va_copy(d,s) ((d) = (s))
|
|
|
|
#endif
|
|
|
|
|
2011-04-05 18:02:12 +02:00
|
|
|
static int
|
2011-05-22 18:32:57 +02:00
|
|
|
vips_operation_set_valist_required( VipsOperation *operation, va_list ap )
|
2011-04-04 16:46:57 +02:00
|
|
|
{
|
2011-09-05 19:16:32 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_operation_set_valist_required:\n" );
|
|
|
|
|
2011-05-22 18:32:57 +02:00
|
|
|
/* Set required input arguments. Can't use vips_argument_map here
|
|
|
|
* :-( because passing va_list by reference is not portable.
|
|
|
|
*/
|
|
|
|
VIPS_ARGUMENT_FOR_ALL( operation,
|
|
|
|
pspec, argument_class, argument_instance ) {
|
|
|
|
|
|
|
|
g_assert( argument_instance );
|
|
|
|
|
2011-09-05 19:16:32 +02:00
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) {
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, ap );
|
2011-04-04 16:46:57 +02:00
|
|
|
|
2011-04-05 18:02:12 +02:00
|
|
|
#ifdef VIPS_DEBUG
|
2011-05-22 18:32:57 +02:00
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
str = g_strdup_value_contents( &value );
|
|
|
|
VIPS_DEBUG_MSG( "\t%s = %s\n",
|
|
|
|
g_param_spec_get_name( pspec ), str );
|
|
|
|
g_free( str );
|
|
|
|
}
|
2011-04-05 18:02:12 +02:00
|
|
|
#endif /*VIPS_DEBUG */
|
|
|
|
|
2011-05-22 18:32:57 +02:00
|
|
|
g_object_set_property( G_OBJECT( operation ),
|
|
|
|
g_param_spec_get_name( pspec ), &value );
|
2011-09-05 19:16:32 +02:00
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_GET( pspec, argument_class, ap );
|
2011-05-19 14:48:09 +02:00
|
|
|
|
|
|
|
#ifdef VIPS_DEBUG
|
2011-05-22 18:32:57 +02:00
|
|
|
printf( "\tskipping arg %p for %s\n",
|
|
|
|
arg, g_param_spec_get_name( pspec ) );
|
2011-05-19 14:48:09 +02:00
|
|
|
#endif /*VIPS_DEBUG */
|
2011-09-05 19:16:32 +02:00
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_END
|
2011-05-19 14:48:09 +02:00
|
|
|
}
|
2011-05-22 18:32:57 +02:00
|
|
|
} VIPS_ARGUMENT_FOR_ALL_END
|
2011-03-29 13:12:20 +02:00
|
|
|
|
2011-05-22 18:32:57 +02:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2011-09-05 19:16:32 +02:00
|
|
|
static int
|
|
|
|
vips_operation_get_valist_required( VipsOperation *operation, va_list ap )
|
2011-03-29 13:12:20 +02:00
|
|
|
{
|
2011-09-05 19:16:32 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_operation_get_valist_required:\n" );
|
|
|
|
|
2011-05-22 18:32:57 +02:00
|
|
|
/* Extract output arguments. Can't use vips_argument_map here
|
|
|
|
* :-( because passing va_list by reference is not portable.
|
|
|
|
*/
|
|
|
|
VIPS_ARGUMENT_FOR_ALL( operation,
|
|
|
|
pspec, argument_class, argument_instance ) {
|
2011-09-05 19:16:32 +02:00
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) {
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, ap );
|
2011-05-22 18:32:57 +02:00
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_GET( pspec, argument_class, ap );
|
2011-05-22 18:32:57 +02:00
|
|
|
|
|
|
|
if( !argument_instance->assigned )
|
|
|
|
continue;
|
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
#ifdef VIPS_DEBUG
|
2011-05-23 19:19:20 +02:00
|
|
|
printf( "\twriting %s to %p\n",
|
2011-05-22 18:32:57 +02:00
|
|
|
g_param_spec_get_name( pspec ), arg );
|
2011-05-19 14:48:09 +02:00
|
|
|
#endif /*VIPS_DEBUG */
|
2011-05-19 15:35:51 +02:00
|
|
|
|
2011-05-23 19:19:20 +02:00
|
|
|
g_object_get( G_OBJECT( operation ),
|
|
|
|
g_param_spec_get_name( pspec ), arg, NULL );
|
|
|
|
|
|
|
|
/* If the pspec is an object, that will up the ref
|
|
|
|
* count. We want to hand over the ref, so we have to
|
|
|
|
* knock it down again.
|
2011-05-22 18:32:57 +02:00
|
|
|
*/
|
2011-05-23 19:19:20 +02:00
|
|
|
if( G_IS_PARAM_SPEC_OBJECT( pspec ) ) {
|
|
|
|
GObject *object;
|
|
|
|
|
|
|
|
object = *((GObject **) arg);
|
|
|
|
g_object_unref( object );
|
|
|
|
}
|
2011-09-05 19:16:32 +02:00
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_END
|
2011-05-22 18:32:57 +02:00
|
|
|
}
|
|
|
|
} VIPS_ARGUMENT_FOR_ALL_END
|
2011-09-05 19:16:32 +02:00
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vips_operation_get_valist_optional( VipsOperation *operation, va_list ap )
|
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
VIPS_DEBUG_MSG( "vips_operation_get_valist_optional:\n" );
|
|
|
|
|
2013-05-07 18:22:29 +02:00
|
|
|
for( name = va_arg( ap, char * ); name; name = va_arg( ap, char * ) ) {
|
2011-09-05 19:16:32 +02:00
|
|
|
GParamSpec *pspec;
|
|
|
|
VipsArgumentClass *argument_class;
|
2011-09-30 10:39:47 +02:00
|
|
|
VipsArgumentInstance *argument_instance;
|
2011-09-05 19:16:32 +02:00
|
|
|
|
|
|
|
VIPS_DEBUG_MSG( "\tname = '%s' (%p)\n", name, name );
|
|
|
|
|
2011-09-30 10:39:47 +02:00
|
|
|
if( vips_object_get_argument( VIPS_OBJECT( operation ), name,
|
|
|
|
&pspec, &argument_class, &argument_instance ) )
|
2011-09-05 19:16:32 +02:00
|
|
|
return( -1 );
|
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, ap );
|
2011-09-05 19:16:32 +02:00
|
|
|
|
2011-10-25 10:58:10 +02:00
|
|
|
/* We must collect input args as we walk the name/value list,
|
|
|
|
* but we don't do anything with them.
|
|
|
|
*/
|
2011-09-05 19:16:32 +02:00
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_GET( pspec, argument_class, ap );
|
2011-09-05 19:16:32 +02:00
|
|
|
|
2011-10-25 10:58:10 +02:00
|
|
|
/* Here's an output arg.
|
|
|
|
*/
|
|
|
|
|
2011-09-05 19:16:32 +02:00
|
|
|
#ifdef VIPS_DEBUG
|
2011-09-30 10:39:47 +02:00
|
|
|
printf( "\twriting %s to %p\n",
|
|
|
|
g_param_spec_get_name( pspec ), arg );
|
2011-09-05 19:16:32 +02:00
|
|
|
#endif /*VIPS_DEBUG */
|
|
|
|
|
2011-09-30 10:39:47 +02:00
|
|
|
/* If the dest pointer is NULL, skip the read.
|
|
|
|
*/
|
|
|
|
if( arg ) {
|
|
|
|
g_object_get( G_OBJECT( operation ),
|
|
|
|
g_param_spec_get_name( pspec ), arg,
|
|
|
|
NULL );
|
|
|
|
|
|
|
|
/* If the pspec is an object, that will up
|
|
|
|
* the ref count. We want to hand over the
|
|
|
|
* ref, so we have to knock it down again.
|
2011-09-05 19:16:32 +02:00
|
|
|
*/
|
2011-09-30 10:39:47 +02:00
|
|
|
if( G_IS_PARAM_SPEC_OBJECT( pspec ) ) {
|
|
|
|
GObject *object;
|
2011-09-05 19:16:32 +02:00
|
|
|
|
2011-09-30 10:39:47 +02:00
|
|
|
object = *((GObject **) arg);
|
|
|
|
g_object_unref( object );
|
|
|
|
}
|
2011-09-05 19:16:32 +02:00
|
|
|
}
|
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_END
|
2011-09-05 19:16:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2011-09-10 15:38:39 +02:00
|
|
|
/* This can change operation to point at an old, cached one.
|
|
|
|
*/
|
2011-09-05 19:16:32 +02:00
|
|
|
static int
|
2011-09-10 15:38:39 +02:00
|
|
|
vips_call_required_optional( VipsOperation **operation,
|
2011-09-05 19:16:32 +02:00
|
|
|
va_list required, va_list optional )
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
va_list a;
|
|
|
|
va_list b;
|
|
|
|
|
|
|
|
/* We need to be able to walk required and optional twice. On x64 gcc,
|
|
|
|
* vips_operation_set_valist_required() etc. will destructively alter
|
2012-01-16 15:54:29 +01:00
|
|
|
* the passed-in va_list. We make a copy and walk that instead.
|
2011-09-05 19:16:32 +02:00
|
|
|
*/
|
|
|
|
va_copy( a, required );
|
|
|
|
va_copy( b, optional );
|
2011-09-10 15:38:39 +02:00
|
|
|
result = vips_operation_set_valist_required( *operation, a ) ||
|
2012-07-09 22:35:53 +02:00
|
|
|
vips_object_set_valist( VIPS_OBJECT( *operation ), b );
|
2011-09-05 19:16:32 +02:00
|
|
|
va_end( a );
|
|
|
|
va_end( b );
|
|
|
|
|
2013-11-22 15:34:13 +01:00
|
|
|
if( result )
|
|
|
|
return( -1 );
|
|
|
|
|
2011-09-09 18:01:29 +02:00
|
|
|
/* Build from cache.
|
|
|
|
*/
|
2012-01-03 17:02:52 +01:00
|
|
|
if( vips_cache_operation_buildp( operation ) )
|
2011-09-05 19:16:32 +02:00
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
/* Walk args again, writing output.
|
|
|
|
*/
|
|
|
|
va_copy( a, required );
|
|
|
|
va_copy( b, optional );
|
2011-09-10 15:38:39 +02:00
|
|
|
result = vips_operation_get_valist_required( *operation, required ) ||
|
|
|
|
vips_operation_get_valist_optional( *operation, optional );
|
2011-09-05 19:16:32 +02:00
|
|
|
va_end( a );
|
|
|
|
va_end( b );
|
|
|
|
|
|
|
|
return( result );
|
2011-03-29 13:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2011-04-05 18:02:12 +02:00
|
|
|
vips_call( const char *operation_name, ... )
|
2011-03-29 13:12:20 +02:00
|
|
|
{
|
|
|
|
VipsOperation *operation;
|
|
|
|
int result;
|
2011-09-05 19:16:32 +02:00
|
|
|
va_list required;
|
|
|
|
va_list optional;
|
2011-03-29 13:12:20 +02:00
|
|
|
|
2011-04-04 16:46:57 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_call: starting for %s ...\n", operation_name );
|
2011-03-29 13:12:20 +02:00
|
|
|
|
|
|
|
if( !(operation = vips_operation_new( operation_name ) ) )
|
|
|
|
return( -1 );
|
|
|
|
|
2011-04-04 16:46:57 +02:00
|
|
|
#ifdef VIPS_DEBUG
|
|
|
|
VIPS_DEBUG_MSG( "where:\n" );
|
2013-06-11 13:32:46 +02:00
|
|
|
vips_object_print_dump( VIPS_OBJECT( operation ) );
|
2011-04-04 16:46:57 +02:00
|
|
|
#endif /*VIPS_DEBUG*/
|
|
|
|
|
2011-09-05 19:16:32 +02:00
|
|
|
/* We have to break the va_list into separate required and optional
|
|
|
|
* components.
|
|
|
|
*
|
|
|
|
* Note the start, grab the required, then copy and reuse.
|
|
|
|
*/
|
|
|
|
va_start( required, operation_name );
|
2011-04-05 18:02:12 +02:00
|
|
|
|
2011-09-05 19:16:32 +02:00
|
|
|
va_copy( optional, required );
|
|
|
|
|
|
|
|
VIPS_ARGUMENT_FOR_ALL( operation,
|
|
|
|
pspec, argument_class, argument_instance ) {
|
|
|
|
|
|
|
|
g_assert( argument_instance );
|
|
|
|
|
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) {
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class,
|
2011-09-05 19:16:32 +02:00
|
|
|
optional );
|
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_GET( pspec, argument_class,
|
2011-09-05 19:16:32 +02:00
|
|
|
optional );
|
|
|
|
|
2012-02-23 11:05:13 +01:00
|
|
|
VIPS_ARGUMENT_COLLECT_END
|
2011-09-05 19:16:32 +02:00
|
|
|
}
|
|
|
|
} VIPS_ARGUMENT_FOR_ALL_END
|
|
|
|
|
2011-09-10 15:38:39 +02:00
|
|
|
result = vips_call_required_optional( &operation, required, optional );
|
2011-09-05 19:16:32 +02:00
|
|
|
|
|
|
|
va_end( required );
|
|
|
|
va_end( optional );
|
|
|
|
|
|
|
|
/* Failed: junk args and back out.
|
2011-05-19 14:48:09 +02:00
|
|
|
*/
|
|
|
|
if( result ) {
|
2011-12-01 11:01:49 +01:00
|
|
|
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
|
2011-05-19 14:48:09 +02:00
|
|
|
g_object_unref( operation );
|
|
|
|
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2011-04-05 18:02:12 +02:00
|
|
|
/* The operation we have built should now have been reffed by one of
|
|
|
|
* its arguments or have finished its work. Either way, we can unref.
|
|
|
|
*/
|
|
|
|
g_object_unref( operation );
|
|
|
|
|
|
|
|
return( result );
|
|
|
|
}
|
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
int
|
|
|
|
vips_call_split( const char *operation_name, va_list optional, ... )
|
2011-04-05 18:02:12 +02:00
|
|
|
{
|
|
|
|
VipsOperation *operation;
|
|
|
|
int result;
|
2011-05-19 14:48:09 +02:00
|
|
|
va_list required;
|
2011-04-05 18:02:12 +02:00
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_call_split: starting for %s ...\n",
|
2011-09-05 19:16:32 +02:00
|
|
|
operation_name );
|
2011-04-05 18:02:12 +02:00
|
|
|
|
2014-02-22 17:08:46 +01:00
|
|
|
if( !(operation = vips_operation_new( operation_name )) )
|
2011-04-05 18:02:12 +02:00
|
|
|
return( -1 );
|
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
va_start( required, optional );
|
2011-09-10 15:38:39 +02:00
|
|
|
result = vips_call_required_optional( &operation, required, optional );
|
2011-05-19 14:48:09 +02:00
|
|
|
va_end( required );
|
2011-03-29 13:12:20 +02:00
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
/* Build failed: junk args and back out.
|
2011-03-29 13:12:20 +02:00
|
|
|
*/
|
2011-05-19 14:48:09 +02:00
|
|
|
if( result ) {
|
2011-12-01 11:01:49 +01:00
|
|
|
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
|
2011-05-19 14:48:09 +02:00
|
|
|
g_object_unref( operation );
|
2011-04-05 15:46:17 +02:00
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
return( -1 );
|
|
|
|
}
|
2011-04-05 18:02:12 +02:00
|
|
|
|
2011-05-19 14:48:09 +02:00
|
|
|
/* The operation we have built should now have been reffed by one of
|
|
|
|
* its arguments or have finished its work. Either way, we can unref.
|
|
|
|
*/
|
|
|
|
g_object_unref( operation );
|
|
|
|
|
2011-04-05 15:46:17 +02:00
|
|
|
return( result );
|
2011-03-29 13:12:20 +02:00
|
|
|
}
|
2011-04-20 18:41:55 +02:00
|
|
|
|
2011-05-16 18:10:08 +02:00
|
|
|
static void *
|
2011-09-04 11:10:39 +02:00
|
|
|
vips_call_find_pspec( VipsObject *object,
|
2011-05-16 18:10:08 +02:00
|
|
|
GParamSpec *pspec,
|
|
|
|
VipsArgumentClass *argument_class,
|
|
|
|
VipsArgumentInstance *argument_instance,
|
|
|
|
void *a, void *b )
|
|
|
|
{
|
|
|
|
const char *name = (const char *) a;
|
|
|
|
|
2011-09-04 11:10:39 +02:00
|
|
|
/* One char names we assume are "-x" style abbreviations, longer names
|
|
|
|
* we match the whole string.
|
|
|
|
*/
|
2011-05-16 18:10:08 +02:00
|
|
|
if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
|
|
|
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
2011-09-04 11:10:39 +02:00
|
|
|
!argument_instance->assigned )
|
|
|
|
if( (strlen( name ) == 1 &&
|
|
|
|
g_param_spec_get_name( pspec )[0] == name[0]) ||
|
|
|
|
strcmp( g_param_spec_get_name( pspec ), name ) == 0 )
|
|
|
|
return( argument_instance );
|
2011-05-16 18:10:08 +02:00
|
|
|
|
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
2011-09-04 11:10:39 +02:00
|
|
|
/* Keep this stuff around for output args.
|
|
|
|
*/
|
|
|
|
typedef struct _VipsCallOptionOutput {
|
|
|
|
VipsArgumentInstance *argument_instance;
|
|
|
|
const char *value;
|
|
|
|
} VipsCallOptionOutput;
|
|
|
|
|
|
|
|
static void
|
|
|
|
vips_call_option_output( VipsObject *object,
|
|
|
|
VipsCallOptionOutput *output )
|
2011-05-16 18:10:08 +02:00
|
|
|
{
|
2011-09-04 11:10:39 +02:00
|
|
|
VipsArgumentInstance *argument_instance = output->argument_instance;
|
|
|
|
GParamSpec *pspec = ((VipsArgument *) argument_instance)->pspec;
|
2011-05-16 18:10:08 +02:00
|
|
|
|
2012-12-06 14:54:32 +01:00
|
|
|
/* Don't look at the output arg if _build() hasn't run sucessfully, it
|
|
|
|
* probably won't have been set.
|
|
|
|
*/
|
|
|
|
if( object->constructed )
|
|
|
|
if( vips_object_get_argument_to_string( object,
|
|
|
|
g_param_spec_get_name( pspec ), output->value ) ) {
|
|
|
|
/* FIXME .. Hmm what can we do here? If an arg is image
|
|
|
|
* output, for example, we will lose the error.
|
|
|
|
*/
|
|
|
|
}
|
2011-05-16 18:10:08 +02:00
|
|
|
|
2011-09-04 11:10:39 +02:00
|
|
|
g_free( output );
|
2011-05-16 18:10:08 +02:00
|
|
|
}
|
|
|
|
|
2011-05-11 15:51:47 +02:00
|
|
|
static gboolean
|
|
|
|
vips_call_options_set( const gchar *option_name, const gchar *value,
|
|
|
|
gpointer data, GError **error )
|
2011-04-20 18:41:55 +02:00
|
|
|
{
|
2011-05-11 15:51:47 +02:00
|
|
|
VipsOperation *operation = (VipsOperation *) data;
|
2011-05-16 18:10:08 +02:00
|
|
|
const char *name;
|
2011-09-04 11:10:39 +02:00
|
|
|
VipsArgumentInstance *argument_instance;
|
|
|
|
VipsArgumentClass *argument_class;
|
2011-09-02 19:21:28 +02:00
|
|
|
GParamSpec *pspec;
|
2011-04-20 18:41:55 +02:00
|
|
|
|
2011-05-16 09:31:55 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_call_options_set: %s = %s\n",
|
|
|
|
option_name, value );
|
|
|
|
|
2011-05-16 18:10:08 +02:00
|
|
|
/* Remove any leading "--" from the option name.
|
|
|
|
*/
|
|
|
|
for( name = option_name; *name == '-'; name++ )
|
|
|
|
;
|
|
|
|
|
2011-09-04 11:10:39 +02:00
|
|
|
if( !(argument_instance = (VipsArgumentInstance *)
|
|
|
|
vips_argument_map(
|
2011-09-02 19:21:28 +02:00
|
|
|
VIPS_OBJECT( operation ),
|
2011-09-04 11:10:39 +02:00
|
|
|
vips_call_find_pspec, (void *) name, NULL )) ) {
|
2011-11-06 14:13:58 +01:00
|
|
|
vips_error( VIPS_OBJECT_GET_CLASS( operation )->nickname,
|
2011-09-02 19:21:28 +02:00
|
|
|
_( "unknown argument '%s'" ), name );
|
2012-04-04 14:37:05 +02:00
|
|
|
vips_error_g( error );
|
2011-09-02 19:21:28 +02:00
|
|
|
return( FALSE );
|
2011-05-16 18:10:08 +02:00
|
|
|
}
|
2011-09-04 11:10:39 +02:00
|
|
|
argument_class = argument_instance->argument_class;
|
|
|
|
pspec = ((VipsArgument *) argument_instance)->pspec;
|
2011-04-20 18:41:55 +02:00
|
|
|
|
2011-09-04 11:10:39 +02:00
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_INPUT) ) {
|
|
|
|
if( vips_object_set_argument_from_string(
|
|
|
|
VIPS_OBJECT( operation ),
|
2012-04-04 14:37:05 +02:00
|
|
|
g_param_spec_get_name( pspec ), value ) ) {
|
|
|
|
vips_error_g( error );
|
2011-09-04 11:10:39 +02:00
|
|
|
return( FALSE );
|
2012-04-04 14:37:05 +02:00
|
|
|
}
|
2011-10-28 16:13:55 +02:00
|
|
|
|
|
|
|
#ifdef VIPS_DEBUG
|
|
|
|
{
|
|
|
|
GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
|
|
|
|
GValue gvalue = { 0, };
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
g_value_init( &gvalue, type );
|
|
|
|
g_object_get_property( G_OBJECT( operation ),
|
|
|
|
g_param_spec_get_name( pspec ), &gvalue );
|
|
|
|
str = g_strdup_value_contents( &gvalue );
|
|
|
|
VIPS_DEBUG_MSG( "\tGValue %s = %s\n",
|
|
|
|
g_param_spec_get_name( pspec ), str );
|
|
|
|
g_free( str );
|
2012-01-04 14:50:10 +01:00
|
|
|
g_value_unset( &gvalue );
|
2011-10-28 16:13:55 +02:00
|
|
|
}
|
|
|
|
#endif /*VIPS_DEBUG*/
|
2011-09-04 11:10:39 +02:00
|
|
|
}
|
|
|
|
else if( (argument_class->flags & VIPS_ARGUMENT_OUTPUT) ) {
|
|
|
|
VipsCallOptionOutput *output;
|
|
|
|
|
|
|
|
/* We can't do output now, we have to attach a callback to do
|
|
|
|
* the processing after the operation has run.
|
|
|
|
*
|
|
|
|
* FIXME ... something like posteval or postbuild might be
|
|
|
|
* better for this?
|
|
|
|
*/
|
|
|
|
output = g_new( VipsCallOptionOutput, 1 );
|
|
|
|
output->argument_instance = argument_instance;
|
|
|
|
output->value = value;
|
|
|
|
g_signal_connect( operation, "preclose",
|
|
|
|
G_CALLBACK( vips_call_option_output ),
|
|
|
|
output );
|
|
|
|
}
|
2011-09-02 19:21:28 +02:00
|
|
|
|
2011-05-11 15:51:47 +02:00
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
vips_call_options_add( VipsObject *object,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
VipsArgumentClass *argument_class,
|
|
|
|
VipsArgumentInstance *argument_instance,
|
|
|
|
void *a, void *b )
|
|
|
|
{
|
|
|
|
GOptionGroup *group = (GOptionGroup *) a;
|
|
|
|
|
|
|
|
if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
|
|
|
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
|
|
|
!argument_instance->assigned ) {
|
2011-09-02 10:10:33 +02:00
|
|
|
const char *name = g_param_spec_get_name( pspec );
|
2011-09-02 19:21:28 +02:00
|
|
|
gboolean needs_string =
|
2012-11-09 14:08:59 +01:00
|
|
|
vips_object_argument_needsstring( object, name );
|
2011-05-11 15:51:47 +02:00
|
|
|
GOptionEntry entry[2];
|
|
|
|
|
2011-09-02 10:10:33 +02:00
|
|
|
entry[0].long_name = name;
|
|
|
|
entry[0].short_name = name[0];
|
2012-08-07 13:52:50 +02:00
|
|
|
entry[0].description = g_param_spec_get_blurb( pspec );
|
|
|
|
|
2011-09-02 10:10:33 +02:00
|
|
|
entry[0].flags = 0;
|
2011-09-02 19:21:28 +02:00
|
|
|
if( !needs_string )
|
2011-09-02 10:10:33 +02:00
|
|
|
entry[0].flags |= G_OPTION_FLAG_NO_ARG;
|
2012-08-07 13:52:50 +02:00
|
|
|
if( argument_class->flags & VIPS_ARGUMENT_DEPRECATED )
|
|
|
|
entry[0].flags |= G_OPTION_FLAG_HIDDEN;
|
|
|
|
|
2011-05-11 15:51:47 +02:00
|
|
|
entry[0].arg = G_OPTION_ARG_CALLBACK;
|
|
|
|
entry[0].arg_data = (gpointer) vips_call_options_set;
|
2011-09-02 19:21:28 +02:00
|
|
|
if( needs_string )
|
2011-05-17 15:45:51 +02:00
|
|
|
entry[0].arg_description =
|
|
|
|
g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) );
|
2011-09-02 19:21:28 +02:00
|
|
|
else
|
|
|
|
entry[0].arg_description = NULL;
|
2011-05-11 15:51:47 +02:00
|
|
|
|
|
|
|
entry[1].long_name = NULL;
|
|
|
|
|
2011-09-02 10:10:33 +02:00
|
|
|
VIPS_DEBUG_MSG( "vips_call_options_add: adding %s\n", name );
|
2011-05-16 09:31:55 +02:00
|
|
|
|
2011-05-11 15:51:47 +02:00
|
|
|
g_option_group_add_entries( group, &entry[0] );
|
2011-04-20 18:41:55 +02:00
|
|
|
}
|
|
|
|
|
2011-05-11 15:51:47 +02:00
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
2011-05-16 18:10:08 +02:00
|
|
|
void
|
|
|
|
vips_call_options( GOptionGroup *group, VipsOperation *operation )
|
2011-05-11 15:51:47 +02:00
|
|
|
{
|
|
|
|
(void) vips_argument_map( VIPS_OBJECT( operation ),
|
|
|
|
vips_call_options_add, group, NULL );
|
|
|
|
}
|
|
|
|
|
2011-07-22 18:28:08 +02:00
|
|
|
/* What we track during an argv call.
|
|
|
|
*/
|
|
|
|
typedef struct _VipsCall {
|
|
|
|
VipsOperation *operation;
|
|
|
|
int argc;
|
|
|
|
char **argv;
|
|
|
|
int i;
|
|
|
|
} VipsCall;
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
vips_call_get_arg( VipsCall *call, int i )
|
|
|
|
{
|
|
|
|
if( i < 0 || i >= call->argc ) {
|
2011-11-06 14:13:58 +01:00
|
|
|
vips_error( VIPS_OBJECT_GET_CLASS( call->operation )->nickname,
|
2011-07-22 18:28:08 +02:00
|
|
|
"%s", _( "too few arguments" ) );
|
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
return( call->argv[i] );
|
|
|
|
}
|
|
|
|
|
2011-05-22 18:32:57 +02:00
|
|
|
static void *
|
|
|
|
vips_call_argv_input( VipsObject *object,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
VipsArgumentClass *argument_class,
|
|
|
|
VipsArgumentInstance *argument_instance,
|
|
|
|
void *a, void *b )
|
|
|
|
{
|
2011-07-22 18:28:08 +02:00
|
|
|
VipsCall *call = (VipsCall *) a;
|
2011-05-22 18:32:57 +02:00
|
|
|
|
|
|
|
/* Loop over all required construct args.
|
|
|
|
*/
|
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
2012-08-07 13:52:50 +02:00
|
|
|
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
|
|
|
!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) ) {
|
2011-10-23 23:05:21 +02:00
|
|
|
const char *name = g_param_spec_get_name( pspec );
|
|
|
|
|
2011-05-23 18:12:09 +02:00
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_INPUT) ) {
|
2011-07-22 18:28:08 +02:00
|
|
|
const char *arg;
|
|
|
|
|
|
|
|
if( !(arg = vips_call_get_arg( call, call->i )) ||
|
|
|
|
vips_object_set_argument_from_string( object,
|
2011-10-23 23:05:21 +02:00
|
|
|
name, arg ) )
|
2011-05-22 18:32:57 +02:00
|
|
|
return( pspec );
|
2011-07-22 18:28:08 +02:00
|
|
|
|
|
|
|
call->i += 1;
|
2011-05-23 18:12:09 +02:00
|
|
|
}
|
|
|
|
else if( (argument_class->flags & VIPS_ARGUMENT_OUTPUT) ) {
|
2012-11-09 14:08:59 +01:00
|
|
|
if( vips_object_argument_needsstring( object, name ) )
|
2011-07-22 18:28:08 +02:00
|
|
|
call->i += 1;
|
2011-05-23 18:12:09 +02:00
|
|
|
}
|
2011-05-22 18:32:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
vips_call_argv_output( VipsObject *object,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
VipsArgumentClass *argument_class,
|
|
|
|
VipsArgumentInstance *argument_instance,
|
|
|
|
void *a, void *b )
|
|
|
|
{
|
2011-07-22 18:28:08 +02:00
|
|
|
VipsCall *call = (VipsCall *) a;
|
2011-05-22 18:32:57 +02:00
|
|
|
|
|
|
|
/* Loop over all required construct args.
|
|
|
|
*/
|
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
|
2012-08-07 13:52:50 +02:00
|
|
|
(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
|
|
|
!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) ) {
|
2011-05-23 18:12:09 +02:00
|
|
|
if( (argument_class->flags & VIPS_ARGUMENT_INPUT) )
|
2011-07-22 18:28:08 +02:00
|
|
|
call->i += 1;
|
2011-05-23 18:12:09 +02:00
|
|
|
else if( (argument_class->flags & VIPS_ARGUMENT_OUTPUT) ) {
|
2011-09-02 19:21:28 +02:00
|
|
|
const char *name = g_param_spec_get_name( pspec );
|
2011-07-22 18:28:08 +02:00
|
|
|
const char *arg;
|
2011-05-23 18:12:09 +02:00
|
|
|
|
|
|
|
arg = NULL;
|
2012-11-09 14:08:59 +01:00
|
|
|
if( vips_object_argument_needsstring( object, name ) ) {
|
2011-07-22 18:28:08 +02:00
|
|
|
arg = vips_call_get_arg( call, call->i );
|
|
|
|
if( !arg )
|
|
|
|
return( pspec );
|
|
|
|
|
|
|
|
call->i += 1;
|
2011-05-23 18:12:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if( vips_object_get_argument_to_string( object,
|
2011-09-02 19:21:28 +02:00
|
|
|
name, arg ) )
|
2011-05-23 18:12:09 +02:00
|
|
|
return( pspec );
|
|
|
|
}
|
|
|
|
}
|
2011-05-22 18:32:57 +02:00
|
|
|
|
2011-05-23 18:12:09 +02:00
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
2011-05-11 15:51:47 +02:00
|
|
|
/* Our main command-line entry point. Optional args should have been set by
|
|
|
|
* the GOption parser already, see above.
|
2011-05-13 11:08:53 +02:00
|
|
|
*
|
|
|
|
* We don't create the operation, so we must not unref it. The caller must
|
2011-11-30 23:06:52 +01:00
|
|
|
* unref on error too. The caller must also call vips_object_unref_outputs() on
|
|
|
|
* all code paths.
|
2011-05-11 15:51:47 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
vips_call_argv( VipsOperation *operation, int argc, char **argv )
|
|
|
|
{
|
2011-07-22 18:28:08 +02:00
|
|
|
VipsCall call;
|
2011-05-11 15:51:47 +02:00
|
|
|
|
2011-05-13 11:08:53 +02:00
|
|
|
g_assert( argc >= 0 );
|
2011-05-11 15:51:47 +02:00
|
|
|
|
2011-05-16 09:31:55 +02:00
|
|
|
#ifdef VIPS_DEBUG
|
|
|
|
printf( "vips_call_argv: " );
|
|
|
|
vips_object_print_name( VIPS_OBJECT( operation ) );
|
|
|
|
printf( "\n" );
|
2011-08-28 13:46:50 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2011-05-16 09:31:55 +02:00
|
|
|
for( i = 0; i < argc; i++ )
|
|
|
|
printf( "%d) %s\n", i, argv[i] );
|
2011-08-28 13:46:50 +02:00
|
|
|
}
|
2011-05-16 09:31:55 +02:00
|
|
|
#endif /*VIPS_DEBUG*/
|
|
|
|
|
2011-07-22 18:28:08 +02:00
|
|
|
call.operation = operation;
|
|
|
|
call.argc = argc;
|
|
|
|
call.argv = argv;
|
|
|
|
|
|
|
|
call.i = 0;
|
2011-11-22 10:26:31 +01:00
|
|
|
if( vips_argument_map( VIPS_OBJECT( operation ),
|
2011-11-30 23:06:52 +01:00
|
|
|
vips_call_argv_input, &call, NULL ) )
|
2011-11-22 10:26:31 +01:00
|
|
|
return( -1 );
|
2011-07-22 18:28:08 +02:00
|
|
|
|
2011-11-21 19:09:28 +01:00
|
|
|
/* Any unused arguments? We must fail. Consider eg. "vips bandjoin a b
|
|
|
|
* c". This would overwrite b with a and ignore c, potentially
|
|
|
|
* disasterous.
|
|
|
|
*/
|
|
|
|
if( argc > call.i ) {
|
|
|
|
vips_error( VIPS_OBJECT_GET_CLASS( operation )->nickname,
|
|
|
|
"%s", _( "too many arguments" ) );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2011-12-02 14:15:05 +01:00
|
|
|
/* We can't use the operation cache, we need to be able to change the
|
|
|
|
* operation pointer. The cache probably wouldn't help anyway.
|
|
|
|
*/
|
2011-11-30 23:06:52 +01:00
|
|
|
if( vips_object_build( VIPS_OBJECT( operation ) ) )
|
2011-04-20 18:41:55 +02:00
|
|
|
return( -1 );
|
|
|
|
|
2011-07-22 18:28:08 +02:00
|
|
|
call.i = 0;
|
|
|
|
if( vips_argument_map( VIPS_OBJECT( operation ),
|
2011-11-30 23:06:52 +01:00
|
|
|
vips_call_argv_output, &call, NULL ) )
|
2011-07-22 18:28:08 +02:00
|
|
|
return( -1 );
|
2011-05-23 18:12:09 +02:00
|
|
|
|
2011-04-20 18:41:55 +02:00
|
|
|
return( 0 );
|
|
|
|
}
|