revise streamo, ppm save uses it

streamo is simpler, and has a fast putc.
This commit is contained in:
John Cupitt 2019-11-13 17:50:02 +00:00
parent 7aec350f1c
commit 3bbadc0198
5 changed files with 101 additions and 85 deletions

View File

@ -149,7 +149,7 @@ int vips__ppm_isppm( const char *filename );
VipsForeignFlags vips__ppm_flags( const char *filename ); VipsForeignFlags vips__ppm_flags( const char *filename );
extern const char *vips__ppm_suffs[]; extern const char *vips__ppm_suffs[];
int vips__ppm_save( VipsImage *in, const char *filename, int vips__ppm_save_stream( VipsImage *in, VipsStreamo *streamo,
gboolean ascii, gboolean squash ); gboolean ascii, gboolean squash );
int vips__rad_israd( VipsStreami *streami ); int vips__rad_israd( VipsStreami *streami );

View File

@ -36,6 +36,8 @@
* linebreaks * linebreaks
* 29/7/19 Kyle-Kyle * 29/7/19 Kyle-Kyle
* - fix a loop with malformed ppm * - fix a loop with malformed ppm
* 13/11/19
* - ppm save redone with streams
*/ */
/* /*
@ -555,22 +557,22 @@ typedef int (*write_fn)( struct _Write *write, VipsPel *p );
*/ */
typedef struct _Write { typedef struct _Write {
VipsImage *in; VipsImage *in;
FILE *fp; VipsStreamo *streamo;
char *name;
write_fn fn; write_fn fn;
} Write; } Write;
static void static void
write_destroy( Write *write ) write_destroy( Write *write )
{ {
VIPS_FREEF( fclose, write->fp ); if( write->streamo )
VIPS_FREE( write->name ); vips_streamo_finish( write->streamo );
VIPS_UNREF( write->streamo );
vips_free( write ); vips_free( write );
} }
static Write * static Write *
write_new( VipsImage *in, const char *name ) write_new( VipsImage *in, VipsStreamo *streamo )
{ {
Write *write; Write *write;
@ -578,13 +580,9 @@ write_new( VipsImage *in, const char *name )
return( NULL ); return( NULL );
write->in = in; write->in = in;
write->name = vips_strdup( NULL, name ); write->streamo = streamo;
write->fp = vips__file_open_write( name, FALSE ); g_object_ref( streamo );
write->fn = NULL;
if( !write->name || !write->fp ) {
write_destroy( write );
return( NULL );
}
return( write ); return( write );
} }
@ -599,17 +597,17 @@ write_ppm_line_ascii( Write *write, VipsPel *p )
for( k = 0; k < write->in->Bands; k++ ) { for( k = 0; k < write->in->Bands; k++ ) {
switch( write->in->BandFmt ) { switch( write->in->BandFmt ) {
case VIPS_FORMAT_UCHAR: case VIPS_FORMAT_UCHAR:
fprintf( write->fp, vips_streamo_writef( write->streamo,
"%d ", p[k] ); "%d ", p[k] );
break; break;
case VIPS_FORMAT_USHORT: case VIPS_FORMAT_USHORT:
fprintf( write->fp, vips_streamo_writef( write->streamo,
"%d ", ((unsigned short *) p)[k] ); "%d ", ((unsigned short *) p)[k] );
break; break;
case VIPS_FORMAT_UINT: case VIPS_FORMAT_UINT:
fprintf( write->fp, vips_streamo_writef( write->streamo,
"%d ", ((unsigned int *) p)[k] ); "%d ", ((unsigned int *) p)[k] );
break; break;
@ -621,11 +619,8 @@ write_ppm_line_ascii( Write *write, VipsPel *p )
p += sk; p += sk;
} }
if( !fprintf( write->fp, "\n" ) ) { if( vips_streamo_writef( write->streamo, "\n" ) )
vips_error_system( errno, "vips2ppm",
"%s", _( "write error" ) );
return( -1 ); return( -1 );
}
return( 0 ); return( 0 );
} }
@ -636,13 +631,10 @@ write_ppm_line_ascii_squash( Write *write, VipsPel *p )
int x; int x;
for( x = 0; x < write->in->Xsize; x++ ) for( x = 0; x < write->in->Xsize; x++ )
fprintf( write->fp, "%d ", p[x] ? 0 : 1 ); vips_streamo_writef( write->streamo, "%d ", p[x] ? 0 : 1 );
if( !fprintf( write->fp, "\n" ) ) { if( vips_streamo_writef( write->streamo, "\n" ) )
vips_error_system( errno, "vips2ppm",
"%s", _( "write error" ) );
return( -1 ); return( -1 );
}
return( 0 ); return( 0 );
} }
@ -650,8 +642,8 @@ write_ppm_line_ascii_squash( Write *write, VipsPel *p )
static int static int
write_ppm_line_binary( Write *write, VipsPel *p ) write_ppm_line_binary( Write *write, VipsPel *p )
{ {
if( vips__file_write( p, VIPS_IMAGE_SIZEOF_LINE( write->in ), 1, if( vips_streamo_write( write->streamo,
write->fp ) ) p, VIPS_IMAGE_SIZEOF_LINE( write->in ) ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
@ -672,11 +664,8 @@ write_ppm_line_binary_squash( Write *write, VipsPel *p )
bits |= p[x] ? 0 : 1; bits |= p[x] ? 0 : 1;
if( n_bits == 8 ) { if( n_bits == 8 ) {
if( fputc( bits, write->fp ) == EOF ) { if( VIPS_STREAMO_PUTC( write->streamo, bits ) )
vips_error_system( errno, "vips2ppm",
"%s", _( "write error" ) );
return( -1 ); return( -1 );
}
bits = 0; bits = 0;
n_bits = 0; n_bits = 0;
@ -685,13 +674,9 @@ write_ppm_line_binary_squash( Write *write, VipsPel *p )
/* Flush any remaining bits in this line. /* Flush any remaining bits in this line.
*/ */
if( n_bits ) { if( n_bits &&
if( fputc( bits, write->fp ) == EOF ) { VIPS_STREAMO_PUTC( write->streamo, bits ) )
vips_error_system( errno, "vips2ppm", return( -1 );
"%s", _( "write error" ) );
return( -1 );
}
}
return( 0 ); return( 0 );
} }
@ -740,23 +725,27 @@ write_ppm( Write *write, gboolean ascii, gboolean squash )
else else
g_assert_not_reached(); g_assert_not_reached();
fprintf( write->fp, "%s\n", magic ); vips_streamo_writef( write->streamo, "%s\n", magic );
time( &timebuf ); time( &timebuf );
fprintf( write->fp, "#vips2ppm - %s\n", ctime( &timebuf ) ); vips_streamo_writef( write->streamo,
fprintf( write->fp, "%d %d\n", in->Xsize, in->Ysize ); "#vips2ppm - %s\n", ctime( &timebuf ) );
vips_streamo_writef( write->streamo, "%d %d\n", in->Xsize, in->Ysize );
if( !squash ) if( !squash )
switch( in->BandFmt ) { switch( in->BandFmt ) {
case VIPS_FORMAT_UCHAR: case VIPS_FORMAT_UCHAR:
fprintf( write->fp, "%d\n", UCHAR_MAX ); vips_streamo_writef( write->streamo,
"%d\n", UCHAR_MAX );
break; break;
case VIPS_FORMAT_USHORT: case VIPS_FORMAT_USHORT:
fprintf( write->fp, "%d\n", USHRT_MAX ); vips_streamo_writef( write->streamo,
"%d\n", USHRT_MAX );
break; break;
case VIPS_FORMAT_UINT: case VIPS_FORMAT_UINT:
fprintf( write->fp, "%d\n", UINT_MAX ); vips_streamo_writef( write->streamo,
"%d\n", UINT_MAX );
break; break;
case VIPS_FORMAT_FLOAT: case VIPS_FORMAT_FLOAT:
@ -767,7 +756,8 @@ write_ppm( Write *write, gboolean ascii, gboolean squash )
scale = 1; scale = 1;
if( !vips_amiMSBfirst() ) if( !vips_amiMSBfirst() )
scale *= -1; scale *= -1;
fprintf( write->fp, "%g\n", scale ); vips_streamo_writef( write->streamo,
"%g\n", scale );
} }
break; break;
@ -791,7 +781,7 @@ write_ppm( Write *write, gboolean ascii, gboolean squash )
} }
int int
vips__ppm_save( VipsImage *in, const char *filename, vips__ppm_save_stream( VipsImage *in, VipsStreamo *streamo,
gboolean ascii, gboolean squash ) gboolean ascii, gboolean squash )
{ {
Write *write; Write *write;
@ -820,7 +810,7 @@ vips__ppm_save( VipsImage *in, const char *filename,
squash = FALSE; squash = FALSE;
} }
if( !(write = write_new( in, filename )) ) if( !(write = write_new( in, streamo )) )
return( -1 ); return( -1 );
if( write_ppm( write, ascii, squash ) ) { if( write_ppm( write, ascii, squash ) ) {

View File

@ -70,13 +70,20 @@ vips_foreign_save_ppm_build( VipsObject *object )
VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object; VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object;
VipsStreamo *streamo;
if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )-> if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )->
build( object ) ) build( object ) )
return( -1 ); return( -1 );
if( vips__ppm_save( save->ready, ppm->filename, if( !(streamo = vips_streamo_new_to_filename( ppm->filename )) )
ppm->ascii, ppm->squash ) )
return( -1 ); return( -1 );
if( vips__ppm_save_stream( save->ready, streamo,
ppm->ascii, ppm->squash ) ) {
VIPS_UNREF( streamo );
return( -1 );
}
VIPS_UNREF( streamo );
return( 0 ); return( 0 );
} }

