Merge branch 'master' into add-magick7
This commit is contained in:
commit
71fb52afa0
@ -29,6 +29,7 @@
|
|||||||
- better upsizing with vips_resize()
|
- better upsizing with vips_resize()
|
||||||
- add imagemagick v7 support, thanks sachinwalia2k8
|
- add imagemagick v7 support, thanks sachinwalia2k8
|
||||||
- added vips_worley(), vips_perlin() noise generators
|
- added vips_worley(), vips_perlin() noise generators
|
||||||
|
- gif loader can write 1, 2, 3, or 4 bands depending on file contents
|
||||||
|
|
||||||
18/5/16 started 8.3.2
|
18/5/16 started 8.3.2
|
||||||
- more robust vips image reading
|
- more robust vips image reading
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
* - from svgload.c
|
* - from svgload.c
|
||||||
* 25/4/16
|
* 25/4/16
|
||||||
* - add giflib5 support
|
* - add giflib5 support
|
||||||
|
* 26/7/16
|
||||||
|
* - transparency was wrong if there was no EXTENSION_RECORD
|
||||||
|
* - write 1, 2, 3, or 4 bands depending on file contents
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -86,6 +89,14 @@ typedef struct _VipsForeignLoadGif {
|
|||||||
*/
|
*/
|
||||||
GifPixelType *line;
|
GifPixelType *line;
|
||||||
|
|
||||||
|
/* We decompress the whole thing to a huge RGBA memory image, and
|
||||||
|
* as we render, watch for bands and transparency. At the end of
|
||||||
|
* loading, we copy 1 or 3 bands, with or without transparency to
|
||||||
|
* output.
|
||||||
|
*/
|
||||||
|
gboolean has_transparency;
|
||||||
|
gboolean has_colour;
|
||||||
|
|
||||||
} VipsForeignLoadGif;
|
} VipsForeignLoadGif;
|
||||||
|
|
||||||
typedef VipsForeignLoadClass VipsForeignLoadGifClass;
|
typedef VipsForeignLoadClass VipsForeignLoadGifClass;
|
||||||
@ -302,39 +313,11 @@ vips_foreign_load_gif_is_a( const char *filename )
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
vips_foreign_load_gif_parse( VipsForeignLoadGif *gif,
|
|
||||||
VipsImage *out )
|
|
||||||
{
|
|
||||||
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 );
|
|
||||||
|
|
||||||
/* We need a line buffer to decompress to.
|
|
||||||
*/
|
|
||||||
gif->line = VIPS_ARRAY( gif, gif->file->SWidth, GifPixelType );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
vips_foreign_load_gif_header( VipsForeignLoad *load )
|
|
||||||
{
|
|
||||||
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
|
|
||||||
|
|
||||||
vips_foreign_load_gif_parse( gif, load->out );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
|
vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
|
||||||
int width, VipsPel * restrict q, VipsPel * restrict p )
|
int width, VipsPel * restrict q, VipsPel * restrict p )
|
||||||
{
|
{
|
||||||
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
|
||||||
ColorMapObject *map = gif->file->Image.ColorMap ?
|
ColorMapObject *map = gif->file->Image.ColorMap ?
|
||||||
gif->file->Image.ColorMap : gif->file->SColorMap;
|
gif->file->Image.ColorMap : gif->file->SColorMap;
|
||||||
|
|
||||||
@ -343,8 +326,13 @@ vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
|
|||||||
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 >= map->ColorCount ) {
|
||||||
v < map->ColorCount ) {
|
vips_warn( class->nickname,
|
||||||
|
"%s", _( "pixel value out of range" ) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( v != gif->transparency ) {
|
||||||
q[0] = map->Colors[v].Red;
|
q[0] = map->Colors[v].Red;
|
||||||
q[1] = map->Colors[v].Green;
|
q[1] = map->Colors[v].Green;
|
||||||
q[2] = map->Colors[v].Blue;
|
q[2] = map->Colors[v].Blue;
|
||||||
@ -370,6 +358,8 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||||||
{
|
{
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
|
||||||
GifFileType *file = gif->file;
|
GifFileType *file = gif->file;
|
||||||
|
ColorMapObject *map = file->Image.ColorMap ?
|
||||||
|
file->Image.ColorMap : file->SColorMap;
|
||||||
|
|
||||||
/* Check that the frame lies within our image.
|
/* Check that the frame lies within our image.
|
||||||
*/
|
*/
|
||||||
@ -382,10 +372,27 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if we have a non-greyscale colourmap for this frame.
|
||||||
|
*/
|
||||||
|
if( !gif->has_colour ) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0; i < map->ColorCount; i++ )
|
||||||
|
if( map->Colors[i].Red != map->Colors[i].Green ||
|
||||||
|
map->Colors[i].Green != map->Colors[i].Blue ) {
|
||||||
|
VIPS_DEBUG_MSG( "gifload: not mono\n" );
|
||||||
|
gif->has_colour = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( file->Image.Interlace ) {
|
if( file->Image.Interlace ) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "gifload: interlaced frame\n" );
|
VIPS_DEBUG_MSG( "gifload: interlaced frame of "
|
||||||
|
"%d x %d pixels at %d x %d\n",
|
||||||
|
file->Image.Width, file->Image.Height,
|
||||||
|
file->Image.Left, file->Image.Top );
|
||||||
|
|
||||||
for( i = 0; i < 4; i++ ) {
|
for( i = 0; i < 4; i++ ) {
|
||||||
int y;
|
int y;
|
||||||
@ -410,6 +417,11 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||||||
else {
|
else {
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "gifload: non-interlaced frame of "
|
||||||
|
"%d x %d pixels at %d x %d\n",
|
||||||
|
file->Image.Width, file->Image.Height,
|
||||||
|
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( out,
|
VipsPel *q = VIPS_IMAGE_ADDR( out,
|
||||||
file->Image.Left, file->Image.Top + y );
|
file->Image.Left, file->Image.Top + y );
|
||||||
@ -429,20 +441,31 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out )
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vips_foreign_load_gif_load( VipsForeignLoad *load )
|
vips_foreign_load_gif_to_memory( VipsForeignLoadGif *gif, VipsImage *out )
|
||||||
{
|
{
|
||||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
|
||||||
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
|
|
||||||
|
|
||||||
int frame_n;
|
int frame_n;
|
||||||
GifRecordType record;
|
GifRecordType record;
|
||||||
|
|
||||||
vips_foreign_load_gif_parse( gif, load->real );
|
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 );
|
||||||
|
|
||||||
|
/* 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
|
/* Turn out into a memory image which we then render the GIF frames
|
||||||
* into.
|
* into.
|
||||||
*/
|
*/
|
||||||
if( vips_image_write_prepare( load->real ) )
|
if( vips_image_write_prepare( out ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* Scan the GIF until we have enough to have completely rendered the
|
/* Scan the GIF until we have enough to have completely rendered the
|
||||||
@ -467,7 +490,7 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( vips_foreign_load_gif_render( gif, load->real ) )
|
if( vips_foreign_load_gif_render( gif, out ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
frame_n += 1;
|
frame_n += 1;
|
||||||
@ -495,6 +518,7 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||||||
* transparency.
|
* transparency.
|
||||||
*/
|
*/
|
||||||
gif->transparency = extension[4];
|
gif->transparency = extension[4];
|
||||||
|
gif->has_transparency = TRUE;
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "gifload: "
|
VIPS_DEBUG_MSG( "gifload: "
|
||||||
"seen transparency %d\n",
|
"seen transparency %d\n",
|
||||||
@ -549,6 +573,62 @@ vips_foreign_load_gif_load( VipsForeignLoad *load )
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_gif_load( VipsForeignLoad *load )
|
||||||
|
{
|
||||||
|
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
|
||||||
|
VipsImage **t = (VipsImage **)
|
||||||
|
vips_object_local_array( VIPS_OBJECT( load ), 4 );
|
||||||
|
|
||||||
|
VipsImage *im;
|
||||||
|
|
||||||
|
/* Render to a memory image.
|
||||||
|
*/
|
||||||
|
im = t[0] = vips_image_new_memory();
|
||||||
|
if( vips_foreign_load_gif_to_memory( gif, im ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Depending on what we found, transform and write to load->real.
|
||||||
|
*/
|
||||||
|
if( gif->has_colour &&
|
||||||
|
gif->has_transparency ) {
|
||||||
|
/* Nothing to do.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else if( gif->has_colour ) {
|
||||||
|
/* RGB.
|
||||||
|
*/
|
||||||
|
if( vips_extract_band( im, &t[1], 0,
|
||||||
|
"n", 3,
|
||||||
|
NULL ) )
|
||||||
|
return( -1 );
|
||||||
|
im = t[1];
|
||||||
|
}
|
||||||
|
else if( gif->has_transparency ) {
|
||||||
|
/* GA.
|
||||||
|
*/
|
||||||
|
if( vips_extract_band( im, &t[1], 0, NULL ) ||
|
||||||
|
vips_extract_band( im, &t[2], 3, NULL ) ||
|
||||||
|
vips_bandjoin2( t[1], t[2], &t[3], NULL ) )
|
||||||
|
return( -1 );
|
||||||
|
im = t[3];
|
||||||
|
im->Type = VIPS_INTERPRETATION_B_W;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* G.
|
||||||
|
*/
|
||||||
|
if( vips_extract_band( im, &t[1], 0, NULL ) )
|
||||||
|
return( -1 );
|
||||||
|
im = t[1];
|
||||||
|
im->Type = VIPS_INTERPRETATION_B_W;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( vips_image_write( im, load->out ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
|
vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
|
||||||
{
|
{
|
||||||
@ -566,7 +646,6 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
|
|||||||
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;
|
||||||
load_class->load = vips_foreign_load_gif_load;
|
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "page", 10,
|
VIPS_ARG_INT( class, "page", 10,
|
||||||
_( "Page" ),
|
_( "Page" ),
|
||||||
@ -580,6 +659,7 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
|
|||||||
static void
|
static void
|
||||||
vips_foreign_load_gif_init( VipsForeignLoadGif *gif )
|
vips_foreign_load_gif_init( VipsForeignLoadGif *gif )
|
||||||
{
|
{
|
||||||
|
gif->transparency = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct _VipsForeignLoadGifFile {
|
typedef struct _VipsForeignLoadGifFile {
|
||||||
@ -607,7 +687,7 @@ vips_foreign_load_gif_file_header( VipsForeignLoad *load )
|
|||||||
|
|
||||||
VIPS_SETSTR( load->out->filename, file->filename );
|
VIPS_SETSTR( load->out->filename, file->filename );
|
||||||
|
|
||||||
return( vips_foreign_load_gif_header( load ) );
|
return( vips_foreign_load_gif_load( load ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *vips_foreign_gif_suffs[] = {
|
static const char *vips_foreign_gif_suffs[] = {
|
||||||
@ -702,7 +782,7 @@ vips_foreign_load_gif_buffer_header( VipsForeignLoad *load )
|
|||||||
vips_foreign_load_gif_buffer_read ) )
|
vips_foreign_load_gif_buffer_read ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
return( vips_foreign_load_gif_header( load ) );
|
return( vips_foreign_load_gif_load( load ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -752,8 +832,8 @@ vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer )
|
|||||||
*
|
*
|
||||||
* Use @page to set page number (frame number) to read.
|
* Use @page to set page number (frame number) to read.
|
||||||
*
|
*
|
||||||
* The whole GIF is parsed and read into memory on header access, the whole
|
* The whole GIF is rendered into memory on header access. The output image
|
||||||
* GIF is rendered on first pixel access.
|
* 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().
|
||||||
*
|
*
|
||||||
|
@ -58,9 +58,7 @@ class TestForeign(unittest.TestCase):
|
|||||||
self.cmyk = self.cmyk.copy(interpretation = Vips.Interpretation.CMYK)
|
self.cmyk = self.cmyk.copy(interpretation = Vips.Interpretation.CMYK)
|
||||||
|
|
||||||
im = Vips.Image.new_from_file(self.gif_file)
|
im = Vips.Image.new_from_file(self.gif_file)
|
||||||
# some libMagick will load this mono image as RGB, some as mono ... test
|
self.onebit = im > 128
|
||||||
# band 0 to be safe
|
|
||||||
self.onebit = im[0] > 128
|
|
||||||
|
|
||||||
# we have test files for formats which have a clear standard
|
# we have test files for formats which have a clear standard
|
||||||
def file_loader(self, loader, test_file, validate):
|
def file_loader(self, loader, test_file, validate):
|
||||||
@ -470,10 +468,10 @@ class TestForeign(unittest.TestCase):
|
|||||||
|
|
||||||
def gif_valid(self, im):
|
def gif_valid(self, im):
|
||||||
a = im(10, 10)
|
a = im(10, 10)
|
||||||
self.assertAlmostEqualObjects(a, [33, 33, 33, 255])
|
self.assertAlmostEqualObjects(a, [33])
|
||||||
self.assertEqual(im.width, 159)
|
self.assertEqual(im.width, 159)
|
||||||
self.assertEqual(im.height, 203)
|
self.assertEqual(im.height, 203)
|
||||||
self.assertEqual(im.bands, 4)
|
self.assertEqual(im.bands, 1)
|
||||||
|
|
||||||
self.file_loader("gifload", self.gif_file, gif_valid)
|
self.file_loader("gifload", self.gif_file, gif_valid)
|
||||||
self.buffer_loader("gifload_buffer", self.gif_file, gif_valid)
|
self.buffer_loader("gifload_buffer", self.gif_file, gif_valid)
|
||||||
|
Loading…
Reference in New Issue
Block a user