add dzsave to a target (#2718)
* start adding dzsave to a target made a thing for gsf to write to a target * fix stray tabs in dzsave * fix dzsave write to "." Early versions of libgsf did not support writing to ".", so we had an ugly workaround, but this should now be OK. Fixing this ought to make write to target simple * dzsave_target compiles no idea if it works though * seems to work now creates stray 0 length files though, very odd * fix stray files from dzsave next: save to buffer is returning null * fix buffer flush in dzsave all tests pass! * update changelog
This commit is contained in:
parent
f8003bda67
commit
a9d64bea54
@ -1,7 +1,7 @@
|
||||
21/11/21 started 8.13
|
||||
- configure fails for requested but unmet dependencies [remicollet]
|
||||
- add support for another quantiser [DarthSim]
|
||||
- add "extend", "background" and "premultiplied" to mapim to fix edge
|
||||
- add "extend", "background" and "premultiplied" to vips_mapim() to fix edge
|
||||
antialiasing [GavinJoyce]
|
||||
- add support for HDR HEIC and AVIF images
|
||||
- add vips_spngsave()
|
||||
@ -16,8 +16,9 @@
|
||||
- add "maxerror" to gifsave [dloebl]
|
||||
- update libnsgif API [tlsa]
|
||||
- deprecate "properties" option to dzsave (now always on)
|
||||
- add vips_dzsave_buffer()
|
||||
- always set the min stack size for pthreads, if we can
|
||||
- add "fail_on" to thumbnail
|
||||
- add "fail-on" to thumbnail
|
||||
- add "gap" option to vips_reduce[hv]() and vips_resize() [kleisauke]
|
||||
- add "ceil" option to vips_shrink() [kleisauke]
|
||||
- quality improvements for image resizing [kleisauke]
|
||||
|
@ -94,6 +94,8 @@
|
||||
* 29/3/22
|
||||
* - always write a properties file
|
||||
* - add .szi as a registered suffix
|
||||
* 9/5/22
|
||||
* - add dzsave_target
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -188,6 +190,99 @@
|
||||
#include <gsf/gsf.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
/* A GSF output object that can write to a VipsTarget.
|
||||
*/
|
||||
|
||||
typedef struct _GsfOutputTarget {
|
||||
GsfOutput output;
|
||||
|
||||
VipsTarget *target;
|
||||
|
||||
} GsfOutputTarget;
|
||||
|
||||
typedef struct {
|
||||
GsfOutputClass output_class;
|
||||
} GsfOutputTargetClass;
|
||||
|
||||
G_DEFINE_TYPE( GsfOutputTarget, gsf_output_target, GSF_OUTPUT_TYPE );
|
||||
|
||||
static gboolean
|
||||
gsf_output_target_close( GsfOutput *output )
|
||||
{
|
||||
GsfOutputTarget *output_target = (GsfOutputTarget *) output;
|
||||
|
||||
if( output_target->target ) {
|
||||
vips_target_finish( output_target->target );
|
||||
VIPS_UNREF( output_target->target );
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
gsf_output_target_finalize( GObject *obj )
|
||||
{
|
||||
GObjectClass *parent_class;
|
||||
GsfOutputTarget *output_target = (GsfOutputTarget *) obj;
|
||||
|
||||
gsf_output_target_close( GSF_OUTPUT( output_target ) );
|
||||
|
||||
parent_class = g_type_class_peek( GSF_OUTPUT_TYPE );
|
||||
parent_class->finalize( obj );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsf_output_target_write( GsfOutput *output,
|
||||
size_t num_bytes, guint8 const *buffer )
|
||||
{
|
||||
GsfOutputTarget *output_target = (GsfOutputTarget *) output;
|
||||
|
||||
if( vips_target_write( output_target->target, buffer, num_bytes ) )
|
||||
return( FALSE );
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsf_output_target_seek( GsfOutput *output, gsf_off_t offset, GSeekType whence )
|
||||
{
|
||||
/* No seek needed.
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsf_output_target_init( GsfOutputTarget *output )
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsf_output_target_class_init( GsfOutputTargetClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
GsfOutputClass *output_class = GSF_OUTPUT_CLASS( class );
|
||||
|
||||
gobject_class->finalize = gsf_output_target_finalize;
|
||||
|
||||
output_class->Close = gsf_output_target_close;
|
||||
output_class->Write = gsf_output_target_write;
|
||||
output_class->Seek = gsf_output_target_seek;
|
||||
}
|
||||
|
||||
static GsfOutput *
|
||||
gsf_output_target_new( VipsTarget *target )
|
||||
{
|
||||
GsfOutputTarget *output;
|
||||
|
||||
output = g_object_new( gsf_output_target_get_type(), NULL );
|
||||
output->target = target;
|
||||
g_object_ref( target );
|
||||
|
||||
return( GSF_OUTPUT( output ) );
|
||||
}
|
||||
|
||||
/* Simple wrapper around libgsf.
|
||||
*
|
||||
* We need to be able to do scattered writes to structured files. So while
|
||||
@ -471,6 +566,14 @@ struct _Layer {
|
||||
struct _VipsForeignSaveDz {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
/* The target we are writing to. This is set by our subclasses.
|
||||
*/
|
||||
VipsTarget *target;
|
||||
|
||||
/* Alternatively, the filename, for filesystem output.
|
||||
*/
|
||||
char *filename;
|
||||
|
||||
char *suffix;
|
||||
int overlap;
|
||||
int tile_size;
|
||||
@ -541,10 +644,6 @@ struct _VipsForeignSaveDz {
|
||||
*/
|
||||
char *dirname;
|
||||
|
||||
/* For DZ save, we have to write to a temp dir. Track the name here.
|
||||
*/
|
||||
char *tempdir;
|
||||
|
||||
/* The root directory name ... $basename with perhaps some extra
|
||||
* stuff, eg. $(basename)_files, etc.
|
||||
*/
|
||||
@ -572,21 +671,6 @@ typedef VipsForeignSaveClass VipsForeignSaveDzClass;
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveDz, vips_foreign_save_dz,
|
||||
VIPS_TYPE_FOREIGN_SAVE );
|
||||
|
||||
/* ZIP and SZI are both written as zip files.
|
||||
*/
|
||||
static gboolean
|
||||
iszip( VipsForeignDzContainer container )
|
||||
{
|
||||
switch( container ) {
|
||||
case VIPS_FOREIGN_DZ_CONTAINER_ZIP:
|
||||
case VIPS_FOREIGN_DZ_CONTAINER_SZI:
|
||||
return( TRUE );
|
||||
|
||||
default:
|
||||
return( FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
#define VIPS_ZIP_FIXED_LH_SIZE (30 + 29)
|
||||
#define VIPS_ZIP_FIXED_CD_SIZE (46 + 9)
|
||||
#define VIPS_ZIP_EOCD_SIZE 22
|
||||
@ -705,12 +789,14 @@ vips_foreign_save_dz_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) gobject;
|
||||
|
||||
VIPS_UNREF( dz->target );
|
||||
|
||||
VIPS_FREEF( layer_free, dz->layer );
|
||||
VIPS_FREEF( vips_gsf_tree_close, dz->tree );
|
||||
VIPS_FREEF( g_object_unref, dz->out );
|
||||
|
||||
VIPS_FREE( dz->basename );
|
||||
VIPS_FREE( dz->dirname );
|
||||
VIPS_FREE( dz->tempdir );
|
||||
VIPS_FREE( dz->root_name );
|
||||
VIPS_FREE( dz->file_suffix );
|
||||
|
||||
@ -2022,7 +2108,9 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) object;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz );
|
||||
|
||||
VipsRect real_pixels;
|
||||
char *p;
|
||||
|
||||
/* Google, zoomify and iiif default to zero overlap, ".jpg".
|
||||
*/
|
||||
@ -2186,6 +2274,52 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
dz->tile_step );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Init basename and dirname from the associated filesystem names, if
|
||||
* we can.
|
||||
*/
|
||||
{
|
||||
const char *filename = dz->filename ?
|
||||
dz->filename :
|
||||
vips_connection_filename( VIPS_CONNECTION( dz->target ) );
|
||||
|
||||
if( !vips_object_argument_isset( object, "basename" ) ) {
|
||||
if( filename )
|
||||
dz->basename = g_path_get_basename( filename );
|
||||
else
|
||||
dz->basename = g_strdup( "untitled" );
|
||||
}
|
||||
|
||||
if( !vips_object_argument_isset( object, "dirname" ) ) {
|
||||
if( filename )
|
||||
dz->dirname = g_path_get_dirname( 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( g_ascii_strcasecmp( p + 1, "zip" ) == 0 )
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
if( g_ascii_strcasecmp( p + 1, "szi" ) == 0 )
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_SZI;
|
||||
}
|
||||
|
||||
/* Remove any legal suffix. We don't remove all suffixes
|
||||
* since we might be writing to a dirname with a dot in.
|
||||
*/
|
||||
if( g_ascii_strcasecmp( p + 1, "zip" ) == 0 ||
|
||||
g_ascii_strcasecmp( p + 1, "szi" ) == 0 ||
|
||||
g_ascii_strcasecmp( p + 1, "dz" ) == 0 )
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
/* Build the skeleton of the image pyramid.
|
||||
*/
|
||||
if( !(dz->layer = pyramid_build( dz, NULL,
|
||||
@ -2197,75 +2331,32 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
else
|
||||
dz->root_name = g_strdup( dz->basename );
|
||||
|
||||
/* Drop any options from @suffix.
|
||||
/* Drop any [options] from @suffix.
|
||||
*/
|
||||
{
|
||||
char filename[VIPS_PATH_MAX];
|
||||
char option_string[VIPS_PATH_MAX];
|
||||
|
||||
vips__filename_split8( dz->suffix, filename, option_string );
|
||||
dz->file_suffix = g_strdup( filename );
|
||||
}
|
||||
|
||||
/* If we will be renaming our temp dir to an existing directory or
|
||||
* file, stop now. See vips_rename() use below.
|
||||
*/
|
||||
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" ),
|
||||
dz->dirname, dz->basename );
|
||||
return( -1 );
|
||||
}
|
||||
dz->file_suffix = g_strdup( dz->suffix );
|
||||
if( (p = (char *) vips__find_rightmost_brackets( dz->file_suffix )) )
|
||||
*p = '\0';
|
||||
|
||||
/* Make the thing we write the tiles into.
|
||||
*/
|
||||
switch( dz->container ) {
|
||||
case VIPS_FOREIGN_DZ_CONTAINER_FS:
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ ) {
|
||||
/* For deepzoom, we have to rearrange the output
|
||||
* directory after writing it, see the end of this
|
||||
* function. We write to a temporary directory, then
|
||||
* pull ${basename}_files and ${basename}.dzi out into
|
||||
* the current directory and remove the temp. The temp
|
||||
* dir must not clash with another file.
|
||||
{
|
||||
GsfOutput *out;
|
||||
GError *error = NULL;
|
||||
char name[VIPS_PATH_MAX];
|
||||
|
||||
/* For filesystem output of deepzoom, we write
|
||||
* dirname/basename_files/ and dirname/basename.dzi, ie. the
|
||||
* output does not go into a subdirectory.
|
||||
*/
|
||||
char name[VIPS_PATH_MAX];
|
||||
int fd;
|
||||
GsfOutput *out;
|
||||
GError *error = NULL;
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ )
|
||||
vips_snprintf( name, VIPS_PATH_MAX,
|
||||
"%s", dz->dirname );
|
||||
else
|
||||
vips_snprintf( name, VIPS_PATH_MAX,
|
||||
"%s/%s", dz->dirname, dz->basename );
|
||||
|
||||
vips_snprintf( name, VIPS_PATH_MAX, "%s-XXXXXX",
|
||||
dz->basename );
|
||||
dz->tempdir = g_build_filename( dz->dirname,
|
||||
name, NULL );
|
||||
if( (fd = g_mkstemp( dz->tempdir )) == -1 ) {
|
||||
vips_error( class->nickname,
|
||||
_( "unable to make temporary file %s" ),
|
||||
dz->tempdir );
|
||||
return( -1 );
|
||||
}
|
||||
close( fd );
|
||||
g_unlink( dz->tempdir );
|
||||
|
||||
if( !(out = (GsfOutput *)
|
||||
gsf_outfile_stdio_new( dz->tempdir,
|
||||
&error )) ) {
|
||||
vips_g_error( &error );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
dz->tree = vips_gsf_tree_new( out, 0 );
|
||||
}
|
||||
else {
|
||||
GsfOutput *out;
|
||||
GError *error = NULL;
|
||||
char name[VIPS_PATH_MAX];
|
||||
|
||||
vips_snprintf( name, VIPS_PATH_MAX, "%s/%s",
|
||||
dz->dirname, dz->basename );
|
||||
if( !(out = (GsfOutput *)
|
||||
gsf_outfile_stdio_new( name, &error )) ) {
|
||||
vips_g_error( &error );
|
||||
@ -2282,25 +2373,19 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
GsfOutput *zip;
|
||||
GsfOutput *out2;
|
||||
GError *error = NULL;
|
||||
char name[VIPS_PATH_MAX];
|
||||
|
||||
/* Output to a file or memory?
|
||||
/* We can have dzsave("x.zip", container="fs"), ie. zip output
|
||||
* from write to file. Make a target if we need one.
|
||||
*/
|
||||
if( dz->dirname ) {
|
||||
const char *suffix =
|
||||
dz->container == VIPS_FOREIGN_DZ_CONTAINER_SZI ?
|
||||
"szi" : "zip";
|
||||
|
||||
vips_snprintf( name, VIPS_PATH_MAX, "%s/%s.%s",
|
||||
dz->dirname, dz->basename, suffix );
|
||||
if( !(dz->out =
|
||||
gsf_output_stdio_new( name, &error )) ) {
|
||||
vips_g_error( &error );
|
||||
if( !dz->target ) {
|
||||
if( !(dz->target =
|
||||
vips_target_new_to_file( dz->filename )) )
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
dz->out = gsf_output_memory_new();
|
||||
|
||||
/* Can be memory, a file (not a directory tree), pipe, etc.
|
||||
*/
|
||||
dz->out = gsf_output_target_new( dz->target );
|
||||
|
||||
if( !(zip = (GsfOutput *)
|
||||
gsf_outfile_zip_new( dz->out, &error )) ) {
|
||||
@ -2377,49 +2462,15 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
write_associated( dz ) )
|
||||
return( -1 );
|
||||
|
||||
/* This is so ugly. In earlier versions of dzsave, we wrote x.dzi and
|
||||
* x_files. Now we write x/x.dzi and x/x_files to make it possible to
|
||||
* create zip files.
|
||||
*
|
||||
* For compatibility, rearrange the directory tree.
|
||||
*
|
||||
* FIXME have a flag to stop this stupidity
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ &&
|
||||
dz->container == VIPS_FOREIGN_DZ_CONTAINER_FS ) {
|
||||
char old_name[VIPS_PATH_MAX];
|
||||
char new_name[VIPS_PATH_MAX];
|
||||
|
||||
vips_snprintf( old_name, VIPS_PATH_MAX, "%s/%s.dzi",
|
||||
dz->tempdir, dz->basename );
|
||||
vips_snprintf( new_name, VIPS_PATH_MAX, "%s/%s.dzi",
|
||||
dz->dirname, dz->basename );
|
||||
if( vips_rename( old_name, new_name ) )
|
||||
return( -1 );
|
||||
|
||||
vips_snprintf( old_name, VIPS_PATH_MAX, "%s/%s_files",
|
||||
dz->tempdir, dz->basename );
|
||||
vips_snprintf( new_name, VIPS_PATH_MAX, "%s/%s_files",
|
||||
dz->dirname, dz->basename );
|
||||
if( vips_rename( old_name, new_name ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_rmdirf( "%s", dz->tempdir ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Shut down the output to flush everything.
|
||||
*/
|
||||
if( vips_gsf_tree_close( dz->tree ) )
|
||||
return( -1 );
|
||||
dz->tree = NULL;
|
||||
|
||||
/* If we are writing a zip to the filesystem, we must unref out to
|
||||
* force it to disc.
|
||||
/* unref out to force flush in gsf_output_target_close().
|
||||
*/
|
||||
if( iszip( dz->container ) &&
|
||||
dz->dirname != NULL )
|
||||
VIPS_FREEF( g_object_unref, dz->out );
|
||||
VIPS_UNREF( dz->out );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -2613,6 +2664,65 @@ vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
dz->skip_blanks = -1;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveDzTarget {
|
||||
VipsForeignSaveDz parent_object;
|
||||
|
||||
VipsTarget *target;
|
||||
|
||||
} VipsForeignSaveDzTarget;
|
||||
|
||||
typedef VipsForeignSaveDzClass VipsForeignSaveDzTargetClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveDzTarget, vips_foreign_save_dz_target,
|
||||
vips_foreign_save_dz_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_dz_target_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) object;
|
||||
VipsForeignSaveDzTarget *target = (VipsForeignSaveDzTarget *) object;
|
||||
|
||||
dz->target = target->target;
|
||||
g_object_ref( target->target );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_dz_target_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_dz_target_class_init( VipsForeignSaveDzTargetClass *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 target" );
|
||||
object_class->build = vips_foreign_save_dz_target_build;
|
||||
|
||||
VIPS_ARG_OBJECT( class, "target", 1,
|
||||
_( "Target" ),
|
||||
_( "Target to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDzTarget, target ),
|
||||
VIPS_TYPE_TARGET );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_dz_target_init( VipsForeignSaveDzTarget *target )
|
||||
{
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) target;
|
||||
|
||||
/* zip default for target output.
|
||||
*/
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveDzFile {
|
||||
VipsForeignSaveDz parent_object;
|
||||
|
||||
@ -2633,39 +2743,7 @@ 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( g_ascii_strcasecmp( p + 1, "zip" ) == 0 )
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
if( g_ascii_strcasecmp( p + 1, "szi" ) == 0 )
|
||||
dz->container = VIPS_FOREIGN_DZ_CONTAINER_SZI;
|
||||
}
|
||||
|
||||
/* Remove any legal suffix. We don't remove all suffixes
|
||||
* since we might be writing to a dirname with a dot in.
|
||||
*/
|
||||
if( g_ascii_strcasecmp( p + 1, "zip" ) == 0 ||
|
||||
g_ascii_strcasecmp( p + 1, "szi" ) == 0 ||
|
||||
g_ascii_strcasecmp( p + 1, "dz" ) == 0 )
|
||||
*p = '\0';
|
||||
}
|
||||
dz->filename = file->filename;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_dz_file_parent_class )->
|
||||
build( object ) )
|
||||
@ -2715,43 +2793,19 @@ static int
|
||||
vips_foreign_save_dz_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) object;
|
||||
VipsForeignSaveDzBuffer *buffer = (VipsForeignSaveDzBuffer *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsBlob *blob;
|
||||
|
||||
if( !vips_object_argument_isset( object, "basename" ) )
|
||||
dz->basename = g_strdup( "untitled" );
|
||||
|
||||
/* Leave dirname NULL to indicate memory output.
|
||||
*/
|
||||
if( !(dz->target = vips_target_new_to_memory()) )
|
||||
return( -1 );
|
||||
|
||||
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) vips_area_free_cb, obuf, olen );
|
||||
g_object_set( object, "buffer", blob, NULL );
|
||||
g_object_get( dz->target, "blob", &blob, NULL );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
@ -2949,3 +3003,46 @@ vips_dzsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_dzsave_target: (method)
|
||||
* @in: image to save
|
||||
* @target: save image to this target
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @basename: %gchar base part of name
|
||||
* * @layout: #VipsForeignDzLayout directory layout convention
|
||||
* * @suffix: %gchar suffix for 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
|
||||
* * @compression: %gint zip deflate compression level
|
||||
* * @region_shrink: #VipsRegionShrink how to shrink each 2x2 region.
|
||||
* * @skip_blanks: %gint skip tiles which are nearly equal to the background
|
||||
* * @no_strip: %gboolean don't strip tiles
|
||||
* * @id: %gchar id for IIIF properties
|
||||
*
|
||||
* As vips_dzsave(), but save to a target.
|
||||
*
|
||||
* See also: vips_dzsave(), vips_image_write_to_target().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_dzsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, target );
|
||||
result = vips_call_split( "dzsave_target", ap, in, target );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
@ -2898,6 +2898,7 @@ vips_foreign_operation_init( 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_save_dz_target_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_webp_file_get_type( void );
|
||||
extern GType vips_foreign_load_webp_buffer_get_type( void );
|
||||
@ -3042,6 +3043,7 @@ vips_foreign_operation_init( void )
|
||||
#ifdef HAVE_GSF
|
||||
vips_foreign_save_dz_file_get_type();
|
||||
vips_foreign_save_dz_buffer_get_type();
|
||||
vips_foreign_save_dz_target_get_type();
|
||||
#endif /*HAVE_GSF*/
|
||||
|
||||
#ifdef HAVE_PNG
|
||||
|
@ -402,7 +402,8 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
|
||||
VipsForeignSaveJpegBuffer *file = (VipsForeignSaveJpegBuffer *) object;
|
||||
VipsForeignSaveJpegBuffer *buffer =
|
||||
(VipsForeignSaveJpegBuffer *) object;
|
||||
|
||||
VipsTarget *target;
|
||||
VipsBlob *blob;
|
||||
@ -425,7 +426,7 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object )
|
||||
}
|
||||
|
||||
g_object_get( target, "blob", &blob, NULL );
|
||||
g_object_set( file, "buffer", blob, NULL );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
VIPS_UNREF( target );
|
||||
|
@ -935,6 +935,9 @@ int vips_dzsave( VipsImage *in, const char *name, ... )
|
||||
VIPS_API
|
||||
int vips_dzsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
__attribute__((sentinel));
|
||||
VIPS_API
|
||||
int vips_dzsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
/**
|
||||
* VipsForeignHeifCompression:
|
||||
|
Loading…
Reference in New Issue
Block a user