add dzsave_buffer
zip only ... see https://github.com/jcupitt/libvips/issues/415
This commit is contained in:
parent
c90b8be0b8
commit
156b0433d5
@ -1,6 +1,7 @@
|
||||
13/10/16 started 8.5.0
|
||||
- rewritten buffer system is safer and frees memory earlier
|
||||
- added tiff save to buffer
|
||||
- added dzsave save to buffer (zip only)
|
||||
|
||||
27/9/16 started 8.4.2
|
||||
- small doc improvements
|
||||
|
@ -67,6 +67,8 @@
|
||||
* - more overlap changes to help gmaps mode
|
||||
* 8/9/16 Felix Bünemann
|
||||
* - move vips-properties out of subdir for gm and zoomify layouts
|
||||
* 15/10/16
|
||||
* - add dzsave_buffer
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -557,10 +559,6 @@ struct _Layer {
|
||||
struct _VipsForeignSaveDz {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
/* Name to write to.
|
||||
*/
|
||||
char *name;
|
||||
|
||||
char *suffix;
|
||||
int overlap;
|
||||
int tile_size;
|
||||
@ -614,11 +612,16 @@ struct _VipsForeignSaveDz {
|
||||
*/
|
||||
VipsGsfDirectory *tree;
|
||||
|
||||
/* @name, but without a path at the start and without a suffix.
|
||||
/* The actual output object our zip (or whatever) is writing to.
|
||||
*/
|
||||
GsfOutput *out;
|
||||
|
||||
/* The name to save as, eg. deepzoom tiles go into ${basename}_files.
|
||||
* No suffix, no path at the start.
|
||||
*/
|
||||
char *basename;
|
||||
|
||||
/* @name, but just the path at the front.
|
||||
/* The directory we write the output to, or NULL for memory output.
|
||||
*/
|
||||
char *dirname;
|
||||
|
||||
@ -650,7 +653,7 @@ struct _VipsForeignSaveDz {
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveDzClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveDz, vips_foreign_save_dz,
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveDz, vips_foreign_save_dz,
|
||||
VIPS_TYPE_FOREIGN_SAVE );
|
||||
|
||||
/* Free a pyramid.
|
||||
@ -672,6 +675,7 @@ vips_foreign_save_dz_dispose( GObject *gobject )
|
||||
|
||||
VIPS_FREEF( layer_free, dz->layer );
|
||||
VIPS_FREEF( vips_gsf_tree_free, dz->tree );
|
||||
VIPS_FREEF( g_object_unref, dz->out );
|
||||
VIPS_FREE( dz->basename );
|
||||
VIPS_FREE( dz->dirname );
|
||||
VIPS_FREE( dz->tempdir );
|
||||
@ -1819,36 +1823,6 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
save->ready->Xsize, save->ready->Ysize, &real_pixels )) )
|
||||
return( -1 );
|
||||
|
||||
/* Drop any path stuff at the start of the output name and remove the
|
||||
* suffix.
|
||||
*/
|
||||
{
|
||||
char *p;
|
||||
|
||||
dz->basename = g_path_get_basename( dz->name );
|
||||
if( (p = (char *) vips__find_rightmost_brackets( dz->basename )) )
|
||||
*p = '\0';
|
||||
if( (p = strrchr( dz->basename, '.' )) ) {
|
||||
/* If we're writing to thing.zip or thing.szi, default to zip
|
||||
* container.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "container" ) )
|
||||
if( strcasecmp( p + 1, "zip" ) == 0 ||
|
||||
strcasecmp( p + 1, "szi" ) == 0 )
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
|
||||
/* Always remove .szi, .zip, .dz. We don't remove all suffixes
|
||||
* since we might be writing to a dirname with a dot in.
|
||||
*/
|
||||
if( strcasecmp( p + 1, "zip" ) == 0 ||
|
||||
strcasecmp( p + 1, "szi" ) == 0 ||
|
||||
strcasecmp( p + 1, "dz" ) == 0 )
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
dz->dirname = g_path_get_dirname( dz->name );
|
||||
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ )
|
||||
dz->root_name = g_strdup_printf( "%s_files", dz->basename );
|
||||
else
|
||||
@ -1869,6 +1843,7 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ &&
|
||||
dz->container == VIPS_FOREIGN_DZ_CONTAINER_FS &&
|
||||
dz->dirname &&
|
||||
vips_existsf( "%s/%s_files", dz->dirname, dz->basename ) ) {
|
||||
vips_error( "dzsave",
|
||||
_( "output directory %s/%s_files exists" ),
|
||||
@ -1934,31 +1909,30 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
|
||||
case VIPS_FOREIGN_DZ_CONTAINER_ZIP:
|
||||
{
|
||||
GsfOutput *out;
|
||||
GsfOutput *zip;
|
||||
GsfOutput *out2;
|
||||
GError *error = NULL;
|
||||
char name[VIPS_PATH_MAX];
|
||||
|
||||
/* This is the zip we are building.
|
||||
/* Output to a file or memory?
|
||||
*/
|
||||
if( dz->dirname ) {
|
||||
vips_snprintf( name, VIPS_PATH_MAX, "%s/%s.zip",
|
||||
dz->dirname, dz->basename );
|
||||
if( !(out = gsf_output_stdio_new( name, &error )) ) {
|
||||
if( !(dz->out = gsf_output_stdio_new( name, &error )) ) {
|
||||
vips_g_error( &error );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
dz->out = gsf_output_memory_new();
|
||||
|
||||
if( !(zip = (GsfOutput *)
|
||||
gsf_outfile_zip_new( out, &error )) ) {
|
||||
gsf_outfile_zip_new( dz->out, &error )) ) {
|
||||
vips_g_error( &error );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* We can unref @out since @zip has a ref to it.
|
||||
*/
|
||||
g_object_unref( out );
|
||||
|
||||
/* Make the base directory inside the zip. All stuff goes into
|
||||
* this.
|
||||
*/
|
||||
@ -2084,7 +2058,7 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "dzsave";
|
||||
object_class->nickname = "dzsave_base";
|
||||
object_class->description = _( "save image to deep zoom format" );
|
||||
object_class->build = vips_foreign_save_dz_build;
|
||||
|
||||
@ -2094,11 +2068,11 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
|
||||
save_class->format_table = bandfmt_dz;
|
||||
save_class->coding[VIPS_CODING_LABQ] = TRUE;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, name ),
|
||||
VIPS_ARG_STRING( class, "basename", 2,
|
||||
_( "Base name" ),
|
||||
_( "Base name to save to" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, basename ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_ENUM( class, "layout", 8,
|
||||
@ -2178,17 +2152,10 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
|
||||
*/
|
||||
|
||||
VIPS_ARG_STRING( class, "dirname", 1,
|
||||
_( "Base name" ),
|
||||
_( "Base name to save to" ),
|
||||
_( "Directory name" ),
|
||||
_( "Directory name to save to" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, name ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_STRING( class, "basename", 1,
|
||||
_( "Base name" ),
|
||||
_( "Base name to save to" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, name ),
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, dirname ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "tile_width", 12,
|
||||
@ -2221,6 +2188,178 @@ vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
dz->compression = 0;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveDzFile {
|
||||
VipsForeignSaveDz parent_object;
|
||||
|
||||
/* Filename for save.
|
||||
*/
|
||||
char *filename;
|
||||
|
||||
} VipsForeignSaveDzFile;
|
||||
|
||||
typedef VipsForeignSaveDzClass VipsForeignSaveDzFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveDzFile, vips_foreign_save_dz_file,
|
||||
vips_foreign_save_dz_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_dz_file_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) object;
|
||||
VipsForeignSaveDzFile *file = (VipsForeignSaveDzFile *) object;
|
||||
|
||||
char *p;
|
||||
|
||||
/* Use @filename to set the default values for dirname and basename.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "basename" ) )
|
||||
dz->basename = g_path_get_basename( file->filename );
|
||||
if( !vips_object_argument_isset( object, "dirname" ) )
|
||||
dz->dirname = g_path_get_dirname( file->filename );
|
||||
|
||||
/* Remove any [options] from basename.
|
||||
*/
|
||||
if( (p = (char *) vips__find_rightmost_brackets( dz->basename )) )
|
||||
*p = '\0';
|
||||
|
||||
/* If we're writing thing.zip or thing.szi, default to zip
|
||||
* container.
|
||||
*/
|
||||
if( (p = strrchr( dz->basename, '.' )) ) {
|
||||
if( !vips_object_argument_isset( object, "container" ) )
|
||||
if( strcasecmp( p + 1, "zip" ) == 0 ||
|
||||
strcasecmp( p + 1, "szi" ) == 0 )
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
|
||||
/* Remove any legal suffix. We don't remove all suffixes
|
||||
* since we might be writing to a dirname with a dot in.
|
||||
*/
|
||||
if( strcasecmp( p + 1, "zip" ) == 0 ||
|
||||
strcasecmp( p + 1, "szi" ) == 0 ||
|
||||
strcasecmp( p + 1, "dz" ) == 0 )
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_dz_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_dz_file_class_init( VipsForeignSaveDzFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "dzsave";
|
||||
object_class->description = _( "save image to deepzoom file" );
|
||||
object_class->build = vips_foreign_save_dz_file_build;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDzFile, filename ),
|
||||
NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_dz_file_init( VipsForeignSaveDzFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveDzBuffer {
|
||||
VipsForeignSaveDz parent_object;
|
||||
|
||||
VipsArea *buf;
|
||||
} VipsForeignSaveDzBuffer;
|
||||
|
||||
typedef VipsForeignSaveDzClass VipsForeignSaveDzBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveDzBuffer, vips_foreign_save_dz_buffer,
|
||||
vips_foreign_save_dz_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_dz_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsBlob *blob;
|
||||
|
||||
/* Memory output must always be zip.
|
||||
*/
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
|
||||
if( !vips_object_argument_isset( object, "basename" ) )
|
||||
dz->basename = g_strdup( "untitled" );
|
||||
|
||||
/* Leave dirname NULL to indicate memory output.
|
||||
*/
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_dz_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
g_assert( GSF_IS_OUTPUT_MEMORY( dz->out ) );
|
||||
|
||||
/* Oh dear, we can't steal gsf's memory, and blob can't unref something
|
||||
* or trigger a notify. We have to copy it.
|
||||
*
|
||||
* Don't use tracked, we want something that can be freed with g_free.
|
||||
*
|
||||
* FIXME ... blob (or area?) needs to support notify or unref.
|
||||
*/
|
||||
olen = gsf_output_size( GSF_OUTPUT( dz->out ) );
|
||||
if( !(obuf = g_try_malloc( olen )) ) {
|
||||
vips_error( "vips_tracked",
|
||||
_( "out of memory --- size == %dMB" ),
|
||||
(int) (olen / (1024.0 * 1024.0)) );
|
||||
return( -1 );
|
||||
}
|
||||
memcpy( obuf,
|
||||
gsf_output_memory_get_bytes( GSF_OUTPUT_MEMORY( dz->out ) ),
|
||||
olen );
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) g_free, obuf, olen );
|
||||
g_object_set( object, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_dz_buffer_class_init( VipsForeignSaveDzBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "dzsave_buffer";
|
||||
object_class->description = _( "save image to dz buffer" );
|
||||
object_class->build = vips_foreign_save_dz_buffer_build;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDzBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_dz_buffer_init( VipsForeignSaveDzBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*HAVE_GSF*/
|
||||
|
||||
/**
|
||||
@ -2231,6 +2370,7 @@ vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @basename: %gchar, base part of name
|
||||
* * @layout: #VipsForeignDzLayout directory layout convention
|
||||
* * @suffix: suffix for tile tiles
|
||||
* * @overlap: %gint set tile overlap
|
||||
@ -2250,6 +2390,9 @@ vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
* ends `.zip`, vips_dzsave() will create a zip file called @name to hold the
|
||||
* tiles. You can use @container to force zip file output.
|
||||
*
|
||||
* Use @basename to set the name of the directory tree we are creating. The
|
||||
* default value is set from @name.
|
||||
*
|
||||
* You can set @suffix to something like `".jpg[Q=85]"` to control the tile
|
||||
* write options.
|
||||
*
|
||||
@ -2292,3 +2435,66 @@ vips_dzsave( VipsImage *in, const char *name, ... )
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_dzsave_buffer:
|
||||
* @in: image to save
|
||||
* @buf: return output buffer here
|
||||
* @len: return output length here
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @basename: %gchar, base part of name
|
||||
* * @layout: #VipsForeignDzLayout directory layout convention
|
||||
* * @suffix: suffix for tile tiles
|
||||
* * @overlap: %gint set tile overlap
|
||||
* * @tile_size: %gint set tile size
|
||||
* * @background: #VipsArrayDouble background colour
|
||||
* * @depth: #VipsForeignDzDepth how deep to make the pyramid
|
||||
* * @centre: %gboolean centre the tiles
|
||||
* * @angle: #VipsAngle rotate the image by this much
|
||||
* * @container: #VipsForeignDzContainer set container type
|
||||
* * @properties: %gboolean write a properties file
|
||||
* * @compression: %gint zip deflate compression level
|
||||
*
|
||||
* As vips_dzsave(), but save to a memory buffer.
|
||||
*
|
||||
* Output is always in a zip container. Use @basename to set the name of the
|
||||
* directory that the zip will create when unzipped.
|
||||
*
|
||||
* The address of the buffer is returned in @buf, the length of the buffer in
|
||||
* @len. You are responsible for freeing the buffer with g_free() when you
|
||||
* are done with it.
|
||||
*
|
||||
* See also: vips_dzsave(), vips_image_write_to_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_dzsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
{
|
||||
va_list ap;
|
||||
VipsArea *area;
|
||||
int result;
|
||||
|
||||
area = NULL;
|
||||
|
||||
va_start( ap, len );
|
||||
result = vips_call_split( "dzsave_buffer", ap, in, &area );
|
||||
va_end( ap );
|
||||
|
||||
if( !result &&
|
||||
area ) {
|
||||
if( buf ) {
|
||||
*buf = area->data;
|
||||
area->free_fn = NULL;
|
||||
}
|
||||
if( len )
|
||||
*len = area->length;
|
||||
|
||||
vips_area_unref( area );
|
||||
}
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
@ -1649,7 +1649,8 @@ vips_foreign_operation_init( void )
|
||||
extern GType vips_foreign_load_magick_buffer_get_type( void );
|
||||
extern GType vips_foreign_load_magick7_file_get_type( void );
|
||||
extern GType vips_foreign_load_magick7_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_dz_get_type( void );
|
||||
extern GType vips_foreign_save_dz_file_get_type( void );
|
||||
extern GType vips_foreign_save_dz_buffer_get_type( void );
|
||||
extern GType vips_foreign_load_webp_file_get_type( void );
|
||||
extern GType vips_foreign_load_webp_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_webp_file_get_type( void );
|
||||
@ -1709,7 +1710,8 @@ vips_foreign_operation_init( void )
|
||||
#endif /*HAVE_GIFLIB*/
|
||||
|
||||
#ifdef HAVE_GSF
|
||||
vips_foreign_save_dz_get_type();
|
||||
vips_foreign_save_dz_file_get_type();
|
||||
vips_foreign_save_dz_buffer_get_type();
|
||||
#endif /*HAVE_GSF*/
|
||||
|
||||
#ifdef HAVE_PNG
|
||||
|
@ -309,10 +309,10 @@ vips_tracked_malloc( size_t size )
|
||||
|
||||
vips_error( "vips_tracked",
|
||||
_( "out of memory --- size == %dMB" ),
|
||||
(int) (size / (1024.0*1024.0)) );
|
||||
(int) (size / (1024.0 * 1024.0)) );
|
||||
vips_warn( "vips_tracked",
|
||||
_( "out of memory --- size == %dMB" ),
|
||||
(int) (size / (1024.0*1024.0)) );
|
||||
(int) (size / (1024.0 * 1024.0)) );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
@ -703,6 +703,17 @@ class TestForeign(unittest.TestCase):
|
||||
shutil.rmtree("test_files")
|
||||
os.unlink("test.dzi")
|
||||
|
||||
# test save to memory buffer
|
||||
self.colour.dzsave("test-10.zip")
|
||||
with open("test-10.zip", 'rb') as f:
|
||||
buf1 = f.read()
|
||||
os.unlink("test-10.zip")
|
||||
buf2 = self.colour.dzsave_buffer(basename = "test-10")
|
||||
self.assertEqual(len(buf1), len(buf2))
|
||||
|
||||
# we can't test the bytes are exactly equal, the timestamps will be
|
||||
# different
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user