From c01c3b91009e938c6a6601c9dc75153e2e2548bf Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 14 Oct 2019 10:46:17 +0100 Subject: [PATCH] add minimise support to VipsStreamInput --- libvips/foreign/jpeg2vips.c | 15 +++-- libvips/include/vips/stream.h | 7 +- libvips/iofuncs/stream.c | 122 +++++++++++++++++++++------------- test/test_descriptors.sh | 7 +- 4 files changed, 91 insertions(+), 60 deletions(-) diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 8948d408..6b89fe69 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -335,6 +335,12 @@ readjpeg_close_cb( VipsObject *object, ReadJpeg *jpeg ) (void) readjpeg_free( jpeg ); } +static void +vips_stream_input_minimise_cb( VipsImage *image, VipsStreamInput *input ) +{ + vips_stream_input_minimise( input ); +} + static ReadJpeg * readjpeg_new( VipsStreamInput *input, VipsImage *out, int shrink, gboolean fail, gboolean autorotate ) @@ -370,6 +376,8 @@ readjpeg_new( VipsStreamInput *input, VipsImage *out, g_signal_connect( out, "close", G_CALLBACK( readjpeg_close_cb ), jpeg ); + g_signal_connect( out, "minimise", + G_CALLBACK( vips_stream_input_minimise_cb ), input ); return( jpeg ); } @@ -777,11 +785,6 @@ read_jpeg_generate( VipsRegion *or, return( -1 ); } - /* We may have been minimised. - */ - if( readjpeg_open_input( jpeg ) ) - return( -1 ); - /* If --fail is set, we make read fail on any warnings. This * will stop on any errors from the previous jpeg_read_scanlines(). * libjpeg warnings are used for serious image corruption, like @@ -946,6 +949,8 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only ) */ vips_autorot_remove_angle( out ); } + + vips_stream_input_minimise( jpeg->input ); } else { if( read_jpeg_image( jpeg, out ) ) diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index 64790c3f..5e29d8c1 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -122,10 +122,10 @@ typedef struct _VipsStreamInput { */ gboolean decode; - /* TRUE is this input source supports rewind. If not, then we save data + /* TRUE is this descriptor supports lseek(). If not, then we save data * read during header phase in a buffer. */ - gboolean rewindable; + gboolean seekable; /*< private >*/ @@ -183,8 +183,7 @@ VipsStreamInput *vips_stream_input_new_from_options( const char *options ); ssize_t vips_stream_input_read( VipsStreamInput *input, unsigned char *data, size_t length ); int vips_stream_input_rewind( VipsStreamInput *input ); -void vips_stream_minimise( VipsStreamInput *input ); -void vips_stream_set_image( VipsStreamInput *input, VipsImage *image ); +void vips_stream_input_minimise( VipsStreamInput *input ); void vips_stream_input_decode( VipsStreamInput *input ); gboolean vips_stream_input_eof( VipsStreamInput *input ); unsigned char *vips_stream_input_sniff( VipsStreamInput *input, size_t length ); diff --git a/libvips/iofuncs/stream.c b/libvips/iofuncs/stream.c index 5dc96241..5998b14e 100644 --- a/libvips/iofuncs/stream.c +++ b/libvips/iofuncs/stream.c @@ -121,16 +121,8 @@ G_DEFINE_ABSTRACT_TYPE( VipsStream, vips_stream, VIPS_TYPE_OBJECT ); VIPS_OBJECT( STREAM )->nickname) static void -vips_stream_finalize( GObject *gobject ) +vips_stream_close( VipsStream *stream ) { - VipsStream *stream = (VipsStream *) gobject; - -#ifdef VIPS_DEBUG - VIPS_DEBUG_MSG( "vips_stream_finalize: " ); - vips_object_print_name( VIPS_OBJECT( gobject ) ); - VIPS_DEBUG_MSG( "\n" ); -#endif /*VIPS_DEBUG*/ - if( stream->close_descriptor >= 0 ) { close( stream->close_descriptor ); stream->close_descriptor = -1; @@ -141,6 +133,21 @@ vips_stream_finalize( GObject *gobject ) stream->tracked_descriptor = -1; } + stream->descriptor = -1; +} + +static void +vips_stream_finalize( GObject *gobject ) +{ + VipsStream *stream = (VipsStream *) gobject; + +#ifdef VIPS_DEBUG + VIPS_DEBUG_MSG( "vips_stream_finalize: " ); + vips_object_print_name( VIPS_OBJECT( gobject ) ); + VIPS_DEBUG_MSG( "\n" ); +#endif /*VIPS_DEBUG*/ + + vips_stream_close( stream ); VIPS_FREE( stream->filename ); G_OBJECT_CLASS( vips_stream_parent_class )->finalize( gobject ); @@ -192,6 +199,41 @@ vips_stream_input_finalize( GObject *gobject ) G_OBJECT_CLASS( vips_stream_input_parent_class )->finalize( gobject ); } +static int +vips_stream_input_open( VipsStreamInput *input ) +{ + VipsStream *stream = VIPS_STREAM( input ); + + if( stream->descriptor == -1 && + stream->tracked_descriptor == -1 && + stream->filename ) { + int fd; + off_t new_pos; + + if( (fd = vips_tracked_open( stream->filename, + MODE_READ )) == -1 ) { + vips_error_system( errno, STREAM_NAME( stream ), + "%s", _( "unable to open for read" ) ); + return( -1 ); + } + + stream->tracked_descriptor = fd; + stream->descriptor = fd; + input->seekable = TRUE; + + VIPS_DEBUG_MSG( "vips_stream_input_open: " + "restoring read position %zd\n", input->read_position ); + new_pos = lseek( stream->descriptor, 0, SEEK_SET ); + if( new_pos == -1 ) { + vips_error_system( errno, STREAM_NAME( stream ), + "%s", _( "unable to lseek()" ) ); + return( 0 ); + } + } + + return( 0 ); +} + static int vips_stream_input_build( VipsObject *object ) { @@ -211,20 +253,9 @@ vips_stream_input_build( VipsObject *object ) return( -1 ); } - if( vips_object_argument_isset( object, "filename" ) ) { - int fd; - - if( (fd = vips_tracked_open( stream->filename, - MODE_READ )) == -1 ) { - vips_error_system( errno, STREAM_NAME( stream ), - "%s", _( "unable to open for read" ) ); - return( -1 ); - } - - stream->tracked_descriptor = fd; - stream->descriptor = fd; - input->rewindable = TRUE; - } + if( vips_object_argument_isset( object, "filename" ) && + vips_stream_input_open( input ) ) + return( -1 ); if( vips_object_argument_isset( object, "descriptor" ) ) { stream->descriptor = dup( stream->descriptor ); @@ -232,11 +263,11 @@ vips_stream_input_build( VipsObject *object ) } if( vips_object_argument_isset( object, "blob" ) ) - input->rewindable = TRUE; + input->seekable = TRUE; - /* Need to save the header if the source is not rewindable. + /* Need to save the header if the source is not seekable. */ - if( !input->rewindable ) + if( !input->seekable ) input->header_bytes = g_byte_array_new(); /* We always want a sniff buffer. @@ -254,6 +285,11 @@ vips_stream_input_read_real( VipsStreamInput *input, VIPS_DEBUG_MSG( "vips_stream_input_read_real:\n" ); + /* Make sure we are open, in case we've been minimised. + */ + if( vips_stream_input_open( input ) ) + return( -1 ); + if( input->blob ) { VipsArea *area = (VipsArea *) input->blob; ssize_t available = VIPS_MIN( length, @@ -288,7 +324,7 @@ vips_stream_input_rewind_real( VipsStreamInput *input ) return( -1 ); } - if( input->rewindable && + if( input->seekable && stream->descriptor != -1 ) { off_t new_pos; @@ -312,7 +348,12 @@ vips_stream_input_rewind_real( VipsStreamInput *input ) static void vips_stream_input_minimise_real( VipsStreamInput *input ) { - VIPS_DEBUG_MSG( "vips_stream_minimise_real:\n" ); + VipsStream *stream = VIPS_STREAM( input ); + + if( stream->filename && + stream->descriptor != -1 && + input->seekable ) + vips_stream_close( stream ); } static void @@ -341,11 +382,11 @@ vips_stream_input_class_init( VipsStreamInputClass *class ) G_STRUCT_OFFSET( VipsStreamInput, blob ), VIPS_TYPE_BLOB ); - VIPS_ARG_BOOL( class, "rewindable", 4, - _( "rewindable" ), - _( "'descriptor' supports rewind" ), + VIPS_ARG_BOOL( class, "seekable", 4, + _( "Seekable" ), + _( "'descriptor' supports lseek()" ), VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsStreamInput, rewindable ), + G_STRUCT_OFFSET( VipsStreamInput, seekable ), FALSE ); } @@ -544,11 +585,11 @@ vips_stream_input_read( VipsStreamInput *input, if( n == 0 ) input->eof = TRUE; - /* If we're not rewindable, we need to save header bytes for + /* If we're not seekable, we need to save header bytes for * reuse. */ if( input->header_bytes && - !input->rewindable && + !input->seekable && !input->decode && n > 0 ) g_byte_array_append( input->header_bytes, @@ -603,19 +644,6 @@ vips_stream_input_decode( VipsStreamInput *input ) } } -static void -vips_stream_input_minimise_cb( VipsImage *image, VipsStreamInput *input ) -{ - vips_stream_input_minimise( input ); -} - -void -vips_stream_input_set_image( VipsStreamInput *input, VipsImage *image ) -{ - g_signal_connect( image, "minimise", - G_CALLBACK( vips_stream_input_minimise_cb ), input ); -} - /** * vips_stream_input_sniff: * @bytes: number of bytes to sniff diff --git a/test/test_descriptors.sh b/test/test_descriptors.sh index 962fa6f3..0d644949 100755 --- a/test/test_descriptors.sh +++ b/test/test_descriptors.sh @@ -7,10 +7,9 @@ set -e . ./variables.sh -# reenable when we have minise support ini stream -# if test_supported jpegload; then -# ./test_descriptors $image -# fi +if test_supported jpegload; then + ./test_descriptors $image +fi if test_supported heifload; then ./test_descriptors $test_images/Example1.heic