Merge branch 'loader-minimise-experiment'
This commit is contained in:
commit
6a75776272
@ -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
|
||||||
|
@ -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] );
|
||||||
}
|
}
|
||||||
|
@ -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,25 +177,28 @@ 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__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 ) &&
|
if( !(vips_rect_includesrect( &insert->rsub, &or->valid ) &&
|
||||||
vips_rect_includesrect( &insert->rmain, &or->valid )) )
|
vips_rect_includesrect( &insert->rmain, &or->valid )) )
|
||||||
@ -208,6 +213,7 @@ vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
|||||||
*/
|
*/
|
||||||
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 );
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 );
|
||||||
|
|
||||||
|
@ -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 );
|
||||||
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 ) )
|
||||||
|
@ -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 ) )
|
||||||
|
@ -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 );
|
||||||
|
|
||||||
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
@ -654,8 +672,7 @@ png2vips_generate( VipsRegion *or,
|
|||||||
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 );
|
||||||
|
@ -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 ) ||
|
||||||
|
@ -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 );
|
||||||
|
@ -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().
|
||||||
*
|
*
|
||||||
|
@ -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 ) )
|
||||||
|
Loading…
Reference in New Issue
Block a user