move the nsgif source into the tree

and remove the old giflib loader
This commit is contained in:
John Cupitt 2021-02-28 12:20:05 +00:00
parent 799f720c13
commit b995a6d244
16 changed files with 2252 additions and 1928 deletions

View File

@ -116,6 +116,13 @@ Debug build:
make
make install
# Built-in loaders
libvips has a number of built-in loaders and savers. You can disable these if
you wish, for example:
./configure --prefix=/Users/john/vips --without-nsgif --without-ppm
# Dependencies
libvips has to have `libglib2.0-dev` and `libexpat1-dev`. Other dependencies
@ -128,14 +135,14 @@ libraries automatically. See `./configure --help` for a set of flags to
control library detection. Packages are generally found with `pkg-config`,
so make sure that is working.
Libraries like giflib and nifti do not use `pkg-config` so libvips will also
Libraries like nifti do not use `pkg-config` so libvips will also
look for them in the default path and in `$prefix`. If you have installed
your own versions of these libraries in a different location, libvips will
not see them. Use switches to libvips configure like:
./configure --prefix=/Users/john/vips \
--with-giflib-includes=/opt/local/include \
--with-giflib-libraries=/opt/local/lib
--with-nifti-includes=/opt/local/include \
--with-nifti-libraries=/opt/local/lib
or perhaps:
@ -151,11 +158,6 @@ The IJG JPEG library. Use the `-turbo` version if you can.
If available, libvips adds support for EXIF metadata in JPEG files.
### giflib
The standard gif loader. If this is not present, vips will try to load gifs
via imagemagick instead.
### librsvg
The usual SVG loader. If this is not present, vips will try to load SVGs

View File

