From bc84d2ce57d3a17caae342f4df8c87e6c78d1af2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 7 Nov 2007 16:53:01 +0000 Subject: [PATCH] new progress feedback system --- ChangeLog | 7 ++- TODO | 4 +- include/vips/internal.h | 17 +++++++ include/vips/proto.h | 6 +++ include/vips/threadgroup.h | 11 ----- include/vips/vips.h | 13 ++++- libsrc/conversion/im_clip.c | 21 ++++++-- libsrc/conversion/im_copy.c | 2 +- libsrc/convolution/im_conv.c | 37 ++++++++++---- libsrc/convolution/im_convf.c | 11 +++-- libsrc/histograms_lut/im_maplut.c | 24 +++++++--- libsrc/iofuncs/callback.c | 18 +++++-- libsrc/iofuncs/im_close.c | 15 ++++-- libsrc/iofuncs/im_generate.c | 35 ++++++++------ libsrc/iofuncs/im_init.c | 5 ++ libsrc/iofuncs/im_init_world.c | 12 +++-- libsrc/iofuncs/im_iterate.c | 19 ++++++-- libsrc/iofuncs/im_open.c | 80 +++++++++++++++++++++++++++++-- libsrc/iofuncs/im_printdesc.c | 4 ++ libsrc/iofuncs/im_setupout.c | 1 + libsrc/iofuncs/im_wbuffer.c | 19 +++++--- libsrc/iofuncs/im_writeline.c | 33 +++++++++---- libsrc/iofuncs/time.c | 63 ++++++++++++++++++++---- libsrc/mosaicing/global_balance.c | 2 +- 24 files changed, 359 insertions(+), 100 deletions(-) diff --git a/ChangeLog b/ChangeLog index 20f6e5a2..fc499ddf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,8 +4,11 @@ - break im_wbuffer() out to a separate API - use im_wbuffer() to make im_vips2jpeg() compress in the background - also im_vips2png(), im_vips2tiff(), im_vips2ppm() -- new system for propogating progress settings down pipelines unbreaks save - feedback in nip2 +- revised evaluation progress system +- new evalstart/evalend/preclose callbacks fix over/underflow reporting +- but the meaning of evalend has changed in a non-backwards-compatible way :( + use preclose instead ito get the old behaviour +- added "--vips-progress" flag to turn on a simple eval progress tracker 28/9/07 started 7.13.1 - vips2dj can print RGB images diff --git a/TODO b/TODO index 807b611a..d74bb929 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,9 @@ -- test ppm writer +- update docs, add pages for new cbs - talk about new progress system in im_add_eval_callback()? +- test ppm writer + - missing libstdc++ in link? what if we configure with no openexr? added -lstdc++ to VIPS_LIBS, but will this work on solaris etc.? maybe have diff --git a/include/vips/internal.h b/include/vips/internal.h index 0db0c601..9d9583b4 100644 --- a/include/vips/internal.h +++ b/include/vips/internal.h @@ -37,6 +37,21 @@ extern "C" { #endif /*__cplusplus*/ +/* Default tile geometry. + */ +extern int im__tile_width; +extern int im__tile_height; +extern int im__fatstrip_height; +extern int im__thinstrip_height; + +/* Default n threads. + */ +extern int im__concurrency; + +/* Give progress feedback. + */ +extern int im__progress; + typedef int (*im__fftproc_fn)( IMAGE *, IMAGE *, IMAGE * ); /* iofuncs @@ -53,7 +68,9 @@ void *im__read_extension_block( IMAGE *im, int *size ); int im__readhist( IMAGE *image ); int im__write_extension_block( IMAGE *im, void *buf, int size ); int im__writehist( IMAGE *image ); +int im__start_eval( IMAGE *im ); int im__handle_eval( IMAGE *im, int w, int h ); +int im__end_eval( IMAGE *im ); int im__time_destroy( IMAGE *im ); extern int im__read_test; diff --git a/include/vips/proto.h b/include/vips/proto.h index 2c86bd46..6d11060c 100644 --- a/include/vips/proto.h +++ b/include/vips/proto.h @@ -74,6 +74,10 @@ typedef void *(*im_header_map_fn)( IMAGE *, const char *, GValue *, void * ); int im_init_world( const char *argv0 ); GOptionGroup *im_get_option_group( void ); +/* Turn progress feedback on and off. + */ +void im_progress_set( int progress ); + const char *im_error_buffer( void ); int im_debugim( IMAGE * ); int im_printlines( IMAGE * ); @@ -149,6 +153,8 @@ int im_ismagick( const char * ); int im_isanalyze( const char *filename ); int im_add_close_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_preclose_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_evalstart_callback( IMAGE *, im_callback_fn, void *, void * ); int im_add_eval_callback( IMAGE *, im_callback_fn, void *, void * ); int im_add_evalend_callback( IMAGE *, im_callback_fn, void *, void * ); diff --git a/include/vips/threadgroup.h b/include/vips/threadgroup.h index 9c6359b8..e27b59b1 100644 --- a/include/vips/threadgroup.h +++ b/include/vips/threadgroup.h @@ -47,17 +47,6 @@ extern "C" { */ #define IM__DEFAULT_STACK_SIZE (2 * 1024 * 1024) -/* Default tile geometry. - */ -extern int im__tile_width; -extern int im__tile_height; -extern int im__fatstrip_height; -extern int im__thinstrip_height; - -/* Default n threads. - */ -extern int im__concurrency; - /* A work function. */ typedef int (*im__work_fn)( REGION *, void *, void *, void * ); diff --git a/include/vips/vips.h b/include/vips/vips.h index dbf36932..0ca40009 100644 --- a/include/vips/vips.h +++ b/include/vips/vips.h @@ -35,6 +35,9 @@ * - added RGB16, GREY16 * 30/10/06 * - added im_window_t + * 7/11/07 + * - added preclose and evalstart callbacks + * - brought time struct in here */ /* @@ -236,12 +239,13 @@ typedef struct { */ typedef struct { struct im__IMAGE *im; /* Image we are part of */ - GTimer *start; /* Start time */ + time_t unused; /* FIXME ... for compatibility */ int run; /* Time we have been running */ int eta; /* Estimated seconds of computation left */ gint64 tpels; /* Number of pels we expect to calculate */ gint64 npels; /* Number of pels calculated so far */ int percent; /* Percent complete */ + GTimer *start; /* Start time */ } im_time_t; /* Image descriptor for subroutine i/o args @@ -303,7 +307,7 @@ typedef struct im__IMAGE { GSList *Meta_traverse; /* Traverse order for Meta */ /* Part of mmap() read ... the sizeof() the header we skip from the - * file start. Usually IM_SIZEOF_HEADER, but can be soomething else + * file start. Usually IM_SIZEOF_HEADER, but can be something else * for binary file read. */ int sizeof_header; @@ -330,6 +334,11 @@ typedef struct im__IMAGE { /* The IMAGE (if any) we should signal eval progress on. */ struct im__IMAGE *progress; + + /* Some more callbacks. Appended to IMAGE for binary compatibility. + */ + GSList *evalstartfns; /* list of start eval callbacks */ + GSList *preclosefns; /* list of pre-close callbacks */ } IMAGE; /* Only define if IM_ENABLE_DEPRECATED is set. diff --git a/libsrc/conversion/im_clip.c b/libsrc/conversion/im_clip.c index 16ff029b..28f93700 100644 --- a/libsrc/conversion/im_clip.c +++ b/libsrc/conversion/im_clip.c @@ -43,6 +43,8 @@ * 21/4/04 JC * - now does floor(), not rint() ... you'll need to round yourself * before calling this if you want round-to-nearest + * 7/11/07 + * - use new evalstart/evalend system */ /* @@ -100,7 +102,18 @@ typedef struct { } Clip; static int -clip_destroy( Clip *clip ) +clip_evalstart( Clip *clip ) +{ + /* Reset counts. + */ + clip->overflow = 0; + clip->underflow = 0; + + return( 0 ); +} + +static int +clip_evalend( Clip *clip ) { /* Print warnings, if necessary. */ @@ -128,8 +141,10 @@ clip_new( IMAGE *in, IMAGE *out, int ofmt ) clip->underflow = 0; clip->overflow = 0; - if( im_add_close_callback( out, - (im_callback_fn) clip_destroy, clip, NULL ) ) + if( im_add_evalstart_callback( out, + (im_callback_fn) clip_evalstart, clip, NULL ) || + im_add_evalend_callback( out, + (im_callback_fn) clip_evalend, clip, NULL ) ) return( NULL ); return( clip ); diff --git a/libsrc/conversion/im_copy.c b/libsrc/conversion/im_copy.c index d712f8a6..acaf6a77 100644 --- a/libsrc/conversion/im_copy.c +++ b/libsrc/conversion/im_copy.c @@ -352,7 +352,7 @@ im__convert_saveable( IMAGE *in, gboolean allow_alpha ) { IMAGE *out; - if( !(out = im_open( "im__convert_saveable", "p" )) ) + if( !(out = im_open( "convert-for-save", "p" )) ) return( NULL ); /* If this is a IM_CODING_LABQ, we can go straight to RGB. diff --git a/libsrc/convolution/im_conv.c b/libsrc/convolution/im_conv.c index 02b232da..7e5040c6 100644 --- a/libsrc/convolution/im_conv.c +++ b/libsrc/convolution/im_conv.c @@ -59,6 +59,8 @@ * - sets Xoffset / Yoffset * 11/11/05 * - simpler inner loop avoids gcc4 bug + * 7/11/07 + * - new evalstart/end callbacks */ /* @@ -119,19 +121,34 @@ typedef struct { } Conv; static int -conv_destroy( Conv *conv ) +conv_close( Conv *conv ) +{ + IM_FREEF( im_free_imask, conv->mask ); + + return( 0 ); +} + +static int +conv_evalstart( Conv *conv ) +{ + /* Reset underflow/overflow count. + */ + conv->overflow = 0; + conv->underflow = 0; + + return( 0 ); +} + +static int +conv_evalend( Conv *conv ) { /* Print underflow/overflow count. */ if( conv->overflow || conv->underflow ) - im_warning( "im_conv: %d overflows and %d underflows detected", + im_warn( "im_conv", + _( "%d overflows and %d underflows detected" ), conv->overflow, conv->underflow ); - if( conv->mask ) { - (void) im_free_imask( conv->mask ); - conv->mask = NULL; - } - return( 0 ); } @@ -154,7 +171,11 @@ conv_new( IMAGE *in, IMAGE *out, INTMASK *mask ) conv->overflow = 0; if( im_add_close_callback( out, - (im_callback_fn) conv_destroy, conv, NULL ) || + (im_callback_fn) conv_close, conv, NULL ) || + im_add_close_callback( out, + (im_callback_fn) conv_evalstart, conv, NULL ) || + im_add_close_callback( out, + (im_callback_fn) conv_evalend, conv, NULL ) || !(conv->coeff = IM_ARRAY( out, ne, int )) || !(conv->mask = im_dup_imask( mask, "conv_mask" )) ) return( NULL ); diff --git a/libsrc/convolution/im_convf.c b/libsrc/convolution/im_convf.c index 3c74bf5b..aa61b269 100644 --- a/libsrc/convolution/im_convf.c +++ b/libsrc/convolution/im_convf.c @@ -92,7 +92,7 @@ typedef struct { } Conv; static int -conv_destroy( Conv *conv ) +conv_close( Conv *conv ) { if( conv->mask ) { (void) im_free_dmask( conv->mask ); @@ -119,7 +119,7 @@ conv_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) conv->coeff = NULL; if( im_add_close_callback( out, - (im_callback_fn) conv_destroy, conv, NULL ) || + (im_callback_fn) conv_close, conv, NULL ) || !(conv->coeff = IM_ARRAY( out, ne, double )) || !(conv->mask = im_dup_dmask( mask, "conv_mask" )) ) return( NULL ); @@ -285,13 +285,14 @@ im_convf_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) /* Check parameters. */ if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { - im_errormsg( "im_convf: input non-complex uncoded please!"); + im_error( "im_convf", + _( "non-complex uncoded only" ) ); return( -1 ); } if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || mask->scale == 0 ) { - im_errormsg( "im_convf: nonsense mask parameters" ); + im_error( "im_convf", _( "nonsense mask parameters" ) ); return( -1 ); } if( im_piocheck( in, out ) ) @@ -311,7 +312,7 @@ im_convf_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) out->Xsize -= mask->xsize - 1; out->Ysize -= mask->ysize - 1; if( out->Xsize <= 0 || out->Ysize <= 0 ) { - im_errormsg( "im_convf: image too small for mask" ); + im_error( "im_convf", _( "image too small for mask" ) ); return( -1 ); } diff --git a/libsrc/histograms_lut/im_maplut.c b/libsrc/histograms_lut/im_maplut.c index b9ce291a..2430d0cf 100644 --- a/libsrc/histograms_lut/im_maplut.c +++ b/libsrc/histograms_lut/im_maplut.c @@ -47,7 +47,9 @@ * - small speed ups * 30/6/04 * - heh, 1 band image + 3 band lut + >8bit output has been broken for 9 - * years :-) + * years :-) + * 7/11/07 + * - new eval start/end system */ /* @@ -103,16 +105,22 @@ typedef struct { int overflow; /* Number of overflows for non-uchar lut */ } LutInfo; +static int +lut_start( LutInfo *st ) +{ + st->overflow = 0; + + return( 0 ); +} + /* Print overflows, if any. */ static int -end_lut( LutInfo *st ) +lut_end( LutInfo *st ) { - if( st->overflow ) { + if( st->overflow ) im_warn( "im_maplut", _( "%d overflows detected" ), st->overflow ); - st->overflow = 0; - } return( 0 ); } @@ -139,8 +147,10 @@ build_luts( IMAGE *out, IMAGE *lut ) st->clp = st->sz - 1; st->overflow = 0; st->table = NULL; - if( im_add_evalend_callback( out, - (im_callback_fn) end_lut, st, NULL ) ) + if( im_add_evalstart_callback( out, + (im_callback_fn) lut_start, st, NULL ) || + im_add_evalend_callback( out, + (im_callback_fn) lut_end, st, NULL ) ) return( NULL ); /* Attach tables. diff --git a/libsrc/iofuncs/callback.c b/libsrc/iofuncs/callback.c index eec3f88b..2c5a73c7 100644 --- a/libsrc/iofuncs/callback.c +++ b/libsrc/iofuncs/callback.c @@ -80,16 +80,20 @@ add_callback( IMAGE *im, GSList **cblist, int (*fn)(), void *a, void *b ) return( 0 ); } -/* Add a close callback to an IMAGE. - */ int im_add_close_callback( IMAGE *im, int (*fn)(), void *a, void *b ) { return( add_callback( im, &im->closefns, fn, a, b ) ); } +int +im_add_preclose_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->preclosefns, fn, a, b ) ); +} + /* Add an eval callback to an IMAGE. You must call this after opening the - * image, but before using it as an argument to an operation. + * image but before using it as an argument to an operation. */ int im_add_eval_callback( IMAGE *im, int (*fn)(), void *a, void *b ) @@ -103,14 +107,18 @@ im_add_eval_callback( IMAGE *im, int (*fn)(), void *a, void *b ) return( add_callback( im, &im->evalfns, fn, a, b ) ); } -/* Add an eval end callback to an IMAGE. - */ int im_add_evalend_callback( IMAGE *im, int (*fn)(), void *a, void *b ) { return( add_callback( im, &im->evalendfns, fn, a, b ) ); } +int +im_add_evalstart_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->evalstartfns, fn, a, b ) ); +} + /* Perform a user callback. */ static void * diff --git a/libsrc/iofuncs/im_close.c b/libsrc/iofuncs/im_close.c index 9b74834c..8983d36a 100644 --- a/libsrc/iofuncs/im_close.c +++ b/libsrc/iofuncs/im_close.c @@ -52,6 +52,8 @@ * - call im__writehist() to send history to XML after image data * 3/1/07 * - free history_list + * 7/11/07 + * - added preclose, removed evalend triggers */ /* @@ -122,7 +124,9 @@ int im__close( IMAGE *im ) { - int result = 0; + int result; + + result = 0; /* No action for NULL image. */ @@ -133,6 +137,11 @@ im__close( IMAGE *im ) printf( "im__close: starting for %s ..\n", im->filename ); #endif /*DEBUG_IO*/ + /* Trigger all pre-close fns. + */ + result |= im__trigger_callbacks( im->preclosefns ); + IM_FREEF( im_slist_free_all, im->preclosefns ); + /* Free any regions defined on this image. This will, in turn, call * all stop functions still running, freeing all regions we have on * other images, etc. @@ -157,9 +166,9 @@ im__close( IMAGE *im ) /* Make sure all evalend functions have been called, perform all close * callbacks, and free eval callbacks. */ - result |= im__trigger_callbacks( im->evalendfns ); - IM_FREEF( im_slist_free_all, im->evalendfns ); + IM_FREEF( im_slist_free_all, im->evalstartfns ); IM_FREEF( im_slist_free_all, im->evalfns ); + IM_FREEF( im_slist_free_all, im->evalendfns ); result |= im__trigger_callbacks( im->closefns ); IM_FREEF( im_slist_free_all, im->closefns ); diff --git a/libsrc/iofuncs/im_generate.c b/libsrc/iofuncs/im_generate.c index e322d1f9..1148c92a 100644 --- a/libsrc/iofuncs/im_generate.c +++ b/libsrc/iofuncs/im_generate.c @@ -41,6 +41,8 @@ * - better how-many-pixels-calculated * 27/11/06 * - merge background write stuff + * 7/11/07 + * - new start/end eval callbacks */ /* @@ -284,12 +286,20 @@ eval_to_memory( im_threadgroup_t *tg, REGION *or ) { int y, chunk; IMAGE *im = or->im; + int result; + + result = 0; #ifdef DEBUG_IO int ntiles = 0; printf( "eval_to_memory: partial image output to memory area\n" ); #endif /*DEBUG_IO*/ + /* Signal start of eval. + */ + if( im__start_eval( im ) ) + return( -1 ); + /* Choose a chunk size ... 1/100th of the height of the image, about. * This sets the granularity of user feedback on eval progress, but * does not affect mem requirements etc. @@ -307,24 +317,28 @@ eval_to_memory( im_threadgroup_t *tg, REGION *or ) pos.top = y; pos.width = im->Xsize; pos.height = chunk; - if( im_region_image( or, &pos ) ) - return( -1 ); + if( (result = im_region_image( or, &pos )) ) + break; /* Ask for evaluation of this area. */ - if( eval_to_region( or, tg ) ) - return( -1 ); + if( (result = eval_to_region( or, tg )) ) + break; #ifdef DEBUG_IO ntiles++; #endif /*DEBUG_IO*/ } + /* Signal end of eval. + */ + result |= im__end_eval( im ); + #ifdef DEBUG_IO - printf( "eval_to_memory: success! %d patches written\n", ntiles ); + printf( "eval_to_memory: %d patches written\n", ntiles ); #endif /*DEBUG_IO*/ - return( 0 ); + return( result ); } /* A write function for VIPS images. Just write() the pixel data. @@ -413,7 +427,7 @@ im_generate( IMAGE *im, im->stop = stop; im->client1 = a; im->client2 = b; - + /* Evaluate. Two output styles: to memory area (im_setbuf() * or im_mmapinrw()) or to file (im_openout()). */ @@ -433,13 +447,6 @@ im_generate( IMAGE *im, im_threadgroup_free( tg ); im_region_free( or ); - /* Evaluation is now complete, with all sequences finished. - * Trigger evalend callbacks, then free them to make sure we - * don't trigger twice. - */ - res |= im__trigger_callbacks( im->evalendfns ); - IM_FREEF( im_slist_free_all, im->evalendfns ); - /* Error? */ if( res ) diff --git a/libsrc/iofuncs/im_init.c b/libsrc/iofuncs/im_init.c index 384acbb3..a917a098 100644 --- a/libsrc/iofuncs/im_init.c +++ b/libsrc/iofuncs/im_init.c @@ -27,6 +27,8 @@ * 2/1/07 * - init magic * - init history_list + * 7/11/07 + * - added preclose and evalstart */ /* @@ -159,6 +161,9 @@ im_init( const char *filename ) im->progress = NULL; + im->evalstartfns = NULL; + im->preclosefns = NULL; + if( !(im->filename = im_strdup( NULL, filename )) ) { im_close( im ); return( NULL ); diff --git a/libsrc/iofuncs/im_init_world.c b/libsrc/iofuncs/im_init_world.c index dbe4fd3c..fc2a8401 100644 --- a/libsrc/iofuncs/im_init_world.c +++ b/libsrc/iofuncs/im_init_world.c @@ -15,6 +15,8 @@ * 8/6/07 * - just warn if plugins fail to load correctly: too annoying to have * VIPS refuse to start because of a dodgy plugin + * 7/11/07 + * - progress feedback option */ /* @@ -190,16 +192,18 @@ im__ngettext( const char *msgid, const char *plural, unsigned long int n ) static GOptionEntry option_entries[] = { { "vips-concurrency", 'c', 0, G_OPTION_ARG_INT, &im__concurrency, N_( "evaluate with N concurrent threads" ), "N" }, - { "vips-tile-width", 'c', 0, G_OPTION_ARG_INT, &im__tile_width, + { "vips-tile-width", 'w', 0, G_OPTION_ARG_INT, &im__tile_width, N_( "set tile width to N (DEBUG)" ), "N" }, - { "vips-tile-height", 'c', 0, G_OPTION_ARG_INT, &im__tile_height, + { "vips-tile-height", 'h', 0, G_OPTION_ARG_INT, &im__tile_height, N_( "set tile height to N (DEBUG)" ), "N" }, - { "vips-thinstrip-height", 'c', 0, + { "vips-thinstrip-height", 't', 0, G_OPTION_ARG_INT, &im__thinstrip_height, N_( "set thinstrip height to N (DEBUG)" ), "N" }, - { "vips-fatstrip-height", 'c', 0, + { "vips-fatstrip-height", 'f', 0, G_OPTION_ARG_INT, &im__fatstrip_height, N_( "set fatstrip height to N (DEBUG)" ), "N" }, + { "vips-progress", 'p', 0, G_OPTION_ARG_NONE, &im__progress, + N_( "show progress feedback" ), NULL }, { NULL } }; diff --git a/libsrc/iofuncs/im_iterate.c b/libsrc/iofuncs/im_iterate.c index 2c357d1e..968f2912 100644 --- a/libsrc/iofuncs/im_iterate.c +++ b/libsrc/iofuncs/im_iterate.c @@ -21,6 +21,8 @@ * - read via a buffer image so we work with mmap window images * 27/11/06 * - merge threadgroup stuff + * 7/11/07 + * - new eval start/progress/end system */ /* @@ -199,12 +201,12 @@ im_iterate( IMAGE *im, { IMAGE *t; im_threadgroup_t *tg; - int res; + int result; if( im_image_sanity( im ) ) return( -1 ); - if( !(t = im_open( "im_iterate_buffer", "p" )) ) + if( !(t = im_open( "iterate", "p" )) ) return( -1 ); if( im_copy( im, t ) ) { im_close( t ); @@ -223,10 +225,19 @@ im_iterate( IMAGE *im, im_diagnostics( "im_iterate: using %d threads", tg->nthr ); #endif /*DEBUG_IO*/ - res = iterate( tg, t, start, generate, stop, b, c ); + /* Signal start of eval. + */ + if( im__start_eval( t ) ) + return( -1 ); + + result = iterate( tg, t, start, generate, stop, b, c ); + + /* Signal end of eval. + */ + result |= im__end_eval( t ); im_threadgroup_free( tg ); im_close( t ); - return( res ); + return( result ); } diff --git a/libsrc/iofuncs/im_open.c b/libsrc/iofuncs/im_open.c index 9b9a672e..44cf4c00 100644 --- a/libsrc/iofuncs/im_open.c +++ b/libsrc/iofuncs/im_open.c @@ -87,6 +87,9 @@ Modified: * 20/9/06 * - test for NULL filename/mode, common if you forget to check argc * (thanks bruno) + * 7/11/07 + * - use preclose, not evalend, for delayed save + * - add simple cmd-line progress feedback */ /* @@ -163,6 +166,16 @@ static const char *im_suffix_csv[] = { NULL }; +/* Progress feedback. Only really useful for testing, tbh. + */ +int im__progress = 0; + +void +im_progress_set( int progress ) +{ + im__progress = progress; +} + /* Open a VIPS image and byte-swap the image data if necessary. */ static IMAGE * @@ -213,7 +226,7 @@ read_vips( const char *filename ) } /* Delayed save: if we write to TIFF or to JPEG format, actually do the write - * to a "p" and on evalend do im_vips2tiff() or whatever. Track save + * to a "p" and on preclose do im_vips2tiff() or whatever. Track save * parameters here. */ typedef struct { @@ -222,7 +235,7 @@ typedef struct { char *filename; /* Save args */ } SaveBlock; -/* From evalend callback: invoke a delayed save. +/* From preclose callback: invoke a delayed save. */ static int invoke_sb( SaveBlock *sb ) @@ -246,7 +259,7 @@ attach_sb( IMAGE *out, int (*save_fn)(), const char *filename ) sb->save_fn = save_fn; sb->filename = im_strdup( out, filename ); - if( im_add_evalend_callback( out, + if( im_add_preclose_callback( out, (im_callback_fn) invoke_sb, (void *) sb, NULL ) ) return( -1 ); @@ -277,7 +290,7 @@ open_lazy_start( IMAGE *out, void *a, void *dummy ) OpenLazy *lazy = (OpenLazy *) a; if( !lazy->lazy_im ) { - if( !(lazy->lazy_im = im_open_local( out, "olstart", "p" )) || + if( !(lazy->lazy_im = im_open_local( out, "read", "p" )) || lazy->read_pixels( lazy->filename, lazy->lazy_im ) ) { IM_FREEF( im_close, lazy->lazy_im ); return( NULL ); @@ -349,6 +362,49 @@ open_sub( OpenLazyFn read_header, OpenLazyFn read_pixels, const char *filename ) return( im ); } +/* Progress feedback. + */ + +/* What we track during an eval. + */ +typedef struct { + IMAGE *im; + + int last_percent; /* The last %complete we displayed */ +} Progress; + +int +evalstart_cb( Progress *progress ) +{ + progress->last_percent = 0; + + return( 0 ); +} + +int +eval_cb( Progress *progress ) +{ + IMAGE *im = progress->im; + + if( im->time->percent != progress->last_percent ) { + printf( "%s: %d%% complete\r", + im->filename, im->time->percent ); + fflush( stdout ); + + progress->last_percent = im->time->percent; + } + + return( 0 ); +} + +int +evalend_cb( Progress *progress ) +{ + printf( "\n" ); + + return( 0 ); +} + IMAGE * im_open( const char *filename, const char *mode ) { @@ -478,7 +534,7 @@ im_open( const char *filename, const char *mode ) im = im_openout( filename ); else if( im_filename_suffix_match( filename, im_suffix_tiff ) ) { - /* TIFF write. Save to a partial, and on evalend + /* TIFF write. Save to a partial, and on preclose * im_vips2tiff from that. */ if( !(im = im_open( "im_open:vips2tiff:1", "p" )) ) @@ -550,6 +606,20 @@ im_open( const char *filename, const char *mode ) return( NULL ); } + /* Attach progress feedback, if required. + */ + if( im__progress ) { + Progress *progress = IM_NEW( im, Progress ); + + progress->im = im; + im_add_evalstart_callback( im, + (im_callback_fn) evalstart_cb, progress, NULL ); + im_add_eval_callback( im, + (im_callback_fn) eval_cb, progress, NULL ); + im_add_evalend_callback( im, + (im_callback_fn) evalend_cb, progress, NULL ); + } + #ifdef DEBUG_IO printf( "im_open: success for %s (%p)\n", im->filename, im ); #endif /*DEBUG_IO*/ diff --git a/libsrc/iofuncs/im_printdesc.c b/libsrc/iofuncs/im_printdesc.c index d865113b..bb9e9828 100644 --- a/libsrc/iofuncs/im_printdesc.c +++ b/libsrc/iofuncs/im_printdesc.c @@ -324,6 +324,10 @@ im_printdesc( IMAGE *image ) printf( "user eval callbacks attached\n" ); if( image->evalendfns ) printf( "user evalend callbacks attached\n" ); + if( image->evalstartfns ) + printf( "user evalstart callbacks attached\n" ); + if( image->preclosefns ) + printf( "user preclose callbacks attached\n" ); if( image->regions ) { printf( "%d regions present\n", g_slist_length( image->regions ) ); diff --git a/libsrc/iofuncs/im_setupout.c b/libsrc/iofuncs/im_setupout.c index a930b498..02c5737b 100644 --- a/libsrc/iofuncs/im_setupout.c +++ b/libsrc/iofuncs/im_setupout.c @@ -105,6 +105,7 @@ im_setupout( IMAGE *im ) printf( "im_setupout: old-style output for %s\n", im->filename ); #endif /*DEBUG_IO*/ + im->dtype = IM_SETBUF; } diff --git a/libsrc/iofuncs/im_wbuffer.c b/libsrc/iofuncs/im_wbuffer.c index f0857f7f..b4bc51c0 100644 --- a/libsrc/iofuncs/im_wbuffer.c +++ b/libsrc/iofuncs/im_wbuffer.c @@ -2,6 +2,8 @@ * * 2/11/07 * - cut from im_generate + * 7/11/07 + * - trigger start/end eval callbacks */ /* @@ -407,19 +409,22 @@ im_wbuffer( im_threadgroup_t *tg, im_wbuffer_fn write_fn, void *a, void *b ) { WriteBuffer *b1, *b2; + int result; + + if( im__start_eval( tg->im ) ) + return( -1 ); + + result = 0; b1 = wbuffer_new( tg, write_fn, a, b ); b2 = wbuffer_new( tg, write_fn, a, b ); - if( !b1 || !b2 || wbuffer_eval_to_file( b1, b2 ) ) { - IM_FREEF( wbuffer_free, b1 ); - IM_FREEF( wbuffer_free, b2 ); - - return( -1 ); - } + if( !b1 || !b2 || wbuffer_eval_to_file( b1, b2 ) ) + result = -1; + im__end_eval( tg->im ); wbuffer_free( b1 ); wbuffer_free( b2 ); - return( 0 ); + return( result ); } diff --git a/libsrc/iofuncs/im_writeline.c b/libsrc/iofuncs/im_writeline.c index 37325acd..6212dbf2 100644 --- a/libsrc/iofuncs/im_writeline.c +++ b/libsrc/iofuncs/im_writeline.c @@ -23,6 +23,8 @@ * - better error messages * 31/10/03 JC * - stop early on kill + * 7/11/07 + * - add eval start/stop */ /* @@ -75,39 +77,52 @@ #endif /*WITH_DMALLOC*/ int -im_writeline( int ypos, IMAGE *image, PEL *linebuffer ) +im_writeline( int ypos, IMAGE *im, PEL *linebuffer ) { - int linesize = IM_IMAGE_SIZEOF_LINE( image ); + int linesize = IM_IMAGE_SIZEOF_LINE( im ); char *tmp; + /* Is this the start of eval? + */ + if( ypos == 0 ) + im__start_eval( im ); + /* Possible cases for output: FILE or SETBUF. */ - switch( image->dtype ) { + switch( im->dtype ) { case IM_SETBUF: case IM_SETBUF_FOREIGN: - tmp = image->data + ypos * linesize; + tmp = im->data + ypos * linesize; memcpy( tmp, linebuffer, linesize ); break; case IM_OPENOUT: - if( im__write( image->fd, linebuffer, linesize ) ) + /* Don't use ypos for this. + */ + if( im__write( im->fd, linebuffer, linesize ) ) return( -1 ); break; default: - im_errormsg( "im_writeline: unable to output to a %s image", - im_dtype2char( image->dtype ) ); + im_error( "im_writeline", + _( "unable to output to a %s image" ), + im_dtype2char( im->dtype ) ); return( -1 ); } /* Trigger evaluation callbacks for this image. */ - if( im__handle_eval( image, image->Xsize, 1 ) ) + if( im__handle_eval( im, im->Xsize, 1 ) ) return( -1 ); - if( im__test_kill( image ) ) + if( im__test_kill( im ) ) return( -1 ); + /* Is this the end of eval? + */ + if( ypos == im->Ysize - 1 ) + im__end_eval( im ); + return( 0 ); } diff --git a/libsrc/iofuncs/time.c b/libsrc/iofuncs/time.c index bae9f1ab..f698740b 100644 --- a/libsrc/iofuncs/time.c +++ b/libsrc/iofuncs/time.c @@ -29,6 +29,10 @@ */ +/* +#define DEBUG + */ + #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ @@ -50,6 +54,10 @@ int im__time_destroy( IMAGE *im ) { if( im->time ) { +#ifdef DEBUG + printf( "im__time_destroy: %s\n", im->filename ); +#endif /*DEBUG*/ + g_timer_destroy( im->time->start ); im_free( im->time ); im->time = NULL; @@ -69,6 +77,10 @@ time_add( IMAGE *im ) !(time = IM_NEW( NULL, im_time_t )) ) return( -1 ); +#ifdef DEBUG + printf( "time_add: %s\n", im->filename ); +#endif /*DEBUG*/ + time->im = im; time->start = g_timer_new(); time->run = 0; @@ -98,6 +110,28 @@ update_time( im_time_t *time, int w, int h ) return( 0 ); } +int +im__start_eval( IMAGE *im ) +{ + im_image_sanity( im ); + + if( im->progress ) { +#ifdef DEBUG + printf( "im__start_eval: %s\n", im->filename ); +#endif /*DEBUG*/ + + im_image_sanity( im->progress ); + + if( time_add( im->progress ) ) + return( -1 ); + + if( im__trigger_callbacks( im->progress->evalstartfns ) ) + return( -1 ); + } + + return( 0 ); +} + /* Handle eval callbacks. w and h are the size of the tile we made this time. * We signal progress on the ->progress IMAGE, see im_add_eval_callback(). We * assume there's no geometry change between adding the feedback request and @@ -107,14 +141,6 @@ int im__handle_eval( IMAGE *im, int w, int h ) { if( im->progress ) { - if( !im->progress->time ) { - /* So we just check sanity first time around. - */ - im_image_sanity( im->progress ); - - if( time_add( im->progress ) ) - return( -1 ); - } if( update_time( im->progress->time, w, h ) ) return( -1 ); @@ -124,3 +150,24 @@ im__handle_eval( IMAGE *im, int w, int h ) return( 0 ); } + +int +im__end_eval( IMAGE *im ) +{ + im_image_sanity( im ); + + if( im->progress ) { +#ifdef DEBUG + printf( "im__end_eval: %s\n", im->filename ); +#endif /*DEBUG*/ + + im_image_sanity( im->progress ); + + if( im__trigger_callbacks( im->progress->evalendfns ) ) + return( -1 ); + + im__time_destroy( im->progress ); + } + + return( 0 ); +} diff --git a/libsrc/mosaicing/global_balance.c b/libsrc/mosaicing/global_balance.c index ba7db608..25915f04 100644 --- a/libsrc/mosaicing/global_balance.c +++ b/libsrc/mosaicing/global_balance.c @@ -974,7 +974,7 @@ local_mask( IMAGE *out, DOUBLEMASK *mask ) if( !mask ) return( NULL ); - if( im_add_evalend_callback( out, + if( im_add_close_callback( out, (im_callback_fn) im_free_dmask, mask, NULL ) ) { im_free_dmask( mask ); return( NULL );