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; gobject_class->get_property = vips_object_get_property;
object_class->nickname = "jpegload_stream"; 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->is_a_stream = vips_foreign_load_jpeg_stream_is_a;
load_class->header = vips_foreign_load_jpeg_stream_header; load_class->header = vips_foreign_load_jpeg_stream_header;
@ -224,6 +224,7 @@ vips_foreign_load_jpeg_stream_class_init(
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadJpegStream, input ), G_STRUCT_OFFSET( VipsForeignLoadJpegStream, input ),
VIPS_TYPE_STREAM_INPUT ); VIPS_TYPE_STREAM_INPUT );
} }
static void static void

View File

@ -196,7 +196,7 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class )
VIPS_ARG_BOOL( class, "optimize_scans", 17, VIPS_ARG_BOOL( class, "optimize_scans", 17,
_( "Optimize scans" ), _( "Optimize scans" ),
_( "Split the spectrum of DCT coefficients into separate scans" ), _( "Split spectrum of DCT coefficients into separate scans" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_scans ), G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_scans ),
FALSE ); FALSE );
@ -216,6 +216,68 @@ vips_foreign_save_jpeg_init( VipsForeignSaveJpeg *jpeg )
jpeg->Q = 75; 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 { typedef struct _VipsForeignSaveJpegFile {
VipsForeignSaveJpeg parent_object; VipsForeignSaveJpeg parent_object;
@ -237,16 +299,23 @@ vips_foreign_save_jpeg_file_build( VipsObject *object )
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object; VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
VipsForeignSaveJpegFile *file = (VipsForeignSaveJpegFile *) object; VipsForeignSaveJpegFile *file = (VipsForeignSaveJpegFile *) object;
VipsStreamOutput *output;
if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_file_parent_class )-> if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_file_parent_class )->
build( object ) ) build( object ) )
return( -1 ); 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->Q, jpeg->profile, jpeg->optimize_coding,
jpeg->interlace, save->strip, jpeg->no_subsample, jpeg->interlace, save->strip, jpeg->no_subsample,
jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->trellis_quant, jpeg->overshoot_deringing,
jpeg->optimize_scans, jpeg->quant_table ) ) jpeg->optimize_scans, jpeg->quant_table ) ) {
VIPS_UNREF( output );
return( -1 ); return( -1 );
}
VIPS_UNREF( output );
return( 0 ); return( 0 );
} }
@ -298,27 +367,31 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object )
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object; VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
VipsForeignSaveJpegBuffer *file = (VipsForeignSaveJpegBuffer *) object; VipsForeignSaveJpegBuffer *file = (VipsForeignSaveJpegBuffer *) object;
void *obuf; VipsStreamOutput *output;
size_t olen;
VipsBlob *blob; VipsBlob *blob;
if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_buffer_parent_class )-> if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_buffer_parent_class )->
build( object ) ) build( object ) )
return( -1 ); return( -1 );
if( vips__jpeg_write_buffer( save->ready, if( !(output = vips_stream_output_new_memory()) )
&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 ) )
return( -1 ); return( -1 );
/* obuf is a g_free() buffer, not vips_free(). if( vips__jpeg_write_stream( save->ready, output,
*/ jpeg->Q, jpeg->profile, jpeg->optimize_coding,
blob = vips_blob_new( (VipsCallbackFn) g_free, obuf, olen ); 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 ); g_object_set( file, "buffer", blob, NULL );
vips_area_unref( VIPS_AREA( blob ) ); vips_area_unref( VIPS_AREA( blob ) );
VIPS_UNREF( output );
return( 0 ); return( 0 );
} }
@ -365,30 +438,39 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object )
VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object; VipsForeignSaveJpeg *jpeg = (VipsForeignSaveJpeg *) object;
void *obuf; VipsStreamOutput *output;
VipsBlob *blob;
const unsigned char *obuf;
size_t olen; size_t olen;
if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_mime_parent_class )-> if( VIPS_OBJECT_CLASS( vips_foreign_save_jpeg_mime_parent_class )->
build( object ) ) build( object ) )
return( -1 ); return( -1 );
if( vips__jpeg_write_buffer( save->ready, if( !(output = vips_stream_output_new_memory()) )
&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 ) )
return( -1 ); 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-length: %zu\r\n", olen );
printf( "Content-type: image/jpeg\r\n" ); printf( "Content-type: image/jpeg\r\n" );
printf( "\r\n" ); printf( "\r\n" );
if( fwrite( obuf, sizeof( char ), olen, stdout ) != olen ) { (void) fwrite( obuf, sizeof( char ), olen, stdout );
vips_error( "VipsJpeg", "%s", _( "error writing output" ) );
return( -1 );
}
fflush( stdout ); fflush( stdout );
g_free( obuf ); vips_area_unref( VIPS_AREA( blob ) );
VIPS_UNREF( output );
return( 0 ); return( 0 );
} }
@ -527,6 +609,44 @@ vips_jpegsave( VipsImage *in, const char *filename, ... )
return( result ); 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) * vips_jpegsave_buffer: (method)
* @in: image to save * @in: image to save

