Merge branch 'loader-minimise-experiment'

This commit is contained in:
John Cupitt 2019-07-28 17:23:01 +01:00
commit 6a75776272
17 changed files with 247 additions and 132 deletions

View File

@ -6,6 +6,7 @@
- disable webp alpha output if all frames fill the canvas and are solid - disable webp alpha output if all frames fill the canvas and are solid
- add "compression" option to heifsave [lovell] - add "compression" option to heifsave [lovell]
- support webp and zstd compression in tiff - support webp and zstd compression in tiff
- loaders use "minimise" to close input files earlier
- integrate support for oss-fuzz [omira-sch] - integrate support for oss-fuzz [omira-sch]
9/7/19 started 8.8.2 9/7/19 started 8.8.2

View File

@ -34,7 +34,8 @@ equal_vector( std::vector<double> a, std::vector<double> b )
printf( "%g", a[i] ); printf( "%g", a[i] );
} }
printf( "], is [" ); 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( ", " );
printf( "%g", a[i] ); printf( "%g", a[i] );
} }

View File

@ -103,6 +103,10 @@ typedef struct _VipsInsert {
VipsRect rout; /* Output space */ VipsRect rout; /* Output space */
VipsRect rmain; /* Position of main in output */ VipsRect rmain; /* Position of main in output */
VipsRect rsub; /* Position of sub in output */ VipsRect rsub; /* Position of sub in output */
/* TRUE if we've minimise sub.
*/
gboolean sub_minimised;
} VipsInsert; } VipsInsert;
typedef VipsConversionClass VipsInsertClass; typedef VipsConversionClass VipsInsertClass;
@ -164,8 +168,6 @@ vips__insert_paste_region( VipsRegion *or, VipsRegion *ir, VipsRect *pos )
return( 0 ); return( 0 );
} }
/* Insert generate function.
*/
static int static int
vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) 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; VipsRect ovl;
/* Does the rect we have been asked for fall entirely inside the /* The part of the subimage we will use.
* 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?
*/ */
vips_rect_intersectrect( &or->valid, &insert->rsub, &ovl ); 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 /* Three cases: we are generating entirely within the sub-image,
* inside both the main and the sub, then there is going to be some * entirely within the main image, or a mixture.
* background.
*/ */
if( !(vips_rect_includesrect( &insert->rsub, &or->valid ) && if( vips_rect_includesrect( &insert->rsub, &or->valid ) ) {
vips_rect_includesrect( &insert->rmain, &or->valid )) ) if( vips__insert_just_one( or, ir[1],
vips_region_paint_pel( or, r, insert->ink ); 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. /* Paste from main.
*/ */
if( vips__insert_paste_region( or, ir[0], &insert->rmain ) ) if( vips__insert_paste_region( or, ir[0], &insert->rmain ) )
return( -1 ); return( -1 );
/* Paste from sub. /* Paste from sub.
*/ */
if( vips__insert_paste_region( or, ir[1], &insert->rsub ) ) if( vips__insert_paste_region( or, ir[1], &insert->rsub ) )
return( -1 ); return( -1 );
}
return( 0 ); return( 0 );
} }

View File

@ -838,7 +838,7 @@ vips_foreign_load_start( VipsImage *out, void *a, void *b )
/* Load the image and check the result. /* 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, * image into @real. They must match exactly in size, bands,
* format and coding for the copy to work. * format and coding for the copy to work.
* *

View File

@ -24,6 +24,9 @@
* - rework as a sequential loader ... simpler, much lower mem use * - rework as a sequential loader ... simpler, much lower mem use
* 6/7/19 [deftomat] * 6/7/19 [deftomat]
* - support array of delays * - 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 ); return( -1 );
} }
/* Read in the image record.
*/
if( vips_foreign_load_gif_scan_image_record( gif ) ) if( vips_foreign_load_gif_scan_image_record( gif ) )
return( -1 ); return( -1 );
@ -647,7 +648,7 @@ vips_foreign_load_gif_header( VipsForeignLoad *load )
break; break;
case EXTENSION_RECORD_TYPE: 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. * cmaps and transparency.
*/ */
if( vips_foreign_load_gif_scan_extension( gif ) ) if( vips_foreign_load_gif_scan_extension( gif ) )
@ -1009,6 +1010,12 @@ vips_foreign_load_gif_generate( VipsRegion *or,
return( 0 ); return( 0 );
} }
static void
vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif )
{
vips_foreign_load_gif_close( gif );
}
static int static int
vips_foreign_load_gif_load( VipsForeignLoad *load ) 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] ) ) if( vips_foreign_load_gif_set_header( gif, t[0] ) )
return( -1 ); 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. /* Strips 8 pixels high to avoid too many tiny regions.
*/ */
if( vips_image_generate( t[0], 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 ) ); 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 static int
vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif ) 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; foreign_class->suffs = vips_foreign_gif_suffs;
load_class->is_a = vips_foreign_load_gif_is_a; 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; gif_class->open = vips_foreign_load_gif_file_open;

