From 55d2ba8a4dd867a182b76469acfc6cad3c35fbb6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 14 Oct 2019 18:32:41 +0100 Subject: [PATCH] start adding png stream save doesn't pass the test suite yet --- libvips/foreign/pforeign.h | 6 +- libvips/foreign/pngsave.c | 134 +++++++++++++++++++++++++++++---- libvips/foreign/vipspng.c | 88 ++++++---------------- libvips/include/vips/foreign.h | 4 + 4 files changed, 146 insertions(+), 86 deletions(-) diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 2c3c17bb..5446e937 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -190,14 +190,10 @@ int vips__png_read_stream( VipsStreamInput *input, VipsImage *out, gboolean vips__png_isinterlaced_stream( VipsStreamInput *input ); extern const char *vips__png_suffs[]; -int vips__png_write( VipsImage *in, const char *filename, +int vips__png_write_stream( VipsImage *in, VipsStreamOutput *output, int compress, int interlace, const char *profile, VipsForeignPngFilter filter, gboolean strip, gboolean palette, int colours, int Q, double dither ); -int vips__png_write_buf( VipsImage *in, - void **obuf, size_t *olen, int compression, int interlace, - const char *profile, VipsForeignPngFilter filter, gboolean strip, - gboolean palette, int colours, int Q, double dither ); /* Map WEBP metadata names to vips names. */ diff --git a/libvips/foreign/pngsave.c b/libvips/foreign/pngsave.c index ab576248..40d8950b 100644 --- a/libvips/foreign/pngsave.c +++ b/libvips/foreign/pngsave.c @@ -179,6 +179,63 @@ vips_foreign_save_png_init( VipsForeignSavePng *png ) png->dither = 1.0; } +typedef struct _VipsForeignSavePngStream { + VipsForeignSavePng parent_object; + + VipsStreamOutput *output; +} VipsForeignSavePngStream; + +typedef VipsForeignSavePngClass VipsForeignSavePngStreamClass; + +G_DEFINE_TYPE( VipsForeignSavePngStream, vips_foreign_save_png_stream, + vips_foreign_save_png_get_type() ); + +static int +vips_foreign_save_png_stream_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + VipsForeignSavePng *png = (VipsForeignSavePng *) object; + VipsForeignSavePngStream *stream = (VipsForeignSavePngStream *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_png_stream_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__png_write_stream( save->ready, stream->output, + png->compression, png->interlace, png->profile, png->filter, + save->strip, png->palette, png->colours, png->Q, png->dither ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_save_png_stream_class_init( VipsForeignSavePngStreamClass *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 = "pngsave_stream"; + object_class->description = _( "save image to png stream" ); + object_class->build = vips_foreign_save_png_stream_build; + + VIPS_ARG_OBJECT( class, "output", 1, + _( "Output" ), + _( "Stream to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePngStream, output ), + VIPS_TYPE_STREAM_OUTPUT ); + +} + +static void +vips_foreign_save_png_stream_init( VipsForeignSavePngStream *stream ) +{ +} + typedef struct _VipsForeignSavePngFile { VipsForeignSavePng parent_object; @@ -197,15 +254,23 @@ vips_foreign_save_png_file_build( VipsObject *object ) VipsForeignSavePng *png = (VipsForeignSavePng *) object; VipsForeignSavePngFile *png_file = (VipsForeignSavePngFile *) object; + VipsStreamOutput *output; + if( VIPS_OBJECT_CLASS( vips_foreign_save_png_file_parent_class )-> build( object ) ) return( -1 ); - if( vips__png_write( save->ready, - png_file->filename, png->compression, png->interlace, - png->profile, png->filter, save->strip, png->palette, - png->colours, png->Q, png->dither ) ) + if( !(output = vips_stream_output_new_from_filename( + png_file->filename )) ) return( -1 ); + if( vips__png_write_stream( save->ready, output, + png->compression, png->interlace, + png->profile, png->filter, save->strip, png->palette, + png->colours, png->Q, png->dither ) ) { + VIPS_UNREF( output ); + return( -1 ); + } + VIPS_UNREF( output ); return( 0 ); } @@ -252,27 +317,32 @@ vips_foreign_save_png_buffer_build( VipsObject *object ) { VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSavePng *png = (VipsForeignSavePng *) object; + VipsForeignSavePngBuffer *buffer = (VipsForeignSavePngBuffer *) object; - void *obuf; - size_t olen; + VipsStreamOutput *output; VipsBlob *blob; if( VIPS_OBJECT_CLASS( vips_foreign_save_png_buffer_parent_class )-> build( object ) ) return( -1 ); - if( vips__png_write_buf( save->ready, &obuf, &olen, - png->compression, png->interlace, png->profile, png->filter, - save->strip, png->palette, png->colours, png->Q, png->dither ) ) + if( !(output = vips_stream_output_new_memory()) ) return( -1 ); - /* vips__png_write_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__png_write_stream( save->ready, output, + png->compression, png->interlace, png->profile, png->filter, + save->strip, png->palette, png->colours, png->Q, + png->dither ) ) { + VIPS_UNREF( output ); + return( -1 ); + } + + g_object_get( output, "blob", &blob, NULL ); + g_object_set( buffer, "buffer", blob, NULL ); vips_area_unref( VIPS_AREA( blob ) ); + VIPS_UNREF( output ); + return( 0 ); } @@ -427,3 +497,39 @@ vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) return( result ); } + +/** + * vips_pngsave_stream: (method) + * @in: image to save + * @output: save image to this stream + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @compression: compression level + * * @interlace: interlace image + * * @profile: ICC profile to embed + * * @filter: libpng row filter flag(s) + * * @palette: enable quantisation to 8bpp palette + * * @colours: max number of palette colours for quantisation + * * @Q: quality for 8bpp quantisation (does not exceed @colours) + * * @dither: amount of dithering for 8bpp quantization + * + * As vips_pngsave(), but save to a stream. + * + * See also: vips_pngsave(), vips_image_write_to_stream(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_pngsave_stream( VipsImage *in, VipsStreamOutput *output, ... ) +{ + va_list ap; + int result; + + va_start( ap, output ); + result = vips_call_split( "pngsave_stream", ap, in, output ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 2c1ab951..9192658f 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -270,7 +270,7 @@ read_new( VipsStreamInput *input, VipsImage *out, gboolean fail ) g_signal_connect( out, "close", G_CALLBACK( read_close_cb ), read ); - g_signal_connect( out, "minimise", + g_signal_connect( out, "minimise", G_CALLBACK( read_minimise_cb ), read ); if( !(read->pPng = png_create_read_struct( @@ -780,8 +780,7 @@ typedef struct { VipsImage *in; VipsImage *memory; - FILE *fp; - VipsDbuf dbuf; + VipsStreamOutput *output; png_structp pPng; png_infop pInfo; @@ -791,9 +790,8 @@ typedef struct { static void write_finish( Write *write ) { - VIPS_FREEF( fclose, write->fp ); VIPS_UNREF( write->memory ); - vips_dbuf_destroy( &write->dbuf ); + VIPS_UNREF( write->output ); if( write->pPng ) png_destroy_write_struct( &write->pPng, &write->pInfo ); } @@ -804,8 +802,17 @@ write_destroy( VipsImage *out, Write *write ) write_finish( write ); } +static void +user_write_data( png_structp pPng, png_bytep data, png_size_t length ) +{ + Write *write = (Write *) png_get_io_ptr( pPng ); + + if( vips_stream_output_write( write->output, data, length ) ) + png_error( pPng, "not enough data" ); +} + static Write * -write_new( VipsImage *in ) +write_new( VipsImage *in, VipsStreamOutput *output ) { Write *write; @@ -814,8 +821,8 @@ write_new( VipsImage *in ) memset( write, 0, sizeof( Write ) ); write->in = in; write->memory = NULL; - write->fp = NULL; - vips_dbuf_init( &write->dbuf ); + write->output = output; + g_object_ref( output ); g_signal_connect( in, "close", G_CALLBACK( write_destroy ), write ); @@ -833,6 +840,8 @@ write_new( VipsImage *in ) PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON ); #endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/ + png_set_write_fn( write->pPng, write, user_write_data, NULL ); + /* Catch PNG errors from png_create_info_struct(). */ if( setjmp( png_jmpbuf( write->pPng ) ) ) @@ -1176,80 +1185,25 @@ write_vips( Write *write, } int -vips__png_write( VipsImage *in, const char *filename, - int compress, int interlace, const char *profile, - VipsForeignPngFilter filter, gboolean strip, - gboolean palette, int colours, int Q, double dither ) -{ - Write *write; - -#ifdef DEBUG - printf( "vips__png_write: writing \"%s\"\n", filename ); -#endif /*DEBUG*/ - - if( !(write = write_new( in )) ) - return( -1 ); - - /* Make output. - */ - if( !(write->fp = vips__file_open_write( filename, FALSE )) ) - return( -1 ); - png_init_io( write->pPng, write->fp ); - - /* Convert it! - */ - if( write_vips( write, - compress, interlace, profile, filter, strip, palette, - colours, Q, dither ) ) { - vips_error( "vips2png", - _( "unable to write \"%s\"" ), filename ); - - return( -1 ); - } - - write_finish( write ); - -#ifdef DEBUG - printf( "vips__png_write: done\n" ); -#endif /*DEBUG*/ - - return( 0 ); -} - -static void -user_write_data( png_structp png_ptr, png_bytep data, png_size_t length ) -{ - Write *write = (Write *) png_get_io_ptr( png_ptr ); - - vips_dbuf_write( &write->dbuf, data, length ); -} - -int -vips__png_write_buf( VipsImage *in, - void **obuf, size_t *olen, int compression, int interlace, +vips__png_write_stream( VipsImage *in, VipsStreamOutput *output, + int compression, int interlace, const char *profile, VipsForeignPngFilter filter, gboolean strip, gboolean palette, int colours, int Q, double dither ) { Write *write; - if( !(write = write_new( in )) ) + if( !(write = write_new( in, output )) ) return( -1 ); - png_set_write_fn( write->pPng, write, user_write_data, NULL ); - - /* Convert it! - */ if( write_vips( write, compression, interlace, profile, filter, strip, palette, colours, Q, dither ) ) { + write_finish( write ); vips_error( "vips2png", "%s", _( "unable to write to buffer" ) ); - return( -1 ); } - *obuf = vips_dbuf_steal( &write->dbuf, olen ); - write_finish( write ); return( 0 ); diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 5b2ea91d..ae7c77b5 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -369,6 +369,8 @@ int vips_jpegload( const char *filename, VipsImage **out, ... ) int vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_jpegsave_stream( VipsImage *in, VipsStreamOutput *output, ... ) + __attribute__((sentinel)); int vips_jpegsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); int vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) @@ -547,6 +549,8 @@ int vips_pngload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_pngsave_stream( VipsImage *in, VipsStreamOutput *output, ... ) + __attribute__((sentinel)); int vips_pngsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); int vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... )