libvips/libsrc/iofuncs/im_prepare.c
2007-08-29 16:23:50 +00:00

385 lines
8.7 KiB
C

/* 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
*/
/*
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 <string.h>
#include <assert.h>
#include <vips/vips.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#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 );
}
int
im__test_kill( IMAGE *im )
{
/* Has kill been set for this image? If yes, abort evaluation.
*/
if( im->kill ) {
im_error( "im__test_kill", _( "killed for image \"%s\"" ),
im->filename );
return( -1 );
}
return( 0 );
}
/* Make REGION reg ready for input of area r. For most image types, we can
* just im_attach, for PARTIAL though we may need to generate.
*/
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 IM_PARTIAL:
if( im_region_fill( reg, r,
(im_region_fill_fn) fill_region, NULL ) )
return( -1 );
break;
case IM_SETBUF:
case IM_SETBUF_FOREIGN:
case IM_MMAPIN:
case IM_MMAPINRW:
case IM_OPENIN:
/* Attach to existing buffer.
*/
if( im_region_image( reg, r ) )
return( -1 );
break;
default:
im_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.
*/
assert( im_rect_includesrect( &reg->valid, &save ) );
return( 0 );
}
/* Copy from one region to another. Copy area r from inside reg to dest,
* positioning the area of pixels at x, y.
*/
void
im__copy_region( REGION *reg, REGION *dest, Rect *r, int x, int y )
{
int z;
int len = IM_IMAGE_SIZEOF_PEL( reg->im ) * r->width;
char *p = IM_REGION_ADDR( reg, r->left, r->top );
char *q = IM_REGION_ADDR( dest, x, y );
int plsk = IM_REGION_LSKIP( reg );
int qlsk = IM_REGION_LSKIP( dest );
#ifdef DEBUG
/* Find the area we will write to in dest.
*/
Rect output;
printf( "im__copy_region: sanity check\n" );
output.left = x;
output.top = y;
output.width = r->width;
output.height = r->height;
/* Must be inside dest->valid.
*/
assert( im_rect_includesrect( &dest->valid, &output ) );
/* Check the area we are reading from in reg.
*/
assert( im_rect_includesrect( &reg->valid, r ) );
#endif /*DEBUG*/
for( z = 0; z < r->height; z++ ) {
memcpy( q, p, len );
p += plsk;
q += qlsk;
}
}
/* 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 ) {
im_error( "im_prepare_to", _( "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__copy_region( reg, dest, r, x, y );
return( 0 );
}
/* 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.
*/
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 ) {
im_error( "im_prepare_to", _( "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 );
assert( clipped.left == r->left );
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 ) ) {
im_error( "im_prepare_to", _( "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 ) ) {
im_error( "im_prepare_to", _( "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 IM_OPENOUT:
case IM_PARTIAL:
/* We are generating with a sequence.
*/
if( im_prepare_to_generate( reg, dest, &final, x, y ) )
return( -1 );
break;
case IM_MMAPIN:
case IM_MMAPINRW:
case IM_OPENIN:
/* Attach to existing buffer and copy to dest.
*/
if( im_region_image( reg, &final ) )
return( -1 );
im__copy_region( reg, dest, &final, x, y );
break;
case IM_SETBUF:
case IM_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__copy_region( reg, dest, &final, x, y );
}
break;
default:
im_error( "im_prepare_to", _( "unable to input from a "
"%s image" ), im_dtype2char( im->dtype ) );
return( -1 );
}
return( 0 );
}
int
im_prepare_many( REGION **reg, Rect *r )
{
for( ; *reg; ++reg )
if( im_prepare( *reg, r ) )
return( -1 );
return( 0 );
}