diff --git a/ChangeLog b/ChangeLog index a9629c56..fc5c9b75 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,7 @@ - disable webp alpha output if all frames fill the canvas and are solid - add "compression" option to heifsave [lovell] - support webp and zstd compression in tiff +- loaders use "minimise" to close input files earlier - integrate support for oss-fuzz [omira-sch] 9/7/19 started 8.8.2 diff --git a/cplusplus/examples/test_overloads.cpp b/cplusplus/examples/test_overloads.cpp index 8a183ca6..ed3a25cc 100644 --- a/cplusplus/examples/test_overloads.cpp +++ b/cplusplus/examples/test_overloads.cpp @@ -34,7 +34,8 @@ equal_vector( std::vector a, std::vector b ) printf( "%g", a[i] ); } printf( "], is [" ); - for( unsigned int i = 0; i < a.size(); i++ ) { if( i > 0 ) + for( unsigned int i = 0; i < a.size(); i++ ) { + if( i > 0 ) printf( ", " ); printf( "%g", a[i] ); } diff --git a/libvips/conversion/insert.c b/libvips/conversion/insert.c index ce45ccdf..27870fee 100644 --- a/libvips/conversion/insert.c +++ b/libvips/conversion/insert.c @@ -103,6 +103,10 @@ typedef struct _VipsInsert { VipsRect rout; /* Output space */ VipsRect rmain; /* Position of main in output */ VipsRect rsub; /* Position of sub in output */ + + /* TRUE if we've minimise sub. + */ + gboolean sub_minimised; } VipsInsert; typedef VipsConversionClass VipsInsertClass; @@ -164,8 +168,6 @@ vips__insert_paste_region( VipsRegion *or, VipsRegion *ir, VipsRect *pos ) return( 0 ); } -/* Insert generate function. - */ static int vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { @@ -175,39 +177,43 @@ vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) VipsRect ovl; - /* Does the rect we have been asked for fall entirely inside the - * sub-image? - */ - if( vips_rect_includesrect( &insert->rsub, &or->valid ) ) - return( vips__insert_just_one( or, ir[1], - insert->rsub.left, insert->rsub.top ) ); - - /* Does it fall entirely inside the main, and not at all inside the - * sub? + /* The part of the subimage we will use. */ vips_rect_intersectrect( &or->valid, &insert->rsub, &ovl ); - if( vips_rect_includesrect( &insert->rmain, &or->valid ) && - vips_rect_isempty( &ovl ) ) - return( vips__insert_just_one( or, ir[0], - insert->rmain.left, insert->rmain.top ) ); - /* Output requires both (or neither) input. If it is not entirely - * inside both the main and the sub, then there is going to be some - * background. + /* Three cases: we are generating entirely within the sub-image, + * entirely within the main image, or a mixture. */ - if( !(vips_rect_includesrect( &insert->rsub, &or->valid ) && - vips_rect_includesrect( &insert->rmain, &or->valid )) ) - vips_region_paint_pel( or, r, insert->ink ); + if( vips_rect_includesrect( &insert->rsub, &or->valid ) ) { + if( vips__insert_just_one( or, ir[1], + insert->rsub.left, insert->rsub.top ) ) + return( -1 ); + } + else if( vips_rect_includesrect( &insert->rmain, &or->valid ) && + vips_rect_isempty( &ovl ) ) { + if( vips__insert_just_one( or, ir[0], + insert->rmain.left, insert->rmain.top ) ) + return( -1 ); + } + else { + /* Output requires both (or neither) input. If it is not + * entirely inside both the main and the sub, then there is + * going to be some background. + */ + if( !(vips_rect_includesrect( &insert->rsub, &or->valid ) && + vips_rect_includesrect( &insert->rmain, &or->valid )) ) + vips_region_paint_pel( or, r, insert->ink ); - /* Paste from main. - */ - if( vips__insert_paste_region( or, ir[0], &insert->rmain ) ) - return( -1 ); + /* Paste from main. + */ + if( vips__insert_paste_region( or, ir[0], &insert->rmain ) ) + return( -1 ); - /* Paste from sub. - */ - if( vips__insert_paste_region( or, ir[1], &insert->rsub ) ) - return( -1 ); + /* Paste from sub. + */ + if( vips__insert_paste_region( or, ir[1], &insert->rsub ) ) + return( -1 ); + } return( 0 ); } diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 6dda604d..a48591ad 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -838,7 +838,7 @@ vips_foreign_load_start( VipsImage *out, void *a, void *b ) /* Load the image and check the result. * - * ->header() read the header into @out, load has read the + * ->header() read the header into @out, load will read the * image into @real. They must match exactly in size, bands, * format and coding for the copy to work. * diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 4f8ceec8..a9b7cdc7 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -24,6 +24,9 @@ * - rework as a sequential loader ... simpler, much lower mem use * 6/7/19 [deftomat] * - support array of delays + * 24/7/19 + * - close early on minimise + * - close early on error */ /* @@ -637,8 +640,6 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) return( -1 ); } - /* Read in the image record. - */ if( vips_foreign_load_gif_scan_image_record( gif ) ) return( -1 ); @@ -647,7 +648,7 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) break; case EXTENSION_RECORD_TYPE: - /* We will need to fetch the extensions to check for + /* We need to fetch the extensions to check for * cmaps and transparency. */ if( vips_foreign_load_gif_scan_extension( gif ) ) @@ -1009,6 +1010,12 @@ vips_foreign_load_gif_generate( VipsRegion *or, return( 0 ); } +static void +vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif ) +{ + vips_foreign_load_gif_close( gif ); +} + static int vips_foreign_load_gif_load( VipsForeignLoad *load ) { @@ -1052,6 +1059,11 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) if( vips_foreign_load_gif_set_header( gif, t[0] ) ) return( -1 ); + /* Close input immediately at end of read. + */ + g_signal_connect( t[0], "minimise", + G_CALLBACK( vips_foreign_load_gif_minimise ), gif ); + /* Strips 8 pixels high to avoid too many tiny regions. */ if( vips_image_generate( t[0], @@ -1189,6 +1201,26 @@ vips_giflib_file_read( GifFileType *file, GifByteType *buffer, int n ) return( (int) fread( (void *) buffer, 1, n, fp ) ); } +static int +vips_foreign_load_gif_file_header( VipsForeignLoad *load ) +{ + VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load; + + if( VIPS_FOREIGN_LOAD_CLASS( + vips_foreign_load_gif_file_parent_class )->header( load ) ) { + /* Close early if header read fails in our base class. + */ + VIPS_FREEF( fclose, file->fp ); + return( -1 ); + } + + return( 0 ); +} + +/* We have to have _open() as a vfunc since our base class needs to be able to + * make two scans of the gif (scan for header, then scan for pixels), so we + * must be able to close and reopen (or rewind). + */ static int vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif ) { @@ -1238,6 +1270,7 @@ vips_foreign_load_gif_file_class_init( foreign_class->suffs = vips_foreign_gif_suffs; load_class->is_a = vips_foreign_load_gif_is_a; + load_class->header = vips_foreign_load_gif_file_header; gif_class->open = vips_foreign_load_gif_file_open; diff --git a/libvips/foreign/heifload.c b/libvips/foreign/heifload.c index 9b77f624..f468b1ed 100644 --- a/libvips/foreign/heifload.c +++ b/libvips/foreign/heifload.c @@ -4,6 +4,9 @@ * - from niftiload.c * 24/7/19 [zhoux2016] * - always fetch metadata from the main image (thumbs don't have it) + * 24/7/19 + * - close early on minimise + * - close early on error */ /* @@ -130,14 +133,21 @@ typedef VipsForeignLoadClass VipsForeignLoadHeifClass; G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadHeif, vips_foreign_load_heif, VIPS_TYPE_FOREIGN_LOAD ); +static void +vips_foreign_load_heif_close( VipsForeignLoadHeif *heif ) +{ + VIPS_FREEF( heif_image_release, heif->img ); + heif->data = NULL; + VIPS_FREEF( heif_image_handle_release, heif->handle ); + VIPS_FREEF( heif_context_free, heif->ctx ); +} + static void vips_foreign_load_heif_dispose( GObject *gobject ) { VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) gobject; - VIPS_FREEF( heif_image_release, heif->img ); - VIPS_FREEF( heif_image_handle_release, heif->handle ); - VIPS_FREEF( heif_context_free, heif->ctx ); + vips_foreign_load_heif_close( heif ); VIPS_FREE( heif->id ); G_OBJECT_CLASS( vips_foreign_load_heif_parent_class )-> @@ -707,6 +717,12 @@ vips_foreign_load_heif_generate( VipsRegion *or, return( 0 ); } +static void +vips_foreign_load_heif_minimise( VipsObject *object, VipsForeignLoadHeif *heif ) +{ + vips_foreign_load_heif_close( heif ); +} + static int vips_foreign_load_heif_load( VipsForeignLoad *load ) { @@ -722,6 +738,12 @@ vips_foreign_load_heif_load( VipsForeignLoad *load ) t[0] = vips_image_new(); if( vips_foreign_load_heif_set_header( heif, t[0] ) ) return( -1 ); + + /* CLose input immediately at end of read. + */ + g_signal_connect( t[0], "minimise", + G_CALLBACK( vips_foreign_load_heif_minimise ), heif ); + if( vips_image_generate( t[0], NULL, vips_foreign_load_heif_generate, NULL, heif, NULL ) || vips_sequential( t[0], &t[1], NULL ) || @@ -821,13 +843,20 @@ vips_foreign_load_heif_file_header( VipsForeignLoad *load ) error = heif_context_read_from_file( heif->ctx, file->filename, NULL ); if( error.code ) { + /* Make we close the fd as soon as we can on error. + */ + vips_foreign_load_heif_close( heif ); vips__heif_error( &error ); return( -1 ); } if( VIPS_FOREIGN_LOAD_CLASS( - vips_foreign_load_heif_file_parent_class )->header( load ) ) + vips_foreign_load_heif_file_parent_class )->header( load ) ) { + /* Close early if our base class fails to read. + */ + vips_foreign_load_heif_close( heif ); return( -1 ); + } VIPS_SETSTR( load->out->filename, file->filename ); diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 8240b164..aacb6918 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -100,6 +100,8 @@ * - strict round down on shrink-on-load * 16/8/18 * - shut down the input file as soon as we can [kleisauke] + * 20/7/19 + * - close input on minimise rather than Y read position */ /* @@ -190,8 +192,7 @@ typedef struct _ReadJpeg { int output_height; } ReadJpeg; -/* This can be called many times. It's called directly at the end of image - * read. +/* This can be called many times. */ static void readjpeg_close_input( ReadJpeg *jpeg ) @@ -237,6 +238,12 @@ readjpeg_close_cb( VipsObject *object, ReadJpeg *jpeg ) (void) readjpeg_free( jpeg ); } +static void +readjpeg_minimise_cb( VipsObject *object, ReadJpeg *jpeg ) +{ + readjpeg_close_input( jpeg ); +} + static ReadJpeg * readjpeg_new( VipsImage *out, int shrink, gboolean fail, gboolean autorotate ) { @@ -270,6 +277,8 @@ readjpeg_new( VipsImage *out, int shrink, gboolean fail, gboolean autorotate ) g_signal_connect( out, "close", G_CALLBACK( readjpeg_close_cb ), jpeg ); + g_signal_connect( out, "minimise", + G_CALLBACK( readjpeg_minimise_cb ), jpeg ); return( jpeg ); } @@ -290,20 +299,17 @@ readjpeg_file( ReadJpeg *jpeg, const char *filename ) static const char * find_chroma_subsample( struct jpeg_decompress_struct *cinfo ) { - gboolean has_subsample; - /* libjpeg only uses 4:4:4 and 4:2:0, confusingly. * * http://poynton.ca/PDFs/Chroma_subsampling_notation.pdf */ - has_subsample = cinfo->max_h_samp_factor > 1 || + gboolean has_subsample = cinfo->max_h_samp_factor > 1 || cinfo->max_v_samp_factor > 1; - if( cinfo->num_components > 3 ) - /* A cmyk image. - */ - return( has_subsample ? "4:2:0:4" : "4:4:4:4" ); - else - return( has_subsample ? "4:2:0" : "4:4:4" ); + gboolean is_cmyk = cinfo->num_components > 3; + + return( is_cmyk ? + (has_subsample ? "4:2:0:4" : "4:4:4:4" ) : + (has_subsample ? "4:2:0" : "4:4:4") ); } static int @@ -718,7 +724,7 @@ read_jpeg_generate( VipsRegion *or, jpeg->y_pos += 1; } - /* Shut down the input file as soon as we can. + /* Shut down the input early if we can. */ if( jpeg->y_pos >= or->im->Ysize ) readjpeg_close_input( jpeg ); diff --git a/libvips/foreign/openexr2vips.c b/libvips/foreign/openexr2vips.c index edeb081e..e36b477e 100644 --- a/libvips/foreign/openexr2vips.c +++ b/libvips/foreign/openexr2vips.c @@ -348,11 +348,6 @@ vips__openexr_generate( VipsRegion *out, } } - /* We can't shut down the input file early for tile read, even if we - * know load is in sequential mode, since we are not inside a - * vips_sequential() and requests are not guaranteed to be in order. - */ - return( 0 ); } diff --git a/libvips/foreign/pdfiumload.c b/libvips/foreign/pdfiumload.c index 4deb2ba0..694bdb0e 100644 --- a/libvips/foreign/pdfiumload.c +++ b/libvips/foreign/pdfiumload.c @@ -380,6 +380,15 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) return( 0 ); } +static void +vips_foreign_load_pdf_minimise( VipsObject *object, VipsForeignLoadPdf *pdf ) +{ + /* In seq mode, we can shut down the input at the end of computation. + */ + if( VIPS_FOREIGN_LOAD( pdf )->access == VIPS_ACCESS_SEQUENTIAL ) + vips_foreign_load_pdf_close( pdf ); +} + static int vips_foreign_load_pdf_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) @@ -436,13 +445,6 @@ vips_foreign_load_pdf_generate( VipsRegion *or, i += 1; } - /* In seq mode, we can shut down the input when we render the last - * row. - */ - if( top >= or->im->Ysize && - load->access == VIPS_ACCESS_SEQUENTIAL ) - vips_foreign_load_pdf_close( pdf ); - /* PDFium writes BRGA, we must swap. * * FIXME ... this is a bit slow. @@ -476,6 +478,11 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load ) */ t[0] = vips_image_new(); + /* Close input immediately at end of read. + */ + g_signal_connect( t[0], "minimise", + G_CALLBACK( vips_foreign_load_pdf_minimise ), pdf ); + vips_foreign_load_pdf_set_image( pdf, t[0] ); if( vips_image_generate( t[0], NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) ) diff --git a/libvips/foreign/pdfload.c b/libvips/foreign/pdfload.c index 1a6e5b74..bf2dffde 100644 --- a/libvips/foreign/pdfload.c +++ b/libvips/foreign/pdfload.c @@ -334,12 +334,20 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) return( 0 ); } +static void +vips_foreign_load_pdf_minimise( VipsObject *object, VipsForeignLoadPdf *pdf ) +{ + /* In seq mode, we can shut down the input at the end of computation. + */ + if( VIPS_FOREIGN_LOAD( pdf )->access == VIPS_ACCESS_SEQUENTIAL ) + vips_foreign_load_pdf_close( pdf ); +} + static int vips_foreign_load_pdf_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) a; - VipsForeignLoad *load = (VipsForeignLoad *) pdf; VipsRect *r = &or->valid; int top; @@ -398,13 +406,6 @@ vips_foreign_load_pdf_generate( VipsRegion *or, i += 1; } - /* In seq mode, we can shut down the input when we render the last - * row. - */ - if( top >= or->im->Ysize && - load->access == VIPS_ACCESS_SEQUENTIAL ) - vips_foreign_load_pdf_close( pdf ); - /* Cairo makes pre-multipled BRGA, we must byteswap and unpremultiply. */ for( y = 0; y < r->height; y++ ) @@ -430,6 +431,11 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load ) */ t[0] = vips_image_new(); + /* Close input immediately at end of read. + */ + g_signal_connect( t[0], "minimise", + G_CALLBACK( vips_foreign_load_pdf_minimise ), pdf ); + vips_foreign_load_pdf_set_image( pdf, t[0] ); if( vips_image_generate( t[0], NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) ) diff --git a/libvips/foreign/radiance.c b/libvips/foreign/radiance.c index be56b322..e8f0dc2b 100644 --- a/libvips/foreign/radiance.c +++ b/libvips/foreign/radiance.c @@ -984,12 +984,17 @@ vips__rad_israd( const char *filename ) return( result == 1 ); } +static void +read_minimise( VipsObject *object, Read *read ) +{ + VIPS_FREEF( buffer_free, read->buffer ); + VIPS_FREEF( fclose, read->fin ); +} + static void read_destroy( VipsObject *object, Read *read ) { VIPS_FREE( read->filename ); - VIPS_FREEF( fclose, read->fin ); - VIPS_FREEF( buffer_free, read->buffer ); } static Read * @@ -1019,6 +1024,8 @@ read_new( const char *filename, VipsImage *out ) read->prims[3][1] = CIE_y_w; read->buffer = NULL; + g_signal_connect( out, "minimise", + G_CALLBACK( read_minimise ), read ); g_signal_connect( out, "close", G_CALLBACK( read_destroy ), read ); diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index b7b20689..33106784 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -189,6 +189,8 @@ * 7/6/19 * - istiff reads the first directory rather than just testing the magic * number, so it ignores more TIFF-like, but not TIFF images + * 20/7/19 + * - use "minimise" for early shutdown, rather than read Y position */ /* @@ -494,6 +496,20 @@ rtiff_close_cb( VipsObject *object, Rtiff *rtiff ) rtiff_free( rtiff ); } +static void +rtiff_minimise_cb( VipsObject *object, Rtiff *rtiff ) +{ +#ifdef DEBUG + printf( "rtiff_minimise_cb: %p minimise\n", rtiff ); +#endif /*DEBUG*/ + + /* Close early for non-tiled TIFFs. Tiled TIFFs are read randomly, so + * the end of a loop doesn't mean the tiff won't be used again. + */ + if( !rtiff->header.tiled ) + rtiff_free( rtiff ); +} + static Rtiff * rtiff_new( VipsImage *out, int page, int n, gboolean autorotate ) { @@ -520,6 +536,9 @@ rtiff_new( VipsImage *out, int page, int n, gboolean autorotate ) g_signal_connect( out, "close", G_CALLBACK( rtiff_close_cb ), rtiff ); + g_signal_connect( out, "minimise", + G_CALLBACK( rtiff_minimise_cb ), rtiff ); + if( rtiff->page < 0 || rtiff->page > 1000000 ) { vips_error( "tiff2vips", _( "bad page number %d" ), rtiff->page ); @@ -1603,11 +1622,6 @@ rtiff_fill_region( VipsRegion *out, VIPS_GATE_STOP( "rtiff_fill_region: work" ); - /* We can't shut down the input file early for tile read, even if we - * know load is in sequential mode, since we are not inside a - * vips_sequential() and requests are not guaranteed to be in order. - */ - return( 0 ); } @@ -1949,11 +1963,11 @@ rtiff_stripwise_generate( VipsRegion *or, rtiff->y_pos += hit.height; } - /* Shut down the input file as soon as we can. + /* Shut down the input file as soon as we can. */ if( rtiff->y_pos >= or->im->Ysize ) { #ifdef DEBUG - printf( "rtiff_stripwise_generate: early shutdown\n" ); + printf( "rtiff_stripwise_generate: early shutdown\n" ); #endif /*DEBUG*/ rtiff_free( rtiff ); } diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 4daaea8d..33946cb6 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -196,6 +196,22 @@ read_close_cb( VipsImage *out, Read *read ) read_destroy( read ); } +static void +read_minimise_cb( VipsImage *out, Read *read ) +{ + /* Catch errors from png_read_end(). This can fail on a truncated + * file. + */ + if( read->pPng ) { + /* Catch and ignore error returns from png_read_end(). + */ + if( !setjmp( png_jmpbuf( read->pPng ) ) ) + png_read_end( read->pPng, NULL ); + } + + read_destroy( read ); +} + static Read * read_new( VipsImage *out, gboolean fail ) { @@ -218,6 +234,8 @@ read_new( VipsImage *out, gboolean fail ) g_signal_connect( out, "close", G_CALLBACK( read_close_cb ), read ); + g_signal_connect( out, "minimise", + G_CALLBACK( read_minimise_cb ), read ); if( !(read->pPng = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, @@ -643,22 +661,21 @@ png2vips_generate( VipsRegion *or, } /* Catch errors from png_read_end(). This can fail on a truncated - * file. + * file. */ if( setjmp( png_jmpbuf( read->pPng ) ) ) { if( read->fail ) { - vips_error( "vipspng", "%s", _( "libpng read error" ) ); + vips_error( "vipspng", "%s", _( "libpng read error" ) ); return( -1 ); } return( 0 ); } - /* We need to shut down the reader immediately at the end of read or - * we won't detach ready for the next image. + /* Early close to free the fd as soon as we can. */ if( read->y_pos >= read->out->Ysize ) { - png_read_end( read->pPng, NULL ); + png_read_end( read->pPng, NULL ); read_destroy( read ); } diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 7e28015e..f5bc67a3 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -342,8 +342,8 @@ vips__iswebp( const char *filename ) return( 0 ); } -static int -read_free( Read *read ) +static void +read_minimise( Read *read ) { WebPDemuxReleaseIterator( &read->iter ); VIPS_UNREF( read->frame ); @@ -359,6 +359,13 @@ read_free( Read *read ) } VIPS_FREEF( vips_tracked_close, read->fd ); +} + +static int +read_free( Read *read ) +{ + read_minimise( read ); + VIPS_FREE( read->filename ); VIPS_FREE( read->delays ); VIPS_FREE( read ); @@ -793,6 +800,12 @@ read_webp_generate( VipsRegion *or, return( 0 ); } +static void +read_minimise_cb( VipsObject *object, Read *read ) +{ + read_minimise( read ); +} + static int read_image( Read *read, VipsImage *out ) { @@ -803,6 +816,9 @@ read_image( Read *read, VipsImage *out ) if( read_header( read, t[0] ) ) return( -1 ); + g_signal_connect( t[0], "minimise", + G_CALLBACK( read_minimise_cb ), read ); + if( vips_image_generate( t[0], NULL, read_webp_generate, NULL, read, NULL ) || vips_sequential( t[0], &t[1], NULL ) || diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index 7353aa89..be5dc94a 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -426,6 +426,8 @@ void vips_image_invalidate_all( VipsImage *image ); void vips_image_minimise_all( VipsImage *image ); +gboolean vips_image_is_sequential( VipsImage *image ); + void vips_image_set_progress( VipsImage *image, gboolean progress ); gboolean vips_image_iskilled( VipsImage *image ); void vips_image_set_kill( VipsImage *image, gboolean kill ); diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 3037b1c4..211c1727 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -451,11 +451,9 @@ vips_image_finalize( GObject *gobject ) VIPS_FREE( image->time ); } - /* Any image data? + /* Free attached memory. */ if( image->data ) { - /* Buffer image. Only free stuff we know we allocated. - */ if( image->dtype == VIPS_IMAGE_SETBUF ) { VIPS_DEBUG_MSG( "vips_image_finalize: " "freeing buffer\n" ); @@ -466,7 +464,7 @@ vips_image_finalize( GObject *gobject ) image->data = NULL; } - /* If this is a temp, delete it. + /* Delete associated files. */ vips_image_delete( image ); @@ -1454,6 +1452,22 @@ vips_image_minimise_all( VipsImage *image ) (VipsSListMap2Fn) vips_image_minimise_all_cb, NULL, NULL ); } +/** + * vips_image_is_sequential: (method) + * @image: #VipsImage to minimise + * + * TRUE if any of the images upstream from @image were opened in sequential + * mode. Some operations change behaviour slightly in sequential mode to + * optimise memory behaviour. + * + * Returns: %TRUE if @image is in sequential mode. + */ +gboolean +vips_image_is_sequential( VipsImage *image ) +{ + return( vips_image_get_typeof( image, VIPS_META_SEQUENTIAL ) ); +} + /* Attach a new time struct, if necessary, and reset it. */ static int @@ -2066,9 +2080,8 @@ vips_image_new_from_memory_copy_cb( VipsImage *image, void *data_copy ) * @format: image format * * Like vips_image_new_from_memory(), but VIPS will make a copy of the memory - * area. This - * means more memory use and an extra copy operation, but is much simpler and - * safer. + * area. This means more memory use and an extra copy operation, but is much + * simpler and safer. * * See also: vips_image_new_from_memory(). * diff --git a/libvips/resample/shrinkv.c b/libvips/resample/shrinkv.c index 0dfac96b..2b91737a 100644 --- a/libvips/resample/shrinkv.c +++ b/libvips/resample/shrinkv.c @@ -45,8 +45,6 @@ * - rename yshrink -> vshrink for greater consistency * 7/3/17 * - add a seq line cache - * 9/7/19 - * - read the tail of the input to force early shutdown in seq readers */ /* @@ -101,7 +99,6 @@ typedef struct _VipsShrinkv { int vshrink; size_t sizeof_line_buffer; - gboolean sequential; } VipsShrinkv; @@ -268,7 +265,6 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq, { VipsShrinkvSequence *seq = (VipsShrinkvSequence *) vseq; VipsShrinkv *shrink = (VipsShrinkv *) b; - VipsResample *resample = VIPS_RESAMPLE( shrink ); VipsRegion *ir = seq->ir; VipsRect *r = &or->valid; @@ -319,38 +315,6 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq, VIPS_COUNT_PIXELS( or, "vips_shrinkv_gen" ); - /* If we are in seq mode and we've just generated the last line of - * the output, make sure we read all of the input. - * - * This will trigger the early shutdown logic in things like the - * tiff loader. - */ - if( shrink->sequential && - r->top + r->height >= or->im->Ysize ) { - /* First unused scanline. resample->in->Ysize because we want - * the height before the embed. - */ - int first = or->im->Ysize * shrink->vshrink; - int unused = resample->in->Ysize - first; - - g_assert( unused >= 0 ); - - for( y = 0; y < unused; y++ ) { - VipsRect s; - - s.left = r->left; - s.top = first + y; - s.width = r->width; - s.height = 1; -#ifdef DEBUG - printf( "shrink_gen: requesting tail %d\n", s.top ); -#endif /*DEBUG*/ - - if( vips_region_prepare( ir, &s ) ) - return( -1 ); - } - } - return( 0 ); } @@ -448,11 +412,9 @@ vips_shrinkv_build( VipsObject *object ) * always have the previous XX lines of the shrunk image, and we won't * fetch out of order. */ - if( vips_image_get_typeof( in, VIPS_META_SEQUENTIAL ) ) { + if( vips_image_is_sequential( in ) ) { g_info( "shrinkv sequential line cache" ); - shrink->sequential = TRUE; - if( vips_sequential( in, &t[3], "tile_height", 10, NULL ) )