View File

@ -172,14 +172,8 @@ extern const char *vips__rad_suffs[];
extern const char *vips__jpeg_suffs[]; extern const char *vips__jpeg_suffs[];
int vips__jpeg_write_file( VipsImage *in, int vips__jpeg_write_stream( VipsImage *in, VipsStreamOutput *output,
const char *filename, int Q, const char *profile, 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,
gboolean optimize_coding, gboolean progressive, gboolean strip, gboolean optimize_coding, gboolean progressive, gboolean strip,
gboolean no_subsample, gboolean trellis_quant, gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans, gboolean overshoot_deringing, gboolean optimize_scans,

View File

@ -207,7 +207,6 @@ static void
write_destroy( Write *write ) write_destroy( Write *write )
{ {
jpeg_destroy_compress( &write->cinfo ); jpeg_destroy_compress( &write->cinfo );
VIPS_FREEF( fclose, write->eman.fp );
VIPS_FREE( write->row_pointer ); VIPS_FREE( write->row_pointer );
VIPS_UNREF( write->inverted ); VIPS_UNREF( write->inverted );
@ -680,59 +679,8 @@ write_vips( Write *write, int qfac, const char *profile,
return( 0 ); return( 0 );
} }
/* Write an image to a jpeg file. #define STREAM_BUFFER_SIZE (4096)
*/
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;
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 { typedef struct {
/* Public jpeg fields. /* Public jpeg fields.
*/ */
@ -743,113 +691,80 @@ typedef struct {
/* Build the output area here. /* Build the output area here.
*/ */
VipsDbuf dbuf; VipsStreamOutput *output;
/* Write the generated area here. /* Our output buffer.
*/ */
void **obuf; /* Allocated buffer, and size */ unsigned char buf[STREAM_BUFFER_SIZE];
size_t *olen; } Dest;
} OutputBuffer;
/* Buffer full method ... allocate a new output block. This is only called /* Buffer full method. This is only called when the output area is exactly
* when the output area is exactly full. * full.
*/ */
METHODDEF(boolean) static jboolean
empty_output_buffer( j_compress_ptr cinfo ) 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 ); dest->pub.next_output_byte = dest->buf;
buf->pub.next_output_byte = dest->pub.free_in_buffer = STREAM_BUFFER_SIZE;
(JOCTET *) vips_dbuf_get_write( &buf->dbuf, &size );
buf->pub.free_in_buffer = size;
/* TRUE means we've made some more space. return( TRUE );
*/
return( 1 );
} }
/* Init dest method. /* Init dest method.
*/ */
METHODDEF(void) static void
init_destination( j_compress_ptr cinfo ) 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 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 ) 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 vips_stream_output_finish( dest->output );
* 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;
} }
/* Set dest to one of our objects. /* Set dest to one of our objects.
*/ */
static void 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? */ if( !cinfo->dest ) { /* first time for this JPEG object? */
cinfo->dest = (struct jpeg_destination_mgr *) cinfo->dest = (struct jpeg_destination_mgr *)
(*cinfo->mem->alloc_small) (*cinfo->mem->alloc_small)
( (j_common_ptr) cinfo, JPOOL_PERMANENT, ( (j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof( OutputBuffer ) ); sizeof( Dest ) );
} }
buf = (OutputBuffer *) cinfo->dest; dest = (Dest *) cinfo->dest;
buf->pub.init_destination = init_destination; dest->pub.init_destination = init_destination;
buf->pub.empty_output_buffer = empty_output_buffer; dest->pub.empty_output_buffer = empty_output_buffer;
buf->pub.term_destination = term_destination; dest->pub.term_destination = term_destination;
dest->output = output;
/* Save output parameters.
*/
vips_dbuf_init( &buf->dbuf );
buf->obuf = obuf;
buf->olen = olen;
} }
int int
vips__jpeg_write_buffer( VipsImage *in, vips__jpeg_write_stream( VipsImage *in, VipsStreamOutput *output,
void **obuf, size_t *olen, int Q, const char *profile, int Q, const char *profile,
gboolean optimize_coding, gboolean progressive, gboolean optimize_coding, gboolean progressive,
gboolean strip, gboolean no_subsample, gboolean trellis_quant, gboolean strip, gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ) gboolean overshoot_deringing, gboolean optimize_scans, int quant_table )
@ -859,17 +774,11 @@ vips__jpeg_write_buffer( VipsImage *in,
if( !(write = write_new( in )) ) if( !(write = write_new( in )) )
return( -1 ); return( -1 );
/* Clear output parameters.
*/
*obuf = NULL;
*olen = 0;
/* Make jpeg compression object. /* Make jpeg compression object.
*/ */
if( setjmp( write->eman.jmp ) ) { if( setjmp( write->eman.jmp ) ) {
/* Here for longjmp() during write_vips(). /* Here for longjmp() during write_vips().
*/ */
buf_destroy( &write->cinfo );
write_destroy( write ); write_destroy( write );
return( -1 ); return( -1 );
@ -878,7 +787,7 @@ vips__jpeg_write_buffer( VipsImage *in,
/* Attach output. /* Attach output.
*/ */
buf_dest( &write->cinfo, obuf, olen ); stream_dest( &write->cinfo, output );
/* Convert! Write errors come back here as an error return. /* 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, Q, profile, optimize_coding, progressive, strip, no_subsample,
trellis_quant, overshoot_deringing, optimize_scans, trellis_quant, overshoot_deringing, optimize_scans,
quant_table ) ) { quant_table ) ) {
buf_destroy( &write->cinfo );
write_destroy( write ); write_destroy( write );
return( -1 ); return( -1 );
} }
buf_destroy( &write->cinfo );
write_destroy( write ); write_destroy( write );
return( 0 ); return( 0 );

View File

@ -173,7 +173,7 @@ VipsStreamInput *vips_stream_input_new_from_memory( const void *data,
size_t size ); size_t size );
ssize_t vips_stream_input_read( VipsStreamInput *input, 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 ); int vips_stream_input_rewind( VipsStreamInput *input );
void vips_stream_input_decode( VipsStreamInput *input ); void vips_stream_input_decode( VipsStreamInput *input );
gboolean vips_stream_input_eof( 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), \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_STREAM_OUTPUT, VipsStreamOutputClass )) 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 { typedef struct _VipsStreamOutput {
VipsStream parent_object; VipsStream parent_object;
/*< private >*/ /*< private >*/
/* For memory output, the blob we write to. /* Write memory output here.
*/
GByteArray *memory;
/* And return memory via this blob.
*/ */
VipsBlob *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_descriptor( int descriptor );
VipsStreamOutput *vips_stream_output_new_from_filename( const char *filename ); VipsStreamOutput *vips_stream_output_new_from_filename( const char *filename );
VipsStreamOutput *vips_stream_output_new_memory( void );
int vips_stream_output_write( VipsStreamOutput *stream, 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 #ifdef __cplusplus
} }

View File

@ -39,6 +39,8 @@
* - add seekable descriptors * - add seekable descriptors
* - can we test for mmapable and seekable? * - can we test for mmapable and seekable?
* - do we need eof? * - 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 * - can we really change all behaviour in the subclass? will we need map and
* seek as well as read and rewind? * seek as well as read and rewind?
*/ */
@ -111,6 +113,20 @@
G_DEFINE_ABSTRACT_TYPE( VipsStream, vips_stream, VIPS_TYPE_OBJECT ); 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 static void
vips_stream_finalize( GObject *gobject ) vips_stream_finalize( GObject *gobject )
{ {
@ -122,16 +138,7 @@ vips_stream_finalize( GObject *gobject )
VIPS_DEBUG_MSG( "\n" ); VIPS_DEBUG_MSG( "\n" );
#endif /*VIPS_DEBUG*/ #endif /*VIPS_DEBUG*/
if( stream->close_descriptor >= 0 ) { vips_stream_close( stream );
close( stream->close_descriptor );
stream->close_descriptor = -1;
}
if( stream->tracked_descriptor >= 0 ) {
vips_tracked_close( stream->tracked_descriptor );
stream->tracked_descriptor = -1;
}
VIPS_FREE( stream->filename ); VIPS_FREE( stream->filename );
G_OBJECT_CLASS( vips_stream_parent_class )->finalize( gobject ); G_OBJECT_CLASS( vips_stream_parent_class )->finalize( gobject );
@ -222,8 +229,10 @@ vips_stream_input_build( VipsObject *object )
input->rewindable = TRUE; 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; stream->close_descriptor = stream->descriptor;
}
if( vips_object_argument_isset( object, "blob" ) ) if( vips_object_argument_isset( object, "blob" ) )
input->rewindable = TRUE; input->rewindable = TRUE;
@ -242,7 +251,7 @@ vips_stream_input_build( VipsObject *object )
static ssize_t static ssize_t
vips_stream_input_read_real( VipsStreamInput *input, vips_stream_input_read_real( VipsStreamInput *input,
unsigned char *buffer, size_t length ) unsigned char *data, size_t length )
{ {
VipsStream *stream = VIPS_STREAM( input ); VipsStream *stream = VIPS_STREAM( input );
@ -256,12 +265,12 @@ vips_stream_input_read_real( VipsStreamInput *input,
if( available <= 0 ) if( available <= 0 )
return( 0 ); return( 0 );
memcpy( buffer, area->data + input->read_position, available ); memcpy( data, area->data + input->read_position, available );
return( available ); return( available );
} }
else if( stream->descriptor != -1 ) { else if( stream->descriptor != -1 ) {
return( read( stream->descriptor, buffer, length ) ); return( read( stream->descriptor, data, length ) );
} }
else { else {
g_assert( 0 ); 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 ); 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 static int
vips_stream_output_build( VipsObject *object ) vips_stream_output_build( VipsObject *object )
{ {
@ -627,20 +648,44 @@ vips_stream_output_build( VipsObject *object )
stream->tracked_descriptor = fd; stream->tracked_descriptor = fd;
stream->descriptor = fd; stream->descriptor = fd;
} }
else if( vips_object_argument_isset( object, "descriptor" ) ) {
if( vips_object_argument_isset( object, "descriptor" ) ) stream->descriptor = dup( stream->descriptor );
stream->close_descriptor = stream->descriptor; stream->close_descriptor = stream->descriptor;
}
else {
output->memory = g_byte_array_new();
}
return( 0 ); 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 static void
vips_stream_output_class_init( VipsStreamOutputClass *class ) vips_stream_output_class_init( VipsStreamOutputClass *class )
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS( class ); GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = VIPS_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->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_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; object_class->build = vips_stream_output_build;
class->write = vips_stream_output_write_real;
VIPS_ARG_BOXED( class, "blob", 1, VIPS_ARG_BOXED( class, "blob", 1,
_( "Blob" ), _( "Blob" ),
_( "Blob to save to" ), _( "Blob to save to" ),
@ -728,41 +775,86 @@ vips_stream_output_new_from_filename( const char *filename )
return( stream ); return( stream );
} }
int /**
vips_stream_output_write( VipsStreamOutput *stream, * vips_stream_output_new_memory:
const unsigned char *buffer, size_t buffer_size ) *
* 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 ) { VIPS_DEBUG_MSG( "vips_stream_output_new_memory:\n" );
ssize_t len;
if( class->write ) stream = VIPS_STREAM_OUTPUT(
len = class->write( stream, buffer, buffer_size ); g_object_new( VIPS_TYPE_STREAM_OUTPUT,
else NULL ) );
len = write( VIPS_STREAM( stream )->descriptor,
buffer, buffer_size );
#ifdef VIPS_DEBUG if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
if( len > 0 ) VIPS_UNREF( stream );
VIPS_DEBUG_MSG( "vips_stream_output_write: " return( NULL );
"written %zd bytes\n", len ); }
#endif /*VIPS_DEBUG*/
/* 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. * make sure we don't get stuck in this loop.
*/ */
if( len <= 0 ) { if( n <= 0 ) {
vips_error_system( errno, vips_error_system( errno, STREAM_NAME( output ),
VIPS_OBJECT( stream )->nickname,
"%s", _( "write error" ) ); "%s", _( "write error" ) );
return( -1 ); return( -1 );
} }
buffer_size -= len; length -= n;
buffer += len; data += n;
} }
return( 0 ); 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 ) );
}