more dzsave overlap fixups

works with odd-sized tiles, overlaps handled correctly
This commit is contained in:
John Cupitt 2012-07-07 13:58:50 +01:00
parent fb23c3df7f
commit 57492bee16

View File

@ -3,9 +3,10 @@
* 21/3/12
* - from the tiff pyramid writer
*
* 5/7/12
* - make tiles down to 1x1 pixels
* - oop make right-hand edge tiles
* 5/7/12 (thanks Alexander Koshman)
* - make tiles down to 1x1 pixels
* - oop make right-hand edge tiles
* - improve overlap handling
*/
/*
@ -35,9 +36,10 @@
*/
/*
*/
#define DEBUG_VERBOSE
#define DEBUG
*/
#define VIPS_DEBUG
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -60,6 +62,15 @@ struct _Layer {
VipsImage *image; /* The image we build */
/* The top of this strip of tiles, excluding the overlap. Go up from
* this to get to the top pixel we write in each one.
*/
int y;
/* The next line we write to in this strip.
*/
int write_y;
VipsRegion *strip; /* The current strip of pixels */
VipsRegion *copy; /* Pixels we copy to the next strip */
@ -153,8 +164,11 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int w, int h )
vips__region_no_ownership( layer->strip );
vips__region_no_ownership( layer->copy );
/* Build a line of tiles here.
/* Build a line of tiles here. Normally strips are height + 2 *
* overlap, but the first row is missing the top edge.
*/
layer->y = 0;
layer->write_y = 0;
strip.left = 0;
strip.top = 0;
strip.width = w;
@ -198,33 +212,24 @@ pyramid_mkdir( VipsForeignSaveDz *dz )
return( 0 );
}
/* Shrink a region by a factor of two, writing the result to a specified
* offset in another region. VIPS_CODING_LABQ only.
/* Generate area @target in @to using pixels in @from. VIPS_CODING_LABQ only.
*/
static void
shrink_region_labpack( VipsRegion *from, VipsRegion *to )
shrink_region_labpack( VipsRegion *from, VipsRegion *to, VipsRect *target )
{
int ls = VIPS_REGION_LSKIP( from );
int x, y;
VipsRect target;
/* Calculate output size and position.
*/
target.left = from->valid.left / 2;
target.top = from->valid.top / 2;
target.width = from->valid.width / 2;
target.height = from->valid.height / 2;
for( y = 0; y < target.height; y++ ) {
for( y = 0; y < target->height; y++ ) {
VipsPel *p = VIPS_REGION_ADDR( from,
from->valid.left, from->valid.top + y * 2 );
target->left * 2, (target->top + y) * 2 );
VipsPel *q = VIPS_REGION_ADDR( to,
target.left, target.top + y );
target->left, target->top + y );
/* Ignore the extra bits for speed.
*/
for( x = 0; x < target.width; x++ ) {
for( x = 0; x < target->width; x++ ) {
signed char *sp = (signed char *) p;
unsigned char *up = (unsigned char *) p;
@ -247,7 +252,7 @@ shrink_region_labpack( VipsRegion *from, VipsRegion *to )
}
#define SHRINK_TYPE_INT( TYPE ) \
for( x = 0; x < target.width; x++ ) { \
for( x = 0; x < target->width; x++ ) { \
TYPE *tp = (TYPE *) p; \
TYPE *tp1 = (TYPE *) (p + ls); \
TYPE *tq = (TYPE *) q; \
@ -266,7 +271,7 @@ shrink_region_labpack( VipsRegion *from, VipsRegion *to )
}
#define SHRINK_TYPE_FLOAT( TYPE ) \
for( x = 0; x < target.width; x++ ) { \
for( x = 0; x < target->width; x++ ) { \
TYPE *tp = (TYPE *) p; \
TYPE *tp1 = (TYPE *) (p + ls); \
TYPE *tq = (TYPE *) q; \
@ -284,31 +289,22 @@ shrink_region_labpack( VipsRegion *from, VipsRegion *to )
q += ps; \
}
/* Shrink a region by a factor of two, writing the result to a specified
* offset in another region. n-band, non-complex.
/* Generate area @target in @to using pixels in @from. Non-complex.
*/
static void
shrink_region( VipsRegion *from, VipsRegion *to )
shrink_region_uncoded( VipsRegion *from, VipsRegion *to, VipsRect *target )
{
int ls = VIPS_REGION_LSKIP( from );
int ps = VIPS_IMAGE_SIZEOF_PEL( from->im );
int nb = from->im->Bands;
int x, y, z;
VipsRect target;
/* Calculate output size and position.
*/
target.left = from->valid.left / 2;
target.top = from->valid.top / 2;
target.width = from->valid.width / 2;
target.height = from->valid.height / 2;
for( y = 0; y < target.height; y++ ) {
for( y = 0; y < target->height; y++ ) {
VipsPel *p = VIPS_REGION_ADDR( from,
from->valid.left, from->valid.top + y * 2 );
target->left * 2, (target->top + y) * 2 );
VipsPel *q = VIPS_REGION_ADDR( to,
target.left, target.top + y );
target->left, target->top + y );
/* Process this line of pels.
*/
@ -342,113 +338,169 @@ static int
strip_save( Layer *layer )
{
VipsForeignSaveDz *dz = layer->dz;
VipsRegion *strip = layer->strip;
VipsImage *image;
VipsRect strip, image;
VipsImage *im;
int x;
if( !(image = vips_image_new_from_memory(
VIPS_REGION_ADDR( strip, 0, strip->valid.top ),
strip->valid.width, strip->valid.height,
strip->im->Bands, strip->im->BandFmt )) )
strip.left = 0;
strip.top = layer->y - dz->overlap;
strip.width = layer->image->Xsize;
strip.height = dz->tile_height + 2 * dz->overlap;
image.left = 0;
image.top = 0;
image.width = layer->image->Xsize;
image.height = layer->image->Ysize;
vips_rect_intersectrect( &image, &strip, &strip );
if( !(im = vips_image_new_from_memory(
VIPS_REGION_ADDR( layer->strip, 0, strip.top ),
strip.width, strip.height,
layer->image->Bands, layer->image->BandFmt )) )
return( -1 );
for( x = 0; x < strip->valid.width; x += dz->tile_width ) {
for( x = 0; x < strip.width; x += dz->tile_width ) {
VipsImage *extr;
VipsRect tile;
char str[1000];
VipsBuf buf = VIPS_BUF_STATIC( str );
tile.left = x;
tile.top = strip->valid.top;
tile.width = dz->tile_width + dz->overlap;
tile.height = dz->tile_height + dz->overlap;
vips_rect_intersectrect( &tile, &strip->valid, &tile );
tile.left = x - dz->overlap;
tile.top = strip.top;
tile.width = dz->tile_width + 2 * dz->overlap;
tile.height = strip.height;
vips_rect_intersectrect( &tile, &strip, &tile );
/* Extract relative to the strip top-left corner.
*/
if( vips_extract_area( image, &extr,
if( vips_extract_area( im, &extr,
tile.left, 0, tile.width, tile.height, NULL ) ) {
g_object_unref( image );
g_object_unref( im );
return( -1 );
}
vips_buf_appendf( &buf, "%s/%d/%d_%d%s",
dz->dirname, layer->n,
x / dz->tile_width,
strip->valid.top / dz->tile_height,
layer->y / dz->tile_height,
dz->suffix );
if( vips_image_write_to_file( extr, vips_buf_all( &buf ) ) ) {
g_object_unref( image );
g_object_unref( im );
g_object_unref( extr );
return( -1 );
}
g_object_unref( extr );
}
g_object_unref( image );
g_object_unref( im );
return( 0 );
}
/* A new strip of pixels has arrived!
* - write
* - shrink to the layer below
* - if that's filled, recurse
static int strip_arrived( Layer *layer );
/* Shrink what pixels we can from this layer into the layer below. If the
* layer below fills, recurse.
*/
static int
strip_shrink( Layer *layer )
{
VipsForeignSaveDz *dz = layer->dz;
VipsForeignSave *save = VIPS_FOREIGN_SAVE( dz );
Layer *below = layer->below;
VipsRegion *from = layer->strip;
VipsRegion *to = below->strip;
VipsRect target;
VipsRect source;
/* Our pixels might cross a strip boundary in the layer below, so we
* have to write repeatedly until we run out of pixels.
*/
for(;;) {
/* The pixels the layer below needs.
*/
target.left = 0;
target.top = below->write_y;
target.width = below->image->Xsize;
target.height = to->valid.height;
vips_rect_intersectrect( &target, &to->valid, &target );
/* Those pixels need this area of this layer.
*/
source.left = target.left * 2;
source.top = target.top * 2;
source.width = target.width * 2;
source.height = target.height * 2;
vips_rect_intersectrect( &source, &from->valid, &source );
/* So these are the pixels in the layer below we can provide.
*/
target.left = source.left / 2;
target.top = source.top / 2;
target.width = source.width / 2;
target.height = source.height / 2;
/* Are we empty? All done.
*/
if( source.height < 2 )
break;
if( save->ready->Coding == VIPS_CODING_NONE )
shrink_region_uncoded( from, to, &target );
else
shrink_region_labpack( from, to, &target );
below->write_y += target.height;
/* If we've filled the strip of the layer below, let it know.
*/
if( below->write_y == VIPS_RECT_BOTTOM( &to->valid ) &&
strip_arrived( below ) )
return( -1 );
}
return( 0 );
}
/* A new strip of pixels has arrived! The strip region has enough pixels in to
* write a line of tiles.
*
* - write a line of tiles
* - shrink what we can to the layer below
* - move our strip down ready for more stuff, copying the overlap
*/
static int
strip_arrived( Layer *layer )
{
VipsForeignSaveDz *dz = layer->dz;
VipsForeignSave *save = VIPS_FOREIGN_SAVE( dz );
Layer *below = layer->below;
VipsRect target;
VipsRect new_strip;
VipsRect overlap;
if( strip_save( layer ) )
return( -1 );
/* The pixels we can make on the layer below.
*/
target.left = layer->strip->valid.left / 2;
target.top = layer->strip->valid.top / 2;
target.width = layer->strip->valid.width / 2;
target.height = layer->strip->valid.height / 2;
if( layer->below &&
strip_shrink( layer ) )
return( -1 );
/* Can have empty strips if this is a 1-pixel high thing at the end.
*/
if( !vips_rect_isempty( &target ) &&
below ) {
/* Shrink into place.
*/
if( save->ready->Coding == VIPS_CODING_NONE )
shrink_region( layer->strip, below->strip );
else
shrink_region_labpack( layer->strip, below->strip );
/* If we've filled the strip of the layer below, recurse.
*/
if( VIPS_RECT_BOTTOM( &target ) ==
VIPS_RECT_BOTTOM( &below->strip->valid ) &&
strip_arrived( below ) )
return( -1 );
}
/* Move our strip down the image.
/* Position our strip down the image.
*/
layer->y += dz->tile_height;
new_strip.left = 0;
new_strip.top = layer->strip->valid.top + layer->strip->valid.height;
new_strip.top = layer->y - dz->overlap;
new_strip.width = layer->image->Xsize;
new_strip.height = dz->tile_height + dz->overlap;
new_strip.height = dz->tile_height + 2 * dz->overlap;
/* What pixels that we will need do we already have? Save them in
* overlap.
*/
vips_rect_intersectrect( &new_strip, &layer->strip->valid, &overlap );
if( !vips_rect_isempty( &overlap ) ) {
/* There are some pixels we need to copy over.
*/
if( vips_region_buffer( layer->copy, &overlap ) )
return( -1 );
vips_region_copy( layer->strip, layer->copy,
@ -458,6 +510,8 @@ strip_arrived( Layer *layer )
if( vips_region_buffer( layer->strip, &new_strip ) )
return( -1 );
/* And copy back again.
*/
if( !vips_rect_isempty( &overlap ) )
vips_region_copy( layer->copy, layer->strip,
&overlap, overlap.left, overlap.top );
@ -465,34 +519,51 @@ strip_arrived( Layer *layer )
return( 0 );
}
/* Another strip of image pixels from vips_sink_disc(). Recursively write
* down the pyramid.
/* Another strip of image pixels from vips_sink_disc(). Write into the top
* pyramid layer.
*/
static int
pyramid_strip( VipsRegion *region, VipsRect *area, void *a )
{
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) a;
int y;
Layer *layer = dz->layer;
y = 0;
while( y < area->height ) {
VipsRect overlap;
for(;;) {
VipsRect target;
/* Write what we can into the current top strip.
/* The bit of strip that needs filling.
*/
vips_rect_intersectrect( area, &dz->layer->strip->valid,
&overlap );
vips_region_copy( region, dz->layer->strip,
&overlap, overlap.left, overlap.top );
target.left = 0;
target.top = layer->write_y;
target.width = layer->image->Xsize;
target.height = layer->strip->valid.height;
vips_rect_intersectrect( &target,
&layer->strip->valid, &target );
/* If we've filled the strip, write.
/* Clip against what we have available.
*/
if( VIPS_RECT_BOTTOM( &overlap ) ==
VIPS_RECT_BOTTOM( &dz->layer->strip->valid ) &&
strip_arrived( dz->layer ) )
vips_rect_intersectrect( &target, area, &target );
/* Are we empty? All done.
*/
if( vips_rect_isempty( &target ) )
break;
/* And copy those pixels in.
*
* FIXME: If the strip fits inside the region we've just
* received, we could skip the copy.
*/
vips_region_copy( region, layer->strip,
&target, target.left, target.top );
layer->write_y += target.height;
/* If we've filled the strip, let it know.
*/
if( layer->write_y ==
VIPS_RECT_BOTTOM( &layer->strip->valid ) &&
strip_arrived( layer ) )
return( -1 );
y += overlap.height;
}
return( 0 );
@ -508,18 +579,6 @@ vips_foreign_save_dz_build( VipsObject *object )
build( object ) )
return( -1 );
/* When we do the /2 shrink we need to be sure that we won't get any
* left-over scan lines.
*/
if( dz->overlap % 2 != 0 ||
dz->tile_width % 2 != 0 ||
dz->tile_height % 2 != 0 ) {
vips_error( "dzsave",
"%s", _( "tile width, height and overlap must all "
"be even" ) );
return( -1 );
}
if( dz->overlap >= dz->tile_width ||
dz->overlap >= dz->tile_height ) {
vips_error( "dzsave",