@ -964,6 +964,16 @@ fi
# not external libraries, but have options to disable them, helps to
# reduce attack surface
AC_ARG_WITH([nsgif],
AS_HELP_STRING([--without-nsgif], [build without nsgif load (default: with)]))
if test x"$with_nsgif" != x"no"; then
AC_DEFINE(HAVE_NSGIF,1,[define to build nsgif load support.])
with_nsgif=yes
fi
AM_CONDITIONAL(ENABLE_NSGIF, [test x"$with_nsgif" = x"yes"])
AC_ARG_WITH([ppm],
AS_HELP_STRING([--without-ppm], [build without ppm (default: with)]))
@ -1074,35 +1084,6 @@ if test x"$with_tiff" != x"no"; then
INCLUDES="$save_INCLUDES"
fi
# 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 libspng first
# 0.6.1 uses "libspng.pc", git master libspng uses "spng.pc"
AC_ARG_WITH([libspng],
@ -1270,8 +1251,6 @@ VIPS_CFLAGS="$VIPS_CFLAGS \
$CFITSIO_CFLAGS \
$LIBWEBP_CFLAGS \
$LIBWEBPMUX_CFLAGS \
$LIBNSGIF_CFLAGS \
$GIFLIB_INCLUDES \
$RSVG_CFLAGS \
$PDFIUM_CFLAGS \
$POPPLER_CFLAGS \
@ -1301,8 +1280,6 @@ VIPS_LIBS="$ZLIB_LIBS \
$FFTW_LIBS \
$ORC_LIBS \
$LCMS_LIBS \
$LIBNSGIF_LIBS \
$GIFLIB_LIBS \
$RSVG_LIBS \
$NIFTI_LIBS \
$PDFIUM_LIBS \
@ -1318,7 +1295,7 @@ VIPS_LIBS="$ZLIB_LIBS \
# autoconf hates multi-line AC_SUBST so we have to have another copy of this
# thing
VIPS_CONFIG="native win32: $vips_os_win32, native OS X: $vips_os_darwin, open files in binary mode: $vips_binary_open, enable debug: $enable_debug, enable deprecated library components: $enable_deprecated, enable docs with gtkdoc: $enable_gtk_doc, gobject introspection: $found_introspection, enable radiance support: $with_radiance, enable analyze support: $with_analyze, enable PPM support: $with_ppm, generate C++ docs: $with_doxygen, use fftw3 for FFT: $with_fftw, Magick package: $with_magickpackage, Magick API version: $magick_version, load with libMagick: $enable_magickload, save with libMagick: $enable_magicksave, accelerate loops with orc: $with_orc, ICC profile support with lcms: $with_lcms, file import with niftiio: $with_nifti, file import with libheif: $with_heif, file import with OpenEXR: $with_OpenEXR, file import with OpenSlide: $with_openslide, file import with matio: $with_matio, PDF import with PDFium: $with_pdfium, PDF import with poppler-glib: $with_poppler, SVG import with librsvg-2.0: $with_rsvg, zlib: $with_zlib, file import with cfitsio: $with_cfitsio, file import/export with libwebp: $with_libwebp, text rendering with pangoft2: $with_pangoft2, file import/export with libspng: $with_libspng, file import/export with libpng: $with_png, support 8bpp PNG quantisation: $with_imagequant, file import/export with libtiff: $with_tiff, file import/export with giflib: $with_giflib, file import/export with libnsgif: $with_libnsgif, file import/export with libjpeg: $with_jpeg, image pyramid export: $with_gsf, use libexif to load/save JPEG metadata: $with_libexif"
VIPS_CONFIG="native win32: $vips_os_win32, native OS X: $vips_os_darwin, open files in binary mode: $vips_binary_open, enable debug: $enable_debug, enable deprecated library components: $enable_deprecated, enable docs with gtkdoc: $enable_gtk_doc, gobject introspection: $found_introspection, enable radiance support: $with_radiance, enable analyze support: $with_analyze, enable PPM load/save: $with_ppm, enable GIF load: $with_nsgif, generate C++ docs: $with_doxygen, use fftw3 for FFT: $with_fftw, Magick package: $with_magickpackage, Magick API version: $magick_version, load with libMagick: $enable_magickload, save with libMagick: $enable_magicksave, accelerate loops with orc: $with_orc, ICC profile support with lcms: $with_lcms, NIfTI load/save with niftiio: $with_nifti, HEIC/AVIF load/save with libheif: $with_heif, EXR load/save with OpenEXR: $with_OpenEXR, slide load with OpenSlide: $with_openslide, Matlab load with matio: $with_matio, PDF load with PDFium: $with_pdfium, PDF load with poppler-glib: $with_poppler, SVG load with librsvg-2.0: $with_rsvg, zlib: $with_zlib, FITS load with cfitsio: $with_cfitsio, WebP load/save with libwebp: $with_libwebp, text rendering with pangoft2: $with_pangoft2, PNG load with libspng: $with_libspng, PNG load/save with libpng: $with_png, support 8bpp PNG quantisation: $with_imagequant, TIFF load/save with libtiff: $with_tiff, JPEG load/save with libjpeg: $with_jpeg, image pyramid export: $with_gsf, use libexif to load/save JPEG metadata: $with_libexif"
AC_SUBST(VIPS_LIBDIR)
@ -1349,6 +1326,7 @@ AC_CONFIG_FILES([
libvips/convolution/Makefile
libvips/deprecated/Makefile
libvips/foreign/Makefile
libvips/foreign/libnsgif/Makefile
libvips/freqfilt/Makefile
libvips/histogram/Makefile
libvips/draw/Makefile
@ -1389,9 +1367,10 @@ enable debug: $enable_debug
enable deprecated library components: $enable_deprecated
enable docs with gtkdoc: $enable_gtk_doc
gobject introspection: $found_introspection
enable radiance support: $with_radiance
enable analyze support: $with_analyze
enable PPM support: $with_ppm
enable radiance load/save: $with_radiance
enable analyze load/save: $with_analyze
enable PPM load/save: $with_ppm
enable GIF load: $with_nsgif
generate C++ docs: $with_doxygen
* optional dependencies
@ -1403,32 +1382,30 @@ save with libMagick: $enable_magicksave
accelerate loops with orc: $with_orc
(requires orc-0.4.11 or later)
ICC profile support with lcms: $with_lcms
file import with niftiio: $with_nifti
file import with libheif: $with_heif
file import with OpenEXR: $with_OpenEXR
file import with OpenSlide: $with_openslide
NIfTI load/save with niftiio: $with_nifti
HEIC/AVIF load/save with libheif: $with_heif
EXR load/save with OpenEXR: $with_OpenEXR
slide load with OpenSlide: $with_openslide
(requires openslide-3.3.0 or later)
file import with matio: $with_matio
PDF import with PDFium: $with_pdfium
PDF import with poppler-glib: $with_poppler
Matlab load with matio: $with_matio
PDF load with PDFium: $with_pdfium
PDF load with poppler-glib: $with_poppler
(requires poppler-glib 0.16.0 or later)
SVG import with librsvg-2.0: $with_rsvg
SVG load with librsvg-2.0: $with_rsvg
(requires librsvg-2.0 2.34.0 or later)
zlib: $with_zlib
file import with cfitsio: $with_cfitsio
file import/export with libwebp: $with_libwebp
FITS load/save with cfitsio: $with_cfitsio
WebP load/save with libwebp: $with_libwebp
(requires libwebp, libwebpmux, libwebpdemux 0.6.0 or later)
text rendering with pangoft2: $with_pangoft2
file import/export with libspng: $with_libspng
PNG load with libspng: $with_libspng
(requires libspng-0.6 or later)
file import/export with libpng: $with_png
PNG load/save with libpng: $with_png
(requires libpng-1.2.9 or later)
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
TIFF load/save with libtiff: $with_tiff
JPEG load/save with libjpeg: $with_jpeg
image pyramid export: $with_gsf
(requires libgsf-1 1.14.26 or later)
use libexif to load/save JPEG metadata: $with_libexif

View File

@ -9,6 +9,10 @@ else
OPTIONAL_DIST_DIR += deprecated
endif
if ENABLE_NSGIF
OPTIONAL_LIB += foreign/libnsgif/libnsgif.la
endif
SUBDIRS = \
include \
foreign \

View File

@ -1,7 +1,7 @@
noinst_LTLIBRARIES = libcolour.la
SUBDIRS = profiles
noinst_LTLIBRARIES = libcolour.la
libcolour_la_SOURCES = \
profiles.c \
profiles.h \

View File

@ -1,3 +1,7 @@
if ENABLE_NSGIF
SUBDIRS = libnsgif
endif
noinst_LTLIBRARIES = libforeign.la
libforeign_la_SOURCES = \
@ -8,8 +12,7 @@ libforeign_la_SOURCES = \
niftisave.c \
quantise.c \
exif.c \
gifnsload.c \
gifload.c \
nsgifload.c \
cairo.c \
pdfload.c \
pdfiumload.c \
@ -67,7 +70,7 @@ libforeign_la_SOURCES = \
jpegload.c \
jpegsave.c
EXTRA_DIST =
EXTRA_DIST = libnsgif
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@

View File

@ -2187,11 +2187,9 @@ vips_foreign_operation_init( void )
extern GType vips_foreign_load_nifti_source_get_type( void );
extern GType vips_foreign_save_nifti_get_type( void );
extern GType vips_foreign_load_gif_file_get_type( void );
extern GType vips_foreign_load_gif_buffer_get_type( void );
extern GType vips_foreign_load_gif_source_get_type( void );
extern GType vips_foreign_load_gifns_file_get_type( void );
extern GType vips_foreign_load_gifns_buffer_get_type( void );
extern GType vips_foreign_load_nsgif_file_get_type( void );
extern GType vips_foreign_load_nsgif_buffer_get_type( void );
extern GType vips_foreign_load_nsgif_source_get_type( void );
vips_foreign_load_csv_file_get_type();
vips_foreign_load_csv_source_get_type();
@ -2251,16 +2249,11 @@ vips_foreign_operation_init( void )
vips_foreign_load_svg_source_get_type();
#endif /*HAVE_RSVG*/
#ifdef HAVE_GIFLIB
vips_foreign_load_gif_file_get_type();
vips_foreign_load_gif_buffer_get_type();
vips_foreign_load_gif_source_get_type();
#endif /*HAVE_GIFLIB*/
#ifdef HAVE_LIBNSGIF
vips_foreign_load_gifns_file_get_type();
vips_foreign_load_gifns_buffer_get_type();
#endif /*HAVE_LIBNSGIF*/
#ifdef HAVE_NSGIF
vips_foreign_load_nsgif_file_get_type();
vips_foreign_load_nsgif_buffer_get_type();
vips_foreign_load_nsgif_source_get_type();
#endif /*HAVE_NSGIF*/
#ifdef HAVE_GSF
vips_foreign_save_dz_file_get_type();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
EXTRA_DIST = \
README-ns \
README.md \
utils
noinst_LTLIBRARIES = libnsgif.la
libnsgif_la_SOURCES = \
libnsgif.h \
libnsgif.c \
lzw.c \
lzw.h

View File

@ -0,0 +1,36 @@
libnsgif - Decoding GIF files
=============================
The functions provided by this library allow for efficient progressive
GIF decoding. Whilst the initialisation does not ensure that there is
sufficient image data to complete the entire frame, it does ensure
that the information provided is valid. Any subsequent attempts to
decode an initialised GIF are guaranteed to succeed, and any bytes of
the image not present are assumed to be totally transparent.
To begin decoding a GIF, the 'gif' structure must be initialised with
the 'gif_data' and 'buffer_size' set to their initial values. The
'buffer_position' should initially be 0, and will be internally
updated as the decoding commences. The caller should then repeatedly
call gif_initialise() with the structure until the function returns 1,
or no more data is avaliable.
Once the initialisation has begun, the decoder completes the variables
'frame_count' and 'frame_count_partial'. The former being the total
number of frames that have been successfully initialised, and the
latter being the number of frames that a partial amount of data is
available for. This assists the caller in managing the animation
whilst decoding is continuing.
To decode a frame, the caller must use gif_decode_frame() which
updates the current 'frame_image' to reflect the desired frame. The
required 'disposal_method' is also updated to reflect how the frame
should be plotted. The caller must not assume that the current
'frame_image' will be valid between calls if initialisation is still
occuring, and should either always request that the frame is decoded
(no processing will occur if the 'decoded_frame' has not been
invalidated by initialisation) or perform the check itself.
It should be noted that gif_finalise() should always be called, even
if no frames were initialised. Additionally, it is the responsibility
of the caller to free 'gif_data'.

View File

@ -0,0 +1,18 @@
# libnsgif
This is [libnsgif](https://www.netsurf-browser.org/projects/libnsgif/),
but within the libviops build system.
Based on libnsgif-0.2.1, with one patch to prevent it modifying the input
buffer on error.
# To update
When netsurf release a new version:
* copy in sources
* reapply any patches from git, eg. no input modification
# To do
No attempt made to run tests or build docs.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,183 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
/**
* \file
* Interface to progressive animated GIF file decoding.
*/
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
#include <stdbool.h>
#include <inttypes.h>
/* Error return values */
typedef enum {
GIF_WORKING = 1,
GIF_OK = 0,
GIF_INSUFFICIENT_FRAME_DATA = -1,
GIF_FRAME_DATA_ERROR = -2,
GIF_INSUFFICIENT_DATA = -3,
GIF_DATA_ERROR = -4,
GIF_INSUFFICIENT_MEMORY = -5,
GIF_FRAME_NO_DISPLAY = -6,
GIF_END_OF_FRAME = -7
} gif_result;
/** GIF frame data */
typedef struct gif_frame {
/** whether the frame should be displayed/animated */
bool display;
/** delay (in cs) before animating the frame */
unsigned int frame_delay;
/* Internal members are listed below */
/** offset (in bytes) to the GIF frame data */
unsigned int frame_pointer;
/** whether the frame has previously been used */
bool virgin;
/** whether the frame is totally opaque */
bool opaque;
/** whether a forcable screen redraw is required */
bool redraw_required;
/** how the previous frame should be disposed; affects plotting */
unsigned char disposal_method;
/** whether we acknoledge transparency */
bool transparency;
/** the index designating a transparent pixel */
unsigned char transparency_index;
/** x co-ordinate of redraw rectangle */
unsigned int redraw_x;
/** y co-ordinate of redraw rectangle */
unsigned int redraw_y;
/** width of redraw rectangle */
unsigned int redraw_width;
/** height of redraw rectangle */
unsigned int redraw_height;
} gif_frame;
/* API for Bitmap callbacks */
typedef void* (*gif_bitmap_cb_create)(int width, int height);
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
/** Bitmap callbacks function table */
typedef struct gif_bitmap_callback_vt {
/** Create a bitmap. */
gif_bitmap_cb_create bitmap_create;
/** Free a bitmap. */
gif_bitmap_cb_destroy bitmap_destroy;
/** Return a pointer to the pixel data in a bitmap. */
gif_bitmap_cb_get_buffer bitmap_get_buffer;
/* Members below are optional */
/** Sets whether a bitmap should be plotted opaque. */
gif_bitmap_cb_set_opaque bitmap_set_opaque;
/** Tests whether a bitmap has an opaque alpha channel. */
gif_bitmap_cb_test_opaque bitmap_test_opaque;
/** The bitmap image has changed, so flush any persistant cache. */
gif_bitmap_cb_modified bitmap_modified;
} gif_bitmap_callback_vt;
/** GIF animation data */
typedef struct gif_animation {
/** LZW decode context */
void *lzw_ctx;
/** callbacks for bitmap functions */
gif_bitmap_callback_vt bitmap_callbacks;
/** pointer to GIF data */
unsigned char *gif_data;
/** width of GIF (may increase during decoding) */
unsigned int width;
/** heigth of GIF (may increase during decoding) */
unsigned int height;
/** number of frames decoded */
unsigned int frame_count;
/** number of frames partially decoded */
unsigned int frame_count_partial;
/** decoded frames */
gif_frame *frames;
/** current frame decoded to bitmap */
int decoded_frame;
/** currently decoded image; stored as bitmap from bitmap_create callback */
void *frame_image;
/** number of times to loop animation */
int loop_count;
/* Internal members are listed below */
/** current index into GIF data */
unsigned int buffer_position;
/** total number of bytes of GIF data available */
unsigned int buffer_size;
/** current number of frame holders */
unsigned int frame_holders;
/** index in the colour table for the background colour */
unsigned int background_index;
/** image aspect ratio (ignored) */
unsigned int aspect_ratio;
/** size of colour table (in entries) */
unsigned int colour_table_size;
/** whether the GIF has a global colour table */
bool global_colours;
/** global colour table */
unsigned int *global_colour_table;
/** local colour table */
unsigned int *local_colour_table;
} gif_animation;
/**
* Initialises necessary gif_animation members.
*/
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
/**
* Initialises any workspace held by the animation and attempts to decode
* any information that hasn't already been decoded.
* If an error occurs, all previously decoded frames are retained.
*
* @return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
* any more frames
* - GIF_INSUFFICIENT_MEMORY for memory error
* - GIF_DATA_ERROR for GIF error
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
/**
* Decodes a GIF frame.
*
* @return Error return value. If a frame does not contain any image data,
* GIF_OK is returned and gif->current_error is set to
* GIF_FRAME_NO_DISPLAY
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
* - GIF_DATA_ERROR for GIF error (invalid frame header)
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
/**
* Releases any workspace held by a gif
*/
void gif_finalise(gif_animation *gif);
#endif

View File

@ -0,0 +1,377 @@
/*
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "lzw.h"
/**
* \file
* \brief LZW decompression (implementation)
*
* Decoder for GIF LZW data.
*/
/**
* Context for reading LZW data.
*
* LZW data is split over multiple sub-blocks. Each sub-block has a
* byte at the start, which says the sub-block size, and then the data.
* Zero-size sub-blocks have no data, and the biggest sub-block size is
* 255, which means there are 255 bytes of data following the sub-block
* size entry.
*
* Note that an individual LZW code can be split over up to three sub-blocks.
*/
struct lzw_read_ctx {
const uint8_t *data; /**< Pointer to start of input data */
uint32_t data_len; /**< Input data length */
uint32_t data_sb_next; /**< Offset to sub-block size */
const uint8_t *sb_data; /**< Pointer to current sub-block in data */
uint32_t sb_bit; /**< Current bit offset in sub-block */
uint32_t sb_bit_count; /**< Bit count in sub-block */
};
/**
* LZW dictionary entry.
*
* Records in the dictionary are composed of 1 or more entries.
* Entries point to previous entries which can be followed to compose
* the complete record. To compose the record in reverse order, take
* the `last_value` from each entry, and move to the previous entry.
* If the previous_entry's index is < the current clear_code, then it
* is the last entry in the record.
*/
struct lzw_dictionary_entry {
uint8_t last_value; /**< Last value for record ending at entry. */
uint8_t first_value; /**< First value for entry's record. */
uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
};
/**
* LZW decompression context.
*/
struct lzw_ctx {
/** Input reading context */
struct lzw_read_ctx input;
uint32_t previous_code; /**< Code read from input previously. */
uint32_t previous_code_first; /**< First value of previous code. */
uint32_t initial_code_size; /**< Starting LZW code size. */
uint32_t current_code_size; /**< Current LZW code size. */
uint32_t current_code_size_max; /**< Max code value for current size. */
uint32_t clear_code; /**< Special Clear code value */
uint32_t eoi_code; /**< Special End of Information code value */
uint32_t current_entry; /**< Next position in table to fill. */
/** Output value stack. */
uint8_t stack_base[1 << LZW_CODE_MAX];
/** LZW decode dictionary. Generated during decode. */
struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
};
/* Exported function, documented in lzw.h */
lzw_result lzw_context_create(struct lzw_ctx **ctx)
{
struct lzw_ctx *c = malloc(sizeof(*c));
if (c == NULL) {
return LZW_NO_MEM;
}
*ctx = c;
return LZW_OK;
}
/* Exported function, documented in lzw.h */
void lzw_context_destroy(struct lzw_ctx *ctx)
{
free(ctx);
}
/**
* Advance the context to the next sub-block in the input data.
*
* \param[in] ctx LZW reading context, updated on success.
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
*/
static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx)
{
uint32_t block_size;
uint32_t next_block_pos = ctx->data_sb_next;
const uint8_t *data_next = ctx->data + next_block_pos;
if (next_block_pos >= ctx->data_len) {
return LZW_NO_DATA;
}
block_size = *data_next;
if ((next_block_pos + block_size) >= ctx->data_len) {
return LZW_NO_DATA;
}
ctx->sb_bit = 0;
ctx->sb_bit_count = block_size * 8;
if (block_size == 0) {
ctx->data_sb_next += 1;
return LZW_OK_EOD;
}
ctx->sb_data = data_next + 1;
ctx->data_sb_next += block_size + 1;
return LZW_OK;
}
/**
* Get the next LZW code of given size from the raw input data.
*
* Reads codes from the input data stream coping with GIF data sub-blocks.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] code_size Size of LZW code to get from data.
* \param[out] code_out Returns an LZW code on success.
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
*/
static inline lzw_result lzw__next_code(
struct lzw_read_ctx *ctx,
uint8_t code_size,
uint32_t *code_out)
{
uint32_t code = 0;
uint8_t current_bit = ctx->sb_bit & 0x7;
uint8_t byte_advance = (current_bit + code_size) >> 3;
assert(byte_advance <= 2);
if (ctx->sb_bit + code_size <= ctx->sb_bit_count) {
/* Fast path: code fully inside this sub-block */
const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
switch (byte_advance) {
case 2: code |= data[2] << 16; /* Fall through */
case 1: code |= data[1] << 8; /* Fall through */
case 0: code |= data[0] << 0;
}
ctx->sb_bit += code_size;
} else {
/* Slow path: code spans sub-blocks */
uint8_t byte = 0;
uint8_t bits_remaining_0 = (code_size < (8 - current_bit)) ?
code_size : (8 - current_bit);
uint8_t bits_remaining_1 = code_size - bits_remaining_0;
uint8_t bits_used[3] = {
[0] = bits_remaining_0,
[1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
[2] = bits_remaining_1 - 8,
};
while (true) {
const uint8_t *data = ctx->sb_data;
lzw_result res;
/* Get any data from end of this sub-block */
while (byte <= byte_advance &&
ctx->sb_bit < ctx->sb_bit_count) {
code |= data[ctx->sb_bit >> 3] << (byte << 3);
ctx->sb_bit += bits_used[byte];
byte++;
}
/* Check if we have all we need */
if (byte > byte_advance) {
break;
}
/* Move to next sub-block */
res = lzw__block_advance(ctx);
if (res != LZW_OK) {
return res;
}
}
}
*code_out = (code >> current_bit) & ((1 << code_size) - 1);
return LZW_OK;
}
/**
* Clear LZW code dictionary.
*
* \param[in] ctx LZW reading context, updated.
* \param[out] stack_pos_out Returns current stack position.
* \return LZW_OK or error code.
*/
static lzw_result lzw__clear_codes(
struct lzw_ctx *ctx,
const uint8_t ** const stack_pos_out)
{
uint32_t code;
uint8_t *stack_pos;
/* Reset dictionary building context */
ctx->current_code_size = ctx->initial_code_size + 1;
ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
ctx->current_entry = (1 << ctx->initial_code_size) + 2;
/* There might be a sequence of clear codes, so process them all */
do {
lzw_result res = lzw__next_code(&ctx->input,
ctx->current_code_size, &code);
if (res != LZW_OK) {
return res;
}
} while (code == ctx->clear_code);
/* The initial code must be from the initial dictionary. */
if (code > ctx->clear_code) {
return LZW_BAD_ICODE;
}
/* Record this initial code as "previous" code, needed during decode. */
ctx->previous_code = code;
ctx->previous_code_first = code;
/* Reset the stack, and add first non-clear code added as first item. */
stack_pos = ctx->stack_base;
*stack_pos++ = code;
*stack_pos_out = stack_pos;
return LZW_OK;
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode_init(
struct lzw_ctx *ctx,
const uint8_t *compressed_data,
uint32_t compressed_data_len,
uint32_t compressed_data_pos,
uint8_t code_size,
const uint8_t ** const stack_base_out,
const uint8_t ** const stack_pos_out)
{
struct lzw_dictionary_entry *table = ctx->table;
/* Initialise the input reading context */
ctx->input.data = compressed_data;
ctx->input.data_len = compressed_data_len;
ctx->input.data_sb_next = compressed_data_pos;
ctx->input.sb_bit = 0;
ctx->input.sb_bit_count = 0;
/* Initialise the dictionary building context */
ctx->initial_code_size = code_size;
ctx->clear_code = (1 << code_size) + 0;
ctx->eoi_code = (1 << code_size) + 1;
/* Initialise the standard dictionary entries */
for (uint32_t i = 0; i < ctx->clear_code; ++i) {
table[i].first_value = i;
table[i].last_value = i;
}
*stack_base_out = ctx->stack_base;
return lzw__clear_codes(ctx, stack_pos_out);
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode(struct lzw_ctx *ctx,
const uint8_t ** const stack_pos_out)
{
lzw_result res;
uint32_t code_new;
uint32_t code_out;
uint8_t last_value;
uint8_t *stack_pos = ctx->stack_base;
uint32_t clear_code = ctx->clear_code;
uint32_t current_entry = ctx->current_entry;
struct lzw_dictionary_entry * const table = ctx->table;
/* Get a new code from the input */
res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
if (res != LZW_OK) {
return res;
}
/* Handle the new code */
if (code_new == clear_code) {
/* Got Clear code */
return lzw__clear_codes(ctx, stack_pos_out);
} else if (code_new == ctx->eoi_code) {
/* Got End of Information code */
return LZW_EOI_CODE;
} else if (code_new > current_entry) {
/* Code is invalid */
return LZW_BAD_CODE;
} else if (code_new < current_entry) {
/* Code is in table */
code_out = code_new;
last_value = table[code_new].first_value;
} else {
/* Code not in table */
*stack_pos++ = ctx->previous_code_first;
code_out = ctx->previous_code;
last_value = ctx->previous_code_first;
}
/* Add to the dictionary, only if there's space */
if (current_entry < (1 << LZW_CODE_MAX)) {
struct lzw_dictionary_entry *entry = table + current_entry;
entry->last_value = last_value;
entry->first_value = ctx->previous_code_first;
entry->previous_entry = ctx->previous_code;
ctx->current_entry++;
}
/* Ensure code size is increased, if needed. */
if (current_entry == ctx->current_code_size_max) {
if (ctx->current_code_size < LZW_CODE_MAX) {
ctx->current_code_size++;
ctx->current_code_size_max =
(1 << ctx->current_code_size) - 1;
}
}
/* Store details of this code as "previous code" to the context. */
ctx->previous_code_first = table[code_new].first_value;
ctx->previous_code = code_new;
/* Put rest of data for this code on output stack.
* Note, in the case of "code not in table", the last entry of the
* current code has already been placed on the stack above. */
while (code_out > clear_code) {
struct lzw_dictionary_entry *entry = table + code_out;
*stack_pos++ = entry->last_value;
code_out = entry->previous_entry;
}
*stack_pos++ = table[code_out].last_value;
*stack_pos_out = stack_pos;
return LZW_OK;
}

View File

@ -0,0 +1,105 @@
/*
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
*/
#ifndef LZW_H_
#define LZW_H_
/**
* \file
* \brief LZW decompression (interface)
*
* Decoder for GIF LZW data.
*/
/** Maximum LZW code size in bits */
#define LZW_CODE_MAX 12
/* Declare lzw internal context structure */
struct lzw_ctx;
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
LZW_EOI_CODE, /**< Error: End of Information code */
LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
LZW_BAD_CODE, /**< Error: Bad LZW code */
} lzw_result;
/**
* Create an LZW decompression context.
*
* \param[out] ctx Returns an LZW decompression context. Caller owned,
* free with lzw_context_destroy().
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_context_create(
struct lzw_ctx **ctx);
/**
* Destroy an LZW decompression context.
*
* \param[in] ctx The LZW decompression context to destroy.
*/
void lzw_context_destroy(
struct lzw_ctx *ctx);
/**
* Initialise an LZW decompression context for decoding.
*
* Caller owns neither `stack_base_out` or `stack_pos_out`.
*
* \param[in] ctx The LZW decompression context to initialise.
* \param[in] compressed_data The compressed data.
* \param[in] compressed_data_len Byte length of compressed data.
* \param[in] compressed_data_pos Start position in data. Must be position
* of a size byte at sub-block start.
* \param[in] code_size The initial LZW code size to use.
* \param[out] stack_base_out Returns base of decompressed data stack.
* \param[out] stack_pos_out Returns current stack position.
* There are `stack_pos_out - stack_base_out`
* current stack entries.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode_init(
struct lzw_ctx *ctx,
const uint8_t *compressed_data,
uint32_t compressed_data_len,
uint32_t compressed_data_pos,
uint8_t code_size,
const uint8_t ** const stack_base_out,
const uint8_t ** const stack_pos_out);
/**
* Fill the LZW stack with decompressed data
*
* Ensure anything on the stack is used before calling this, as anything
* on the stack before this call will be trampled.
*
* Caller does not own `stack_pos_out`.
*
* \param[in] ctx LZW reading context, updated.
* \param[out] stack_pos_out Returns current stack position.
* Use with `stack_base_out` value from previous
* lzw_decode_init() call.
* There are `stack_pos_out - stack_base_out`
* current stack entries.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode(
struct lzw_ctx *ctx,
const uint8_t ** const stack_pos_out);
#endif

View File

@ -0,0 +1,21 @@
/*
* Copyright 2003 James Bursa <bursa@users.sourceforge.net>
* Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
#include <stdio.h>
#ifndef _LIBNSGIF_LOG_H_
#define _LIBNSGIF_LOG_H_
#ifdef NDEBUG
# define LOG(x) ((void) 0)
#else
# define LOG(x) do { fprintf(stderr, x), fputc('\n', stderr); } while (0)
#endif /* NDEBUG */
#endif /* _LIBNSGIF_LOG_H_ */

View File

@ -62,33 +62,33 @@
*
* - hard to detect mono images -- local_colour_table in libnsgif is only set
* when we decode a frame, so we can't tell just from init whether any
* frames
* frames have colour info
*
* - don't bother detecting alpha -- if we can't detect RGB, alpha won't help
* much
*
*/
#ifdef HAVE_LIBNSGIF
#ifdef HAVE_NSGIF
#include <libnsgif/libnsgif.h>
#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_gifns_get_type())
#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_nsgif_get_type())
#define VIPS_FOREIGN_LOAD_GIF( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifns ))
VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadNsgif ))
#define VIPS_FOREIGN_LOAD_GIF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_CAST( (klass), \
VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifnsClass))
VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadNsgifClass))
#define VIPS_IS_FOREIGN_LOAD_GIF( obj ) \
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FOREIGN_LOAD_GIF ))
#define VIPS_IS_FOREIGN_LOAD_GIF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FOREIGN_LOAD_GIF ))
#define VIPS_FOREIGN_LOAD_GIF_GET_CLASS( obj ) \
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifnsClass ))
VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadNsgifClass ))
typedef struct _VipsForeignLoadGifns {
typedef struct _VipsForeignLoadNsgif {
VipsForeignLoad parent_object;
/* Load this page (frame number).
@ -124,15 +124,15 @@ typedef struct _VipsForeignLoadGifns {
*/
int gif_delay;
} VipsForeignLoadGifns;
} VipsForeignLoadNsgif;
typedef VipsForeignLoadClass VipsForeignLoadGifnsClass;
typedef VipsForeignLoadClass VipsForeignLoadNsgifClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGifns, vips_foreign_load_gifns,
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadNsgif, vips_foreign_load_nsgif,
VIPS_TYPE_FOREIGN_LOAD );
static const char *
vips_foreign_load_gifns_errstr( gif_result result )
vips_foreign_load_nsgif_errstr( gif_result result )
{
switch( result ) {
case GIF_WORKING:
@ -168,20 +168,20 @@ vips_foreign_load_gifns_errstr( gif_result result )
}
static void
vips_foreign_load_gifns_error( VipsForeignLoadGifns *gif, gif_result result )
vips_foreign_load_nsgif_error( VipsForeignLoadNsgif *gif, gif_result result )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
vips_error( class->nickname, "%s",
vips_foreign_load_gifns_errstr( result ) );
vips_foreign_load_nsgif_errstr( result ) );
}
static void
vips_foreign_load_gifns_dispose( GObject *gobject )
vips_foreign_load_nsgif_dispose( GObject *gobject )
{
VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) gobject;
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) gobject;
VIPS_DEBUG_MSG( "vips_foreign_load_gifns_dispose:\n" );
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_dispose:\n" );
if( gif->anim ) {
gif_finalise( gif->anim );
@ -190,24 +190,24 @@ vips_foreign_load_gifns_dispose( GObject *gobject )
VIPS_UNREF( gif->source );
VIPS_FREE( gif->delay );
G_OBJECT_CLASS( vips_foreign_load_gifns_parent_class )->
G_OBJECT_CLASS( vips_foreign_load_nsgif_parent_class )->
dispose( gobject );
}
static VipsForeignFlags
vips_foreign_load_gifns_get_flags_filename( const char *filename )
vips_foreign_load_nsgif_get_flags_filename( const char *filename )
{
return( VIPS_FOREIGN_SEQUENTIAL );
}
static VipsForeignFlags
vips_foreign_load_gifns_get_flags( VipsForeignLoad *load )
vips_foreign_load_nsgif_get_flags( VipsForeignLoad *load )
{
return( VIPS_FOREIGN_SEQUENTIAL );
}
static gboolean
vips_foreign_load_gifns_is_a_source( VipsSource *source )
vips_foreign_load_nsgif_is_a_source( VipsSource *source )
{
const unsigned char *data;
@ -268,10 +268,10 @@ print_animation( gif_animation *anim )
#endif /*VERBOSE*/
static int
vips_foreign_load_gifns_set_header( VipsForeignLoadGifns *gif,
vips_foreign_load_nsgif_set_header( VipsForeignLoadNsgif *gif,
VipsImage *image )
{
VIPS_DEBUG_MSG( "vips_foreign_load_gifns_set_header:\n" );
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_set_header:\n" );
vips_image_init_fields( image,
gif->anim->width, gif->anim->height * gif->n, 4,
@ -304,17 +304,17 @@ vips_foreign_load_gifns_set_header( VipsForeignLoadGifns *gif,
* Close as soon as we can to free up the fd.
*/
static int
vips_foreign_load_gifns_header( VipsForeignLoad *load )
vips_foreign_load_nsgif_header( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load;
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) load;
const void *data;
size_t size;
gif_result result;
int i;
VIPS_DEBUG_MSG( "vips_foreign_load_gifns_header:\n" );
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_header:\n" );
/* We map in the image, then minimise to close any underlying file
* object. This won't unmap.
@ -331,7 +331,7 @@ vips_foreign_load_gifns_header( VipsForeignLoad *load )
if( result != GIF_OK &&
result != GIF_WORKING &&
result != GIF_INSUFFICIENT_FRAME_DATA ) {
vips_foreign_load_gifns_error( gif, result );
vips_foreign_load_nsgif_error( gif, result );
return( -1 );
}
else if( result == GIF_INSUFFICIENT_FRAME_DATA &&
@ -348,7 +348,7 @@ vips_foreign_load_gifns_header( VipsForeignLoad *load )
gif->frame_count_displayable = i + 1;
#ifdef VERBOSE
if( gif->frame_count_displayable != gif->anim->frame_count )
printf( "vips_foreign_load_gifns_open: "
printf( "vips_foreign_load_nsgif_open: "
"removed %d undisplayable frames\n",
gif->anim->frame_count - gif->frame_count_displayable );
#endif /*VERBOSE*/
@ -379,22 +379,22 @@ vips_foreign_load_gifns_header( VipsForeignLoad *load )
gif->gif_delay = gif->anim->frames[0].frame_delay;
vips_foreign_load_gifns_set_header( gif, load->out );
vips_foreign_load_nsgif_set_header( gif, load->out );
return( 0 );
}
static int
vips_foreign_load_gifns_generate( VipsRegion *or,
vips_foreign_load_nsgif_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsRect *r = &or->valid;
VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) a;
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) a;
int y;
#ifdef VERBOSE
VIPS_DEBUG_MSG( "vips_foreign_load_gifns_generate: "
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_generate: "
"top = %d, height = %d\n", r->top, r->height );
#endif /*VERBOSE*/
@ -415,7 +415,7 @@ vips_foreign_load_gifns_generate( VipsRegion *or,
VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n",
page, result );
if( result != GIF_OK ) {
vips_foreign_load_gifns_error( gif, result );
vips_foreign_load_nsgif_error( gif, result );
return( -1 );
}
#ifdef VERBOSE
@ -433,24 +433,24 @@ vips_foreign_load_gifns_generate( VipsRegion *or,
}
static int
vips_foreign_load_gifns_load( VipsForeignLoad *load )
vips_foreign_load_nsgif_load( VipsForeignLoad *load )
{
VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load;
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) load;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( load ), 4 );
VIPS_DEBUG_MSG( "vips_foreign_load_gifns_load:\n" );
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_load:\n" );
/* Make the output pipeline.
*/
t[0] = vips_image_new();
if( vips_foreign_load_gifns_set_header( gif, t[0] ) )
if( vips_foreign_load_nsgif_set_header( gif, t[0] ) )
return( -1 );
/* Strips 8 pixels high to avoid too many tiny regions.
*/
if( vips_image_generate( t[0],
NULL, vips_foreign_load_gifns_generate, NULL, gif, NULL ) ||
NULL, vips_foreign_load_nsgif_generate, NULL, gif, NULL ) ||
vips_sequential( t[0], &t[1],
"tile_height", VIPS__FATSTRIP_HEIGHT,
NULL ) ||
@ -461,18 +461,18 @@ vips_foreign_load_gifns_load( VipsForeignLoad *load )
}
static void
vips_foreign_load_gifns_class_init( VipsForeignLoadGifnsClass *class )
vips_foreign_load_nsgif_class_init( VipsForeignLoadNsgifClass *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->dispose = vips_foreign_load_gifns_dispose;
gobject_class->dispose = vips_foreign_load_nsgif_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "gifnsload_base";
object_class->nickname = "gifload_base";
object_class->description = _( "load GIF with libnsgif" );
/* High priority, so that we handle vipsheader etc.
@ -480,29 +480,29 @@ vips_foreign_load_gifns_class_init( VipsForeignLoadGifnsClass *class )
foreign_class->priority = 50;
load_class->get_flags_filename =
vips_foreign_load_gifns_get_flags_filename;
load_class->get_flags = vips_foreign_load_gifns_get_flags;
load_class->header = vips_foreign_load_gifns_header;
load_class->load = vips_foreign_load_gifns_load;
vips_foreign_load_nsgif_get_flags_filename;
load_class->get_flags = vips_foreign_load_nsgif_get_flags;
load_class->header = vips_foreign_load_nsgif_header;
load_class->load = vips_foreign_load_nsgif_load;
VIPS_ARG_INT( class, "page", 10,
_( "Page" ),
_( "Load this page from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifns, page ),
G_STRUCT_OFFSET( VipsForeignLoadNsgif, page ),
0, 100000, 0 );
VIPS_ARG_INT( class, "n", 6,
_( "n" ),
_( "Load this many pages" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifns, n ),
G_STRUCT_OFFSET( VipsForeignLoadNsgif, n ),
-1, 100000, 1 );
}
static void *
vips_foreign_load_gifns_bitmap_create( int width, int height )
vips_foreign_load_nsgif_bitmap_create( int width, int height )
{
/* ensure a stupidly large bitmap is not created
*/
@ -511,7 +511,7 @@ vips_foreign_load_gifns_bitmap_create( int width, int height )
}
static void
vips_foreign_load_gifns_bitmap_set_opaque( void *bitmap, bool opaque )
vips_foreign_load_nsgif_bitmap_set_opaque( void *bitmap, bool opaque )
{
(void) opaque; /* unused */
(void) bitmap; /* unused */
@ -519,7 +519,7 @@ vips_foreign_load_gifns_bitmap_set_opaque( void *bitmap, bool opaque )
}
static bool
vips_foreign_load_gifns_bitmap_test_opaque( void *bitmap )
vips_foreign_load_nsgif_bitmap_test_opaque( void *bitmap )
{
(void) bitmap; /* unused */
g_assert( bitmap );
@ -528,7 +528,7 @@ vips_foreign_load_gifns_bitmap_test_opaque( void *bitmap )
}
static unsigned char *
vips_foreign_load_gifns_bitmap_get_buffer( void *bitmap )
vips_foreign_load_nsgif_bitmap_get_buffer( void *bitmap )
{
g_assert( bitmap );
@ -536,14 +536,14 @@ vips_foreign_load_gifns_bitmap_get_buffer( void *bitmap )
}
static void
vips_foreign_load_gifns_bitmap_destroy( void *bitmap )
vips_foreign_load_nsgif_bitmap_destroy( void *bitmap )
{
g_assert( bitmap );
free( bitmap );
}
static void
vips_foreign_load_gifns_bitmap_modified( void *bitmap )
vips_foreign_load_nsgif_bitmap_modified( void *bitmap )
{
(void) bitmap; /* unused */
g_assert( bitmap );
@ -551,77 +551,77 @@ vips_foreign_load_gifns_bitmap_modified( void *bitmap )
return;
}
static gif_bitmap_callback_vt vips_foreign_load_gifns_bitmap_callbacks = {
vips_foreign_load_gifns_bitmap_create,
vips_foreign_load_gifns_bitmap_destroy,
vips_foreign_load_gifns_bitmap_get_buffer,
vips_foreign_load_gifns_bitmap_set_opaque,
vips_foreign_load_gifns_bitmap_test_opaque,
vips_foreign_load_gifns_bitmap_modified
static gif_bitmap_callback_vt vips_foreign_load_nsgif_bitmap_callbacks = {
vips_foreign_load_nsgif_bitmap_create,
vips_foreign_load_nsgif_bitmap_destroy,
vips_foreign_load_nsgif_bitmap_get_buffer,
vips_foreign_load_nsgif_bitmap_set_opaque,
vips_foreign_load_nsgif_bitmap_test_opaque,
vips_foreign_load_nsgif_bitmap_modified
};
static void
vips_foreign_load_gifns_init( VipsForeignLoadGifns *gif )
vips_foreign_load_nsgif_init( VipsForeignLoadNsgif *gif )
{
gif->anim = g_new0( gif_animation, 1 );
gif_create( gif->anim, &vips_foreign_load_gifns_bitmap_callbacks );
gif_create( gif->anim, &vips_foreign_load_nsgif_bitmap_callbacks );
gif->n = 1;
}
typedef struct _VipsForeignLoadGifnsFile {
VipsForeignLoadGifns parent_object;
typedef struct _VipsForeignLoadNsgifFile {
VipsForeignLoadNsgif parent_object;
/* Filename for load.
*/
char *filename;
} VipsForeignLoadGifnsFile;
} VipsForeignLoadNsgifFile;
typedef VipsForeignLoadGifnsClass VipsForeignLoadGifnsFileClass;
typedef VipsForeignLoadNsgifClass VipsForeignLoadNsgifFileClass;
G_DEFINE_TYPE( VipsForeignLoadGifnsFile, vips_foreign_load_gifns_file,
vips_foreign_load_gifns_get_type() );
G_DEFINE_TYPE( VipsForeignLoadNsgifFile, vips_foreign_load_nsgif_file,
vips_foreign_load_nsgif_get_type() );
static int
vips_foreign_load_gif_file_build( VipsObject *object )
{
VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) object;
VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) object;
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) object;
VipsForeignLoadNsgifFile *file = (VipsForeignLoadNsgifFile *) object;
if( file->filename )
if( !(gif->source =
vips_source_new_from_file( file->filename )) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_load_gifns_file_parent_class )->
if( VIPS_OBJECT_CLASS( vips_foreign_load_nsgif_file_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static const char *vips_foreign_gifns_suffs[] = {
static const char *vips_foreign_nsgif_suffs[] = {
".gif",
NULL
};
static gboolean
vips_foreign_load_gifns_file_is_a( const char *filename )
vips_foreign_load_nsgif_file_is_a( const char *filename )
{
VipsSource *source;
gboolean result;
if( !(source = vips_source_new_from_file( filename )) )
return( FALSE );
result = vips_foreign_load_gifns_is_a_source( source );
result = vips_foreign_load_nsgif_is_a_source( source );
VIPS_UNREF( source );
return( result );
}
static void
vips_foreign_load_gifns_file_class_init(
VipsForeignLoadGifnsFileClass *class )
vips_foreign_load_nsgif_file_class_init(
VipsForeignLoadNsgifFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
@ -631,48 +631,48 @@ vips_foreign_load_gifns_file_class_init(
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "gifnsload";
object_class->nickname = "gifload";
object_class->description = _( "load GIF with libnsgif" );
object_class->build = vips_foreign_load_gif_file_build;
foreign_class->suffs = vips_foreign_gifns_suffs;
foreign_class->suffs = vips_foreign_nsgif_suffs;
load_class->is_a = vips_foreign_load_gifns_file_is_a;
load_class->is_a = vips_foreign_load_nsgif_file_is_a;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifnsFile, filename ),
G_STRUCT_OFFSET( VipsForeignLoadNsgifFile, filename ),
NULL );
}
static void
vips_foreign_load_gifns_file_init( VipsForeignLoadGifnsFile *file )
vips_foreign_load_nsgif_file_init( VipsForeignLoadNsgifFile *file )
{
}
typedef struct _VipsForeignLoadGifnsBuffer {
VipsForeignLoadGifns parent_object;
typedef struct _VipsForeignLoadNsgifBuffer {
VipsForeignLoadNsgif parent_object;
/* Load from a buffer.
*/
VipsArea *blob;
} VipsForeignLoadGifnsBuffer;
} VipsForeignLoadNsgifBuffer;
typedef VipsForeignLoadGifnsClass VipsForeignLoadGifnsBufferClass;
typedef VipsForeignLoadNsgifClass VipsForeignLoadNsgifBufferClass;
G_DEFINE_TYPE( VipsForeignLoadGifnsBuffer, vips_foreign_load_gifns_buffer,
vips_foreign_load_gifns_get_type() );
G_DEFINE_TYPE( VipsForeignLoadNsgifBuffer, vips_foreign_load_nsgif_buffer,
vips_foreign_load_nsgif_get_type() );
static int
vips_foreign_load_gifns_buffer_build( VipsObject *object )
vips_foreign_load_nsgif_buffer_build( VipsObject *object )
{
VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) object;
VipsForeignLoadGifnsBuffer *buffer =
(VipsForeignLoadGifnsBuffer *) object;
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) object;
VipsForeignLoadNsgifBuffer *buffer =
(VipsForeignLoadNsgifBuffer *) object;
if( buffer->blob &&
!(gif->source = vips_source_new_from_memory(
@ -680,7 +680,7 @@ vips_foreign_load_gifns_buffer_build( VipsObject *object )
buffer->blob->length )) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_load_gifns_buffer_parent_class )->
if( VIPS_OBJECT_CLASS( vips_foreign_load_nsgif_buffer_parent_class )->
build( object ) )
return( -1 );
@ -688,22 +688,22 @@ vips_foreign_load_gifns_buffer_build( VipsObject *object )
}
static gboolean
vips_foreign_load_gifns_buffer_is_a_buffer( const void *buf, size_t len )
vips_foreign_load_nsgif_buffer_is_a_buffer( const void *buf, size_t len )
{
VipsSource *source;
gboolean result;
if( !(source = vips_source_new_from_memory( buf, len )) )
return( FALSE );
result = vips_foreign_load_gifns_is_a_source( source );
result = vips_foreign_load_nsgif_is_a_source( source );
VIPS_UNREF( source );
return( result );
}
static void
vips_foreign_load_gifns_buffer_class_init(
VipsForeignLoadGifnsBufferClass *class )
vips_foreign_load_nsgif_buffer_class_init(
VipsForeignLoadNsgifBufferClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
@ -712,24 +712,198 @@ vips_foreign_load_gifns_buffer_class_init(
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "gifnsload_buffer";
object_class->nickname = "gifload_buffer";
object_class->description = _( "load GIF with libnsgif" );
object_class->build = vips_foreign_load_gifns_buffer_build;
object_class->build = vips_foreign_load_nsgif_buffer_build;
load_class->is_a_buffer = vips_foreign_load_gifns_buffer_is_a_buffer;
load_class->is_a_buffer = vips_foreign_load_nsgif_buffer_is_a_buffer;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadGifnsBuffer, blob ),
G_STRUCT_OFFSET( VipsForeignLoadNsgifBuffer, blob ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_load_gifns_buffer_init( VipsForeignLoadGifnsBuffer *buffer )
vips_foreign_load_nsgif_buffer_init( VipsForeignLoadNsgifBuffer *buffer )
{
}
#endif /*HAVE_LIBNSGIF*/
typedef struct _VipsForeignLoadNsgifSource {
VipsForeignLoadNsgif parent_object;
/* Load from a source.
*/
VipsSource *source;
} VipsForeignLoadNsgifSource;
typedef VipsForeignLoadClass VipsForeignLoadNsgifSourceClass;
G_DEFINE_TYPE( VipsForeignLoadNsgifSource, vips_foreign_load_nsgif_source,
vips_foreign_load_nsgif_get_type() );
static int
vips_foreign_load_nsgif_source_build( VipsObject *object )
{
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) object;
VipsForeignLoadNsgifSource *source =
(VipsForeignLoadNsgifSource *) object;
if( source->source ) {
gif->source = source->source;
g_object_ref( gif->source );
}
if( VIPS_OBJECT_CLASS( vips_foreign_load_nsgif_source_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_load_nsgif_source_class_init(
VipsForeignLoadNsgifSourceClass *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_source";
object_class->description = _( "load gif from source" );
object_class->build = vips_foreign_load_nsgif_source_build;
load_class->is_a_source = vips_foreign_load_nsgif_is_a_source;
VIPS_ARG_OBJECT( class, "source", 1,
_( "Source" ),
_( "Source to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadNsgifSource, source ),
VIPS_TYPE_SOURCE );
}
static void
vips_foreign_load_nsgif_source_init( VipsForeignLoadNsgifSource *source )
{
}
#endif /*HAVE_NSGIF*/
/**
* vips_gifload:
* @filename: file to load
* @out: (out): output image
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @page: %gint, page (frame) to read
* * @n: %gint, load this many pages
*
* Read a GIF file into a libvips image.
*
* Use @page to select a page to render, numbering from zero.
*
* Use @n to select the number of pages to render. The default is 1. Pages are
* rendered in a vertical column. Set to -1 to mean "until the end of the
* document". Use vips_grid() to change page layout.
*
* The output image is always RGBA.
*
* See also: vips_image_new_from_file().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_gifload( const char *filename, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_call_split( "gifload", ap, filename, out );
va_end( ap );
return( result );
}
/**
* vips_gifload_buffer:
* @buf: (array length=len) (element-type guint8): memory area to load
* @len: (type gsize): size of memory area
* @out: (out): image to write
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @page: %gint, page (frame) to read
* * @n: %gint, load this many pages
*
* Read a GIF-formatted memory block into a VIPS image. Exactly as
* vips_gifload(), but read from a memory buffer.
*
* You must not free the buffer while @out is active. The
* #VipsObject::postclose signal on @out is a good place to free.
*
* See also: vips_gifload().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_gifload_buffer( void *buf, size_t len, VipsImage **out, ... )
{
va_list ap;
VipsBlob *blob;
int result;
/* We don't take a copy of the data or free it.
*/
blob = vips_blob_new( NULL, buf, len );
va_start( ap, out );
result = vips_call_split( "gifload_buffer", ap, blob, out );
va_end( ap );
vips_area_unref( VIPS_AREA( blob ) );
return( result );
}
/**
* vips_gifload_source:
* @source: source to load
* @out: (out): image to write
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @page: %gint, page (frame) to read
* * @n: %gint, load this many pages
*
* Exactly as vips_gifload(), but read from a source.
*
* See also: vips_gifload().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_gifload_source( VipsSource *source, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_call_split( "gifload_source", ap, source, out );
va_end( ap );
return( result );
}