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:
John Cupitt 2022-05-20 18:38:17 +01:00 committed by GitHub
parent 58b53506ff
commit f8003bda67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 711 additions and 411 deletions

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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*/

View File

@ -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
} }

View File

@ -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 );
}

View File

@ -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 );

View File

@ -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 * );

View File

@ -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, ... )

View File

@ -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,

View File

@ -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

View File

@ -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 );
} }
/** /**

View File

@ -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 );