diff --git a/ChangeLog b/ChangeLog index fd6dd28f..81ee057f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,7 @@ repeating stuff - im_flood() and friends rewritten, typically 4x faster - removed --with-cimg option, added --disable-cxx +- added im_system_image() (thanks Roland) 26/11/09 started 7.20.3 - updated en_GB.po translation diff --git a/TODO b/TODO index 6334fbaa..ddef6b05 100644 --- a/TODO +++ b/TODO @@ -16,25 +16,6 @@ for swapping ... they are asm macros so we should see a speedup -Something like: - -const char *im_system_image( IMAGE *in, const char *command, char **log ); - -Actions: - -- create two empty temporary files -- write the image to the first -- call system() on the expanded command -- capture stdout/stderr into log -- delete the temp input file -- return the output filename, or NULL if the command failed (log is still - returned in this case) - - The caller would open the output file, either with im_open(), or with it's - own system (nip2 has it's own open file thing to give progress feedback and - use disc for format conversion), and be responsible for deleting the temp - output file at some point. - - can we use conv_sep to speed up the memuse benchmarks? - move im_shrink & friends to resample? diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index badf0377..4a0fe689 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -32,6 +32,7 @@ libconversion_la_SOURCES = \ im_scaleps.c \ im_subsample.c \ im_system.c \ + im_system_image.c \ im_tbjoin.c \ im_text.c \ im_gaussnoise.c \ diff --git a/libvips/conversion/conver_dispatch.c b/libvips/conversion/conver_dispatch.c index a98d8894..fe234dcc 100644 --- a/libvips/conversion/conver_dispatch.c +++ b/libvips/conversion/conver_dispatch.c @@ -73,7 +73,7 @@ system_vec( im_object *argv ) return( 0 ); } -static im_arg_desc system_arg_types[] = { +static im_arg_desc system_args[] = { IM_INPUT_IMAGE( "im" ), IM_INPUT_STRING( "command" ), IM_OUTPUT_STRING( "output" ) @@ -84,8 +84,43 @@ static im_function system_desc = { "run command on image", /* Description */ 0, /* Flags */ system_vec, /* Dispatch function */ - IM_NUMBER( system_arg_types ), /* Size of arg list */ - system_arg_types /* Arg list */ + IM_NUMBER( system_args ), /* Size of arg list */ + system_args /* Arg list */ +}; + +static int +system_image_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + char *in_format = argv[1]; + char *out_format = argv[2]; + char *cmd = argv[3]; + char **log = (char **) &argv[4]; + char **out_file = (char **) &argv[5]; + + *out_file = im_system_image( in, in_format, out_format, cmd, log ); + if( !*out_file ) + *out_file = im_strdup( NULL, "" ); + + return( 0 ); +} + +static im_arg_desc system_image_args[] = { + IM_INPUT_IMAGE( "im" ), + IM_INPUT_STRING( "in_format" ), + IM_INPUT_STRING( "out_format" ), + IM_INPUT_STRING( "command" ), + IM_OUTPUT_STRING( "log" ), + IM_OUTPUT_STRING( "out_file" ) +}; + +static im_function system_image_desc = { + "im_system_image", /* Name */ + "run command on image, with image output",/* Description */ + 0, /* Flags */ + system_image_vec, /* Dispatch function */ + IM_NUMBER( system_image_args ), /* Size of arg list */ + system_image_args /* Arg list */ }; static int @@ -1433,6 +1468,7 @@ static im_function *conv_list[] = { &scaleps_desc, &subsample_desc, &system_desc, + &system_image_desc, &tbjoin_desc, &text_desc, &vips2mask_desc, diff --git a/libvips/conversion/im_system.c b/libvips/conversion/im_system.c index 638ca9c8..a9edd45b 100644 --- a/libvips/conversion/im_system.c +++ b/libvips/conversion/im_system.c @@ -68,27 +68,6 @@ #define pclose(f) _pclose(f) #endif /*OS_WIN32*/ -/* Do popen(), with printf-style args. - */ -static FILE * -popenf( const char *fmt, const char *mode, ... ) -{ - va_list args; - char buf[IM_MAX_STRSIZE]; - FILE *fp; - - va_start( args, mode ); - (void) im_vsnprintf( buf, IM_MAX_STRSIZE, fmt, args ); - va_end( args ); - - if( !(fp = popen( buf, mode )) ) { - im_error( "popenf", "%s", strerror( errno ) ); - return( NULL ); - } - - return( fp ); -} - /* Run a command on an IMAGE ... copy to tmp (if necessary), run * command on it, unlink (if we copied), return stdout from command. */ @@ -109,12 +88,11 @@ im_system( IMAGE *im, const char *cmd, char **out ) } im_close( disc ); } - else if( (fp = popenf( cmd, "r", im->filename )) ) { + else if( (fp = im_popenf( cmd, "r", im->filename )) ) { char line[IM_MAX_STRSIZE]; - VipsBuf buf; - char str[IM_MAX_STRSIZE]; + char txt[IM_MAX_STRSIZE]; + VipsBuf buf = VIPS_BUF_STATIC( txt ); - vips_buf_init_static( &buf, str, IM_MAX_STRSIZE ); while( fgets( line, IM_MAX_STRSIZE, fp ) ) if( !vips_buf_appends( &buf, line ) ) break; diff --git a/libvips/conversion/im_system_image.c b/libvips/conversion/im_system_image.c new file mode 100644 index 00000000..45a48743 --- /dev/null +++ b/libvips/conversion/im_system_image.c @@ -0,0 +1,155 @@ +/* im_system_image(): run a command on an image, get an image result + * + * 8/1/09 + * - from im_system() + */ + +/* + + This file is part of VIPS. + + VIPS 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 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAX_STRSIZE (4096) + +#ifdef OS_WIN32 +#define popen(b,m) _popen(b,m) +#define pclose(f) _pclose(f) +#endif /*OS_WIN32*/ + +static int +system_image( IMAGE *im, + const char *in_name, const char *out_name, const char *cmd_format, + char **log ) +{ + IMAGE *disc; + FILE *fp; + char line[IM_MAX_STRSIZE]; + char txt[IM_MAX_STRSIZE]; + VipsBuf buf = VIPS_BUF_STATIC( txt ); + int result; + + if( !(disc = im_open( in_name, "w" )) ) + return( -1 ); + if( im_copy( im, disc ) ) { + im_close( im ); + g_unlink( in_name ); + + return( -1 ); + } + im_close( im ); + + if( !(fp = im_popenf( cmd_format, "r", in_name, out_name )) ) { + g_unlink( in_name ); + + return( -1 ); + } + + while( fgets( line, IM_MAX_STRSIZE, fp ) ) + if( !vips_buf_appends( &buf, line ) ) + break; + + result = pclose( fp ); + + g_unlink( in_name ); + + if( log ) + *log = im_strdup( NULL, vips_buf_all( &buf ) ); + + return( result ); +} + +/* + + Run a command on an image, returning a new image. + + "mycommand --dostuff %s -o %s" + + have separate format strings for input and output? + + "%s.jpg" + + Actions: + +- create two empty temporary files +- write the image to the first +- call system() on the expanded command +- capture stdout/stderr into log +- delete the temp input file +- return the output filename, or NULL if the command failed (log is still + set in this case) + + The caller would open the output file, either with im_open(), or with it's + own system (nip2 has it's own open file thing to give progress feedback and + use disc for format conversion), and be responsible for deleting the temp + output file at some point. + + */ + +char * +im_system_image( IMAGE *im, + const char *in_format, const char *out_format, const char *cmd_format, + char **log ) +{ + char *in_name; + char *out_name; + + if( log ) + *log = NULL; + + in_name = im__temp_name( in_format ); + out_name = im__temp_name( in_format ); + + if( !in_name || + !out_name || + system_image( im, in_name, out_name, cmd_format, log ) ) { + g_free( in_name ); + g_free( out_name ); + + return( NULL ); + } + g_free( in_name ); + + return( out_name ); +} diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index f48a6664..dd538ec8 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -111,6 +111,9 @@ int im_subsample( IMAGE *in, IMAGE *out, int x, int y ); int im_zoom( IMAGE *in, IMAGE *out, int x, int y ); int im_system( IMAGE *im, const char *cmd, char **out ); +char *im_system_image( IMAGE *im, + const char *in_format, const char *out_format, const char *cmd_format, + char **log ); #ifdef __cplusplus } diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index bea6b016..949ec036 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -248,10 +248,13 @@ const char *vips__token_need( const char *buffer, VipsToken need_token, int im_existsf( const char *name, ... ) __attribute__((format(printf, 1, 2))); +FILE *im_popenf( const char *fmt, const char *mode, ... ) + __attribute__((format(printf, 1, 3))); int im_ispoweroftwo( int p ); int im_isvips( const char *filename ); int im_amiMSBfirst( void ); +char *im__temp_name( const char *format ); IMAGE *im__open_temp( void ); int im_bits_of_fmt( VipsBandFmt fmt ); diff --git a/libvips/include/vips/vips.h b/libvips/include/vips/vips.h index 2e3aa428..1abb78c5 100644 --- a/libvips/include/vips/vips.h +++ b/libvips/include/vips/vips.h @@ -82,6 +82,7 @@ extern "C" { #endif /*__cplusplus*/ #include +#include #include #include diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index 606b9f28..bbbd40a8 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -28,8 +28,8 @@ */ /* -#define DEBUG */ +#define DEBUG #ifdef HAVE_CONFIG_H #include @@ -860,7 +860,7 @@ im__file_read( FILE *fp, const char *name, unsigned int *length_out ) } while( !feof( fp ) ); #ifdef DEBUG - printf( "read %d bytes from unseekable stream\n", len ); + printf( "read %ld bytes from unseekable stream\n", len ); #endif /*DEBUG*/ } else { @@ -1413,6 +1413,36 @@ im_existsf( const char *name, ... ) return( 0 ); } +#ifdef OS_WIN32 +#define popen(b,m) _popen(b,m) +#define pclose(f) _pclose(f) +#endif /*OS_WIN32*/ + +/* Do popen(), with printf-style args. + */ +FILE * +im_popenf( const char *fmt, const char *mode, ... ) +{ + va_list args; + char buf[4096]; + FILE *fp; + + va_start( args, mode ); + (void) im_vsnprintf( buf, 4096, fmt, args ); + va_end( args ); + +#ifdef DEBUG + printf( "im_popenf: running: %s\n", buf ); +#endif /*DEBUG*/ + + if( !(fp = popen( buf, mode )) ) { + im_error( "popenf", "%s", strerror( errno ) ); + return( NULL ); + } + + return( fp ); +} + /* True if an int is a power of two ... 1, 2, 4, 8, 16, 32, etc. Do with just * integer arithmetic for portability. A previous Nicos version using doubles * and log/log failed on x86 with rounding problems. Return 0 for not @@ -1478,30 +1508,55 @@ im_amiMSBfirst( void ) return( 1 ); } +/* Make a temporary file name. The format parameter is something like "%s.jpg" + * and will be expanded to something like "/tmp/vips-12-34587.jpg". + * + * You need to free the result. A real file will also be created, though we + * delete it for you. + */ +char * +im__temp_name( const char *format ) +{ + static int serial = 1; + + const char *tmpd; + char file[FILENAME_MAX]; + char file2[FILENAME_MAX]; + + char *name; + int fd; + + if( !(tmpd = g_getenv( "TMPDIR" )) ) + tmpd = "/tmp"; + + im_snprintf( file, FILENAME_MAX, "vips-%d-XXXXXX", serial++ ); + im_snprintf( file2, FILENAME_MAX, format, file ); + name = g_build_filename( tmpd, file2, NULL ); + + if( (fd = g_mkstemp( name )) == -1 ) { + im_error( "tempfile", + _( "unable to make temporary file %s" ), name ); + g_free( name ); + return( NULL ); + } + close( fd ); + g_unlink( name ); + + return( name ); +} + /* Make a disc IMAGE which will be automatically unlinked on im_close(). */ IMAGE * im__open_temp( void ) { - const char *tmpd; char *name; - int fd; IMAGE *disc; - if( !(tmpd = g_getenv( "TMPDIR" )) ) - tmpd = "/tmp"; - name = g_build_filename( tmpd, "vips_XXXXXX.v", NULL ); - - if( (fd = g_mkstemp( name )) == -1 ) { - im_error( "tempfile", - _( "unable to make temp file %s" ), name ); - g_free( name ); + if( !(name = im__temp_name( "%s.v" )) ) return( NULL ); - } - close( fd ); if( !(disc = im_open( name, "w" )) ) { - unlink( name ); g_free( name ); return( NULL ); } @@ -1510,7 +1565,7 @@ im__open_temp( void ) if( im_add_close_callback( disc, (im_callback_fn) unlink, disc->filename, NULL ) ) { im_close( disc ); - unlink( name ); + g_unlink( name ); } return( disc );