diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index fdfac85b..6409d588 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -149,8 +149,8 @@ int vips__ppm_isppm( const char *filename ); VipsForeignFlags vips__ppm_flags( const char *filename ); extern const char *vips__ppm_suffs[]; -int vips__ppm_save( VipsImage *in, const char *filename, - gboolean ascii, gboolean squash ); +int vips__ppm_save_stream( VipsImage *in, VipsStreamo *streamo, + gboolean ascii, gboolean squash ); int vips__rad_israd( VipsStreami *streami ); int vips__rad_header( VipsStreami *streami, VipsImage *out ); diff --git a/libvips/foreign/ppm.c b/libvips/foreign/ppm.c index 7fe27c01..df0289b9 100644 --- a/libvips/foreign/ppm.c +++ b/libvips/foreign/ppm.c @@ -36,6 +36,8 @@ * linebreaks * 29/7/19 Kyle-Kyle * - 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 { VipsImage *in; - FILE *fp; - char *name; + VipsStreamo *streamo; write_fn fn; } Write; static void write_destroy( Write *write ) { - VIPS_FREEF( fclose, write->fp ); - VIPS_FREE( write->name ); + if( write->streamo ) + vips_streamo_finish( write->streamo ); + VIPS_UNREF( write->streamo ); vips_free( write ); } static Write * -write_new( VipsImage *in, const char *name ) +write_new( VipsImage *in, VipsStreamo *streamo ) { Write *write; @@ -578,14 +580,10 @@ write_new( VipsImage *in, const char *name ) return( NULL ); write->in = in; - write->name = vips_strdup( NULL, name ); - write->fp = vips__file_open_write( name, FALSE ); + write->streamo = streamo; + g_object_ref( streamo ); + write->fn = NULL; - if( !write->name || !write->fp ) { - write_destroy( write ); - return( NULL ); - } - return( write ); } @@ -599,17 +597,17 @@ write_ppm_line_ascii( Write *write, VipsPel *p ) for( k = 0; k < write->in->Bands; k++ ) { switch( write->in->BandFmt ) { case VIPS_FORMAT_UCHAR: - fprintf( write->fp, + vips_streamo_writef( write->streamo, "%d ", p[k] ); break; case VIPS_FORMAT_USHORT: - fprintf( write->fp, + vips_streamo_writef( write->streamo, "%d ", ((unsigned short *) p)[k] ); break; case VIPS_FORMAT_UINT: - fprintf( write->fp, + vips_streamo_writef( write->streamo, "%d ", ((unsigned int *) p)[k] ); break; @@ -621,11 +619,8 @@ write_ppm_line_ascii( Write *write, VipsPel *p ) p += sk; } - if( !fprintf( write->fp, "\n" ) ) { - vips_error_system( errno, "vips2ppm", - "%s", _( "write error" ) ); + if( vips_streamo_writef( write->streamo, "\n" ) ) return( -1 ); - } return( 0 ); } @@ -636,13 +631,10 @@ write_ppm_line_ascii_squash( Write *write, VipsPel *p ) int 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" ) ) { - vips_error_system( errno, "vips2ppm", - "%s", _( "write error" ) ); + if( vips_streamo_writef( write->streamo, "\n" ) ) return( -1 ); - } return( 0 ); } @@ -650,8 +642,8 @@ write_ppm_line_ascii_squash( Write *write, VipsPel *p ) static int write_ppm_line_binary( Write *write, VipsPel *p ) { - if( vips__file_write( p, VIPS_IMAGE_SIZEOF_LINE( write->in ), 1, - write->fp ) ) + if( vips_streamo_write( write->streamo, + p, VIPS_IMAGE_SIZEOF_LINE( write->in ) ) ) return( -1 ); return( 0 ); @@ -672,11 +664,8 @@ write_ppm_line_binary_squash( Write *write, VipsPel *p ) bits |= p[x] ? 0 : 1; if( n_bits == 8 ) { - if( fputc( bits, write->fp ) == EOF ) { - vips_error_system( errno, "vips2ppm", - "%s", _( "write error" ) ); + if( VIPS_STREAMO_PUTC( write->streamo, bits ) ) return( -1 ); - } 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. */ - if( n_bits ) { - if( fputc( bits, write->fp ) == EOF ) { - vips_error_system( errno, "vips2ppm", - "%s", _( "write error" ) ); - return( -1 ); - } - } + if( n_bits && + VIPS_STREAMO_PUTC( write->streamo, bits ) ) + return( -1 ); return( 0 ); } @@ -740,23 +725,27 @@ write_ppm( Write *write, gboolean ascii, gboolean squash ) else g_assert_not_reached(); - fprintf( write->fp, "%s\n", magic ); + vips_streamo_writef( write->streamo, "%s\n", magic ); time( &timebuf ); - fprintf( write->fp, "#vips2ppm - %s\n", ctime( &timebuf ) ); - fprintf( write->fp, "%d %d\n", in->Xsize, in->Ysize ); + vips_streamo_writef( write->streamo, + "#vips2ppm - %s\n", ctime( &timebuf ) ); + vips_streamo_writef( write->streamo, "%d %d\n", in->Xsize, in->Ysize ); if( !squash ) switch( in->BandFmt ) { case VIPS_FORMAT_UCHAR: - fprintf( write->fp, "%d\n", UCHAR_MAX ); + vips_streamo_writef( write->streamo, + "%d\n", UCHAR_MAX ); break; case VIPS_FORMAT_USHORT: - fprintf( write->fp, "%d\n", USHRT_MAX ); + vips_streamo_writef( write->streamo, + "%d\n", USHRT_MAX ); break; case VIPS_FORMAT_UINT: - fprintf( write->fp, "%d\n", UINT_MAX ); + vips_streamo_writef( write->streamo, + "%d\n", UINT_MAX ); break; case VIPS_FORMAT_FLOAT: @@ -767,7 +756,8 @@ write_ppm( Write *write, gboolean ascii, gboolean squash ) scale = 1; if( !vips_amiMSBfirst() ) scale *= -1; - fprintf( write->fp, "%g\n", scale ); + vips_streamo_writef( write->streamo, + "%g\n", scale ); } break; @@ -791,7 +781,7 @@ write_ppm( Write *write, gboolean ascii, gboolean squash ) } int -vips__ppm_save( VipsImage *in, const char *filename, +vips__ppm_save_stream( VipsImage *in, VipsStreamo *streamo, gboolean ascii, gboolean squash ) { Write *write; @@ -820,7 +810,7 @@ vips__ppm_save( VipsImage *in, const char *filename, squash = FALSE; } - if( !(write = write_new( in, filename )) ) + if( !(write = write_new( in, streamo )) ) return( -1 ); if( write_ppm( write, ascii, squash ) ) { diff --git a/libvips/foreign/ppmsave.c b/libvips/foreign/ppmsave.c index 33f0cc4b..3b986d0f 100644 --- a/libvips/foreign/ppmsave.c +++ b/libvips/foreign/ppmsave.c @@ -70,13 +70,20 @@ vips_foreign_save_ppm_build( VipsObject *object ) VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object; + VipsStreamo *streamo; + if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )-> build( object ) ) return( -1 ); - if( vips__ppm_save( save->ready, ppm->filename, - ppm->ascii, ppm->squash ) ) + if( !(streamo = vips_streamo_new_to_filename( ppm->filename )) ) return( -1 ); + if( vips__ppm_save_stream( save->ready, streamo, + ppm->ascii, ppm->squash ) ) { + VIPS_UNREF( streamo ); + return( -1 ); + } + VIPS_UNREF( streamo ); return( 0 ); } diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index 21beee53..4e0e0fc1 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -365,11 +365,11 @@ typedef struct _VipsStreamo { */ 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 *write_point; - int bytes_remaining; + int write_point; } VipsStreamo; @@ -395,6 +395,14 @@ VipsStreamo *vips_streamo_new_to_memory( void ); int vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length ); 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, ... ) __attribute__((format(printf, 2, 3))); diff --git a/libvips/iofuncs/streamo.c b/libvips/iofuncs/streamo.c index 77933dca..7c0d62dd 100644 --- a/libvips/iofuncs/streamo.c +++ b/libvips/iofuncs/streamo.c @@ -198,8 +198,7 @@ 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; + streamo->write_point = 0; } /** @@ -323,18 +322,14 @@ vips_streamo_write_unbuffered( VipsStreamo *streamo, static int vips_streamo_flush( VipsStreamo *streamo ) { - int bytes_in_buffer = - VIPS_STREAMO_BUFFER_SIZE - streamo->bytes_remaining; + g_assert( streamo->write_point >= 0 ); + g_assert( streamo->write_point <= VIPS_STREAMO_BUFFER_SIZE ); - g_assert( bytes_in_buffer >= 0 ); - g_assert( bytes_in_buffer <= VIPS_STREAMO_BUFFER_SIZE ); - - if( bytes_in_buffer > 0 ) { + if( streamo->write_point > 0 ) { if( vips_streamo_write_unbuffered( streamo, - streamo->output_buffer, bytes_in_buffer ) ) + streamo->output_buffer, streamo->write_point ) ) return( -1 ); - streamo->bytes_remaining = VIPS_STREAMO_BUFFER_SIZE; - streamo->write_point = streamo->output_buffer; + streamo->write_point = 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 ); - if( streamo->bytes_remaining >= length ) { - memcpy( streamo->write_point, buffer, length ); - streamo->bytes_remaining -= length; - streamo->write_point += length; + if( length > VIPS_STREAMO_BUFFER_SIZE - streamo->write_point && + vips_streamo_flush( streamo ) ) + return( -1 ); + + 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 { - 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 ); + memcpy( streamo->output_buffer + streamo->write_point, + buffer, length ); + streamo->write_point += length; } return( 0 ); @@ -443,6 +431,30 @@ vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... ) 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: * @streamo: output stream to operate on @@ -497,8 +509,7 @@ vips_streamo_write_amp( VipsStreamo *streamo, const char *str ) return( -1 ); } else { - if( !vips_streamo_write( streamo, - (guchar *) p, 1 ) ) + if( !vips_streamo_putc( streamo, *p ) ) return( -1 ); }