commit
2b6cc4229e
@ -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]
|
||||||
|
@ -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,23 +845,20 @@ 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,
|
||||||
* depending on the current dispose mode.
|
* depending on the current dispose mode.
|
||||||
@ -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,14 +1038,13 @@ 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) ?
|
||||||
|
extension[4] : NO_TRANSPARENT_INDEX;
|
||||||
VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: "
|
VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: "
|
||||||
"transparency = %d\n", gif->transparent_index );
|
"transparency = %d\n", gif->transparent_index );
|
||||||
|
|
||||||
@ -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.
|
||||||
|
@ -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*/
|
||||||
|
Loading…
Reference in New Issue
Block a user