dzsave can write zoomify and google maps
use "layout" to pick the directory layout, deepzoom / zoomify / google all allowed
This commit is contained in:
parent
aec36aa5a7
commit
dee1aac7b1
@ -1,3 +1,6 @@
|
||||
3/10/12 started 7.31.0
|
||||
- dzsave can write zoomify and google maps layout as well
|
||||
|
||||
13/9/12 started 7.30.3
|
||||
- linecache sized itself too large
|
||||
- fix a compile failure if libtiff was not found (thanks Martin)
|
||||
|
@ -20,6 +20,8 @@
|
||||
* 1/10/12
|
||||
* - did not write low pyramid layers for images with an odd number of
|
||||
* scan lines (thanks Martin)
|
||||
* 3/10/12
|
||||
* - add zoomify and google maps output
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -64,6 +66,15 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
/* Round N down to P boundary.
|
||||
*/
|
||||
#define ROUND_DOWN(N,P) ((N) - ((N) % P))
|
||||
|
||||
/* Round N up to P boundary.
|
||||
*/
|
||||
#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) ))
|
||||
|
||||
typedef struct _VipsForeignSaveDz VipsForeignSaveDz;
|
||||
typedef struct _Layer Layer;
|
||||
@ -80,6 +91,12 @@ struct _Layer {
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/* Number of tiles across and down in this layer. Zoomify needs this
|
||||
* to calculate the directory to put each tile in.
|
||||
*/
|
||||
int tiles_across;
|
||||
int tiles_down;
|
||||
|
||||
VipsImage *image; /* The image we build */
|
||||
|
||||
/* The top of this strip of tiles, excluding the overlap. Go up from
|
||||
@ -111,8 +128,13 @@ struct _VipsForeignSaveDz {
|
||||
char *suffix;
|
||||
int overlap;
|
||||
int tile_size;
|
||||
VipsForeignDzLayout layout;
|
||||
|
||||
Layer *layer; /* x2 shrink pyr layer */
|
||||
|
||||
/* Count zoomify tiles we write.
|
||||
*/
|
||||
int tile_count;
|
||||
};
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveDzClass;
|
||||
@ -151,11 +173,15 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height )
|
||||
Layer *layer = VIPS_NEW( dz, Layer );
|
||||
|
||||
VipsRect strip;
|
||||
int limit;
|
||||
|
||||
layer->dz = dz;
|
||||
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->image = NULL;
|
||||
layer->strip = NULL;
|
||||
layer->copy = NULL;
|
||||
@ -207,8 +233,16 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( width > 1 ||
|
||||
height > 1 ) {
|
||||
/* DeepZoom stops at 1x1 pixels, others when the image fits within a
|
||||
* tile.
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ )
|
||||
limit = 1;
|
||||
else
|
||||
limit = dz->tile_size;
|
||||
|
||||
if( width > limit ||
|
||||
height > limit ) {
|
||||
/* Round up, so eg. a 5 pixel wide image becomes 3 a layer
|
||||
* down.
|
||||
*/
|
||||
@ -236,18 +270,20 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height )
|
||||
static int
|
||||
pyramid_mkdir( VipsForeignSaveDz *dz )
|
||||
{
|
||||
Layer *layer;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if( vips_existsf( "%s_files", dz->basename ) ) {
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ )
|
||||
vips_snprintf( buf, PATH_MAX, "%s_files", dz->basename );
|
||||
else
|
||||
vips_snprintf( buf, PATH_MAX, "%s", dz->basename );
|
||||
|
||||
if( vips_existsf( "%s", buf ) ) {
|
||||
vips_error( "dzsave",
|
||||
_( "Directory \"%s_files\" exists" ), dz->basename );
|
||||
_( "Directory \"%s\" exists" ), buf );
|
||||
return( -1 );
|
||||
}
|
||||
if( vips_mkdirf( "%s_files", dz->basename ) )
|
||||
if( vips_mkdirf( "%s", buf ) )
|
||||
return( -1 );
|
||||
for( layer = dz->layer; layer; layer = layer->below )
|
||||
if( vips_mkdirf( "%s_files/%d", dz->basename, layer->n ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -280,6 +316,49 @@ write_dzi( VipsForeignSaveDz *dz )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_properties( VipsForeignSaveDz *dz )
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
vips_snprintf( buf, PATH_MAX, "%s/ImageProperties.xml", dz->basename );
|
||||
if( !(fp = vips__file_open_write( buf, TRUE )) )
|
||||
return( -1 );
|
||||
|
||||
fprintf( fp, "<IMAGE_PROPERTIES "
|
||||
"WIDTH=\"%d\" HEIGHT=\"%d\" NUMTILES=\"%d\" "
|
||||
"NUMIMAGES=\"1\" VERSION=\"1.8\" TILESIZE=\"%d\" />\n",
|
||||
dz->layer->width,
|
||||
dz->layer->height,
|
||||
dz->tile_count,
|
||||
dz->tile_size );
|
||||
|
||||
fclose( fp );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_blank( VipsForeignSaveDz *dz )
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
VipsImage *black;
|
||||
|
||||
vips_snprintf( buf, PATH_MAX, "%s/blank.png", dz->basename );
|
||||
if( vips_black( &black, dz->tile_size, dz->tile_size,
|
||||
"bands", 3,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write_to_file( black, buf ) ) {
|
||||
g_object_unref( black );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( black );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Generate area @target in @to using pixels in @from. VIPS_CODING_LABQ only.
|
||||
*/
|
||||
static void
|
||||
@ -492,6 +571,104 @@ strip_allocate( VipsThreadState *state, void *a, gboolean *stop )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Create a tile name in the current layout.
|
||||
*/
|
||||
static int
|
||||
tile_name( Layer *layer, char *buf, int x, int y )
|
||||
{
|
||||
VipsForeignSaveDz *dz = layer->dz;
|
||||
|
||||
char dirname[PATH_MAX];
|
||||
char dirname2[PATH_MAX];
|
||||
Layer *p;
|
||||
int n;
|
||||
|
||||
/* We have to lock or there's a race between exists() and mkdir().
|
||||
*/
|
||||
g_mutex_lock( vips__global_lock );
|
||||
|
||||
switch( dz->layout ) {
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_DZ:
|
||||
vips_snprintf( dirname, PATH_MAX, "%s_files/%d",
|
||||
dz->basename, layer->n );
|
||||
vips_snprintf( buf, PATH_MAX, "%s/%d_%d%s",
|
||||
dirname, x, y, dz->suffix );
|
||||
|
||||
if( !vips_existsf( "%s", dirname ) &&
|
||||
vips_mkdirf( "%s", dirname ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY:
|
||||
/* We need to work out the tile number so we can calculate the
|
||||
* directory to put this tile in.
|
||||
*
|
||||
* Tiles are numbered from 0 for the most-zoomed-out tile.
|
||||
*/
|
||||
n = 0;
|
||||
|
||||
/* Count all tiles in layers below this one.
|
||||
*/
|
||||
for( p = layer->below; p; p = p->below )
|
||||
n += p->tiles_across * p->tiles_down;
|
||||
|
||||
/* And count tiles so far in this layer.
|
||||
*/
|
||||
n += y * layer->tiles_across + x;
|
||||
|
||||
vips_snprintf( dirname, PATH_MAX, "%s/TileGroup%d",
|
||||
dz->basename, n / 256 );
|
||||
vips_snprintf( buf, PATH_MAX, "%s/%d-%d-%d%s",
|
||||
dirname, layer->n, x, y, dz->suffix );
|
||||
|
||||
/* Used at the end in ImageProperties.xml
|
||||
*/
|
||||
dz->tile_count += 1;
|
||||
|
||||
if( !vips_existsf( "%s", dirname ) &&
|
||||
vips_mkdirf( "%s", dirname ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_GOOGLE:
|
||||
vips_snprintf( dirname, PATH_MAX, "%s/%d",
|
||||
dz->basename, layer->n );
|
||||
vips_snprintf( dirname2, PATH_MAX, "%s/%d",
|
||||
dirname, y );
|
||||
vips_snprintf( buf, PATH_MAX, "%s/%d%s",
|
||||
dirname2, x, dz->suffix );
|
||||
|
||||
if( !vips_existsf( "%s", dirname ) &&
|
||||
vips_mkdirf( "%s", dirname ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( !vips_existsf( "%s", dirname2 ) &&
|
||||
vips_mkdirf( "%s", dirname2 ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert( 0 );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
strip_work( VipsThreadState *state, void *a )
|
||||
{
|
||||
@ -499,29 +676,41 @@ strip_work( VipsThreadState *state, void *a )
|
||||
Layer *layer = strip->layer;
|
||||
VipsForeignSaveDz *dz = layer->dz;
|
||||
|
||||
VipsImage *extr;
|
||||
char str[1000];
|
||||
VipsBuf buf = VIPS_BUF_STATIC( str );
|
||||
VipsImage *x;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if( tile_name( layer, buf,
|
||||
state->x / dz->tile_size, state->y / dz->tile_size ) )
|
||||
return( -1 );
|
||||
|
||||
/* Extract relative to the strip top-left corner.
|
||||
*/
|
||||
if( vips_extract_area( state->im, &extr,
|
||||
if( vips_extract_area( state->im, &x,
|
||||
state->pos.left, 0,
|
||||
state->pos.width, state->pos.height, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
vips_buf_appendf( &buf, "%s_files/%d/%d_%d%s",
|
||||
dz->basename, layer->n,
|
||||
state->x / dz->tile_size,
|
||||
state->y / dz->tile_size,
|
||||
dz->suffix );
|
||||
/* Google tiles need to be padded up to tilesize.
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE ) {
|
||||
VipsImage *z;
|
||||
|
||||
if( vips_image_write_to_file( extr, vips_buf_all( &buf ) ) ) {
|
||||
g_object_unref( extr );
|
||||
return( -1 );
|
||||
if( vips_embed( x, &z, 0, 0, dz->tile_size, dz->tile_size,
|
||||
"extend", VIPS_EXTEND_WHITE,
|
||||
NULL ) ) {
|
||||
g_object_unref( x );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( x );
|
||||
|
||||
x = z;
|
||||
}
|
||||
|
||||
g_object_unref( extr );
|
||||
if( vips_image_write_to_file( x, buf ) ) {
|
||||
g_object_unref( x );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( x );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -802,6 +991,16 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) object;
|
||||
|
||||
/* Google and zoomify default to zero overlap, ".jpg".
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY ||
|
||||
dz->layout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE ) {
|
||||
if( !vips_object_get_argument_assigned( object, "overlap" ) )
|
||||
dz->overlap = 0;
|
||||
if( !vips_object_get_argument_assigned( object, "suffix" ) )
|
||||
VIPS_SETSTR( dz->suffix, ".jpg" );
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_dz_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
@ -819,13 +1018,33 @@ 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 ) ||
|
||||
write_dzi( dz ) )
|
||||
if( pyramid_mkdir( dz ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_sink_disc( save->ready, pyramid_strip, dz ) )
|
||||
return( -1 );
|
||||
|
||||
switch( dz->layout ) {
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_DZ:
|
||||
if( write_dzi( dz ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY:
|
||||
if( write_properties( dz ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_GOOGLE:
|
||||
if( write_blank( dz ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert( 0 );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -878,6 +1097,14 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, basename ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_ENUM( class, "layout", 8,
|
||||
_( "Layout" ),
|
||||
_( "Directory layout" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, layout ),
|
||||
VIPS_TYPE_FOREIGN_DZ_LAYOUT,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_DZ );
|
||||
|
||||
VIPS_ARG_STRING( class, "suffix", 9,
|
||||
_( "suffix" ),
|
||||
_( "Filename suffix for tiles" ),
|
||||
@ -929,8 +1156,10 @@ static void
|
||||
vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
{
|
||||
VIPS_SETSTR( dz->suffix, ".jpeg" );
|
||||
dz->layout = VIPS_FOREIGN_DZ_LAYOUT_DZ;
|
||||
dz->overlap = 1;
|
||||
dz->tile_size = 256;
|
||||
dz->tile_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -941,24 +1170,27 @@ vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* @suffix: suffix for tile tiles (default ".jpg")
|
||||
* @overlap; set tile overlap (default 1)
|
||||
* @tile_size; set tile size (default 256)
|
||||
* @layout; directory layout convention
|
||||
* @suffix: suffix for tile tiles
|
||||
* @overlap; set tile overlap
|
||||
* @tile_size; set tile size
|
||||
*
|
||||
* Save an image to a deep zoom - style directory tree. A directory called
|
||||
* Save an image as a set of tiles at various resolutions. By default dzsave
|
||||
* uses DeepZoom layout -- use @layout to pick other conventions.
|
||||
*
|
||||
* In DeepZoom layout a directory called
|
||||
* "@basename_files" is created to hold the tiles, and an XML file called
|
||||
* "@basename.dzi" is written with the image metadata,
|
||||
* "@basename.dzi" is written with the image metadata,
|
||||
*
|
||||
* The image is shrunk in a series of x2 reductions until it fits within a
|
||||
* single pixel. Each layer is written out to a separate subdirectory of
|
||||
* @dirname_files, with directory "0" holding the smallest, single pixel image.
|
||||
*
|
||||
* Each tile is written as a separate file named as "@x_@y@suffix", where @x
|
||||
* and @y are the tile coordinates, with (0, 0) as the top-left tile.
|
||||
* In Zoomify and Google layout, a directory called @basename is created to
|
||||
* hold the tile structure.
|
||||
*
|
||||
* You can set @suffix to something like ".jpg[Q=85]" to set the tile write
|
||||
* options.
|
||||
*
|
||||
* You can set the size and overlap of tiles with @tile_size and @overlap.
|
||||
* They default to the correct settings for the selected @layout.
|
||||
*
|
||||
* See also: vips_tiffsave().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
|
@ -17,6 +17,8 @@ GType vips_foreign_tiff_predictor_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_TIFF_PREDICTOR (vips_foreign_tiff_predictor_get_type())
|
||||
GType vips_foreign_tiff_resunit_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_TIFF_RESUNIT (vips_foreign_tiff_resunit_get_type())
|
||||
GType vips_foreign_dz_layout_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_DZ_LAYOUT (vips_foreign_dz_layout_get_type())
|
||||
/* enumerations from "../../../libvips/include/vips/arithmetic.h" */
|
||||
GType vips_operation_math_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_OPERATION_MATH (vips_operation_math_get_type())
|
||||
|
@ -424,6 +424,21 @@ int vips_radload( const char *filename, VipsImage **out, ... )
|
||||
int vips_radsave( VipsImage *in, const char *filename, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
/**
|
||||
* VipsForeignDzLayout:
|
||||
* @VIPS_FOREIGN_DZ_LAYOUT_DZ: use DeepZoom directory layout
|
||||
* @VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY: use Zoomify directory layout
|
||||
* @VIPS_FOREIGN_DZ_LAYOUT_GOOGLE: use Google maps directory layout
|
||||
*
|
||||
* Use inches or centimeters as the resolution unit for a tiff file.
|
||||
*/
|
||||
typedef enum {
|
||||
VIPS_FOREIGN_DZ_LAYOUT_DZ,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_GOOGLE,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_LAST
|
||||
} VipsForeignDzLayout;
|
||||
|
||||
int vips_dzsave( VipsImage *in, const char *basename, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
|
@ -105,6 +105,25 @@ vips_foreign_tiff_resunit_get_type( void )
|
||||
|
||||
return( etype );
|
||||
}
|
||||
GType
|
||||
vips_foreign_dz_layout_get_type( void )
|
||||
{
|
||||
static GType etype = 0;
|
||||
|
||||
if( etype == 0 ) {
|
||||
static const GEnumValue values[] = {
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_DZ, "VIPS_FOREIGN_DZ_LAYOUT_DZ", "dz"},
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY, "VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY", "zoomify"},
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_GOOGLE, "VIPS_FOREIGN_DZ_LAYOUT_GOOGLE", "google"},
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_LAST, "VIPS_FOREIGN_DZ_LAYOUT_LAST", "last"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
etype = g_enum_register_static( "VipsForeignDzLayout", values );
|
||||
}
|
||||
|
||||
return( etype );
|
||||
}
|
||||
/* enumerations from "../../libvips/include/vips/conversion.h" */
|
||||
GType
|
||||
vips_extend_get_type( void )
|
||||
|
Loading…
Reference in New Issue
Block a user