vips_system() allows many input images

you can change the image argument order too, with %Ns

added postbuild signal
This commit is contained in:
John Cupitt 2014-04-09 11:42:03 +01:00
parent 9d5fbe3935
commit a17ef9b7c8
15 changed files with 401 additions and 71 deletions

View File

@ -17,6 +17,9 @@
- add "mode" param to vips_draw_image()
- add vips_hough_circle()
- reduce default cache size to 1,000 operations
- added "postbuild" signal
- vips_system() now supports many input images and you can change image
argument order
6/3/14 started 7.38.6
- grey ramp minimum was wrong

18
TODO
View File

@ -1,7 +1,25 @@
- try:
$ vips system "vips add %s %s %s" --in "k2.jpg k4.jpg" \
--out-format "%s.tif" --out x.v --log
memory: high-water mark 9.37 MB
""
memory: high-water mark 27.89 MB
$
but x.v does not appear
- can we use postbuild elsewhere? look at use of "preclose" / "written", etc.
- why is cache in nip2 so slow? its awful
investigate again
is it using vips_sinkscreen()? should it use tilecache?
- test draw_mask on labq images

View File

@ -78,7 +78,7 @@ typedef struct _VipsBandjoin {
/* The input images.
*/
VipsArea *in;
VipsArrayImage *in;
} VipsBandjoin;
typedef VipsBandaryClass VipsBandjoinClass;

View File

@ -1754,14 +1754,24 @@ im_text( IMAGE *out, const char *text, const char *font,
int
im_system( VipsImage *im, const char *cmd, char **out )
{
VipsArea *area;
VipsImage **array;
char *str;
area = vips_area_new_array_object( 1 );
array = (VipsImage **) area->data;
array[0] = im;
if( vips_system( cmd,
"in", im,
"in", area,
"in_format", "%s.v",
"log", &str,
NULL ) )
NULL ) ) {
vips_area_unref( area );
return( -1 );
}
vips_area_unref( area );
if( out )
*out = str;
@ -1774,17 +1784,27 @@ im_system_image( VipsImage *im,
const char *in_format, const char *out_format, const char *cmd_format,
char **log )
{
VipsArea *area;
VipsImage **array;
char *str;
VipsImage *out;
area = vips_area_new_array_object( 1 );
array = (VipsImage **) area->data;
array[0] = im;
if( vips_system( cmd_format,
"in", im,
"in", area,
"out", &out,
"in_format", in_format,
"out_format", out_format,
"log", &str,
NULL ) )
NULL ) ) {
vips_area_unref( area );
return( NULL );
}
vips_area_unref( area );
if( log )
*log = str;

View File

@ -307,6 +307,7 @@ void vips_convolution_operation_init( void );
void vips_draw_operation_init( void );
guint64 vips__parse_size( const char *size_string );
int vips__substitute( const char *domain, char *buf, size_t len, char *sub );
IMAGE *vips__deprecated_open_read( const char *filename, gboolean sequential );
IMAGE *vips__deprecated_open_write( const char *filename );

View File

