sync fork

This commit is contained in:
Tomáš Szabo 2019-06-30 17:10:56 +02:00
commit 166aae1440
No known key found for this signature in database
GPG Key ID: 96F1E84929851783
4 changed files with 225 additions and 200 deletions

View File

@ -1,5 +1,6 @@
20/6/19 started 8.9.0 20/6/19 started 8.9.0
- add vips_image_get/set_array_int() - add vips_image_get/set_array_int()
- disable webp alpha output if all frame fill the canvas and are solid
24/5/19 started 8.8.1 24/5/19 started 8.8.1
- improve realpath() use on older libc - improve realpath() use on older libc

View File

@ -27,7 +27,7 @@
/* /*
This file is part of VIPS. This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
@ -90,10 +90,10 @@
/* Added in giflib5. /* Added in giflib5.
*/ */
#ifndef HAVE_GIFLIB_5 #ifndef HAVE_GIFLIB_5
#define DISPOSAL_UNSPECIFIED 0 #define DISPOSAL_UNSPECIFIED 0
#define DISPOSE_DO_NOT 1 #define DISPOSE_DO_NOT 1
#define DISPOSE_BACKGROUND 2 #define DISPOSE_BACKGROUND 2
#define DISPOSE_PREVIOUS 3 #define DISPOSE_PREVIOUS 3
#endif #endif
#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_gif_get_type()) #define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_gif_get_type())
@ -142,7 +142,7 @@ typedef struct _VipsForeignLoadGif {
/* The GIF comment, if any. /* The GIF comment, if any.
*/ */
char *comment; char *comment;
/* The number of pages (frame) in the image. /* The number of pages (frame) in the image.
*/ */
@ -199,12 +199,12 @@ typedef struct _VipsForeignLoadGifClass {
int (*open)( VipsForeignLoadGif *gif ); int (*open)( VipsForeignLoadGif *gif );
} VipsForeignLoadGifClass; } VipsForeignLoadGifClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif, G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif,
VIPS_TYPE_FOREIGN_LOAD ); VIPS_TYPE_FOREIGN_LOAD );
/* From gif2rgb.c ... offsets and jumps for interlaced GIF images. /* From gif2rgb.c ... offsets and jumps for interlaced GIF images.
*/ */
static int static int
InterlacedOffset[] = { 0, 4, 2, 1 }, InterlacedOffset[] = { 0, 4, 2, 1 },
InterlacedJumps[] = { 8, 8, 4, 2 }; InterlacedJumps[] = { 8, 8, 4, 2 };
@ -214,50 +214,50 @@ static const char *
vips_foreign_load_gif_errstr( int error_code ) vips_foreign_load_gif_errstr( int error_code )
{ {
#ifdef HAVE_GIFLIB_5 #ifdef HAVE_GIFLIB_5
return( GifErrorString( error_code ) ); return( GifErrorString( error_code ) );
#else /*!HAVE_GIFLIB_5*/ #else /*!HAVE_GIFLIB_5*/
switch( error_code ) { switch( error_code ) {
case D_GIF_ERR_OPEN_FAILED: case D_GIF_ERR_OPEN_FAILED:
return( _( "Failed to open given file" ) ); return( _( "Failed to open given file" ) );
case D_GIF_ERR_READ_FAILED: case D_GIF_ERR_READ_FAILED:
return( _( "Failed to read from given file" ) ); return( _( "Failed to read from given file" ) );
case D_GIF_ERR_NOT_GIF_FILE: case D_GIF_ERR_NOT_GIF_FILE:
return( _( "Data is not a GIF file" ) ); return( _( "Data is not a GIF file" ) );
case D_GIF_ERR_NO_SCRN_DSCR: case D_GIF_ERR_NO_SCRN_DSCR:
return( _( "No screen descriptor detected" ) ); return( _( "No screen descriptor detected" ) );
case D_GIF_ERR_NO_IMAG_DSCR: case D_GIF_ERR_NO_IMAG_DSCR:
return( _( "No image descriptor detected" ) ); return( _( "No image descriptor detected" ) );
case D_GIF_ERR_NO_COLOR_MAP: case D_GIF_ERR_NO_COLOR_MAP:
return( _( "Neither global nor local color map" ) ); return( _( "Neither global nor local color map" ) );
case D_GIF_ERR_WRONG_RECORD: case D_GIF_ERR_WRONG_RECORD:
return( _( "Wrong record type detected" ) ); return( _( "Wrong record type detected" ) );
case D_GIF_ERR_DATA_TOO_BIG: case D_GIF_ERR_DATA_TOO_BIG:
return( _( "Number of pixels bigger than width * height" ) ); return( _( "Number of pixels bigger than width * height" ) );
case D_GIF_ERR_NOT_ENOUGH_MEM: case D_GIF_ERR_NOT_ENOUGH_MEM:
return( _( "Failed to allocate required memory" ) ); return( _( "Failed to allocate required memory" ) );
case D_GIF_ERR_CLOSE_FAILED: case D_GIF_ERR_CLOSE_FAILED:
return( _( "Failed to close given file" ) ); return( _( "Failed to close given file" ) );
case D_GIF_ERR_NOT_READABLE: case D_GIF_ERR_NOT_READABLE:
return( _( "Given file was not opened for read" ) ); return( _( "Given file was not opened for read" ) );
case D_GIF_ERR_IMAGE_DEFECT: case D_GIF_ERR_IMAGE_DEFECT:
return( _( "Image is defective, decoding aborted" ) ); return( _( "Image is defective, decoding aborted" ) );
case D_GIF_ERR_EOF_TOO_SOON: case D_GIF_ERR_EOF_TOO_SOON:
return( _( "Image EOF detected, before image complete" ) ); return( _( "Image EOF detected, before image complete" ) );
default: default:
return( _( "Unknown error" ) ); return( _( "Unknown error" ) );
} }
#endif /*HAVE_GIFLIB_5*/ #endif /*HAVE_GIFLIB_5*/
} }
@ -270,7 +270,7 @@ vips_foreign_load_gif_error_vips( VipsForeignLoadGif *gif, int error )
const char *message; const char *message;
if( (message = vips_foreign_load_gif_errstr( error )) ) if( (message = vips_foreign_load_gif_errstr( error )) )
vips_error( class->nickname, "%s", message ); vips_error( class->nickname, "%s", message );
} }
static void static void
@ -281,14 +281,14 @@ vips_foreign_load_gif_error( VipsForeignLoadGif *gif )
error = 0; error = 0;
#ifdef HAVE_GIFLIB_5 #ifdef HAVE_GIFLIB_5
if( gif->file ) if( gif->file )
error = gif->file->Error; error = gif->file->Error;
#else #else
error = GifLastError(); error = GifLastError();
#endif #endif
if( error ) if( error )
vips_foreign_load_gif_error_vips( gif, error ); vips_foreign_load_gif_error_vips( gif, error );
} }
static void static void
@ -296,16 +296,16 @@ vips_foreign_load_gif_close( VipsForeignLoadGif *gif )
{ {
#ifdef HAVE_GIFLIB_5 #ifdef HAVE_GIFLIB_5
if( gif->file ) { if( gif->file ) {
int error; int error;
if( DGifCloseFile( gif->file, &error ) == GIF_ERROR ) if( DGifCloseFile( gif->file, &error ) == GIF_ERROR )
vips_foreign_load_gif_error_vips( gif, error ); vips_foreign_load_gif_error_vips( gif, error );
gif->file = NULL; gif->file = NULL;
} }
#else #else
if( gif->file ) { if( gif->file ) {
if( DGifCloseFile( gif->file ) == GIF_ERROR ) if( DGifCloseFile( gif->file ) == GIF_ERROR )
vips_foreign_load_gif_error_vips( gif, GifLastError() ); vips_foreign_load_gif_error_vips( gif, GifLastError() );
gif->file = NULL; gif->file = NULL;
} }
#endif #endif
@ -316,13 +316,13 @@ vips_foreign_load_gif_dispose( GObject *gobject )
{ {
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject;
vips_foreign_load_gif_close( gif ); vips_foreign_load_gif_close( gif );
VIPS_UNREF( gif->frame ); VIPS_UNREF( gif->frame );
VIPS_UNREF( gif->previous ); VIPS_UNREF( gif->previous );
VIPS_FREE( gif->comment ); VIPS_FREE( gif->comment );
VIPS_FREE( gif->line ); VIPS_FREE( gif->line );
VIPS_FREE( gif->delays ); VIPS_FREE( gif->delays );
G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )->
dispose( gobject ); dispose( gobject );
@ -346,7 +346,7 @@ vips_foreign_load_gif_is_a_buffer( const void *buf, size_t len )
const guchar *str = (const guchar *) buf; const guchar *str = (const guchar *) buf;
if( len >= 4 && if( len >= 4 &&
str[0] == 'G' && str[0] == 'G' &&
str[1] == 'I' && str[1] == 'I' &&
str[2] == 'F' && str[2] == 'F' &&
str[3] == '8' ) str[3] == '8' )
@ -368,31 +368,31 @@ vips_foreign_load_gif_is_a( const char *filename )
} }
static int static int
vips_foreign_load_gif_ext_next( VipsForeignLoadGif *gif, vips_foreign_load_gif_ext_next( VipsForeignLoadGif *gif,
GifByteType **extension ) GifByteType **extension )
{ {
if( DGifGetExtensionNext( gif->file, extension ) == GIF_ERROR ) { if( DGifGetExtensionNext( gif->file, extension ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
if( *extension ) if( *extension )
VIPS_DEBUG_MSG( "gifload: EXTENSION_NEXT\n" ); VIPS_DEBUG_MSG( "gifload: EXTENSION_NEXT\n" );
return( 0 ); return( 0 );
} }
static int static int
vips_foreign_load_gif_code_next( VipsForeignLoadGif *gif, vips_foreign_load_gif_code_next( VipsForeignLoadGif *gif,
GifByteType **extension ) GifByteType **extension )
{ {
if( DGifGetCodeNext( gif->file, extension ) == GIF_ERROR ) { if( DGifGetCodeNext( gif->file, extension ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
if( *extension ) if( *extension )
VIPS_DEBUG_MSG( "gifload: CODE_NEXT\n" ); VIPS_DEBUG_MSG( "gifload: CODE_NEXT\n" );
return( 0 ); return( 0 );
} }
@ -400,7 +400,7 @@ vips_foreign_load_gif_code_next( VipsForeignLoadGif *gif,
/* Quickly scan an image record. /* Quickly scan an image record.
*/ */
static int static int
vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif ) vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif )
{ {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
GifFileType *file = gif->file; GifFileType *file = gif->file;
@ -420,8 +420,8 @@ vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif )
file->Image.Height < 1 || file->Image.Height < 1 ||
file->Image.Height > 10000 || file->Image.Height > 10000 ||
file->Image.Top + file->Image.Height > file->SHeight ) { file->Image.Top + file->Image.Height > file->SHeight ) {
vips_error( class->nickname, "%s", _( "bad frame size" ) ); vips_error( class->nickname, "%s", _( "bad frame size" ) );
return( -1 ); return( -1 );
} }
/* Test for a non-greyscale colourmap for this frame. /* Test for a non-greyscale colourmap for this frame.
@ -430,7 +430,7 @@ vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif )
map ) { map ) {
int i; int i;
for( i = 0; i < map->ColorCount; i++ ) for( i = 0; i < map->ColorCount; i++ )
if( map->Colors[i].Red != map->Colors[i].Green || if( map->Colors[i].Red != map->Colors[i].Green ||
map->Colors[i].Green != map->Colors[i].Blue ) { map->Colors[i].Green != map->Colors[i].Blue ) {
gif->has_colour = TRUE; gif->has_colour = TRUE;
@ -441,7 +441,7 @@ vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif )
/* Step over compressed image data. /* Step over compressed image data.
*/ */
do { do {
if( vips_foreign_load_gif_code_next( gif, &extension ) ) if( vips_foreign_load_gif_code_next( gif, &extension ) )
return( -1 ); return( -1 );
} while( extension != NULL ); } while( extension != NULL );
@ -458,17 +458,17 @@ 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", (const char*) (extension + 1) ) )
have_netscape = TRUE; have_netscape = TRUE;
while( extension != NULL ) { while( extension != NULL ) {
if( vips_foreign_load_gif_ext_next( gif, &extension ) ) if( vips_foreign_load_gif_ext_next( gif, &extension ) )
return( -1 ); return( -1 );
if( have_netscape && if( have_netscape &&
extension && extension &&
extension[0] == 3 && extension[0] == 3 &&
extension[1] == 1 ) extension[1] == 1 )
gif->loop = extension[2] | (extension[3] << 8); gif->loop = extension[2] | (extension[3] << 8);
} }
@ -479,21 +479,21 @@ static int
vips_foreign_load_gif_scan_comment_ext( VipsForeignLoadGif *gif, vips_foreign_load_gif_scan_comment_ext( VipsForeignLoadGif *gif,
GifByteType *extension ) GifByteType *extension )
{ {
VIPS_DEBUG_MSG( "gifload: type: comment\n" ); VIPS_DEBUG_MSG( "gifload: type: comment\n" );
if( !gif->comment ) { if( !gif->comment ) {
/* Up to 257 with a NULL terminator. /* Up to 257 with a NULL terminator.
*/ */
char comment[257]; char comment[257];
vips_strncpy( comment, (char *) (extension + 1), 256 ); vips_strncpy( comment, (char *) (extension + 1), 256 );
comment[extension[0]] = '\0'; comment[extension[0]] = '\0';
gif->comment = g_strdup( comment ); gif->comment = g_strdup( comment );
} }
while( extension != NULL ) while( extension != NULL )
if( vips_foreign_load_gif_ext_next( gif, &extension ) ) if( vips_foreign_load_gif_ext_next( gif, &extension ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
} }
@ -504,18 +504,18 @@ vips_foreign_load_gif_scan_extension( VipsForeignLoadGif *gif )
GifByteType *extension; GifByteType *extension;
int ext_code; int ext_code;
if( DGifGetExtension( gif->file, &ext_code, &extension ) == if( DGifGetExtension( gif->file, &ext_code, &extension ) ==
GIF_ERROR ) { GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
if( extension ) if( extension )
switch( ext_code ) { switch( ext_code ) {
case GRAPHICS_EXT_FUNC_CODE: case GRAPHICS_EXT_FUNC_CODE:
if( extension[0] == 4 && if( extension[0] == 4 &&
extension[1] & 0x1 ) { extension[1] & 0x1 ) {
VIPS_DEBUG_MSG( "gifload: has transp.\n" ); VIPS_DEBUG_MSG( "gifload: has transp.\n" );
gif->has_transparency = TRUE; gif->has_transparency = TRUE;
} }
@ -524,32 +524,32 @@ vips_foreign_load_gif_scan_extension( VipsForeignLoadGif *gif )
} }
gif->delays[gif->n_pages] = (extension[2] | (extension[3] << 8)) * 10; gif->delays[gif->n_pages] = (extension[2] | (extension[3] << 8)) * 10;
while( extension != NULL ) while( extension != NULL )
if( vips_foreign_load_gif_ext_next( gif, if( vips_foreign_load_gif_ext_next( gif,
&extension ) ) &extension ) )
return( -1 ); return( -1 );
break; break;
case APPLICATION_EXT_FUNC_CODE: case APPLICATION_EXT_FUNC_CODE:
if( vips_foreign_load_gif_scan_application_ext( gif, if( vips_foreign_load_gif_scan_application_ext( gif,
extension ) ) extension ) )
return( -1 ); return( -1 );
break; break;
case COMMENT_EXT_FUNC_CODE: case COMMENT_EXT_FUNC_CODE:
if( vips_foreign_load_gif_scan_comment_ext( gif, if( vips_foreign_load_gif_scan_comment_ext( gif,
extension ) ) extension ) )
return( -1 ); return( -1 );
break; break;
default: default:
/* Step over any NEXT blocks for unknown extensions. /* Step over any NEXT blocks for unknown extensions.
*/ */
while( extension != NULL ) while( extension != NULL )
if( vips_foreign_load_gif_ext_next( gif, if( vips_foreign_load_gif_ext_next( gif,
&extension ) ) &extension ) )
return( -1 ); return( -1 );
break; break;
} }
@ -560,16 +560,16 @@ static int
vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image )
{ {
vips_image_init_fields( image, vips_image_init_fields( image,
gif->file->SWidth, gif->file->SHeight * gif->n, gif->file->SWidth, gif->file->SHeight * gif->n,
(gif->has_colour ? 3 : 1) + (gif->has_transparency ? 1 : 0), (gif->has_colour ? 3 : 1) + (gif->has_transparency ? 1 : 0),
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
gif->has_colour ? gif->has_colour ?
VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_B_W, VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_B_W,
1.0, 1.0 ); 1.0, 1.0 );
vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) )
vips_image_set_int( image, vips_image_set_int( image,
VIPS_META_PAGE_HEIGHT, gif->file->SHeight ); VIPS_META_PAGE_HEIGHT, gif->file->SHeight );
vips_image_set_int( image, VIPS_META_N_PAGES, gif->n_pages ); vips_image_set_int( image, VIPS_META_N_PAGES, gif->n_pages );
vips_image_set_int( image, "gif-loop", gif->loop ); vips_image_set_int( image, "gif-loop", gif->loop );
@ -581,20 +581,20 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image )
vips_image_set_int( image, "gif-delay", 4 ); vips_image_set_int( image, "gif-delay", 4 );
} }
if( gif->comment ) if( gif->comment )
vips_image_set_string( image, "gif-comment", gif->comment ); vips_image_set_string( image, "gif-comment", gif->comment );
return( 0 ); return( 0 );
} }
/* Attempt to quickly scan a GIF and discover what we need for our header. We /* Attempt to quickly scan a GIF and discover what we need for our header. We
* need to scan the whole file to get n_pages, transparency and colour. * need to scan the whole file to get n_pages, transparency and colour.
*/ */
static int static int
vips_foreign_load_gif_header( VipsForeignLoad *load ) vips_foreign_load_gif_header( VipsForeignLoad *load )
{ {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGifClass *gif_class = VipsForeignLoadGifClass *gif_class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
@ -605,17 +605,17 @@ vips_foreign_load_gif_header( VipsForeignLoad *load )
gif->n_pages = 0; gif->n_pages = 0;
do { do {
if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) { if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
switch( record ) { switch( record ) {
case IMAGE_DESC_RECORD_TYPE: case IMAGE_DESC_RECORD_TYPE:
if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) { if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
/* Read in the image record. /* Read in the image record.
@ -654,8 +654,8 @@ vips_foreign_load_gif_header( VipsForeignLoad *load )
if( gif->page < 0 || if( gif->page < 0 ||
gif->n <= 0 || gif->n <= 0 ||
gif->page + gif->n > gif->n_pages ) { gif->page + gif->n > gif->n_pages ) {
vips_error( class->nickname, "%s", _( "bad page number" ) ); vips_error( class->nickname, "%s", _( "bad page number" ) );
return( -1 ); return( -1 );
} }
/* And set the output vips header from what we've learned. /* And set the output vips header from what we've learned.
@ -705,13 +705,13 @@ vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
iq = (guint32 *) q; iq = (guint32 *) q;
for( x = 0; x < width; x++ ) { for( x = 0; x < width; x++ ) {
VipsPel v = p[x]; VipsPel v = p[x];
if( v == gif->transparency ) { if( v == gif->transparency ) {
/* In DISPOSE_DO_NOT mode, the previous frame shows /* In DISPOSE_DO_NOT mode, the previous frame shows
* through (ie. we do nothing). In all other modes, * through (ie. we do nothing). In all other modes,
* it's just transparent. * it's just transparent.
*/ */
if( gif->dispose != DISPOSE_DO_NOT ) if( gif->dispose != DISPOSE_DO_NOT )
iq[x] = 0; iq[x] = 0;
} }
else else
@ -721,11 +721,11 @@ vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
} }
} }
/* 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.
*/ */
static int static int
vips_foreign_load_gif_render( VipsForeignLoadGif *gif ) vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
{ {
GifFileType *file = gif->file; GifFileType *file = gif->file;
@ -733,11 +733,11 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
*/ */
vips_foreign_load_gif_build_cmap( gif ); vips_foreign_load_gif_build_cmap( gif );
/* BACKGROUND means we reset the frame to 0 (transparent) before we /* BACKGROUND means we reset the frame to 0 (transparent) before we
* render the next set of pixels. * render the next set of pixels.
*/ */
if( gif->dispose == DISPOSE_BACKGROUND ) if( gif->dispose == DISPOSE_BACKGROUND )
memset( VIPS_IMAGE_ADDR( gif->frame, 0, 0 ), 0, memset( VIPS_IMAGE_ADDR( gif->frame, 0, 0 ), 0,
VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) ); VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );
/* PREVIOUS means we init the frame with the frame before last, ie. we /* PREVIOUS means we init the frame with the frame before last, ie. we
@ -745,11 +745,11 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
* *
* Anything other than PREVIOUS, we must update the previous buffer, * Anything other than PREVIOUS, we must update the previous buffer,
*/ */
if( gif->dispose == DISPOSE_PREVIOUS ) if( gif->dispose == DISPOSE_PREVIOUS )
memcpy( VIPS_IMAGE_ADDR( gif->frame, 0, 0 ), memcpy( VIPS_IMAGE_ADDR( gif->frame, 0, 0 ),
VIPS_IMAGE_ADDR( gif->previous, 0, 0 ), VIPS_IMAGE_ADDR( gif->previous, 0, 0 ),
VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) ); VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );
else else
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->frame ) ); VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );
@ -760,25 +760,25 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: " VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: "
"interlaced frame of %d x %d pixels at %d x %d\n", "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 );
for( i = 0; i < 4; i++ ) { for( i = 0; i < 4; i++ ) {
int y; int y;
for( y = InterlacedOffset[i]; for( y = InterlacedOffset[i];
y < file->Image.Height; y < file->Image.Height;
y += InterlacedJumps[i] ) { y += InterlacedJumps[i] ) {
VipsPel *q = VIPS_IMAGE_ADDR( gif->frame, VipsPel *q = VIPS_IMAGE_ADDR( gif->frame,
file->Image.Left, file->Image.Top + y ); file->Image.Left, file->Image.Top + y );
if( DGifGetLine( gif->file, gif->line, if( DGifGetLine( gif->file, gif->line,
file->Image.Width ) == GIF_ERROR ) { file->Image.Width ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
vips_foreign_load_gif_render_line( gif, vips_foreign_load_gif_render_line( gif,
file->Image.Width, q, gif->line ); file->Image.Width, q, gif->line );
} }
} }
} }
@ -788,20 +788,20 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif )
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 );
for( y = 0; y < file->Image.Height; y++ ) { for( y = 0; y < file->Image.Height; y++ ) {
VipsPel *q = VIPS_IMAGE_ADDR( gif->frame, VipsPel *q = VIPS_IMAGE_ADDR( gif->frame,
file->Image.Left, file->Image.Top + y ); file->Image.Left, file->Image.Top + y );
if( DGifGetLine( gif->file, gif->line, if( DGifGetLine( gif->file, gif->line,
file->Image.Width ) == GIF_ERROR ) { file->Image.Width ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
vips_foreign_load_gif_render_line( gif, vips_foreign_load_gif_render_line( gif,
file->Image.Width, q, gif->line ); file->Image.Width, q, gif->line );
} }
} }
@ -814,23 +814,23 @@ vips_foreign_load_gif_extension( VipsForeignLoadGif *gif )
GifByteType *extension; GifByteType *extension;
int ext_code; int ext_code;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension:\n" ); VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension:\n" );
if( DGifGetExtension( gif->file, &ext_code, &extension ) == if( DGifGetExtension( gif->file, &ext_code, &extension ) ==
GIF_ERROR ) { GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
if( extension && if( extension &&
ext_code == GRAPHICS_EXT_FUNC_CODE && ext_code == GRAPHICS_EXT_FUNC_CODE &&
extension[0] == 4 ) { extension[0] == 4 ) {
/* Bytes are flags, delay low, delay high, /* Bytes are flags, delay low, delay high,
* transparency. Flag bit 1 means transparency * transparency. Flag bit 1 means transparency
* is being set. * is being set.
*/ */
gif->transparency = -1; gif->transparency = -1;
if( extension[1] & 0x1 ) if( extension[1] & 0x1 )
gif->transparency = extension[4]; gif->transparency = extension[4];
/* Set the current dispose mode. This is read during frame load /* Set the current dispose mode. This is read during frame load
@ -838,12 +838,12 @@ vips_foreign_load_gif_extension( VipsForeignLoadGif *gif )
*/ */
gif->dispose = (extension[1] >> 2) & 0x7; gif->dispose = (extension[1] >> 2) & 0x7;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: " VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: "
"dispose = %d\n", gif->dispose ); "dispose = %d\n", gif->dispose );
} }
while( extension != NULL ) while( extension != NULL )
if( vips_foreign_load_gif_ext_next( gif, &extension ) ) if( vips_foreign_load_gif_ext_next( gif, &extension ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
} }
@ -857,24 +857,24 @@ vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif )
gboolean have_read_frame; gboolean have_read_frame;
have_read_frame = FALSE; have_read_frame = FALSE;
do { do {
if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) { if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
switch( record ) { switch( record ) {
case IMAGE_DESC_RECORD_TYPE: case IMAGE_DESC_RECORD_TYPE:
VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: " VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: "
"IMAGE_DESC_RECORD_TYPE\n" ); "IMAGE_DESC_RECORD_TYPE\n" );
if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) { if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) {
vips_foreign_load_gif_error( gif ); vips_foreign_load_gif_error( gif );
return( -1 ); return( -1 );
} }
if( vips_foreign_load_gif_render( gif ) ) if( vips_foreign_load_gif_render( gif ) )
return( -1 ); return( -1 );
have_read_frame = TRUE; have_read_frame = TRUE;
@ -887,7 +887,7 @@ vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif )
case TERMINATE_RECORD_TYPE: case TERMINATE_RECORD_TYPE:
VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: " VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: "
"TERMINATE_RECORD_TYPE\n" ); "TERMINATE_RECORD_TYPE\n" );
gif->eof = TRUE; gif->eof = TRUE;
break; break;
@ -911,7 +911,7 @@ vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif )
} }
static int static int
vips_foreign_load_gif_generate( VipsRegion *or, vips_foreign_load_gif_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop ) void *seq, void *a, void *b, gboolean *stop )
{ {
VipsRect *r = &or->valid; VipsRect *r = &or->valid;
@ -931,17 +931,17 @@ vips_foreign_load_gif_generate( VipsRegion *or,
g_assert( line >= 0 && line < gif->frame->Ysize ); g_assert( line >= 0 && line < gif->frame->Ysize );
g_assert( page >= 0 && page < gif->n_pages ); g_assert( page >= 0 && page < gif->n_pages );
/* current_page == 0 means we've not loaded any pages yet. So /* current_page == 0 means we've not loaded any pages yet. So
* we need to have loaded the page beyond the page we want. * we need to have loaded the page beyond the page we want.
*/ */
while( gif->current_page <= page ) { while( gif->current_page <= page ) {
if( vips_foreign_load_gif_next_page( gif ) ) if( vips_foreign_load_gif_next_page( gif ) )
return( -1 ); return( -1 );
gif->current_page += 1; gif->current_page += 1;
} }
/* @frame is always RGBA, but or may be G, GA, RGB or RGBA. /* @frame is always RGBA, but or may be G, GA, RGB or RGBA.
* We have to pick out the values we want. * We have to pick out the values we want.
*/ */
p = VIPS_IMAGE_ADDR( gif->frame, 0, line ); p = VIPS_IMAGE_ADDR( gif->frame, 0, line );
@ -993,10 +993,10 @@ vips_foreign_load_gif_generate( VipsRegion *or,
static int static int
vips_foreign_load_gif_load( VipsForeignLoad *load ) vips_foreign_load_gif_load( VipsForeignLoad *load )
{ {
VipsForeignLoadGifClass *class = VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( load ), 4 ); vips_object_local_array( VIPS_OBJECT( load ), 4 );
/* Rewind. /* Rewind.
@ -1004,27 +1004,27 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
if( class->open( gif ) ) if( class->open( gif ) )
return( -1 ); return( -1 );
VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" );
/* Make the memory image we accumulate pixels in. We always accumulate /* Make the memory image we accumulate pixels in. We always accumulate
* to RGBA, then trim down to whatever the output image needs on * to RGBA, then trim down to whatever the output image needs on
* _generate. * _generate.
*/ */
gif->frame = vips_image_new_memory(); gif->frame = vips_image_new_memory();
vips_image_init_fields( gif->frame, vips_image_init_fields( gif->frame,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( gif->frame ) ) if( vips_image_write_prepare( gif->frame ) )
return( -1 ); return( -1 );
/* A copy of the previous state of the frame, in case we have to /* A copy of the previous state of the frame, in case we have to
* process a DISPOSE_PREVIOUS. * process a DISPOSE_PREVIOUS.
*/ */
gif->previous = vips_image_new_memory(); gif->previous = vips_image_new_memory();
vips_image_init_fields( gif->previous, vips_image_init_fields( gif->previous,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( gif->previous ) ) if( vips_image_write_prepare( gif->previous ) )
return( -1 ); return( -1 );
/* Make the output pipeline. /* Make the output pipeline.
@ -1035,10 +1035,10 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
/* 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],
NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) || NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) ||
vips_sequential( t[0], &t[1], vips_sequential( t[0], &t[1],
"tile_height", VIPS__FATSTRIP_HEIGHT, "tile_height", VIPS__FATSTRIP_HEIGHT,
NULL ) || NULL ) ||
vips_image_write( t[1], load->real ) ) vips_image_write( t[1], load->real ) )
return( -1 ); return( -1 );
@ -1055,13 +1055,13 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif )
if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func, &error )) ) { if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func, &error )) ) {
vips_foreign_load_gif_error_vips( gif, error ); vips_foreign_load_gif_error_vips( gif, error );
return( -1 ); return( -1 );
} }
} }
#else #else
if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func )) ) { if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func )) ) {
vips_foreign_load_gif_error_vips( gif, GifLastError() ); vips_foreign_load_gif_error_vips( gif, GifLastError() );
return( -1 ); return( -1 );
} }
#endif #endif
@ -1070,9 +1070,9 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif )
/* Allocate a line buffer now that we have the GIF width. /* Allocate a line buffer now that we have the GIF width.
*/ */
VIPS_FREE( gif->line ) VIPS_FREE( gif->line )
if( !(gif->line = VIPS_ARRAY( NULL, gif->file->SWidth, GifPixelType )) ) if( !(gif->line = VIPS_ARRAY( NULL, gif->file->SWidth, GifPixelType )) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
} }
@ -1096,7 +1096,7 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
object_class->nickname = "gifload_base"; object_class->nickname = "gifload_base";
object_class->description = _( "load GIF with giflib" ); object_class->description = _( "load GIF with giflib" );
load_class->get_flags_filename = load_class->get_flags_filename =
vips_foreign_load_gif_get_flags_filename; vips_foreign_load_gif_get_flags_filename;
load_class->get_flags = vips_foreign_load_gif_get_flags; load_class->get_flags = vips_foreign_load_gif_get_flags;
@ -1132,7 +1132,7 @@ typedef struct _VipsForeignLoadGifFile {
/* Filename for load. /* Filename for load.
*/ */
char *filename; char *filename;
/* The FILE* we read from. /* The FILE* we read from.
*/ */
@ -1142,7 +1142,7 @@ typedef struct _VipsForeignLoadGifFile {
typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass; typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass;
G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file, G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file,
vips_foreign_load_gif_get_type() ); vips_foreign_load_gif_get_type() );
static void static void
@ -1150,7 +1150,7 @@ vips_foreign_load_gif_file_dispose( GObject *gobject )
{ {
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject; VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject;
VIPS_FREEF( fclose, file->fp ); VIPS_FREEF( fclose, file->fp );
G_OBJECT_CLASS( vips_foreign_load_gif_file_parent_class )-> G_OBJECT_CLASS( vips_foreign_load_gif_file_parent_class )->
dispose( gobject ); dispose( gobject );
@ -1159,9 +1159,9 @@ vips_foreign_load_gif_file_dispose( GObject *gobject )
/* Our input function for file open. We can't use DGifOpenFileName(), since /* Our input function for file open. We can't use DGifOpenFileName(), since
* that just calls open() and won't work with unicode on win32. We can't use * that just calls open() and won't work with unicode on win32. We can't use
* DGifOpenFileHandle() since that's an fd from open() and you can't pass those * DGifOpenFileHandle() since that's an fd from open() and you can't pass those
* across DLL boundaries on Windows. * across DLL boundaries on Windows.
*/ */
static int static int
vips_giflib_file_read( GifFileType *file, GifByteType *buffer, int n ) vips_giflib_file_read( GifFileType *file, GifByteType *buffer, int n )
{ {
FILE *fp = (FILE *) file->UserData; FILE *fp = (FILE *) file->UserData;
@ -1176,20 +1176,20 @@ vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif )
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif;
if( !file->fp ) { if( !file->fp ) {
if( !(file->fp = if( !(file->fp =
vips__file_open_read( file->filename, NULL, FALSE )) ) vips__file_open_read( file->filename, NULL, FALSE )) )
return( -1 ); return( -1 );
VIPS_SETSTR( load->out->filename, file->filename ); VIPS_SETSTR( load->out->filename, file->filename );
} }
else else
rewind( file->fp ); rewind( file->fp );
vips_foreign_load_gif_close( gif ); vips_foreign_load_gif_close( gif );
gif->userPtr = file->fp; gif->userPtr = file->fp;
gif->read_func = vips_giflib_file_read; gif->read_func = vips_giflib_file_read;
return( VIPS_FOREIGN_LOAD_GIF_CLASS( return( VIPS_FOREIGN_LOAD_GIF_CLASS(
vips_foreign_load_gif_file_parent_class )->open( gif ) ); vips_foreign_load_gif_file_parent_class )->open( gif ) );
} }
@ -1199,7 +1199,7 @@ static const char *vips_foreign_gif_suffs[] = {
}; };
static void static void
vips_foreign_load_gif_file_class_init( vips_foreign_load_gif_file_class_init(
VipsForeignLoadGifFileClass *class ) VipsForeignLoadGifFileClass *class )
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS( class ); GObjectClass *gobject_class = G_OBJECT_CLASS( class );
@ -1221,10 +1221,10 @@ vips_foreign_load_gif_file_class_init(
gif_class->open = vips_foreign_load_gif_file_open; gif_class->open = vips_foreign_load_gif_file_open;
VIPS_ARG_STRING( class, "filename", 1, VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ), _( "Filename" ),
_( "Filename to load from" ), _( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifFile, filename ), G_STRUCT_OFFSET( VipsForeignLoadGifFile, filename ),
NULL ); NULL );
@ -1251,7 +1251,7 @@ typedef struct _VipsForeignLoadGifBuffer {
typedef VipsForeignLoadGifClass VipsForeignLoadGifBufferClass; typedef VipsForeignLoadGifClass VipsForeignLoadGifBufferClass;
G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer, G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer,
vips_foreign_load_gif_get_type() ); vips_foreign_load_gif_get_type() );
/* Callback from the gif loader. /* Callback from the gif loader.
@ -1261,7 +1261,7 @@ G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer,
static int static int
vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n ) vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n )
{ {
VipsForeignLoadGifBuffer *buffer = VipsForeignLoadGifBuffer *buffer =
(VipsForeignLoadGifBuffer *) file->UserData; (VipsForeignLoadGifBuffer *) file->UserData;
size_t will_read = VIPS_MIN( n, buffer->bytes_to_go ); size_t will_read = VIPS_MIN( n, buffer->bytes_to_go );
@ -1269,7 +1269,7 @@ vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n )
buffer->p += will_read; buffer->p += will_read;
buffer->bytes_to_go -= will_read; buffer->bytes_to_go -= will_read;
return( will_read ); return( will_read );
} }
static int static int
@ -1283,12 +1283,12 @@ vips_foreign_load_gif_buffer_open( VipsForeignLoadGif *gif )
gif->userPtr = gif; gif->userPtr = gif;
gif->read_func = vips_giflib_buffer_read;; gif->read_func = vips_giflib_buffer_read;;
return( VIPS_FOREIGN_LOAD_GIF_CLASS( return( VIPS_FOREIGN_LOAD_GIF_CLASS(
vips_foreign_load_gif_file_parent_class )->open( gif ) ); vips_foreign_load_gif_file_parent_class )->open( gif ) );
} }
static void static void
vips_foreign_load_gif_buffer_class_init( vips_foreign_load_gif_buffer_class_init(
VipsForeignLoadGifBufferClass *class ) VipsForeignLoadGifBufferClass *class )
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS( class ); GObjectClass *gobject_class = G_OBJECT_CLASS( class );
@ -1306,10 +1306,10 @@ vips_foreign_load_gif_buffer_class_init(
gif_class->open = vips_foreign_load_gif_buffer_open; gif_class->open = vips_foreign_load_gif_buffer_open;
VIPS_ARG_BOXED( class, "buffer", 1, VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ), _( "Buffer" ),
_( "Buffer to load from" ), _( "Buffer to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifBuffer, buf ), G_STRUCT_OFFSET( VipsForeignLoadGifBuffer, buf ),
VIPS_TYPE_BLOB ); VIPS_TYPE_BLOB );
@ -1339,11 +1339,11 @@ vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer )
* *
* Use @n to select the number of pages to render. The default is 1. Pages are * Use @n to select the number of pages to render. The default is 1. Pages are
* rendered in a vertical column, with each individual page aligned to the * rendered in a vertical column, with each individual page aligned to the
* left. Set to -1 to mean "until the end of the document". Use vips_grid() * left. Set to -1 to mean "until the end of the document". Use vips_grid()
* to change page layout. * to change page layout.
* *
* The whole GIF is rendered into memory on header access. The output image * The whole GIF is rendered into memory on header access. The output image
* will be 1, 2, 3 or 4 bands depending on what the reader finds in the file. * will be 1, 2, 3 or 4 bands depending on what the reader finds in the file.
* *
* See also: vips_image_new_from_file(). * See also: vips_image_new_from_file().
* *
@ -1375,10 +1375,10 @@ vips_gifload( const char *filename, VipsImage **out, ... )
* * @n: %gint, load this many pages * * @n: %gint, load this many pages
* *
* Read a GIF-formatted memory block into a VIPS image. Exactly as * Read a GIF-formatted memory block into a VIPS image. Exactly as
* vips_gifload(), but read from a memory buffer. * vips_gifload(), but read from a memory buffer.
* *
* You must not free the buffer while @out is active. The * You must not free the buffer while @out is active. The
* #VipsObject::postclose signal on @out is a good place to free. * #VipsObject::postclose signal on @out is a good place to free.
* *
* See also: vips_gifload(). * See also: vips_gifload().
* *

View File

@ -6,6 +6,8 @@
* 17/2/19 * 17/2/19
* - support ICC, XMP, EXIF, IPTC metadata * - support ICC, XMP, EXIF, IPTC metadata
* - write with a single call to vips_sink_disc() * - write with a single call to vips_sink_disc()
* 29/6/19
* - support "strip" option
*/ */
/* /*
@ -170,7 +172,8 @@ vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
*/ */
image->dispose = BackgroundDispose; image->dispose = BackgroundDispose;
if( magick_set_magick_profile( image, im, magick->exception ) ) { if( !save->strip &&
magick_set_magick_profile( image, im, magick->exception ) ) {
magick_vips_error( class->nickname, magick->exception ); magick_vips_error( class->nickname, magick->exception );
return( -1 ); return( -1 );
} }

View File

@ -20,6 +20,8 @@
* 30/4/19 * 30/4/19
* - deprecate shrink, use scale instead, and make it a double ... this * - deprecate shrink, use scale instead, and make it a double ... this
* lets us do faster and more accurate thumbnailing * lets us do faster and more accurate thumbnailing
* 27/6/19
* - disable alpha output if all frame fill the canvas and are solid
*/ */
/* /*
@ -97,17 +99,22 @@ typedef struct {
*/ */
double scale; double scale;
/* Size of each frame in input image coordinates.
*/
int canvas_width;
int canvas_height;
/* Size of each frame, in scaled output image coordinates,
*/
int frame_width;
int frame_height;
/* Size of final output image. /* Size of final output image.
*/ */
int width; int width;
int height; int height;
/* Size of each frame. /* TRUE if we will save the final image as RGBA.
*/
int frame_width;
int frame_height;
/* TRUE for RGBA.
*/ */
int alpha; int alpha;
@ -418,8 +425,6 @@ static int
read_header( Read *read, VipsImage *out ) read_header( Read *read, VipsImage *out )
{ {
WebPData data; WebPData data;
int canvas_width;
int canvas_height;
int flags; int flags;
int i; int i;
@ -430,16 +435,19 @@ read_header( Read *read, VipsImage *out )
return( -1 ); return( -1 );
} }
canvas_width = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH ); read->canvas_width =
canvas_height = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT ); WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH );
read->canvas_height =
WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT );
/* We round-to-nearest cf. pdfload etc. /* We round-to-nearest cf. pdfload etc.
*/ */
read->frame_width = VIPS_RINT( canvas_width * read->scale ); read->frame_width = VIPS_RINT( read->canvas_width * read->scale );
read->frame_height = VIPS_RINT( canvas_height * read->scale ); read->frame_height = VIPS_RINT( read->canvas_height * read->scale );
#ifdef DEBUG #ifdef DEBUG
printf( "webp2vips: canvas_width = %d\n", canvas_width ); printf( "webp2vips: canvas_width = %d\n", read->canvas_width );
printf( "webp2vips: canvas_height = %d\n", canvas_height ); printf( "webp2vips: canvas_height = %d\n", read->canvas_height );
printf( "webp2vips: frame_width = %d\n", read->frame_width ); printf( "webp2vips: frame_width = %d\n", read->frame_width );
printf( "webp2vips: frame_height = %d\n", read->frame_height ); printf( "webp2vips: frame_height = %d\n", read->frame_height );
#endif /*DEBUG*/ #endif /*DEBUG*/
@ -493,7 +501,20 @@ read_header( Read *read, VipsImage *out )
/* webp uses ms for delays, gif uses centiseconds. /* webp uses ms for delays, gif uses centiseconds.
*/ */
vips_image_set_int( out, "gif-delay", vips_image_set_int( out, "gif-delay",
VIPS_RINT( read->delays[0] / 10.0 ) ); VIPS_RINT( read->delays[0] / 10.0 ) );
/* We need the alpha in an animation if:
* - any frame has transparent pixels
* - any frame doesn't fill the whole canvas.
*/
do {
if( iter.has_alpha ||
iter.width != read->canvas_width ||
iter.height != read->canvas_height ) {
read->alpha = TRUE;
break;
}
} while( WebPDemuxNextFrame( &iter ) );
} }
WebPDemuxReleaseIterator( &iter ); WebPDemuxReleaseIterator( &iter );