reinstate libnsgif loader

only 1/2 done
This commit is contained in:
John Cupitt 2019-08-15 14:30:25 +01:00
parent 6fa1b7cbf5
commit 1d77a57780
3 changed files with 494 additions and 10 deletions

View File

@ -1193,14 +1193,34 @@ if test x"$with_tiff" != x"no"; then
INCLUDES="$save_INCLUDES"
fi
# giflib
FIND_GIFLIB(
[with_giflib="yes (found by search)"
],
[AC_MSG_WARN([giflib not found; disabling direct GIF support])
with_giflib=no
]
)
# libnsgif
# search for libnsgif with pkg-config, see https://github.com/jcupitt/libnsgif
AC_ARG_WITH([libnsgif],
AS_HELP_STRING([--without-libnsgif],
[build without libnsgif (default: test)]))
if test x"$with_libnsgif" != x"no"; then
PKG_CHECK_MODULES(LIBNSGIF, libnsgif,
[AC_DEFINE(HAVE_LIBNSGIF,1,[define if you have libnsgif installed.])
with_libnsgif="yes"
with_giflib="no (using libnsgif)"
PACKAGES_USED="$PACKAGES_USED libnsgif"
],
[with_libnsgif="no"
]
)
fi
# giflib if no libnsgif
if test x"$with_libnsgif" != x"yes"; then
FIND_GIFLIB(
[with_giflib="yes (found by search)"
],
[AC_MSG_WARN([giflib not found; disabling direct GIF support])
with_giflib=no
]
)
fi
# look for PNG with pkg-config ... fall back to our tester
AC_ARG_WITH([png],
@ -1327,14 +1347,14 @@ fi
# Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS
# sort includes to get longer, more specific dirs first
# helps, for example, selecting graphicsmagick over imagemagick
VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $JPEG_CFLAGS $PNG_CFLAGS $IMAGEQUANT_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $PDFIUM_INCLUDES $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS $HEIF_CFLAGS
VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $JPEG_CFLAGS $PNG_CFLAGS $IMAGEQUANT_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $LIBNSGIF_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $PDFIUM_INCLUDES $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS $HEIF_CFLAGS
do
echo $i
done | sort -ru`
VIPS_CFLAGS=`echo $VIPS_CFLAGS`
VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS"
VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES $NIFTI_INCLUDES"
VIPS_LIBS="$ZLIB_LIBS $HEIF_LIBS $MAGICK_LIBS $PNG_LIBS $IMAGEQUANT_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $NIFTI_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
VIPS_LIBS="$ZLIB_LIBS $HEIF_LIBS $MAGICK_LIBS $PNG_LIBS $IMAGEQUANT_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $LIBNSGIF_LIBS $GIFLIB_LIBS $RSVG_LIBS $NIFTI_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
AC_SUBST(VIPS_LIBDIR)
@ -1434,6 +1454,7 @@ file import/export with libpng: $with_png
support 8bpp PNG quantisation: $with_imagequant
(requires libimagequant)
file import/export with libtiff: $with_tiff
file import/export with libnsgif: $with_libnsgif
file import/export with giflib: $with_giflib
file import/export with libjpeg: $with_jpeg
image pyramid export: $with_gsf

View File

@ -8,6 +8,7 @@ libforeign_la_SOURCES = \
niftisave.c \
quantise.c \
exif.c \
gifnsload.c \
gifload.c \
cairo.c \
pdfload.c \

462
libvips/foreign/gifnsload.c Normal file
View File

@ -0,0 +1,462 @@
/* load a GIF with libnsgif
*
* 6/10/18
* - from gifload.c
*/
/*
This file is part of VIPS.
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/*
#define VIPS_DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <vips/vips.h>
#include <vips/buf.h>
#include <vips/internal.h>
#include <vips/debug.h>
#ifdef HAVE_LIBNSGIF
#include <libnsgif/libnsgif.h>
typedef struct _VipsForeignLoadGif {
VipsForeignLoad parent_object;
/* Load this page (frame number).
*/
int page;
/* Load this many pages.
*/
int n;
/* The animation created by libnsgif.
*/
gif_animation *gif;
/* The data/size pair we pass to libnsgif.
*/
size_t size;
unsigned char *data;
} VipsForeignLoadGif;
typedef VipsForeignLoadClass VipsForeignLoadGifClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif,
VIPS_TYPE_FOREIGN_LOAD );
static const char *
vips_foreign_load_gif_errstr( gif_result result )
{
switch( result ) {
case GIF_WORKING:
return( _( "Working" ) );
case GIF_OK:
return( _( "OK" ) );
case GIF_INSUFFICIENT_FRAME_DATA:
return( _( "Insufficient data to complete frame" ) );
case GIF_FRAME_DATA_ERROR:
return( _( "GIF frame data error" ) );
case GIF_INSUFFICIENT_DATA:
return( _( "Insufficient data to do anything" ) );
case GIF_DATA_ERROR:
return( _( "GIF header data error" ) );
case GIF_INSUFFICIENT_MEMORY:
return( _( "Insuficient memory to process" ) );
case GIF_FRAME_NO_DISPLAY:
return( _( "No display" ) );
case GIF_END_OF_FRAME:
return( _( "At end of frame" ) );
default:
return( _( "Unknown error" ) );
}
}
static void
vips_foreign_load_gif_dispose( GObject *gobject )
{
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject;
VIPS_FREEF( gif_finalise, gif->gif );
G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )->
dispose( 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 );
}
static VipsForeignFlags
vips_foreign_load_gif_get_flags( VipsForeignLoad *load )
{
return( VIPS_FOREIGN_PARTIAL );
}
static gboolean
vips_foreign_load_gif_is_a_buffer( const void *buf, size_t len )
{
const guchar *str = (const guchar *) buf;
if( len >= 4 &&
str[0] == 'G' &&
str[1] == 'I' &&
str[2] == 'F' &&
str[3] == '8' )
return( 1 );
return( 0 );
}
static gboolean
vips_foreign_load_gif_is_a( const char *filename )
{
unsigned char buf[4];
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
vips_foreign_load_gif_is_a_buffer( buf, 4 ) )
return( 1 );
return( 0 );
}
static void *
vips_foreign_load_gif_bitmap_create( int width, int height )
{
/* ensure a stupidly large bitmap is not created */
return calloc(width * height, 4);
}
static void
vips_foreign_load_gif_bitmap_set_opaque( void *bitmap, bool opaque )
{
(void) opaque; /* unused */
(void) bitmap; /* unused */
g_assert(bitmap);
}
static bool
vips_foreign_load_gif_bitmap_test_opaque( void *bitmap )
{
(void) bitmap; /* unused */
g_assert(bitmap);
return false;
}
static unsigned char *
vips_foreign_load_gif_bitmap_get_buffer( void *bitmap )
{
g_assert(bitmap);
return bitmap;
}
static void
vips_foreign_load_gif_bitmap_destroy( void *bitmap )
{
g_assert(bitmap);
free(bitmap);
}
static void
vips_foreign_load_gif_bitmap_modified( void *bitmap )
{
(void) bitmap; /* unused */
g_assert(bitmap);
return;
}
static gif_bitmap_callback_vt vips_foreign_load_gif_bitmap_callbacks = {
vips_foreign_load_gif_bitmap_create,
vips_foreign_load_gif_bitmap_destroy,
vips_foreign_load_gif_bitmap_get_buffer,
vips_foreign_load_gif_bitmap_set_opaque,
vips_foreign_load_gif_bitmap_test_opaque,
vips_foreign_load_gif_bitmap_modified
};
static int
vips_foreign_load_gif_load( VipsForeignLoad *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 )) )
return( -1 );
gif_create( gif->gif, &vips_foreign_load_gif_bitmap_callbacks );
/* Decode entire GIF.
*
* FIXME ... add progressive decode.
*
* FIXME ... only decode as far as we need for the selected page
*
*/
do {
result = gif_initialise( gif->gif, gif->size, gif->data );
if( result != GIF_OK &&
result != GIF_WORKING )
return 1;
} while( result != GIF_OK );
/* Render from libnsgif memory areas into output image.
*/
return( 0 );
}
static void
vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_gif_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "gifload_base";
object_class->description = _( "load GIF with libnsgif" );
load_class->get_flags_filename =
vips_foreign_load_gif_get_flags_filename;
load_class->get_flags = vips_foreign_load_gif_get_flags;
VIPS_ARG_INT( class, "page", 10,
_( "Page" ),
_( "Load this page from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGif, page ),
0, 100000, 0 );
VIPS_ARG_INT( class, "n", 6,
_( "n" ),
_( "Load this many pages" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGif, n ),
-1, 100000, 1 );
}
static void
vips_foreign_load_gif_init( VipsForeignLoadGif *gif )
{
gif->n = 1;
}
typedef struct _VipsForeignLoadGifFile {
VipsForeignLoadGif parent_object;
/* Filename for load.
*/
char *filename;
} VipsForeignLoadGifFile;
typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass;
G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file,
vips_foreign_load_gif_get_type() );
static int
vips_foreign_load_gif_file_header( VipsForeignLoad *load )
{
VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load;
/* mmap the file.
*/
if( vips_rawload( filename, &t[0],
out->Xsize, out->Ysize, VIPS_IMAGE_SIZEOF_PEL( out ),
"offset", header_offset,
NULL ) ||
if( vips_foreign_load_gif_open( gif, file->filename ) )
return( -1 );
VIPS_SETSTR( load->out->filename, file->filename );
return( vips_foreign_load_gif_load( load ) );
}
static const char *vips_foreign_gif_suffs[] = {
".gif",
NULL
};
static void
vips_foreign_load_gif_file_class_init(
VipsForeignLoadGifFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "gifload";
object_class->description = _( "load GIF with libnsgif" );
foreign_class->suffs = vips_foreign_gif_suffs;
load_class->is_a = vips_foreign_load_gif_is_a;
load_class->header = vips_foreign_load_gif_file_header;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifFile, filename ),
NULL );
}
static void
vips_foreign_load_gif_file_init( VipsForeignLoadGifFile *file )
{
}
typedef struct _VipsForeignLoadGifBuffer {
VipsForeignLoadGif parent_object;
/* Load from a buffer.
*/
VipsArea *buf;
/* Current read point, bytes left in buffer.
*/
VipsPel *p;
size_t bytes_to_go;
} VipsForeignLoadGifBuffer;
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;
if( vips_foreign_load_gif_open_buffer( gif, vips_giflib_buffer_read ) )
return( -1 );
return( vips_foreign_load_gif_load( load ) );
}
static void
vips_foreign_load_gif_buffer_class_init(
VipsForeignLoadGifBufferClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "gifload_buffer";
object_class->description = _( "load GIF with libnsgif" );
load_class->is_a_buffer = vips_foreign_load_gif_is_a_buffer;
load_class->header = vips_foreign_load_gif_buffer_header;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifBuffer, buf ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer )
{
}
#endif /*HAVE_LIBNSGIF*/