diff --git a/TODO b/TODO
index e4e24aca..68d0d9be 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,7 @@
+- C++/Python need an im_affinei_all, perhaps with a char* for the
+ interpolator?
+
+
blocking bugs
=============
diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c
index 5ff88742..b4810747 100644
--- a/libvips/foreign/dzsave.c
+++ b/libvips/foreign/dzsave.c
@@ -2,13 +2,17 @@
*
* 21/3/12
* - from the tiff pyramid writer
- *
* 5/7/12 (thanks Alexander Koshman)
* - make tiles down to 1x1 pixels
* - oop make right-hand edge tiles
* - improve overlap handling
* 7/7/12
* - threaded write
+ * 6/8/12 (thanks to Benjamin Gilbert for pointing out the errors)
+ * - shrink down to a 1x1 pixel tile, even for very long and thin images
+ * - round image size up on shrink
+ * - write a .dzi file with the pyramid params
+ * - default tile size and overlap now matches the openslide writer
*/
/*
@@ -62,6 +66,13 @@ typedef struct _Layer Layer;
struct _Layer {
VipsForeignSaveDz *dz;
+ /* The real size of the image. image->Xsize and image->Ysize are
+ * always even to make x2 shrink easy. The real image may be a
+ * smaller, odd size,
+ */
+ int width;
+ int height;
+
VipsImage *image; /* The image we build */
/* The top of this strip of tiles, excluding the overlap. Go up from
@@ -128,7 +139,7 @@ vips_foreign_save_dz_dispose( GObject *gobject )
/* Build a pyramid.
*/
static Layer *
-pyramid_build( VipsForeignSaveDz *dz, Layer *above, int w, int h )
+pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height )
{
VipsForeignSave *save = VIPS_FOREIGN_SAVE( dz );
Layer *layer = VIPS_NEW( dz, Layer );
@@ -136,6 +147,9 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int w, int h )
VipsRect strip;
layer->dz = dz;
+ layer->width = width;
+ layer->height = height;
+
layer->image = NULL;
layer->strip = NULL;
layer->copy = NULL;
@@ -150,13 +164,17 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int w, int h )
layer->below = NULL;
layer->above = above;
+ /* We round the image size up to an even number to make x2 shrink
+ * easy.
+ */
layer->image = vips_image_new();
if( vips_image_copy_fields( layer->image, save->ready ) ) {
layer_free( layer );
return( NULL );
}
- layer->image->Xsize = w;
- layer->image->Ysize = h;
+ layer->image->Xsize = width + (width & 1);
+ layer->image->Ysize = height + (height & 1);
+
layer->strip = vips_region_new( layer->image );
layer->copy = vips_region_new( layer->image );
@@ -176,17 +194,20 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int w, int h )
layer->write_y = 0;
strip.left = 0;
strip.top = 0;
- strip.width = w;
+ strip.width = layer->image->Xsize;
strip.height = dz->tile_height + dz->overlap + 1;
if( vips_region_buffer( layer->strip, &strip ) ) {
layer_free( layer );
return( NULL );
}
- if( w > 1 &&
- h > 1 ) {
+ if( width > 1 ||
+ height > 1 ) {
+ /* Round up, so eg. a 5 pixel wide image becomes 3 a layer
+ * down.
+ */
if( !(layer->below = pyramid_build( dz,
- layer, w / 2, h / 2 )) ) {
+ layer, (width + 1) / 2, (height + 1) / 2 )) ) {
layer_free( layer );
return( NULL );
}
@@ -217,6 +238,37 @@ pyramid_mkdir( VipsForeignSaveDz *dz )
return( 0 );
}
+static int
+write_dzi( VipsForeignSaveDz *dz )
+{
+ FILE *fp;
+ char buf[PATH_MAX];
+ char *name;
+
+ name = g_path_get_basename( dz->dirname );
+ vips_snprintf( buf, PATH_MAX, "%s/%s.dzi", dz->dirname, name );
+ g_free( name );
+ if( !(fp = vips__file_open_write( buf, TRUE )) )
+ return( -1 );
+
+ fprintf( fp, "\n" );
+ fprintf( fp, "suffix + 1 );
+ fprintf( fp, " Overlap=\"%d\"\n", dz->overlap );
+ fprintf( fp, " TileSize=\"%d\"\n", dz->tile_width );
+ fprintf( fp, " >\n" );
+ fprintf( fp, " layer->height );
+ fprintf( fp, " Width=\"%d\"\n", dz->layer->width );
+ fprintf( fp, " />\n" );
+ fprintf( fp, "\n" );
+
+ fclose( fp );
+
+ return( 0 );
+}
+
/* Generate area @target in @to using pixels in @from. VIPS_CODING_LABQ only.
*/
static void
@@ -341,6 +393,7 @@ shrink_region_uncoded( VipsRegion *from, VipsRegion *to, VipsRect *target )
*/
typedef struct _Strip {
Layer *layer;
+
VipsImage *image;
/* Allocate the next tile on this boundary.
@@ -365,15 +418,22 @@ strip_init( Strip *strip, Layer *layer )
strip->image = NULL;
strip->x = 0;
- line.left = 0;
- line.top = layer->y - dz->overlap;
- line.width = layer->image->Xsize;
- line.height = dz->tile_height + 2 * dz->overlap;
-
+ /* The image we wrap around our pixel buffer must be the full width,
+ * including any rounding up, since we must have contiguous pixels.
+ * We can trim the height down though.
+ *
+ * When we loop across the strip writing tiles we have to look out for
+ * the smaller width.
+ */
image.left = 0;
image.top = 0;
image.width = layer->image->Xsize;
- image.height = layer->image->Ysize;
+ image.height = layer->height;
+
+ line.left = 0;
+ line.top = layer->y - dz->overlap;
+ line.width = image.width;
+ line.height = dz->tile_height + 2 * dz->overlap;
vips_rect_intersectrect( &image, &line, &line );
@@ -395,6 +455,11 @@ strip_allocate( VipsThreadState *state, void *a, gboolean *stop )
VipsRect image;
+ image.left = 0;
+ image.top = 0;
+ image.width = layer->width;
+ image.height = layer->height;
+
/* Position this tile.
*/
state->pos.left = strip->x - dz->overlap;
@@ -402,11 +467,6 @@ strip_allocate( VipsThreadState *state, void *a, gboolean *stop )
state->pos.width = dz->tile_width + 2 * dz->overlap;
state->pos.height = state->im->Ysize;
- image.left = 0;
- image.top = 0;
- image.width = state->im->Xsize;
- image.height = state->im->Ysize;
-
vips_rect_intersectrect( &image, &state->pos, &state->pos );
state->x = strip->x;
state->y = layer->y;
@@ -474,6 +534,59 @@ strip_save( Layer *layer )
return( 0 );
}
+/* A strip has filled, but the rightmost column and the bottom-most row may
+ * not have if we've rounded the size up!
+ *
+ * Fill them, if necessary, by copyping the previous row/column.
+ */
+static void
+layer_generate_extras( Layer *layer )
+{
+ VipsRegion *strip = layer->strip;
+
+ /* We only work for full-width strips.
+ */
+ g_assert( strip->valid.width == layer->image->Xsize );
+
+ if( layer->width < layer->image->Xsize ) {
+ int ps = VIPS_IMAGE_SIZEOF_PEL( strip->im );
+
+ int b, y;
+
+ /* Need to add a right-most column.
+ */
+ for( y = 0; y < strip->valid.height; y++ ) {
+ VipsPel *p = VIPS_REGION_ADDR( strip,
+ layer->width - 1, strip->valid.top + y );
+ VipsPel *q = p + ps;
+
+ for( b = 0; b < ps; b++ )
+ q[b] = p[b];
+ }
+ }
+
+ if( layer->height < layer->image->Ysize ) {
+ VipsRect last;
+
+ /* The last two lines of the image.
+ */
+ last.left = 0;
+ last.top = layer->image->Ysize - 2;
+ last.width = layer->image->Xsize;
+ last.height = 2;
+
+ /* Do we have them both? Fill the last with the next-to-last.
+ */
+ vips_rect_intersectrect( &last, &strip->valid, &last );
+ if( last.height == 2 ) {
+ last.height = 1;
+
+ vips_region_copy( strip, strip, &last,
+ 0, last.top + 1 );
+ }
+ }
+}
+
static int strip_arrived( Layer *layer );
/* Shrink what pixels we can from this layer into the layer below. If the
@@ -491,6 +604,11 @@ strip_shrink( Layer *layer )
VipsRect target;
VipsRect source;
+ /* We may have an extra column of pixels on the right or
+ * bottom that need filling: generate them.
+ */
+ layer_generate_extras( layer );
+
/* Our pixels might cross a strip boundary in the layer below, so we
* have to write repeatedly until we run out of pixels.
*/
@@ -534,10 +652,15 @@ strip_shrink( Layer *layer )
below->write_y += target.height;
/* If we've filled the strip of the layer below, let it know.
+ * We can either fill the region, if it's somewhere half-way
+ * down the image, or, if it's at the bottom, get to the last
+ * writeable line.
*/
- if( below->write_y == VIPS_RECT_BOTTOM( &to->valid ) &&
- strip_arrived( below ) )
- return( -1 );
+ if( below->write_y == VIPS_RECT_BOTTOM( &to->valid ) ||
+ below->write_y == below->height ) {
+ if( strip_arrived( below ) )
+ return( -1 );
+ }
}
return( 0 );
@@ -635,6 +758,7 @@ pyramid_strip( VipsRegion *region, VipsRect *area, void *a )
*/
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.
@@ -671,7 +795,8 @@ vips_foreign_save_dz_build( VipsObject *object )
if( !(dz->layer = pyramid_build( dz,
NULL, save->ready->Xsize, save->ready->Ysize )) )
return( -1 );
- if( pyramid_mkdir( dz ) )
+ if( pyramid_mkdir( dz ) ||
+ write_dzi( dz ) )
return( -1 );
if( vips_sink_disc( save->ready, pyramid_strip, dz ) )
@@ -762,8 +887,8 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
static void
vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
{
- VIPS_SETSTR( dz->suffix, ".jpg" );
- dz->overlap = 0;
- dz->tile_width = 128;
- dz->tile_height = 128;
+ VIPS_SETSTR( dz->suffix, ".jpeg" );
+ dz->overlap = 1;
+ dz->tile_width = 256;
+ dz->tile_height = 256;
}
diff --git a/po/vips7.pot b/po/vips7.pot
index 1af3842a..b7d257a5 100644
--- a/po/vips7.pot
+++ b/po/vips7.pot
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=glib&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-07-20 15:34+0100\n"
+"POT-Creation-Date: 2012-07-23 11:27+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -2589,141 +2589,141 @@ msgstr ""
msgid "per-thread state for sink"
msgstr ""
-#: ../libvips/iofuncs/error.c:210
+#: ../libvips/iofuncs/error.c:213
msgid "windows error"
msgstr ""
-#: ../libvips/iofuncs/error.c:219
+#: ../libvips/iofuncs/error.c:222
msgid "unix error"
msgstr ""
-#: ../libvips/iofuncs/error.c:310 ../libvips/iofuncs/error.c:311
-#: ../libvips/iofuncs/error.c:360 ../libvips/iofuncs/error.c:361
+#: ../libvips/iofuncs/error.c:313 ../libvips/iofuncs/error.c:314
+#: ../libvips/iofuncs/error.c:363 ../libvips/iofuncs/error.c:364
#, c-format
msgid "%s: "
msgstr ""
-#: ../libvips/iofuncs/error.c:310
+#: ../libvips/iofuncs/error.c:313
msgid "vips diagnostic"
msgstr ""
-#: ../libvips/iofuncs/error.c:360
+#: ../libvips/iofuncs/error.c:363
msgid "vips warning"
msgstr ""
-#: ../libvips/iofuncs/error.c:444
+#: ../libvips/iofuncs/error.c:453
msgid "image must be uncoded"
msgstr ""
-#: ../libvips/iofuncs/error.c:472
+#: ../libvips/iofuncs/error.c:481
msgid "image coding must be NONE or LABQ"
msgstr ""
-#: ../libvips/iofuncs/error.c:500
+#: ../libvips/iofuncs/error.c:509
msgid "unknown image coding"
msgstr ""
-#: ../libvips/iofuncs/error.c:526
+#: ../libvips/iofuncs/error.c:535
msgid "Radiance coding only"
msgstr ""
-#: ../libvips/iofuncs/error.c:552
+#: ../libvips/iofuncs/error.c:561
msgid "LABQ coding only"
msgstr ""
-#: ../libvips/iofuncs/error.c:576
+#: ../libvips/iofuncs/error.c:585
msgid "image must one band"
msgstr ""
-#: ../libvips/iofuncs/error.c:601
+#: ../libvips/iofuncs/error.c:610
#, c-format
msgid "image must have %d bands"
msgstr ""
-#: ../libvips/iofuncs/error.c:626
+#: ../libvips/iofuncs/error.c:635
msgid "image must have one or three bands"
msgstr ""
-#: ../libvips/iofuncs/error.c:654
+#: ../libvips/iofuncs/error.c:663
msgid "images must have the same number of bands, or one must be single-band"
msgstr ""
-#: ../libvips/iofuncs/error.c:681
+#: ../libvips/iofuncs/error.c:690
#, c-format
msgid "image must have 1 or %d bands"
msgstr ""
-#: ../libvips/iofuncs/error.c:705
+#: ../libvips/iofuncs/error.c:714
msgid "image must be non-complex"
msgstr ""
-#: ../libvips/iofuncs/error.c:729
+#: ../libvips/iofuncs/error.c:738
msgid "image must be complex"
msgstr ""
-#: ../libvips/iofuncs/error.c:755
+#: ../libvips/iofuncs/error.c:764
#, c-format
msgid "image must be %s"
msgstr ""
-#: ../libvips/iofuncs/error.c:780
+#: ../libvips/iofuncs/error.c:789
msgid "image must be integer"
msgstr ""
-#: ../libvips/iofuncs/error.c:805
+#: ../libvips/iofuncs/error.c:814
msgid "image must be unsigned integer"
msgstr ""
-#: ../libvips/iofuncs/error.c:833
+#: ../libvips/iofuncs/error.c:842
msgid "image must be 8- or 16-bit integer, signed or unsigned"
msgstr ""
-#: ../libvips/iofuncs/error.c:860
+#: ../libvips/iofuncs/error.c:869
msgid "image must be 8- or 16-bit unsigned integer"
msgstr ""
-#: ../libvips/iofuncs/error.c:886
+#: ../libvips/iofuncs/error.c:895
msgid "image must be 8- or 16-bit unsigned integer, or float"
msgstr ""
-#: ../libvips/iofuncs/error.c:914
+#: ../libvips/iofuncs/error.c:923
msgid "image must be unsigned int or float"
msgstr ""
-#: ../libvips/iofuncs/error.c:939
+#: ../libvips/iofuncs/error.c:948
msgid "images must match in size"
msgstr ""
-#: ../libvips/iofuncs/error.c:965
+#: ../libvips/iofuncs/error.c:974
msgid "images must have the same number of bands"
msgstr ""
-#: ../libvips/iofuncs/error.c:1019
+#: ../libvips/iofuncs/error.c:1028
msgid "images must have the same band format"
msgstr ""
-#: ../libvips/iofuncs/error.c:1045
+#: ../libvips/iofuncs/error.c:1054
msgid "images must have the same coding"
msgstr ""
-#: ../libvips/iofuncs/error.c:1070
+#: ../libvips/iofuncs/error.c:1079
#, c-format
msgid "vector must have 1 or %d elements"
msgstr ""
-#: ../libvips/iofuncs/error.c:1095
+#: ../libvips/iofuncs/error.c:1104
msgid "histograms must have width or height 1"
msgstr ""
-#: ../libvips/iofuncs/error.c:1100
+#: ../libvips/iofuncs/error.c:1109
msgid "histograms must have not have more than 65536 elements"
msgstr ""
-#: ../libvips/iofuncs/error.c:1129 ../libvips/iofuncs/error.c:1157
+#: ../libvips/iofuncs/error.c:1138 ../libvips/iofuncs/error.c:1166
msgid "nonsense mask parameters"
msgstr ""
-#: ../libvips/iofuncs/error.c:1182
+#: ../libvips/iofuncs/error.c:1191
msgid "mask must be 1D"
msgstr ""
@@ -2883,71 +2883,75 @@ msgstr ""
msgid "unable to mmap \"%s\" to same address"
msgstr ""
-#: ../libvips/iofuncs/init.c:380
+#: ../libvips/iofuncs/init.c:402
+msgid "abort on first error or warning"
+msgstr ""
+
+#: ../libvips/iofuncs/init.c:405
msgid "evaluate with N concurrent threads"
msgstr ""
-#: ../libvips/iofuncs/init.c:383
+#: ../libvips/iofuncs/init.c:408
msgid "set tile width to N (DEBUG)"
msgstr ""
-#: ../libvips/iofuncs/init.c:386
+#: ../libvips/iofuncs/init.c:411
msgid "set tile height to N (DEBUG)"
msgstr ""
-#: ../libvips/iofuncs/init.c:389
+#: ../libvips/iofuncs/init.c:414
msgid "set thinstrip height to N (DEBUG)"
msgstr ""
-#: ../libvips/iofuncs/init.c:392
+#: ../libvips/iofuncs/init.c:417
msgid "set fatstrip height to N (DEBUG)"
msgstr ""
-#: ../libvips/iofuncs/init.c:395
+#: ../libvips/iofuncs/init.c:420
msgid "show progress feedback"
msgstr ""
-#: ../libvips/iofuncs/init.c:398
+#: ../libvips/iofuncs/init.c:423
msgid "leak-check on exit"
msgstr ""
-#: ../libvips/iofuncs/init.c:401
+#: ../libvips/iofuncs/init.c:426
msgid "images larger than N are decompressed to disc"
msgstr ""
-#: ../libvips/iofuncs/init.c:404
+#: ../libvips/iofuncs/init.c:429
msgid "disable vectorised versions of operations"
msgstr ""
-#: ../libvips/iofuncs/init.c:407
+#: ../libvips/iofuncs/init.c:432
msgid "cache at most N operations"
msgstr ""
-#: ../libvips/iofuncs/init.c:410
+#: ../libvips/iofuncs/init.c:435
msgid "cache at most N bytes in memory"
msgstr ""
-#: ../libvips/iofuncs/init.c:413
+#: ../libvips/iofuncs/init.c:438
msgid "allow at most N open files"
msgstr ""
-#: ../libvips/iofuncs/init.c:416
+#: ../libvips/iofuncs/init.c:441
msgid "trace operation cache"
msgstr ""
-#: ../libvips/iofuncs/init.c:419
+#: ../libvips/iofuncs/init.c:444
msgid "dump operation cache on exit"
msgstr ""
-#: ../libvips/iofuncs/init.c:422
+#: ../libvips/iofuncs/init.c:447
msgid "print libvips version"
msgstr ""
-#: ../libvips/iofuncs/init.c:445
+#: ../libvips/iofuncs/init.c:470
msgid "VIPS Options"
msgstr ""
-#: ../libvips/iofuncs/init.c:445
+#: ../libvips/iofuncs/init.c:470
msgid "Show VIPS options"
msgstr ""