Add tiffsave target (#2798)
* compiles, but untested * works, but libtiff needs seek for write next: add seek methods to target * add target seek and read seem to work next: disc temps for disc output * add libnsgif COPYING oops, we were missing the COPYING file see https://github.com/libvips/libvips/issues/2800 thanks mika-fischer * tiffsave uses a disc temp if it can * revise temp target rules only make a disc temp if we are writing to a filesystem target * add new target methods to targetcustom
This commit is contained in:
parent
58b53506ff
commit
f8003bda67
ChangeLog
libvips
@ -21,6 +21,9 @@
|
||||
- add "gap" option to vips_reduce[hv]() and vips_resize() [kleisauke]
|
||||
- add "ceil" option to vips_shrink() [kleisauke]
|
||||
- quality improvements for image resizing [kleisauke]
|
||||
- add vips_source_new_from_target()
|
||||
- add vips_target_seek(), vips_target_read(), vips_target_new_temp()
|
||||
- add vips_tiffsave_target()
|
||||
- add vips_target_end(), deprecate vips_target_finish()
|
||||
|
||||
26/11/21 started 8.12.3
|
||||
|
@ -2878,6 +2878,7 @@ vips_foreign_operation_init( void )
|
||||
extern GType vips_foreign_load_tiff_source_get_type( void );
|
||||
extern GType vips_foreign_save_tiff_file_get_type( void );
|
||||
extern GType vips_foreign_save_tiff_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_tiff_target_get_type( void );
|
||||
|
||||
extern GType vips_foreign_load_raw_get_type( void );
|
||||
extern GType vips_foreign_save_raw_get_type( void );
|
||||
@ -3090,6 +3091,7 @@ vips_foreign_operation_init( void )
|
||||
vips_foreign_load_tiff_source_get_type();
|
||||
vips_foreign_save_tiff_file_get_type();
|
||||
vips_foreign_save_tiff_buffer_get_type();
|
||||
vips_foreign_save_tiff_target_get_type();
|
||||
#endif /*HAVE_TIFF*/
|
||||
|
||||
#if defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE)
|
||||
|
@ -51,7 +51,7 @@ extern "C" {
|
||||
|
||||
void vips__tiff_init( void );
|
||||
|
||||
int vips__tiff_write( VipsImage *in, const char *filename,
|
||||
int vips__tiff_write_target( VipsImage *in, VipsTarget *target,
|
||||
VipsForeignTiffCompression compression, int Q,
|
||||
VipsForeignTiffPredictor predictor,
|
||||
const char *profile,
|
||||
@ -72,27 +72,6 @@ int vips__tiff_write( VipsImage *in, const char *filename,
|
||||
gboolean premultiply,
|
||||
int page_height );
|
||||
|
||||
int vips__tiff_write_buf( VipsImage *in,
|
||||
void **obuf, size_t *olen,
|
||||
VipsForeignTiffCompression compression, int Q,
|
||||
VipsForeignTiffPredictor predictor,
|
||||
const char *profile,
|
||||
gboolean tile, int tile_width, int tile_height,
|
||||
gboolean pyramid,
|
||||
int bitdepth,
|
||||
gboolean miniswhite,
|
||||
VipsForeignTiffResunit resunit, double xres, double yres,
|
||||
gboolean bigtiff,
|
||||
gboolean rgbjpeg,
|
||||
gboolean properties, gboolean strip,
|
||||
VipsRegionShrink region_shrink,
|
||||
int level,
|
||||
gboolean lossless,
|
||||
VipsForeignDzDepth depth,
|
||||
gboolean subifd,
|
||||
gboolean premultiply,
|
||||
int page_height );
|
||||
|
||||
gboolean vips__istiff_source( VipsSource *source );
|
||||
gboolean vips__istifftiled_source( VipsSource *source );
|
||||
int vips__tiff_read_header_source( VipsSource *source, VipsImage *out,
|
||||
|
@ -90,48 +90,6 @@ vips__tiff_init( void )
|
||||
TIFFSetWarningHandler( vips__thandler_warning );
|
||||
}
|
||||
|
||||
/* Open TIFF for output.
|
||||
*/
|
||||
TIFF *
|
||||
vips__tiff_openout( const char *path, gboolean bigtiff )
|
||||
{
|
||||
TIFF *tif;
|
||||
const char *mode = bigtiff ? "w8" : "w";
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips__tiff_openout( \"%s\", \"%s\" )\n", path, mode );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Need the utf-16 version on Windows.
|
||||
*/
|
||||
#ifdef G_OS_WIN32
|
||||
{
|
||||
GError *error = NULL;
|
||||
wchar_t *path16;
|
||||
|
||||
if( !(path16 = (wchar_t *)
|
||||
g_utf8_to_utf16( path, -1, NULL, NULL, &error )) ) {
|
||||
vips_g_error( &error );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
tif = TIFFOpenW( path16, mode );
|
||||
|
||||
g_free( path16 );
|
||||
}
|
||||
#else /*!G_OS_WIN32*/
|
||||
tif = TIFFOpen( path, mode );
|
||||
#endif /*G_OS_WIN32*/
|
||||
|
||||
if( !tif ) {
|
||||
vips_error( "tiff",
|
||||
_( "unable to open \"%s\" for output" ), path );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( tif );
|
||||
}
|
||||
|
||||
/* TIFF input from a vips source.
|
||||
*/
|
||||
|
||||
@ -152,13 +110,11 @@ openin_source_write( thandle_t st, tdata_t buffer, tsize_t size )
|
||||
}
|
||||
|
||||
static toff_t
|
||||
openin_source_seek( thandle_t st, toff_t position, int whence )
|
||||
openin_source_seek( thandle_t st, toff_t offset, int whence )
|
||||
{
|
||||
VipsSource *source = VIPS_SOURCE( st );
|
||||
|
||||
/* toff_t is usually uint64, with -1 cast to uint64 to indicate error.
|
||||
*/
|
||||
return( (toff_t) vips_source_seek( source, position, whence ) );
|
||||
return( (toff_t) vips_source_seek( source, offset, whence ) );
|
||||
}
|
||||
|
||||
static int
|
||||
@ -240,146 +196,95 @@ vips__tiff_openin_source( VipsSource *source )
|
||||
return( tiff );
|
||||
}
|
||||
|
||||
/* TIFF output to a memory buffer.
|
||||
/* TIFF output to a target.
|
||||
*/
|
||||
|
||||
typedef struct _VipsTiffOpenoutBuffer {
|
||||
VipsDbuf dbuf;
|
||||
|
||||
/* On close, consolidate and write the output here.
|
||||
*/
|
||||
void **out_data;
|
||||
size_t *out_length;
|
||||
} VipsTiffOpenoutBuffer;
|
||||
|
||||
/* libtiff needs this (!!?!?!) for writing multipage images.
|
||||
*/
|
||||
static tsize_t
|
||||
openout_buffer_read( thandle_t st, tdata_t data, tsize_t size )
|
||||
openout_target_read( thandle_t st, tdata_t data, tsize_t size )
|
||||
{
|
||||
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
|
||||
VipsTarget *target = (VipsTarget *) st;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "openout_buffer_read: %zd bytes\n", size );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
return( vips_dbuf_read( &buffer->dbuf, data, size ) );
|
||||
return( vips_target_read( target, data, size ) );
|
||||
}
|
||||
|
||||
static tsize_t
|
||||
openout_buffer_write( thandle_t st, tdata_t data, tsize_t size )
|
||||
openout_target_write( thandle_t st, tdata_t data, tsize_t size )
|
||||
{
|
||||
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
|
||||
VipsTarget *target = (VipsTarget *) st;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "openout_buffer_write: %zd bytes\n", size );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
vips_dbuf_write( &buffer->dbuf, data, size );
|
||||
if( vips_target_write( target, data, size ) )
|
||||
return( (tsize_t) -1 );
|
||||
|
||||
return( size );
|
||||
}
|
||||
|
||||
static int
|
||||
openout_buffer_close( thandle_t st )
|
||||
static toff_t
|
||||
openout_target_seek( thandle_t st, toff_t offset, int whence )
|
||||
{
|
||||
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
|
||||
VipsTarget *target = (VipsTarget *) st;
|
||||
|
||||
*(buffer->out_data) = vips_dbuf_steal( &buffer->dbuf,
|
||||
buffer->out_length);
|
||||
return( vips_target_seek( target, offset, whence ) );
|
||||
}
|
||||
|
||||
static int
|
||||
openout_target_close( thandle_t st )
|
||||
{
|
||||
VipsTarget *target = (VipsTarget *) st;
|
||||
|
||||
if( vips_target_end( target ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static toff_t
|
||||
openout_buffer_seek( thandle_t st, toff_t position, int whence )
|
||||
{
|
||||
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "openout_buffer_seek: position %zd, whence %d ",
|
||||
position, whence );
|
||||
switch( whence ) {
|
||||
case SEEK_SET:
|
||||
printf( "set" );
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
printf( "end" );
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
printf( "cur" );
|
||||
break;
|
||||
|
||||
default:
|
||||
printf( "unknown" );
|
||||
break;
|
||||
}
|
||||
printf( "\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
vips_dbuf_seek( &buffer->dbuf, position, whence );
|
||||
|
||||
return( vips_dbuf_tell( &buffer->dbuf ) );
|
||||
}
|
||||
|
||||
static toff_t
|
||||
openout_buffer_length( thandle_t st )
|
||||
openout_target_length( thandle_t st )
|
||||
{
|
||||
g_assert_not_reached();
|
||||
|
||||
return( 0 );
|
||||
return( (toff_t) -1 );
|
||||
}
|
||||
|
||||
static int
|
||||
openout_buffer_map( thandle_t st, tdata_t *start, toff_t *len )
|
||||
openout_target_map( thandle_t st, tdata_t *start, toff_t *len )
|
||||
{
|
||||
g_assert_not_reached();
|
||||
|
||||
return( 0 );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
static void
|
||||
openout_buffer_unmap( thandle_t st, tdata_t start, toff_t len )
|
||||
openout_target_unmap( thandle_t st, tdata_t start, toff_t len )
|
||||
{
|
||||
g_assert_not_reached();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* On TIFFClose(), @data and @length are set to point to the output buffer.
|
||||
*/
|
||||
TIFF *
|
||||
vips__tiff_openout_buffer( VipsImage *image,
|
||||
gboolean bigtiff, void **out_data, size_t *out_length )
|
||||
vips__tiff_openout_target( VipsTarget *target, gboolean bigtiff )
|
||||
{
|
||||
const char *mode = bigtiff ? "w8" : "w";
|
||||
|
||||
VipsTiffOpenoutBuffer *buffer;
|
||||
TIFF *tiff;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips__tiff_openout_buffer:\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
g_assert( out_data );
|
||||
g_assert( out_length );
|
||||
|
||||
buffer = VIPS_NEW( image, VipsTiffOpenoutBuffer );
|
||||
vips_dbuf_init( &buffer->dbuf );
|
||||
buffer->out_data = out_data;
|
||||
buffer->out_length = out_length;
|
||||
|
||||
if( !(tiff = TIFFClientOpen( "memory output", mode,
|
||||
(thandle_t) buffer,
|
||||
openout_buffer_read,
|
||||
openout_buffer_write,
|
||||
openout_buffer_seek,
|
||||
openout_buffer_close,
|
||||
openout_buffer_length,
|
||||
openout_buffer_map,
|
||||
openout_buffer_unmap )) ) {
|
||||
vips_error( "vips__tiff_openout_buffer", "%s",
|
||||
_( "unable to open memory buffer for output" ) );
|
||||
if( !(tiff = TIFFClientOpen( "target output", mode,
|
||||
(thandle_t) target,
|
||||
openout_target_read,
|
||||
openout_target_write,
|
||||
openout_target_seek,
|
||||
openout_target_close,
|
||||
openout_target_length,
|
||||
openout_target_map,
|
||||
openout_target_unmap )) ) {
|
||||
vips_error( "vips__tiff_openout_target", "%s",
|
||||
_( "unable to open target for output" ) );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
@ -387,4 +292,3 @@ vips__tiff_openout_buffer( VipsImage *image,
|
||||
}
|
||||
|
||||
#endif /*HAVE_TIFF*/
|
||||
|
||||
|
@ -40,8 +40,7 @@ extern "C" {
|
||||
TIFF *vips__tiff_openin_source( VipsSource *source );
|
||||
|
||||
TIFF *vips__tiff_openout( const char *path, gboolean bigtiff );
|
||||
TIFF *vips__tiff_openout_buffer( VipsImage *image,
|
||||
gboolean bigtiff, void **out_data, size_t *out_length );
|
||||
TIFF *vips__tiff_openout_target( VipsTarget *target, gboolean bigtiff );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
||||
* - deprecate "squash"
|
||||
* 1/5/21
|
||||
* - add "premultiply" flag
|
||||
* 10/5/22
|
||||
* - add vips_tiffsave_target()
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -83,6 +85,10 @@
|
||||
typedef struct _VipsForeignSaveTiff {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
/* Set by subclasses.
|
||||
*/
|
||||
VipsTarget *target;
|
||||
|
||||
/* Many options argh.
|
||||
*/
|
||||
VipsForeignTiffCompression compression;
|
||||
@ -116,6 +122,17 @@ typedef VipsForeignSaveClass VipsForeignSaveTiffClass;
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveTiff, vips_foreign_save_tiff,
|
||||
VIPS_TYPE_FOREIGN_SAVE );
|
||||
|
||||
static void
|
||||
vips_foreign_save_tiff_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) gobject;
|
||||
|
||||
VIPS_UNREF( tiff->target );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_save_tiff_parent_class )->
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
#define UC VIPS_FORMAT_UCHAR
|
||||
|
||||
/* Type promotion for jpeg-in-tiff save ... just always go to uchar.
|
||||
@ -186,6 +203,37 @@ vips_foreign_save_tiff_build( VipsObject *object )
|
||||
tiff->yres *= 2.54;
|
||||
}
|
||||
|
||||
/* Handle the deprecated squash parameter.
|
||||
*/
|
||||
if( tiff->squash )
|
||||
/* We set that even in the case of LAB to LABQ.
|
||||
*/
|
||||
tiff->bitdepth = 1;
|
||||
|
||||
if( vips__tiff_write_target( save->ready, tiff->target,
|
||||
tiff->compression, tiff->Q, tiff->predictor,
|
||||
tiff->profile,
|
||||
tiff->tile, tiff->tile_width, tiff->tile_height,
|
||||
tiff->pyramid,
|
||||
tiff->bitdepth,
|
||||
tiff->miniswhite,
|
||||
tiff->resunit, tiff->xres, tiff->yres,
|
||||
tiff->bigtiff,
|
||||
tiff->rgbjpeg,
|
||||
tiff->properties,
|
||||
save->strip,
|
||||
tiff->region_shrink,
|
||||
tiff->level,
|
||||
tiff->lossless,
|
||||
tiff->depth,
|
||||
tiff->subifd,
|
||||
tiff->premultiply,
|
||||
save->page_height ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_target_end( tiff->target ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -197,11 +245,12 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class )
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_foreign_save_tiff_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "tiffsave_base";
|
||||
object_class->description = _( "save image to tiff file" );
|
||||
object_class->description = _( "save image as tiff" );
|
||||
object_class->build = vips_foreign_save_tiff_build;
|
||||
|
||||
foreign_class->suffs = vips__foreign_tiff_suffs;
|
||||
@ -392,6 +441,61 @@ vips_foreign_save_tiff_init( VipsForeignSaveTiff *tiff )
|
||||
tiff->bitdepth = 0;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveTiffTarget {
|
||||
VipsForeignSaveTiff parent_object;
|
||||
|
||||
VipsTarget *target;
|
||||
} VipsForeignSaveTiffTarget;
|
||||
|
||||
typedef VipsForeignSaveTiffClass VipsForeignSaveTiffTargetClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveTiffTarget, vips_foreign_save_tiff_target,
|
||||
vips_foreign_save_tiff_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_tiff_target_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
|
||||
VipsForeignSaveTiffTarget *target =
|
||||
(VipsForeignSaveTiffTarget *) object;
|
||||
|
||||
tiff->target = target->target;
|
||||
g_object_ref( tiff->target );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_target_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_tiff_target_class_init(
|
||||
VipsForeignSaveTiffTargetClass *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 = "tiffsave_target";
|
||||
object_class->description = _( "save image to tiff target" );
|
||||
object_class->build = vips_foreign_save_tiff_target_build;
|
||||
|
||||
VIPS_ARG_OBJECT( class, "target", 1,
|
||||
_( "Target" ),
|
||||
_( "Target to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveTiffTarget, target ),
|
||||
VIPS_TYPE_TARGET );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_tiff_target_init( VipsForeignSaveTiffTarget *target )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveTiffFile {
|
||||
VipsForeignSaveTiff parent_object;
|
||||
|
||||
@ -406,40 +510,14 @@ G_DEFINE_TYPE( VipsForeignSaveTiffFile, vips_foreign_save_tiff_file,
|
||||
static int
|
||||
vips_foreign_save_tiff_file_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
|
||||
VipsForeignSaveTiffFile *file = (VipsForeignSaveTiffFile *) object;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_file_parent_class )->
|
||||
build( object ) )
|
||||
if( !(tiff->target = vips_target_new_to_file( file->filename )) )
|
||||
return( -1 );
|
||||
|
||||
/* Handle the deprecated squash parameter.
|
||||
*/
|
||||
if( tiff->squash )
|
||||
/* We set that even in the case of LAB to LABQ.
|
||||
*/
|
||||
tiff->bitdepth = 1;
|
||||
|
||||
if( vips__tiff_write( save->ready, file->filename,
|
||||
tiff->compression, tiff->Q, tiff->predictor,
|
||||
tiff->profile,
|
||||
tiff->tile, tiff->tile_width, tiff->tile_height,
|
||||
tiff->pyramid,
|
||||
tiff->bitdepth,
|
||||
tiff->miniswhite,
|
||||
tiff->resunit, tiff->xres, tiff->yres,
|
||||
tiff->bigtiff,
|
||||
tiff->rgbjpeg,
|
||||
tiff->properties,
|
||||
save->strip,
|
||||
tiff->region_shrink,
|
||||
tiff->level,
|
||||
tiff->lossless,
|
||||
tiff->depth,
|
||||
tiff->subifd,
|
||||
tiff->premultiply,
|
||||
save->page_height ) )
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -485,40 +563,21 @@ G_DEFINE_TYPE( VipsForeignSaveTiffBuffer, vips_foreign_save_tiff_buffer,
|
||||
static int
|
||||
vips_foreign_save_tiff_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
|
||||
VipsForeignSaveTiffBuffer *buffer =
|
||||
(VipsForeignSaveTiffBuffer *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsBlob *blob;
|
||||
|
||||
if( !(tiff->target = vips_target_new_to_memory()) )
|
||||
return( -1 );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips__tiff_write_buf( save->ready, &obuf, &olen,
|
||||
tiff->compression, tiff->Q, tiff->predictor,
|
||||
tiff->profile,
|
||||
tiff->tile, tiff->tile_width, tiff->tile_height,
|
||||
tiff->pyramid,
|
||||
tiff->bitdepth,
|
||||
tiff->miniswhite,
|
||||
tiff->resunit, tiff->xres, tiff->yres,
|
||||
tiff->bigtiff,
|
||||
tiff->rgbjpeg,
|
||||
tiff->properties,
|
||||
save->strip,
|
||||
tiff->region_shrink,
|
||||
tiff->level,
|
||||
tiff->lossless,
|
||||
tiff->depth,
|
||||
tiff->subifd,
|
||||
tiff->premultiply,
|
||||
save->page_height ) )
|
||||
return( -1 );
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) vips_area_free_cb, obuf, olen );
|
||||
g_object_set( object, "buffer", blob, NULL );
|
||||
g_object_get( tiff->target, "blob", &blob, NULL );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
@ -654,8 +713,7 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
|
||||
* resolutions. By default these values are taken from the VIPS image header.
|
||||
* libvips resolution is always in pixels per millimetre.
|
||||
*
|
||||
* Set @bigtiff to attempt to write a bigtiff.
|
||||
* Bigtiff is a variant of the TIFF
|
||||
* Set @bigtiff to attempt to write a bigtiff. Bigtiff is a variant of the TIFF
|
||||
* format that allows more than 4GB in a file.
|
||||
*
|
||||
* Set @properties to write all vips metadata to the IMAGEDESCRIPTION tag as
|
||||
@ -673,7 +731,7 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
|
||||
* Set @subifd to save pyramid layers as sub-directories of the main image.
|
||||
* Setting this option can improve compatibility with formats like OME.
|
||||
*
|
||||
* Set @premultiply tio save with premultiplied alpha. Some programs, such as
|
||||
* Set @premultiply to save with premultiplied alpha. Some programs, such as
|
||||
* InDesign, will only work with premultiplied alpha.
|
||||
*
|
||||
* See also: vips_tiffload(), vips_image_write_to_file().
|
||||
@ -761,3 +819,52 @@ vips_tiffsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_tiffsave_target: (method)
|
||||
* @in: image to save
|
||||
* @target: save image to this target
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @compression: use this #VipsForeignTiffCompression
|
||||
* * @Q: %gint quality factor
|
||||
* * @predictor: use this #VipsForeignTiffPredictor
|
||||
* * @profile: %gchararray, filename of ICC profile to attach
|
||||
* * @tile: %gboolean, set %TRUE to write a tiled tiff
|
||||
* * @tile_width: %gint for tile size
|
||||
* * @tile_height: %gint for tile size
|
||||
* * @pyramid: %gboolean, write an image pyramid
|
||||
* * @bitdepth: %int, set write bit depth to 1, 2, 4 or 8
|
||||
* * @miniswhite: %gboolean, write 1-bit images as MINISWHITE
|
||||
* * @resunit: #VipsForeignTiffResunit for resolution unit
|
||||
* * @xres: %gdouble horizontal resolution in pixels/mm
|
||||
* * @yres: %gdouble vertical resolution in pixels/mm
|
||||
* * @bigtiff: %gboolean, write a BigTiff file
|
||||
* * @properties: %gboolean, set %TRUE to write an IMAGEDESCRIPTION tag
|
||||
* * @region_shrink: #VipsRegionShrink How to shrink each 2x2 region.
|
||||
* * @level: %gint, Zstd compression level
|
||||
* * @lossless: %gboolean, WebP losssless mode
|
||||
* * @depth: #VipsForeignDzDepth how deep to make the pyramid
|
||||
* * @subifd: %gboolean write pyr layers as sub-ifds
|
||||
* * @premultiply: %gboolean write premultiplied alpha
|
||||
*
|
||||
* As vips_tiffsave(), but save to a target.
|
||||
*
|
||||
* See also: vips_tiffsave(), vips_image_write_to_target().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_tiffsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, target );
|
||||
result = vips_call_split( "tiffsave_target", ap, in, target );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
@ -202,6 +202,8 @@
|
||||
* 20/10/21 [jacopoabramo]
|
||||
* - subifd enables pyramid
|
||||
* - add support for page_height param
|
||||
* 11/5/22
|
||||
* - switch to terget API for output
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -293,14 +295,9 @@ typedef struct _Wtiff Wtiff;
|
||||
struct _Layer {
|
||||
Wtiff *wtiff; /* Main wtiff struct */
|
||||
|
||||
/* The filename for this layer, for file output.
|
||||
/* The temp target for this layer.
|
||||
*/
|
||||
char *lname;
|
||||
|
||||
/* The memory area for this layer, for memory output.
|
||||
*/
|
||||
void *buf;
|
||||
size_t len;
|
||||
VipsTarget *target;
|
||||
|
||||
int width, height; /* Layer size */
|
||||
int sub; /* Subsample factor for this layer */
|
||||
@ -335,14 +332,9 @@ struct _Wtiff {
|
||||
*/
|
||||
VipsImage *ready;
|
||||
|
||||
/* File to write to, or NULL.
|
||||
/* Target to write to.
|
||||
*/
|
||||
char *filename; /* Name we write to */
|
||||
|
||||
/* Memory area to output, or NULL.
|
||||
*/
|
||||
void **obuf;
|
||||
size_t *olen;
|
||||
VipsTarget *target;
|
||||
|
||||
Layer *layer; /* Top of pyramid */
|
||||
VipsPel *tbuf; /* TIFF output buffer */
|
||||
@ -453,9 +445,6 @@ wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
|
||||
else
|
||||
(*layer)->sub = above->sub * 2;
|
||||
|
||||
(*layer)->lname = NULL;
|
||||
(*layer)->buf = NULL;
|
||||
(*layer)->len = 0;
|
||||
(*layer)->tif = NULL;
|
||||
(*layer)->image = NULL;
|
||||
(*layer)->write_y = 0;
|
||||
@ -466,26 +455,16 @@ wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
|
||||
(*layer)->below = NULL;
|
||||
(*layer)->above = above;
|
||||
|
||||
/* The name for the top layer is the output filename.
|
||||
*
|
||||
* We need lname to be freed automatically: it has to stay
|
||||
* alive until after wtiff_gather().
|
||||
/* The target we write to. The base layer writes to the main
|
||||
* output, each layer smaller writes to a memory temp.
|
||||
*/
|
||||
if( wtiff->filename ) {
|
||||
if( !above )
|
||||
(*layer)->lname = vips_strdup(
|
||||
VIPS_OBJECT( wtiff->ready ),
|
||||
wtiff->filename );
|
||||
else {
|
||||
char *lname;
|
||||
|
||||
lname = vips__temp_name( "%s.tif" );
|
||||
(*layer)->lname = vips_strdup(
|
||||
VIPS_OBJECT( wtiff->ready ),
|
||||
lname );
|
||||
g_free( lname );
|
||||
}
|
||||
if( !above ) {
|
||||
(*layer)->target = wtiff->target;
|
||||
g_object_ref( (*layer)->target );
|
||||
}
|
||||
else
|
||||
(*layer)->target =
|
||||
vips_target_new_temp( wtiff->target );
|
||||
|
||||
/*
|
||||
printf( "wtiff_layer_init: sub = %d, width = %d, height = %d\n",
|
||||
@ -929,14 +908,8 @@ wtiff_allocate_layers( Wtiff *wtiff )
|
||||
vips__region_no_ownership( layer->strip );
|
||||
vips__region_no_ownership( layer->copy );
|
||||
|
||||
if( layer->lname )
|
||||
layer->tif = vips__tiff_openout(
|
||||
layer->lname, wtiff->bigtiff );
|
||||
else {
|
||||
layer->tif = vips__tiff_openout_buffer(
|
||||
wtiff->ready, wtiff->bigtiff,
|
||||
&layer->buf, &layer->len );
|
||||
}
|
||||
layer->tif = vips__tiff_openout_target( layer->target,
|
||||
wtiff->bigtiff );
|
||||
if( !layer->tif )
|
||||
return( -1 );
|
||||
}
|
||||
@ -962,42 +935,17 @@ wtiff_allocate_layers( Wtiff *wtiff )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Delete any temp files we wrote.
|
||||
*/
|
||||
static void
|
||||
wtiff_delete_temps( Wtiff *wtiff )
|
||||
{
|
||||
Layer *layer;
|
||||
|
||||
/* Don't delete the top layer: that's the output file.
|
||||
*/
|
||||
if( wtiff->layer &&
|
||||
wtiff->layer->below )
|
||||
for( layer = wtiff->layer->below; layer; layer = layer->below )
|
||||
if( layer->lname ) {
|
||||
#ifndef DEBUG
|
||||
unlink( layer->lname );
|
||||
#else /*!DEBUG*/
|
||||
printf( "wtiff_delete_temps: "
|
||||
"debug mode, not deleting %s\n",
|
||||
layer->lname );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
VIPS_FREE( layer->buf );
|
||||
layer->lname = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free a single pyramid layer.
|
||||
*/
|
||||
static void
|
||||
layer_free( Layer *layer )
|
||||
{
|
||||
/* Don't unref the target for this layer -- we'll need it for gather.
|
||||
*/
|
||||
VIPS_FREEF( TIFFClose, layer->tif );
|
||||
VIPS_UNREF( layer->strip );
|
||||
VIPS_UNREF( layer->copy );
|
||||
VIPS_UNREF( layer->image );
|
||||
VIPS_FREE( layer->buf );
|
||||
VIPS_FREEF( TIFFClose, layer->tif );
|
||||
}
|
||||
|
||||
/* Free an entire pyramid.
|
||||
@ -1014,11 +962,16 @@ layer_free_all( Layer *layer )
|
||||
static void
|
||||
wtiff_free( Wtiff *wtiff )
|
||||
{
|
||||
wtiff_delete_temps( wtiff );
|
||||
Layer *layer;
|
||||
|
||||
/* unref all the targets, including the base layer.
|
||||
*/
|
||||
for( layer = wtiff->layer; layer; layer = layer->below )
|
||||
VIPS_UNREF( layer->target );
|
||||
|
||||
VIPS_UNREF( wtiff->ready );
|
||||
VIPS_FREE( wtiff->tbuf );
|
||||
VIPS_FREEF( layer_free_all, wtiff->layer );
|
||||
VIPS_FREE( wtiff->filename );
|
||||
VIPS_FREE( wtiff );
|
||||
}
|
||||
|
||||
@ -1129,7 +1082,7 @@ ready_to_write( Wtiff *wtiff )
|
||||
}
|
||||
|
||||
static Wtiff *
|
||||
wtiff_new( VipsImage *input, const char *filename,
|
||||
wtiff_new( VipsImage *input, VipsTarget *target,
|
||||
VipsForeignTiffCompression compression, int Q,
|
||||
VipsForeignTiffPredictor predictor,
|
||||
const char *profile,
|
||||
@ -1156,7 +1109,7 @@ wtiff_new( VipsImage *input, const char *filename,
|
||||
return( NULL );
|
||||
wtiff->input = input;
|
||||
wtiff->ready = NULL;
|
||||
wtiff->filename = filename ? vips_strdup( NULL, filename ) : NULL;
|
||||
wtiff->target = target;
|
||||
wtiff->layer = NULL;
|
||||
wtiff->tbuf = NULL;
|
||||
wtiff->compression = get_compression( compression );
|
||||
@ -2110,19 +2063,12 @@ wtiff_gather( Wtiff *wtiff )
|
||||
TIFF *in;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "appending layer %s ...\n", layer->lname );
|
||||
printf( "appending layer sub = %d ...\n", layer->sub );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( layer->lname ) {
|
||||
if( !(source = vips_source_new_from_file(
|
||||
layer->lname )) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
if( !(source = vips_source_new_from_memory(
|
||||
layer->buf, layer->len )) )
|
||||
return( -1 );
|
||||
}
|
||||
if( !(source =
|
||||
vips_source_new_from_target( layer->target )) )
|
||||
return( -1 );
|
||||
|
||||
if( !(in = vips__tiff_openin_source( source )) ) {
|
||||
VIPS_UNREF( source );
|
||||
@ -2204,6 +2150,8 @@ wtiff_page_end( Wtiff *wtiff )
|
||||
/* Append any pyr layers, if necessary.
|
||||
*/
|
||||
if( wtiff->layer->below ) {
|
||||
Layer *layer;
|
||||
|
||||
/* Free any lower pyramid resources ... this will
|
||||
* TIFFClose() (but not delete) the smaller layers
|
||||
* ready for us to read from them again.
|
||||
@ -2215,14 +2163,14 @@ wtiff_page_end( Wtiff *wtiff )
|
||||
if( wtiff_gather( wtiff ) )
|
||||
return( -1 );
|
||||
|
||||
/* We can delete any temps now ready for the next page.
|
||||
/* unref all the lower targets.
|
||||
*/
|
||||
wtiff_delete_temps( wtiff );
|
||||
for( layer = wtiff->layer->below; layer; layer = layer->below )
|
||||
VIPS_UNREF( layer->target );
|
||||
|
||||
/* And free all lower pyr layers ready to be rebuilt for the
|
||||
* next page.
|
||||
/* ... ready for the next page.
|
||||
*/
|
||||
VIPS_FREEF( layer_free_all, wtiff->layer->below );
|
||||
wtiff->layer->below = NULL;
|
||||
}
|
||||
|
||||
wtiff->page_number += 1;
|
||||
@ -2294,55 +2242,7 @@ wtiff_sink_disc_strip( VipsRegion *region, VipsRect *area, void *a )
|
||||
}
|
||||
|
||||
int
|
||||
vips__tiff_write( VipsImage *input, const char *filename,
|
||||
VipsForeignTiffCompression compression, int Q,
|
||||
VipsForeignTiffPredictor predictor,
|
||||
const char *profile,
|
||||
gboolean tile, int tile_width, int tile_height,
|
||||
gboolean pyramid,
|
||||
int bitdepth,
|
||||
gboolean miniswhite,
|
||||
VipsForeignTiffResunit resunit, double xres, double yres,
|
||||
gboolean bigtiff,
|
||||
gboolean rgbjpeg,
|
||||
gboolean properties, gboolean strip,
|
||||
VipsRegionShrink region_shrink,
|
||||
int level,
|
||||
gboolean lossless,
|
||||
VipsForeignDzDepth depth,
|
||||
gboolean subifd,
|
||||
gboolean premultiply,
|
||||
int page_height )
|
||||
{
|
||||
Wtiff *wtiff;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
vips__tiff_init();
|
||||
|
||||
if( !(wtiff = wtiff_new( input, filename,
|
||||
compression, Q, predictor, profile,
|
||||
tile, tile_width, tile_height, pyramid, bitdepth,
|
||||
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
|
||||
properties, strip, region_shrink, level, lossless, depth,
|
||||
subifd, premultiply, page_height )) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_sink_disc( wtiff->ready, wtiff_sink_disc_strip, wtiff ) ) {
|
||||
wtiff_free( wtiff );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
wtiff_free( wtiff );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
vips__tiff_write_buf( VipsImage *input,
|
||||
void **obuf, size_t *olen,
|
||||
vips__tiff_write_target( VipsImage *input, VipsTarget *target,
|
||||
VipsForeignTiffCompression compression, int Q,
|
||||
VipsForeignTiffPredictor predictor,
|
||||
const char *profile,
|
||||
@ -2366,7 +2266,7 @@ vips__tiff_write_buf( VipsImage *input,
|
||||
|
||||
vips__tiff_init();
|
||||
|
||||
if( !(wtiff = wtiff_new( input, NULL,
|
||||
if( !(wtiff = wtiff_new( input, target,
|
||||
compression, Q, predictor, profile,
|
||||
tile, tile_width, tile_height, pyramid, bitdepth,
|
||||
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
|
||||
@ -2374,26 +2274,11 @@ vips__tiff_write_buf( VipsImage *input,
|
||||
subifd, premultiply, page_height )) )
|
||||
return( -1 );
|
||||
|
||||
wtiff->obuf = obuf;
|
||||
wtiff->olen = olen;
|
||||
|
||||
if( vips_sink_disc( wtiff->ready, wtiff_sink_disc_strip, wtiff ) ) {
|
||||
wtiff_free( wtiff );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Now close the top layer, and we'll get a pointer we can return
|
||||
* to our caller.
|
||||
*/
|
||||
VIPS_FREEF( TIFFClose, wtiff->layer->tif );
|
||||
|
||||
*obuf = wtiff->layer->buf;
|
||||
*olen = wtiff->layer->len;
|
||||
|
||||
/* Now our caller owns it, we must not free it.
|
||||
*/
|
||||
wtiff->layer->buf = NULL;
|
||||
|
||||
wtiff_free( wtiff );
|
||||
|
||||
return( 0 );
|
||||
|
@ -225,6 +225,8 @@ VipsSource *vips_source_new_from_file( const char *filename );
|
||||
VIPS_API
|
||||
VipsSource *vips_source_new_from_blob( VipsBlob *blob );
|
||||
VIPS_API
|
||||
VipsSource *vips_source_new_from_target( VipsTarget *target );
|
||||
VIPS_API
|
||||
VipsSource *vips_source_new_from_memory( const void *data, size_t size );
|
||||
VIPS_API
|
||||
VipsSource *vips_source_new_from_options( const char *options );
|
||||
@ -410,9 +412,11 @@ struct _VipsTarget {
|
||||
*/
|
||||
gboolean ended;
|
||||
|
||||
/* Write memory output here.
|
||||
/* Write memory output and track write point here. We use a GString
|
||||
* rather than a GByteArray since we need eg. g_string_overwrite_len().
|
||||
*/
|
||||
GByteArray *memory_buffer;
|
||||
GString *memory_buffer;
|
||||
off_t position;
|
||||
|
||||
/* And return memory via this blob.
|
||||
*/
|
||||
@ -424,6 +428,11 @@ struct _VipsTarget {
|
||||
unsigned char output_buffer[VIPS_TARGET_BUFFER_SIZE];
|
||||
int write_point;
|
||||
|
||||
/* Temp targets on the filesystem need deleting, sometimes.
|
||||
*/
|
||||
gboolean delete_on_close;
|
||||
char *delete_on_close_filename;
|
||||
|
||||
};
|
||||
|
||||
typedef struct _VipsTargetClass {
|
||||
@ -436,6 +445,24 @@ typedef struct _VipsTargetClass {
|
||||
*/
|
||||
gint64 (*write)( VipsTarget *, const void *, size_t );
|
||||
|
||||
/* libtiff needs to be able to seek and read on targets,
|
||||
* unfortunately.
|
||||
*
|
||||
* This will not work for eg. pipes, of course.
|
||||
*/
|
||||
|
||||
/* Read from the target into the supplied buffer, args exactly as
|
||||
* read(2). Set errno on error.
|
||||
*
|
||||
* We must return gint64, since ssize_t is often defined as unsigned
|
||||
* on Windows.
|
||||
*/
|
||||
gint64 (*read)( VipsTarget *, void *, size_t );
|
||||
|
||||
/* Seek output. Args exactly as lseek(2).
|
||||
*/
|
||||
off_t (*seek)( VipsTarget *, off_t offset, int whence);
|
||||
|
||||
/* Output has been generated, so do any clearing up,
|
||||
* eg. copy the bytes we saved in memory to the target blob.
|
||||
*/
|
||||
@ -457,8 +484,14 @@ VipsTarget *vips_target_new_to_file( const char *filename );
|
||||
VIPS_API
|
||||
VipsTarget *vips_target_new_to_memory( void );
|
||||
VIPS_API
|
||||
VipsTarget *vips_target_new_temp( VipsTarget *target );
|
||||
VIPS_API
|
||||
int vips_target_write( VipsTarget *target, const void *data, size_t length );
|
||||
VIPS_API
|
||||
gint64 vips_target_read( VipsTarget *target, void *buffer, size_t length );
|
||||
VIPS_API
|
||||
off_t vips_target_seek( VipsTarget *target, off_t offset, int whence );
|
||||
VIPS_API
|
||||
int vips_target_end( VipsTarget *target );
|
||||
VIPS_DEPRECATED_FOR(vips_target_end)
|
||||
void vips_target_finish( VipsTarget *target );
|
||||
@ -514,6 +547,8 @@ typedef struct _VipsTargetCustomClass {
|
||||
*/
|
||||
|
||||
gint64 (*write)( VipsTargetCustom *, const void *, gint64 );
|
||||
gint64 (*read)( VipsTargetCustom *, void *, gint64 );
|
||||
gint64 (*seek)( VipsTargetCustom *, gint64, int );
|
||||
int (*end)( VipsTargetCustom * );
|
||||
void (*finish)( VipsTargetCustom * );
|
||||
|
||||
|
@ -597,6 +597,9 @@ int vips_tiffsave( VipsImage *in, const char *filename, ... )
|
||||
VIPS_API
|
||||
int vips_tiffsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
__attribute__((sentinel));
|
||||
VIPS_API
|
||||
int vips_tiffsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
VIPS_API
|
||||
int vips_openexrload( const char *filename, VipsImage **out, ... )
|
||||
|
@ -784,6 +784,7 @@ vips_object_get_argument( VipsObject *object, const char *name,
|
||||
_( "no vips argument named `%s'" ), name );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( !(*argument_instance = vips__argument_get_instance(
|
||||
*argument_class, object )) ) {
|
||||
vips_error( class->nickname,
|
||||
|
@ -12,6 +12,8 @@
|
||||
* descriptors
|
||||
* 8/10/21
|
||||
* - fix named pipes
|
||||
* 10/5/22
|
||||
* - add vips_source_new_from_target()
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -487,6 +489,48 @@ vips_source_new_from_blob( VipsBlob *blob )
|
||||
return( source );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_source_new_from_target:
|
||||
* @target: build the source from this target
|
||||
*
|
||||
* Create a source from a temp target that has been written to.
|
||||
*
|
||||
* Returns: a new source.
|
||||
*/
|
||||
VipsSource *
|
||||
vips_source_new_from_target( VipsTarget *target )
|
||||
{
|
||||
VipsConnection *connection = VIPS_CONNECTION( target );
|
||||
|
||||
VipsSource *source;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_source_new_from_target: %p\n", target );
|
||||
|
||||
/* Flush output buffer, move memory into the blob, etc.
|
||||
*/
|
||||
if( vips_target_end( target ) )
|
||||
return( NULL );
|
||||
|
||||
if( connection->descriptor > 0 ) {
|
||||
source = vips_source_new_from_descriptor(
|
||||
connection->descriptor );
|
||||
}
|
||||
else if( target->memory ) {
|
||||
VipsBlob *blob;
|
||||
|
||||
g_object_get( target, "blob", &blob, NULL );
|
||||
source = vips_source_new_from_blob( blob );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
}
|
||||
else {
|
||||
vips_error( vips_connection_nick( connection ),
|
||||
"%s", _( "unimplemented target" ) );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( source );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_source_new_from_memory:
|
||||
* @data: memory area to load
|
||||
|
@ -65,7 +65,10 @@
|
||||
#include <vips/debug.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#define MODE_WRITE CLOEXEC (BINARYIZE (O_WRONLY | O_CREAT | O_TRUNC))
|
||||
/* libtiff needs to be able to seek and read back output files, unfortunately,
|
||||
* so we must open read-write.
|
||||
*/
|
||||
#define MODE_READWRITE CLOEXEC (BINARYIZE (O_RDWR | O_CREAT | O_TRUNC))
|
||||
|
||||
G_DEFINE_TYPE( VipsTarget, vips_target, VIPS_TYPE_CONNECTION );
|
||||
|
||||
@ -76,12 +79,22 @@ vips_target_finalize( GObject *gobject )
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_finalize:\n" );
|
||||
|
||||
VIPS_FREEF( g_byte_array_unref, target->memory_buffer );
|
||||
if( target->memory_buffer ) {
|
||||
g_string_free( target->memory_buffer, TRUE );
|
||||
target->memory_buffer = NULL;
|
||||
}
|
||||
|
||||
if( target->blob ) {
|
||||
vips_area_unref( VIPS_AREA( target->blob ) );
|
||||
target->blob = NULL;
|
||||
}
|
||||
|
||||
if( target->delete_on_close &&
|
||||
target->delete_on_close_filename )
|
||||
g_unlink( target->delete_on_close_filename );
|
||||
|
||||
VIPS_FREE( target->delete_on_close_filename );
|
||||
|
||||
G_OBJECT_CLASS( vips_target_parent_class )->finalize( gobject );
|
||||
}
|
||||
|
||||
@ -111,7 +124,7 @@ vips_target_build( VipsObject *object )
|
||||
/* 0644 is rw user, r group and other.
|
||||
*/
|
||||
if( (fd = vips_tracked_open( filename,
|
||||
MODE_WRITE, 0644 )) == -1 ) {
|
||||
MODE_READWRITE, 0644 )) == -1 ) {
|
||||
vips_error_system( errno,
|
||||
vips_connection_nick( connection ),
|
||||
"%s", _( "unable to open for write" ) );
|
||||
@ -133,7 +146,8 @@ vips_target_build( VipsObject *object )
|
||||
#endif /*G_OS_WIN32*/
|
||||
}
|
||||
else if( target->memory )
|
||||
target->memory_buffer = g_byte_array_new();
|
||||
target->memory_buffer =
|
||||
g_string_sized_new( VIPS_TARGET_BUFFER_SIZE );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -143,9 +157,97 @@ vips_target_write_real( VipsTarget *target, const void *data, size_t length )
|
||||
{
|
||||
VipsConnection *connection = VIPS_CONNECTION( target );
|
||||
|
||||
gint64 result;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_write_real: %zd bytes\n", length );
|
||||
|
||||
return( write( connection->descriptor, data, length ) );
|
||||
if( target->memory_buffer ) {
|
||||
VIPS_DEBUG_MSG( "vips_target_write_real: to position %zd\n",
|
||||
target->position );
|
||||
|
||||
g_string_overwrite_len( target->memory_buffer, target->position,
|
||||
data, length );
|
||||
target->position += length;
|
||||
result = length;
|
||||
}
|
||||
else
|
||||
result = write( connection->descriptor, data, length );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
static off_t
|
||||
vips_target_seek_real( VipsTarget *target, off_t offset, int whence )
|
||||
{
|
||||
VipsConnection *connection = VIPS_CONNECTION( target );
|
||||
const char *nick = vips_connection_nick( connection );
|
||||
|
||||
off_t new_position;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_seek_real: offset = %ld, whence = %d\n",
|
||||
offset, whence );
|
||||
|
||||
if( target->memory_buffer ) {
|
||||
switch( whence ) {
|
||||
case SEEK_SET:
|
||||
new_position = offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
new_position = target->position + offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
new_position = target->memory_buffer->len + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( nick, "%s", _( "bad 'whence'" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( new_position > target->memory_buffer->len )
|
||||
g_string_set_size( target->memory_buffer,
|
||||
new_position );
|
||||
|
||||
target->position = new_position;
|
||||
}
|
||||
else
|
||||
new_position = lseek( connection->descriptor, offset, whence );
|
||||
|
||||
return( new_position );
|
||||
}
|
||||
|
||||
static gint64
|
||||
vips_target_read_real( VipsTarget *target, void *data, size_t length )
|
||||
{
|
||||
gint64 bytes_read;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_read_real: %zd bytes\n", length );
|
||||
|
||||
if( target->memory_buffer ) {
|
||||
bytes_read = VIPS_MIN( length,
|
||||
target->memory_buffer->len - target->position );
|
||||
|
||||
VIPS_DEBUG_MSG( " %zd bytes from memory\n", bytes_read );
|
||||
memcpy( data,
|
||||
target->memory_buffer->str +
|
||||
target->position,
|
||||
bytes_read );
|
||||
target->position += bytes_read;
|
||||
}
|
||||
else {
|
||||
VipsConnection *connection = VIPS_CONNECTION( target );
|
||||
int fd = connection->descriptor;
|
||||
|
||||
do {
|
||||
bytes_read = read( fd, data, length );
|
||||
} while( bytes_read < 0 && errno == EINTR );
|
||||
}
|
||||
|
||||
VIPS_DEBUG_MSG( " read %zd bytes\n", bytes_read );
|
||||
|
||||
return( bytes_read );
|
||||
}
|
||||
|
||||
static int
|
||||
@ -178,6 +280,8 @@ vips_target_class_init( VipsTargetClass *class )
|
||||
object_class->build = vips_target_build;
|
||||
|
||||
class->write = vips_target_write_real;
|
||||
class->read = vips_target_read_real;
|
||||
class->seek = vips_target_seek_real;
|
||||
class->end = vips_target_end_real;
|
||||
class->finish = vips_target_finish_real;
|
||||
|
||||
@ -295,6 +399,50 @@ vips_target_new_to_memory( void )
|
||||
return( target );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_target_new_temp:
|
||||
* @based_on: base the temporary target on this target
|
||||
*
|
||||
* Create a temporary target -- either a temporary file on disc, or an area in
|
||||
* memory, depending on what sort of target @based_on is.
|
||||
*
|
||||
* See also: vips_target_new_to_file().
|
||||
*
|
||||
* Returns: a new target.
|
||||
*/
|
||||
VipsTarget *
|
||||
vips_target_new_temp( VipsTarget *based_on )
|
||||
{
|
||||
VipsTarget *target;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_new_temp: %p\n", based_on );
|
||||
|
||||
if( vips_connection_filename( VIPS_CONNECTION( based_on ) ) ) {
|
||||
int descriptor;
|
||||
char *filename;
|
||||
|
||||
if( !(filename = vips__temp_name( "%s.target" )) )
|
||||
return( NULL );
|
||||
if( (descriptor =
|
||||
vips__open_image_write( filename, TRUE )) < 0 ) {
|
||||
g_free( filename );
|
||||
return( NULL );
|
||||
}
|
||||
if( !(target = vips_target_new_to_descriptor( descriptor )) ) {
|
||||
g_free( filename );
|
||||
vips_tracked_close( descriptor );
|
||||
return( NULL );
|
||||
}
|
||||
vips_tracked_close( descriptor );
|
||||
target->delete_on_close = TRUE;
|
||||
target->delete_on_close_filename = filename;
|
||||
}
|
||||
else
|
||||
target = vips_target_new_to_memory();
|
||||
|
||||
return( target );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_target_write_unbuffered( VipsTarget *target,
|
||||
const void *data, size_t length )
|
||||
@ -306,29 +454,26 @@ vips_target_write_unbuffered( VipsTarget *target,
|
||||
if( target->ended )
|
||||
return( 0 );
|
||||
|
||||
if( target->memory_buffer )
|
||||
g_byte_array_append( target->memory_buffer, data, length );
|
||||
else
|
||||
while( length > 0 ) {
|
||||
gint64 bytes_written;
|
||||
while( length > 0 ) {
|
||||
gint64 bytes_written;
|
||||
|
||||
bytes_written = class->write( target, data, length );
|
||||
bytes_written = class->write( target, data, length );
|
||||
|
||||
/* n == 0 isn't strictly an error, but we treat it as
|
||||
* one to make sure we don't get stuck in this loop.
|
||||
*/
|
||||
if( bytes_written <= 0 ) {
|
||||
vips_error_system( errno,
|
||||
vips_connection_nick(
|
||||
VIPS_CONNECTION( target ) ),
|
||||
"%s", _( "write error" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
length -= bytes_written;
|
||||
data += bytes_written;
|
||||
/* n == 0 isn't strictly an error, but we treat it as
|
||||
* one to make sure we don't get stuck in this loop.
|
||||
*/
|
||||
if( bytes_written <= 0 ) {
|
||||
vips_error_system( errno,
|
||||
vips_connection_nick(
|
||||
VIPS_CONNECTION( target ) ),
|
||||
"%s", _( "write error" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
length -= bytes_written;
|
||||
data += bytes_written;
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -384,6 +529,70 @@ vips_target_write( VipsTarget *target, const void *buffer, size_t length )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_target_read:
|
||||
* @target: target to operate on
|
||||
* @buffer: store bytes here
|
||||
* @length: length of @buffer in bytes
|
||||
*
|
||||
* Read up to @length bytes from @target and store the bytes in @buffer.
|
||||
* Return the number of bytes actually read. If all bytes have been read from
|
||||
* the file, return 0.
|
||||
*
|
||||
* Arguments exactly as read(2).
|
||||
*
|
||||
* Reading froma target sounds weird, but libtiff needs this for
|
||||
* multi-page writes. This method will fail for targets like pipes.
|
||||
*
|
||||
* Returns: the number of bytes read, 0 on end of file, -1 on error.
|
||||
*/
|
||||
gint64
|
||||
vips_target_read( VipsTarget *target, void *buffer, size_t length )
|
||||
{
|
||||
VipsTargetClass *class = VIPS_TARGET_GET_CLASS( target );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_read: %zd bytes\n", length );
|
||||
|
||||
if( vips_target_flush( target ) )
|
||||
return( -1 );
|
||||
|
||||
return( class->read( target, buffer, length ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_target_seek:
|
||||
* @target: target to operate on
|
||||
* @position: position to seek to
|
||||
* @whence: seek relative to beginning, offset, or end
|
||||
*
|
||||
* Seek the target. This behaves exactly as lseek(2).
|
||||
*
|
||||
* Seeking a target sounds weird, but libtiff needs this. This method will
|
||||
* fail for targets like pipes.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
off_t
|
||||
vips_target_seek( VipsTarget *target, off_t position, int whence )
|
||||
{
|
||||
VipsTargetClass *class = VIPS_TARGET_GET_CLASS( target );
|
||||
|
||||
off_t new_position;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_seek: pos = %ld, whence = %d\n",
|
||||
position, whence );
|
||||
|
||||
if( vips_target_flush( target ) )
|
||||
return( -1 );
|
||||
|
||||
new_position = class->seek( target, position, whence );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_seek: new_position = %ld\n",
|
||||
new_position );
|
||||
|
||||
return( new_position );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_target_end:
|
||||
* @target: target to operate on
|
||||
@ -413,11 +622,11 @@ vips_target_end( VipsTarget *target )
|
||||
/* Move the target buffer into the blob so it can be read out.
|
||||
*/
|
||||
if( target->memory_buffer ) {
|
||||
unsigned char *data;
|
||||
const char *data;
|
||||
size_t length;
|
||||
|
||||
length = target->memory_buffer->len;
|
||||
data = g_byte_array_free( target->memory_buffer, FALSE );
|
||||
data = g_string_free( target->memory_buffer, FALSE );
|
||||
target->memory_buffer = NULL;
|
||||
vips_blob_set( target->blob,
|
||||
(VipsCallbackFn) vips_area_free_cb, data, length );
|
||||
@ -465,7 +674,7 @@ vips_target_finish( VipsTarget *target )
|
||||
unsigned char *
|
||||
vips_target_steal( VipsTarget *target, size_t *length )
|
||||
{
|
||||
unsigned char *data;
|
||||
const char *data;
|
||||
|
||||
(void) vips_target_flush( target );
|
||||
|
||||
@ -479,17 +688,17 @@ vips_target_steal( VipsTarget *target, size_t *length )
|
||||
|
||||
if( length )
|
||||
*length = target->memory_buffer->len;
|
||||
data = g_byte_array_free( target->memory_buffer, FALSE );
|
||||
data = g_string_free( target->memory_buffer, FALSE );
|
||||
target->memory_buffer = NULL;
|
||||
|
||||
/* We must have a valid byte array or end will fail.
|
||||
*/
|
||||
target->memory_buffer = g_byte_array_new();
|
||||
target->memory_buffer = g_string_sized_new( 0 );
|
||||
|
||||
if( vips_target_end( target ) )
|
||||
return( NULL );
|
||||
|
||||
return( data );
|
||||
return( (unsigned char *) data );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,6 +64,8 @@ G_DEFINE_TYPE( VipsTargetCustom, vips_target_custom, VIPS_TYPE_TARGET );
|
||||
*/
|
||||
enum {
|
||||
SIG_WRITE,
|
||||
SIG_READ,
|
||||
SIG_SEEK,
|
||||
SIG_END,
|
||||
SIG_FINISH,
|
||||
SIG_LAST
|
||||
@ -91,6 +93,68 @@ vips_target_custom_write_real( VipsTarget *target,
|
||||
return( bytes_written );
|
||||
}
|
||||
|
||||
static gint64
|
||||
vips_target_custom_read_real( VipsTarget *target, void *buffer, size_t length )
|
||||
{
|
||||
gint64 bytes_read;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_custom_read_real:\n" );
|
||||
|
||||
/* Return this value (error) if there's no attached handler.
|
||||
*/
|
||||
bytes_read = 0;
|
||||
|
||||
g_signal_emit( target, vips_target_custom_signals[SIG_READ], 0,
|
||||
buffer, (gint64) length, &bytes_read );
|
||||
|
||||
VIPS_DEBUG_MSG( " vips_target_custom_read_real, seen %zd bytes\n",
|
||||
bytes_read );
|
||||
|
||||
return( bytes_read );
|
||||
}
|
||||
|
||||
static gint64
|
||||
vips_target_custom_seek_real( VipsTarget *target, gint64 offset, int whence )
|
||||
{
|
||||
GValue args[3] = { { 0 } };
|
||||
GValue result = { 0 };
|
||||
gint64 new_position;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_custom_seek_real:\n" );
|
||||
|
||||
/* Set the signal args.
|
||||
*/
|
||||
g_value_init( &args[0], G_TYPE_OBJECT );
|
||||
g_value_set_object( &args[0], target );
|
||||
g_value_init( &args[1], G_TYPE_INT64 );
|
||||
g_value_set_int64( &args[1], offset );
|
||||
g_value_init( &args[2], G_TYPE_INT );
|
||||
g_value_set_int( &args[2], whence );
|
||||
|
||||
/* Set the default value if no handlers are attached.
|
||||
*/
|
||||
g_value_init( &result, G_TYPE_INT64 );
|
||||
g_value_set_int64( &result, -1 );
|
||||
|
||||
/* We need to use this signal interface since we want a default value
|
||||
* if no handlers are attached.
|
||||
*/
|
||||
g_signal_emitv( (const GValue *) &args,
|
||||
vips_target_custom_signals[SIG_SEEK], 0, &result );
|
||||
|
||||
new_position = g_value_get_int64( &result );
|
||||
|
||||
g_value_unset( &args[0] );
|
||||
g_value_unset( &args[1] );
|
||||
g_value_unset( &args[2] );
|
||||
g_value_unset( &result );
|
||||
|
||||
VIPS_DEBUG_MSG( " vips_target_custom_seek_real, seen new pos %zd\n",
|
||||
new_position );
|
||||
|
||||
return( new_position );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_target_custom_end_real( VipsTarget *target )
|
||||
{
|
||||
@ -125,6 +189,24 @@ vips_target_custom_write_signal_real( VipsTargetCustom *target_custom,
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gint64
|
||||
vips_target_custom_read_signal_real( VipsTargetCustom *target_custom,
|
||||
void *data, gint64 length )
|
||||
{
|
||||
VIPS_DEBUG_MSG( "vips_target_custom_read_signal_real:\n" );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gint64
|
||||
vips_target_custom_seek_signal_real( VipsTargetCustom *target_custom,
|
||||
gint64 offset, int whence )
|
||||
{
|
||||
VIPS_DEBUG_MSG( "vips_target_custom_seek_signal_real:\n" );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_target_custom_end_signal_real( VipsTargetCustom *target_custom )
|
||||
{
|
||||
@ -149,10 +231,14 @@ vips_target_custom_class_init( VipsTargetCustomClass *class )
|
||||
object_class->description = _( "Custom target" );
|
||||
|
||||
target_class->write = vips_target_custom_write_real;
|
||||
target_class->read = vips_target_custom_read_real;
|
||||
target_class->seek = vips_target_custom_seek_real;
|
||||
target_class->end = vips_target_custom_end_real;
|
||||
target_class->finish = vips_target_custom_finish_real;
|
||||
|
||||
class->write = vips_target_custom_write_signal_real;
|
||||
class->read = vips_target_custom_read_signal_real;
|
||||
class->seek = vips_target_custom_seek_signal_real;
|
||||
class->end = vips_target_custom_end_signal_real;
|
||||
class->finish = vips_target_custom_finish_signal_real;
|
||||
|
||||
@ -175,6 +261,49 @@ vips_target_custom_class_init( VipsTargetCustomClass *class )
|
||||
G_TYPE_INT64, 2,
|
||||
G_TYPE_POINTER, G_TYPE_INT64 );
|
||||
|
||||
/**
|
||||
* VipsTargetCustom::read:
|
||||
* @target_custom: the target being operated on
|
||||
* @buffer: %gpointer, buffer to fill
|
||||
* @size: %gint64, size of buffer
|
||||
*
|
||||
* This signal is emitted to read bytes from the target into @buffer.
|
||||
*
|
||||
* The handler for an unreadable target should always return -1.
|
||||
*
|
||||
* Returns: the number of bytes read. Return 0 for EOF.
|
||||
*/
|
||||
vips_target_custom_signals[SIG_READ] = g_signal_new( "read",
|
||||
G_TYPE_FROM_CLASS( class ),
|
||||
G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET( VipsTargetCustomClass, read ),
|
||||
NULL, NULL,
|
||||
vips_INT64__POINTER_INT64,
|
||||
G_TYPE_INT64, 2,
|
||||
G_TYPE_POINTER, G_TYPE_INT64 );
|
||||
|
||||
/**
|
||||
* VipsTargetCustom::seek:
|
||||
* @target_custom: the target being operated on
|
||||
* @offset: %gint64, seek offset
|
||||
* @whence: %gint, seek origin
|
||||
*
|
||||
* This signal is emitted to seek the target. The handler should
|
||||
* change the target position appropriately.
|
||||
*
|
||||
* The handler for an unseekable target should always return -1.
|
||||
*
|
||||
* Returns: the new seek position.
|
||||
*/
|
||||
vips_target_custom_signals[SIG_SEEK] = g_signal_new( "seek",
|
||||
G_TYPE_FROM_CLASS( class ),
|
||||
G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET( VipsTargetCustomClass, seek ),
|
||||
NULL, NULL,
|
||||
vips_INT64__INT64_INT,
|
||||
G_TYPE_INT64, 2,
|
||||
G_TYPE_INT64, G_TYPE_INT );
|
||||
|
||||
/**
|
||||
* VipsTargetCustom::end:
|
||||
* @target_custom: the target being operated on
|
||||
@ -227,8 +356,8 @@ vips_target_custom_new( void )
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_target_custom_new:\n" );
|
||||
|
||||
target_custom = VIPS_TARGET_CUSTOM( g_object_new(
|
||||
VIPS_TYPE_TARGET_CUSTOM, NULL ) );
|
||||
target_custom = VIPS_TARGET_CUSTOM(
|
||||
g_object_new( VIPS_TYPE_TARGET_CUSTOM, NULL ) );
|
||||
|
||||
if( vips_object_build( VIPS_OBJECT( target_custom ) ) ) {
|
||||
VIPS_UNREF( target_custom );
|
||||
|
Loading…
x
Reference in New Issue
Block a user