View File

@ -365,11 +365,11 @@ typedef struct _VipsStreamo {
*/ */
VipsBlob *blob; VipsBlob *blob;
/* Buffer small writes here. /* Buffer small writes here. write_point is the index of the next
* character to write.
*/ */
unsigned char output_buffer[VIPS_STREAMO_BUFFER_SIZE]; unsigned char output_buffer[VIPS_STREAMO_BUFFER_SIZE];
unsigned char *write_point; int write_point;
int bytes_remaining;
} VipsStreamo; } VipsStreamo;
@ -395,6 +395,14 @@ VipsStreamo *vips_streamo_new_to_memory( void );
int vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length ); int vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length );
void vips_streamo_finish( VipsStreamo *streamo ); void vips_streamo_finish( VipsStreamo *streamo );
int vips_streamo_putc( VipsStreamo *streamo, int ch );
#define VIPS_STREAMO_PUTC( S, C ) ( \
(S)->write_point <= VIPS_STREAMO_BUFFER_SIZE ? \
((S)->output_buffer[(S)->write_point++] = (C), 0) : \
vips_streamo_putc( (S), (C) ) \
)
int vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... ) int vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... )
__attribute__((format(printf, 2, 3))); __attribute__((format(printf, 2, 3)));

View File

@ -198,8 +198,7 @@ static void
vips_streamo_init( VipsStreamo *streamo ) vips_streamo_init( VipsStreamo *streamo )
{ {
streamo->blob = vips_blob_new( NULL, NULL, 0 ); streamo->blob = vips_blob_new( NULL, NULL, 0 );
streamo->bytes_remaining = VIPS_STREAMO_BUFFER_SIZE; streamo->write_point = 0;
streamo->write_point = streamo->output_buffer;
} }
/** /**
@ -323,18 +322,14 @@ vips_streamo_write_unbuffered( VipsStreamo *streamo,
static int static int
vips_streamo_flush( VipsStreamo *streamo ) vips_streamo_flush( VipsStreamo *streamo )
{ {
int bytes_in_buffer = g_assert( streamo->write_point >= 0 );
VIPS_STREAMO_BUFFER_SIZE - streamo->bytes_remaining; g_assert( streamo->write_point <= VIPS_STREAMO_BUFFER_SIZE );
g_assert( bytes_in_buffer >= 0 ); if( streamo->write_point > 0 ) {
g_assert( bytes_in_buffer <= VIPS_STREAMO_BUFFER_SIZE );
if( bytes_in_buffer > 0 ) {
if( vips_streamo_write_unbuffered( streamo, if( vips_streamo_write_unbuffered( streamo,
streamo->output_buffer, bytes_in_buffer ) ) streamo->output_buffer, streamo->write_point ) )
return( -1 ); return( -1 );
streamo->bytes_remaining = VIPS_STREAMO_BUFFER_SIZE; streamo->write_point = 0;
streamo->write_point = streamo->output_buffer;
} }
return( 0 ); return( 0 );
@ -355,27 +350,20 @@ vips_streamo_write( VipsStreamo *streamo, const void *buffer, size_t length )
{ {
VIPS_DEBUG_MSG( "vips_streamo_write: %zd bytes\n", length ); VIPS_DEBUG_MSG( "vips_streamo_write: %zd bytes\n", length );
if( streamo->bytes_remaining >= length ) { if( length > VIPS_STREAMO_BUFFER_SIZE - streamo->write_point &&
memcpy( streamo->write_point, buffer, length ); vips_streamo_flush( streamo ) )
streamo->bytes_remaining -= length; return( -1 );
streamo->write_point += length;
if( length > VIPS_STREAMO_BUFFER_SIZE - streamo->write_point ) {
/* Still too large? Do an unbuffered write.
*/
if( vips_streamo_write_unbuffered( streamo, buffer, length ) )
return( -1 );
} }
else { else {
if( vips_streamo_flush( streamo ) ) memcpy( streamo->output_buffer + streamo->write_point,
return( -1 ); buffer, length );
streamo->write_point += length;
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 ); return( 0 );
@ -443,6 +431,30 @@ vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... )
return( result ); return( result );
} }
/**
* vips_streamo_putc:
* @streamo: output stream to operate on
* @ch: character to write
*
* Write a single character @ch to @streamo. See the macro VIPS_STREAMO_PUTC()
* for a faster way to do this.
*
* Returns: 0 on success, -1 on error.
*/
int
vips_streamo_putc( VipsStreamo *streamo, int ch )
{
VIPS_DEBUG_MSG( "vips_streamo_putc: %d\n", ch );
if( streamo->write_point > VIPS_STREAMO_BUFFER_SIZE &&
vips_streamo_flush( streamo ) )
return( -1 );
streamo->output_buffer[streamo->write_point++] = ch;
return( 0 );
}
/** /**
* vips_streamo_write_amp: * vips_streamo_write_amp:
* @streamo: output stream to operate on * @streamo: output stream to operate on
@ -497,8 +509,7 @@ vips_streamo_write_amp( VipsStreamo *streamo, const char *str )
return( -1 ); return( -1 );
} }
else { else {
if( !vips_streamo_write( streamo, if( !vips_streamo_putc( streamo, *p ) )
(guchar *) p, 1 ) )
return( -1 ); return( -1 );
} }