Merge pull request #2 from libvips/master

Merge from main
This commit is contained in:
Elad Laufer 2020-02-10 11:59:40 +02:00 committed by GitHub
commit 2b6cc4229e
3 changed files with 172 additions and 158 deletions

View File

@ -10,6 +10,7 @@
- add VIPS_LEAK env var - add VIPS_LEAK env var
- add vips_pipe_read_limit_set(), --vips-pipe-read-limit, - add vips_pipe_read_limit_set(), --vips-pipe-read-limit,
VIPS_PIPE_READ_LIMIT VIPS_PIPE_READ_LIMIT
- revise gifload to fix BACKGROUND and PREVIOUS dispose [alon-ne]
31/1/19 started 8.9.2 31/1/19 started 8.9.2
- fix a deadlock with --vips-leak [DarthSim] - fix a deadlock with --vips-leak [DarthSim]

View File

@ -37,11 +37,8 @@
* 30/1/19 * 30/1/19
* - rework on top of VipsSource * - rework on top of VipsSource
* - add gifload_source * - add gifload_source
* 31/1/20 * 5/2/20 alon-ne
* - treat DISPOSAL_UNSPECIFIED as _DO_NOT, since that's what many GIFs * - fix DISPOSE_BACKGROUND and DISPOSE_PREVIOUS
* in the wild appear to do
* 5/2/20
* - Fix handling of DISPOSE_BACKGROUND and DISPOSE_PREVIOUS
*/ */
/* /*
@ -119,7 +116,6 @@
#define NO_TRANSPARENT_INDEX -1 #define NO_TRANSPARENT_INDEX -1
#define TRANSPARENT_MASK 0x01 #define TRANSPARENT_MASK 0x01
#define GIF_TRANSPARENT_COLOR 0x00ffffff
#define DISPOSE_MASK 0x07 #define DISPOSE_MASK 0x07
#define DISPOSE_SHIFT 2 #define DISPOSE_SHIFT 2
@ -176,12 +172,12 @@ typedef struct _VipsForeignLoadGif {
*/ */
int n_pages; int n_pages;
/* A memory image the sized of one frame ... we accumulate to this as /* A memory image the size of one frame ... we accumulate to this as
* we scan the image, and copy lines to the output on generate. * we scan the image, and copy lines to the output on generate.
*/ */
VipsImage *frame; VipsImage *frame;
/* A memory scratch buffer the sized as one frame, used together with frame for rendering /* A scratch buffer the size of frame, used for rendering.
*/ */
VipsImage *scratch; VipsImage *scratch;
@ -569,8 +565,10 @@ vips_foreign_load_gif_scan_application_ext( VipsForeignLoadGif *gif,
*/ */
have_netscape = FALSE; have_netscape = FALSE;
if( extension[0] == 11 && if( extension[0] == 11 &&
(vips_isprefix( "NETSCAPE2.0", (const char*) (extension + 1) ) || (vips_isprefix( "NETSCAPE2.0",
vips_isprefix( "ANIMEXTS1.0", (const char*) (extension + 1) ))) (const char*) (extension + 1) ) ||
vips_isprefix( "ANIMEXTS1.0",
(const char*) (extension + 1) )) )
have_netscape = TRUE; have_netscape = TRUE;
while( extension != NULL ) { while( extension != NULL ) {
@ -847,22 +845,19 @@ vips_foreign_load_gif_build_cmap( VipsForeignLoadGif *gif )
} }
static void static void
vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif, int width, VipsPel * restrict dst) vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
int width, VipsPel * restrict dst )
{ {
guint32 *idst; guint32 *idst = (guint32 *) dst;
idst = (guint32 *) dst;
int x; int x;
for( x = 0; x < width; x++ ) {
for( x = 0; x < width; x++ ) {
VipsPel v = gif->line[x]; VipsPel v = gif->line[x];
if( v != gif->transparent_index ) { if( v != gif->transparent_index )
/* Blast in the RGBA for this value.
*/
idst[x] = gif->cmap[v]; idst[x] = gif->cmap[v];
} }
}
} }
/* Render the current gif frame into an RGBA buffer. GIFs can accumulate, /* Render the current gif frame into an RGBA buffer. GIFs can accumulate,
@ -882,14 +877,14 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
*/ */
vips_foreign_load_gif_build_cmap( gif ); vips_foreign_load_gif_build_cmap( gif );
/* PREVIOUS means we init the frame with the last un-disposed frame. So the last un-disposed frame is used as /* PREVIOUS means we init the frame with the last un-disposed frame.
* a backdrop for the new frame. * So the last un-disposed frame is used as a backdrop for the new
* frame.
*/ */
if( gif->dispose == DISPOSE_PREVIOUS ) { if( gif->dispose == DISPOSE_PREVIOUS )
memcpy( VIPS_IMAGE_ADDR( gif->scratch, 0, 0 ), memcpy( VIPS_IMAGE_ADDR( gif->scratch, 0, 0 ),
VIPS_IMAGE_ADDR( gif->previous, 0, 0 ), VIPS_IMAGE_ADDR( gif->previous, 0, 0 ),
VIPS_IMAGE_SIZEOF_IMAGE( gif->scratch ) ); VIPS_IMAGE_SIZEOF_IMAGE( gif->scratch ) );
}
/* giflib does not check that the Left / Top / Width / Height for this /* giflib does not check that the Left / Top / Width / Height for this
* Image is inside the canvas. * Image is inside the canvas.
@ -922,37 +917,44 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
for( i = 0; i < 4; i++ ) { for( i = 0; i < 4; i++ ) {
int y; int y;
for( y = InterlacedOffset[i]; y < file->Image.Height; y += InterlacedJumps[i] ) {
if( DGifGetLine( gif->file, gif->line, file->Image.Width ) == GIF_ERROR ) { for( y = InterlacedOffset[i]; y < file->Image.Height;
y += InterlacedJumps[i] ) {
VipsPel *dst = VIPS_IMAGE_ADDR( gif->scratch,
file->Image.Left, file->Image.Top + y );
if( DGifGetLine( gif->file,
gif->line, file->Image.Width ) ==
GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
VipsPel *dst = VIPS_IMAGE_ADDR(gif->scratch, file->Image.Left, file->Image.Top + y ); vips_foreign_load_gif_render_line( gif,
file->Image.Width, dst );
vips_foreign_load_gif_render_line( gif, file->Image.Width, dst );
} }
} }
} }
else { else {
int y;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: " VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: "
"non-interlaced frame of %d x %d pixels at %d x %d\n", "non-interlaced frame of %d x %d pixels at %d x %d\n",
file->Image.Width, file->Image.Height, file->Image.Width, file->Image.Height,
file->Image.Left, file->Image.Top ); file->Image.Left, file->Image.Top );
int y;
for( y = 0; y < file->Image.Height; y++ ) { for( y = 0; y < file->Image.Height; y++ ) {
VipsPel *dst = VIPS_IMAGE_ADDR( gif->scratch,
file->Image.Left, file->Image.Top + y );
if( DGifGetLine( gif->file, gif->line, file->Image.Width ) == GIF_ERROR ) { if( DGifGetLine( gif->file,
gif->line, file->Image.Width ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
VipsPel *dst = VIPS_IMAGE_ADDR(gif->scratch, file->Image.Left, file->Image.Top + y ); vips_foreign_load_gif_render_line( gif,
file->Image.Width, dst );
vips_foreign_load_gif_render_line( gif, file->Image.Width, dst );
} }
} }
@ -962,25 +964,40 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
VIPS_IMAGE_ADDR(gif->scratch, 0, 0 ), VIPS_IMAGE_ADDR(gif->scratch, 0, 0 ),
VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) ); VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );
if( gif->dispose == DISPOSE_BACKGROUND ) {
/* BACKGROUND means we reset the frame to transparent before we /* BACKGROUND means we reset the frame to transparent before we
* render the next set of pixels. * render the next set of pixels.
*/ */
if( gif->dispose == DISPOSE_BACKGROUND ) { guint32 *q = (guint32 *) VIPS_IMAGE_ADDR( gif->scratch,
int y1; file->Image.Left, file->Image.Top );
int x1;
for( y1 = file->Image.Top; y1 < file->Image.Top + file->Image.Height; y1++ ) { /* What we write for transparent pixels. We want RGB to be
for (x1 = file->Image.Left; x1 < file->Image.Left + file->Image.Width; x1++ ) { * 255, and A to be 0.
*((guint32 *) VIPS_IMAGE_ADDR(gif->scratch, x1, y1 )) = GIF_TRANSPARENT_COLOR; */
guint32 ink = GUINT32_TO_BE( 0xffffff00 );
int x, y;
/* Generate the first line a pixel at a time, memcpy() for
* subsequent lines.
*/
if( file->Image.Height > 0 )
for( x = 0; x < file->Image.Width; x++ )
q[x] = ink;
for( y = 1; y < file->Image.Height; y++ )
memcpy( q + gif->scratch->Xsize * y,
q,
file->Image.Width * sizeof( guint32 ) );
} }
} else if( gif->dispose == DISPOSAL_UNSPECIFIED ||
} gif->dispose == DISPOSE_DO_NOT )
else if( gif->dispose == DISPOSAL_UNSPECIFIED || gif->dispose == DISPOSE_DO_NOT) { /* Copy the frame to previous, so it can be restored if
/* Copy the frame to previous, so it can be restored if DISPOSE_PREVIOUS is specified in a later frame. * DISPOSE_PREVIOUS is specified in a later frame.
*/ */
memcpy( VIPS_IMAGE_ADDR( gif->previous, 0, 0 ), memcpy( VIPS_IMAGE_ADDR( gif->previous, 0, 0 ),
VIPS_IMAGE_ADDR(gif->frame, 0, 0 ), VIPS_IMAGE_ADDR(gif->frame, 0, 0 ),
VIPS_IMAGE_SIZEOF_IMAGE( gif->previous ) ); VIPS_IMAGE_SIZEOF_IMAGE( gif->previous ) );
}
/* Reset values, as Graphic Control Extension is optional /* Reset values, as Graphic Control Extension is optional
*/ */
@ -1021,21 +1038,20 @@ vips_foreign_load_gif_extension( VipsForeignLoadGif *gif )
if( extension && if( extension &&
ext_code == GRAPHICS_EXT_FUNC_CODE && ext_code == GRAPHICS_EXT_FUNC_CODE &&
extension[0] == 4 ) { extension[0] == 4 ) {
int flags = extension[1]; int flags = extension[1];
/* Bytes are flags, delay low, delay high, /* Bytes are flags, delay low, delay high, transparency.
* transparency. Flag bit 1 means transparency * Flag bit 1 means transparency is being set.
* is being set.
*/ */
gif->transparent_index = ( flags & TRANSPARENT_MASK ) ? extension[4] : NO_TRANSPARENT_INDEX; gif->transparent_index = (flags & TRANSPARENT_MASK) ?
VIPS_DEBUG_MSG("vips_foreign_load_gif_extension: " extension[4] : NO_TRANSPARENT_INDEX;
"transparency = %d\n", gif->transparent_index); VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: "
"transparency = %d\n", gif->transparent_index );
/* Set the current dispose mode. This is read during frame load /* Set the current dispose mode. This is read during frame load
* to set the meaning of background and transparent pixels. * to set the meaning of background and transparent pixels.
*/ */
gif->dispose = ( flags >> DISPOSE_SHIFT ) & DISPOSE_MASK; gif->dispose = (flags >> DISPOSE_SHIFT) & DISPOSE_MASK;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: " VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: "
"dispose = %s\n", dispose2str( gif->dispose ) ); "dispose = %s\n", dispose2str( gif->dispose ) );
@ -1198,6 +1214,23 @@ vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif )
vips_source_minimise( gif->source ); vips_source_minimise( gif->source );
} }
static VipsImage *
vips_foreign_load_gif_temp( VipsForeignLoadGif *gif )
{
VipsImage *temp;
temp = vips_image_new_memory();
vips_image_init_fields( temp,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( temp ) ) {
VIPS_UNREF( temp );
return( NULL );
}
return( temp );
}
static int static int
vips_foreign_load_gif_load( VipsForeignLoad *load ) vips_foreign_load_gif_load( VipsForeignLoad *load )
{ {
@ -1210,35 +1243,11 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
if( vips_foreign_load_gif_open_giflib( gif ) ) if( vips_foreign_load_gif_open_giflib( gif ) )
return( -1 ); return( -1 );
/* Make the memory image we accumulate pixels in. We always accumulate /* Set of temp images we use during rendering.
* to RGBA, then trim down to whatever the output image needs on
* _generate.
*/ */
gif->frame = vips_image_new_memory(); if( !(gif->frame = vips_foreign_load_gif_temp( gif )) ||
vips_image_init_fields( gif->frame, !(gif->scratch = vips_foreign_load_gif_temp( gif )) ||
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, !(gif->previous = vips_foreign_load_gif_temp( gif )) )
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( gif->frame ) )
return( -1 );
/* An additional scratch buffer, same size as gif->frame. Used together with gif->frame for rendering.
*/
gif->scratch = vips_image_new_memory();
vips_image_init_fields( gif->scratch,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( gif->scratch ) )
return( -1 );
/* A copy of the previous state of the frame, in case we have to
* process a DISPOSE_PREVIOUS.
*/
gif->previous = vips_image_new_memory();
vips_image_init_fields( gif->previous,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( gif->previous ) )
return( -1 ); return( -1 );
/* Make the output pipeline. /* Make the output pipeline.

View File

@ -276,16 +276,18 @@ read_new( VipsSource *source, VipsImage *out, gboolean fail )
user_error_function, user_warning_function )) ) user_error_function, user_warning_function )) )
return( NULL ); return( NULL );
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE /* Prevent libpng (>=1.6.11) verifying sRGB profiles. Many PNGs have
/* Prevent libpng (>=1.6.11) verifying sRGB profiles. * broken profiles, but we still want to be able to open them.
*/ */
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
png_set_option( read->pPng, png_set_option( read->pPng,
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON ); PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/ #endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Disable CRC checking in fuzzing mode. Most fuzzed images will have
/* Disable CRC checking in fuzzing mode. * bad CRCs so this check would break fuzzing.
*/ */
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
png_set_crc_action( read->pPng, png_set_crc_action( read->pPng,
PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE ); PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE );
#endif /*FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION*/ #endif /*FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION*/
@ -813,9 +815,11 @@ write_new( VipsImage *in, VipsTarget *target )
return( NULL ); return( NULL );
} }
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE /* Prevent libpng (>=1.6.11) verifying sRGB profiles. We are often
/* Prevent libpng (>=1.6.11) verifying sRGB profiles. * asked to copy images containing bad profiles, and this check would
* prevent that.
*/ */
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
png_set_option( write->pPng, png_set_option( write->pPng,
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON ); PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/ #endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/