View File

@ -4,6 +4,9 @@
* - from niftiload.c * - from niftiload.c
* 24/7/19 [zhoux2016] * 24/7/19 [zhoux2016]
* - always fetch metadata from the main image (thumbs don't have it) * - 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, G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadHeif, vips_foreign_load_heif,
VIPS_TYPE_FOREIGN_LOAD ); 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 static void
vips_foreign_load_heif_dispose( GObject *gobject ) vips_foreign_load_heif_dispose( GObject *gobject )
{ {
VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) gobject; VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) gobject;
VIPS_FREEF( heif_image_release, heif->img ); vips_foreign_load_heif_close( heif );
VIPS_FREEF( heif_image_handle_release, heif->handle );
VIPS_FREEF( heif_context_free, heif->ctx );
VIPS_FREE( heif->id ); VIPS_FREE( heif->id );
G_OBJECT_CLASS( vips_foreign_load_heif_parent_class )-> G_OBJECT_CLASS( vips_foreign_load_heif_parent_class )->
@ -707,6 +717,12 @@ vips_foreign_load_heif_generate( VipsRegion *or,
return( 0 ); return( 0 );
} }
static void
vips_foreign_load_heif_minimise( VipsObject *object, VipsForeignLoadHeif *heif )
{
vips_foreign_load_heif_close( heif );
}
static int static int
vips_foreign_load_heif_load( VipsForeignLoad *load ) vips_foreign_load_heif_load( VipsForeignLoad *load )
{ {
@ -722,6 +738,12 @@ vips_foreign_load_heif_load( VipsForeignLoad *load )
t[0] = vips_image_new(); t[0] = vips_image_new();
if( vips_foreign_load_heif_set_header( heif, t[0] ) ) if( vips_foreign_load_heif_set_header( heif, t[0] ) )
return( -1 ); 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], if( vips_image_generate( t[0],
NULL, vips_foreign_load_heif_generate, NULL, heif, NULL ) || NULL, vips_foreign_load_heif_generate, NULL, heif, NULL ) ||
vips_sequential( t[0], &t[1], 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 ); error = heif_context_read_from_file( heif->ctx, file->filename, NULL );
if( error.code ) { 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 ); vips__heif_error( &error );
return( -1 ); return( -1 );
} }
if( VIPS_FOREIGN_LOAD_CLASS( 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 ); return( -1 );
}
VIPS_SETSTR( load->out->filename, file->filename ); VIPS_SETSTR( load->out->filename, file->filename );

View File

