fold im_demand_hint

demand hint stuff moves into generate
This commit is contained in:
John Cupitt 2011-03-24 17:03:27 +00:00
parent 23be545441
commit 9259d686f9
9 changed files with 253 additions and 314 deletions

View File

@ -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.

View File

@ -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 );

View File

@ -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*/

View File

@ -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 \

View File

@ -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 ) );
}

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/debug.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#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 ) );
}

View File

@ -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!

View File

@ -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 );
}

View File

@ -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 );