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:
parent
20cb0da247
commit
d991b73ac5
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user