gifload supports n and page-height

This commit is contained in:
John Cupitt 2016-11-26 15:07:12 +00:00
parent 577588c2d1
commit 0d9bf6a81e
4 changed files with 133 additions and 91 deletions

View File

@ -13,6 +13,7 @@
- add tiff multi-page read/write
- add VIPS_META_PAGE_HEIGHT metadata
- IM6/IM7 magickload supports page/n/page-height, all_frames deprecated
- gifload supports n/page-height
11/11/16 started 8.4.4
- fix crash in vips.exe arg parsing on Windows, thanks Yury

27
TODO
View File

@ -1,30 +1,3 @@
- all toilet roll loaders need to set "page-height"
magick, pdf, gif and tiff need to use the same page/n interface
magick6
deprecated all_frames, added n, added tests, added page-height
magick7
same
gifload has page, but no n
add an n param
add page-height
pdfload has page, n, allows n == -1, sets page-height
- not sure about utf8 error messages on win
- strange:

View File

@ -12,7 +12,7 @@
* 19/8/16
* - better transparency detection, thanks diegocsandrim
* 25/11/16
* - support @n
* - support @n, page-height
*/
/*
@ -428,6 +428,13 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
}
}
/* We need a line buffer to decompress to.
*/
if( !gif->line )
if( !(gif->line = VIPS_ARRAY( gif,
gif->file->SWidth, GifPixelType )) )
return( -1 );
if( file->Image.Interlace ) {
int i;
@ -482,44 +489,17 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
return( 0 );
}
/* Write the next page, if there is one, to @page. Set EOF if we hit the end of
* the file. @page must be a memory image of the right size.
*/
static int
vips_foreign_load_gif_to_memory( VipsForeignLoadGif *gif,
VipsImage *previous, VipsImage *out )
vips_foreign_load_gif_page( VipsForeignLoadGif *gif, VipsImage *out )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
GifRecordType record;
int n_pages;
vips_image_init_fields( out,
gif->file->SWidth, gif->file->SHeight,
4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
n_pages = 0;
/* We will have the whole GIF frame in memory, so we can render any
* area.
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL );
/* We need a line buffer to decompress to.
*/
gif->line = VIPS_ARRAY( gif, gif->file->SWidth, GifPixelType );
/* Turn out into a memory image which we then render the GIF frames
* into.
*/
if( vips_image_write_prepare( out ) )
return( -1 );
/* And init with the previous frame, if any.
*/
if( previous )
memcpy( VIPS_IMAGE_ADDR( out, 0, 0 ),
VIPS_IMAGE_ADDR( previous, 0, 0 ),
VIPS_IMAGE_SIZEOF_IMAGE( out ) );
/* Scan the GIF until we have enough to have completely rendered the
* next frame.
*/
do {
GifByteType *extension;
int ext_code;
@ -541,10 +521,10 @@ vips_foreign_load_gif_to_memory( VipsForeignLoadGif *gif,
if( vips_foreign_load_gif_render( gif, out ) )
return( -1 );
gif->current_page += 1;
n_pages += 1;
VIPS_DEBUG_MSG( "gifload: frame %d:\n",
gif->current_page );
VIPS_DEBUG_MSG( "gifload: page %d:\n",
gif->current_page + n_pages );
break;
@ -607,23 +587,41 @@ vips_foreign_load_gif_to_memory( VipsForeignLoadGif *gif,
default:
break;
}
} while( gif->current_page + frame_n <= gif->page &&
} while( n_pages < 1 &&
!gif->eof );
if( frame_n <= gif->page ) {
vips_error( class->nickname,
"%s", _( "too few frames in GIF file" ) );
return( -1 );
}
/* We've rendered to a memory image ... we can shut down the GIF
* reader now.
*/
vips_foreign_load_gif_close( gif );
gif->current_page += n_pages;
return( 0 );
}
static VipsImage *
vips_foreign_load_gif_new_page( VipsForeignLoadGif *gif )
{
VipsImage *out;
out = vips_image_new_memory();
vips_image_init_fields( out,
gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
/* We will have the whole GIF frame in memory, so we can render any
* area.
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL );
/* Turn out into a memory image which we then render the GIF frames
* into.
*/
if( vips_image_write_prepare( out ) ) {
g_object_unref( out );
return( NULL );
}
return( out );
}
static void
unref_array( GSList *list )
{
@ -635,52 +633,112 @@ unref_array( GSList *list )
* so we can't just allocate a large area.
*/
static int
vips_foreign_load_gif_frames( VipsForeignLoadGif *gif, VipsImage **out )
vips_foreign_load_gif_pages( VipsForeignLoadGif *gif, VipsImage **out )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
GSList *frames;
VipsImage *frame;
VipsImage *previous;
VipsImage **t;
GifRecordType record;
int n_frames;
int i;
frames = NULL;
previous = NULL;
do {
VipsImage *frame;
/* Accumulate any start stuff up to the first frame we need.
*/
if( !(frame = vips_foreign_load_gif_new_page( gif )) )
return( -1 );
do {
if( vips_foreign_load_gif_page( gif, frame ) ) {
g_object_unref( frame );
return( -1 );
}
} while( !gif->eof &&
gif->current_page <= gif->page );
if( vips_foreign_load_gif_to_memory( gif, previous, &frame ) ) {
if( gif->eof ) {
vips_error( class->nickname,
"%s", _( "too few frames in GIF file" ) );
g_object_unref( frame );
return( -1 );
}
frames = g_slist_append( frames, frame );
previous = frame;
while( gif->n == -1 ||
gif->current_page < gif->page + gif->n ) {
/* We might need a frame for this read to render to.
*/
if( !(frame = vips_foreign_load_gif_new_page( gif )) ) {
unref_array( frames );
return( -1 );
}
frames = g_slist_append( frames, frame );
previous = frame;
} while( (gif->n == -1 && !gif->eof) ||
(gif->current_page < gif->page + gif->n) );
/* And init with the previous frame, if any.
*/
if( previous )
memcpy( VIPS_IMAGE_ADDR( frame, 0, 0 ),
VIPS_IMAGE_ADDR( previous, 0, 0 ),
VIPS_IMAGE_SIZEOF_IMAGE( frame ) );
if( vips_foreign_load_gif_page( gif, frame ) ) {
g_object_unref( frame );
unref_array( frames );
return( -1 );
}
if( gif->eof ) {
/* Nope, didn't need the new frame.
*/
g_object_unref( frame );
break;
}
else {
frames = g_slist_append( frames, frame );
previous = frame;
}
}
n_frames = g_slist_length( frames );
if( gif->eof &&
gif->n != -1 &&
n_frames < gif->n ) {
unref_array( frames );
vips_error( class->nickname,
"%s", _( "too few frames in GIF file" ) );
return( -1 );
}
/* We've rendered to a set of memory images ... we can shut down the GIF
* reader now.
*/
vips_foreign_load_gif_close( gif );
n_frames = gif->current_page - gif->page;
if( !(t = VIPS_ARRAY( gif, n_frames, VipsImage * )) ) {
unref_array( frames );
return( -1 );
}
for( i = 0; i < n_frames; i++ )
t[i] = g_slist_nth( frames, i );
t[i] = (VipsImage *) g_slist_nth_data( frames, i );
if( vips_arrayjoin( t, out, n_frames, NULL ) ) {
if( vips_arrayjoin( t, out, n_frames,
"across", 1,
NULL ) ) {
unref_array( frames );
return( -1 );
}
unref_array( frames );
if( n_frames > 1 )
vips_image_set_int( *out, VIPS_META_PAGE_HEIGHT, frame->Ysize );
return( 0 );
}
@ -693,11 +751,9 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
VipsImage *im;
/* Render to a memory image.
*/
im = t[0] = vips_image_new_memory();
if( vips_foreign_load_gif_to_memory( gif, im ) )
if( vips_foreign_load_gif_pages( gif, &t[0] ) )
return( -1 );
im = t[0];
/* Depending on what we found, transform and write to load->real.
*/

View File

@ -560,6 +560,18 @@ class TestForeign(unittest.TestCase):
self.file_loader("gifload", self.gif_file, gif_valid)
self.buffer_loader("gifload_buffer", self.gif_file, gif_valid)
x1 = Vips.Image.new_from_file(self.gif_anim_file )
x2 = Vips.Image.new_from_file(self.gif_anim_file, n = 2 )
self.assertEqual(x2.height, 2 * x1.height)
page_height = x2.get_value("page-height")
self.assertEqual(page_height, x1.height)
x2 = Vips.Image.new_from_file(self.gif_anim_file, n = -1 )
self.assertEqual(x2.height, 5 * x1.height)
x2 = Vips.Image.new_from_file(self.gif_anim_file, page = 1, n = -1 )
self.assertEqual(x2.height, 4 * x1.height)
def test_svgload(self):
x = Vips.type_find("VipsForeign", "svgload")
if not x.is_instantiatable():