sync fork
This commit is contained in:
commit
166aae1440
@ -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
|
||||||
|
@ -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().
|
||||||
*
|
*
|
||||||
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
@ -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 );
|
||||||
|
Loading…
Reference in New Issue
Block a user