getting closer

This commit is contained in:
John Cupitt 2019-09-06 12:57:03 +01:00
parent b04299aaa2
commit 844c35f525
2 changed files with 231 additions and 54 deletions

View File

@ -1955,11 +1955,11 @@ vips_foreign_operation_init( void )
vips_foreign_load_svg_buffer_get_type();
#endif /*HAVE_RSVG*/
#ifdef HAVE_GIFLIB
#if defined(HAVE_GIFLIB) || defined(HAVE_LIBNSGIF)
vips_foreign_load_gif_get_type();
vips_foreign_load_gif_file_get_type();
vips_foreign_load_gif_buffer_get_type();
#endif /*HAVE_GIFLIB*/
#endif /*gif*/
#ifdef HAVE_GSF
vips_foreign_save_dz_file_get_type();

View File

@ -32,8 +32,8 @@
*/
/*
#define VIPS_DEBUG
*/
#define VIPS_DEBUG
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -68,12 +68,12 @@ typedef struct _VipsForeignLoadGif {
/* The animation created by libnsgif.
*/
gif_animation *gif;
gif_animation *anim;
/* The data/size pair we pass to libnsgif.
*/
size_t size;
unsigned char *data;
size_t size;
} VipsForeignLoadGif;
@ -123,7 +123,9 @@ vips_foreign_load_gif_dispose( GObject *gobject )
{
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject;
VIPS_FREEF( gif_finalise, gif->gif );
VIPS_DEBUG_MSG( "vips_foreign_load_gif_dispose:\n" );
VIPS_FREEF( gif_finalise, gif->anim );
G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )->
dispose( gobject );
@ -132,15 +134,13 @@ vips_foreign_load_gif_dispose( GObject *gobject )
static VipsForeignFlags
vips_foreign_load_gif_get_flags_filename( const char *filename )
{
/* We can render any part of the image on demand.
*/
return( VIPS_FOREIGN_PARTIAL );
return( VIPS_FOREIGN_SEQUENTIAL );
}
static VipsForeignFlags
vips_foreign_load_gif_get_flags( VipsForeignLoad *load )
{
return( VIPS_FOREIGN_PARTIAL );
return( VIPS_FOREIGN_SEQUENTIAL );
}
static gboolean
@ -148,6 +148,8 @@ vips_foreign_load_gif_is_a_buffer( const void *buf, size_t len )
{
const guchar *str = (const guchar *) buf;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_is_a_buffer:\n" );
if( len >= 4 &&
str[0] == 'G' &&
str[1] == 'I' &&
@ -173,7 +175,8 @@ vips_foreign_load_gif_is_a( const char *filename )
static void *
vips_foreign_load_gif_bitmap_create( int width, int height )
{
/* ensure a stupidly large bitmap is not created */
/* ensure a stupidly large bitmap is not created
*/
return calloc(width * height, 4);
}
@ -225,19 +228,94 @@ static gif_bitmap_callback_vt vips_foreign_load_gif_bitmap_callbacks = {
vips_foreign_load_gif_bitmap_modified
};
static int
vips_foreign_load_gif_load( VipsForeignLoad *load )
static void
print_frame( gif_frame *frame )
{
printf( "frame:\n" );
printf( " display = %d\n", frame->display );
printf( " frame_delay = %d\n", frame->frame_delay );
printf( " virgin = %d\n", frame->virgin );
printf( " opaque = %d\n", frame->opaque );
printf( " redraw_required = %d\n", frame->redraw_required );
printf( " disposal_method = %d\n", frame->disposal_method );
printf( " transparency = %d\n", frame->transparency );
printf( " transparency_index = %d\n", frame->transparency_index );
printf( " redraw_x = %d\n", frame->redraw_x );
printf( " redraw_y = %d\n", frame->redraw_y );
printf( " redraw_width = %d\n", frame->redraw_width );
printf( " redraw_height = %d\n", frame->redraw_height );
}
static void
print_animation( gif_animation *anim )
{
int i;
printf( "animation:\n" );
printf( " width = %d\n", anim->width );
printf( " height = %d\n", anim->height );
printf( " frame_count = %d\n", anim->frame_count );
printf( " frame_count_partial = %d\n", anim->frame_count_partial );
printf( " decoded_frame = %d\n", anim->decoded_frame );
printf( " frame_image = %p\n", anim->frame_image );
printf( " loop_count = %d\n", anim->loop_count );
printf( " frame_holders = %d\n", anim->frame_holders );
printf( " background_index = %d\n", anim->background_index );
printf( " colour_table_size = %d\n", anim->colour_table_size );
printf( " global_colours = %d\n", anim->global_colours );
printf( " global_colour_table = %p\n", anim->global_colour_table );
printf( " local_colour_table = %p\n", anim->local_colour_table );
for( i = 0; i < anim->frame_count; i++ ) {
printf( "%d ", i );
print_frame( &anim->frames[i] );
}
}
/* Scan the GIF as quickly as we can and extract transparency, bands, pages,
* etc.
*
* Don't flag any errors unless we have to: we want to work for corrupt or
* malformed GIFs.
*/
static int
vips_foreign_load_gif_scan_header( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( load ), 4 );
gif_result result;
VipsImage *im;
if( !(gif->gif = VIPS_NEW( load, gif_animation )) )
VIPS_DEBUG_MSG( "vips_foreign_load_gif_scan_header:\n" );
if( !(gif->anim = VIPS_NEW( load, gif_animation )) )
return( -1 );
gif_create( gif->gif, &vips_foreign_load_gif_bitmap_callbacks );
gif_create( gif->anim, &vips_foreign_load_gif_bitmap_callbacks );
print_animation( gif->anim );
result = gif_initialise( gif->anim, gif->size, gif->data );
VIPS_DEBUG_MSG( " gif_initialise() = %d\n", result );
print_animation( gif->anim );
if( result != GIF_OK &&
result != GIF_WORKING ) {
vips_error( class->nickname, "%s",
vips_foreign_load_gif_errstr( result ) );
return( -1 );
}
return( 0 );
}
static int
vips_foreign_load_gif_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsRect *r = &or->valid;
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a;
gif_result result;
/* Decode entire GIF.
*
@ -247,11 +325,17 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
*
*/
do {
result = gif_initialise( gif->gif, gif->size, gif->data );
if( result != GIF_OK &&
result != GIF_WORKING )
return 1;
} while( result != GIF_OK );
result = gif_decode_frame( gif->anim, frame );
VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n",
frame, result );
if( result != GIF_OK ) {
vips_error( class->nickname, "%s",
vips_foreign_load_gif_errstr( result ) );
return( -1 );
}
print_animation( gif->anim );
} while( gif->anim->decoded_frame < gif->anim->frame_count - 1 );
/* Render from libnsgif memory areas into output image.
*/
@ -259,6 +343,65 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
return( 0 );
}
static int
vips_foreign_load_gif_load( VipsForeignLoad *load )
{
VipsForeignLoadGifClass *class =
(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( load ), 4 );
if( class->open( gif ) )
return( -1 );
VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" );
/* Make the memory image we accumulate pixels in. We always accumulate
* to RGBA, then trim down to whatever the output image needs on
* _generate.
*/
gif->frame = vips_image_new_memory();
vips_image_init_fields( gif->frame,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( gif->frame ) )
return( -1 );
/* A copy of the previous state of the frame, in case we have to
* process a DISPOSE_PREVIOUS.
*/
gif->previous = vips_image_new_memory();
vips_image_init_fields( gif->previous,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
if( vips_image_write_prepare( gif->previous ) )
return( -1 );
/* Make the output pipeline.
*/
t[0] = vips_image_new();
if( vips_foreign_load_gif_set_header( gif, t[0] ) )
return( -1 );
/* Close input immediately at end of read.
*/
g_signal_connect( t[0], "minimise",
G_CALLBACK( vips_foreign_load_gif_minimise ), gif );
/* Strips 8 pixels high to avoid too many tiny regions.
*/
if( vips_image_generate( t[0],
NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) ||
vips_sequential( t[0], &t[1],
"tile_height", VIPS__FATSTRIP_HEIGHT,
NULL ) ||
vips_image_write( t[1], load->real ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
{
@ -306,6 +449,12 @@ typedef struct _VipsForeignLoadGifFile {
*/
char *filename;
/* mmap here.
*/
int fd;
void *base;
gint64 length;
} VipsForeignLoadGifFile;
typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass;
@ -313,26 +462,73 @@ typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass;
G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file,
vips_foreign_load_gif_get_type() );
static void
vips_foreign_load_gif_file_dispose( GObject *gobject )
{
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject;
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_dispose:\n" );
G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )->
dispose( gobject );
if( file->fd >= 0 ) {
(void) g_close( file->fd, NULL );
file->fd = -1;
}
if( file->base ) {
vips__munmap( file->base, file->length );
file->base = NULL;
file->length = 0;
}
}
static int
vips_foreign_load_gif_file_header( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load;
/* mmap the file.
struct stat st;
mode_t m;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_header:\n" );
/* Open and mmap the file.
*/
if( vips_rawload( filename, &t[0],
out->Xsize, out->Ysize, VIPS_IMAGE_SIZEOF_PEL( out ),
"offset", header_offset,
NULL ) ||
file->fd = vips__open_read( file->filename );
if( file->fd == -1 ) {
vips_error_system( errno, class->nickname,
_( "unable to open file \"%s\" for reading" ),
file->filename );
return( -1 );
}
if( (file->length = vips_file_length( file->fd )) == -1 )
return( -1 );
if( vips_foreign_load_gif_open( gif, file->filename ) )
if( fstat( file->fd, &st ) == -1 ) {
vips_error( class->nickname,
"%s", _( "unable to get file status" ) );
return( -1 );
}
m = (mode_t) st.st_mode;
if( !S_ISREG( m ) ) {
vips_error( class->nickname,
"%s", _( "not a regular file" ) );
return( -1 );
}
if( !(file->base = vips__mmap( file->fd, 0, file->length, 0 )) )
return( -1 );
VIPS_SETSTR( load->out->filename, file->filename );
gif->data = file->base;
gif->size = file->length;
return( vips_foreign_load_gif_load( load ) );
return( vips_foreign_load_gif_scan_header( load ) );
}
static const char *vips_foreign_gif_suffs[] = {
@ -349,6 +545,7 @@ vips_foreign_load_gif_file_class_init(
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_gif_file_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
@ -372,6 +569,7 @@ vips_foreign_load_gif_file_class_init(
static void
vips_foreign_load_gif_file_init( VipsForeignLoadGifFile *file )
{
file->fd = -1;
}
typedef struct _VipsForeignLoadGifBuffer {
@ -393,39 +591,18 @@ typedef VipsForeignLoadGifClass VipsForeignLoadGifBufferClass;
G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer,
vips_foreign_load_gif_get_type() );
/* Callback from the gif loader.
*
* Read up to len bytes into buffer, return number of bytes read, 0 for EOF.
*/
static int
vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n )
{
VipsForeignLoadGifBuffer *buffer =
(VipsForeignLoadGifBuffer *) file->UserData;
size_t will_read = VIPS_MIN( n, buffer->bytes_to_go );
memcpy( buf, buffer->p, will_read );
buffer->p += will_read;
buffer->bytes_to_go -= will_read;
return( will_read );
}
static int
vips_foreign_load_gif_buffer_header( VipsForeignLoad *load )
{
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) load;
/* Init the read point.
*/
buffer->p = buffer->buf->data;
buffer->bytes_to_go = buffer->buf->length;
VIPS_DEBUG_MSG( "vips_foreign_load_gif_buffer_header:\n" );
if( vips_foreign_load_gif_open_buffer( gif, vips_giflib_buffer_read ) )
return( -1 );
gif->data = buffer->buf->data;
gif->size = buffer->buf->length;
return( vips_foreign_load_gif_load( load ) );
return( vips_foreign_load_gif_scan_header( load ) );
}
static void