diff --git a/libvips/include/vips/generate.h b/libvips/include/vips/generate.h index 6276e9d9..c8d2490a 100644 --- a/libvips/include/vips/generate.h +++ b/libvips/include/vips/generate.h @@ -56,8 +56,9 @@ int im_iterate( VipsImage *im, void *a, void *b ); -int im_demand_hint_array( VipsImage *im, im_demand_type hint, VipsImage **in ); -int im_demand_hint( VipsImage *im, im_demand_type hint, ... ) +int vips_demand_hint_array( VipsImage *image, + VipsDemandStyle hint, VipsImage **in ); +int vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) __attribute__((sentinel)); /* Buffer processing. diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index 66159e30..1f235894 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -132,8 +132,8 @@ typedef enum { VipsImage *im__convert_saveable( VipsImage *in, im__saveable_t saveable, int format_table[10] ); -void im__link_break_all( VipsImage *im ); -void *im__link_map( VipsImage *im, VSListMap2Fn fn, void *a, void *b ); +void vips__link_break_all( VipsImage *im ); +void *vips__link_map( VipsImage *im, VSListMap2Fn fn, void *a, void *b ); GValue *im__gvalue_ref_string_new( const char *text ); void im__gslist_gvalue_free( GSList *list ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 6130898f..00241101 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -281,6 +281,9 @@ VipsDemandStyle im_char2dhint( const char *str ); #define im_updatehist vips_image_history_args #define im_history_get vips_image_get_history +#define im_demand_hint vips_demand_hint +#define im_demand_hint_array vips_demand_hint_array + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index f7d0c9b9..0776cee1 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -10,8 +10,7 @@ libiofuncs_la_SOURCES = \ error.c \ image.c \ vips.c \ - im_demand_hint.c \ - im_generate.c \ + generate.c \ im_mapfile.c \ sinkmemory.c \ sinkscreen.c \ diff --git a/libvips/iofuncs/im_generate.c b/libvips/iofuncs/generate.c similarity index 63% rename from libvips/iofuncs/im_generate.c rename to libvips/iofuncs/generate.c index be645cbd..174e59ce 100644 --- a/libvips/iofuncs/im_generate.c +++ b/libvips/iofuncs/generate.c @@ -457,3 +457,241 @@ im_generate( IMAGE *im, return( 0 ); } +/* Max number of images we can handle. + */ +#define MAX_IMAGES (1000) + +/* Make a upstream/downstream link. upstream is one of downstream's inputs. + */ +static void +vips__link_make( VipsImage *image_up, VipsImage *image_down ) +{ + g_assert( image_up ); + g_assert( image_down ); + + image_up->downstream = + g_slist_prepend( image_up->downstream, image_down ); + image_down->upstream = + g_slist_prepend( image_down->upstream, image_up ); + + /* Propogate the progress indicator. + */ + if( image_up->progress_signal && + !image_down->progress_signal ) + image_down->progress_signal = image_up->progress_signal; +} + +static void * +vips__link_break( VipsImage *image_up, VipsImage *image_down ) +{ + g_assert( image_up ); + g_assert( image_down ); + g_assert( g_slist_find( image_up->downstream, image_down ) ); + g_assert( g_slist_find( image_down->upstream, image_up ) ); + + image_up->downstream = + g_slist_remove( image_up->downstream, image_down ); + image_down->upstream = + g_slist_remove( image_down->upstream, image_up ); + + /* Unlink the progress chain. + */ + if( image_down->progress_signal && + image_down->progress_signal == image_up->progress_signal ) + image_down->progress_signal = NULL; + + return( NULL ); +} + +static void * +vips__link_break_rev( VipsImage *image_down, VipsImage *image_up ) +{ + return( vips__link_break( image_up, image_down ) ); +} + +/* An VipsImage is going ... break all links. + */ +void +vips__link_break_all( VipsImage *image ) +{ + im_slist_map2( image->upstream, + (VSListMap2Fn) vips__link_break, image, NULL ); + im_slist_map2( image->downstream, + (VSListMap2Fn) vips__link_break_rev, image, NULL ); + + g_assert( !image->upstream ); + g_assert( !image->downstream ); +} + +static void * +vips__link_mapp( VipsImage *image, + VSListMap2Fn fn, int *serial, void *a, void *b ) +{ + void *res; + + /* Loop? + */ + if( image->serial == *serial ) + return( NULL ); + image->serial = *serial; + + if( (res = fn( image, a, b )) ) + return( res ); + + return( im_slist_map4( image->downstream, + (VSListMap4Fn) vips__link_mapp, fn, serial, a, b ) ); +} + +static void * +vips__link_map_cb( VipsImage *image, GSList **images ) +{ + *images = g_slist_prepend( *images, image ); + + return( NULL ); +} + +/* Apply a function to an image and all downstream images, direct and indirect. + */ +void * +vips__link_map( VipsImage *image, VSListMap2Fn fn, void *a, void *b ) +{ + static int serial = 0; + + GSList *images; + GSList *p; + void *result; + + /* The function might do anything, including removing images + * or invalidating other images, so we can't trigger them from within + * the image loop. Instead we collect a list of images, ref them, + * run the functions, and unref. + */ + + serial += 1; + images = NULL; + vips__link_mapp( image, + (VSListMap2Fn) vips__link_map_cb, &serial, &images, NULL ); + + for( p = images; p; p = p->next ) + g_object_ref( p->data ); + result = im_slist_map2( images, fn, a, b ); + for( p = images; p; p = p->next ) + g_object_unref( p->data ); + g_slist_free( images ); + + return( result ); +} + +/** + * vips_demand_hint_array: + * @image: image to set hint for + * @hint: hint for this image + * @in: array of input images to this operation + * + * Operations can set demand hints, that is, hints to the VIPS IO system about + * the type of region geometry this operation works best with. For example, + * operations which transform coordinates will usually work best with + * %IM_SMALLTILE, operations which work on local windows of pixels will like + * %IM_FATSTRIP. + * + * VIPS uses the list of input images to build the tree of operations it needs + * for the cache invalidation system. You have to call this function, or its + * varargs friend vips_demand_hint(). + * + * See also: vips_demand_hint(), im_generate(). + * + * Returns: 0 on success, or -1 on error. + */ +int +vips_demand_hint_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ) +{ + int i, len, nany; + VipsDemandStyle set_hint; + + /* How many input images are there? And how many are IM_ANY? + */ + for( i = 0, len = 0, nany = 0; in[i]; i++, len++ ) + if( in[i]->dhint == VIPS_DEMAND_STYLE_ANY ) + nany++; + + set_hint = hint; + if( len == 0 ) + /* No input images? Just set the requested hint. We don't + * force ANY, since the operation might be something like + * tiled read of an EXR image, where we certainly don't want + * ANY. + */ + ; + else if( nany == len ) + /* Special case: if all the inputs are IM_ANY, then output can + * be IM_ANY regardless of what this function wants. + */ + set_hint = VIPS_DEMAND_STYLE_ANY; + else + /* Find the most restrictive of all the hints available to us. + */ + for( i = 0; i < len; i++ ) + set_hint = (VipsDemandStyle) VIPS_MIN( + (int) set_hint, (int) in[i]->dhint ); + + image->dhint = set_hint; + +#ifdef DEBUG + printf( "vips_demand_hint_array: set dhint for \"%s\" to %s\n", + im->filename, + VIPS_ENUM_NICK( VIPS_TYPE_DEMAND_STYLE, image->dhint ) ); + printf( "\toperation requested %s\n", + VIPS_ENUM_NICK( VIPS_TYPE_DEMAND_STYLE, hint ) ); + printf( "\tinputs were:\n" ); + printf( "\t" ); + for( i = 0; in[i]; i++ ) + printf( "%s ", VIPS_ENUM_NICK( VIPS_TYPE_DEMAND_STYLE, + in[i]->dhint ) ); + printf( "\n" ); +#endif /*DEBUG*/ + + /* im depends on all these ims. + */ + for( i = 0; i < len; i++ ) + vips__link_make( in[i], image ); + + /* Set a flag on the image to say we remember to call this thing. + * im_generate() and friends check this. + */ + image->hint_set = TRUE; + + return( 0 ); +} + +/** + * vips_demand_hint: + * @image: image to set hint for + * @hint: hint for this image + * @Varargs: %NULL-terminated list of input images to this operation + * + * Build an array and call vips_demand_hint_array(). + * + * See also: vips_demand_hint(), im_generate(). + * + * Returns: 0 on success, or -1 on error. + */ +int +vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) +{ + va_list ap; + int i; + VipsImage *ar[MAX_IMAGES]; + + va_start( ap, hint ); + for( i = 0; i < MAX_IMAGES && + (ar[i] = va_arg( ap, VipsImage * )); i++ ) + ; + va_end( ap ); + if( i == MAX_IMAGES ) { + vips_error( "vips_demand_hint", "%s", _( "too many images" ) ); + return( -1 ); + } + + return( vips_demand_hint_array( image, hint, ar ) ); +} + diff --git a/libvips/iofuncs/im_demand_hint.c b/libvips/iofuncs/im_demand_hint.c deleted file mode 100644 index 7c262809..00000000 --- a/libvips/iofuncs/im_demand_hint.c +++ /dev/null @@ -1,302 +0,0 @@ -/* demand hints - * - * Copyright: The National Gallery, 1993 - * Written on: 6/9/93 - * Modified on : - * 2/3/98 JC - * - IM_ANY added - * 19/5/06 - * - minor change to rules: don't force ANY on no-input operations ... - * fails for image import - * 1/12/06 - * - build parent/child links as well - * 8/10/09 - * - gtkdoc comments - * 5/3/10 - * - move link maintenance to im_demand_hint - * 8/3/10 - * - rename parent/child as downstream/upstream, much clearer! - */ - -/* - - 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 - - */ - -/* -#define DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include -#include -#include - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -/* Max number of images we can handle. - */ -#define MAX_IMAGES (1000) - -/* Make a upstream/downstream link. upstream is one of downstream's inputs. - */ -static void -im__link_make( IMAGE *im_up, IMAGE *im_down ) -{ - g_assert( im_up ); - g_assert( im_down ); - - im_up->downstream = g_slist_prepend( im_up->downstream, im_down ); - im_down->upstream = g_slist_prepend( im_down->upstream, im_up ); - - /* Propogate the progress indicator. - */ - if( im_up->progress_signal && - !im_down->progress_signal ) - im_down->progress_signal = im_up->progress_signal; -} - -static void * -im__link_break( IMAGE *im_up, IMAGE *im_down ) -{ - g_assert( im_up ); - g_assert( im_down ); - g_assert( g_slist_find( im_up->downstream, im_down ) ); - g_assert( g_slist_find( im_down->upstream, im_up ) ); - - im_up->downstream = g_slist_remove( im_up->downstream, im_down ); - im_down->upstream = g_slist_remove( im_down->upstream, im_up ); - - /* Unlink the progress chain. - */ - if( im_down->progress_signal && - im_down->progress_signal == im_up->progress_signal ) - im_down->progress_signal = NULL; - - return( NULL ); -} - -static void * -im__link_break_rev( IMAGE *im_down, IMAGE *im_up ) -{ - return( im__link_break( im_up, im_down ) ); -} - -/* An IMAGE is going ... break all links. - */ -void -im__link_break_all( IMAGE *im ) -{ - im_slist_map2( im->upstream, - (VSListMap2Fn) im__link_break, im, NULL ); - im_slist_map2( im->downstream, - (VSListMap2Fn) im__link_break_rev, im, NULL ); - - g_assert( !im->upstream ); - g_assert( !im->downstream ); -} - -static void * -im__link_mapp( IMAGE *im, VSListMap2Fn fn, int *serial, void *a, void *b ) -{ - void *res; - - /* Loop? - */ - if( im->serial == *serial ) - return( NULL ); - im->serial = *serial; - - if( (res = fn( im, a, b )) ) - return( res ); - - return( im_slist_map4( im->downstream, - (VSListMap4Fn) im__link_mapp, fn, serial, a, b ) ); -} - -static void * -im__link_map_cb( VipsImage *image, GSList **images ) -{ - *images = g_slist_prepend( *images, image ); - - return( NULL ); -} - -/* Apply a function to an image and all downstream images, direct and indirect. - */ -void * -im__link_map( IMAGE *im, VSListMap2Fn fn, void *a, void *b ) -{ - static int serial = 0; - - GSList *images; - GSList *p; - void *result; - - /* The function might do anything, including removing images - * or invalidating other images, so we can't trigger them from within - * the image loop. Instead we collect a list of images, ref them, - * run the functions, and unref. - */ - - serial += 1; - images = NULL; - im__link_mapp( im, - (VSListMap2Fn) im__link_map_cb, &serial, &images, NULL ); - - for( p = images; p; p = p->next ) - g_object_ref( p->data ); - result = im_slist_map2( images, fn, a, b ); - for( p = images; p; p = p->next ) - g_object_unref( p->data ); - g_slist_free( images ); - - return( result ); -} - -/* Given two im_demand_type, return the most restrictive. - */ -static im_demand_type -find_least( im_demand_type a, im_demand_type b ) -{ - return( (im_demand_type) VIPS_MIN( (int) a, (int) b ) ); -} - -/** - * im_demand_hint_array: - * @im: image to set hint for - * @hint: hint for this image - * @in: array of input images to this operation - * - * Operations can set demand hints, that is, hints to the VIPS IO system about - * the type of region geometry this operation works best with. For example, - * operations which transform coordinates will usually work best with - * %IM_SMALLTILE, operations which work on local windows of pixels will like - * %IM_FATSTRIP. - * - * VIPS uses the list of input images to build the tree of operations it needs - * for the cache invalidation system. You have to call this function, or its - * varargs friend im_demand_hint(). - * - * See also: im_demand_hint(), im_generate(). - * - * Returns: 0 on success, or -1 on error. - */ -int -im_demand_hint_array( IMAGE *im, VipsDemandStyle hint, IMAGE **in ) -{ - int i, len, nany; - VipsDemandStyle set_hint; - - /* How many input images are there? And how many are IM_ANY? - */ - for( i = 0, len = 0, nany = 0; in[i]; i++, len++ ) - if( in[i]->dhint == VIPS_DEMAND_STYLE_ANY ) - nany++; - - set_hint = hint; - if( len == 0 ) - /* No input images? Just set the requested hint. We don't - * force ANY, since the operation might be something like - * tiled read of an EXR image, where we certainly don't want - * ANY. - */ - ; - else if( nany == len ) - /* Special case: if all the inputs are IM_ANY, then output can - * be IM_ANY regardless of what this function wants. - */ - set_hint = VIPS_DEMAND_STYLE_ANY; - else - /* Find the most restrictive of all the hints available to us. - */ - for( i = 0; i < len; i++ ) - set_hint = find_least( set_hint, in[i]->dhint ); - - im->dhint = set_hint; - -#ifdef DEBUG - printf( "im_demand_hint_array: set dhint for \"%s\" to %s\n", - im->filename, im_dhint2char( im->dhint ) ); - printf( "\toperation requested %s\n", im_dhint2char( hint ) ); - printf( "\tinputs were:\n" ); - printf( "\t" ); - for( i = 0; in[i]; i++ ) - printf( "%s ", im_dhint2char( in[i]->dhint ) ); - printf( "\n" ); -#endif /*DEBUG*/ - - /* im depends on all these ims. - */ - for( i = 0; i < len; i++ ) - im__link_make( in[i], im ); - - /* Set a flag on the image to say we remember to call this thing. - * im_generate() and friends check this. - */ - im->hint_set = TRUE; - - return( 0 ); -} - -/** - * im_demand_hint: - * @im: image to set hint for - * @hint: hint for this image - * @Varargs: %NULL-terminated list of input images to this operation - * - * Build an array and call im_demand_hint_array(). - * - * See also: im_demand_hint(), im_generate(). - * - * Returns: 0 on success, or -1 on error. - */ -int -im_demand_hint( IMAGE *im, VipsDemandStyle hint, ... ) -{ - va_list ap; - int i; - IMAGE *ar[MAX_IMAGES]; - - va_start( ap, hint ); - for( i = 0; i < MAX_IMAGES && (ar[i] = va_arg( ap, IMAGE * )); i++ ) - ; - va_end( ap ); - if( i == MAX_IMAGES ) { - vips_error( "im_demand_hint", "%s", _( "too many images" ) ); - return( -1 ); - } - - return( im_demand_hint_array( im, hint, ar ) ); -} diff --git a/libvips/iofuncs/im_wrapmany.c b/libvips/iofuncs/im_wrapmany.c index 539cf377..17f54d09 100644 --- a/libvips/iofuncs/im_wrapmany.c +++ b/libvips/iofuncs/im_wrapmany.c @@ -208,7 +208,7 @@ im_wrapmany( IMAGE **in, IMAGE *out, im_wrapmany_fn fn, void *a, void *b ) /* Hint demand style. Being a buffer processor, we are happiest with * thin strips. */ - if( im_demand_hint_array( out, VIPS_DEMAND_STYLE_THINSTRIP, in ) ) + if( vips_demand_hint_array( out, VIPS_DEMAND_STYLE_THINSTRIP, in ) ) return( -1 ); /* Generate! diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 7d2b071e..fe1314ac 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -351,7 +351,7 @@ vips_image_finalize( GObject *gobject ) /* No more upstream/downstream links. */ - im__link_break_all( image ); + vips__link_break_all( image ); /* Any file mapping? */ @@ -776,7 +776,7 @@ vips_image_open_lazy( VipsImage *image, * to memory, sadly, so we can't suggest ANY. */ if( format->header( filename, image ) || - im_demand_hint( image, VIPS_DEMAND_STYLE_THINSTRIP, NULL ) ) + vips_demand_hint( image, VIPS_DEMAND_STYLE_THINSTRIP, NULL ) ) return( -1 ); /* Then 'start' creates the real image and 'gen' paints 'out' with @@ -1290,7 +1290,7 @@ vips_image_invalidate_all_cb( VipsImage *image ) void vips_image_invalidate_all( VipsImage *image ) { - (void) im__link_map( image, + (void) vips__link_map( image, (VSListMap2Fn) vips_image_invalidate_all_cb, NULL, NULL ); } diff --git a/libvips/iofuncs/sinkscreen.c b/libvips/iofuncs/sinkscreen.c index 33d1a62e..54a46b11 100644 --- a/libvips/iofuncs/sinkscreen.c +++ b/libvips/iofuncs/sinkscreen.c @@ -1089,12 +1089,12 @@ vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask, if( im_piocheck( in, out ) || vips_image_copy_fields( out, in ) || - im_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) + vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) return( -1 ); if( mask ) { if( im_poutcheck( mask ) || vips_image_copy_fields( mask, in ) || - im_demand_hint( mask, + vips_demand_hint( mask, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) return( -1 );