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
@ -21,6 +21,9 @@
|
|||||||
- add "gap" option to vips_reduce[hv]() and vips_resize() [kleisauke]
|
- add "gap" option to vips_reduce[hv]() and vips_resize() [kleisauke]
|
||||||
- add "ceil" option to vips_shrink() [kleisauke]
|
- add "ceil" option to vips_shrink() [kleisauke]
|
||||||
- quality improvements for image resizing [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()
|
- add vips_target_end(), deprecate vips_target_finish()
|
||||||
|
|
||||||
26/11/21 started 8.12.3
|
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_load_tiff_source_get_type( void );
|
||||||
extern GType vips_foreign_save_tiff_file_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_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_load_raw_get_type( void );
|
||||||
extern GType vips_foreign_save_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_load_tiff_source_get_type();
|
||||||
vips_foreign_save_tiff_file_get_type();
|
vips_foreign_save_tiff_file_get_type();
|
||||||
vips_foreign_save_tiff_buffer_get_type();
|
vips_foreign_save_tiff_buffer_get_type();
|
||||||
|
vips_foreign_save_tiff_target_get_type();
|
||||||
#endif /*HAVE_TIFF*/
|
#endif /*HAVE_TIFF*/
|
||||||
|
|
||||||
#if defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE)
|
#if defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE)
|
||||||
|
@ -51,7 +51,7 @@ extern "C" {
|
|||||||
|
|
||||||
void vips__tiff_init( void );
|
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,
|
VipsForeignTiffCompression compression, int Q,
|
||||||
VipsForeignTiffPredictor predictor,
|
VipsForeignTiffPredictor predictor,
|
||||||
const char *profile,
|
const char *profile,
|
||||||
@ -72,27 +72,6 @@ int vips__tiff_write( VipsImage *in, const char *filename,
|
|||||||
gboolean premultiply,
|
gboolean premultiply,
|
||||||
int page_height );
|
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__istiff_source( VipsSource *source );
|
||||||
gboolean vips__istifftiled_source( VipsSource *source );
|
gboolean vips__istifftiled_source( VipsSource *source );
|
||||||
int vips__tiff_read_header_source( VipsSource *source, VipsImage *out,
|
int vips__tiff_read_header_source( VipsSource *source, VipsImage *out,
|
||||||
|
@ -90,48 +90,6 @@ vips__tiff_init( void )
|
|||||||
TIFFSetWarningHandler( vips__thandler_warning );
|
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.
|
/* 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
|
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 );
|
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, offset, whence ) );
|
||||||
*/
|
|
||||||
return( (toff_t) vips_source_seek( source, position, whence ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -240,146 +196,95 @@ vips__tiff_openin_source( VipsSource *source )
|
|||||||
return( tiff );
|
return( tiff );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TIFF output to a memory buffer.
|
/* TIFF output to a target.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct _VipsTiffOpenoutBuffer {
|
/* libtiff needs this (!!?!?!) for writing multipage images.
|
||||||
VipsDbuf dbuf;
|
*/
|
||||||
|
|
||||||
/* On close, consolidate and write the output here.
|
|
||||||
*/
|
|
||||||
void **out_data;
|
|
||||||
size_t *out_length;
|
|
||||||
} VipsTiffOpenoutBuffer;
|
|
||||||
|
|
||||||
static tsize_t
|
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
|
return( vips_target_read( target, data, size ) );
|
||||||
printf( "openout_buffer_read: %zd bytes\n", size );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
return( vips_dbuf_read( &buffer->dbuf, data, size ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static tsize_t
|
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
|
if( vips_target_write( target, data, size ) )
|
||||||
printf( "openout_buffer_write: %zd bytes\n", size );
|
return( (tsize_t) -1 );
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
vips_dbuf_write( &buffer->dbuf, data, size );
|
|
||||||
|
|
||||||
return( size );
|
return( size );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static toff_t
|
||||||
openout_buffer_close( thandle_t st )
|
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,
|
return( vips_target_seek( target, offset, whence ) );
|
||||||
buffer->out_length);
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
openout_target_close( thandle_t st )
|
||||||
|
{
|
||||||
|
VipsTarget *target = (VipsTarget *) st;
|
||||||
|
|
||||||
|
if( vips_target_end( target ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static toff_t
|
static toff_t
|
||||||
openout_buffer_seek( thandle_t st, toff_t position, int whence )
|
openout_target_length( thandle_t st )
|
||||||
{
|
|
||||||
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 )
|
|
||||||
{
|
{
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
|
|
||||||
return( 0 );
|
return( (toff_t) -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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();
|
g_assert_not_reached();
|
||||||
|
|
||||||
return( 0 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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();
|
g_assert_not_reached();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* On TIFFClose(), @data and @length are set to point to the output buffer.
|
|
||||||
*/
|
|
||||||
TIFF *
|
TIFF *
|
||||||
vips__tiff_openout_buffer( VipsImage *image,
|
vips__tiff_openout_target( VipsTarget *target, gboolean bigtiff )
|
||||||
gboolean bigtiff, void **out_data, size_t *out_length )
|
|
||||||
{
|
{
|
||||||
const char *mode = bigtiff ? "w8" : "w";
|
const char *mode = bigtiff ? "w8" : "w";
|
||||||
|
|
||||||
VipsTiffOpenoutBuffer *buffer;
|
|
||||||
TIFF *tiff;
|
TIFF *tiff;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf( "vips__tiff_openout_buffer:\n" );
|
printf( "vips__tiff_openout_buffer:\n" );
|
||||||
#endif /*DEBUG*/
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
g_assert( out_data );
|
if( !(tiff = TIFFClientOpen( "target output", mode,
|
||||||
g_assert( out_length );
|
(thandle_t) target,
|
||||||
|
openout_target_read,
|
||||||
buffer = VIPS_NEW( image, VipsTiffOpenoutBuffer );
|
openout_target_write,
|
||||||
vips_dbuf_init( &buffer->dbuf );
|
openout_target_seek,
|
||||||
buffer->out_data = out_data;
|
openout_target_close,
|
||||||
buffer->out_length = out_length;
|
openout_target_length,
|
||||||
|
openout_target_map,
|
||||||
if( !(tiff = TIFFClientOpen( "memory output", mode,
|
openout_target_unmap )) ) {
|
||||||
(thandle_t) buffer,
|
vips_error( "vips__tiff_openout_target", "%s",
|
||||||
openout_buffer_read,
|
_( "unable to open target for output" ) );
|
||||||
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" ) );
|
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,4 +292,3 @@ vips__tiff_openout_buffer( VipsImage *image,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif /*HAVE_TIFF*/
|
#endif /*HAVE_TIFF*/
|
||||||
|
|
||||||
|
@ -40,8 +40,7 @@ extern "C" {
|
|||||||
TIFF *vips__tiff_openin_source( VipsSource *source );
|
TIFF *vips__tiff_openin_source( VipsSource *source );
|
||||||
|
|
||||||
TIFF *vips__tiff_openout( const char *path, gboolean bigtiff );
|
TIFF *vips__tiff_openout( const char *path, gboolean bigtiff );
|
||||||
TIFF *vips__tiff_openout_buffer( VipsImage *image,
|
TIFF *vips__tiff_openout_target( VipsTarget *target, gboolean bigtiff );
|
||||||
gboolean bigtiff, void **out_data, size_t *out_length );
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
* - deprecate "squash"
|
* - deprecate "squash"
|
||||||
* 1/5/21
|
* 1/5/21
|
||||||
* - add "premultiply" flag
|
* - add "premultiply" flag
|
||||||
|
* 10/5/22
|
||||||
|
* - add vips_tiffsave_target()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -83,6 +85,10 @@
|
|||||||
typedef struct _VipsForeignSaveTiff {
|
typedef struct _VipsForeignSaveTiff {
|
||||||
VipsForeignSave parent_object;
|
VipsForeignSave parent_object;
|
||||||
|
|
||||||
|
/* Set by subclasses.
|
||||||
|
*/
|
||||||
|
VipsTarget *target;
|
||||||
|
|
||||||
/* Many options argh.
|
/* Many options argh.
|
||||||
*/
|
*/
|
||||||
VipsForeignTiffCompression compression;
|
VipsForeignTiffCompression compression;
|
||||||
@ -116,6 +122,17 @@ typedef VipsForeignSaveClass VipsForeignSaveTiffClass;
|
|||||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveTiff, vips_foreign_save_tiff,
|
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveTiff, vips_foreign_save_tiff,
|
||||||
VIPS_TYPE_FOREIGN_SAVE );
|
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
|
#define UC VIPS_FORMAT_UCHAR
|
||||||
|
|
||||||
/* Type promotion for jpeg-in-tiff save ... just always go to 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;
|
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 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,11 +245,12 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class )
|
|||||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||||
|
|
||||||
|
gobject_class->dispose = vips_foreign_save_tiff_dispose;
|
||||||
gobject_class->set_property = vips_object_set_property;
|
gobject_class->set_property = vips_object_set_property;
|
||||||
gobject_class->get_property = vips_object_get_property;
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
object_class->nickname = "tiffsave_base";
|
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;
|
object_class->build = vips_foreign_save_tiff_build;
|
||||||
|
|
||||||
foreign_class->suffs = vips__foreign_tiff_suffs;
|
foreign_class->suffs = vips__foreign_tiff_suffs;
|
||||||
@ -392,6 +441,61 @@ vips_foreign_save_tiff_init( VipsForeignSaveTiff *tiff )
|
|||||||
tiff->bitdepth = 0;
|
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 {
|
typedef struct _VipsForeignSaveTiffFile {
|
||||||
VipsForeignSaveTiff parent_object;
|
VipsForeignSaveTiff parent_object;
|
||||||
|
|
||||||
@ -406,40 +510,14 @@ G_DEFINE_TYPE( VipsForeignSaveTiffFile, vips_foreign_save_tiff_file,
|
|||||||
static int
|
static int
|
||||||
vips_foreign_save_tiff_file_build( VipsObject *object )
|
vips_foreign_save_tiff_file_build( VipsObject *object )
|
||||||
{
|
{
|
||||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
|
||||||
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
|
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
|
||||||
VipsForeignSaveTiffFile *file = (VipsForeignSaveTiffFile *) object;
|
VipsForeignSaveTiffFile *file = (VipsForeignSaveTiffFile *) object;
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_file_parent_class )->
|
if( !(tiff->target = vips_target_new_to_file( file->filename )) )
|
||||||
build( object ) )
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* Handle the deprecated squash parameter.
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_file_parent_class )->
|
||||||
*/
|
build( object ) )
|
||||||
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 ) )
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
@ -485,40 +563,21 @@ G_DEFINE_TYPE( VipsForeignSaveTiffBuffer, vips_foreign_save_tiff_buffer,
|
|||||||
static int
|
static int
|
||||||
vips_foreign_save_tiff_buffer_build( VipsObject *object )
|
vips_foreign_save_tiff_buffer_build( VipsObject *object )
|
||||||
{
|
{
|
||||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
|
||||||
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
|
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
|
||||||
|
VipsForeignSaveTiffBuffer *buffer =
|
||||||
|
(VipsForeignSaveTiffBuffer *) object;
|
||||||
|
|
||||||
void *obuf;
|
|
||||||
size_t olen;
|
|
||||||
VipsBlob *blob;
|
VipsBlob *blob;
|
||||||
|
|
||||||
|
if( !(tiff->target = vips_target_new_to_memory()) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_buffer_parent_class )->
|
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_buffer_parent_class )->
|
||||||
build( object ) )
|
build( object ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
if( vips__tiff_write_buf( save->ready, &obuf, &olen,
|
g_object_get( tiff->target, "blob", &blob, NULL );
|
||||||
tiff->compression, tiff->Q, tiff->predictor,
|
g_object_set( buffer, "buffer", blob, NULL );
|
||||||
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 );
|
|
||||||
vips_area_unref( VIPS_AREA( blob ) );
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
return( 0 );
|
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.
|
* resolutions. By default these values are taken from the VIPS image header.
|
||||||
* libvips resolution is always in pixels per millimetre.
|
* libvips resolution is always in pixels per millimetre.
|
||||||
*
|
*
|
||||||
* Set @bigtiff to attempt to write a bigtiff.
|
* Set @bigtiff to attempt to write a bigtiff. Bigtiff is a variant of the TIFF
|
||||||
* Bigtiff is a variant of the TIFF
|
|
||||||
* format that allows more than 4GB in a file.
|
* format that allows more than 4GB in a file.
|
||||||
*
|
*
|
||||||
* Set @properties to write all vips metadata to the IMAGEDESCRIPTION tag as
|
* 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.
|
* Set @subifd to save pyramid layers as sub-directories of the main image.
|
||||||
* Setting this option can improve compatibility with formats like OME.
|
* 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.
|
* InDesign, will only work with premultiplied alpha.
|
||||||
*
|
*
|
||||||
* See also: vips_tiffload(), vips_image_write_to_file().
|
* 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 );
|
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]
|
* 20/10/21 [jacopoabramo]
|
||||||
* - subifd enables pyramid
|
* - subifd enables pyramid
|
||||||
* - add support for page_height param
|
* - 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 {
|
struct _Layer {
|
||||||
Wtiff *wtiff; /* Main wtiff struct */
|
Wtiff *wtiff; /* Main wtiff struct */
|
||||||
|
|
||||||
/* The filename for this layer, for file output.
|
/* The temp target for this layer.
|
||||||
*/
|
*/
|
||||||
char *lname;
|
VipsTarget *target;
|
||||||
|
|
||||||
/* The memory area for this layer, for memory output.
|
|
||||||
*/
|
|
||||||
void *buf;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
int width, height; /* Layer size */
|
int width, height; /* Layer size */
|
||||||
int sub; /* Subsample factor for this layer */
|
int sub; /* Subsample factor for this layer */
|
||||||
@ -335,14 +332,9 @@ struct _Wtiff {
|
|||||||
*/
|
*/
|
||||||
VipsImage *ready;
|
VipsImage *ready;
|
||||||
|
|
||||||
/* File to write to, or NULL.
|
/* Target to write to.
|
||||||
*/
|
*/
|
||||||
char *filename; /* Name we write to */
|
VipsTarget *target;
|
||||||
|
|
||||||
/* Memory area to output, or NULL.
|
|
||||||
*/
|
|
||||||
void **obuf;
|
|
||||||
size_t *olen;
|
|
||||||
|
|
||||||
Layer *layer; /* Top of pyramid */
|
Layer *layer; /* Top of pyramid */
|
||||||
VipsPel *tbuf; /* TIFF output buffer */
|
VipsPel *tbuf; /* TIFF output buffer */
|
||||||
@ -453,9 +445,6 @@ wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
|
|||||||
else
|
else
|
||||||
(*layer)->sub = above->sub * 2;
|
(*layer)->sub = above->sub * 2;
|
||||||
|
|
||||||
(*layer)->lname = NULL;
|
|
||||||
(*layer)->buf = NULL;
|
|
||||||
(*layer)->len = 0;
|
|
||||||
(*layer)->tif = NULL;
|
(*layer)->tif = NULL;
|
||||||
(*layer)->image = NULL;
|
(*layer)->image = NULL;
|
||||||
(*layer)->write_y = 0;
|
(*layer)->write_y = 0;
|
||||||
@ -466,26 +455,16 @@ wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
|
|||||||
(*layer)->below = NULL;
|
(*layer)->below = NULL;
|
||||||
(*layer)->above = above;
|
(*layer)->above = above;
|
||||||
|
|
||||||
/* The name for the top layer is the output filename.
|
/* The target we write to. The base layer writes to the main
|
||||||
*
|
* output, each layer smaller writes to a memory temp.
|
||||||
* We need lname to be freed automatically: it has to stay
|
|
||||||
* alive until after wtiff_gather().
|
|
||||||
*/
|
*/
|
||||||
if( wtiff->filename ) {
|
if( !above ) {
|
||||||
if( !above )
|
(*layer)->target = wtiff->target;
|
||||||
(*layer)->lname = vips_strdup(
|
g_object_ref( (*layer)->target );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
(*layer)->target =
|
||||||
|
vips_target_new_temp( wtiff->target );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
printf( "wtiff_layer_init: sub = %d, width = %d, height = %d\n",
|
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->strip );
|
||||||
vips__region_no_ownership( layer->copy );
|
vips__region_no_ownership( layer->copy );
|
||||||
|
|
||||||
if( layer->lname )
|
layer->tif = vips__tiff_openout_target( layer->target,
|
||||||
layer->tif = vips__tiff_openout(
|
wtiff->bigtiff );
|
||||||
layer->lname, wtiff->bigtiff );
|
|
||||||
else {
|
|
||||||
layer->tif = vips__tiff_openout_buffer(
|
|
||||||
wtiff->ready, wtiff->bigtiff,
|
|
||||||
&layer->buf, &layer->len );
|
|
||||||
}
|
|
||||||
if( !layer->tif )
|
if( !layer->tif )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
@ -962,42 +935,17 @@ wtiff_allocate_layers( Wtiff *wtiff )
|
|||||||
return( 0 );
|
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.
|
/* Free a single pyramid layer.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
layer_free( Layer *layer )
|
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->strip );
|
||||||
VIPS_UNREF( layer->copy );
|
VIPS_UNREF( layer->copy );
|
||||||
VIPS_UNREF( layer->image );
|
VIPS_UNREF( layer->image );
|
||||||
VIPS_FREE( layer->buf );
|
|
||||||
VIPS_FREEF( TIFFClose, layer->tif );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free an entire pyramid.
|
/* Free an entire pyramid.
|
||||||
@ -1014,11 +962,16 @@ layer_free_all( Layer *layer )
|
|||||||
static void
|
static void
|
||||||
wtiff_free( Wtiff *wtiff )
|
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_UNREF( wtiff->ready );
|
||||||
VIPS_FREE( wtiff->tbuf );
|
VIPS_FREE( wtiff->tbuf );
|
||||||
VIPS_FREEF( layer_free_all, wtiff->layer );
|
VIPS_FREEF( layer_free_all, wtiff->layer );
|
||||||
VIPS_FREE( wtiff->filename );
|
|
||||||
VIPS_FREE( wtiff );
|
VIPS_FREE( wtiff );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1129,7 +1082,7 @@ ready_to_write( Wtiff *wtiff )
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Wtiff *
|
static Wtiff *
|
||||||
wtiff_new( VipsImage *input, const char *filename,
|
wtiff_new( VipsImage *input, VipsTarget *target,
|
||||||
VipsForeignTiffCompression compression, int Q,
|
VipsForeignTiffCompression compression, int Q,
|
||||||
VipsForeignTiffPredictor predictor,
|
VipsForeignTiffPredictor predictor,
|
||||||
const char *profile,
|
const char *profile,
|
||||||
@ -1156,7 +1109,7 @@ wtiff_new( VipsImage *input, const char *filename,
|
|||||||
return( NULL );
|
return( NULL );
|
||||||
wtiff->input = input;
|
wtiff->input = input;
|
||||||
wtiff->ready = NULL;
|
wtiff->ready = NULL;
|
||||||
wtiff->filename = filename ? vips_strdup( NULL, filename ) : NULL;
|
wtiff->target = target;
|
||||||
wtiff->layer = NULL;
|
wtiff->layer = NULL;
|
||||||
wtiff->tbuf = NULL;
|
wtiff->tbuf = NULL;
|
||||||
wtiff->compression = get_compression( compression );
|
wtiff->compression = get_compression( compression );
|
||||||
@ -2110,19 +2063,12 @@ wtiff_gather( Wtiff *wtiff )
|
|||||||
TIFF *in;
|
TIFF *in;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf( "appending layer %s ...\n", layer->lname );
|
printf( "appending layer sub = %d ...\n", layer->sub );
|
||||||
#endif /*DEBUG*/
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
if( layer->lname ) {
|
if( !(source =
|
||||||
if( !(source = vips_source_new_from_file(
|
vips_source_new_from_target( layer->target )) )
|
||||||
layer->lname )) )
|
return( -1 );
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if( !(source = vips_source_new_from_memory(
|
|
||||||
layer->buf, layer->len )) )
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !(in = vips__tiff_openin_source( source )) ) {
|
if( !(in = vips__tiff_openin_source( source )) ) {
|
||||||
VIPS_UNREF( source );
|
VIPS_UNREF( source );
|
||||||
@ -2204,6 +2150,8 @@ wtiff_page_end( Wtiff *wtiff )
|
|||||||
/* Append any pyr layers, if necessary.
|
/* Append any pyr layers, if necessary.
|
||||||
*/
|
*/
|
||||||
if( wtiff->layer->below ) {
|
if( wtiff->layer->below ) {
|
||||||
|
Layer *layer;
|
||||||
|
|
||||||
/* Free any lower pyramid resources ... this will
|
/* Free any lower pyramid resources ... this will
|
||||||
* TIFFClose() (but not delete) the smaller layers
|
* TIFFClose() (but not delete) the smaller layers
|
||||||
* ready for us to read from them again.
|
* ready for us to read from them again.
|
||||||
@ -2215,14 +2163,14 @@ wtiff_page_end( Wtiff *wtiff )
|
|||||||
if( wtiff_gather( wtiff ) )
|
if( wtiff_gather( wtiff ) )
|
||||||
return( -1 );
|
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
|
/* ... ready for the next page.
|
||||||
* next page.
|
|
||||||
*/
|
*/
|
||||||
VIPS_FREEF( layer_free_all, wtiff->layer->below );
|
wtiff->layer->below = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
wtiff->page_number += 1;
|
wtiff->page_number += 1;
|
||||||
@ -2294,55 +2242,7 @@ wtiff_sink_disc_strip( VipsRegion *region, VipsRect *area, void *a )
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vips__tiff_write( VipsImage *input, const char *filename,
|
vips__tiff_write_target( VipsImage *input, VipsTarget *target,
|
||||||
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,
|
|
||||||
VipsForeignTiffCompression compression, int Q,
|
VipsForeignTiffCompression compression, int Q,
|
||||||
VipsForeignTiffPredictor predictor,
|
VipsForeignTiffPredictor predictor,
|
||||||
const char *profile,
|
const char *profile,
|
||||||
@ -2366,7 +2266,7 @@ vips__tiff_write_buf( VipsImage *input,
|
|||||||
|
|
||||||
vips__tiff_init();
|
vips__tiff_init();
|
||||||
|
|
||||||
if( !(wtiff = wtiff_new( input, NULL,
|
if( !(wtiff = wtiff_new( input, target,
|
||||||
compression, Q, predictor, profile,
|
compression, Q, predictor, profile,
|
||||||
tile, tile_width, tile_height, pyramid, bitdepth,
|
tile, tile_width, tile_height, pyramid, bitdepth,
|
||||||
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
|
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
|
||||||
@ -2374,26 +2274,11 @@ vips__tiff_write_buf( VipsImage *input,
|
|||||||
subifd, premultiply, page_height )) )
|
subifd, premultiply, page_height )) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
wtiff->obuf = obuf;
|
|
||||||
wtiff->olen = olen;
|
|
||||||
|
|
||||||
if( vips_sink_disc( wtiff->ready, wtiff_sink_disc_strip, wtiff ) ) {
|
if( vips_sink_disc( wtiff->ready, wtiff_sink_disc_strip, wtiff ) ) {
|
||||||
wtiff_free( wtiff );
|
wtiff_free( wtiff );
|
||||||
return( -1 );
|
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 );
|
wtiff_free( wtiff );
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
@ -225,6 +225,8 @@ VipsSource *vips_source_new_from_file( const char *filename );
|
|||||||
VIPS_API
|
VIPS_API
|
||||||
VipsSource *vips_source_new_from_blob( VipsBlob *blob );
|
VipsSource *vips_source_new_from_blob( VipsBlob *blob );
|
||||||
VIPS_API
|
VIPS_API
|
||||||
|
VipsSource *vips_source_new_from_target( VipsTarget *target );
|
||||||
|
VIPS_API
|
||||||
VipsSource *vips_source_new_from_memory( const void *data, size_t size );
|
VipsSource *vips_source_new_from_memory( const void *data, size_t size );
|
||||||
VIPS_API
|
VIPS_API
|
||||||
VipsSource *vips_source_new_from_options( const char *options );
|
VipsSource *vips_source_new_from_options( const char *options );
|
||||||
@ -410,9 +412,11 @@ struct _VipsTarget {
|
|||||||
*/
|
*/
|
||||||
gboolean ended;
|
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.
|
/* And return memory via this blob.
|
||||||
*/
|
*/
|
||||||
@ -424,6 +428,11 @@ struct _VipsTarget {
|
|||||||
unsigned char output_buffer[VIPS_TARGET_BUFFER_SIZE];
|
unsigned char output_buffer[VIPS_TARGET_BUFFER_SIZE];
|
||||||
int write_point;
|
int write_point;
|
||||||
|
|
||||||
|
/* Temp targets on the filesystem need deleting, sometimes.
|
||||||
|
*/
|
||||||
|
gboolean delete_on_close;
|
||||||
|
char *delete_on_close_filename;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _VipsTargetClass {
|
typedef struct _VipsTargetClass {
|
||||||
@ -436,6 +445,24 @@ typedef struct _VipsTargetClass {
|
|||||||
*/
|
*/
|
||||||
gint64 (*write)( VipsTarget *, const void *, size_t );
|
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,
|
/* Output has been generated, so do any clearing up,
|
||||||
* eg. copy the bytes we saved in memory to the target blob.
|
* 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
|
VIPS_API
|
||||||
VipsTarget *vips_target_new_to_memory( void );
|
VipsTarget *vips_target_new_to_memory( void );
|
||||||
VIPS_API
|
VIPS_API
|
||||||
|
VipsTarget *vips_target_new_temp( VipsTarget *target );
|
||||||
|
VIPS_API
|
||||||
int vips_target_write( VipsTarget *target, const void *data, size_t length );
|
int vips_target_write( VipsTarget *target, const void *data, size_t length );
|
||||||
VIPS_API
|
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 );
|
int vips_target_end( VipsTarget *target );
|
||||||
VIPS_DEPRECATED_FOR(vips_target_end)
|
VIPS_DEPRECATED_FOR(vips_target_end)
|
||||||
void vips_target_finish( VipsTarget *target );
|
void vips_target_finish( VipsTarget *target );
|
||||||
@ -514,6 +547,8 @@ typedef struct _VipsTargetCustomClass {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
gint64 (*write)( VipsTargetCustom *, const void *, gint64 );
|
gint64 (*write)( VipsTargetCustom *, const void *, gint64 );
|
||||||
|
gint64 (*read)( VipsTargetCustom *, void *, gint64 );
|
||||||
|
gint64 (*seek)( VipsTargetCustom *, gint64, int );
|
||||||
int (*end)( VipsTargetCustom * );
|
int (*end)( VipsTargetCustom * );
|
||||||
void (*finish)( VipsTargetCustom * );
|
void (*finish)( VipsTargetCustom * );
|
||||||
|
|
||||||
|
@ -597,6 +597,9 @@ int vips_tiffsave( VipsImage *in, const char *filename, ... )
|
|||||||
VIPS_API
|
VIPS_API
|
||||||
int vips_tiffsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
int vips_tiffsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||||
__attribute__((sentinel));
|
__attribute__((sentinel));
|
||||||
|
VIPS_API
|
||||||
|
int vips_tiffsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||||
|
__attribute__((sentinel));
|
||||||
|
|
||||||
VIPS_API
|
VIPS_API
|
||||||
int vips_openexrload( const char *filename, VipsImage **out, ... )
|
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 );
|
_( "no vips argument named `%s'" ), name );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !(*argument_instance = vips__argument_get_instance(
|
if( !(*argument_instance = vips__argument_get_instance(
|
||||||
*argument_class, object )) ) {
|
*argument_class, object )) ) {
|
||||||
vips_error( class->nickname,
|
vips_error( class->nickname,
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
* descriptors
|
* descriptors
|
||||||
* 8/10/21
|
* 8/10/21
|
||||||
* - fix named pipes
|
* - 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 );
|
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:
|
* vips_source_new_from_memory:
|
||||||
* @data: memory area to load
|
* @data: memory area to load
|
||||||
|
@ -65,7 +65,10 @@
|
|||||||
#include <vips/debug.h>
|
#include <vips/debug.h>
|
||||||
#include <vips/internal.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 );
|
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_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 ) {
|
if( target->blob ) {
|
||||||
vips_area_unref( VIPS_AREA( target->blob ) );
|
vips_area_unref( VIPS_AREA( target->blob ) );
|
||||||
target->blob = NULL;
|
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 );
|
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.
|
/* 0644 is rw user, r group and other.
|
||||||
*/
|
*/
|
||||||
if( (fd = vips_tracked_open( filename,
|
if( (fd = vips_tracked_open( filename,
|
||||||
MODE_WRITE, 0644 )) == -1 ) {
|
MODE_READWRITE, 0644 )) == -1 ) {
|
||||||
vips_error_system( errno,
|
vips_error_system( errno,
|
||||||
vips_connection_nick( connection ),
|
vips_connection_nick( connection ),
|
||||||
"%s", _( "unable to open for write" ) );
|
"%s", _( "unable to open for write" ) );
|
||||||
@ -133,7 +146,8 @@ vips_target_build( VipsObject *object )
|
|||||||
#endif /*G_OS_WIN32*/
|
#endif /*G_OS_WIN32*/
|
||||||
}
|
}
|
||||||
else if( target->memory )
|
else if( target->memory )
|
||||||
target->memory_buffer = g_byte_array_new();
|
target->memory_buffer =
|
||||||
|
g_string_sized_new( VIPS_TARGET_BUFFER_SIZE );
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
@ -143,9 +157,97 @@ vips_target_write_real( VipsTarget *target, const void *data, size_t length )
|
|||||||
{
|
{
|
||||||
VipsConnection *connection = VIPS_CONNECTION( target );
|
VipsConnection *connection = VIPS_CONNECTION( target );
|
||||||
|
|
||||||
|
gint64 result;
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_target_write_real: %zd bytes\n", length );
|
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
|
static int
|
||||||
@ -178,6 +280,8 @@ vips_target_class_init( VipsTargetClass *class )
|
|||||||
object_class->build = vips_target_build;
|
object_class->build = vips_target_build;
|
||||||
|
|
||||||
class->write = vips_target_write_real;
|
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->end = vips_target_end_real;
|
||||||
class->finish = vips_target_finish_real;
|
class->finish = vips_target_finish_real;
|
||||||
|
|
||||||
@ -295,6 +399,50 @@ vips_target_new_to_memory( void )
|
|||||||
return( target );
|
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
|
static int
|
||||||
vips_target_write_unbuffered( VipsTarget *target,
|
vips_target_write_unbuffered( VipsTarget *target,
|
||||||
const void *data, size_t length )
|
const void *data, size_t length )
|
||||||
@ -306,29 +454,26 @@ vips_target_write_unbuffered( VipsTarget *target,
|
|||||||
if( target->ended )
|
if( target->ended )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
if( target->memory_buffer )
|
while( length > 0 ) {
|
||||||
g_byte_array_append( target->memory_buffer, data, length );
|
gint64 bytes_written;
|
||||||
else
|
|
||||||
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
|
/* n == 0 isn't strictly an error, but we treat it as
|
||||||
* one to make sure we don't get stuck in this loop.
|
* one to make sure we don't get stuck in this loop.
|
||||||
*/
|
*/
|
||||||
if( bytes_written <= 0 ) {
|
if( bytes_written <= 0 ) {
|
||||||
vips_error_system( errno,
|
vips_error_system( errno,
|
||||||
vips_connection_nick(
|
vips_connection_nick(
|
||||||
VIPS_CONNECTION( target ) ),
|
VIPS_CONNECTION( target ) ),
|
||||||
"%s", _( "write error" ) );
|
"%s", _( "write error" ) );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
|
||||||
|
|
||||||
length -= bytes_written;
|
|
||||||
data += bytes_written;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
length -= bytes_written;
|
||||||
|
data += bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,6 +529,70 @@ vips_target_write( VipsTarget *target, const void *buffer, size_t length )
|
|||||||
return( 0 );
|
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:
|
* vips_target_end:
|
||||||
* @target: target to operate on
|
* @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.
|
/* Move the target buffer into the blob so it can be read out.
|
||||||
*/
|
*/
|
||||||
if( target->memory_buffer ) {
|
if( target->memory_buffer ) {
|
||||||
unsigned char *data;
|
const char *data;
|
||||||
size_t length;
|
size_t length;
|
||||||
|
|
||||||
length = target->memory_buffer->len;
|
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;
|
target->memory_buffer = NULL;
|
||||||
vips_blob_set( target->blob,
|
vips_blob_set( target->blob,
|
||||||
(VipsCallbackFn) vips_area_free_cb, data, length );
|
(VipsCallbackFn) vips_area_free_cb, data, length );
|
||||||
@ -465,7 +674,7 @@ vips_target_finish( VipsTarget *target )
|
|||||||
unsigned char *
|
unsigned char *
|
||||||
vips_target_steal( VipsTarget *target, size_t *length )
|
vips_target_steal( VipsTarget *target, size_t *length )
|
||||||
{
|
{
|
||||||
unsigned char *data;
|
const char *data;
|
||||||
|
|
||||||
(void) vips_target_flush( target );
|
(void) vips_target_flush( target );
|
||||||
|
|
||||||
@ -479,17 +688,17 @@ vips_target_steal( VipsTarget *target, size_t *length )
|
|||||||
|
|
||||||
if( length )
|
if( length )
|
||||||
*length = target->memory_buffer->len;
|
*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;
|
target->memory_buffer = NULL;
|
||||||
|
|
||||||
/* We must have a valid byte array or end will fail.
|
/* 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 ) )
|
if( vips_target_end( target ) )
|
||||||
return( NULL );
|
return( NULL );
|
||||||
|
|
||||||
return( data );
|
return( (unsigned char *) data );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,6 +64,8 @@ G_DEFINE_TYPE( VipsTargetCustom, vips_target_custom, VIPS_TYPE_TARGET );
|
|||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
SIG_WRITE,
|
SIG_WRITE,
|
||||||
|
SIG_READ,
|
||||||
|
SIG_SEEK,
|
||||||
SIG_END,
|
SIG_END,
|
||||||
SIG_FINISH,
|
SIG_FINISH,
|
||||||
SIG_LAST
|
SIG_LAST
|
||||||
@ -91,6 +93,68 @@ vips_target_custom_write_real( VipsTarget *target,
|
|||||||
return( bytes_written );
|
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
|
static int
|
||||||
vips_target_custom_end_real( VipsTarget *target )
|
vips_target_custom_end_real( VipsTarget *target )
|
||||||
{
|
{
|
||||||
@ -125,6 +189,24 @@ vips_target_custom_write_signal_real( VipsTargetCustom *target_custom,
|
|||||||
return( 0 );
|
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
|
static int
|
||||||
vips_target_custom_end_signal_real( VipsTargetCustom *target_custom )
|
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" );
|
object_class->description = _( "Custom target" );
|
||||||
|
|
||||||
target_class->write = vips_target_custom_write_real;
|
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->end = vips_target_custom_end_real;
|
||||||
target_class->finish = vips_target_custom_finish_real;
|
target_class->finish = vips_target_custom_finish_real;
|
||||||
|
|
||||||
class->write = vips_target_custom_write_signal_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->end = vips_target_custom_end_signal_real;
|
||||||
class->finish = vips_target_custom_finish_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_INT64, 2,
|
||||||
G_TYPE_POINTER, G_TYPE_INT64 );
|
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:
|
* VipsTargetCustom::end:
|
||||||
* @target_custom: the target being operated on
|
* @target_custom: the target being operated on
|
||||||
@ -227,8 +356,8 @@ vips_target_custom_new( void )
|
|||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_target_custom_new:\n" );
|
VIPS_DEBUG_MSG( "vips_target_custom_new:\n" );
|
||||||
|
|
||||||
target_custom = VIPS_TARGET_CUSTOM( g_object_new(
|
target_custom = VIPS_TARGET_CUSTOM(
|
||||||
VIPS_TYPE_TARGET_CUSTOM, NULL ) );
|
g_object_new( VIPS_TYPE_TARGET_CUSTOM, NULL ) );
|
||||||
|
|
||||||
if( vips_object_build( VIPS_OBJECT( target_custom ) ) ) {
|
if( vips_object_build( VIPS_OBJECT( target_custom ) ) ) {
|
||||||
VIPS_UNREF( target_custom );
|
VIPS_UNREF( target_custom );
|
||||||
|
Loading…
x
Reference in New Issue
Block a user