@ -100,6 +100,8 @@
* - strict round down on shrink-on-load * - strict round down on shrink-on-load
* 16/8/18 * 16/8/18
* - shut down the input file as soon as we can [kleisauke] * - 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; int output_height;
} ReadJpeg; } ReadJpeg;
/* This can be called many times. It's called directly at the end of image /* This can be called many times.
* read.
*/ */
static void static void
readjpeg_close_input( ReadJpeg *jpeg ) readjpeg_close_input( ReadJpeg *jpeg )
@ -237,6 +238,12 @@ readjpeg_close_cb( VipsObject *object, ReadJpeg *jpeg )
(void) readjpeg_free( jpeg ); (void) readjpeg_free( jpeg );
} }
static void
readjpeg_minimise_cb( VipsObject *object, ReadJpeg *jpeg )
{
readjpeg_close_input( jpeg );
}
static ReadJpeg * static ReadJpeg *
readjpeg_new( VipsImage *out, int shrink, gboolean fail, gboolean autorotate ) 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_signal_connect( out, "close",
G_CALLBACK( readjpeg_close_cb ), jpeg ); G_CALLBACK( readjpeg_close_cb ), jpeg );
g_signal_connect( out, "minimise",
G_CALLBACK( readjpeg_minimise_cb ), jpeg );
return( jpeg ); return( jpeg );
} }
@ -290,20 +299,17 @@ readjpeg_file( ReadJpeg *jpeg, const char *filename )
static const char * static const char *
find_chroma_subsample( struct jpeg_decompress_struct *cinfo ) find_chroma_subsample( struct jpeg_decompress_struct *cinfo )
{ {
gboolean has_subsample;
/* libjpeg only uses 4:4:4 and 4:2:0, confusingly. /* libjpeg only uses 4:4:4 and 4:2:0, confusingly.
* *
* http://poynton.ca/PDFs/Chroma_subsampling_notation.pdf * 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; cinfo->max_v_samp_factor > 1;
if( cinfo->num_components > 3 ) gboolean is_cmyk = cinfo->num_components > 3;
/* A cmyk image.
*/ return( is_cmyk ?
return( has_subsample ? "4:2:0:4" : "4:4:4:4" ); (has_subsample ? "4:2:0:4" : "4:4:4:4" ) :
else (has_subsample ? "4:2:0" : "4:4:4") );
return( has_subsample ? "4:2:0" : "4:4:4" );
} }
static int static int
@ -718,7 +724,7 @@ read_jpeg_generate( VipsRegion *or,
jpeg->y_pos += 1; 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 ) if( jpeg->y_pos >= or->im->Ysize )
readjpeg_close_input( jpeg ); readjpeg_close_input( jpeg );

View File

@ -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 ); return( 0 );
} }

View File

@ -380,6 +380,15 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load )
return( 0 ); 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 static int
vips_foreign_load_pdf_generate( VipsRegion *or, vips_foreign_load_pdf_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop ) void *seq, void *a, void *b, gboolean *stop )
@ -436,13 +445,6 @@ vips_foreign_load_pdf_generate( VipsRegion *or,
i += 1; 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. /* PDFium writes BRGA, we must swap.
* *
* FIXME ... this is a bit slow. * FIXME ... this is a bit slow.
@ -476,6 +478,11 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load )
*/ */
t[0] = vips_image_new(); 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] ); vips_foreign_load_pdf_set_image( pdf, t[0] );
if( vips_image_generate( t[0], if( vips_image_generate( t[0],
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) ) NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) )

View File

@ -334,12 +334,20 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load )
return( 0 ); 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 static int
vips_foreign_load_pdf_generate( VipsRegion *or, vips_foreign_load_pdf_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop ) void *seq, void *a, void *b, gboolean *stop )
{ {
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) a; VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) a;
VipsForeignLoad *load = (VipsForeignLoad *) pdf;
VipsRect *r = &or->valid; VipsRect *r = &or->valid;
int top; int top;
@ -398,13 +406,6 @@ vips_foreign_load_pdf_generate( VipsRegion *or,
i += 1; 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. /* Cairo makes pre-multipled BRGA, we must byteswap and unpremultiply.
*/ */
for( y = 0; y < r->height; y++ ) for( y = 0; y < r->height; y++ )
@ -430,6 +431,11 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load )
*/ */
t[0] = vips_image_new(); 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] ); vips_foreign_load_pdf_set_image( pdf, t[0] );
if( vips_image_generate( t[0], if( vips_image_generate( t[0],
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) ) NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) )

View File

@ -984,12 +984,17 @@ vips__rad_israd( const char *filename )
return( result == 1 ); return( result == 1 );
} }
static void
read_minimise( VipsObject *object, Read *read )
{
VIPS_FREEF( buffer_free, read->buffer );
VIPS_FREEF( fclose, read->fin );
}
static void static void
read_destroy( VipsObject *object, Read *read ) read_destroy( VipsObject *object, Read *read )
{ {
VIPS_FREE( read->filename ); VIPS_FREE( read->filename );
VIPS_FREEF( fclose, read->fin );
VIPS_FREEF( buffer_free, read->buffer );
} }
static Read * static Read *
@ -1019,6 +1024,8 @@ read_new( const char *filename, VipsImage *out )
read->prims[3][1] = CIE_y_w; read->prims[3][1] = CIE_y_w;
read->buffer = NULL; read->buffer = NULL;
g_signal_connect( out, "minimise",
G_CALLBACK( read_minimise ), read );
g_signal_connect( out, "close", g_signal_connect( out, "close",
G_CALLBACK( read_destroy ), read ); G_CALLBACK( read_destroy ), read );

View File