@ -445,6 +445,10 @@ struct _VipsObjectClass {
*/
int (*build)( VipsObject *object );
/* Just after build ... the object is fully ready for work.
*/
int (*postbuild)( VipsObject *object );
/* Try to print something about the class, handy for help displays.
* Keep to one line.
*/

View File

@ -162,6 +162,7 @@ GType vips_array_int_get_type( void );
* The #GType for a #VipsArrayImage.
*/
#define VIPS_TYPE_ARRAY_IMAGE (vips_array_image_get_type())
typedef VipsArea VipsArrayImage;
GType vips_array_image_get_type( void );
void vips_value_set_area( GValue *value, VipsCallbackFn free_fn, void *data );

View File

@ -1,6 +1,8 @@
noinst_LTLIBRARIES = libiofuncs.la
libiofuncs_la_SOURCES = \
vipsmarshal.h \
vipsmarshal.c \
type.c \
gate.c \
enumtypes.c \
@ -33,7 +35,17 @@ libiofuncs_la_SOURCES = \
system.c \
buffer.c
EXTRA_DIST = enumtemplate
vipsmarshal.h:
glib-genmarshal --prefix=vips --header vipsmarshal.list > vipsmarshal.h
vipsmarshal.c:
echo "#include \"vipsmarshal.h\"" > vipsmarshal.c
glib-genmarshal --prefix=vips --body vipsmarshal.list >> vipsmarshal.c
EXTRA_DIST = \
enumtemplate \
vipsmarshal.h \
vipsmarshal.c \
vipsmarshal.list
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@

View File

@ -52,6 +52,8 @@
#include <gobject/gvaluecollector.h>
#include "vipsmarshal.h"
/**
* SECTION: object
* @short_description: the VIPS base object class
@ -111,6 +113,7 @@
/* Our signals.
*/
enum {
SIG_POSTBUILD,
SIG_PRECLOSE,
SIG_CLOSE,
SIG_POSTCLOSE,
@ -132,6 +135,24 @@ static GHashTable *vips__object_nickname_table = NULL;
G_DEFINE_ABSTRACT_TYPE( VipsObject, vips_object, G_TYPE_OBJECT );
/* Don't call this directly, see vips_object_build().
*/
static int
vips_object_postbuild( VipsObject *object )
{
int result;
#ifdef DEBUG
printf( "vips_object_postbuild: " );
vips_object_print_name( object );
printf( "\n" );
#endif /*DEBUG*/
g_signal_emit( object, vips_object_signals[SIG_POSTBUILD], 0, &result );
return( result );
}
void
vips_object_preclose( VipsObject *object )
{
@ -250,6 +271,11 @@ vips_object_build( VipsObject *object )
*/
object->constructed = TRUE;
/* Only postbuild on success.
*/
if( !result )
result = vips_object_postbuild( object );
return( result );
}
@ -1285,6 +1311,20 @@ vips_object_real_build( VipsObject *object )
return( result );
}
static int
vips_object_real_postbuild( VipsObject *object )
{
#ifdef DEBUG
printf( "vips_object_real_postbuild: " );
vips_object_print_name( object );
printf( "\n" );
#endif /*DEBUG*/
g_assert( object->constructed );
return( 0 );
}
static void
vips_object_real_summary_class( VipsObjectClass *class, VipsBuf *buf )
{
@ -1384,6 +1424,7 @@ vips_object_class_init( VipsObjectClass *class )
gobject_class->get_property = vips_object_get_property;
class->build = vips_object_real_build;
class->postbuild = vips_object_real_postbuild;
class->summary_class = vips_object_real_summary_class;
class->summary = vips_object_real_summary;
class->dump = vips_object_real_dump;
@ -1419,6 +1460,13 @@ vips_object_class_init( VipsObjectClass *class )
G_STRUCT_OFFSET( VipsObject, description ),
"" );
vips_object_signals[SIG_POSTBUILD] = g_signal_new( "postbuild",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET( VipsObjectClass, postbuild ),
NULL, NULL,
vips_INT__VOID,
G_TYPE_INT, 0 );
vips_object_signals[SIG_PRECLOSE] = g_signal_new( "preclose",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_RUN_LAST,

View File

@ -773,28 +773,30 @@ vips_call_find_pspec( VipsObject *object,
*/
typedef struct _VipsCallOptionOutput {
VipsArgumentInstance *argument_instance;
const char *value;
char *value;
} VipsCallOptionOutput;
static void
static int
vips_call_option_output( VipsObject *object,
VipsCallOptionOutput *output )
{
VipsArgumentInstance *argument_instance = output->argument_instance;
GParamSpec *pspec = ((VipsArgument *) argument_instance)->pspec;
int result;
/* Don't look at the output arg if _build() hasn't run sucessfully, it
* probably won't have been set.
*/
result = 0;
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.
*/
}
result = vips_object_get_argument_to_string( object,
g_param_spec_get_name( pspec ), output->value );
VIPS_FREE( output->value );
g_free( output );
return( result );
}
static gboolean
@ -857,14 +859,11 @@ vips_call_options_set( const gchar *option_name, const gchar *value,
/* 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",
output->value = g_strdup( value );
g_signal_connect( operation, "postbuild",
G_CALLBACK( vips_call_option_output ),
output );
}

View File

@ -62,21 +62,21 @@
#include <errno.h>
#include <vips/vips.h>
#include <vips/internal.h>
typedef struct _VipsSystem {
VipsOperation parent_instance;
VipsImage *in;
VipsArrayImage *in;
VipsImage *out;
char *cmd_format;
char *in_format;
char *out_format;
char *log;
/* Set to delete in_name on close.
/* Array of names we wrote the input images to.
*/
gboolean delete_on_close;
char *in_name;
char **in_name;
char *out_name;
@ -91,11 +91,14 @@ vips_system_dispose( GObject *gobject )
{
VipsSystem *system = (VipsSystem *) gobject;
if( system->delete_on_close &&
system->in_name )
g_unlink( system->in_name );
VIPS_FREE( system->in_name );
system->delete_on_close = FALSE;
if( system->in_name ) {
int i;
for( i = 0; i < system->in->n; i++ ) {
g_unlink( system->in_name[i] );
VIPS_FREE( system->in_name[i] );
}
}
VIPS_FREE( system->out_name );
@ -108,6 +111,10 @@ vips_system_build( VipsObject *object )
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsSystem *system = (VipsSystem *) object;
int i;
char cmd[VIPS_PATH_MAX];
FILE *fp;
char line[VIPS_PATH_MAX];
char txt[VIPS_PATH_MAX];
@ -117,19 +124,27 @@ vips_system_build( VipsObject *object )
if( VIPS_OBJECT_CLASS( vips_system_parent_class )->build( object ) )
return( -1 );
/* Write the input image to a file. We must always make a copy of the
* file, even if this is a disc file already, in case the command
* needs a different format.
/* Write the input images to files. We must always make copies of the
* files, even if this image is a disc file already, in case the
* command needs a different format.
*/
if( system->in ) {
char *in_format = system->in_format ?
system->in_format : "%s.tif";
VipsImage **in = (VipsImage **) system->in->data;
if( !(system->in_name = vips__temp_name( in_format )) )
return( -1 );
if( vips_image_write_to_file( system->in, system->in_name ) )
return( -1 );
system->delete_on_close = TRUE;
if( !(system->in_name = VIPS_ARRAY( object,
system->in->n, char * )) )
return( -1 );
memset( system->in_name, 0, system->in->n * sizeof( char * ) );
for( i = 0; i < system->in->n; i++ ) {
if( !(system->in_name[i] =
vips__temp_name( in_format )) )
return( -1 );
if( vips_image_write_to_file( in[i],
system->in_name[i] ) )
return( -1 );
}
}
/* Make the output filename.
@ -138,16 +153,19 @@ vips_system_build( VipsObject *object )
!(system->out_name = vips__temp_name( system->out_format )) )
return( -1 );
if( system->in_name ) {
if( !(fp = vips_popenf( system->cmd_format, "r",
system->in_name, system->out_name )) )
return( -1 );
}
else {
if( !(fp = vips_popenf( system->cmd_format, "r",
system->out_name )) )
return( -1 );
}
vips_strncpy( cmd, system->cmd_format, VIPS_PATH_MAX );
if( system->in )
for( i = 0; i < system->in->n; i++ )
if( vips__substitute( class->nickname,
cmd, VIPS_PATH_MAX, system->in_name[i] ) )
return( -1 );
if( system->out_name &&
vips__substitute( class->nickname,
cmd, VIPS_PATH_MAX, system->out_name ) )
return( -1 );
if( !(fp = vips_popenf( "%s", "r", cmd )) )
return( -1 );
while( fgets( line, VIPS_PATH_MAX, fp ) )
if( !vips_buf_appends( &buf, line ) )
@ -161,13 +179,12 @@ vips_system_build( VipsObject *object )
return( -1 );
}
if( system->out_format ) {
if( system->out_name ) {
VipsImage *out;
if( !(out = vips_image_new_from_file( system->out_name )) )
return( -1 );
vips_image_set_delete_on_close( out, TRUE );
g_object_set( system, "out", out, NULL );
}
@ -188,11 +205,12 @@ vips_system_class_init( VipsSystemClass *class )
vobject_class->description = _( "run an external command" );
vobject_class->build = vips_system_build;
VIPS_ARG_IMAGE( class, "in", 0,
VIPS_ARG_BOXED( class, "in", 0,
_( "Input" ),
_( "Input image" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsSystem, in ) );
_( "Array of input images" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsSystem, in ),
VIPS_TYPE_ARRAY_IMAGE );
VIPS_ARG_IMAGE( class, "out", 1,
_( "Output" ),
@ -242,21 +260,20 @@ vips_system_init( VipsSystem *system )
*
* Optional arguments:
*
* @in: input image
* @in: array of input images
* @out: output image
* @in_format: write input file like this
* @in_format: write input files like this
* @out_format: write output filename like this
* @log: stdout of command is returned here
*
* vips_system() runs a command, optionally passing an image in and
* optionally getting an image
* back. The command's stdout is returned in @log.
* vips_system() runs a command, optionally passing a set of images in and
* optionally getting an image back. The command's stdout is returned in @log.
*
* First, if @in is set, it is written to a file. The filename is formed by
* substituting something like "vips-49857-1" for the first %%s in @in_format,
* then
* prepending "/tmp". If the environment variable TMPDIR is defined, it
* can be used to set a different temporary directory.
* First, if @in is set, the array of images are written to files. The
* filenames are formed by substituting something like "vips-49857-1" for
* the first %%s in @in_format, then prepending "/tmp". If the environment
* variable TMPDIR is defined, it can be used to set a different temporary
* directory.
*
* On Windows, if the environment variable TMPDIR is not defined, VIPS calls
* GetTempPath() to get the user's preferred temporary area. If that fails, it
@ -268,16 +285,17 @@ vips_system_init( VipsSystem *system )
*
* If @out_format is set, an output filename is formed in the same way.
*
* The
* command string to run is made by substituting the first %%s in @cmd_format
* for the name of the input file, if @in is set, and the second %%s for the
* output filename, if set.
* The command string to run is made by substituting the first set of %%s
* in @cmd_format for the names of the input files, if @in is set, and then
* the next %%s for the output filename, if @out_format is set.
* You can put a number between the %% and the s to change the order in which
* the substitution occurs.
*
* The command is executed with popen() and the output captured in @log.
*
* After the command finishes, if @out_format is set, the output image is
* opened and returned in @out.
* Closing the output image will automatically delete the output file.
* Closing @out image will automatically delete the output file.
*
* Finally the input images are deleted.
*
@ -285,7 +303,7 @@ vips_system_init( VipsSystem *system )
* image, using JPEG files to pass images into and out of the convert command.
*
* |[
* VipsImage *in;
* VipsArrayImage *in;
* VipsImage *out;
* char *log;
*
@ -313,6 +331,3 @@ vips_system( const char *cmd_format, ... )
return( result );
}

View File

@ -1673,3 +1673,76 @@ vips_enum_from_nick( const char *domain, GType type, const char *nick )
return( -1 );
}
#define BIGBUF (10000)
/* Scan @buf for the first "%ns" (eg. "%12s") and substitute the
* lowest-numbered one for @sub. @buf is @len bytes
* in size.
*
* If there are no %ns, use the first %s.
*/
int
vips__substitute( const char *domain, char *buf, size_t len, char *sub )
{
size_t buflen = strlen( buf );
size_t sublen = strlen( sub );
int lowest_n;
char *sub_start;
char *p;
char *sub_end;
size_t before_len, marker_len, after_len, final_len;
g_assert( buflen < len );
lowest_n = -1;
sub_start = NULL;
for( p = buf; (p = strchr( p, '%' )); p++ )
if( isdigit( p[1] ) ) {
char *q;
for( q = p + 1; isdigit( *q ); q++ )
;
if( q[0] == 's' ) {
int n;
n = atoi( p + 1 );
if( lowest_n == -1 ||
n < lowest_n ) {
sub_start = p;
sub_end = q + 1;
}
}
}
if( !sub_start )
for( p = buf; (p = strchr( p, '%' )); p++ )
if( p[1] == 's' ) {
sub_start = p;
sub_end = p + 2;
break;
}
if( !sub_start ) {
vips_error( domain,
"%s", _( "string contains no substitute marker" ) );
return( -1 );
}
before_len = sub_start - buf;
marker_len = sub_end - sub_start;
after_len = buflen - (before_len + marker_len);
final_len = before_len + sublen + after_len + 1;
if( final_len > len ) {
vips_error( domain,
"%s", _( "not enough space to substitute" ) );
return( -1 );
}
memmove( buf + before_len + sublen, buf + before_len + marker_len,
after_len + 1 );
memmove( buf + before_len, sub, sublen );
return( 0 );
}

View File

@ -0,0 +1,89 @@
#include "vipsmarshal.h"
#include <glib-object.h>
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
#define g_marshal_value_peek_int(v) g_value_get_int (v)
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
#define g_marshal_value_peek_long(v) g_value_get_long (v)
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
#define g_marshal_value_peek_float(v) g_value_get_float (v)
#define g_marshal_value_peek_double(v) g_value_get_double (v)
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v) g_value_get_param (v)
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
#define g_marshal_value_peek_object(v) g_value_get_object (v)
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
* Do not access GValues directly in your code. Instead, use the
* g_value_get_*() functions
*/
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
/* INT:VOID (vipsmarshal.list:25) */
void
vips_INT__VOID (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{
typedef gint (*GMarshalFunc_INT__VOID) (gpointer data1,
gpointer data2);
register GMarshalFunc_INT__VOID callback;
register GCClosure *cc = (GCClosure*) closure;
register gpointer data1, data2;
gint v_return;
g_return_if_fail (return_value != NULL);
g_return_if_fail (n_param_values == 1);
if (G_CCLOSURE_SWAP_DATA (closure))
{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}
else
{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}
callback = (GMarshalFunc_INT__VOID) (marshal_data ? marshal_data : cc->callback);
v_return = callback (data1,
data2);
g_value_set_int (return_value, v_return);
}

View File

@ -0,0 +1,20 @@
#ifndef __vips_MARSHAL_H__
#define __vips_MARSHAL_H__
#include <glib-object.h>
G_BEGIN_DECLS
/* INT:VOID (vipsmarshal.list:25) */
extern void vips_INT__VOID (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
G_END_DECLS
#endif /* __vips_MARSHAL_H__ */

View File

@ -0,0 +1,27 @@
# see glib-genmarshal(1) for a detailed description of the file format,
# possible parameter types are:
# VOID indicates no return type, or no extra
# parameters. if VOID is used as the parameter
# list, no additional parameters may be present.
# BOOLEAN for boolean types (gboolean)
# CHAR for signed char types (gchar)
# UCHAR for unsigned char types (guchar)
# INT for signed integer types (gint)
# UINT for unsigned integer types (guint)
# LONG for signed long integer types (glong)
# ULONG for unsigned long integer types (gulong)
# ENUM for enumeration types (gint)
# FLAGS for flag enumeration types (guint)
# FLOAT for single-precision float types (gfloat)
# DOUBLE for double-precision float types (gdouble)
# STRING for string types (gchar*)
# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
# POINTER for anonymous pointer types (gpointer)
# PARAM for GParamSpec or derived types (GParamSpec*)
# OBJECT for GObject or derived types (GObject*)
# NONE deprecated alias for VOID
# BOOL deprecated alias for BOOLEAN
INT: VOID