feat: support frame delays

This commit is contained in:
Tomáš Szabo 2019-06-21 15:06:22 +02:00
parent ea2367e24c
commit ae50136845
No known key found for this signature in database
GPG Key ID: 96F1E84929851783
5 changed files with 89 additions and 48 deletions

View File

@ -132,12 +132,9 @@ typedef struct _VipsForeignLoadGif {
gboolean has_transparency;
gboolean has_colour;
/* Delay in 1/100ths of a second. We only track a single delay
* value for the whole file, and we report the first delay we see. Some
* GIFs have a long delay on the final frame.
/* Delays between frames (in miliseconds).
*/
gboolean has_delay;
int delay;
int *delays;
/* Number of times to loop the animation.
*/
@ -324,7 +321,8 @@ vips_foreign_load_gif_dispose( GObject *gobject )
VIPS_UNREF( gif->frame );
VIPS_UNREF( gif->previous );
VIPS_FREE( gif->comment );
VIPS_FREE( gif->line )
VIPS_FREE( gif->line );
VIPS_FREE( gif->delays );
G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )->
dispose( gobject );
@ -521,11 +519,10 @@ vips_foreign_load_gif_scan_extension( VipsForeignLoadGif *gif )
gif->has_transparency = TRUE;
}
if( !gif->has_delay ) {
VIPS_DEBUG_MSG( "gifload: has delay\n" );
gif->has_delay = TRUE;
gif->delay = extension[2] | (extension[3] << 8);
}
if( gif->n_pages % 64 == 0 ) {
gif->delays = (int *) g_realloc( gif->delays, (gif->n_pages + 64) * sizeof(int) );
}
gif->delays[gif->n_pages] = (extension[2] | (extension[3] << 8)) * 10;
while( extension != NULL )
if( vips_foreign_load_gif_ext_next( gif,
@ -575,20 +572,18 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image )
vips_image_set_int( image,
VIPS_META_PAGE_HEIGHT, gif->file->SHeight );
vips_image_set_int( image, VIPS_META_N_PAGES, gif->n_pages );
vips_image_set_int( image, "gif-delay", gif->delay );
vips_image_set_int( image, "gif-loop", gif->loop );
if ( gif->delays ) {
vips_image_set_int( image, "gif-delay", VIPS_RINT( gif->delays[0] / 10.0 ) );
vips_image_set_array_int( image, "delay", gif->delays, gif->n_pages );
} else {
vips_image_set_int( image, "gif-delay", 4 );
}
if( gif->comment )
vips_image_set_string( image, "gif-comment", gif->comment );
{
int array[10];
int i;
for( i = 0; i < 10; i++ )
array[i] = i;
vips_image_set_array_int( image, "delay", array, 10 );
}
return( 0 );
}
@ -1126,7 +1121,7 @@ vips_foreign_load_gif_init( VipsForeignLoadGif *gif )
{
gif->n = 1;
gif->transparency = -1;
gif->delay = 4;
gif->delays = NULL;
gif->loop = 0;
gif->comment = NULL;
gif->dispose = 0;

View File

@ -109,6 +109,8 @@ vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
Image *image;
int number;
int *numbers;
int numbers_length;
const char *str;
g_assert( !magick->current_image );
@ -139,9 +141,12 @@ vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
im->Xsize, magick->page_height, magick->exception ) )
return( -1 );
if( vips_image_get_typeof( im, "gif-delay" ) &&
!vips_image_get_int( im, "gif-delay", &number ) )
image->delay = (size_t) number;
if( vips_image_get_typeof( im, "delay" ) &&
!vips_image_get_array_int( im, "delay", &numbers, &numbers_length ) ) {
int page_index = magick->position.top / magick->page_height;
if( page_index < numbers_length )
image->delay = (size_t) VIPS_RINT( numbers[page_index] / 10.0 );
}
/* ImageMagick uses iterations like this (at least in gif save):
* 0 - set 0 loops (infinite)

View File

@ -289,6 +289,29 @@ get_int( VipsImage *image, const char *field, int default_value )
return( default_value );
}
/* Get an image field as an array of ints.
*/
static int*
get_array_int( VipsImage *image, const char *field, int* n )
{
int *value;
if( vips_image_get_typeof( image, field ) &&
!vips_image_get_array_int( image, field, &value, n ) )
return( value );
return( NULL );
}
/* Returns a delay on a given index or the default delay.
*/
static int
extract_delay( int index, int *delays, int delays_length, int default_delay )
{
if( delays == NULL || index > delays_length ) return( default_delay );
return( delays[index] );
}
/* Write a set of animated frames into write->memory_writer.
*/
static int
@ -297,7 +320,9 @@ write_webp_anim( VipsWebPWrite *write, VipsImage *image, int page_height )
/* 100ms is the webp default. gif-delay is in centiseconds (the GIF
* standard).
*/
const int delay = 10 * get_int( image, "gif-delay", 10 );
const int default_delay = 10 * get_int( image, "gif-delay", 10 );
int delays_length;
const int *delays = get_array_int( image, "delay", &delays_length );
WebPAnimEncoderOptions anim_config;
WebPData webp_data;
@ -348,9 +373,19 @@ write_webp_anim( VipsWebPWrite *write, VipsImage *image, int page_height )
WebPPictureFree( &pic );
timestamp_ms += delay;
int page_index = top / page_height;
timestamp_ms += extract_delay( page_index, delays, delays_length, default_delay );
}
/* Closes encoder and add last frame delay.
*/
if( !WebPAnimEncoderAdd( write->enc,
NULL, timestamp_ms, NULL ) ) {
vips_error( "vips2webp",
"%s", _( "anim close error" ) );
return( -1 );
}
if( !WebPAnimEncoderAssemble( write->enc, &webp_data ) ) {
vips_error( "vips2webp",
"%s", _( "anim build error" ) );

View File

@ -115,9 +115,9 @@ typedef struct {
*/
int frame_count;
/* Delay between frames. We don't let this change between frames.
/* Delays between frames (in miliseconds).
*/
int delay;
int *delays;
/* If we are opening a file object, the fd.
*/
@ -376,7 +376,7 @@ read_new( const char *filename, const void *data, size_t length,
read->page = page;
read->n = n;
read->scale = scale;
read->delay = 100;
read->delays = NULL;
read->fd = 0;
read->demux = NULL;
read->frame = NULL;
@ -470,21 +470,32 @@ read_header( Read *read, VipsImage *out )
vips_image_set_int( out,
VIPS_META_PAGE_HEIGHT, read->frame_height );
/* We must get the first frame to get the delay. Frames number
* from 1.
*/
if( WebPDemuxGetFrame( read->demux, 1, &iter ) ) {
read->delay = iter.duration;
if ( read->frame_count > 1) {
read->delays = (int *) g_malloc( read->frame_count * sizeof(int) );
for( int i = 0; i < read->frame_count; i++ ) {
if( WebPDemuxGetFrame( read->demux, i + 1, &iter ) ) {
read->delays[i] = iter.duration;
} else {
read->delays[i] = 0;
}
}
#ifdef DEBUG
printf( "webp2vips: duration = %d\n", read->delay );
for( int i = 0; i < read->frame_count; i++ ) {
printf( "webp2vips: frame = %d; duration = %d\n", i + 1, read->delays[i] );
}
#endif /*DEBUG*/
/* webp uses ms for delays, gif uses centiseconds.
*/
vips_image_set_int( out, "gif-delay",
VIPS_RINT( read->delay / 10.0 ) );
}
vips_image_set_array_int( out, "delay", read->delays, read->frame_count );
g_free( read->delays );
/* webp uses ms for delays, gif uses centiseconds.
*/
vips_image_set_int( out, "gif-delay",
VIPS_RINT( read->delays[0] / 10.0 ) );
}
WebPDemuxReleaseIterator( &iter );
if( read->n == -1 )
@ -688,11 +699,6 @@ read_next_frame( Read *read )
printf( "don't blend\n" );
#endif /*DEBUG*/
if( read->frame_count > 1 &&
read->iter.duration != read->delay )
g_warning( "webp2vips: "
"not all frames have equal duration" );
if( !(frame = read_frame( read,
area.width, area.height,
read->iter.fragment.bytes, read->iter.fragment.size )) )

View File

@ -466,8 +466,8 @@ vips_foreign_save_webp_mime_init( VipsForeignSaveWebpMime *mime )
* frames between frames. Setting 0 means no keyframes. By default, keyframes
* are disabled.
*
* Use the metadata items `gif-loop` and `gif-delay` to set the number of
* loops for the animation and the frame delay.
* Use the metadata items `gif-loop` and `delay` to set the number of
* loops for the animation and the frame delays.
*
* The writer will attach ICC, EXIF and XMP metadata, unless @strip is set to
* %TRUE.