@ -189,6 +189,8 @@
* 7/6/19 * 7/6/19
* - istiff reads the first directory rather than just testing the magic * - istiff reads the first directory rather than just testing the magic
* number, so it ignores more TIFF-like, but not TIFF images * 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 ); 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 * static Rtiff *
rtiff_new( VipsImage *out, int page, int n, gboolean autorotate ) 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_signal_connect( out, "close",
G_CALLBACK( rtiff_close_cb ), rtiff ); G_CALLBACK( rtiff_close_cb ), rtiff );
g_signal_connect( out, "minimise",
G_CALLBACK( rtiff_minimise_cb ), rtiff );
if( rtiff->page < 0 || rtiff->page > 1000000 ) { if( rtiff->page < 0 || rtiff->page > 1000000 ) {
vips_error( "tiff2vips", _( "bad page number %d" ), vips_error( "tiff2vips", _( "bad page number %d" ),
rtiff->page ); rtiff->page );
@ -1603,11 +1622,6 @@ rtiff_fill_region( VipsRegion *out,
VIPS_GATE_STOP( "rtiff_fill_region: work" ); 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 ); return( 0 );
} }
@ -1949,11 +1963,11 @@ rtiff_stripwise_generate( VipsRegion *or,
rtiff->y_pos += hit.height; 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 ) { if( rtiff->y_pos >= or->im->Ysize ) {
#ifdef DEBUG #ifdef DEBUG
printf( "rtiff_stripwise_generate: early shutdown\n" ); printf( "rtiff_stripwise_generate: early shutdown\n" );
#endif /*DEBUG*/ #endif /*DEBUG*/
rtiff_free( rtiff ); rtiff_free( rtiff );
} }

View File

