diff --git a/TODO b/TODO index 932c7fa7..84e03c8b 100644 --- a/TODO +++ b/TODO @@ -5,8 +5,10 @@ - im_sanity() should become a method on VipsObject, see debug.c -- REGION needs moving at the same time, since regions have to ref their image, - argh +- rename Rect to VipsRect + +- remove im__handle_eval etc. from headers + - pre/post/close should be on VipsObject, not VipsImage diff --git a/libvips/include/vips/generate.h b/libvips/include/vips/generate.h index e569762a..d1fcd025 100644 --- a/libvips/include/vips/generate.h +++ b/libvips/include/vips/generate.h @@ -36,11 +36,6 @@ extern "C" { #endif /*__cplusplus*/ -/* IMAGE functions which use regions. - */ -int im_prepare( VipsRegion *reg, Rect *r ); -int im_prepare_to( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ); - typedef void *(*im_start_fn)( IMAGE *out, void *a, void *b ); typedef int (*im_generate_fn)( VipsRegion *out, void *seq, void *a, void *b ); typedef int (*im_stop_fn)( void *seq, void *a, void *b ); @@ -90,10 +85,7 @@ int im_render_priority( IMAGE *in, IMAGE *out, IMAGE *mask, void (*notify)( IMAGE *, Rect *, void * ), void *client ); int im_cache( IMAGE *in, IMAGE *out, int width, int height, int max ); -/* WIO. - */ int im_setupout( IMAGE *im ); -int im_writeline( int ypos, IMAGE *im, PEL *linebuffer ); #ifdef __cplusplus } diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index 47764ab1..2d6a1216 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -296,6 +296,8 @@ typedef struct _VipsImageClass { } VipsImageClass; +GType vips_image_get_type( void ); + extern const size_t vips__sizeof_bandfmt[]; /* Pixel address calculation macros. diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 6180e03f..856bdecf 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -79,7 +79,7 @@ typedef enum { /* Useful flag combinations. User-visible ones are: -VIPS_ARGUMENT_REQURED_INPUT Eg. the "left" argument for an add operation +VIPS_ARGUMENT_REQUIRED_INPUT Eg. the "left" argument for an add operation VIPS_ARGUMENT_OPTIONAL_INPUT Eg. the "caption" for an object diff --git a/libvips/include/vips/private.h b/libvips/include/vips/private.h index 9875b4c6..81d0350c 100644 --- a/libvips/include/vips/private.h +++ b/libvips/include/vips/private.h @@ -146,19 +146,19 @@ typedef enum region_type { /* Functions on regions. */ struct _VipsRegion; -void im__region_take_ownership( struct _VipsRegion *reg ); -void im__region_check_ownership( struct _VipsRegion *reg ); -void im__region_no_ownership( struct _VipsRegion *reg ); +void vips__region_take_ownership( struct _VipsRegion *reg ); +void vips__region_check_ownership( struct _VipsRegion *reg ); +void vips__region_no_ownership( struct _VipsRegion *reg ); void im__copy_region( struct _VipsRegion *reg, struct _VipsRegion *dest, Rect *r, int x, int y ); void im__find_demand_size( struct _VipsImage *im, int *pw, int *ph ); -int im__call_start( struct _VipsRegion *reg ); -void im__call_stop( struct _VipsRegion *reg ); +int vips__region_start( struct _VipsRegion *reg ); +void vips__region_stop( struct _VipsRegion *reg ); -typedef int (*im_region_fill_fn)( struct _VipsRegion *, void * ); -int im_region_fill( struct _VipsRegion *reg, Rect *r, im_region_fill_fn fn, void *a ); -void im_region_print( struct _VipsRegion *region ); +typedef int (*VipsRegionFillFn)( struct _VipsRegion *, void * ); +int vips_region_fill( struct _VipsRegion *reg, + Rect *r, VipsRegionFillFn fn, void *a ); int im_prepare_many( struct _VipsRegion **reg, Rect *r ); diff --git a/libvips/include/vips/region.h b/libvips/include/vips/region.h index e1e52c05..38744a9b 100644 --- a/libvips/include/vips/region.h +++ b/libvips/include/vips/region.h @@ -97,6 +97,8 @@ typedef struct _VipsRegionClass { } VipsRegionClass; +GType vips_region_get_type( void ); + VipsRegion *vips_region_new( VipsImage *im ); int vips_region_buffer( VipsRegion *reg, Rect *r ); @@ -111,6 +113,11 @@ void vips_region_black( VipsRegion *reg ); void vips_region_copy( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ); +int vips_region_prepare( VipsRegion *reg, Rect *r ); +int vips_region_prepare_to( VipsRegion *reg, + VipsRegion *dest, Rect *r, int x, int y ); +int vips_region_prepare_many( VipsRegion **reg, Rect *r ); + /* Macros on REGIONs. * VIPS_REGION_LSKIP() add to move down line * VIPS_REGION_N_ELEMENTS() number of elements across region diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index b84aca56..61df0c01 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -15,7 +15,6 @@ libiofuncs_la_SOURCES = \ im_generate.c \ im_histlin.c \ im_mapfile.c \ - im_prepare.c \ im_setupout.c \ im_guess_prefix.c \ sinkmemory.c \ diff --git a/libvips/iofuncs/im_generate.c b/libvips/iofuncs/im_generate.c index 908a3d21..7745ed1f 100644 --- a/libvips/iofuncs/im_generate.c +++ b/libvips/iofuncs/im_generate.c @@ -131,7 +131,7 @@ im_start_one( IMAGE *out, void *a, void *b ) { IMAGE *in = (IMAGE *) a; - return( im_region_create( in ) ); + return( vips_region_new( in ) ); } /** @@ -147,9 +147,9 @@ im_start_one( IMAGE *out, void *a, void *b ) int im_stop_one( void *seq, void *a, void *b ) { - REGION *reg = (REGION *) seq; + VipsRegion *reg = (VipsRegion *) seq; - im_region_free( reg ); + g_object_unref( reg ); return( 0 ); } @@ -168,13 +168,13 @@ im_stop_one( void *seq, void *a, void *b ) int im_stop_many( void *seq, void *a, void *b ) { - REGION **ar = (REGION **) seq; + VipsRegion **ar = (VipsRegion **) seq; if( ar ) { int i; for( i = 0; ar[i]; i++ ) - im_region_free( ar[i] ); + g_object_unref( ar[i] ); im_free( (char *) ar ); } @@ -198,7 +198,7 @@ im_start_many( IMAGE *out, void *a, void *b ) IMAGE **in = (IMAGE **) a; int i, n; - REGION **ar; + VipsRegion **ar; /* How many images? */ @@ -207,13 +207,13 @@ im_start_many( IMAGE *out, void *a, void *b ) /* Alocate space for region array. */ - if( !(ar = VIPS_ARRAY( NULL, n + 1, REGION * )) ) + if( !(ar = VIPS_ARRAY( NULL, n + 1, VipsRegion * )) ) return( NULL ); /* Create a set of regions. */ for( i = 0; i < n; i++ ) - if( !(ar[i] = im_region_create( in[i] )) ) { + if( !(ar[i] = vips_region_new( in[i] )) ) { im_stop_many( ar, NULL, NULL ); return( NULL ); } @@ -281,7 +281,7 @@ im_allocate_input_array( IMAGE *out, ... ) /** * im_generate_fn: - * @out: #REGION to fill + * @out: #VipsRegion to fill * @seq: sequence value * @a: user data * @b: user data @@ -311,13 +311,13 @@ im_allocate_input_array( IMAGE *out, ... ) /* A write function for VIPS images. Just write() the pixel data. */ static int -write_vips( REGION *region, Rect *area, void *a, void *b ) +write_vips( VipsRegion *region, Rect *area, void *a, void *b ) { size_t nwritten, count; void *buf; count = region->bpl * area->height; - buf = IM_REGION_ADDR( region, 0, area->top ); + buf = VIPS_REGION_ADDR( region, 0, area->top ); do { nwritten = write( region->im->fd, buf, count ); if( nwritten == (size_t) -1 ) diff --git a/libvips/iofuncs/im_prepare.c b/libvips/iofuncs/im_prepare.c deleted file mode 100644 index 3e34fbd1..00000000 --- a/libvips/iofuncs/im_prepare.c +++ /dev/null @@ -1,378 +0,0 @@ -/* Request an area of an image for input. - * - * J.Cupitt, 17/4/93. - * - * Modified: - * 22/11/94 JC - * - check for start and stop functions removed, as can now be NULL - * 22/2/95 JC - * - im_fill_copy() added - * 18/4/95 JC - * - kill flag added for compute cases - * 27/10/95 JC - * - im_fill_copy() now only uses im_generate() mechanism if it has to - * - im_fill_copy() renamed as im_prepare_inplace() - * 18/8/99 JC - * - oops ... cache stuff removed, interacted badly with inplace ops - * 26/3/02 JC - * - better error message for im_prepare() - * 9/8/02 JC - * - im_prepare_inplace() broke with mmap() windows ... im_prepare_to() - * replaces it and is nicer - * 30/9/05 - * - hmm, did not stop if a start function failed - * 7/10/09 - * - gtkdoc comments - */ - -/* - - 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 -#include - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -/* Generate into a region. - */ -static int -fill_region( REGION *reg ) -{ - IMAGE *im = reg->im; - - /* Start new sequence, if necessary. - */ - if( im__call_start( reg ) ) - return( -1 ); - - /* Ask for evaluation. - */ - if( im->generate( reg, reg->seq, im->client1, im->client2 ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_prepare: - * @reg: region to prepare - * @r: #Rect of pixels you need to be able to address - * - * im_prepare() fills @reg with pixels. After calling, you can address at - * least the area @r with IM_REGION_ADDR() and get valid pixels. - * - * im_prepare() runs in-line, that is, computation is done by the calling - * thread, no new threads are involved, and computation blocks until the - * pixels are ready. - * - * Use im_prepare_thread() to calculate an area of pixels with many - * threads. Use im_render_priority() to calculate an area of pixels in the - * background. - * - * See also: im_prepare_thread(), im_render_priority(), im_prepare_to(). - * - * Returns: 0 on success, or -1 on error. - */ -int -im_prepare( REGION *reg, Rect *r ) -{ - IMAGE *im = reg->im; - - Rect save = *r; - - im__region_check_ownership( reg ); - - if( im__test_kill( im ) ) - return( -1 ); - - /* We use save for sanity checking valid: we test at the end that the - * pixels we have generated are indeed all the ones that were asked - * for. - * - * However, r may be clipped by the image size, so we need to clip - * save as well to make sure we don't fail the assert due to that. - */ -{ - Rect image; - - image.left = 0; - image.top = 0; - image.width = reg->im->Xsize; - image.height = reg->im->Ysize; - im_rect_intersectrect( &save, &image, &save ); -} - -#ifdef DEBUG - printf( "im_prepare: left = %d, top = %d, width = %d, height = %d\n", - r->left, r->top, r->width, r->height ); -#endif /*DEBUG*/ - - switch( im->dtype ) { - case VIPS_IMAGE_PARTIAL: - if( im_region_fill( reg, r, - (im_region_fill_fn) fill_region, NULL ) ) - return( -1 ); - - break; - - case VIPS_IMAGE_SETBUF: - case VIPS_IMAGE_SETBUF_FOREIGN: - case VIPS_IMAGE_MMAPIN: - case VIPS_IMAGE_MMAPINRW: - case VIPS_IMAGE_OPENIN: - /* Attach to existing buffer. - */ - if( im_region_image( reg, r ) ) - return( -1 ); - - break; - - default: - vips_error( "im_prepare", _( "unable to input from a %s image" ), - im_dtype2char( im->dtype ) ); - return( -1 ); - } - - /* valid should now include all the pixels that were asked for. - */ - g_assert( im_rect_includesrect( ®->valid, &save ) ); - - return( 0 ); -} - -/* We need to make pixels using reg's generate function, and write the result - * to dest. - */ -static int -im_prepare_to_generate( REGION *reg, REGION *dest, Rect *r, int x, int y ) -{ - IMAGE *im = reg->im; - char *p; - - if( !im->generate ) { - vips_error( "im_prepare_to", "%s", _( "incomplete header" ) ); - return( -1 ); - } - - if( im_region_region( reg, dest, r, x, y ) ) - return( -1 ); - - /* Remember where reg is pointing now. - */ - p = IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); - - /* Run sequence into reg. - */ - if( fill_region( reg ) ) - return( -1 ); - - /* The generate function may not have actually made any pixels ... it - * might just have redirected reg to point somewhere else. If it has, - * we need an extra copy operation. - */ - if( IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ) != p ) - im_region_copy( reg, dest, r, x, y ); - - return( 0 ); -} - -/** - * im_prepare_to: - * @reg: region to prepare - * @dest: region to write to - * @r: #Rect of pixels you need to be able to address - * @x: postion of @r in @dest - * @y: postion of @r in @dest - * - * Like im_prepare(): fill @reg with data, ready to be read from by our caller. - * Unlike im_prepare(), rather than allocating memory local to @reg for the - * result, we guarantee that we will fill the pixels in @dest at offset @x, @y. - * In other words, we generate an extra copy operation if necessary. - * - * Also unlike im_prepare(), @dest is not set up for writing for you with - * im_region_buffer(). You can - * point @dest at anything, and pixels really will be written there. - * This makes im_prepare_to() useful for making the ends of pipelines, since - * it (effectively) makes a break in the pipe. - * - * See also: im_prepare(), vips_sink_disc(). - * - * Returns: 0 on success, or -1 on error - */ -int -im_prepare_to( REGION *reg, REGION *dest, Rect *r, int x, int y ) -{ - IMAGE *im = reg->im; - Rect image; - Rect wanted; - Rect clipped; - Rect clipped2; - Rect final; - - if( im__test_kill( im ) ) - return( -1 ); - - /* Sanity check. - */ - if( !dest->data || - dest->im->BandFmt != reg->im->BandFmt || - dest->im->Bands != reg->im->Bands ) { - vips_error( "im_prepare_to", - "%s", _( "inappropriate region type" ) ); - return( -1 ); - } - - /* clip r first against the size of reg->im, then again against the - * memory we have available to write to on dest. Just like - * im_region_region() - */ - image.top = 0; - image.left = 0; - image.width = reg->im->Xsize; - image.height = reg->im->Ysize; - im_rect_intersectrect( r, &image, &clipped ); - - g_assert( clipped.left == r->left ); - g_assert( clipped.top == r->top ); - - wanted.left = x + (clipped.left - r->left); - wanted.top = y + (clipped.top - r->top); - wanted.width = clipped.width; - wanted.height = clipped.height; - - /* Test that dest->valid is large enough. - */ - if( !im_rect_includesrect( &dest->valid, &wanted ) ) { - vips_error( "im_prepare_to", "%s", _( "dest too small" ) ); - return( -1 ); - } - - im_rect_intersectrect( &wanted, &dest->valid, &clipped2 ); - - /* Translate back to reg's coordinate space and set as valid. - */ - final.left = r->left + (clipped2.left - wanted.left); - final.top = r->top + (clipped2.top - wanted.top); - final.width = clipped2.width; - final.height = clipped2.height; - - x = clipped2.left; - y = clipped2.top; - - if( im_rect_isempty( &final ) ) { - vips_error( "im_prepare_to", - "%s", _( "valid clipped to nothing" ) ); - return( -1 ); - } - -#ifdef DEBUG - printf( "im_prepare_to: left = %d, top = %d, width = %d, height = %d\n", - final.left, final.top, final.width, final.height ); -#endif /*DEBUG*/ - - /* Input or output image type? - */ - switch( im->dtype ) { - case VIPS_IMAGE_OPENOUT: - case VIPS_IMAGE_PARTIAL: - /* We are generating with a sequence. - */ - if( im_prepare_to_generate( reg, dest, &final, x, y ) ) - return( -1 ); - - break; - - case VIPS_IMAGE_MMAPIN: - case VIPS_IMAGE_MMAPINRW: - case VIPS_IMAGE_OPENIN: - /* Attach to existing buffer and copy to dest. - */ - if( im_region_image( reg, &final ) ) - return( -1 ); - im_region_copy( reg, dest, &final, x, y ); - - break; - - case VIPS_IMAGE_SETBUF: - case VIPS_IMAGE_SETBUF_FOREIGN: - /* Could be either input or output. If there is a generate - * function, we are outputting. - */ - if( im->generate ) { - if( im_prepare_to_generate( reg, dest, &final, x, y ) ) - return( -1 ); - } - else { - if( im_region_image( reg, &final ) ) - return( -1 ); - im_region_copy( reg, dest, &final, x, y ); - } - - break; - - default: - vips_error( "im_prepare_to", _( "unable to input from a " - "%s image" ), im_dtype2char( im->dtype ) ); - return( -1 ); - } - - /* We've written fresh pixels to dest, it's no longer invalid (if it - * was). - * - * We need this extra thing here because, unlike im_prepare(), we - * don't im_region_buffer() dest before writing it. - */ - dest->invalid = FALSE; - - return( 0 ); -} - -int -im_prepare_many( REGION **reg, Rect *r ) -{ - for( ; *reg; ++reg ) - if( im_prepare( *reg, r ) ) - return( -1 ); - - return( 0 ); -} diff --git a/libvips/iofuncs/im_wrapmany.c b/libvips/iofuncs/im_wrapmany.c index 3a03a75f..539cf377 100644 --- a/libvips/iofuncs/im_wrapmany.c +++ b/libvips/iofuncs/im_wrapmany.c @@ -62,12 +62,12 @@ typedef struct { */ #define MAX_INPUT_IMAGES (64) -/* Convert a REGION. +/* Convert a VipsRegion. */ static int -process_region( REGION *or, void *seq, void *a, void *b ) +process_region( VipsRegion *or, void *seq, void *a, void *b ) { - REGION **ir = (REGION **) seq; + VipsRegion **ir = (VipsRegion **) seq; Bundle *bun = (Bundle *) b; PEL *p[MAX_INPUT_IMAGES], *q; @@ -76,13 +76,13 @@ process_region( REGION *or, void *seq, void *a, void *b ) /* Prepare all input regions and make buffer pointers. */ for( i = 0; ir[i]; i++ ) { - if( im_prepare( ir[i], &or->valid ) ) + if( vips_region_prepare( ir[i], &or->valid ) ) return( -1 ); - p[i] = (PEL *) IM_REGION_ADDR( ir[i], + p[i] = (PEL *) VIPS_REGION_ADDR( ir[i], or->valid.left, or->valid.top ); } p[i] = NULL; - q = (PEL *) IM_REGION_ADDR( or, or->valid.left, or->valid.top ); + q = (PEL *) VIPS_REGION_ADDR( or, or->valid.left, or->valid.top ); /* Convert linewise. */ @@ -103,8 +103,8 @@ process_region( REGION *or, void *seq, void *a, void *b ) /* Move pointers on. */ for( i = 0; ir[i]; i++ ) - p[i] += IM_REGION_LSKIP( ir[i] ); - q += IM_REGION_LSKIP( or ); + p[i] += VIPS_REGION_LSKIP( ir[i] ); + q += VIPS_REGION_LSKIP( or ); } return( 0 ); diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 6f2db82d..e5ccf4be 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -448,6 +448,8 @@ vips_image_print( VipsObject *object, VipsBuf *buf ) vips_image_get_bands( image ), VIPS_ENUM_NICK( VIPS_TYPE_INTERPRETATION, vips_image_get_interpretation( image ) ) ); + + VIPS_OBJECT_CLASS( vips_image_parent_class )->print( object, buf ); } static gboolean @@ -623,26 +625,26 @@ open_lazy_start( VipsImage *out, void *a, void *dummy ) } } - return( im_region_create( lazy->real ) ); + return( vips_region_new( lazy->real ) ); } /* Just copy. */ static int -open_lazy_generate( REGION *or, void *seq, void *a, void *b ) +open_lazy_generate( VipsRegion *or, void *seq, void *a, void *b ) { - REGION *ir = (REGION *) seq; + VipsRegion *ir = (VipsRegion *) seq; Rect *r = &or->valid; /* Ask for input we need. */ - if( im_prepare( ir, r ) ) + if( vips_region_prepare( ir, r ) ) return( -1 ); /* Attach output region to that. */ - if( im_region_region( or, ir, r, r->left, r->top ) ) + if( vips_region_region( or, ir, r, r->left, r->top ) ) return( -1 ); return( 0 ); @@ -948,7 +950,7 @@ vips_image_build( VipsObject *object ) } static void * -vips_region_invalidate( REGION *reg ) +vips_region_invalidate( VipsRegion *reg ) { reg->invalid = TRUE; diff --git a/libvips/iofuncs/package.c b/libvips/iofuncs/package.c index 648fcd4d..df891d98 100644 --- a/libvips/iofuncs/package.c +++ b/libvips/iofuncs/package.c @@ -977,9 +977,9 @@ build_args( im_function *fn, im_object *vargv, int argc, char **argv ) /* Free a region, but return 0 so we can be used as a close callback. */ static void -region_local_image_cb( VipsImage *main, REGION *reg ) +region_local_image_cb( VipsImage *main, VipsRegion *reg ) { - im_region_free( reg ); + g_object_unref( reg ); } /* Make a region on sub, closed by callback on main. @@ -987,9 +987,9 @@ region_local_image_cb( VipsImage *main, REGION *reg ) static int region_local_image( IMAGE *main, IMAGE *sub ) { - REGION *reg; + VipsRegion *reg; - if( !(reg = im_region_create( sub )) ) + if( !(reg = vips_region_new( sub )) ) return( -1 ); g_signal_connect( main, "close", G_CALLBACK( region_local_image_cb ), reg ); diff --git a/libvips/iofuncs/rect.c b/libvips/iofuncs/rect.c index 836fc48a..c69bbb41 100644 --- a/libvips/iofuncs/rect.c +++ b/libvips/iofuncs/rect.c @@ -91,12 +91,12 @@ im_rect_includesrect( Rect *r1, Rect *r2 ) void im_rect_intersectrect( Rect *r1, Rect *r2, Rect *r3 ) { - int left = IM_MAX( r1->left, r2->left ); - int top = IM_MAX( r1->top, r2->top ); - int right = IM_MIN( IM_RECT_RIGHT( r1 ), IM_RECT_RIGHT( r2 ) ); - int bottom = IM_MIN( IM_RECT_BOTTOM( r1 ), IM_RECT_BOTTOM( r2 ) ); - int width = IM_MAX( 0, right - left ); - int height = IM_MAX( 0, bottom - top ); + int left = VIPS_MAX( r1->left, r2->left ); + int top = VIPS_MAX( r1->top, r2->top ); + int right = VIPS_MIN( IM_RECT_RIGHT( r1 ), IM_RECT_RIGHT( r2 ) ); + int bottom = VIPS_MIN( IM_RECT_BOTTOM( r1 ), IM_RECT_BOTTOM( r2 ) ); + int width = VIPS_MAX( 0, right - left ); + int height = VIPS_MAX( 0, bottom - top ); r3->left = left; r3->top = top; @@ -124,11 +124,11 @@ im_rect_unionrect( Rect *r1, Rect *r2, Rect *r3 ) else if( im_rect_isempty( r2 ) ) *r3 = *r1; else { - int left = IM_MIN( r1->left, r2->left ); - int top = IM_MIN( r1->top, r2->top ); - int width = IM_MAX( IM_RECT_RIGHT( r1 ), + int left = VIPS_MIN( r1->left, r2->left ); + int top = VIPS_MIN( r1->top, r2->top ); + int width = VIPS_MAX( IM_RECT_RIGHT( r1 ), IM_RECT_RIGHT( r2 ) ) - left; - int height = IM_MAX( IM_RECT_BOTTOM( r1 ), + int height = VIPS_MAX( IM_RECT_BOTTOM( r1 ), IM_RECT_BOTTOM( r2 ) )- top; r3->left = left; @@ -152,7 +152,7 @@ im_rect_equalsrect( Rect *r1, Rect *r2 ) Rect * im_rect_dup( Rect *r ) { - Rect *out = IM_NEW( NULL, Rect ); + Rect *out = VIPS_NEW( NULL, Rect ); if( !out ) return( NULL ); diff --git a/libvips/iofuncs/region.c b/libvips/iofuncs/region.c index ed404e8d..4aaa9310 100644 --- a/libvips/iofuncs/region.c +++ b/libvips/iofuncs/region.c @@ -39,6 +39,8 @@ * - gtkdoc comments * 5/3/10 * - move invalid stuff to region + * 3/3/11 + * - move on top of VipsObject, rename as VipsRegion */ /* @@ -84,21 +86,13 @@ #ifdef HAVE_UNISTD_H #include #endif /*HAVE_UNISTD_H*/ -#include #include -#ifdef HAVE_SYS_MMAN_H -#include -#endif #include #include #include #include -#ifdef OS_WIN32 -#include -#endif /*OS_WIN32*/ - #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ @@ -130,13 +124,13 @@ /** * VipsRegion: - * @im: the #IMAGE that this region is defined on + * @im: the #VipsImage that this region is defined on * @valid: the #Rect of pixels that this region represents * - * A small part of an #IMAGE. @valid holds the left/top/width/height of the + * A small part of a #VipsImage. @valid holds the left/top/width/height of the * area of pixels that are available from the region. * - * See also: VIPS_REGION_ADDR(), im_region_create(), im_prepare(). + * See also: VIPS_REGION_ADDR(), vips_region_new(), vips_region_prepare(). */ /** @@ -178,36 +172,51 @@ * VIPS_REGION_ADDR_TOPLEFT: * @R: a #VipsRegion * - * This macro returns a pointer to the top-left pixel in the #VipsRegion, that is, - * the pixel at (@R->valid.left, @R->valid.top). + * This macro returns a pointer to the top-left pixel in the #VipsRegion, that + * is, the pixel at (@R->valid.left, @R->valid.top). * * Returns: The address of the top-left pixel in the region. */ -#ifdef DEBUG -/* Track all regions here for debugging. +/* Properties. */ -static GSList *im__regions_all = NULL; -#endif /*DEBUG*/ +enum { + PROP_IMAGE = 1, + PROP_LAST +}; + +G_DEFINE_TYPE( VipsRegion, vips_region, VIPS_TYPE_OBJECT ); + +static void +vips_region_finalize( GObject *gobject ) +{ +#ifdef VIPS_DEBUG + VIPS_DEBUG_MSG( "vips_region_finalize: " ); + vips_object_print( VIPS_OBJECT( gobject ) ); +#endif /*VIPS_DEBUG*/ + + G_OBJECT_CLASS( vips_region_parent_class )->finalize( gobject ); +} /* Call a start function if no sequence is running on this VipsRegion. */ int -im__call_start( VipsRegion *reg ) +vips__region_start( VipsRegion *region ) { - IMAGE *im = reg->im; + VipsImage *image = region->im; /* Have we a sequence running on this region? Start one if not. */ - if( !reg->seq && im->start ) { - g_mutex_lock( im->sslock ); - reg->seq = im->start( im, im->client1, im->client2 ); - g_mutex_unlock( im->sslock ); + if( !region->seq && image->start ) { + g_mutex_lock( image->sslock ); + region->seq = + image->start( image, image->client1, image->client2 ); + g_mutex_unlock( image->sslock ); - if( !reg->seq ) { - vips_error( "im__call_start", + if( !region->seq ) { + vips_error( "vips__region_start", _( "start function failed for image %s" ), - im->filename ); + image->filename ); return( -1 ); } } @@ -215,66 +224,134 @@ im__call_start( VipsRegion *reg ) return( 0 ); } -/* Call a stop function if a sequence is running in this VipsRegion. No error - * return is possible, really. +/* Call a stop function if a sequence is running in this VipsRegion. */ void -im__call_stop( VipsRegion *reg ) +vips__region_stop( VipsRegion *region ) { - IMAGE *im = reg->im; - int res; + IMAGE *image = region->im; /* Stop any running sequence. */ - if( reg->seq && im->stop ) { - g_mutex_lock( im->sslock ); - res = im->stop( reg->seq, im->client1, im->client2 ); - g_mutex_unlock( im->sslock ); + if( region->seq && image->stop ) { + int result; - if( res ) - error_exit( "panic: user stop callback failed " - "for image %s", im->filename ); + g_mutex_lock( image->sslock ); + result = image->stop( region->seq, + image->client1, image->client2 ); + g_mutex_unlock( image->sslock ); + + /* stop function can return an error, but we have nothing we + * can really do with it, sadly. + */ + if( result ) + vips_warn( "VipsRegion", + "stop callback failed for image %s", + image->filename ); - reg->seq = NULL; + region->seq = NULL; } } +/* Free any resources we have. + */ +static void +vips_region_reset( VipsRegion *region ) +{ + VIPS_FREEF( im_window_unref, region->window ); + VIPS_FREEF( im_buffer_unref, region->buffer ); + region->invalid = FALSE; +} + +static void +vips_region_dispose( GObject *gobject ) +{ + VipsRegion *region = VIPS_REGION( gobject ); + VipsImage *image = region->im; + +#ifdef VIPS_DEBUG + VIPS_DEBUG_MSG( "vips_region_dispose: " ); + vips_object_print( VIPS_OBJECT( gobject ) ); +#endif /*VIPS_DEBUG*/ + + /* Stop this sequence. + */ + vips__region_stop( region ); + + /* Free any attached memory. + */ + vips_region_reset( region ); + + /* Detach from image. + */ + g_mutex_lock( image->sslock ); + image->regions = g_slist_remove( image->regions, region ); + g_mutex_unlock( image->sslock ); + region->im = NULL; + + G_OBJECT_CLASS( vips_region_parent_class )->dispose( gobject ); +} + +static void +vips_region_print( VipsObject *object, VipsBuf *buf ) +{ + VipsRegion *region = VIPS_REGION( object ); + + vips_buf_appendf( buf, "VipsRegion: %p, ", region ); + vips_buf_appendf( buf, "im = %p, ", region->im ); + vips_buf_appendf( buf, "valid.left = %d, ", region->valid.left ); + vips_buf_appendf( buf, "valid.top = %d, ", region->valid.top ); + vips_buf_appendf( buf, "valid.width = %d, ", region->valid.width ); + vips_buf_appendf( buf, "valid.height = %d, ", region->valid.height ); + vips_buf_appendf( buf, "type = %d, ", region->type ); + vips_buf_appendf( buf, "data = %p, ", region->data ); + vips_buf_appendf( buf, "bpl = %d, ", region->bpl ); + vips_buf_appendf( buf, "seq = %p, ", region->seq ); + vips_buf_appendf( buf, "thread = %p, ", region->thread ); + vips_buf_appendf( buf, "window = %p, ", region->window ); + vips_buf_appendf( buf, "buffer = %p\n", region->buffer ); + vips_buf_appendf( buf, "invalid = %d\n", region->invalid ); + + VIPS_OBJECT_CLASS( vips_region_parent_class )->print( object, buf ); +} + /* If a region is being created in one thread (eg. the main thread) and then * used in another (eg. a worker thread), the new thread needs to tell VIPS * to stop sanity g_assert() fails. The previous owner needs to * im__region_no_ownership() before we can call this. */ void -im__region_take_ownership( VipsRegion *reg ) +vips__region_take_ownership( VipsRegion *region ) { /* Lock so that there's a memory barrier with the thread doing the * im__region_no_ownership() before us. */ - g_mutex_lock( reg->im->sslock ); + g_mutex_lock( region->im->sslock ); - if( reg->thread != g_thread_self() ) { - g_assert( reg->thread == NULL ); + if( region->thread != g_thread_self() ) { + g_assert( region->thread == NULL ); /* We don't want to move shared buffers: the other region * using this buffer will still be on the other thread. * Not sure if this will ever happen: if it does, we'll * need to dup the buffer. */ - g_assert( !reg->buffer || reg->buffer->ref_count == 1 ); + g_assert( !region->buffer || region->buffer->ref_count == 1 ); - reg->thread = g_thread_self(); + region->thread = g_thread_self(); } - g_mutex_unlock( reg->im->sslock ); + g_mutex_unlock( region->im->sslock ); } void -im__region_check_ownership( VipsRegion *reg ) +vips__region_check_ownership( VipsRegion *region ) { - if( reg->thread ) { - g_assert( reg->thread == g_thread_self() ); - if( reg->buffer && reg->buffer->cache ) - g_assert( reg->thread == reg->buffer->cache->thread ); + if( region->thread ) { + g_assert( region->thread == g_thread_self() ); + if( region->buffer && region->buffer->cache ) + g_assert( region->thread == + region->buffer->cache->thread ); } } @@ -282,136 +359,102 @@ im__region_check_ownership( VipsRegion *reg ) * this thread's buffer cache. */ void -im__region_no_ownership( VipsRegion *reg ) +vips__region_no_ownership( VipsRegion *region ) { - g_mutex_lock( reg->im->sslock ); + g_mutex_lock( region->im->sslock ); - im__region_check_ownership( reg ); + vips__region_check_ownership( region ); - reg->thread = NULL; - if( reg->buffer ) - im_buffer_undone( reg->buffer ); + region->thread = NULL; + if( region->buffer ) + im_buffer_undone( region->buffer ); - g_mutex_unlock( reg->im->sslock ); + g_mutex_unlock( region->im->sslock ); } -/** - * im_region_create: - * @im: image to create this region on - * - * Create a region. #VipsRegion s start out empty, you need to call im_prepare() to - * fill them with pixels. - * - * See also: im_prepare(), im_region_free(). - */ -VipsRegion * -im_region_create( IMAGE *im ) -{ - VipsRegion *reg; +static int +vips_region_build( VipsObject *object ) +{ + VipsRegion *region = VIPS_REGION( object ); + VipsImage *image = region->im; - g_assert( !im_image_sanity( im ) ); + VIPS_DEBUG_MSG( "vips_region_build: %p\n", region ); - if( !(reg = VIPS_NEW( NULL, VipsRegion )) ) - return( NULL ); + if( VIPS_OBJECT_CLASS( vips_region_parent_class )->build( object ) ) + return( -1 ); - reg->im = im; - reg->valid.left = 0; - reg->valid.top = 0; - reg->valid.width = 0; - reg->valid.height = 0; - reg->type = VIPS_REGION_NONE; - reg->data = NULL; - reg->bpl = 0; - reg->seq = NULL; - reg->thread = NULL; - reg->window = NULL; - reg->buffer = NULL; - reg->invalid = FALSE; - - im__region_take_ownership( reg ); + vips__region_take_ownership( region ); /* We're usually inside the ss lock anyway. But be safe ... */ - g_mutex_lock( im->sslock ); - im->regions = g_slist_prepend( im->regions, reg ); - g_mutex_unlock( im->sslock ); + g_mutex_lock( image->sslock ); + image->regions = g_slist_prepend( image->regions, region ); + g_mutex_unlock( image->sslock ); -#ifdef DEBUG - g_mutex_lock( im__global_lock ); - im__regions_all = g_slist_prepend( im__regions_all, reg ); - printf( "%d regions in vips\n", g_slist_length( im__regions_all ) ); - g_mutex_unlock( im__global_lock ); -#endif /*DEBUG*/ - - return( reg ); + return( 0 ); } -/* Free any resources we have. - */ static void -im_region_reset( VipsRegion *reg ) +vips_region_class_init( VipsRegionClass *class ) { - VIPS_FREEF( im_window_unref, reg->window ); - VIPS_FREEF( im_buffer_unref, reg->buffer ); - reg->invalid = FALSE; + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + GParamSpec *pspec; + + gobject_class->finalize = vips_region_finalize; + gobject_class->dispose = vips_region_dispose; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->print = vips_region_print; + vobject_class->build = vips_region_build; + + /* Create properties. + */ + pspec = g_param_spec_object( "image", "Image", + _( "The image this region is defined upon" ), + VIPS_TYPE_IMAGE, + G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, PROP_IMAGE, pspec ); + vips_object_class_install_argument( vobject_class, pspec, + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsRegion, im ) ); +} + +static void +vips_region_init( VipsRegion *region ) +{ + /* Init to 0 is fine for most header fields. Others have default set + * by property system. + */ + + region->type = VIPS_REGION_NONE; } /** - * im_region_free: - * @reg: #VipsRegion to free + * vips_region_new: + * @image: image to create this region on * - * Free a region and any resources it holds. + * Create a region. #VipsRegion s start out empty, you need to call + * vips_region_prepare() to fill them with pixels. * - * If @im has previously been closed, then freeing the last #VipsRegion on @in can - * cause @im to finally be freed as well. + * See also: vips_region_prepare(). */ -void -im_region_free( VipsRegion *reg ) -{ - IMAGE *im; +VipsRegion * +vips_region_new( VipsImage *image ) +{ + VipsRegion *region; - if( !reg ) - return; - im = reg->im; - - /* Stop this sequence. - */ - im__call_stop( reg ); - - /* Free any attached memory. - */ - im_region_reset( reg ); - - /* Detach from image. - */ - g_mutex_lock( im->sslock ); - im->regions = g_slist_remove( im->regions, reg ); - g_mutex_unlock( im->sslock ); - reg->im = NULL; - - /* Was this the last region on an image with close_pending? If yes, - * close the image too. - */ - if( !im->regions && im->close_pending ) { -#ifdef DEBUG_IO - printf( "im_region_free: closing pending image \"%s\"\n", - im->filename ); -#endif /*DEBUG_IO*/ - /* Time to close the image. - */ - im->close_pending = 0; - im_close( im ); + region = VIPS_REGION( g_object_new( VIPS_TYPE_REGION, NULL ) ); + g_object_set( region, + "image", image, + NULL ); + if( vips_object_build( VIPS_OBJECT( region ) ) ) { + VIPS_UNREF( region ); + return( NULL ); } - im_free( reg ); - -#ifdef DEBUG - g_mutex_lock( im__global_lock ); - g_assert( g_slist_find( im__regions_all, reg ) ); - im__regions_all = g_slist_remove( im__regions_all, reg ); - printf( "%d regions in vips\n", g_slist_length( im__regions_all ) ); - g_mutex_unlock( im__global_lock ); -#endif /*DEBUG*/ + return( region ); } /* Region should be a pixel buffer. On return, check @@ -420,7 +463,7 @@ im_region_free( VipsRegion *reg ) */ /** - * im_region_buffer: + * vips_region_buffer: * @reg: region to operate upon * @r: #Rect of pixels you need to be able to address * @@ -430,14 +473,14 @@ im_region_free( VipsRegion *reg ) * Returns: 0 on success, or -1 for error. */ int -im_region_buffer( VipsRegion *reg, Rect *r ) +vips_region_buffer( VipsRegion *reg, Rect *r ) { VipsImage *im = reg->im; Rect image; Rect clipped; - im__region_check_ownership( reg ); + vips__region_check_ownership( reg ); /* Clip against image. */ @@ -450,7 +493,7 @@ im_region_buffer( VipsRegion *reg, Rect *r ) /* Test for empty. */ if( im_rect_isempty( &clipped ) ) { - vips_error( "im_region_buffer", + vips_error( "VipsRegion", "%s", _( "valid clipped to nothing" ) ); return( -1 ); } @@ -461,12 +504,12 @@ im_region_buffer( VipsRegion *reg, Rect *r ) * If not, try to reuse the current buffer. */ if( reg->invalid ) { - im_region_reset( reg ); + vips_region_reset( reg ); if( !(reg->buffer = im_buffer_new( im, &clipped )) ) return( -1 ); } else { - /* Don't call im_region_reset() ... we combine buffer unref + /* Don't call vips_region_reset() ... we combine buffer unref * and new buffer ref in one call to reduce malloc/free * cycling. */ @@ -487,7 +530,7 @@ im_region_buffer( VipsRegion *reg, Rect *r ) } /** - * im_region_image: + * vips_region_image: * @reg: region to operate upon * @r: #Rect of pixels you need to be able to address * @@ -498,14 +541,14 @@ im_region_buffer( VipsRegion *reg, Rect *r ) * Returns: 0 on success, or -1 for error. */ int -im_region_image( VipsRegion *reg, Rect *r ) +vips_region_image( VipsRegion *reg, Rect *r ) { Rect image; Rect clipped; /* Sanity check. */ - im__region_check_ownership( reg ); + vips__region_check_ownership( reg ); /* Clip against image. */ @@ -518,7 +561,7 @@ im_region_image( VipsRegion *reg, Rect *r ) /* Test for empty. */ if( im_rect_isempty( &clipped ) ) { - vips_error( "im_region_image", + vips_error( "VipsRegion", "%s", _( "valid clipped to nothing" ) ); return( -1 ); } @@ -526,7 +569,7 @@ im_region_image( VipsRegion *reg, Rect *r ) if( reg->im->data ) { /* We have the whole image available ... easy! */ - im_region_reset( reg ); + vips_region_reset( reg ); /* We can't just set valid = clipped, since this may be an * incompletely calculated memory buffer. Just set valid to r. @@ -545,7 +588,7 @@ im_region_image( VipsRegion *reg, Rect *r ) reg->window->top > clipped.top || reg->window->top + reg->window->height < clipped.top + clipped.height ) { - im_region_reset( reg ); + vips_region_reset( reg ); if( !(reg->window = im_window_ref( reg->im, clipped.top, clipped.height )) ) @@ -564,7 +607,7 @@ im_region_image( VipsRegion *reg, Rect *r ) reg->data = reg->window->data; } else { - vips_error( "im_region_image", + vips_error( "VipsRegion", "%s", _( "bad image type" ) ); return( -1 ); } @@ -573,7 +616,7 @@ im_region_image( VipsRegion *reg, Rect *r ) } /** - * im_region_region: + * vips_region_region: * @reg: region to operate upon * @dest: region to connect to * @r: #Rect of pixels you need to be able to address @@ -597,7 +640,7 @@ im_region_image( VipsRegion *reg, Rect *r ) * Returns: 0 on success, or -1 for error. */ int -im_region_region( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) +vips_region_region( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) { Rect image; Rect wanted; @@ -610,11 +653,11 @@ im_region_region( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) if( !dest->data || VIPS_IMAGE_SIZEOF_PEL( dest->im ) != VIPS_IMAGE_SIZEOF_PEL( reg->im ) ) { - vips_error( "im_region_region", + vips_error( "VipsRegion", "%s", _( "inappropriate region type" ) ); return( -1 ); } - im__region_check_ownership( reg ); + vips__region_check_ownership( reg ); /* We can't test @@ -643,7 +686,7 @@ im_region_region( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) /* Test that dest->valid is large enough. */ if( !im_rect_includesrect( &dest->valid, &wanted ) ) { - vips_error( "im_region_region", + vips_error( "VipsRegion", "%s", _( "dest too small" ) ); return( -1 ); } @@ -662,14 +705,14 @@ im_region_region( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) /* Test for empty. */ if( im_rect_isempty( &final ) ) { - vips_error( "im_region_region", + vips_error( "VipsRegion", "%s", _( "valid clipped to nothing" ) ); return( -1 ); } /* Init new stuff. */ - im_region_reset( reg ); + vips_region_reset( reg ); reg->valid = final; reg->bpl = dest->bpl; reg->data = VIPS_REGION_ADDR( dest, clipped2.left, clipped2.top ); @@ -679,7 +722,7 @@ im_region_region( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) } /** - * im_region_equalsregion: + * vips_region_equalsregion: * @reg1: region to test * @reg2: region to test * @@ -694,7 +737,7 @@ im_region_region( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) * Returns: non-zero on equality. */ int -im_region_equalsregion( VipsRegion *reg1, VipsRegion *reg2 ) +vips_region_equalsregion( VipsRegion *reg1, VipsRegion *reg2 ) { return( reg1->im == reg2->im && im_rect_equalsrect( ®1->valid, ®2->valid ) && @@ -702,7 +745,7 @@ im_region_equalsregion( VipsRegion *reg1, VipsRegion *reg2 ) } /** - * im_region_position: + * vips_region_position: * @reg: region to operate upon * @x: position to move to * @y: position to move to @@ -715,7 +758,7 @@ im_region_equalsregion( VipsRegion *reg1, VipsRegion *reg2 ) * Returns: 0 on success, or -1 for error. */ int -im_region_position( VipsRegion *reg, int x, int y ) +vips_region_position( VipsRegion *reg, int x, int y ) { Rect req, image, clipped; @@ -731,7 +774,7 @@ im_region_position( VipsRegion *reg, int x, int y ) req.height = reg->valid.height; im_rect_intersectrect( &image, &req, &clipped ); if( x < 0 || y < 0 || im_rect_isempty( &clipped ) ) { - vips_error( "im_region_position", "%s", _( "bad position" ) ); + vips_error( "VipsRegion", "%s", _( "bad position" ) ); return( -1 ); } @@ -742,14 +785,14 @@ im_region_position( VipsRegion *reg, int x, int y ) } int -im_region_fill( VipsRegion *reg, Rect *r, im_region_fill_fn fn, void *a ) +vips_region_fill( VipsRegion *reg, Rect *r, VipsRegionFillFn fn, void *a ) { g_assert( reg->im->dtype == VIPS_IMAGE_PARTIAL ); g_assert( reg->im->generate ); /* Should have local memory. */ - if( im_region_buffer( reg, r ) ) + if( vips_region_buffer( reg, r ) ) return( -1 ); /* Evaluate into or, if we've not got calculated pixels. @@ -768,32 +811,7 @@ im_region_fill( VipsRegion *reg, Rect *r, im_region_fill_fn fn, void *a ) } /** - * im_region_print: - * @reg: region to operate upon - * - * Print out interesting fields from @reg. Handy for debug. - */ -void -im_region_print( VipsRegion *reg ) -{ - printf( "VipsRegion: %p, ", reg ); - printf( "im = %p, ", reg->im ); - printf( "valid.left = %d, ", reg->valid.left ); - printf( "valid.top = %d, ", reg->valid.top ); - printf( "valid.width = %d, ", reg->valid.width ); - printf( "valid.height = %d, ", reg->valid.height ); - printf( "type = %d, ", reg->type ); - printf( "data = %p, ", reg->data ); - printf( "bpl = %d, ", reg->bpl ); - printf( "seq = %p, ", reg->seq ); - printf( "thread = %p, ", reg->thread ); - printf( "window = %p, ", reg->window ); - printf( "buffer = %p\n", reg->buffer ); - printf( "invalid = %d\n", reg->invalid ); -} - -/** - * im_region_paint: + * vips_region_paint: * @reg: region to operate upon * @r: area to paint * @value: value to paint @@ -802,10 +820,10 @@ im_region_print( VipsRegion *reg ) * memset(), so it usually needs to be 0 or 255. @r is clipped against * @reg->valid. * - * See also: im_region_black(). + * See also: vips_region_black(). */ void -im_region_paint( VipsRegion *reg, Rect *r, int value ) +vips_region_paint( VipsRegion *reg, Rect *r, int value ) { Rect ovl; @@ -824,21 +842,21 @@ im_region_paint( VipsRegion *reg, Rect *r, int value ) } /** - * im_region_black: + * vips_region_black: * @reg: region to operate upon * * Paints 0 into the valid part of @reg. * - * See also: im_region_paint(). + * See also: vips_region_paint(). */ void -im_region_black( VipsRegion *reg ) +vips_region_black( VipsRegion *reg ) { - im_region_paint( reg, ®->valid, 0 ); + vips_region_paint( reg, ®->valid, 0 ); } /** - * im_region_copy: + * vips_region_copy: * @reg: source region * @dest: destination region * @r: #Rect of pixels you need to copy @@ -849,10 +867,10 @@ im_region_black( VipsRegion *reg ) * positioning the area of pixels at @x, @y. The two regions must have pixels * which are the same size. * - * See also: im_region_paint(). + * See also: vips_region_paint(). */ void -im_region_copy( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) +vips_region_copy( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) { int z; int len = VIPS_IMAGE_SIZEOF_PEL( reg->im ) * r->width; @@ -866,7 +884,7 @@ im_region_copy( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) */ Rect output; - printf( "im_region_copy: sanity check\n" ); + printf( "vips_region_copy: sanity check\n" ); output.left = x; output.top = y; @@ -894,3 +912,317 @@ im_region_copy( VipsRegion *reg, VipsRegion *dest, Rect *r, int x, int y ) q += qlsk; } } + +/* Generate into a region. + */ +static int +vips_region_generate( VipsRegion *reg ) +{ + VipsImage *im = reg->im; + + /* Start new sequence, if necessary. + */ + if( vips__region_start( reg ) ) + return( -1 ); + + /* Ask for evaluation. + */ + if( im->generate( reg, reg->seq, im->client1, im->client2 ) ) + return( -1 ); + + return( 0 ); +} + +/** + * vips_region_prepare: + * @reg: region to prepare + * @r: #Rect of pixels you need to be able to address + * + * vips_region_prepare() fills @reg with pixels. After calling, + * you can address at least the area @r with VIPS_REGION_ADDR() and get + * valid pixels. + * + * vips_region_prepare() runs in-line, that is, computation is done by + * the calling thread, no new threads are involved, and computation + * blocks until the pixels are ready. + * + * Use vips_region_prepare_thread() to calculate an area of pixels with many + * threads. Use im_render_priority() to calculate an area of pixels in the + * background. + * + * See also: vips_region_prepare_thread(), im_render_priority(), + * vips_region_prepare_to(). + * + * Returns: 0 on success, or -1 on error. + */ +int +vips_region_prepare( VipsRegion *reg, Rect *r ) +{ + VipsImage *im = reg->im; + + Rect save = *r; + + vips__region_check_ownership( reg ); + + if( vips_image_test_kill( im ) ) + return( -1 ); + + /* We use save for sanity checking valid: we test at the end that the + * pixels we have generated are indeed all the ones that were asked + * for. + * + * However, r may be clipped by the image size, so we need to clip + * save as well to make sure we don't fail the assert due to that. + */ +{ + Rect image; + + image.left = 0; + image.top = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( &save, &image, &save ); +} + +#ifdef DEBUG + printf( "vips_region_prepare: " + "left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); +#endif /*DEBUG*/ + + switch( im->dtype ) { + case VIPS_IMAGE_PARTIAL: + if( vips_region_fill( reg, r, + (VipsRegionFillFn) vips_region_generate, NULL ) ) + return( -1 ); + + break; + + case VIPS_IMAGE_SETBUF: + case VIPS_IMAGE_SETBUF_FOREIGN: + case VIPS_IMAGE_MMAPIN: + case VIPS_IMAGE_MMAPINRW: + case VIPS_IMAGE_OPENIN: + /* Attach to existing buffer. + */ + if( vips_region_image( reg, r ) ) + return( -1 ); + + break; + + default: + vips_error( "vips_region_prepare", + _( "unable to input from a %s image" ), + VIPS_ENUM_STRING( VIPS_TYPE_DEMAND_STYLE, im->dtype ) ); + return( -1 ); + } + + /* valid should now include all the pixels that were asked for. + */ + g_assert( im_rect_includesrect( ®->valid, &save ) ); + + return( 0 ); +} + +/* We need to make pixels using reg's generate function, and write the result + * to dest. + */ +static int +vips_region_prepare_to_generate( VipsRegion *reg, + VipsRegion *dest, Rect *r, int x, int y ) +{ + IMAGE *im = reg->im; + char *p; + + if( !im->generate ) { + vips_error( "im_prepare_to", "%s", _( "incomplete header" ) ); + return( -1 ); + } + + if( vips_region_region( reg, dest, r, x, y ) ) + return( -1 ); + + /* Remember where reg is pointing now. + */ + p = VIPS_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); + + /* Run sequence into reg. + */ + if( vips_region_generate( reg ) ) + return( -1 ); + + /* The generate function may not have actually made any pixels ... it + * might just have redirected reg to point somewhere else. If it has, + * we need an extra copy operation. + */ + if( VIPS_REGION_ADDR( reg, reg->valid.left, reg->valid.top ) != p ) + vips_region_copy( reg, dest, r, x, y ); + + return( 0 ); +} + +/** + * vips_region_prepare_to: + * @reg: region to prepare + * @dest: region to write to + * @r: #Rect of pixels you need to be able to address + * @x: postion of @r in @dest + * @y: postion of @r in @dest + * + * Like vips_region_prepare(): fill @reg with data, ready to be read from by + * our caller. Unlike vips_region_prepare(), rather than allocating memory + * local to @reg for the result, we guarantee that we will fill the pixels + * in @dest at offset @x, @y. In other words, we generate an extra copy + * operation if necessary. + * + * Also unlike vips_region_prepare(), @dest is not set up for writing for + * you with + * vips_region_buffer(). You can + * point @dest at anything, and pixels really will be written there. + * This makes vips_prepare_to() useful for making the ends of pipelines, since + * it (effectively) makes a break in the pipe. + * + * See also: vips_region_prepare(), vips_sink_disc(). + * + * Returns: 0 on success, or -1 on error + */ +int +vips_region_prepare_to( VipsRegion *reg, + VipsRegion *dest, Rect *r, int x, int y ) +{ + VipsImage *im = reg->im; + Rect image; + Rect wanted; + Rect clipped; + Rect clipped2; + Rect final; + + if( vips_image_test_kill( im ) ) + return( -1 ); + + /* Sanity check. + */ + if( !dest->data || + dest->im->BandFmt != reg->im->BandFmt || + dest->im->Bands != reg->im->Bands ) { + vips_error( "im_prepare_to", + "%s", _( "inappropriate region type" ) ); + return( -1 ); + } + + /* clip r first against the size of reg->im, then again against the + * memory we have available to write to on dest. Just like + * im_region_region() + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + g_assert( clipped.left == r->left ); + g_assert( clipped.top == r->top ); + + wanted.left = x + (clipped.left - r->left); + wanted.top = y + (clipped.top - r->top); + wanted.width = clipped.width; + wanted.height = clipped.height; + + /* Test that dest->valid is large enough. + */ + if( !im_rect_includesrect( &dest->valid, &wanted ) ) { + vips_error( "im_prepare_to", "%s", _( "dest too small" ) ); + return( -1 ); + } + + im_rect_intersectrect( &wanted, &dest->valid, &clipped2 ); + + /* Translate back to reg's coordinate space and set as valid. + */ + final.left = r->left + (clipped2.left - wanted.left); + final.top = r->top + (clipped2.top - wanted.top); + final.width = clipped2.width; + final.height = clipped2.height; + + x = clipped2.left; + y = clipped2.top; + + if( im_rect_isempty( &final ) ) { + vips_error( "im_prepare_to", + "%s", _( "valid clipped to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "im_prepare_to: left = %d, top = %d, width = %d, height = %d\n", + final.left, final.top, final.width, final.height ); +#endif /*DEBUG*/ + + /* Input or output image type? + */ + switch( im->dtype ) { + case VIPS_IMAGE_OPENOUT: + case VIPS_IMAGE_PARTIAL: + /* We are generating with a sequence. + */ + if( vips_region_prepare_to_generate( reg, dest, &final, x, y ) ) + return( -1 ); + + break; + + case VIPS_IMAGE_MMAPIN: + case VIPS_IMAGE_MMAPINRW: + case VIPS_IMAGE_OPENIN: + /* Attach to existing buffer and copy to dest. + */ + if( vips_region_image( reg, &final ) ) + return( -1 ); + vips_region_copy( reg, dest, &final, x, y ); + + break; + + case VIPS_IMAGE_SETBUF: + case VIPS_IMAGE_SETBUF_FOREIGN: + /* Could be either input or output. If there is a generate + * function, we are outputting. + */ + if( im->generate ) { + if( vips_region_prepare_to_generate( reg, + dest, &final, x, y ) ) + return( -1 ); + } + else { + if( vips_region_image( reg, &final ) ) + return( -1 ); + vips_region_copy( reg, dest, &final, x, y ); + } + + break; + + default: + vips_error( "im_prepare_to", _( "unable to input from a " + "%s image" ), im_dtype2char( im->dtype ) ); + return( -1 ); + } + + /* We've written fresh pixels to dest, it's no longer invalid (if it + * was). + * + * We need this extra thing here because, unlike + * vips_region_prepare(), we don't vips_region_buffer() dest before + * writing it. + */ + dest->invalid = FALSE; + + return( 0 ); +} + +int +vips_region_prepare_many( VipsRegion **reg, Rect *r ) +{ + for( ; *reg; ++reg ) + if( vips_region_prepare( *reg, r ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/iofuncs/sink.c b/libvips/iofuncs/sink.c index 334afb5d..d5ec94b4 100644 --- a/libvips/iofuncs/sink.c +++ b/libvips/iofuncs/sink.c @@ -93,7 +93,7 @@ typedef struct _SinkThreadState { /* The region we walk over sink.t copy. We can't use * parent_object.reg, it's defined on the outer image. */ - REGION *reg; + VipsRegion *reg; } SinkThreadState; typedef struct _SinkThreadStateClass { @@ -131,7 +131,7 @@ sink_thread_state_dispose( GObject *gobject ) Sink *sink = (Sink *) ((VipsThreadState *) state)->a; sink_call_stop( sink, state ); - VIPS_FREEF( im_region_free, state->reg ); + VIPS_FREEF( g_object_unref, state->reg ); G_OBJECT_CLASS( sink_thread_state_parent_class )->dispose( gobject ); } @@ -163,7 +163,7 @@ sink_thread_state_build( VipsObject *object ) SinkThreadState *state = (SinkThreadState *) object; Sink *sink = (Sink *) ((VipsThreadState *) state)->a; - if( !(state->reg = im_region_create( sink->t )) || + if( !(state->reg = vips_region_new( sink->t )) || sink_call_start( sink, state ) ) return( -1 ); @@ -278,7 +278,7 @@ sink_work( VipsThreadState *state, void *a ) SinkThreadState *sstate = (SinkThreadState *) state; Sink *sink = (Sink *) a; - if( im_prepare( sstate->reg, &state->pos ) || + if( vips_region_prepare( sstate->reg, &state->pos ) || sink->generate( sstate->reg, sstate->seq, sink->a, sink->b ) ) return( -1 ); diff --git a/libvips/iofuncs/sinkdisc.c b/libvips/iofuncs/sinkdisc.c index 93e11926..5a880548 100644 --- a/libvips/iofuncs/sinkdisc.c +++ b/libvips/iofuncs/sinkdisc.c @@ -70,7 +70,7 @@ typedef struct _WriteBuffer { struct _Write *write; - REGION *region; /* Pixels */ + VipsRegion *region; /* Pixels */ Rect area; /* Part of image this region covers */ im_semaphore_t go; /* Start bg thread loop */ im_semaphore_t nwrite; /* Number of threads writing to region */ @@ -168,7 +168,7 @@ wbuffer_free( WriteBuffer *wbuffer ) wbuffer->thread = NULL; } - VIPS_FREEF( im_region_free, wbuffer->region ); + VIPS_FREEF( g_object_unref, wbuffer->region ); im_semaphore_destroy( &wbuffer->go ); im_semaphore_destroy( &wbuffer->nwrite ); im_semaphore_destroy( &wbuffer->done ); @@ -234,14 +234,14 @@ wbuffer_new( Write *write ) wbuffer->thread = NULL; wbuffer->kill = FALSE; - if( !(wbuffer->region = im_region_create( write->im )) ) { + if( !(wbuffer->region = vips_region_new( write->im )) ) { wbuffer_free( wbuffer ); return( NULL ); } /* The worker threads need to be able to move the buffers around. */ - im__region_no_ownership( wbuffer->region ); + vips__region_no_ownership( wbuffer->region ); #ifdef HAVE_THREADS /* Make this last (picks up parts of wbuffer on startup). @@ -315,11 +315,11 @@ wbuffer_position( WriteBuffer *wbuffer, int top, int height ) /* The workers take turns to move the buffers. */ - im__region_take_ownership( wbuffer->region ); + vips__region_take_ownership( wbuffer->region ); - result = im_region_buffer( wbuffer->region, &wbuffer->area ); + result = vips_region_buffer( wbuffer->region, &wbuffer->area ); - im__region_no_ownership( wbuffer->region ); + vips__region_no_ownership( wbuffer->region ); /* This should be an exclusive buffer, hopefully. */ diff --git a/libvips/iofuncs/sinkmemory.c b/libvips/iofuncs/sinkmemory.c index 63843111..27c04420 100644 --- a/libvips/iofuncs/sinkmemory.c +++ b/libvips/iofuncs/sinkmemory.c @@ -58,7 +58,7 @@ typedef struct _Sink { /* A big region for the image memory. All the threads write to this. */ - REGION *all; + VipsRegion *all; /* The position we're at in the image. */ @@ -75,7 +75,7 @@ typedef struct _Sink { static void sink_free( Sink *sink ) { - VIPS_FREEF( im_region_free, sink->all ); + VIPS_FREEF( g_object_unref, sink->all ); } static int @@ -92,8 +92,8 @@ sink_init( Sink *sink, VipsImage *im ) all.width = im->Xsize; all.height = im->Ysize; - if( !(sink->all = im_region_create( im )) || - im_region_image( sink->all, &all ) ) { + if( !(sink->all = vips_region_new( im )) || + vips_region_image( sink->all, &all ) ) { sink_free( sink ); return( -1 ); } @@ -148,7 +148,7 @@ sink_work( VipsThreadState *state, void *a ) { Sink *sink = (Sink *) a; - if( im_prepare_to( state->reg, sink->all, + if( vips_region_prepare_to( state->reg, sink->all, &state->pos, state->pos.left, state->pos.top ) ) return( -1 ); diff --git a/libvips/iofuncs/sinkscreen.c b/libvips/iofuncs/sinkscreen.c index 03c36642..e780ed9e 100644 --- a/libvips/iofuncs/sinkscreen.c +++ b/libvips/iofuncs/sinkscreen.c @@ -88,7 +88,7 @@ typedef struct { struct _Render *render; Rect area; /* Place here (unclipped) */ - REGION *region; /* REGION with the pixels */ + VipsRegion *region; /* VipsRegion with the pixels */ /* The tile contains calculated pixels. Though the region may have been * invalidated behind our backs: we have to check that too. @@ -208,7 +208,7 @@ tile_free( Tile *tile ) { VIPS_DEBUG_MSG_AMBER( "tile_free\n" ); - VIPS_FREEF( im_region_free, tile->region ); + VIPS_FREEF( g_object_unref, tile->region ); im_free( tile ); return( NULL ); @@ -426,9 +426,10 @@ render_work( VipsThreadState *state, void *a ) VIPS_DEBUG_MSG( "calculating tile %p %dx%d\n", tile, tile->area.left, tile->area.top ); - if( im_prepare_to( state->reg, tile->region, + if( vips_region_prepare_to( state->reg, tile->region, &tile->area, tile->area.left, tile->area.top ) ) { - VIPS_DEBUG_MSG_RED( "render_work: im_prepare_to() failed\n" ); + VIPS_DEBUG_MSG_RED( "render_work: " + "vips_region_prepare_to() failed\n" ); return( -1 ); } tile->painted = TRUE; @@ -665,7 +666,7 @@ tile_new( Render *render ) tile->dirty = FALSE; tile->ticks = render->ticks; - if( !(tile->region = im_region_create( render->in )) ) { + if( !(tile->region = vips_region_new( render->in )) ) { (void) tile_free( tile ); return( NULL ); } @@ -699,7 +700,7 @@ render_tile_add( Tile *tile, Rect *area ) /* Ignore buffer allocate errors, there's not much we could do with * them. */ - if( im_region_buffer( tile->region, &tile->area ) ) + if( vips_region_buffer( tile->region, &tile->area ) ) VIPS_DEBUG_MSG_RED( "render_tile_add: " "buffer allocate failed\n" ); @@ -739,7 +740,7 @@ tile_touch( Tile *tile ) /* Queue a tile for calculation. */ static void -tile_queue( Tile *tile, REGION *reg ) +tile_queue( Tile *tile, VipsRegion *reg ) { Render *render = tile->render; @@ -772,7 +773,7 @@ tile_queue( Tile *tile, REGION *reg ) */ g_mutex_unlock( render->lock ); - if( im_prepare_to( reg, tile->region, + if( vips_region_prepare_to( reg, tile->region, &tile->area, tile->area.left, tile->area.top ) ) VIPS_DEBUG_MSG_RED( "tile_queue: prepare failed\n" ); @@ -813,7 +814,7 @@ render_tile_get_painted( Render *render ) * or if we've no threads or no notify, calculate immediately. */ static Tile * -render_tile_request( Render *render, REGION *reg, Rect *area ) +render_tile_request( Render *render, VipsRegion *reg, Rect *area ) { Tile *tile; @@ -863,7 +864,7 @@ render_tile_request( Render *render, REGION *reg, Rect *area ) /* Copy what we can from the tile into the region. */ static void -tile_copy( Tile *tile, REGION *to ) +tile_copy( Tile *tile, VipsRegion *to ) { Rect ovlap; int y; @@ -884,9 +885,9 @@ tile_copy( Tile *tile, REGION *to ) tile, tile->area.left, tile->area.top ); for( y = ovlap.top; y < IM_RECT_BOTTOM( &ovlap ); y++ ) { - PEL *p = (PEL *) IM_REGION_ADDR( tile->region, + PEL *p = (PEL *) VIPS_REGION_ADDR( tile->region, ovlap.left, y ); - PEL *q = (PEL *) IM_REGION_ADDR( to, ovlap.left, y ); + PEL *q = (PEL *) VIPS_REGION_ADDR( to, ovlap.left, y ); memcpy( q, p, len ); } @@ -894,7 +895,7 @@ tile_copy( Tile *tile, REGION *to ) else { VIPS_DEBUG_MSG( "tile_copy: zero filling for %p %dx%d\n", tile, tile->area.left, tile->area.top ); - im_region_paint( to, &ovlap, 0 ); + vips_region_paint( to, &ovlap, 0 ); } } @@ -903,16 +904,16 @@ image_start( IMAGE *out, void *a, void *b ) { Render *render = (Render *) a; - return( im_region_create( render->in ) ); + return( vips_region_new( render->in ) ); } /* Loop over the output region, filling with data from cache. */ static int -image_fill( REGION *out, void *seq, void *a, void *b ) +image_fill( VipsRegion *out, void *seq, void *a, void *b ) { Render *render = (Render *) a; - REGION *reg = (REGION *) seq; + VipsRegion *reg = (VipsRegion *) seq; Rect *r = &out->valid; int x, y; @@ -959,9 +960,9 @@ image_fill( REGION *out, void *seq, void *a, void *b ) static int image_stop( void *seq, void *a, void *b ) { - REGION *reg = (REGION *) seq; + VipsRegion *reg = (VipsRegion *) seq; - im_region_free( reg ); + g_object_unref( reg ); return( 0 ); } @@ -969,7 +970,7 @@ image_stop( void *seq, void *a, void *b ) /* The mask image is 255 / 0 for the state of painted for each tile. */ static int -mask_fill( REGION *out, void *seq, void *a, void *b ) +mask_fill( VipsRegion *out, void *seq, void *a, void *b ) { #ifdef HAVE_THREADS Render *render = (Render *) a; @@ -1005,12 +1006,12 @@ mask_fill( REGION *out, void *seq, void *a, void *b ) /* Only mark painted tiles containing valid pixels. */ - im_region_paint( out, &area, value ); + vips_region_paint( out, &area, value ); } g_mutex_unlock( render->lock ); #else /*!HAVE_THREADS*/ - im_region_paint( out, &out->valid, 255 ); + vips_region_paint( out, &out->valid, 255 ); #endif /*HAVE_THREADS*/ return( 0 ); @@ -1044,7 +1045,8 @@ mask_fill( REGION *out, void *seq, void *a, void *b ) * priority, negative numbers are low priority, positive numbers high * priority. * - * Calls to im_prepare() on @out return immediately and hold whatever is + * Calls to vips_region_prepare() on @out return immediately and hold + * whatever is * currently in cache for that #Rect (check @mask to see which parts of the * #Rect are valid). Any pixels in the #Rect which are not in cache are added * to a queue, and the @notify callback will trigger when those pixels are @@ -1056,10 +1058,11 @@ mask_fill( REGION *out, void *seq, void *a, void *b ) * ready. In a glib-based application, this is easily done with g_idle_add(). * * If @notify is %NULL then im_render_priority() runs synchronously. - * im_prepare() on @out will always block until the pixels have been + * vips_region_prepare() on @out will always block until the pixels have been * calculated. * - * See also: im_cache(), im_tile_cache(), im_prepare(), vips_sink_disc(), + * See also: im_cache(), im_tile_cache(), vips_region_prepare(), + * vips_sink_disc(), * vips_sink(). * * Returns: 0 on sucess, -1 on error.