fixes to deep zoom write

- 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

thanks to Benjamin Gilbert for pointing out the errors
This commit is contained in:
John Cupitt 2012-08-06 15:14:59 +01:00
parent 392b59e261
commit dab22df75b
3 changed files with 212 additions and 79 deletions

4
TODO
View File

@ -1,3 +1,7 @@
- C++/Python need an im_affinei_all, perhaps with a char* for the
interpolator?
blocking bugs blocking bugs
============= =============

View File

@ -2,13 +2,17 @@
* *
* 21/3/12 * 21/3/12
* - from the tiff pyramid writer * - from the tiff pyramid writer
*
* 5/7/12 (thanks Alexander Koshman) * 5/7/12 (thanks Alexander Koshman)
* - make tiles down to 1x1 pixels * - make tiles down to 1x1 pixels
* - oop make right-hand edge tiles * - oop make right-hand edge tiles
* - improve overlap handling * - improve overlap handling
* 7/7/12 * 7/7/12
* - threaded write * - 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 { struct _Layer {
VipsForeignSaveDz *dz; 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 */ VipsImage *image; /* The image we build */
/* The top of this strip of tiles, excluding the overlap. Go up from /* 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. /* Build a pyramid.
*/ */
static Layer * 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 ); VipsForeignSave *save = VIPS_FOREIGN_SAVE( dz );
Layer *layer = VIPS_NEW( dz, Layer ); Layer *layer = VIPS_NEW( dz, Layer );
@ -136,6 +147,9 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int w, int h )
VipsRect strip; VipsRect strip;
layer->dz = dz; layer->dz = dz;
layer->width = width;
layer->height = height;
layer->image = NULL; layer->image = NULL;
layer->strip = NULL; layer->strip = NULL;
layer->copy = NULL; layer->copy = NULL;
@ -150,13 +164,17 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int w, int h )
layer->below = NULL; layer->below = NULL;
layer->above = above; layer->above = above;
/* We round the image size up to an even number to make x2 shrink
* easy.
*/
layer->image = vips_image_new(); layer->image = vips_image_new();
if( vips_image_copy_fields( layer->image, save->ready ) ) { if( vips_image_copy_fields( layer->image, save->ready ) ) {
layer_free( layer ); layer_free( layer );
return( NULL ); return( NULL );
} }
layer->image->Xsize = w; layer->image->Xsize = width + (width & 1);
layer->image->Ysize = h; layer->image->Ysize = height + (height & 1);
layer->strip = vips_region_new( layer->image ); layer->strip = vips_region_new( layer->image );
layer->copy = 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; layer->write_y = 0;
strip.left = 0; strip.left = 0;
strip.top = 0; strip.top = 0;
strip.width = w; strip.width = layer->image->Xsize;
strip.height = dz->tile_height + dz->overlap + 1; strip.height = dz->tile_height + dz->overlap + 1;
if( vips_region_buffer( layer->strip, &strip ) ) { if( vips_region_buffer( layer->strip, &strip ) ) {
layer_free( layer ); layer_free( layer );
return( NULL ); return( NULL );
} }
if( w > 1 && if( width > 1 ||
h > 1 ) { height > 1 ) {
/* Round up, so eg. a 5 pixel wide image becomes 3 a layer
* down.
*/
if( !(layer->below = pyramid_build( dz, if( !(layer->below = pyramid_build( dz,
layer, w / 2, h / 2 )) ) { layer, (width + 1) / 2, (height + 1) / 2 )) ) {
layer_free( layer ); layer_free( layer );
return( NULL ); return( NULL );
} }
@ -217,6 +238,37 @@ pyramid_mkdir( VipsForeignSaveDz *dz )
return( 0 ); 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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
fprintf( fp, "<Image "
"xmlns=\"http://schemas.microsoft.com/deepzoom/2008\"\n" );
fprintf( fp, " Format=\"%s\"\n", dz->suffix + 1 );
fprintf( fp, " Overlap=\"%d\"\n", dz->overlap );
fprintf( fp, " TileSize=\"%d\"\n", dz->tile_width );
fprintf( fp, " >\n" );
fprintf( fp, " <Size \n" );
fprintf( fp, " Height=\"%d\"\n", dz->layer->height );
fprintf( fp, " Width=\"%d\"\n", dz->layer->width );
fprintf( fp, " />\n" );
fprintf( fp, "</Image>\n" );
fclose( fp );
return( 0 );
}
/* Generate area @target in @to using pixels in @from. VIPS_CODING_LABQ only. /* Generate area @target in @to using pixels in @from. VIPS_CODING_LABQ only.
*/ */
static void static void
@ -341,6 +393,7 @@ shrink_region_uncoded( VipsRegion *from, VipsRegion *to, VipsRect *target )
*/ */
typedef struct _Strip { typedef struct _Strip {
Layer *layer; Layer *layer;
VipsImage *image; VipsImage *image;
/* Allocate the next tile on this boundary. /* Allocate the next tile on this boundary.
@ -365,15 +418,22 @@ strip_init( Strip *strip, Layer *layer )
strip->image = NULL; strip->image = NULL;
strip->x = 0; strip->x = 0;
line.left = 0; /* The image we wrap around our pixel buffer must be the full width,
line.top = layer->y - dz->overlap; * including any rounding up, since we must have contiguous pixels.
line.width = layer->image->Xsize; * We can trim the height down though.
line.height = dz->tile_height + 2 * dz->overlap; *
* When we loop across the strip writing tiles we have to look out for
* the smaller width.
*/
image.left = 0; image.left = 0;
image.top = 0; image.top = 0;
image.width = layer->image->Xsize; 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 ); vips_rect_intersectrect( &image, &line, &line );
@ -395,6 +455,11 @@ strip_allocate( VipsThreadState *state, void *a, gboolean *stop )
VipsRect image; VipsRect image;
image.left = 0;
image.top = 0;
image.width = layer->width;
image.height = layer->height;
/* Position this tile. /* Position this tile.
*/ */
state->pos.left = strip->x - dz->overlap; 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.width = dz->tile_width + 2 * dz->overlap;
state->pos.height = state->im->Ysize; 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 ); vips_rect_intersectrect( &image, &state->pos, &state->pos );
state->x = strip->x; state->x = strip->x;
state->y = layer->y; state->y = layer->y;
@ -474,6 +534,59 @@ strip_save( Layer *layer )
return( 0 ); 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 ); static int strip_arrived( Layer *layer );
/* Shrink what pixels we can from this layer into the layer below. If the /* 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 target;
VipsRect source; 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 /* Our pixels might cross a strip boundary in the layer below, so we
* have to write repeatedly until we run out of pixels. * have to write repeatedly until we run out of pixels.
*/ */
@ -534,10 +652,15 @@ strip_shrink( Layer *layer )
below->write_y += target.height; below->write_y += target.height;
/* If we've filled the strip of the layer below, let it know. /* 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 ) && if( below->write_y == VIPS_RECT_BOTTOM( &to->valid ) ||
strip_arrived( below ) ) below->write_y == below->height ) {
return( -1 ); if( strip_arrived( below ) )
return( -1 );
}
} }
return( 0 ); return( 0 );
@ -635,6 +758,7 @@ pyramid_strip( VipsRegion *region, VipsRect *area, void *a )
*/ */
vips_region_copy( region, layer->strip, vips_region_copy( region, layer->strip,
&target, target.left, target.top ); &target, target.left, target.top );
layer->write_y += target.height; layer->write_y += target.height;
/* If we've filled the strip, let it know. /* 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, if( !(dz->layer = pyramid_build( dz,
NULL, save->ready->Xsize, save->ready->Ysize )) ) NULL, save->ready->Xsize, save->ready->Ysize )) )
return( -1 ); return( -1 );
if( pyramid_mkdir( dz ) ) if( pyramid_mkdir( dz ) ||
write_dzi( dz ) )
return( -1 ); return( -1 );
if( vips_sink_disc( save->ready, pyramid_strip, dz ) ) if( vips_sink_disc( save->ready, pyramid_strip, dz ) )
@ -762,8 +887,8 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
static void static void
vips_foreign_save_dz_init( VipsForeignSaveDz *dz ) vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
{ {
VIPS_SETSTR( dz->suffix, ".jpg" ); VIPS_SETSTR( dz->suffix, ".jpeg" );
dz->overlap = 0; dz->overlap = 1;
dz->tile_width = 128; dz->tile_width = 256;
dz->tile_height = 128; dz->tile_height = 256;
} }

View File

@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=glib&keywords=I18N+L10N&component=general\n" "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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -2589,141 +2589,141 @@ msgstr ""
msgid "per-thread state for sink" msgid "per-thread state for sink"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:210 #: ../libvips/iofuncs/error.c:213
msgid "windows error" msgid "windows error"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:219 #: ../libvips/iofuncs/error.c:222
msgid "unix error" msgid "unix error"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:310 ../libvips/iofuncs/error.c:311 #: ../libvips/iofuncs/error.c:313 ../libvips/iofuncs/error.c:314
#: ../libvips/iofuncs/error.c:360 ../libvips/iofuncs/error.c:361 #: ../libvips/iofuncs/error.c:363 ../libvips/iofuncs/error.c:364
#, c-format #, c-format
msgid "%s: " msgid "%s: "
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:310 #: ../libvips/iofuncs/error.c:313
msgid "vips diagnostic" msgid "vips diagnostic"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:360 #: ../libvips/iofuncs/error.c:363
msgid "vips warning" msgid "vips warning"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:444 #: ../libvips/iofuncs/error.c:453
msgid "image must be uncoded" msgid "image must be uncoded"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:472 #: ../libvips/iofuncs/error.c:481
msgid "image coding must be NONE or LABQ" msgid "image coding must be NONE or LABQ"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:500 #: ../libvips/iofuncs/error.c:509
msgid "unknown image coding" msgid "unknown image coding"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:526 #: ../libvips/iofuncs/error.c:535
msgid "Radiance coding only" msgid "Radiance coding only"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:552 #: ../libvips/iofuncs/error.c:561
msgid "LABQ coding only" msgid "LABQ coding only"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:576 #: ../libvips/iofuncs/error.c:585
msgid "image must one band" msgid "image must one band"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:601 #: ../libvips/iofuncs/error.c:610
#, c-format #, c-format
msgid "image must have %d bands" msgid "image must have %d bands"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:626 #: ../libvips/iofuncs/error.c:635
msgid "image must have one or three bands" msgid "image must have one or three bands"
msgstr "" 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" msgid "images must have the same number of bands, or one must be single-band"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:681 #: ../libvips/iofuncs/error.c:690
#, c-format #, c-format
msgid "image must have 1 or %d bands" msgid "image must have 1 or %d bands"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:705 #: ../libvips/iofuncs/error.c:714
msgid "image must be non-complex" msgid "image must be non-complex"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:729 #: ../libvips/iofuncs/error.c:738
msgid "image must be complex" msgid "image must be complex"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:755 #: ../libvips/iofuncs/error.c:764
#, c-format #, c-format
msgid "image must be %s" msgid "image must be %s"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:780 #: ../libvips/iofuncs/error.c:789
msgid "image must be integer" msgid "image must be integer"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:805 #: ../libvips/iofuncs/error.c:814
msgid "image must be unsigned integer" msgid "image must be unsigned integer"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:833 #: ../libvips/iofuncs/error.c:842
msgid "image must be 8- or 16-bit integer, signed or unsigned" msgid "image must be 8- or 16-bit integer, signed or unsigned"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:860 #: ../libvips/iofuncs/error.c:869
msgid "image must be 8- or 16-bit unsigned integer" msgid "image must be 8- or 16-bit unsigned integer"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:886 #: ../libvips/iofuncs/error.c:895
msgid "image must be 8- or 16-bit unsigned integer, or float" msgid "image must be 8- or 16-bit unsigned integer, or float"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:914 #: ../libvips/iofuncs/error.c:923
msgid "image must be unsigned int or float" msgid "image must be unsigned int or float"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:939 #: ../libvips/iofuncs/error.c:948
msgid "images must match in size" msgid "images must match in size"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:965 #: ../libvips/iofuncs/error.c:974
msgid "images must have the same number of bands" msgid "images must have the same number of bands"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:1019 #: ../libvips/iofuncs/error.c:1028
msgid "images must have the same band format" msgid "images must have the same band format"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:1045 #: ../libvips/iofuncs/error.c:1054
msgid "images must have the same coding" msgid "images must have the same coding"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:1070 #: ../libvips/iofuncs/error.c:1079
#, c-format #, c-format
msgid "vector must have 1 or %d elements" msgid "vector must have 1 or %d elements"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:1095 #: ../libvips/iofuncs/error.c:1104
msgid "histograms must have width or height 1" msgid "histograms must have width or height 1"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:1100 #: ../libvips/iofuncs/error.c:1109
msgid "histograms must have not have more than 65536 elements" msgid "histograms must have not have more than 65536 elements"
msgstr "" 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" msgid "nonsense mask parameters"
msgstr "" msgstr ""
#: ../libvips/iofuncs/error.c:1182 #: ../libvips/iofuncs/error.c:1191
msgid "mask must be 1D" msgid "mask must be 1D"
msgstr "" msgstr ""
@ -2883,71 +2883,75 @@ msgstr ""
msgid "unable to mmap \"%s\" to same address" msgid "unable to mmap \"%s\" to same address"
msgstr "" 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" msgid "evaluate with N concurrent threads"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:383 #: ../libvips/iofuncs/init.c:408
msgid "set tile width to N (DEBUG)" msgid "set tile width to N (DEBUG)"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:386 #: ../libvips/iofuncs/init.c:411
msgid "set tile height to N (DEBUG)" msgid "set tile height to N (DEBUG)"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:389 #: ../libvips/iofuncs/init.c:414
msgid "set thinstrip height to N (DEBUG)" msgid "set thinstrip height to N (DEBUG)"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:392 #: ../libvips/iofuncs/init.c:417
msgid "set fatstrip height to N (DEBUG)" msgid "set fatstrip height to N (DEBUG)"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:395 #: ../libvips/iofuncs/init.c:420
msgid "show progress feedback" msgid "show progress feedback"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:398 #: ../libvips/iofuncs/init.c:423
msgid "leak-check on exit" msgid "leak-check on exit"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:401 #: ../libvips/iofuncs/init.c:426
msgid "images larger than N are decompressed to disc" msgid "images larger than N are decompressed to disc"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:404 #: ../libvips/iofuncs/init.c:429
msgid "disable vectorised versions of operations" msgid "disable vectorised versions of operations"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:407 #: ../libvips/iofuncs/init.c:432
msgid "cache at most N operations" msgid "cache at most N operations"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:410 #: ../libvips/iofuncs/init.c:435
msgid "cache at most N bytes in memory" msgid "cache at most N bytes in memory"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:413 #: ../libvips/iofuncs/init.c:438
msgid "allow at most N open files" msgid "allow at most N open files"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:416 #: ../libvips/iofuncs/init.c:441
msgid "trace operation cache" msgid "trace operation cache"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:419 #: ../libvips/iofuncs/init.c:444
msgid "dump operation cache on exit" msgid "dump operation cache on exit"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:422 #: ../libvips/iofuncs/init.c:447
msgid "print libvips version" msgid "print libvips version"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:445 #: ../libvips/iofuncs/init.c:470
msgid "VIPS Options" msgid "VIPS Options"
msgstr "" msgstr ""
#: ../libvips/iofuncs/init.c:445 #: ../libvips/iofuncs/init.c:470
msgid "Show VIPS options" msgid "Show VIPS options"
msgstr "" msgstr ""