@ -196,6 +196,22 @@ read_close_cb( VipsImage *out, Read *read )
read_destroy( 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 * static Read *
read_new( VipsImage *out, gboolean fail ) read_new( VipsImage *out, gboolean fail )
{ {
@ -218,6 +234,8 @@ read_new( VipsImage *out, gboolean fail )
g_signal_connect( out, "close", g_signal_connect( out, "close",
G_CALLBACK( read_close_cb ), read ); G_CALLBACK( read_close_cb ), read );
g_signal_connect( out, "minimise",
G_CALLBACK( read_minimise_cb ), read );
if( !(read->pPng = png_create_read_struct( if( !(read->pPng = png_create_read_struct(
PNG_LIBPNG_VER_STRING, NULL, 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 /* Catch errors from png_read_end(). This can fail on a truncated
* file. * file.
*/ */
if( setjmp( png_jmpbuf( read->pPng ) ) ) { if( setjmp( png_jmpbuf( read->pPng ) ) ) {
if( read->fail ) { if( read->fail ) {
vips_error( "vipspng", "%s", _( "libpng read error" ) ); vips_error( "vipspng", "%s", _( "libpng read error" ) );
return( -1 ); return( -1 );
} }
return( 0 ); return( 0 );
} }
/* We need to shut down the reader immediately at the end of read or /* Early close to free the fd as soon as we can.
* we won't detach ready for the next image.
*/ */
if( read->y_pos >= read->out->Ysize ) { if( read->y_pos >= read->out->Ysize ) {
png_read_end( read->pPng, NULL ); png_read_end( read->pPng, NULL );
read_destroy( read ); read_destroy( read );
} }

View File

@ -342,8 +342,8 @@ vips__iswebp( const char *filename )
return( 0 ); return( 0 );
} }
static int static void
read_free( Read *read ) read_minimise( Read *read )
{ {
WebPDemuxReleaseIterator( &read->iter ); WebPDemuxReleaseIterator( &read->iter );
VIPS_UNREF( read->frame ); VIPS_UNREF( read->frame );
@ -359,6 +359,13 @@ read_free( Read *read )
} }
VIPS_FREEF( vips_tracked_close, read->fd ); VIPS_FREEF( vips_tracked_close, read->fd );
}
static int
read_free( Read *read )
{
read_minimise( read );
VIPS_FREE( read->filename ); VIPS_FREE( read->filename );
VIPS_FREE( read->delays ); VIPS_FREE( read->delays );
VIPS_FREE( read ); VIPS_FREE( read );
@ -793,6 +800,12 @@ read_webp_generate( VipsRegion *or,
return( 0 ); return( 0 );
} }
static void
read_minimise_cb( VipsObject *object, Read *read )
{
read_minimise( read );
}
static int static int
read_image( Read *read, VipsImage *out ) read_image( Read *read, VipsImage *out )
{ {
@ -803,6 +816,9 @@ read_image( Read *read, VipsImage *out )
if( read_header( read, t[0] ) ) if( read_header( read, t[0] ) )
return( -1 ); return( -1 );
g_signal_connect( t[0], "minimise",
G_CALLBACK( read_minimise_cb ), read );
if( vips_image_generate( t[0], if( vips_image_generate( t[0],
NULL, read_webp_generate, NULL, read, NULL ) || NULL, read_webp_generate, NULL, read, NULL ) ||
vips_sequential( t[0], &t[1], NULL ) || vips_sequential( t[0], &t[1], NULL ) ||

View File

@ -426,6 +426,8 @@ void vips_image_invalidate_all( VipsImage *image );
void vips_image_minimise_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 ); void vips_image_set_progress( VipsImage *image, gboolean progress );
gboolean vips_image_iskilled( VipsImage *image ); gboolean vips_image_iskilled( VipsImage *image );
void vips_image_set_kill( VipsImage *image, gboolean kill ); void vips_image_set_kill( VipsImage *image, gboolean kill );

View File

@ -451,11 +451,9 @@ vips_image_finalize( GObject *gobject )
VIPS_FREE( image->time ); VIPS_FREE( image->time );
} }
/* Any image data? /* Free attached memory.
*/ */
if( image->data ) { if( image->data ) {
/* Buffer image. Only free stuff we know we allocated.
*/
if( image->dtype == VIPS_IMAGE_SETBUF ) { if( image->dtype == VIPS_IMAGE_SETBUF ) {
VIPS_DEBUG_MSG( "vips_image_finalize: " VIPS_DEBUG_MSG( "vips_image_finalize: "
"freeing buffer\n" ); "freeing buffer\n" );
@ -466,7 +464,7 @@ vips_image_finalize( GObject *gobject )
image->data = NULL; image->data = NULL;
} }
/* If this is a temp, delete it. /* Delete associated files.
*/ */
vips_image_delete( image ); vips_image_delete( image );
@ -1454,6 +1452,22 @@ vips_image_minimise_all( VipsImage *image )
(VipsSListMap2Fn) vips_image_minimise_all_cb, NULL, NULL ); (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. /* Attach a new time struct, if necessary, and reset it.
*/ */
static int static int
@ -2066,9 +2080,8 @@ vips_image_new_from_memory_copy_cb( VipsImage *image, void *data_copy )
* @format: image format * @format: image format
* *
* Like vips_image_new_from_memory(), but VIPS will make a copy of the memory * Like vips_image_new_from_memory(), but VIPS will make a copy of the memory
* area. This * area. This means more memory use and an extra copy operation, but is much
* means more memory use and an extra copy operation, but is much simpler and * simpler and safer.
* safer.
* *
* See also: vips_image_new_from_memory(). * See also: vips_image_new_from_memory().
* *

View File

@ -45,8 +45,6 @@
* - rename yshrink -> vshrink for greater consistency * - rename yshrink -> vshrink for greater consistency
* 7/3/17 * 7/3/17
* - add a seq line cache * - 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; int vshrink;
size_t sizeof_line_buffer; size_t sizeof_line_buffer;
gboolean sequential;
} VipsShrinkv; } VipsShrinkv;
@ -268,7 +265,6 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq,
{ {
VipsShrinkvSequence *seq = (VipsShrinkvSequence *) vseq; VipsShrinkvSequence *seq = (VipsShrinkvSequence *) vseq;
VipsShrinkv *shrink = (VipsShrinkv *) b; VipsShrinkv *shrink = (VipsShrinkv *) b;
VipsResample *resample = VIPS_RESAMPLE( shrink );
VipsRegion *ir = seq->ir; VipsRegion *ir = seq->ir;
VipsRect *r = &or->valid; VipsRect *r = &or->valid;
@ -319,38 +315,6 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq,
VIPS_COUNT_PIXELS( or, "vips_shrinkv_gen" ); 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 ); 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 * always have the previous XX lines of the shrunk image, and we won't
* fetch out of order. * 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" ); g_info( "shrinkv sequential line cache" );
shrink->sequential = TRUE;
if( vips_sequential( in, &t[3], if( vips_sequential( in, &t[3],
"tile_height", 10, "tile_height", 10,
NULL ) ) NULL ) )