add formatted output streams
you can printf() to output streams, implemented radiance stream write as a test
This commit is contained in:
parent
40d679f299
commit
23663e8a88
@ -1980,6 +1980,7 @@ vips_foreign_operation_init( void )
|
||||
extern GType vips_foreign_load_rad_get_type( void );
|
||||
extern GType vips_foreign_save_rad_file_get_type( void );
|
||||
extern GType vips_foreign_save_rad_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_rad_stream_get_type( void );
|
||||
extern GType vips_foreign_load_mat_get_type( void );
|
||||
extern GType vips_foreign_load_ppm_get_type( void );
|
||||
extern GType vips_foreign_save_ppm_get_type( void );
|
||||
@ -2072,6 +2073,7 @@ vips_foreign_operation_init( void )
|
||||
vips_foreign_load_rad_get_type();
|
||||
vips_foreign_save_rad_file_get_type();
|
||||
vips_foreign_save_rad_buffer_get_type();
|
||||
vips_foreign_save_rad_stream_get_type();
|
||||
#endif /*HAVE_RADIANCE*/
|
||||
|
||||
#ifdef HAVE_POPPLER
|
||||
|
@ -156,8 +156,7 @@ int vips__rad_israd( const char *filename );
|
||||
int vips__rad_header( const char *filename, VipsImage *out );
|
||||
int vips__rad_load( const char *filename, VipsImage *out );
|
||||
|
||||
int vips__rad_save( VipsImage *in, const char *filename );
|
||||
int vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen );
|
||||
int vips__rad_save( VipsImage *in, VipsStreamo *output );
|
||||
|
||||
extern const char *vips__rad_suffs[];
|
||||
|
||||
|
@ -499,18 +499,6 @@ formatval( /* get format value (return true if format) */
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fputformat( /* put out a format value */
|
||||
const char *s,
|
||||
FILE *fp
|
||||
)
|
||||
{
|
||||
fputs(FMTSTR, fp);
|
||||
fputs(s, fp);
|
||||
putc('\n', fp);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
getheader( /* get header from file */
|
||||
FILE *fp,
|
||||
@ -923,31 +911,6 @@ rle_scanline_write( COLR *scanline, int width,
|
||||
}
|
||||
}
|
||||
|
||||
/* Write a single scanline. buffer is at least MAX_LINE bytes and is used to
|
||||
* construct the RLE scanline. Don't allocate this on the stack so we don't
|
||||
* die too horribly on small-stack libc.
|
||||
*/
|
||||
static int
|
||||
scanline_write( unsigned char *buffer, COLR *scanline, int width, FILE *fp )
|
||||
{
|
||||
if( width < MINELEN ||
|
||||
width > MAXELEN )
|
||||
/* Write as a flat scanline.
|
||||
*/
|
||||
return( fwrite( scanline, sizeof( COLR ), width, fp ) - width );
|
||||
else {
|
||||
/* An RLE scanline.
|
||||
*/
|
||||
int length;
|
||||
|
||||
rle_scanline_write( scanline, width, buffer, &length );
|
||||
|
||||
g_assert( length <= MAX_LINE );
|
||||
|
||||
return( fwrite( buffer, 1, length, fp ) - length );
|
||||
}
|
||||
}
|
||||
|
||||
/* What we track during radiance file read.
|
||||
*/
|
||||
typedef struct {
|
||||
@ -1211,11 +1174,7 @@ vips__rad_load( const char *filename, VipsImage *out )
|
||||
*/
|
||||
typedef struct {
|
||||
VipsImage *in;
|
||||
|
||||
char *filename;
|
||||
FILE *fout;
|
||||
|
||||
VipsDbuf dbuf;
|
||||
VipsStreamo *output;
|
||||
|
||||
char format[256];
|
||||
double expos;
|
||||
@ -1223,20 +1182,20 @@ typedef struct {
|
||||
double aspect;
|
||||
RGBPRIMS prims;
|
||||
RESOLU rs;
|
||||
unsigned char *line;
|
||||
} Write;
|
||||
|
||||
static void
|
||||
write_destroy( Write *write )
|
||||
{
|
||||
VIPS_FREE( write->filename );
|
||||
VIPS_FREEF( fclose, write->fout );
|
||||
vips_dbuf_destroy( &write->dbuf );
|
||||
VIPS_FREE( write->line );
|
||||
VIPS_UNREF( write->output );
|
||||
|
||||
vips_free( write );
|
||||
}
|
||||
|
||||
static Write *
|
||||
write_new( VipsImage *in )
|
||||
write_new( VipsImage *in, VipsStreamo *output )
|
||||
{
|
||||
Write *write;
|
||||
int i;
|
||||
@ -1245,11 +1204,8 @@ write_new( VipsImage *in )
|
||||
return( NULL );
|
||||
|
||||
write->in = in;
|
||||
|
||||
write->filename = NULL;
|
||||
write->fout = NULL;
|
||||
|
||||
vips_dbuf_init( &write->dbuf );
|
||||
write->output = output;
|
||||
g_object_ref( output );
|
||||
|
||||
strcpy( write->format, COLRFMT );
|
||||
write->expos = 1.0;
|
||||
@ -1265,6 +1221,11 @@ write_new( VipsImage *in )
|
||||
write->prims[3][0] = CIE_x_w;
|
||||
write->prims[3][1] = CIE_y_w;
|
||||
|
||||
if( !(write->line = VIPS_ARRAY( NULL, MAX_LINE, unsigned char )) ) {
|
||||
write_destroy( write );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
return( write );
|
||||
}
|
||||
|
||||
@ -1279,7 +1240,8 @@ vips2rad_make_header( Write *write )
|
||||
vips_image_get_double( write->in, "rad-expos", &write->expos );
|
||||
|
||||
if( vips_image_get_typeof( write->in, "rad-aspect" ) )
|
||||
vips_image_get_double( write->in, "rad-aspect", &write->aspect );
|
||||
vips_image_get_double( write->in,
|
||||
"rad-aspect", &write->aspect );
|
||||
|
||||
if( vips_image_get_typeof( write->in, "rad-format" ) &&
|
||||
!vips_image_get_string( write->in, "rad-format", &str ) )
|
||||
@ -1292,7 +1254,8 @@ vips2rad_make_header( Write *write )
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
if( vips_image_get_typeof( write->in, colcor_name[i] ) &&
|
||||
!vips_image_get_double( write->in, colcor_name[i], &d ) )
|
||||
!vips_image_get_double( write->in,
|
||||
colcor_name[i], &d ) )
|
||||
write->colcor[i] = d;
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
@ -1316,16 +1279,53 @@ vips2rad_put_header( Write *write )
|
||||
{
|
||||
vips2rad_make_header( write );
|
||||
|
||||
fprintf( write->fout, "#?RADIANCE\n" );
|
||||
vips_streamo_writef( write->output, "#?RADIANCE\n" );
|
||||
vips_streamo_writef( write->output, "%s%s\n", FMTSTR, write->format );
|
||||
vips_streamo_writef( write->output, "%s%e\n", EXPOSSTR, write->expos );
|
||||
vips_streamo_writef( write->output,
|
||||
"%s %f %f %f\n", COLCORSTR,
|
||||
write->colcor[RED], write->colcor[GRN], write->colcor[BLU] );
|
||||
vips_streamo_writef( write->output,
|
||||
"SOFTWARE=vips %s\n", vips_version_string() );
|
||||
vips_streamo_writef( write->output,
|
||||
"%s%f\n", ASPECTSTR, write->aspect );
|
||||
vips_streamo_writef( write->output,
|
||||
"%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",
|
||||
PRIMARYSTR,
|
||||
write->prims[RED][CIEX], write->prims[RED][CIEY],
|
||||
write->prims[GRN][CIEX], write->prims[GRN][CIEY],
|
||||
write->prims[BLU][CIEX], write->prims[BLU][CIEY],
|
||||
write->prims[WHT][CIEX], write->prims[WHT][CIEY] );
|
||||
vips_streamo_writef( write->output, "\n" );
|
||||
vips_streamo_writef( write->output,
|
||||
"%s", resolu2str( resolu_buf, &write->rs ) );
|
||||
|
||||
fputformat( write->format, write->fout );
|
||||
fputexpos( write->expos, write->fout );
|
||||
fputcolcor( write->colcor, write->fout );
|
||||
fprintf( write->fout, "SOFTWARE=vips %s\n", vips_version_string() );
|
||||
fputaspect( write->aspect, write->fout );
|
||||
fputprims( write->prims, write->fout );
|
||||
fputs( "\n", write->fout );
|
||||
fputsresolu( &write->rs, write->fout );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Write a single scanline to buffer.
|
||||
*/
|
||||
static int
|
||||
scanline_write( Write *write, COLR *scanline, int width )
|
||||
{
|
||||
if( width < MINELEN ||
|
||||
width > MAXELEN ) {
|
||||
/* Too large or small for RLE ... do a simple write.
|
||||
*/
|
||||
if( vips_streamo_write( write->output,
|
||||
scanline, sizeof( COLR ) * width ) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
int length;
|
||||
|
||||
/* An RLE scanline.
|
||||
*/
|
||||
rle_scanline_write( scanline, width, write->line, &length );
|
||||
|
||||
if( vips_streamo_write( write->output, write->line, length ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -1334,23 +1334,12 @@ static int
|
||||
vips2rad_put_data_block( VipsRegion *region, VipsRect *area, void *a )
|
||||
{
|
||||
Write *write = (Write *) a;
|
||||
|
||||
size_t size;
|
||||
unsigned char *buffer;
|
||||
int i;
|
||||
|
||||
/* You have to seek back after a write.
|
||||
*/
|
||||
buffer = vips_dbuf_get_write( &write->dbuf, &size );
|
||||
vips_dbuf_seek( &write->dbuf, 0, SEEK_SET );
|
||||
|
||||
g_assert( size >= MAX_LINE );
|
||||
|
||||
for( i = 0; i < area->height; i++ ) {
|
||||
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i );
|
||||
|
||||
if( scanline_write( buffer,
|
||||
(COLR *) p, area->width, write->fout ) )
|
||||
if( scanline_write( write, (COLR *) p, area->width ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
@ -1367,124 +1356,7 @@ vips2rad_put_data( Write *write )
|
||||
}
|
||||
|
||||
int
|
||||
vips__rad_save( VipsImage *in, const char *filename )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips2rad: writing \"%s\"\n", filename );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( vips_image_pio_input( in ) ||
|
||||
vips_check_coding_rad( "vips2rad", in ) )
|
||||
return( -1 );
|
||||
if( !(write = write_new( in )) )
|
||||
return( -1 );
|
||||
|
||||
write->filename = vips_strdup( NULL, filename );
|
||||
write->fout = vips__file_open_write( filename, FALSE );
|
||||
|
||||
/* scanline_write() needs a buffer to write compressed scanlines to.
|
||||
* We use the dbuf ... why not.
|
||||
*/
|
||||
vips_dbuf_allocate( &write->dbuf, MAX_LINE );
|
||||
|
||||
if( !write->filename ||
|
||||
!write->fout ||
|
||||
vips2rad_put_header( write ) ||
|
||||
vips2rad_put_data( write ) ) {
|
||||
write_destroy( write );
|
||||
return( -1 );
|
||||
}
|
||||
write_destroy( write );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips2rad_put_header_buf( Write *write )
|
||||
{
|
||||
vips2rad_make_header( write );
|
||||
|
||||
vips_dbuf_writef( &write->dbuf, "#?RADIANCE\n" );
|
||||
vips_dbuf_writef( &write->dbuf, "%s%s\n", FMTSTR, write->format );
|
||||
vips_dbuf_writef( &write->dbuf, "%s%e\n", EXPOSSTR, write->expos );
|
||||
vips_dbuf_writef( &write->dbuf, "%s %f %f %f\n",
|
||||
COLCORSTR,
|
||||
write->colcor[RED], write->colcor[GRN], write->colcor[BLU] );
|
||||
vips_dbuf_writef( &write->dbuf, "SOFTWARE=vips %s\n",
|
||||
vips_version_string() );
|
||||
vips_dbuf_writef( &write->dbuf, "%s%f\n", ASPECTSTR, write->aspect );
|
||||
vips_dbuf_writef( &write->dbuf,
|
||||
"%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",
|
||||
PRIMARYSTR,
|
||||
write->prims[RED][CIEX], write->prims[RED][CIEY],
|
||||
write->prims[GRN][CIEX], write->prims[GRN][CIEY],
|
||||
write->prims[BLU][CIEX], write->prims[BLU][CIEY],
|
||||
write->prims[WHT][CIEX], write->prims[WHT][CIEY] );
|
||||
vips_dbuf_writef( &write->dbuf, "\n" );
|
||||
vips_dbuf_writef( &write->dbuf, "%s",
|
||||
resolu2str( resolu_buf, &write->rs ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Write a single scanline to buffer.
|
||||
*/
|
||||
static int
|
||||
scanline_write_buf( Write *write, COLR *scanline, int width )
|
||||
{
|
||||
unsigned char *buffer;
|
||||
size_t size;
|
||||
int length;
|
||||
|
||||
vips_dbuf_allocate( &write->dbuf, MAX_LINE );
|
||||
buffer = vips_dbuf_get_write( &write->dbuf, &size );
|
||||
|
||||
if( width < MINELEN ||
|
||||
width > MAXELEN ) {
|
||||
/* Write as a flat scanline.
|
||||
*/
|
||||
length = sizeof( COLR ) * width;
|
||||
memcpy( buffer, scanline, length );
|
||||
}
|
||||
else
|
||||
/* An RLE scanline.
|
||||
*/
|
||||
rle_scanline_write( scanline, width, buffer, &length );
|
||||
|
||||
vips_dbuf_seek( &write->dbuf, length - size, SEEK_CUR );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips2rad_put_data_block_buf( VipsRegion *region, VipsRect *area, void *a )
|
||||
{
|
||||
Write *write = (Write *) a;
|
||||
int i;
|
||||
|
||||
for( i = 0; i < area->height; i++ ) {
|
||||
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i );
|
||||
|
||||
if( scanline_write_buf( write, (COLR *) p, area->width ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips2rad_put_data_buf( Write *write )
|
||||
{
|
||||
if( vips_sink_disc( write->in, vips2rad_put_data_block_buf, write ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen )
|
||||
vips__rad_save( VipsImage *in, VipsStreamo *output )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
@ -1495,16 +1367,16 @@ vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen )
|
||||
if( vips_image_pio_input( in ) ||
|
||||
vips_check_coding_rad( "vips2rad", in ) )
|
||||
return( -1 );
|
||||
if( !(write = write_new( in )) )
|
||||
if( !(write = write_new( in, output )) )
|
||||
return( -1 );
|
||||
|
||||
if( vips2rad_put_header_buf( write ) ||
|
||||
vips2rad_put_data_buf( write ) ) {
|
||||
if( vips2rad_put_header( write ) ||
|
||||
vips2rad_put_data( write ) ) {
|
||||
write_destroy( write );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
*obuf = vips_dbuf_steal( &write->dbuf, olen );
|
||||
vips_streamo_finish( output );
|
||||
|
||||
write_destroy( write );
|
||||
|
||||
|
@ -56,7 +56,6 @@
|
||||
typedef struct _VipsForeignSaveRad {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
char *filename;
|
||||
} VipsForeignSaveRad;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveRadClass;
|
||||
@ -85,14 +84,10 @@ static int vips_foreign_save_rad_format_table[10] = {
|
||||
static void
|
||||
vips_foreign_save_rad_class_init( VipsForeignSaveRadClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "radsave_base";
|
||||
object_class->description = _( "save Radiance" );
|
||||
|
||||
@ -127,12 +122,19 @@ vips_foreign_save_rad_file_build( VipsObject *object )
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveRadFile *file = (VipsForeignSaveRadFile *) object;
|
||||
|
||||
VipsStreamo *output;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips__rad_save( save->ready, file->filename ) )
|
||||
if( !(output = vips_streamo_new_from_filename( file->filename )) )
|
||||
return( -1 );
|
||||
if( vips__rad_save( save->ready, output ) ) {
|
||||
VIPS_UNREF( output );
|
||||
return( -1 );
|
||||
}
|
||||
VIPS_UNREF( output );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -163,6 +165,60 @@ vips_foreign_save_rad_file_init( VipsForeignSaveRadFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveRadStream {
|
||||
VipsForeignSaveRad parent_object;
|
||||
|
||||
VipsStreamo *output;
|
||||
} VipsForeignSaveRadStream;
|
||||
|
||||
typedef VipsForeignSaveRadClass VipsForeignSaveRadStreamClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveRadStream, vips_foreign_save_rad_stream,
|
||||
vips_foreign_save_rad_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_rad_stream_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveRadStream *stream = (VipsForeignSaveRadStream *) object;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_stream_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips__rad_save( save->ready, stream->output ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_rad_stream_class_init( VipsForeignSaveRadStreamClass *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 = "radsave_stream";
|
||||
object_class->description = _( "save image to Radiance stream" );
|
||||
object_class->build = vips_foreign_save_rad_stream_build;
|
||||
|
||||
VIPS_ARG_OBJECT( class, "output", 1,
|
||||
_( "Output" ),
|
||||
_( "Stream to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveRadStream, output ),
|
||||
VIPS_TYPE_STREAMO );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_rad_stream_init( VipsForeignSaveRadStream *stream )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveRadBuffer {
|
||||
VipsForeignSaveRad parent_object;
|
||||
|
||||
@ -179,24 +235,27 @@ vips_foreign_save_rad_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsStreamo *output;
|
||||
VipsBlob *blob;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips__rad_save_buf( save->ready, &obuf, &olen ) )
|
||||
if( !(output = vips_streamo_new_memory()) )
|
||||
return( -1 );
|
||||
|
||||
/* vips__rad_save_buf() makes a buffer that needs g_free(), not
|
||||
* vips_free().
|
||||
*/
|
||||
blob = vips_blob_new( (VipsCallbackFn) g_free, obuf, olen );
|
||||
g_object_set( object, "buffer", blob, NULL );
|
||||
if( vips__rad_save( save->ready, output ) ) {
|
||||
VIPS_UNREF( output );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
g_object_get( output, "blob", &blob, NULL );
|
||||
g_object_set( save, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
VIPS_UNREF( output );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -299,3 +358,28 @@ vips_radsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_radsave_stream: (method)
|
||||
* @in: image to save
|
||||
* @output: save image to this stream
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* As vips_radsave(), but save to a stream.
|
||||
*
|
||||
* See also: vips_radsave().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_radsave_stream( VipsImage *in, VipsStreamo *output, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, output );
|
||||
result = vips_call_split( "radsave_stream", ap, in, output );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
@ -205,12 +205,9 @@ vips__tiff_openin_stream( VipsStreami *input )
|
||||
printf( "vips__tiff_openin_stream:\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Unreffed on close(), see above.
|
||||
*/
|
||||
if( vips_streami_rewind( input ) )
|
||||
return( NULL );
|
||||
|
||||
g_object_ref( input );
|
||||
if( !(tiff = TIFFClientOpen( "stream input", "rm",
|
||||
(thandle_t) input,
|
||||
openin_stream_read,
|
||||
@ -225,6 +222,10 @@ vips__tiff_openin_stream( VipsStreami *input )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Unreffed on close(), see above.
|
||||
*/
|
||||
g_object_ref( input );
|
||||
|
||||
return( tiff );
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,8 @@ size_t vips_dbuf_read( VipsDbuf *dbuf, unsigned char *data, size_t size );
|
||||
unsigned char *vips_dbuf_get_write( VipsDbuf *dbuf, size_t *size );
|
||||
gboolean vips_dbuf_write( VipsDbuf *dbuf,
|
||||
const unsigned char *data, size_t size );
|
||||
gboolean vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... );
|
||||
gboolean vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... )
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
gboolean vips_dbuf_write_amp( VipsDbuf *dbuf, const char *str );
|
||||
void vips_dbuf_reset( VipsDbuf *dbuf );
|
||||
void vips_dbuf_destroy( VipsDbuf *dbuf );
|
||||
|
@ -576,6 +576,8 @@ int vips_radsave( VipsImage *in, const char *filename, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_radsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_radsave_stream( VipsImage *in, VipsStreamo *output, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
int vips_pdfload( const char *filename, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
|
@ -184,13 +184,14 @@ typedef struct _VipsStreamiClass {
|
||||
*/
|
||||
|
||||
/* Read from the stream into the supplied buffer, args exactly as
|
||||
* read(2).
|
||||
* read(2). Set errno on error.
|
||||
*/
|
||||
ssize_t (*read)( VipsStreami *, void *, size_t );
|
||||
|
||||
/* Seek to a certain position, args exactly as lseek(2).
|
||||
/* Seek to a certain position, args exactly as lseek(2). Set errno on
|
||||
* error.
|
||||
*
|
||||
* Unseekable streams should just return -1. VipsStreami will then
|
||||
* Unseekable streams should always return -1. VipsStreami will then
|
||||
* seek by _read()ing bytes into memory as required.
|
||||
*/
|
||||
gint64 (*seek)( VipsStreami *, gint64 offset, int );
|
||||
@ -230,6 +231,8 @@ gint64 vips_streami_size( VipsStreami *streami );
|
||||
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
||||
VIPS_TYPE_STREAMO, VipsStreamoClass ))
|
||||
|
||||
#define VIPS_STREAMO_BUFFER_SIZE (4096)
|
||||
|
||||
/* Output to something like a socket, pipe or memory area.
|
||||
*/
|
||||
typedef struct _VipsStreamo {
|
||||
@ -245,16 +248,22 @@ typedef struct _VipsStreamo {
|
||||
*/
|
||||
VipsBlob *blob;
|
||||
|
||||
/* Buffer small writes here.
|
||||
*/
|
||||
unsigned char output_buffer[VIPS_STREAMO_BUFFER_SIZE];
|
||||
unsigned char *write_point;
|
||||
int bytes_remaining;
|
||||
|
||||
} VipsStreamo;
|
||||
|
||||
typedef struct _VipsStreamoClass {
|
||||
VipsStreamClass parent_class;
|
||||
|
||||
/* If defined, output some bytes with this. Otherwise use write().
|
||||
/* Write to output. Args exactly as write(2).
|
||||
*/
|
||||
ssize_t (*write)( VipsStreamo *, const void *, size_t );
|
||||
|
||||
/* A complete output image has been generated, so do any clearing up,
|
||||
/* Output has been generated, so do any clearing up,
|
||||
* eg. copy the bytes we saved in memory to the stream blob.
|
||||
*/
|
||||
void (*finish)( VipsStreamo * );
|
||||
@ -269,6 +278,10 @@ VipsStreamo *vips_streamo_new_memory( void );
|
||||
int vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length );
|
||||
void vips_streamo_finish( VipsStreamo *streamo );
|
||||
|
||||
int vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... )
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
int vips_streamo_write_amp( VipsStreamo *streamo, const char *str );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
@ -34,9 +34,7 @@
|
||||
/* TODO
|
||||
*
|
||||
* - filename encoding
|
||||
* - need printf()-style output? eg. for CSV write ... see dbuf.c
|
||||
* - make vfuncs just call write() and seek() (for tiff), all other processing
|
||||
* in the invocation ... cf. streami
|
||||
* - seem to have some test-suite ref leaks?
|
||||
* - test we can really change all behaviour in the subclass ... add callbacks
|
||||
* as well to make it simpler for language bindings
|
||||
*/
|
||||
@ -157,37 +155,15 @@ vips_streamo_write_real( VipsStreamo *streamo, const void *data, size_t length )
|
||||
{
|
||||
VipsStream *stream = VIPS_STREAM( streamo );
|
||||
|
||||
ssize_t len;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_streamo_write_real: %zd bytes\n", length );
|
||||
|
||||
if( streamo->memory ) {
|
||||
g_byte_array_append( streamo->memory, data, length );
|
||||
len = length;
|
||||
}
|
||||
else
|
||||
len = write( stream->descriptor, data, length );
|
||||
|
||||
return( len );
|
||||
return( write( stream->descriptor, data, length ) );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_streamo_finish_real( VipsStreamo *streamo )
|
||||
{
|
||||
VIPS_DEBUG_MSG( "vips_streamo_finish_real:\n" );
|
||||
|
||||
/* Move the stream buffer into the blob so it can be read out.
|
||||
*/
|
||||
if( streamo->memory ) {
|
||||
unsigned char *data;
|
||||
size_t length;
|
||||
|
||||
length = streamo->memory->len;
|
||||
data = g_byte_array_free( streamo->memory, FALSE );
|
||||
streamo->memory = NULL;
|
||||
vips_blob_set( streamo->blob,
|
||||
(VipsCallbackFn) g_free, data, length );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -224,6 +200,8 @@ static void
|
||||
vips_streamo_init( VipsStreamo *streamo )
|
||||
{
|
||||
streamo->blob = vips_blob_new( NULL, NULL, 0 );
|
||||
streamo->bytes_remaining = VIPS_STREAMO_BUFFER_SIZE;
|
||||
streamo->write_point = streamo->output_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,24 +293,27 @@ vips_streamo_new_memory( void )
|
||||
return( streamo );
|
||||
}
|
||||
|
||||
int
|
||||
vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length )
|
||||
static int
|
||||
vips_streamo_write_unbuffered( VipsStreamo *streamo,
|
||||
const void *data, size_t length )
|
||||
{
|
||||
VipsStreamoClass *class = VIPS_STREAMO_GET_CLASS( streamo );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_streamo_write: %zd bytes\n", length );
|
||||
|
||||
if( streamo->memory )
|
||||
g_byte_array_append( streamo->memory, data, length );
|
||||
else
|
||||
while( length > 0 ) {
|
||||
ssize_t n;
|
||||
|
||||
n = class->write( streamo, 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.
|
||||
/* 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( n <= 0 ) {
|
||||
vips_error_system( errno,
|
||||
vips_stream_nick( VIPS_STREAM( streamo ) ),
|
||||
vips_stream_nick(
|
||||
VIPS_STREAM( streamo ) ),
|
||||
"%s", _( "write error" ) );
|
||||
return( -1 );
|
||||
}
|
||||
@ -344,6 +325,75 @@ vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_streamo_flush( VipsStreamo *streamo )
|
||||
{
|
||||
int bytes_in_buffer =
|
||||
VIPS_STREAMO_BUFFER_SIZE - streamo->bytes_remaining;
|
||||
|
||||
g_assert( bytes_in_buffer >= 0 );
|
||||
g_assert( bytes_in_buffer <= VIPS_STREAMO_BUFFER_SIZE );
|
||||
|
||||
if( bytes_in_buffer > 0 ) {
|
||||
if( vips_streamo_write_unbuffered( streamo,
|
||||
streamo->output_buffer, bytes_in_buffer ) )
|
||||
return( -1 );
|
||||
streamo->bytes_remaining = VIPS_STREAMO_BUFFER_SIZE;
|
||||
streamo->write_point = streamo->output_buffer;
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_streamo_write:
|
||||
* @streamo: output stream to operate on
|
||||
* @buffer: bytes to write
|
||||
* @length: length of @buffer in bytes
|
||||
*
|
||||
* Write @length bytes from @buffer to the output.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_streamo_write( VipsStreamo *streamo, const void *buffer, size_t length )
|
||||
{
|
||||
VIPS_DEBUG_MSG( "vips_streamo_write: %zd bytes\n", length );
|
||||
|
||||
if( streamo->bytes_remaining >= length ) {
|
||||
memcpy( streamo->write_point, buffer, length );
|
||||
streamo->bytes_remaining -= length;
|
||||
streamo->write_point += length;
|
||||
}
|
||||
else {
|
||||
if( vips_streamo_flush( streamo ) )
|
||||
return( -1 );
|
||||
|
||||
if( streamo->bytes_remaining >= length ) {
|
||||
memcpy( streamo->write_point, buffer, length );
|
||||
streamo->bytes_remaining -= length;
|
||||
streamo->write_point += length;
|
||||
}
|
||||
else
|
||||
/* The buffer is empty and we still have too much
|
||||
* data. Write directly.
|
||||
*/
|
||||
if( vips_streamo_write_unbuffered( streamo,
|
||||
buffer, length ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_streamo_finish:
|
||||
* @streamo: output stream to operate on
|
||||
* @buffer: bytes to write
|
||||
* @length: length of @buffer in bytes
|
||||
*
|
||||
* Call this at the end of write to make the stream do any cleaning up.
|
||||
*/
|
||||
void
|
||||
vips_streamo_finish( VipsStreamo *streamo )
|
||||
{
|
||||
@ -351,5 +401,112 @@ vips_streamo_finish( VipsStreamo *streamo )
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_streamo_finish:\n" );
|
||||
|
||||
(void) vips_streamo_flush( streamo );
|
||||
|
||||
/* Move the stream buffer into the blob so it can be read out.
|
||||
*/
|
||||
if( streamo->memory ) {
|
||||
unsigned char *data;
|
||||
size_t length;
|
||||
|
||||
length = streamo->memory->len;
|
||||
data = g_byte_array_free( streamo->memory, FALSE );
|
||||
streamo->memory = NULL;
|
||||
vips_blob_set( streamo->blob,
|
||||
(VipsCallbackFn) g_free, data, length );
|
||||
}
|
||||
else
|
||||
class->finish( streamo );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_streamo_writef:
|
||||
* @streamo: output stream to operate on
|
||||
* @fmt: <function>printf()</function>-style format string
|
||||
* @...: arguments to format string
|
||||
*
|
||||
* Format the string and write to @streamo.
|
||||
*
|
||||
* Returns: 0 on success, and -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
char *line;
|
||||
int result;
|
||||
|
||||
va_start( ap, fmt );
|
||||
line = g_strdup_vprintf( fmt, ap );
|
||||
va_end( ap );
|
||||
|
||||
result = vips_streamo_write( streamo,
|
||||
(unsigned char *) line, strlen( line ) );
|
||||
|
||||
g_free( line );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_streamo_write_amp:
|
||||
* @streamo: output stream to operate on
|
||||
* @str: string to write
|
||||
*
|
||||
* Write @str to @streamo, but escape stuff that xml hates in text. Our
|
||||
* argument string is utf-8.
|
||||
*
|
||||
* XML rules:
|
||||
*
|
||||
* - We must escape &<>
|
||||
* - Don't escape \n, \t, \r
|
||||
* - Do escape the other ASCII codes.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_streamo_write_amp( VipsStreamo *streamo, const char *str )
|
||||
{
|
||||
const char *p;
|
||||
|
||||
for( p = str; *p; p++ )
|
||||
if( *p < 32 &&
|
||||
*p != '\n' &&
|
||||
*p != '\t' &&
|
||||
*p != '\r' ) {
|
||||
/* You'd think we could output "%x;", but xml
|
||||
* 1.0 parsers barf on that. xml 1.1 allows this, but
|
||||
* there are almost no parsers.
|
||||
*
|
||||
* U+2400 onwards are unicode glyphs for the ASCII
|
||||
* control characters, so we can use them -- thanks
|
||||
* electroly.
|
||||
*/
|
||||
if( !vips_streamo_writef( streamo,
|
||||
"&#x%04x;", 0x2400 + *p ) )
|
||||
return( -1 );
|
||||
}
|
||||
else if( *p == '<' ) {
|
||||
if( !vips_streamo_write( streamo,
|
||||
(guchar *) "<", 4 ) )
|
||||
return( -1 );
|
||||
}
|
||||
else if( *p == '>' ) {
|
||||
if( !vips_streamo_write( streamo,
|
||||
(guchar *) ">", 4 ) )
|
||||
return( -1 );
|
||||
}
|
||||
else if( *p == '&' ) {
|
||||
if( !vips_streamo_write( streamo,
|
||||
(guchar *) "&", 5 ) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
if( !vips_streamo_write( streamo,
|
||||
(guchar *) p, 1 ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user