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 "ceil" option to vips_shrink() [kleisauke]
- quality improvements for image resizing [kleisauke]
- add vips_source_new_from_target()
- add vips_target_seek(), vips_target_read(), vips_target_new_temp()
- add vips_tiffsave_target()
- add vips_target_end(), deprecate vips_target_finish()
26/11/21 started 8.12.3

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_save_tiff_file_get_type( void );
extern GType vips_foreign_save_tiff_buffer_get_type( void );
extern GType vips_foreign_save_tiff_target_get_type( void );
extern GType vips_foreign_load_raw_get_type( void );
extern GType vips_foreign_save_raw_get_type( void );
@ -3090,6 +3091,7 @@ vips_foreign_operation_init( void )
vips_foreign_load_tiff_source_get_type();
vips_foreign_save_tiff_file_get_type();
vips_foreign_save_tiff_buffer_get_type();
vips_foreign_save_tiff_target_get_type();
#endif /*HAVE_TIFF*/
#if defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE)

View File

@ -51,7 +51,7 @@ extern "C" {
void vips__tiff_init( void );
int vips__tiff_write( VipsImage *in, const char *filename,
int vips__tiff_write_target( VipsImage *in, VipsTarget *target,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
const char *profile,
@ -72,27 +72,6 @@ int vips__tiff_write( VipsImage *in, const char *filename,
gboolean premultiply,
int page_height );
int vips__tiff_write_buf( VipsImage *in,
void **obuf, size_t *olen,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
const char *profile,
gboolean tile, int tile_width, int tile_height,
gboolean pyramid,
int bitdepth,
gboolean miniswhite,
VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff,
gboolean rgbjpeg,
gboolean properties, gboolean strip,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth,
gboolean subifd,
gboolean premultiply,
int page_height );
gboolean vips__istiff_source( VipsSource *source );
gboolean vips__istifftiled_source( VipsSource *source );
int vips__tiff_read_header_source( VipsSource *source, VipsImage *out,

View File

@ -90,48 +90,6 @@ vips__tiff_init( void )
TIFFSetWarningHandler( vips__thandler_warning );
}
/* Open TIFF for output.
*/
TIFF *
vips__tiff_openout( const char *path, gboolean bigtiff )
{
TIFF *tif;
const char *mode = bigtiff ? "w8" : "w";
#ifdef DEBUG
printf( "vips__tiff_openout( \"%s\", \"%s\" )\n", path, mode );
#endif /*DEBUG*/
/* Need the utf-16 version on Windows.
*/
#ifdef G_OS_WIN32
{
GError *error = NULL;
wchar_t *path16;
if( !(path16 = (wchar_t *)
g_utf8_to_utf16( path, -1, NULL, NULL, &error )) ) {
vips_g_error( &error );
return( NULL );
}
tif = TIFFOpenW( path16, mode );
g_free( path16 );
}
#else /*!G_OS_WIN32*/
tif = TIFFOpen( path, mode );
#endif /*G_OS_WIN32*/
if( !tif ) {
vips_error( "tiff",
_( "unable to open \"%s\" for output" ), path );
return( NULL );
}
return( tif );
}
/* TIFF input from a vips source.
*/
@ -152,13 +110,11 @@ openin_source_write( thandle_t st, tdata_t buffer, tsize_t size )
}
static toff_t
openin_source_seek( thandle_t st, toff_t position, int whence )
openin_source_seek( thandle_t st, toff_t offset, int whence )
{
VipsSource *source = VIPS_SOURCE( st );
/* toff_t is usually uint64, with -1 cast to uint64 to indicate error.
*/
return( (toff_t) vips_source_seek( source, position, whence ) );
return( (toff_t) vips_source_seek( source, offset, whence ) );
}
static int
@ -240,146 +196,95 @@ vips__tiff_openin_source( VipsSource *source )
return( tiff );
}
/* TIFF output to a memory buffer.
/* TIFF output to a target.
*/
typedef struct _VipsTiffOpenoutBuffer {
VipsDbuf dbuf;
/* On close, consolidate and write the output here.
*/
void **out_data;
size_t *out_length;
} VipsTiffOpenoutBuffer;
/* libtiff needs this (!!?!?!) for writing multipage images.
*/
static tsize_t
openout_buffer_read( thandle_t st, tdata_t data, tsize_t size )
openout_target_read( thandle_t st, tdata_t data, tsize_t size )
{
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
VipsTarget *target = (VipsTarget *) st;
#ifdef DEBUG
printf( "openout_buffer_read: %zd bytes\n", size );
#endif /*DEBUG*/
return( vips_dbuf_read( &buffer->dbuf, data, size ) );
return( vips_target_read( target, data, size ) );
}
static tsize_t
openout_buffer_write( thandle_t st, tdata_t data, tsize_t size )
openout_target_write( thandle_t st, tdata_t data, tsize_t size )
{
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
VipsTarget *target = (VipsTarget *) st;
#ifdef DEBUG
printf( "openout_buffer_write: %zd bytes\n", size );
#endif /*DEBUG*/
vips_dbuf_write( &buffer->dbuf, data, size );
if( vips_target_write( target, data, size ) )
return( (tsize_t) -1 );
return( size );
}
static int
openout_buffer_close( thandle_t st )
static toff_t
openout_target_seek( thandle_t st, toff_t offset, int whence )
{
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
VipsTarget *target = (VipsTarget *) st;
*(buffer->out_data) = vips_dbuf_steal( &buffer->dbuf,
buffer->out_length);
return( vips_target_seek( target, offset, whence ) );
}
static int
openout_target_close( thandle_t st )
{
VipsTarget *target = (VipsTarget *) st;
if( vips_target_end( target ) )
return( -1 );
return( 0 );
}
static toff_t
openout_buffer_seek( thandle_t st, toff_t position, int whence )
{
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
#ifdef DEBUG
printf( "openout_buffer_seek: position %zd, whence %d ",
position, whence );
switch( whence ) {
case SEEK_SET:
printf( "set" );
break;
case SEEK_END:
printf( "end" );
break;
case SEEK_CUR:
printf( "cur" );
break;
default:
printf( "unknown" );
break;
}
printf( "\n" );
#endif /*DEBUG*/
vips_dbuf_seek( &buffer->dbuf, position, whence );
return( vips_dbuf_tell( &buffer->dbuf ) );
}
static toff_t
openout_buffer_length( thandle_t st )
openout_target_length( thandle_t st )
{
g_assert_not_reached();
return( 0 );
return( (toff_t) -1 );
}
static int
openout_buffer_map( thandle_t st, tdata_t *start, toff_t *len )
openout_target_map( thandle_t st, tdata_t *start, toff_t *len )
{
g_assert_not_reached();
return( 0 );
return( -1 );
}
static void
openout_buffer_unmap( thandle_t st, tdata_t start, toff_t len )
openout_target_unmap( thandle_t st, tdata_t start, toff_t len )
{
g_assert_not_reached();
return;
}
/* On TIFFClose(), @data and @length are set to point to the output buffer.
*/
TIFF *
vips__tiff_openout_buffer( VipsImage *image,
gboolean bigtiff, void **out_data, size_t *out_length )
vips__tiff_openout_target( VipsTarget *target, gboolean bigtiff )
{
const char *mode = bigtiff ? "w8" : "w";
VipsTiffOpenoutBuffer *buffer;
TIFF *tiff;
#ifdef DEBUG
printf( "vips__tiff_openout_buffer:\n" );
#endif /*DEBUG*/
g_assert( out_data );
g_assert( out_length );
buffer = VIPS_NEW( image, VipsTiffOpenoutBuffer );
vips_dbuf_init( &buffer->dbuf );
buffer->out_data = out_data;
buffer->out_length = out_length;
if( !(tiff = TIFFClientOpen( "memory output", mode,
(thandle_t) buffer,
openout_buffer_read,
openout_buffer_write,
openout_buffer_seek,
openout_buffer_close,
openout_buffer_length,
openout_buffer_map,
openout_buffer_unmap )) ) {
vips_error( "vips__tiff_openout_buffer", "%s",
_( "unable to open memory buffer for output" ) );
if( !(tiff = TIFFClientOpen( "target output", mode,
(thandle_t) target,
openout_target_read,
openout_target_write,
openout_target_seek,
openout_target_close,
openout_target_length,
openout_target_map,
openout_target_unmap )) ) {
vips_error( "vips__tiff_openout_target", "%s",
_( "unable to open target for output" ) );
return( NULL );
}
@ -387,4 +292,3 @@ vips__tiff_openout_buffer( VipsImage *image,
}
#endif /*HAVE_TIFF*/

View File

@ -40,8 +40,7 @@ extern "C" {
TIFF *vips__tiff_openin_source( VipsSource *source );
TIFF *vips__tiff_openout( const char *path, gboolean bigtiff );
TIFF *vips__tiff_openout_buffer( VipsImage *image,
gboolean bigtiff, void **out_data, size_t *out_length );
TIFF *vips__tiff_openout_target( VipsTarget *target, gboolean bigtiff );
#ifdef __cplusplus
}

View File

@ -28,6 +28,8 @@
* - deprecate "squash"
* 1/5/21
* - add "premultiply" flag
* 10/5/22
* - add vips_tiffsave_target()
*/
/*
@ -83,6 +85,10 @@
typedef struct _VipsForeignSaveTiff {
VipsForeignSave parent_object;
/* Set by subclasses.
*/
VipsTarget *target;
/* Many options argh.
*/
VipsForeignTiffCompression compression;
@ -116,6 +122,17 @@ typedef VipsForeignSaveClass VipsForeignSaveTiffClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveTiff, vips_foreign_save_tiff,
VIPS_TYPE_FOREIGN_SAVE );
static void
vips_foreign_save_tiff_dispose( GObject *gobject )
{
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) gobject;
VIPS_UNREF( tiff->target );
G_OBJECT_CLASS( vips_foreign_save_tiff_parent_class )->
dispose( gobject );
}
#define UC VIPS_FORMAT_UCHAR
/* Type promotion for jpeg-in-tiff save ... just always go to uchar.
@ -186,6 +203,37 @@ vips_foreign_save_tiff_build( VipsObject *object )
tiff->yres *= 2.54;
}
/* Handle the deprecated squash parameter.
*/
if( tiff->squash )
/* We set that even in the case of LAB to LABQ.
*/
tiff->bitdepth = 1;
if( vips__tiff_write_target( save->ready, tiff->target,
tiff->compression, tiff->Q, tiff->predictor,
tiff->profile,
tiff->tile, tiff->tile_width, tiff->tile_height,
tiff->pyramid,
tiff->bitdepth,
tiff->miniswhite,
tiff->resunit, tiff->xres, tiff->yres,
tiff->bigtiff,
tiff->rgbjpeg,
tiff->properties,
save->strip,
tiff->region_shrink,
tiff->level,
tiff->lossless,
tiff->depth,
tiff->subifd,
tiff->premultiply,
save->page_height ) )
return( -1 );
if( vips_target_end( tiff->target ) )
return( -1 );
return( 0 );
}
@ -197,11 +245,12 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class )
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
gobject_class->dispose = vips_foreign_save_tiff_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "tiffsave_base";
object_class->description = _( "save image to tiff file" );
object_class->description = _( "save image as tiff" );
object_class->build = vips_foreign_save_tiff_build;
foreign_class->suffs = vips__foreign_tiff_suffs;
@ -392,6 +441,61 @@ vips_foreign_save_tiff_init( VipsForeignSaveTiff *tiff )
tiff->bitdepth = 0;
}
typedef struct _VipsForeignSaveTiffTarget {
VipsForeignSaveTiff parent_object;
VipsTarget *target;
} VipsForeignSaveTiffTarget;
typedef VipsForeignSaveTiffClass VipsForeignSaveTiffTargetClass;
G_DEFINE_TYPE( VipsForeignSaveTiffTarget, vips_foreign_save_tiff_target,
vips_foreign_save_tiff_get_type() );
static int
vips_foreign_save_tiff_target_build( VipsObject *object )
{
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
VipsForeignSaveTiffTarget *target =
(VipsForeignSaveTiffTarget *) object;
tiff->target = target->target;
g_object_ref( tiff->target );
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_target_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_save_tiff_target_class_init(
VipsForeignSaveTiffTargetClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "tiffsave_target";
object_class->description = _( "save image to tiff target" );
object_class->build = vips_foreign_save_tiff_target_build;
VIPS_ARG_OBJECT( class, "target", 1,
_( "Target" ),
_( "Target to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveTiffTarget, target ),
VIPS_TYPE_TARGET );
}
static void
vips_foreign_save_tiff_target_init( VipsForeignSaveTiffTarget *target )
{
}
typedef struct _VipsForeignSaveTiffFile {
VipsForeignSaveTiff parent_object;
@ -406,40 +510,14 @@ G_DEFINE_TYPE( VipsForeignSaveTiffFile, vips_foreign_save_tiff_file,
static int
vips_foreign_save_tiff_file_build( VipsObject *object )
{
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
VipsForeignSaveTiffFile *file = (VipsForeignSaveTiffFile *) object;
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_file_parent_class )->
build( object ) )
if( !(tiff->target = vips_target_new_to_file( file->filename )) )
return( -1 );
/* Handle the deprecated squash parameter.
*/
if( tiff->squash )
/* We set that even in the case of LAB to LABQ.
*/
tiff->bitdepth = 1;
if( vips__tiff_write( save->ready, file->filename,
tiff->compression, tiff->Q, tiff->predictor,
tiff->profile,
tiff->tile, tiff->tile_width, tiff->tile_height,
tiff->pyramid,
tiff->bitdepth,
tiff->miniswhite,
tiff->resunit, tiff->xres, tiff->yres,
tiff->bigtiff,
tiff->rgbjpeg,
tiff->properties,
save->strip,
tiff->region_shrink,
tiff->level,
tiff->lossless,
tiff->depth,
tiff->subifd,
tiff->premultiply,
save->page_height ) )
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_file_parent_class )->
build( object ) )
return( -1 );
return( 0 );
@ -485,40 +563,21 @@ G_DEFINE_TYPE( VipsForeignSaveTiffBuffer, vips_foreign_save_tiff_buffer,
static int
vips_foreign_save_tiff_buffer_build( VipsObject *object )
{
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object;
VipsForeignSaveTiffBuffer *buffer =
(VipsForeignSaveTiffBuffer *) object;
void *obuf;
size_t olen;
VipsBlob *blob;
if( !(tiff->target = vips_target_new_to_memory()) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_buffer_parent_class )->
build( object ) )
return( -1 );
if( vips__tiff_write_buf( save->ready, &obuf, &olen,
tiff->compression, tiff->Q, tiff->predictor,
tiff->profile,
tiff->tile, tiff->tile_width, tiff->tile_height,
tiff->pyramid,
tiff->bitdepth,
tiff->miniswhite,
tiff->resunit, tiff->xres, tiff->yres,
tiff->bigtiff,
tiff->rgbjpeg,
tiff->properties,
save->strip,
tiff->region_shrink,
tiff->level,
tiff->lossless,
tiff->depth,
tiff->subifd,
tiff->premultiply,
save->page_height ) )
return( -1 );
blob = vips_blob_new( (VipsCallbackFn) vips_area_free_cb, obuf, olen );
g_object_set( object, "buffer", blob, NULL );
g_object_get( tiff->target, "blob", &blob, NULL );
g_object_set( buffer, "buffer", blob, NULL );
vips_area_unref( VIPS_AREA( blob ) );
return( 0 );
@ -654,8 +713,7 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
* resolutions. By default these values are taken from the VIPS image header.
* libvips resolution is always in pixels per millimetre.
*
* Set @bigtiff to attempt to write a bigtiff.
* Bigtiff is a variant of the TIFF
* Set @bigtiff to attempt to write a bigtiff. Bigtiff is a variant of the TIFF
* format that allows more than 4GB in a file.
*
* Set @properties to write all vips metadata to the IMAGEDESCRIPTION tag as
@ -673,7 +731,7 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer )
* Set @subifd to save pyramid layers as sub-directories of the main image.
* Setting this option can improve compatibility with formats like OME.
*
* Set @premultiply tio save with premultiplied alpha. Some programs, such as
* Set @premultiply to save with premultiplied alpha. Some programs, such as
* InDesign, will only work with premultiplied alpha.
*
* See also: vips_tiffload(), vips_image_write_to_file().
@ -761,3 +819,52 @@ vips_tiffsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
return( result );
}
/**
* vips_tiffsave_target: (method)
* @in: image to save
* @target: save image to this target
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @compression: use this #VipsForeignTiffCompression
* * @Q: %gint quality factor
* * @predictor: use this #VipsForeignTiffPredictor
* * @profile: %gchararray, filename of ICC profile to attach
* * @tile: %gboolean, set %TRUE to write a tiled tiff
* * @tile_width: %gint for tile size
* * @tile_height: %gint for tile size
* * @pyramid: %gboolean, write an image pyramid
* * @bitdepth: %int, set write bit depth to 1, 2, 4 or 8
* * @miniswhite: %gboolean, write 1-bit images as MINISWHITE
* * @resunit: #VipsForeignTiffResunit for resolution unit
* * @xres: %gdouble horizontal resolution in pixels/mm
* * @yres: %gdouble vertical resolution in pixels/mm
* * @bigtiff: %gboolean, write a BigTiff file
* * @properties: %gboolean, set %TRUE to write an IMAGEDESCRIPTION tag
* * @region_shrink: #VipsRegionShrink How to shrink each 2x2 region.
* * @level: %gint, Zstd compression level
* * @lossless: %gboolean, WebP losssless mode
* * @depth: #VipsForeignDzDepth how deep to make the pyramid
* * @subifd: %gboolean write pyr layers as sub-ifds
* * @premultiply: %gboolean write premultiplied alpha
*
* As vips_tiffsave(), but save to a target.
*
* See also: vips_tiffsave(), vips_image_write_to_target().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_tiffsave_target( VipsImage *in, VipsTarget *target, ... )
{
va_list ap;
int result;
va_start( ap, target );
result = vips_call_split( "tiffsave_target", ap, in, target );
va_end( ap );
return( result );
}

View File

@ -202,6 +202,8 @@
* 20/10/21 [jacopoabramo]
* - subifd enables pyramid
* - add support for page_height param
* 11/5/22
* - switch to terget API for output
*/
/*
@ -293,14 +295,9 @@ typedef struct _Wtiff Wtiff;
struct _Layer {
Wtiff *wtiff; /* Main wtiff struct */
/* The filename for this layer, for file output.
/* The temp target for this layer.
*/
char *lname;
/* The memory area for this layer, for memory output.
*/
void *buf;
size_t len;
VipsTarget *target;
int width, height; /* Layer size */
int sub; /* Subsample factor for this layer */
@ -335,14 +332,9 @@ struct _Wtiff {
*/
VipsImage *ready;
/* File to write to, or NULL.
/* Target to write to.
*/
char *filename; /* Name we write to */
/* Memory area to output, or NULL.
*/
void **obuf;
size_t *olen;
VipsTarget *target;
Layer *layer; /* Top of pyramid */
VipsPel *tbuf; /* TIFF output buffer */
@ -453,9 +445,6 @@ wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
else
(*layer)->sub = above->sub * 2;
(*layer)->lname = NULL;
(*layer)->buf = NULL;
(*layer)->len = 0;
(*layer)->tif = NULL;
(*layer)->image = NULL;
(*layer)->write_y = 0;
@ -466,26 +455,16 @@ wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
(*layer)->below = NULL;
(*layer)->above = above;
/* The name for the top layer is the output filename.
*
* We need lname to be freed automatically: it has to stay
* alive until after wtiff_gather().
/* The target we write to. The base layer writes to the main
* output, each layer smaller writes to a memory temp.
*/
if( wtiff->filename ) {
if( !above )
(*layer)->lname = vips_strdup(
VIPS_OBJECT( wtiff->ready ),
wtiff->filename );
else {
char *lname;
lname = vips__temp_name( "%s.tif" );
(*layer)->lname = vips_strdup(
VIPS_OBJECT( wtiff->ready ),
lname );
g_free( lname );
}
if( !above ) {
(*layer)->target = wtiff->target;
g_object_ref( (*layer)->target );
}
else
(*layer)->target =
vips_target_new_temp( wtiff->target );
/*
printf( "wtiff_layer_init: sub = %d, width = %d, height = %d\n",
@ -929,14 +908,8 @@ wtiff_allocate_layers( Wtiff *wtiff )
vips__region_no_ownership( layer->strip );
vips__region_no_ownership( layer->copy );
if( layer->lname )
layer->tif = vips__tiff_openout(
layer->lname, wtiff->bigtiff );
else {
layer->tif = vips__tiff_openout_buffer(
wtiff->ready, wtiff->bigtiff,
&layer->buf, &layer->len );
}
layer->tif = vips__tiff_openout_target( layer->target,
wtiff->bigtiff );
if( !layer->tif )
return( -1 );
}
@ -962,42 +935,17 @@ wtiff_allocate_layers( Wtiff *wtiff )
return( 0 );
}
/* Delete any temp files we wrote.
*/
static void
wtiff_delete_temps( Wtiff *wtiff )
{
Layer *layer;
/* Don't delete the top layer: that's the output file.
*/
if( wtiff->layer &&
wtiff->layer->below )
for( layer = wtiff->layer->below; layer; layer = layer->below )
if( layer->lname ) {
#ifndef DEBUG
unlink( layer->lname );
#else /*!DEBUG*/
printf( "wtiff_delete_temps: "
"debug mode, not deleting %s\n",
layer->lname );
#endif /*DEBUG*/
VIPS_FREE( layer->buf );
layer->lname = NULL;
}
}
/* Free a single pyramid layer.
*/
static void
layer_free( Layer *layer )
{
/* Don't unref the target for this layer -- we'll need it for gather.
*/
VIPS_FREEF( TIFFClose, layer->tif );
VIPS_UNREF( layer->strip );
VIPS_UNREF( layer->copy );
VIPS_UNREF( layer->image );
VIPS_FREE( layer->buf );
VIPS_FREEF( TIFFClose, layer->tif );
}
/* Free an entire pyramid.
@ -1014,11 +962,16 @@ layer_free_all( Layer *layer )
static void
wtiff_free( Wtiff *wtiff )
{
wtiff_delete_temps( wtiff );
Layer *layer;
/* unref all the targets, including the base layer.
*/
for( layer = wtiff->layer; layer; layer = layer->below )
VIPS_UNREF( layer->target );
VIPS_UNREF( wtiff->ready );
VIPS_FREE( wtiff->tbuf );
VIPS_FREEF( layer_free_all, wtiff->layer );
VIPS_FREE( wtiff->filename );
VIPS_FREE( wtiff );
}
@ -1129,7 +1082,7 @@ ready_to_write( Wtiff *wtiff )
}
static Wtiff *
wtiff_new( VipsImage *input, const char *filename,
wtiff_new( VipsImage *input, VipsTarget *target,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
const char *profile,
@ -1156,7 +1109,7 @@ wtiff_new( VipsImage *input, const char *filename,
return( NULL );
wtiff->input = input;
wtiff->ready = NULL;
wtiff->filename = filename ? vips_strdup( NULL, filename ) : NULL;
wtiff->target = target;
wtiff->layer = NULL;
wtiff->tbuf = NULL;
wtiff->compression = get_compression( compression );
@ -2110,19 +2063,12 @@ wtiff_gather( Wtiff *wtiff )
TIFF *in;
#ifdef DEBUG
printf( "appending layer %s ...\n", layer->lname );
printf( "appending layer sub = %d ...\n", layer->sub );
#endif /*DEBUG*/
if( layer->lname ) {
if( !(source = vips_source_new_from_file(
layer->lname )) )
return( -1 );
}
else {
if( !(source = vips_source_new_from_memory(
layer->buf, layer->len )) )
return( -1 );
}
if( !(source =
vips_source_new_from_target( layer->target )) )
return( -1 );
if( !(in = vips__tiff_openin_source( source )) ) {
VIPS_UNREF( source );
@ -2204,6 +2150,8 @@ wtiff_page_end( Wtiff *wtiff )
/* Append any pyr layers, if necessary.
*/
if( wtiff->layer->below ) {
Layer *layer;
/* Free any lower pyramid resources ... this will
* TIFFClose() (but not delete) the smaller layers
* ready for us to read from them again.
@ -2215,14 +2163,14 @@ wtiff_page_end( Wtiff *wtiff )
if( wtiff_gather( wtiff ) )
return( -1 );
/* We can delete any temps now ready for the next page.
/* unref all the lower targets.
*/
wtiff_delete_temps( wtiff );
for( layer = wtiff->layer->below; layer; layer = layer->below )
VIPS_UNREF( layer->target );
/* And free all lower pyr layers ready to be rebuilt for the
* next page.
/* ... ready for the next page.
*/
VIPS_FREEF( layer_free_all, wtiff->layer->below );
wtiff->layer->below = NULL;
}
wtiff->page_number += 1;
@ -2294,55 +2242,7 @@ wtiff_sink_disc_strip( VipsRegion *region, VipsRect *area, void *a )
}
int
vips__tiff_write( VipsImage *input, const char *filename,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
const char *profile,
gboolean tile, int tile_width, int tile_height,
gboolean pyramid,
int bitdepth,
gboolean miniswhite,
VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff,
gboolean rgbjpeg,
gboolean properties, gboolean strip,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth,
gboolean subifd,
gboolean premultiply,
int page_height )
{
Wtiff *wtiff;
#ifdef DEBUG
printf( "tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() );
#endif /*DEBUG*/
vips__tiff_init();
if( !(wtiff = wtiff_new( input, filename,
compression, Q, predictor, profile,
tile, tile_width, tile_height, pyramid, bitdepth,
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
properties, strip, region_shrink, level, lossless, depth,
subifd, premultiply, page_height )) )
return( -1 );
if( vips_sink_disc( wtiff->ready, wtiff_sink_disc_strip, wtiff ) ) {
wtiff_free( wtiff );
return( -1 );
}
wtiff_free( wtiff );
return( 0 );
}
int
vips__tiff_write_buf( VipsImage *input,
void **obuf, size_t *olen,
vips__tiff_write_target( VipsImage *input, VipsTarget *target,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
const char *profile,
@ -2366,7 +2266,7 @@ vips__tiff_write_buf( VipsImage *input,
vips__tiff_init();
if( !(wtiff = wtiff_new( input, NULL,
if( !(wtiff = wtiff_new( input, target,
compression, Q, predictor, profile,
tile, tile_width, tile_height, pyramid, bitdepth,
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
@ -2374,26 +2274,11 @@ vips__tiff_write_buf( VipsImage *input,
subifd, premultiply, page_height )) )
return( -1 );
wtiff->obuf = obuf;
wtiff->olen = olen;
if( vips_sink_disc( wtiff->ready, wtiff_sink_disc_strip, wtiff ) ) {
wtiff_free( wtiff );
return( -1 );
}
/* Now close the top layer, and we'll get a pointer we can return
* to our caller.
*/
VIPS_FREEF( TIFFClose, wtiff->layer->tif );
*obuf = wtiff->layer->buf;
*olen = wtiff->layer->len;
/* Now our caller owns it, we must not free it.
*/
wtiff->layer->buf = NULL;
wtiff_free( wtiff );
return( 0 );

View File

@ -225,6 +225,8 @@ VipsSource *vips_source_new_from_file( const char *filename );
VIPS_API
VipsSource *vips_source_new_from_blob( VipsBlob *blob );
VIPS_API
VipsSource *vips_source_new_from_target( VipsTarget *target );
VIPS_API
VipsSource *vips_source_new_from_memory( const void *data, size_t size );
VIPS_API
VipsSource *vips_source_new_from_options( const char *options );
@ -410,9 +412,11 @@ struct _VipsTarget {
*/
gboolean ended;
/* Write memory output here.
/* Write memory output and track write point here. We use a GString
* rather than a GByteArray since we need eg. g_string_overwrite_len().
*/
GByteArray *memory_buffer;
GString *memory_buffer;
off_t position;
/* And return memory via this blob.
*/
@ -424,6 +428,11 @@ struct _VipsTarget {
unsigned char output_buffer[VIPS_TARGET_BUFFER_SIZE];
int write_point;
/* Temp targets on the filesystem need deleting, sometimes.
*/
gboolean delete_on_close;
char *delete_on_close_filename;
};
typedef struct _VipsTargetClass {
@ -436,6 +445,24 @@ typedef struct _VipsTargetClass {
*/
gint64 (*write)( VipsTarget *, const void *, size_t );
/* libtiff needs to be able to seek and read on targets,
* unfortunately.
*
* This will not work for eg. pipes, of course.
*/
/* Read from the target into the supplied buffer, args exactly as
* read(2). Set errno on error.
*
* We must return gint64, since ssize_t is often defined as unsigned
* on Windows.
*/
gint64 (*read)( VipsTarget *, void *, size_t );
/* Seek output. Args exactly as lseek(2).
*/
off_t (*seek)( VipsTarget *, off_t offset, int whence);
/* Output has been generated, so do any clearing up,
* eg. copy the bytes we saved in memory to the target blob.
*/
@ -457,8 +484,14 @@ VipsTarget *vips_target_new_to_file( const char *filename );
VIPS_API
VipsTarget *vips_target_new_to_memory( void );
VIPS_API
VipsTarget *vips_target_new_temp( VipsTarget *target );
VIPS_API
int vips_target_write( VipsTarget *target, const void *data, size_t length );
VIPS_API
gint64 vips_target_read( VipsTarget *target, void *buffer, size_t length );
VIPS_API
off_t vips_target_seek( VipsTarget *target, off_t offset, int whence );
VIPS_API
int vips_target_end( VipsTarget *target );
VIPS_DEPRECATED_FOR(vips_target_end)
void vips_target_finish( VipsTarget *target );
@ -514,6 +547,8 @@ typedef struct _VipsTargetCustomClass {
*/
gint64 (*write)( VipsTargetCustom *, const void *, gint64 );
gint64 (*read)( VipsTargetCustom *, void *, gint64 );
gint64 (*seek)( VipsTargetCustom *, gint64, int );
int (*end)( VipsTargetCustom * );
void (*finish)( VipsTargetCustom * );

View File

@ -597,6 +597,9 @@ int vips_tiffsave( VipsImage *in, const char *filename, ... )
VIPS_API
int vips_tiffsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
__attribute__((sentinel));
VIPS_API
int vips_tiffsave_target( VipsImage *in, VipsTarget *target, ... )
__attribute__((sentinel));
VIPS_API
int vips_openexrload( const char *filename, VipsImage **out, ... )

View File

@ -784,6 +784,7 @@ vips_object_get_argument( VipsObject *object, const char *name,
_( "no vips argument named `%s'" ), name );
return( -1 );
}
if( !(*argument_instance = vips__argument_get_instance(
*argument_class, object )) ) {
vips_error( class->nickname,

View File

@ -12,6 +12,8 @@
* descriptors
* 8/10/21
* - fix named pipes
* 10/5/22
* - add vips_source_new_from_target()
*/
/*
@ -487,6 +489,48 @@ vips_source_new_from_blob( VipsBlob *blob )
return( source );
}
/**
* vips_source_new_from_target:
* @target: build the source from this target
*
* Create a source from a temp target that has been written to.
*
* Returns: a new source.
*/
VipsSource *
vips_source_new_from_target( VipsTarget *target )
{
VipsConnection *connection = VIPS_CONNECTION( target );
VipsSource *source;
VIPS_DEBUG_MSG( "vips_source_new_from_target: %p\n", target );
/* Flush output buffer, move memory into the blob, etc.
*/
if( vips_target_end( target ) )
return( NULL );
if( connection->descriptor > 0 ) {
source = vips_source_new_from_descriptor(
connection->descriptor );
}
else if( target->memory ) {
VipsBlob *blob;
g_object_get( target, "blob", &blob, NULL );
source = vips_source_new_from_blob( blob );
vips_area_unref( VIPS_AREA( blob ) );
}
else {
vips_error( vips_connection_nick( connection ),
"%s", _( "unimplemented target" ) );
return( NULL );
}
return( source );
}
/**
* vips_source_new_from_memory:
* @data: memory area to load

View File

@ -65,7 +65,10 @@
#include <vips/debug.h>
#include <vips/internal.h>
#define MODE_WRITE CLOEXEC (BINARYIZE (O_WRONLY | O_CREAT | O_TRUNC))
/* libtiff needs to be able to seek and read back output files, unfortunately,
* so we must open read-write.
*/
#define MODE_READWRITE CLOEXEC (BINARYIZE (O_RDWR | O_CREAT | O_TRUNC))
G_DEFINE_TYPE( VipsTarget, vips_target, VIPS_TYPE_CONNECTION );
@ -76,12 +79,22 @@ vips_target_finalize( GObject *gobject )
VIPS_DEBUG_MSG( "vips_target_finalize:\n" );
VIPS_FREEF( g_byte_array_unref, target->memory_buffer );
if( target->memory_buffer ) {
g_string_free( target->memory_buffer, TRUE );
target->memory_buffer = NULL;
}
if( target->blob ) {
vips_area_unref( VIPS_AREA( target->blob ) );
target->blob = NULL;
}
if( target->delete_on_close &&
target->delete_on_close_filename )
g_unlink( target->delete_on_close_filename );
VIPS_FREE( target->delete_on_close_filename );
G_OBJECT_CLASS( vips_target_parent_class )->finalize( gobject );
}
@ -111,7 +124,7 @@ vips_target_build( VipsObject *object )
/* 0644 is rw user, r group and other.
*/
if( (fd = vips_tracked_open( filename,
MODE_WRITE, 0644 )) == -1 ) {
MODE_READWRITE, 0644 )) == -1 ) {
vips_error_system( errno,
vips_connection_nick( connection ),
"%s", _( "unable to open for write" ) );
@ -133,7 +146,8 @@ vips_target_build( VipsObject *object )
#endif /*G_OS_WIN32*/
}
else if( target->memory )
target->memory_buffer = g_byte_array_new();
target->memory_buffer =
g_string_sized_new( VIPS_TARGET_BUFFER_SIZE );
return( 0 );
}
@ -143,9 +157,97 @@ vips_target_write_real( VipsTarget *target, const void *data, size_t length )
{
VipsConnection *connection = VIPS_CONNECTION( target );
gint64 result;
VIPS_DEBUG_MSG( "vips_target_write_real: %zd bytes\n", length );
return( write( connection->descriptor, data, length ) );
if( target->memory_buffer ) {
VIPS_DEBUG_MSG( "vips_target_write_real: to position %zd\n",
target->position );
g_string_overwrite_len( target->memory_buffer, target->position,
data, length );
target->position += length;
result = length;
}
else
result = write( connection->descriptor, data, length );
return( result );
}
static off_t
vips_target_seek_real( VipsTarget *target, off_t offset, int whence )
{
VipsConnection *connection = VIPS_CONNECTION( target );
const char *nick = vips_connection_nick( connection );
off_t new_position;
VIPS_DEBUG_MSG( "vips_target_seek_real: offset = %ld, whence = %d\n",
offset, whence );
if( target->memory_buffer ) {
switch( whence ) {
case SEEK_SET:
new_position = offset;
break;
case SEEK_CUR:
new_position = target->position + offset;
break;
case SEEK_END:
new_position = target->memory_buffer->len + offset;
break;
default:
vips_error( nick, "%s", _( "bad 'whence'" ) );
return( -1 );
}
if( new_position > target->memory_buffer->len )
g_string_set_size( target->memory_buffer,
new_position );
target->position = new_position;
}
else
new_position = lseek( connection->descriptor, offset, whence );
return( new_position );
}
static gint64
vips_target_read_real( VipsTarget *target, void *data, size_t length )
{
gint64 bytes_read;
VIPS_DEBUG_MSG( "vips_target_read_real: %zd bytes\n", length );
if( target->memory_buffer ) {
bytes_read = VIPS_MIN( length,
target->memory_buffer->len - target->position );
VIPS_DEBUG_MSG( " %zd bytes from memory\n", bytes_read );
memcpy( data,
target->memory_buffer->str +
target->position,
bytes_read );
target->position += bytes_read;
}
else {
VipsConnection *connection = VIPS_CONNECTION( target );
int fd = connection->descriptor;
do {
bytes_read = read( fd, data, length );
} while( bytes_read < 0 && errno == EINTR );
}
VIPS_DEBUG_MSG( " read %zd bytes\n", bytes_read );
return( bytes_read );
}
static int
@ -178,6 +280,8 @@ vips_target_class_init( VipsTargetClass *class )
object_class->build = vips_target_build;
class->write = vips_target_write_real;
class->read = vips_target_read_real;
class->seek = vips_target_seek_real;
class->end = vips_target_end_real;
class->finish = vips_target_finish_real;
@ -295,6 +399,50 @@ vips_target_new_to_memory( void )
return( target );
}
/**
* vips_target_new_temp:
* @based_on: base the temporary target on this target
*
* Create a temporary target -- either a temporary file on disc, or an area in
* memory, depending on what sort of target @based_on is.
*
* See also: vips_target_new_to_file().
*
* Returns: a new target.
*/
VipsTarget *
vips_target_new_temp( VipsTarget *based_on )
{
VipsTarget *target;
VIPS_DEBUG_MSG( "vips_target_new_temp: %p\n", based_on );
if( vips_connection_filename( VIPS_CONNECTION( based_on ) ) ) {
int descriptor;
char *filename;
if( !(filename = vips__temp_name( "%s.target" )) )
return( NULL );
if( (descriptor =
vips__open_image_write( filename, TRUE )) < 0 ) {
g_free( filename );
return( NULL );
}
if( !(target = vips_target_new_to_descriptor( descriptor )) ) {
g_free( filename );
vips_tracked_close( descriptor );
return( NULL );
}
vips_tracked_close( descriptor );
target->delete_on_close = TRUE;
target->delete_on_close_filename = filename;
}
else
target = vips_target_new_to_memory();
return( target );
}
static int
vips_target_write_unbuffered( VipsTarget *target,
const void *data, size_t length )
@ -306,29 +454,26 @@ vips_target_write_unbuffered( VipsTarget *target,
if( target->ended )
return( 0 );
if( target->memory_buffer )
g_byte_array_append( target->memory_buffer, data, length );
else
while( length > 0 ) {
gint64 bytes_written;
while( length > 0 ) {
gint64 bytes_written;
bytes_written = class->write( target, data, length );
bytes_written = class->write( target, data, length );
/* n == 0 isn't strictly an error, but we treat it as
* one to make sure we don't get stuck in this loop.
*/
if( bytes_written <= 0 ) {
vips_error_system( errno,
vips_connection_nick(
VIPS_CONNECTION( target ) ),
"%s", _( "write error" ) );
return( -1 );
}
length -= bytes_written;
data += bytes_written;
/* n == 0 isn't strictly an error, but we treat it as
* one to make sure we don't get stuck in this loop.
*/
if( bytes_written <= 0 ) {
vips_error_system( errno,
vips_connection_nick(
VIPS_CONNECTION( target ) ),
"%s", _( "write error" ) );
return( -1 );
}
length -= bytes_written;
data += bytes_written;
}
return( 0 );
}
@ -384,6 +529,70 @@ vips_target_write( VipsTarget *target, const void *buffer, size_t length )
return( 0 );
}
/**
* vips_target_read:
* @target: target to operate on
* @buffer: store bytes here
* @length: length of @buffer in bytes
*
* Read up to @length bytes from @target and store the bytes in @buffer.
* Return the number of bytes actually read. If all bytes have been read from
* the file, return 0.
*
* Arguments exactly as read(2).
*
* Reading froma target sounds weird, but libtiff needs this for
* multi-page writes. This method will fail for targets like pipes.
*
* Returns: the number of bytes read, 0 on end of file, -1 on error.
*/
gint64
vips_target_read( VipsTarget *target, void *buffer, size_t length )
{
VipsTargetClass *class = VIPS_TARGET_GET_CLASS( target );
VIPS_DEBUG_MSG( "vips_target_read: %zd bytes\n", length );
if( vips_target_flush( target ) )
return( -1 );
return( class->read( target, buffer, length ) );
}
/**
* vips_target_seek:
* @target: target to operate on
* @position: position to seek to
* @whence: seek relative to beginning, offset, or end
*
* Seek the target. This behaves exactly as lseek(2).
*
* Seeking a target sounds weird, but libtiff needs this. This method will
* fail for targets like pipes.
*
* Returns: 0 on success, -1 on error.
*/
off_t
vips_target_seek( VipsTarget *target, off_t position, int whence )
{
VipsTargetClass *class = VIPS_TARGET_GET_CLASS( target );
off_t new_position;
VIPS_DEBUG_MSG( "vips_target_seek: pos = %ld, whence = %d\n",
position, whence );
if( vips_target_flush( target ) )
return( -1 );
new_position = class->seek( target, position, whence );
VIPS_DEBUG_MSG( "vips_target_seek: new_position = %ld\n",
new_position );
return( new_position );
}
/**
* vips_target_end:
* @target: target to operate on
@ -413,11 +622,11 @@ vips_target_end( VipsTarget *target )
/* Move the target buffer into the blob so it can be read out.
*/
if( target->memory_buffer ) {
unsigned char *data;
const char *data;
size_t length;
length = target->memory_buffer->len;
data = g_byte_array_free( target->memory_buffer, FALSE );
data = g_string_free( target->memory_buffer, FALSE );
target->memory_buffer = NULL;
vips_blob_set( target->blob,
(VipsCallbackFn) vips_area_free_cb, data, length );
@ -465,7 +674,7 @@ vips_target_finish( VipsTarget *target )
unsigned char *
vips_target_steal( VipsTarget *target, size_t *length )
{
unsigned char *data;
const char *data;
(void) vips_target_flush( target );
@ -479,17 +688,17 @@ vips_target_steal( VipsTarget *target, size_t *length )
if( length )
*length = target->memory_buffer->len;
data = g_byte_array_free( target->memory_buffer, FALSE );
data = g_string_free( target->memory_buffer, FALSE );
target->memory_buffer = NULL;
/* We must have a valid byte array or end will fail.
*/
target->memory_buffer = g_byte_array_new();
target->memory_buffer = g_string_sized_new( 0 );
if( vips_target_end( target ) )
return( NULL );
return( data );
return( (unsigned char *) data );
}
/**

View File

@ -64,6 +64,8 @@ G_DEFINE_TYPE( VipsTargetCustom, vips_target_custom, VIPS_TYPE_TARGET );
*/
enum {
SIG_WRITE,
SIG_READ,
SIG_SEEK,
SIG_END,
SIG_FINISH,
SIG_LAST
@ -91,6 +93,68 @@ vips_target_custom_write_real( VipsTarget *target,
return( bytes_written );
}
static gint64
vips_target_custom_read_real( VipsTarget *target, void *buffer, size_t length )
{
gint64 bytes_read;
VIPS_DEBUG_MSG( "vips_target_custom_read_real:\n" );
/* Return this value (error) if there's no attached handler.
*/
bytes_read = 0;
g_signal_emit( target, vips_target_custom_signals[SIG_READ], 0,
buffer, (gint64) length, &bytes_read );
VIPS_DEBUG_MSG( " vips_target_custom_read_real, seen %zd bytes\n",
bytes_read );
return( bytes_read );
}
static gint64
vips_target_custom_seek_real( VipsTarget *target, gint64 offset, int whence )
{
GValue args[3] = { { 0 } };
GValue result = { 0 };
gint64 new_position;
VIPS_DEBUG_MSG( "vips_target_custom_seek_real:\n" );
/* Set the signal args.
*/
g_value_init( &args[0], G_TYPE_OBJECT );
g_value_set_object( &args[0], target );
g_value_init( &args[1], G_TYPE_INT64 );
g_value_set_int64( &args[1], offset );
g_value_init( &args[2], G_TYPE_INT );
g_value_set_int( &args[2], whence );
/* Set the default value if no handlers are attached.
*/
g_value_init( &result, G_TYPE_INT64 );
g_value_set_int64( &result, -1 );
/* We need to use this signal interface since we want a default value
* if no handlers are attached.
*/
g_signal_emitv( (const GValue *) &args,
vips_target_custom_signals[SIG_SEEK], 0, &result );
new_position = g_value_get_int64( &result );
g_value_unset( &args[0] );
g_value_unset( &args[1] );
g_value_unset( &args[2] );
g_value_unset( &result );
VIPS_DEBUG_MSG( " vips_target_custom_seek_real, seen new pos %zd\n",
new_position );
return( new_position );
}
static int
vips_target_custom_end_real( VipsTarget *target )
{
@ -125,6 +189,24 @@ vips_target_custom_write_signal_real( VipsTargetCustom *target_custom,
return( 0 );
}
static gint64
vips_target_custom_read_signal_real( VipsTargetCustom *target_custom,
void *data, gint64 length )
{
VIPS_DEBUG_MSG( "vips_target_custom_read_signal_real:\n" );
return( 0 );
}
static gint64
vips_target_custom_seek_signal_real( VipsTargetCustom *target_custom,
gint64 offset, int whence )
{
VIPS_DEBUG_MSG( "vips_target_custom_seek_signal_real:\n" );
return( -1 );
}
static int
vips_target_custom_end_signal_real( VipsTargetCustom *target_custom )
{
@ -149,10 +231,14 @@ vips_target_custom_class_init( VipsTargetCustomClass *class )
object_class->description = _( "Custom target" );
target_class->write = vips_target_custom_write_real;
target_class->read = vips_target_custom_read_real;
target_class->seek = vips_target_custom_seek_real;
target_class->end = vips_target_custom_end_real;
target_class->finish = vips_target_custom_finish_real;
class->write = vips_target_custom_write_signal_real;
class->read = vips_target_custom_read_signal_real;
class->seek = vips_target_custom_seek_signal_real;
class->end = vips_target_custom_end_signal_real;
class->finish = vips_target_custom_finish_signal_real;
@ -175,6 +261,49 @@ vips_target_custom_class_init( VipsTargetCustomClass *class )
G_TYPE_INT64, 2,
G_TYPE_POINTER, G_TYPE_INT64 );
/**
* VipsTargetCustom::read:
* @target_custom: the target being operated on
* @buffer: %gpointer, buffer to fill
* @size: %gint64, size of buffer
*
* This signal is emitted to read bytes from the target into @buffer.
*
* The handler for an unreadable target should always return -1.
*
* Returns: the number of bytes read. Return 0 for EOF.
*/
vips_target_custom_signals[SIG_READ] = g_signal_new( "read",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_ACTION,
G_STRUCT_OFFSET( VipsTargetCustomClass, read ),
NULL, NULL,
vips_INT64__POINTER_INT64,
G_TYPE_INT64, 2,
G_TYPE_POINTER, G_TYPE_INT64 );
/**
* VipsTargetCustom::seek:
* @target_custom: the target being operated on
* @offset: %gint64, seek offset
* @whence: %gint, seek origin
*
* This signal is emitted to seek the target. The handler should
* change the target position appropriately.
*
* The handler for an unseekable target should always return -1.
*
* Returns: the new seek position.
*/
vips_target_custom_signals[SIG_SEEK] = g_signal_new( "seek",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_ACTION,
G_STRUCT_OFFSET( VipsTargetCustomClass, seek ),
NULL, NULL,
vips_INT64__INT64_INT,
G_TYPE_INT64, 2,
G_TYPE_INT64, G_TYPE_INT );
/**
* VipsTargetCustom::end:
* @target_custom: the target being operated on
@ -227,8 +356,8 @@ vips_target_custom_new( void )
VIPS_DEBUG_MSG( "vips_target_custom_new:\n" );
target_custom = VIPS_TARGET_CUSTOM( g_object_new(
VIPS_TYPE_TARGET_CUSTOM, NULL ) );
target_custom = VIPS_TARGET_CUSTOM(
g_object_new( VIPS_TYPE_TARGET_CUSTOM, NULL ) );
if( vips_object_build( VIPS_OBJECT( target_custom ) ) ) {
VIPS_UNREF( target_custom );