Merge branch 'fix-dzsave-overlap'

This commit is contained in:
John Cupitt 2015-09-10 14:08:42 +01:00
commit 96796d3202
3 changed files with 161 additions and 26 deletions

View File

@ -1,4 +1,4 @@
7/5/15 started 8.1.0
7/5/15 starteld 8.1.0
- add vips_premultiply(), vips_unpremultiply()
- change the alpha range rules for vips_flatten() to match vips_premultiply()
- vipsthumbnail uses vips_resize() rather than its own code
@ -17,6 +17,7 @@
- can now set any jpeg exif tag, not just modify existing tags
- add vips_hist_entropy()
- vips_log(), vips_log10() are zero-avoiding
- better overlap handling for dzsave, thanks robclouth
7/5/15 started 8.0.3
- dzsave and tif pyr write could fail for some image dimensions, thanks Jonas

View File

@ -53,6 +53,8 @@
* - use a better temp dir name for fs dz output
* 8/8/15
* - allow zip > 4gb if we have a recent libgsf
* 9/9/15
* - better overlap handling, thanks robclouth
*/
/*
@ -375,8 +377,7 @@ struct _Layer {
*/
VipsImage *image;
/* 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.
/* The top of this strip of tiles.
*/
int y;
@ -414,6 +415,11 @@ struct _VipsForeignSaveDz {
Layer *layer; /* x2 shrink pyr layer */
/* We step by tile_size - overlap as we move across the image ...
* make a note of it.
*/
int tile_step;
/* Count zoomify tiles we write.
*/
int tile_count;
@ -481,7 +487,8 @@ vips_foreign_save_dz_dispose( GObject *gobject )
VIPS_FREE( dz->root_name );
VIPS_FREE( dz->file_suffix );
G_OBJECT_CLASS( vips_foreign_save_dz_parent_class )->dispose( gobject );
G_OBJECT_CLASS( vips_foreign_save_dz_parent_class )->
dispose( gobject );
}
/* Build a pyramid.
@ -503,8 +510,8 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above,
layer->width = width;
layer->height = height;
layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size;
layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size;
layer->tiles_across = ROUND_UP( width, dz->tile_step ) / dz->tile_step;
layer->tiles_down = ROUND_UP( height, dz->tile_step ) / dz->tile_step;
layer->real_pixels = *real_pixels;
@ -543,8 +550,7 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above,
vips__region_no_ownership( layer->strip );
vips__region_no_ownership( layer->copy );
/* Build a line of tiles here. Normally strips are height + 2 *
* overlap, but the first row is missing the top edge.
/* Build a line of tiles here.
*
* Expand the strip if necessary to make sure we have an even
* number of lines.
@ -554,7 +560,7 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above,
strip.left = 0;
strip.top = 0;
strip.width = layer->image->Xsize;
strip.height = dz->tile_size + dz->overlap;
strip.height = dz->tile_size;
if( (strip.height & 1) == 1 )
strip.height += 1;
if( vips_region_buffer( layer->strip, &strip ) ) {
@ -638,7 +644,8 @@ write_dzi( VipsForeignSaveDz *dz )
if( (p = (char *) vips__find_rightmost_brackets( buf )) )
*p = '\0';
gsf_output_printf( out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
gsf_output_printf( out, "<?xml "
"version=\"1.0\" encoding=\"UTF-8\"?>\n" );
gsf_output_printf( out, "<Image "
"xmlns=\"http://schemas.microsoft.com/deepzoom/2008\"\n" );
gsf_output_printf( out, " Format=\"%s\"\n", buf );
@ -923,9 +930,9 @@ strip_init( Strip *strip, Layer *layer )
image.height = layer->height;
line.left = 0;
line.top = layer->y - dz->overlap;
line.top = layer->y;
line.width = image.width;
line.height = dz->tile_size + 2 * dz->overlap;
line.height = dz->tile_size;
vips_rect_intersectrect( &image, &line, &line );
@ -963,16 +970,16 @@ strip_allocate( VipsThreadState *state, void *a, gboolean *stop )
/* Position this tile.
*/
state->pos.left = strip->x - dz->overlap;
state->pos.top = 0;
state->pos.width = dz->tile_size + 2 * dz->overlap;
state->pos.height = state->im->Ysize;
state->pos.left = strip->x;
state->pos.top = layer->y;
state->pos.width = dz->tile_size;
state->pos.height = dz->tile_size;
vips_rect_intersectrect( &image, &state->pos, &state->pos );
state->x = strip->x;
state->y = layer->y;
strip->x += dz->tile_size;
strip->x += dz->tile_step;
if( vips_rect_isempty( &state->pos ) ) {
*stop = TRUE;
@ -1055,6 +1062,10 @@ tile_name( Layer *layer, int x, int y )
return( NULL );
}
#ifdef DEBUG_VERBOSE
printf( "tile_name: writing to %s\n", name );
#endif /*DEBUG_VERBOSE*/
return( out );
}
@ -1124,10 +1135,6 @@ strip_work( VipsThreadState *state, void *a )
x = t;
}
#ifdef DEBUG_VERBOSE
printf( "strip_work: writing to %s\n", buf );
#endif /*DEBUG_VERBOSE*/
vips_image_set_int( x, "hide-progress", 1 );
if( vips_image_write_to_buffer( x, dz->suffix, &buf, &len, NULL ) ) {
g_object_unref( x );
@ -1140,7 +1147,7 @@ strip_work( VipsThreadState *state, void *a )
g_mutex_lock( vips__global_lock );
out = tile_name( layer,
state->x / dz->tile_size, state->y / dz->tile_size );
state->x / dz->tile_step, state->y / dz->tile_step );
status = gsf_output_write( out, len, buf );
dz->bytes_written += len;
@ -1201,6 +1208,10 @@ strip_save( Layer *layer )
}
strip_free( &strip );
#ifdef DEBUG
printf( "strip_save: success\n" );
#endif /*DEBUG*/
return( 0 );
}
@ -1272,6 +1283,11 @@ strip_shrink( Layer *layer )
VipsRect target;
VipsRect source;
#ifdef DEBUG
printf( "strip_shrink: %d lines in layer %d to layer %d\n",
from->valid.height, layer->n, below->n );
#endif/*DEBUG*/
/* We may have an extra column of pixels on the right or
* bottom that need filling: generate them.
*/
@ -1336,7 +1352,7 @@ strip_shrink( Layer *layer )
*
* - write a line of tiles
* - shrink what we can to the layer below
* - move our strip down by the tile height
* - move our strip down by the tile step
* - copy the overlap with the previous strip
*/
static int
@ -1348,6 +1364,11 @@ strip_arrived( Layer *layer )
VipsRect overlap;
VipsRect image_area;
#ifdef DEBUG
printf( "strip_arrived: layer %d, strip at %d, height %d\n",
layer->n, layer->y, layer->strip->valid.height );
#endif/*DEBUG*/
if( strip_save( layer ) )
return( -1 );
@ -1360,11 +1381,11 @@ strip_arrived( Layer *layer )
* Expand the strip if necessary to make sure we have an even
* number of lines.
*/
layer->y += dz->tile_size;
layer->y += dz->tile_step;
new_strip.left = 0;
new_strip.top = layer->y - dz->overlap;
new_strip.top = layer->y;
new_strip.width = layer->image->Xsize;
new_strip.height = dz->tile_size + 2 * dz->overlap;
new_strip.height = dz->tile_size;
image_area.left = 0;
image_area.top = 0;
@ -1534,6 +1555,9 @@ vips_foreign_save_dz_build( VipsObject *object )
save->ready = z;
}
/* How much we step by as we write tiles.
*/
dz->tile_step = dz->tile_size - dz->overlap;
/* The real pixels we have from our input. This is about to get
* expanded with background.

View File

@ -4,6 +4,7 @@ from __future__ import division
import unittest
import math
import os
import shutil
#import logging
#logging.basicConfig(level = logging.DEBUG)
@ -345,6 +346,115 @@ class TestForeign(unittest.TestCase):
def test_rad(self):
self.save_load("%s.hdr", self.colour)
def test_dzsave(self):
x = Vips.type_find("VipsForeign", "dzsave")
if not x.is_instantiatable():
print("no dzsave support in this vips, skipping test")
return
# dzsave is hard to test, there are so many options
# test each option separately and hope they all function together
# correctly
# default deepzoom layout
self.colour.dzsave("test")
# test right edge ... default is 256x256 tiles, overlap 1
x = Vips.Image.new_from_file("test_files/10/3_2.jpeg")
self.assertEqual(x.width, 256)
y = Vips.Image.new_from_file("test_files/10/4_2.jpeg")
self.assertEqual(y.width,
self.colour.width - 255 * int(self.colour.width / 255))
# test bottom edge
x = Vips.Image.new_from_file("test_files/10/3_2.jpeg")
self.assertEqual(x.height, 256)
y = Vips.Image.new_from_file("test_files/10/3_3.jpeg")
self.assertEqual(y.height,
self.colour.height - 255 * int(self.colour.height / 255))
# there should be a bottom layer
x = Vips.Image.new_from_file("test_files/0/0_0.jpeg")
self.assertEqual(x.width, 1)
self.assertEqual(x.height, 1)
# 10 should be the final layer
self.assertFalse(os.path.isdir("test_files/11"))
shutil.rmtree("test_files")
os.unlink("test.dzi")
# default google layout
self.colour.dzsave("test", layout = "google")
# test bottom-right tile ... default is 256x256 tiles, overlap 0
x = Vips.Image.new_from_file("test/2/2/3.jpg")
self.assertEqual(x.width, 256)
self.assertEqual(x.height, 256)
self.assertFalse(os.path.exists("test/2/2/4.jpg"))
self.assertFalse(os.path.exists("test/3"))
x = Vips.Image.new_from_file("test/blank.png")
self.assertEqual(x.width, 256)
self.assertEqual(x.height, 256)
shutil.rmtree("test")
# default zoomify layout
self.colour.dzsave("test", layout = "zoomify")
# 256x256 tiles, no overlap
self.assertTrue(os.path.exists("test/ImageProperties.xml"))
x = Vips.Image.new_from_file("test/TileGroup0/2-3-2.jpg")
self.assertEqual(x.width, 256)
self.assertEqual(x.height, 256)
shutil.rmtree("test")
# test zip output
self.colour.dzsave("test.zip")
self.assertFalse(os.path.exists("test_files"))
self.assertFalse(os.path.exists("test.dzi"))
os.unlink("test.zip")
# test suffix
self.colour.dzsave("test", suffix = ".png")
x = Vips.Image.new_from_file("test_files/10/3_2.png")
self.assertEqual(x.width, 256)
shutil.rmtree("test_files")
os.unlink("test.dzi")
# test overlap
self.colour.dzsave("test", overlap = 200)
y = Vips.Image.new_from_file("test_files/10/18_6.jpeg")
self.assertEqual(y.width,
self.colour.width - 56 * int(self.colour.width / 56))
shutil.rmtree("test_files")
os.unlink("test.dzi")
# test tile-size
self.colour.dzsave("test", tile_size = 512)
y = Vips.Image.new_from_file("test_files/10/2_1.jpeg")
self.assertEqual(y.width,
self.colour.width - 511 * int(self.colour.width / 511))
shutil.rmtree("test_files")
os.unlink("test.dzi")
# test tile-size
self.colour.dzsave("test", tile_size = 512)
y = Vips.Image.new_from_file("test_files/10/2_1.jpeg")
self.assertEqual(y.width,
self.colour.width - 511 * int(self.colour.width / 511))
shutil.rmtree("test_files")
os.unlink("test.dzi")
if __name__ == '__main__':
unittest.main()