jpegsave_stream mostly done

$ vips jpegsave_buffer x.jpg

doesn't work though -- need to rethink how output blobs are made
This commit is contained in:
John Cupitt 2019-10-11 18:17:42 +01:00
parent 20cb0da247
commit d991b73ac5
6 changed files with 326 additions and 207 deletions

View File

@ -212,7 +212,7 @@ vips_foreign_load_jpeg_stream_class_init(
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "jpegload_stream";
object_class->description = _( "load jpeg from stream" );
object_class->description = _( "load image from jpeg stream" );
load_class->is_a_stream = vips_foreign_load_jpeg_stream_is_a;
load_class->header = vips_foreign_load_jpeg_stream_header;
@ -224,6 +224,7 @@ vips_foreign_load_jpeg_stream_class_init(
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadJpegStream, input ),
VIPS_TYPE_STREAM_INPUT );
}
static void

View File

@ -196,7 +196,7 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class )
VIPS_ARG_BOOL( class, "optimize_scans", 17,
_( "Optimize scans" ),
_( "Split the spectrum of DCT coefficients into separate scans" ),
_( "Split spectrum of DCT coefficients into separate scans" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_scans ),
FALSE );
@ -216,6 +216,68 @@ vips_foreign_save_jpeg_init( VipsForeignSaveJpeg *jpeg )
jpeg->Q = 75;
}
typedef struct _VipsForeignSaveJpegStream {
VipsForeignSaveJpeg parent_object;
VipsStreamOutput *output;
} VipsForeignSaveJpegStream;
typedef VipsForeignSaveJpegClass VipsForeignSaveJpegStreamClass;
G_DEFINE_TYPE( VipsForeignSaveJpegStream, vips_foreign_save_jpeg_stream,
vips_foreign_save_jpeg_get_type() );
static int
vips_foreign_save_jpeg_stream_build( VipsObject *object )
{
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
VipsForeignSaveJpegStream *stream =
(VipsForeignSaveJpegStream *) object;
if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_stream_parent_class )->
build( object ) )
return( -1 );
if( vips__jpeg_write_stream( save->ready, stream->output,
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
jpeg->interlace, save->strip, jpeg->no_subsample,
jpeg->trellis_quant, jpeg->overshoot_deringing,
jpeg->optimize_scans, jpeg->quant_table ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_save_jpeg_stream_class_init(
VipsForeignSaveJpegStreamClass *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 = "jpegsave_stream";
object_class->description = _( "save image to jpeg stream" );
object_class->build = vips_foreign_save_jpeg_stream_build;
VIPS_ARG_OBJECT( class, "output", 1,
_( "Output" ),
_( "Stream to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveJpegStream, output ),
VIPS_TYPE_STREAM_OUTPUT );
}
static void
vips_foreign_save_jpeg_stream_init( VipsForeignSaveJpegStream *stream )
{
}
typedef struct _VipsForeignSaveJpegFile {
VipsForeignSaveJpeg parent_object;
@ -237,16 +299,23 @@ vips_foreign_save_jpeg_file_build( VipsObject *object )
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
VipsForeignSaveJpegFile *file = (VipsForeignSaveJpegFile *) object;
VipsStreamOutput *output;
if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_file_parent_class )->
build( object ) )
return( -1 );
if( vips__jpeg_write_file( save->ready, file->filename,
if( !(output = vips_stream_output_new_from_filename( file->filename )) )
return( -1 );
if( vips__jpeg_write_stream( save->ready, output,
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
jpeg->interlace, save->strip, jpeg->no_subsample,
jpeg->trellis_quant, jpeg->overshoot_deringing,
jpeg->optimize_scans, jpeg->quant_table ) )
jpeg->optimize_scans, jpeg->quant_table ) ) {
VIPS_UNREF( output );
return( -1 );
}
VIPS_UNREF( output );
return( 0 );
}
@ -298,27 +367,31 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object )
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
VipsForeignSaveJpegBuffer *file = (VipsForeignSaveJpegBuffer *) object;
void *obuf;
size_t olen;
VipsStreamOutput *output;
VipsBlob *blob;
if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_buffer_parent_class )->
build( object ) )
return( -1 );
if( vips__jpeg_write_buffer( save->ready,
&obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding,
jpeg->interlace, save->strip, jpeg->no_subsample,
jpeg->trellis_quant, jpeg->overshoot_deringing,
jpeg->optimize_scans, jpeg->quant_table ) )
if( !(output = vips_stream_output_new_memory()) )
return( -1 );
/* obuf is a g_free() buffer, not vips_free().
*/
blob = vips_blob_new( (VipsCallbackFn) g_free, obuf, olen );
if( vips__jpeg_write_stream( save->ready, output,
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
jpeg->interlace, save->strip, jpeg->no_subsample,
jpeg->trellis_quant, jpeg->overshoot_deringing,
jpeg->optimize_scans, jpeg->quant_table ) ) {
VIPS_UNREF( output );
return( -1 );
}
g_object_get( output, "blob", &blob, NULL );
g_object_set( file, "buffer", blob, NULL );
vips_area_unref( VIPS_AREA( blob ) );
VIPS_UNREF( output );
return( 0 );
}
@ -365,30 +438,39 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object )
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
void *obuf;
VipsStreamOutput *output;
VipsBlob *blob;
const unsigned char *obuf;
size_t olen;
if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_mime_parent_class )->
build( object ) )
return( -1 );
if( vips__jpeg_write_buffer( save->ready,
&obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding,
jpeg->interlace, save->strip, jpeg->no_subsample,
jpeg->trellis_quant, jpeg->overshoot_deringing,
jpeg->optimize_scans, jpeg->quant_table ) )
if( !(output = vips_stream_output_new_memory()) )
return( -1 );
if( vips__jpeg_write_stream( save->ready, output,
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
jpeg->interlace, save->strip, jpeg->no_subsample,
jpeg->trellis_quant, jpeg->overshoot_deringing,
jpeg->optimize_scans, jpeg->quant_table ) ) {
VIPS_UNREF( output );
return( -1 );
}
g_object_get( output, "blob", &blob, NULL );
obuf = vips_blob_get( blob, &olen );
printf( "Content-length: %zu\r\n", olen );
printf( "Content-type: image/jpeg\r\n" );
printf( "\r\n" );
if( fwrite( obuf, sizeof( char ), olen, stdout ) != olen ) {
vips_error( "VipsJpeg", "%s", _( "error writing output" ) );
return( -1 );
}
(void) fwrite( obuf, sizeof( char ), olen, stdout );
fflush( stdout );
g_free( obuf );
vips_area_unref( VIPS_AREA( blob ) );
VIPS_UNREF( output );
return( 0 );
}
@ -527,6 +609,44 @@ vips_jpegsave( VipsImage *in, const char *filename, ... )
return( result );
}
/**
* vips_jpegsave_stream: (method)
* @in: image to save
* @output: save image to this stream
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @Q: %gint, quality factor
* * @profile: filename of ICC profile to attach
* * @optimize_coding: %gboolean, compute optimal Huffman coding tables
* * @interlace: %gboolean, write an interlaced (progressive) jpeg
* * @strip: %gboolean, remove all metadata from image
* * @no_subsample: %gboolean, disable chroma subsampling
* * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
* * @quant_table: %gint, quantization table index
*
* As vips_jpegsave(), but save to a stream.
*
* See also: vips_jpegsave(), vips_image_write_to_stream().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_jpegsave_stream( VipsImage *in, VipsStreamOutput *output, ... )
{
va_list ap;
int result;
va_start( ap, output );
result = vips_call_split( "jpegsave_stream", ap, in, output );
va_end( ap );
return( result );
}
/**
* vips_jpegsave_buffer: (method)
* @in: image to save

View File

@ -172,14 +172,8 @@ extern const char *vips__rad_suffs[];
extern const char *vips__jpeg_suffs[];
int vips__jpeg_write_file( VipsImage *in,
const char *filename, int Q, const char *profile,
gboolean optimize_coding, gboolean progressive, gboolean strip,
gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans,
int quant_table );
int vips__jpeg_write_buffer( VipsImage *in,
void **obuf, size_t *olen, int Q, const char *profile,
int vips__jpeg_write_stream( VipsImage *in, VipsStreamOutput *output,
int Q, const char *profile,
gboolean optimize_coding, gboolean progressive, gboolean strip,
gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans,

View File

@ -207,7 +207,6 @@ static void
write_destroy( Write *write )
{
jpeg_destroy_compress( &write->cinfo );
VIPS_FREEF( fclose, write->eman.fp );
VIPS_FREE( write->row_pointer );
VIPS_UNREF( write->inverted );
@ -680,59 +679,8 @@ write_vips( Write *write, int qfac, const char *profile,
return( 0 );
}
/* Write an image to a jpeg file.
*/
int
vips__jpeg_write_file( VipsImage *in,
const char *filename, int Q, const char *profile,
gboolean optimize_coding, gboolean progressive, gboolean strip,
gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table )
{
Write *write;
#define STREAM_BUFFER_SIZE (4096)
if( !(write = write_new( in )) )
return( -1 );
if( setjmp( write->eman.jmp ) ) {
/* Here for longjmp() from new_error_exit().
*/
write_destroy( write );
return( -1 );
}
/* Can't do this in write_new(), has to be after we've made the
* setjmp().
*/
jpeg_create_compress( &write->cinfo );
/* Make output.
*/
if( !(write->eman.fp = vips__file_open_write( filename, FALSE )) ) {
write_destroy( write );
return( -1 );
}
jpeg_stdio_dest( &write->cinfo, write->eman.fp );
/* Convert!
*/
if( write_vips( write,
Q, profile, optimize_coding, progressive, strip, no_subsample,
trellis_quant, overshoot_deringing, optimize_scans,
quant_table ) ) {
write_destroy( write );
return( -1 );
}
write_destroy( write );
return( 0 );
}
/* Just like the above, but we write to a memory buffer.
*
* A memory buffer for the compressed image.
*/
typedef struct {
/* Public jpeg fields.
*/
@ -743,113 +691,80 @@ typedef struct {
/* Build the output area here.
*/
VipsDbuf dbuf;
VipsStreamOutput *output;
/* Write the generated area here.
/* Our output buffer.
*/
void **obuf; /* Allocated buffer, and size */
size_t *olen;
} OutputBuffer;
unsigned char buf[STREAM_BUFFER_SIZE];
} Dest;
/* Buffer full method ... allocate a new output block. This is only called
* when the output area is exactly full.
/* Buffer full method. This is only called when the output area is exactly
* full.
*/
METHODDEF(boolean)
static jboolean
empty_output_buffer( j_compress_ptr cinfo )
{
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
Dest *dest = (Dest *) cinfo->dest;
size_t size;
if( vips_stream_output_write( dest->output,
dest->buf, STREAM_BUFFER_SIZE ) )
ERREXIT( cinfo, JERR_FILE_WRITE );
vips_dbuf_allocate( &buf->dbuf, 10000 );
buf->pub.next_output_byte =
(JOCTET *) vips_dbuf_get_write( &buf->dbuf, &size );
buf->pub.free_in_buffer = size;
dest->pub.next_output_byte = dest->buf;
dest->pub.free_in_buffer = STREAM_BUFFER_SIZE;
/* TRUE means we've made some more space.
*/
return( 1 );
return( TRUE );
}
/* Init dest method.
*/
METHODDEF(void)
static void
init_destination( j_compress_ptr cinfo )
{
empty_output_buffer( cinfo );
Dest *dest = (Dest *) cinfo->dest;
dest->pub.next_output_byte = dest->buf;
dest->pub.free_in_buffer = STREAM_BUFFER_SIZE;
}
/* Free the buffer writer.
/* Flush any remaining bytes to the output.
*/
static void
buf_destroy( j_compress_ptr cinfo )
{
if( cinfo->dest ) {
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
vips_dbuf_destroy( &buf->dbuf );
}
}
/* Cleanup. Copy the set of blocks out as a big lump. This is only called by
* libjpeg on successful write --- you must call buf_destroy() explicitly to
* release resources.
*/
METHODDEF(void)
term_destination( j_compress_ptr cinfo )
{
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
Dest *dest = (Dest *) cinfo->dest;
size_t size;
if( vips_stream_output_write( dest->output,
dest->buf, STREAM_BUFFER_SIZE - dest->pub.free_in_buffer ) )
ERREXIT( cinfo, JERR_FILE_WRITE );
/* We probably won't have filled the area that was last allocated in
* empty_output_buffer(). Chop the data size down to the length that
* was actually written.
*/
vips_dbuf_seek( &buf->dbuf, -buf->pub.free_in_buffer, SEEK_END );
vips_dbuf_truncate( &buf->dbuf );
*(buf->obuf) = vips_dbuf_steal( &buf->dbuf, &size );
*(buf->olen) = size;
vips_stream_output_finish( dest->output );
}
/* Set dest to one of our objects.
*/
static void
buf_dest( j_compress_ptr cinfo, void **obuf, size_t *olen )
stream_dest( j_compress_ptr cinfo, VipsStreamOutput *output )
{
OutputBuffer *buf;
Dest *dest;
/* The destination object is made permanent so that multiple JPEG
* images can be written to the same file without re-executing
* jpeg_stdio_dest. This makes it dangerous to use this manager and
* a different destination manager serially with the same JPEG object,
* because their private object sizes may be different.
*
* Caveat programmer.
*/
if( !cinfo->dest ) { /* first time for this JPEG object? */
cinfo->dest = (struct jpeg_destination_mgr *)
(*cinfo->mem->alloc_small)
( (j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof( OutputBuffer ) );
sizeof( Dest ) );
}
buf = (OutputBuffer *) cinfo->dest;
buf->pub.init_destination = init_destination;
buf->pub.empty_output_buffer = empty_output_buffer;
buf->pub.term_destination = term_destination;
/* Save output parameters.
*/
vips_dbuf_init( &buf->dbuf );
buf->obuf = obuf;
buf->olen = olen;
dest = (Dest *) cinfo->dest;
dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination;
dest->output = output;
}
int
vips__jpeg_write_buffer( VipsImage *in,
void **obuf, size_t *olen, int Q, const char *profile,
vips__jpeg_write_stream( VipsImage *in, VipsStreamOutput *output,
int Q, const char *profile,
gboolean optimize_coding, gboolean progressive,
gboolean strip, gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table )
@ -859,17 +774,11 @@ vips__jpeg_write_buffer( VipsImage *in,
if( !(write = write_new( in )) )
return( -1 );
/* Clear output parameters.
*/
*obuf = NULL;
*olen = 0;
/* Make jpeg compression object.
*/
if( setjmp( write->eman.jmp ) ) {
/* Here for longjmp() during write_vips().
*/
buf_destroy( &write->cinfo );
write_destroy( write );
return( -1 );
@ -878,7 +787,7 @@ vips__jpeg_write_buffer( VipsImage *in,
/* Attach output.
*/
buf_dest( &write->cinfo, obuf, olen );
stream_dest( &write->cinfo, output );
/* Convert! Write errors come back here as an error return.
*/
@ -886,12 +795,9 @@ vips__jpeg_write_buffer( VipsImage *in,
Q, profile, optimize_coding, progressive, strip, no_subsample,
trellis_quant, overshoot_deringing, optimize_scans,
quant_table ) ) {
buf_destroy( &write->cinfo );
write_destroy( write );
return( -1 );
}
buf_destroy( &write->cinfo );
write_destroy( write );
return( 0 );

View File

@ -173,7 +173,7 @@ VipsStreamInput *vips_stream_input_new_from_memory( const void *data,
size_t size );
ssize_t vips_stream_input_read( VipsStreamInput *input,
unsigned char *buffer, size_t length );
unsigned char *data, size_t length );
int vips_stream_input_rewind( VipsStreamInput *input );
void vips_stream_input_decode( VipsStreamInput *input );
gboolean vips_stream_input_eof( VipsStreamInput *input );
@ -194,14 +194,18 @@ unsigned char *vips_stream_input_sniff( VipsStreamInput *input, size_t length );
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_STREAM_OUTPUT, VipsStreamOutputClass ))
/* Read or output to something like a socket or pipe.
/* Output to something like a socket, pipe or memory area.
*/
typedef struct _VipsStreamOutput {
VipsStream parent_object;
/*< private >*/
/* For memory output, the blob we write to.
/* Write memory output here.
*/
GByteArray *memory;
/* And return memory via this blob.
*/
VipsBlob *blob;
@ -220,8 +224,10 @@ GType vips_stream_output_get_type( void );
VipsStreamOutput *vips_stream_output_new_from_descriptor( int descriptor );
VipsStreamOutput *vips_stream_output_new_from_filename( const char *filename );
VipsStreamOutput *vips_stream_output_new_memory( void );
int vips_stream_output_write( VipsStreamOutput *stream,
const unsigned char *buffer, size_t buffer_size );
const unsigned char *data, size_t length );
void vips_stream_output_finish( VipsStreamOutput *output );
#ifdef __cplusplus
}

