From 442720a0a118633d8dc6c5adbedc6fc6dcef8cd4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 11 Oct 2019 05:38:58 +0100 Subject: [PATCH] add magic "-" stdin filename this almost works! $ vips invert - x.jpg < k2.jpg --- libvips/foreign/foreign.c | 49 +++++++++++++++++++++++++++++++ libvips/foreign/jpeg2vips.c | 6 +++- libvips/foreign/jpegload.c | 4 ++- libvips/include/vips/foreign.h | 1 + libvips/include/vips/image.h | 5 +++- libvips/iofuncs/image.c | 42 +++++++++++++++++++++++++++ libvips/iofuncs/object.c | 31 ++++++++++++++++---- libvips/iofuncs/stream.c | 53 ++++++++++++++++++++++++---------- 8 files changed, 166 insertions(+), 25 deletions(-) diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 4135e15c..fdf87822 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -621,6 +621,55 @@ vips_foreign_find_load_buffer( const void *data, size_t size ) return( G_OBJECT_CLASS_NAME( load_class ) ); } +/* Can this VipsForeign open this stream? + */ +static void * +vips_foreign_find_load_stream_sub( void *item, void *a, void *b ) +{ + VipsForeignLoadClass *load_class = VIPS_FOREIGN_LOAD_CLASS( item ); + VipsStreamInput *input = VIPS_STREAM_INPUT( a ); + + if( load_class->is_a_stream && + load_class->is_a_stream( input ) ) + return( load_class ); + + return( NULL ); +} + +/** + * vips_foreign_find_load_stream: + * @input: stream to load from + * + * Searches for an operation you could use to load a stream. To see the + * range of buffer loaders supported by your vips, try something like: + * + * vips -l | grep load_stream + * + * See also: vips_image_new_from_stream(). + * + * Returns: (transfer none): the name of an operation on success, %NULL on + * error. + */ +const char * +vips_foreign_find_load_stream( VipsStreamInput *input ) +{ + VipsForeignLoadClass *load_class; + + if( !(load_class = (VipsForeignLoadClass *) vips_foreign_map( + "VipsForeignLoad", + vips_foreign_find_load_stream_sub, + input, NULL )) ) { + vips_error( "VipsForeignLoad", + "%s", _( "stream is not in a known format" ) ); + (void) vips_stream_input_rewind( input ); + return( NULL ); + } + + (void) vips_stream_input_rewind( input ); + + return( G_OBJECT_CLASS_NAME( load_class ) ); +} + /** * vips_foreign_is_a: * @loader: name of loader to use for test diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 150ae7b5..b6135c26 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -134,9 +134,9 @@ */ /* + */ #define DEBUG_VERBOSE #define DEBUG - */ #ifdef HAVE_CONFIG_H #include @@ -1216,6 +1216,10 @@ vips__jpeg_read_stream( VipsStreamInput *input, VipsImage *out, if( vips__jpeg_read( jpeg, out, header_only ) ) return( -1 ); + /* We've read the header ... it's pixel decode from now on. + vips_stream_input_decode( input ); + */ + return( 0 ); } diff --git a/libvips/foreign/jpegload.c b/libvips/foreign/jpegload.c index b55ba73d..cfe7e4a4 100644 --- a/libvips/foreign/jpegload.c +++ b/libvips/foreign/jpegload.c @@ -34,9 +34,9 @@ */ /* + */ #define DEBUG_VERBOSE #define DEBUG - */ #ifdef HAVE_CONFIG_H #include @@ -359,6 +359,8 @@ vips_foreign_load_jpeg_stream_load( VipsForeignLoad *load ) static gboolean vips_foreign_load_jpeg_stream_is_a( VipsStreamInput *input ) { + printf( "vips_foreign_load_jpeg_stream_is_a:\n" ); + return( vips__isjpeg_stream( input ) ); } diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index b7819b60..772dd12d 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -240,6 +240,7 @@ GType vips_foreign_load_get_type(void); const char *vips_foreign_find_load( const char *filename ); const char *vips_foreign_find_load_buffer( const void *data, size_t size ); +const char *vips_foreign_find_load_stream( VipsStreamInput *stream ); VipsForeignFlags vips_foreign_flags( const char *loader, const char *filename ); gboolean vips_foreign_is_a( const char *loader, const char *filename ); diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index be5dc94a..2e9b9992 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -450,13 +450,16 @@ VipsImage *vips_image_new_from_memory_copy( const void *data, size_t size, VipsImage *vips_image_new_from_buffer( const void *buf, size_t len, const char *option_string, ... ) __attribute__((sentinel)); +VipsImage *vips_image_new_from_stream( VipsStreamInput *input, + const char *option_string, ... ) __attribute__((sentinel)); VipsImage *vips_image_new_matrix( int width, int height ); VipsImage *vips_image_new_matrixv( int width, int height, ... ); VipsImage *vips_image_new_matrix_from_array( int width, int height, const double *array, int size ); VipsImage *vips_image_matrix_from_array( int width, int height, const double *array, int size ); -VipsImage *vips_image_new_from_image( VipsImage *image, const double *c, int n ); +VipsImage *vips_image_new_from_image( VipsImage *image, + const double *c, int n ); VipsImage *vips_image_new_from_image1( VipsImage *image, double c ); void vips_image_set_delete_on_close( VipsImage *image, diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index ce7bf1fc..d979dea6 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -2167,6 +2167,48 @@ vips_image_new_from_buffer( const void *buf, size_t len, return( out ); } +/** + * vips_image_new_from_stream: (constructor) + * @input: (transfer none): stream to fetch image from + * @option_string: set of extra options as a string + * @...: %NULL-terminated list of optional named arguments + * + * Loads an image from the formatted stream @input, + * loader recommended by vips_foreign_find_load_stream(). + * + * Load options may be given in @option_string as "[name=value,...]" or given as + * a NULL-terminated list of name-value pairs at the end of the arguments. + * Options given in the function call override options given in the string. + * + * See also: vips_image_write_to_buffer(). + * + * Returns: (transfer full): the new #VipsImage, or %NULL on error. + */ +VipsImage * +vips_image_new_from_stream( VipsStreamInput *input, + const char *option_string, ... ) +{ + const char *operation_name; + va_list ap; + int result; + VipsImage *out; + + vips_check_init(); + + if( !(operation_name = vips_foreign_find_load_stream( input )) ) + return( NULL ); + + va_start( ap, option_string ); + result = vips_call_split_option_string( operation_name, + option_string, ap, input, &out ); + va_end( ap ); + + if( result ) + return( NULL ); + + return( out ); +} + /** * vips_image_new_matrix: (constructor) * @width: image width diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 39567f53..5520b057 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -1899,16 +1899,35 @@ vips_object_set_argument_from_string( VipsObject *object, /* Read the filename. */ - if( flags & (VIPS_OPERATION_SEQUENTIAL_UNBUFFERED | - VIPS_OPERATION_SEQUENTIAL) ) + if( flags & + (VIPS_OPERATION_SEQUENTIAL_UNBUFFERED | + VIPS_OPERATION_SEQUENTIAL) ) access = VIPS_ACCESS_SEQUENTIAL; else access = VIPS_ACCESS_RANDOM; - if( !(out = vips_image_new_from_file( value, - "access", access, - NULL )) ) - return( -1 ); + /* The special filename "-" means stdin. + */ + if( strcmp( value, "-" ) == 0 ) { + VipsStreamInput *input; + + if( !(input = + vips_stream_input_new_from_descriptor( 0 )) ) + return( -1 ); + if( !(out = vips_image_new_from_stream( input, "", + "access", access, + NULL )) ) { + VIPS_UNREF( input ); + return( -1 ); + } + VIPS_UNREF( input ); + } + else { + if( !(out = vips_image_new_from_file( value, + "access", access, + NULL )) ) + return( -1 ); + } g_value_init( &gvalue, VIPS_TYPE_IMAGE ); g_value_set_object( &gvalue, out ); diff --git a/libvips/iofuncs/stream.c b/libvips/iofuncs/stream.c index 1716956f..49803881 100644 --- a/libvips/iofuncs/stream.c +++ b/libvips/iofuncs/stream.c @@ -41,8 +41,8 @@ */ /* -#define VIPS_DEBUG */ +#define VIPS_DEBUG #ifdef HAVE_CONFIG_H #include @@ -243,6 +243,8 @@ vips_stream_input_read_real( VipsStreamInput *input, { VipsStream *stream = VIPS_STREAM( input ); + VIPS_DEBUG_MSG( "vips_stream_input_read_real:\n" ); + if( input->blob ) { VipsArea *area = (VipsArea *) input->blob; ssize_t available = VIPS_MIN( length, @@ -255,7 +257,7 @@ vips_stream_input_read_real( VipsStreamInput *input, return( available ); } - else if( stream->descriptor ) { + else if( stream->descriptor != -1 ) { return( read( stream->descriptor, buffer, length ) ); } else { @@ -269,6 +271,8 @@ vips_stream_input_rewind_real( VipsStreamInput *input ) { VipsStream *stream = VIPS_STREAM( input ); + VIPS_DEBUG_MSG( "vips_stream_input_rewind_real:\n" ); + if( input->decode ) { vips_error( STREAM_NAME( stream ), "%s", _( "can't rewind after decode begins" ) ); @@ -279,6 +283,9 @@ vips_stream_input_rewind_real( VipsStreamInput *input ) stream->descriptor != -1 ) { off_t new_pos; + VIPS_DEBUG_MSG( " rewinding desriptor %d\n", + stream->descriptor ); + new_pos = lseek( stream->descriptor, 0, SEEK_SET ); if( new_pos == -1 ) { vips_error_system( errno, STREAM_NAME( stream ), @@ -468,12 +475,14 @@ vips_stream_input_read( VipsStreamInput *input, ssize_t bytes_read; + VIPS_DEBUG_MSG( "vips_stream_input_read:\n" ); + bytes_read = 0; /* Are we serving from header_bytes? Get what we can from there. */ if( input->header_bytes && - input->header_bytes->len < input->read_position ) { + input->read_position < input->header_bytes->len ) { ssize_t available; available = VIPS_MIN( length, @@ -485,19 +494,21 @@ vips_stream_input_read( VipsStreamInput *input, buffer += available; length -= available; bytes_read += available; + + VIPS_DEBUG_MSG( " %zd bytes from cache\n", available ); } /* Any more bytes required? Call the read() method. */ if( length > 0 ) { - ssize_t read; + ssize_t n; - if( (read = class->read( input, buffer, length )) == -1 ) { + if( (n = class->read( input, buffer, length )) == -1 ) { vips_error_system( errno, STREAM_NAME( input ), "%s", _( "read error" ) ); return( -1 ); } - if( read == 0 ) + if( n == 0 ) input->eof = TRUE; /* If we're not rewindable, we need to save header bytes for @@ -506,14 +517,18 @@ vips_stream_input_read( VipsStreamInput *input, if( input->header_bytes && !input->rewindable && !input->decode && - read > 0 ) + n > 0 ) g_byte_array_append( input->header_bytes, - buffer, read ); + buffer, n ); - input->read_position += read; - bytes_read += read; + input->read_position += n; + bytes_read += n; + + VIPS_DEBUG_MSG( " %zd bytes from read()\n", n ); } + VIPS_DEBUG_MSG( " %zd bytes total\n", bytes_read ); + return( bytes_read ); } @@ -522,18 +537,24 @@ vips_stream_input_rewind( VipsStreamInput *input ) { VipsStreamInputClass *class = VIPS_STREAM_INPUT_GET_CLASS( input ); + VIPS_DEBUG_MSG( "vips_stream_input_rewind:\n" ); + return( class->rewind( input ) ); } gboolean vips_stream_input_eof( VipsStreamInput *input ) { + VIPS_DEBUG_MSG( "vips_stream_input_eof:\n" ); + return( input->eof ); } void vips_stream_input_decode( VipsStreamInput *input ) { + VIPS_DEBUG_MSG( "vips_stream_input_decode:\n" ); + input->decode = TRUE; VIPS_FREEF( g_byte_array_unref, input->header_bytes ); VIPS_FREEF( g_byte_array_unref, input->sniff ); @@ -548,20 +569,20 @@ vips_stream_input_decode( VipsStreamInput *input ) unsigned char * vips_stream_input_sniff( VipsStreamInput *input, size_t length ) { - ssize_t read; + ssize_t n; unsigned char *q; + VIPS_DEBUG_MSG( "vips_stream_input_sniff: %zd bytes\n", length ); + if( vips_stream_input_rewind( input ) ) return( NULL ); g_byte_array_set_size( input->sniff, length ); - for( q = input->sniff->data; length > 0; length -= read, q += read ) { - read = vips_stream_input_read( input, q, length ); - if( read == -1 || - read == 0 ) + for( q = input->sniff->data; length > 0; length -= n, q += n ) + if( (n = vips_stream_input_read( input, q, length )) == -1 || + n == 0 ) return( NULL ); - } return( input->sniff->data ); }