View File

@ -39,6 +39,8 @@
* - add seekable descriptors
* - can we test for mmapable and seekable?
* - do we need eof?
* - need a close() vfunc? output is a bit ugly, and node streams might need
* it
* - can we really change all behaviour in the subclass? will we need map and
* seek as well as read and rewind?
*/
@ -111,6 +113,20 @@
G_DEFINE_ABSTRACT_TYPE( VipsStream, vips_stream, VIPS_TYPE_OBJECT );
static void
vips_stream_close( VipsStream *stream )
{
if( stream->close_descriptor >= 0 ) {
close( stream->close_descriptor );
stream->close_descriptor = -1;
}
if( stream->tracked_descriptor >= 0 ) {
vips_tracked_close( stream->tracked_descriptor );
stream->tracked_descriptor = -1;
}
}
static void
vips_stream_finalize( GObject *gobject )
{
@ -122,16 +138,7 @@ vips_stream_finalize( GObject *gobject )
VIPS_DEBUG_MSG( "\n" );
#endif /*VIPS_DEBUG*/
if( stream->close_descriptor >= 0 ) {
close( stream->close_descriptor );
stream->close_descriptor = -1;
}
if( stream->tracked_descriptor >= 0 ) {
vips_tracked_close( stream->tracked_descriptor );
stream->tracked_descriptor = -1;
}
vips_stream_close( stream );
VIPS_FREE( stream->filename );
G_OBJECT_CLASS( vips_stream_parent_class )->finalize( gobject );
@ -222,8 +229,10 @@ vips_stream_input_build( VipsObject *object )
input->rewindable = TRUE;
}
if( vips_object_argument_isset( object, "descriptor" ) )
if( vips_object_argument_isset( object, "descriptor" ) ) {
stream->descriptor = dup( stream->descriptor );
stream->close_descriptor = stream->descriptor;
}
if( vips_object_argument_isset( object, "blob" ) )
input->rewindable = TRUE;
@ -242,7 +251,7 @@ vips_stream_input_build( VipsObject *object )
static ssize_t
vips_stream_input_read_real( VipsStreamInput *input,
unsigned char *buffer, size_t length )
unsigned char *data, size_t length )
{
VipsStream *stream = VIPS_STREAM( input );
@ -256,12 +265,12 @@ vips_stream_input_read_real( VipsStreamInput *input,
if( available <= 0 )
return( 0 );
memcpy( buffer, area->data + input->read_position, available );
memcpy( data, area->data + input->read_position, available );
return( available );
}
else if( stream->descriptor != -1 ) {
return( read( stream->descriptor, buffer, length ) );
return( read( stream->descriptor, data, length ) );
}
else {
g_assert( 0 );
@ -594,6 +603,18 @@ vips_stream_input_sniff( VipsStreamInput *input, size_t length )
G_DEFINE_TYPE( VipsStreamOutput, vips_stream_output, VIPS_TYPE_STREAM );
static void
vips_stream_output_finalize( GObject *gobject )
{
VipsStreamOutput *output = VIPS_STREAM_OUTPUT( gobject );
VIPS_DEBUG_MSG( "vips_stream_output_finalize:\n" );
VIPS_FREEF( g_byte_array_unref, output->memory );
G_OBJECT_CLASS( vips_stream_output_parent_class )->finalize( gobject );
}
static int
vips_stream_output_build( VipsObject *object )
{
@ -627,20 +648,44 @@ vips_stream_output_build( VipsObject *object )
stream->tracked_descriptor = fd;
stream->descriptor = fd;
}
if( vips_object_argument_isset( object, "descriptor" ) )
else if( vips_object_argument_isset( object, "descriptor" ) ) {
stream->descriptor = dup( stream->descriptor );
stream->close_descriptor = stream->descriptor;
}
else {
output->memory = g_byte_array_new();
}
return( 0 );
}
static ssize_t
vips_stream_output_write_real( VipsStreamOutput *output,
const unsigned char *data, size_t length )
{
VipsStream *stream = VIPS_STREAM( output );
ssize_t len;
VIPS_DEBUG_MSG( "vips_stream_output_write_real: %zd bytes\n", length );
if( output->memory ) {
g_byte_array_append( output->memory, data, length );
len = length;
}
else
len = write( stream->descriptor, data, length );
return( len );
}
static void
vips_stream_output_class_init( VipsStreamOutputClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
gobject_class->finalize = vips_stream_input_finalize;
gobject_class->finalize = vips_stream_output_finalize;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
@ -649,6 +694,8 @@ vips_stream_output_class_init( VipsStreamOutputClass *class )
object_class->build = vips_stream_output_build;
class->write = vips_stream_output_write_real;
VIPS_ARG_BOXED( class, "blob", 1,
_( "Blob" ),
_( "Blob to save to" ),
@ -728,41 +775,86 @@ vips_stream_output_new_from_filename( const char *filename )
return( stream );
}
int
vips_stream_output_write( VipsStreamOutput *stream,
const unsigned char *buffer, size_t buffer_size )
/**
* vips_stream_output_new_memory:
*
* Optional args:
*
* @blob: #VipsBlob, memory area containing output
*
* Create a stream which will output to a memory area. Use @blob to get
* memory output, if this is a memory stream.
*
* See also: vips_stream_output_write().
*
* Returns: a new #VipsStream
*/
VipsStreamOutput *
vips_stream_output_new_memory( void )
{
VipsStreamOutputClass *class = VIPS_STREAM_OUTPUT_GET_CLASS( stream );
VipsStreamOutput *stream;
while( buffer_size > 0 ) {
ssize_t len;
VIPS_DEBUG_MSG( "vips_stream_output_new_memory:\n" );
if( class->write )
len = class->write( stream, buffer, buffer_size );
else
len = write( VIPS_STREAM( stream )->descriptor,
buffer, buffer_size );
stream = VIPS_STREAM_OUTPUT(
g_object_new( VIPS_TYPE_STREAM_OUTPUT,
NULL ) );
#ifdef VIPS_DEBUG
if( len > 0 )
VIPS_DEBUG_MSG( "vips_stream_output_write: "
"written %zd bytes\n", len );
#endif /*VIPS_DEBUG*/
if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
VIPS_UNREF( stream );
return( NULL );
}
/* len == 0 isn't strictly an error, but we treat it as one to
return( stream );
}
int
vips_stream_output_write( VipsStreamOutput *output,
const unsigned char *data, size_t length )
{
VipsStreamOutputClass *class = VIPS_STREAM_OUTPUT_GET_CLASS( output );
VIPS_DEBUG_MSG( "vips_stream_output_write: %zd bytes\n", length );
while( length > 0 ) {
ssize_t n;
n = class->write( output, 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( len <= 0 ) {
vips_error_system( errno,
VIPS_OBJECT( stream )->nickname,
if( n <= 0 ) {
vips_error_system( errno, STREAM_NAME( output ),
"%s", _( "write error" ) );
return( -1 );
}
buffer_size -= len;
buffer += len;
length -= n;
data += n;
}
return( 0 );
}
void
vips_stream_output_finish( VipsStreamOutput *output )
{
VIPS_DEBUG_MSG( "vips_stream_output_finish:\n" );
if( output->memory ) {
unsigned char *data;
size_t length;
length = output->memory->len;
data = g_byte_array_free( output->memory, FALSE );
output->memory = NULL;
g_object_set( output,
"blob", vips_blob_new( (VipsCallbackFn) g_free,
data, length ),
NULL );
}
vips_stream_close( VIPS_STREAM( output ) );
}