From 1d77a57780b350b7876d213368f0666dfe94cc05 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 15 Aug 2019 14:30:25 +0100 Subject: [PATCH 01/20] reinstate libnsgif loader only 1/2 done --- configure.ac | 41 +++- libvips/foreign/Makefile.am | 1 + libvips/foreign/gifnsload.c | 462 ++++++++++++++++++++++++++++++++++++ 3 files changed, 494 insertions(+), 10 deletions(-) create mode 100644 libvips/foreign/gifnsload.c diff --git a/configure.ac b/configure.ac index 7f2b6b11..5db7d1e2 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index b44cf53c..fb4c353a 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -8,6 +8,7 @@ libforeign_la_SOURCES = \ niftisave.c \ quantise.c \ exif.c \ + gifnsload.c \ gifload.c \ cairo.c \ pdfload.c \ diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c new file mode 100644 index 00000000..b3c586a5 --- /dev/null +++ b/libvips/foreign/gifnsload.c @@ -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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_LIBNSGIF + +#include + +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*/ From 844c35f5257227b228c73163ee40e8e7d7b7f826 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 6 Sep 2019 12:57:03 +0100 Subject: [PATCH 02/20] getting closer --- libvips/foreign/foreign.c | 4 +- libvips/foreign/gifnsload.c | 281 +++++++++++++++++++++++++++++------- 2 files changed, 231 insertions(+), 54 deletions(-) diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index a48591ad..016643a0 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1955,11 +1955,11 @@ vips_foreign_operation_init( void ) vips_foreign_load_svg_buffer_get_type(); #endif /*HAVE_RSVG*/ -#ifdef HAVE_GIFLIB +#if defined(HAVE_GIFLIB) || defined(HAVE_LIBNSGIF) vips_foreign_load_gif_get_type(); vips_foreign_load_gif_file_get_type(); vips_foreign_load_gif_buffer_get_type(); -#endif /*HAVE_GIFLIB*/ +#endif /*gif*/ #ifdef HAVE_GSF vips_foreign_save_dz_file_get_type(); diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index b3c586a5..14c5f297 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -32,8 +32,8 @@ */ /* -#define VIPS_DEBUG */ +#define VIPS_DEBUG #ifdef HAVE_CONFIG_H #include @@ -68,12 +68,12 @@ typedef struct _VipsForeignLoadGif { /* The animation created by libnsgif. */ - gif_animation *gif; + gif_animation *anim; /* The data/size pair we pass to libnsgif. */ - size_t size; unsigned char *data; + size_t size; } VipsForeignLoadGif; @@ -123,7 +123,9 @@ vips_foreign_load_gif_dispose( GObject *gobject ) { VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; - VIPS_FREEF( gif_finalise, gif->gif ); + VIPS_DEBUG_MSG( "vips_foreign_load_gif_dispose:\n" ); + + VIPS_FREEF( gif_finalise, gif->anim ); G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> dispose( gobject ); @@ -132,15 +134,13 @@ vips_foreign_load_gif_dispose( GObject *gobject ) static VipsForeignFlags vips_foreign_load_gif_get_flags_filename( const char *filename ) { - /* We can render any part of the image on demand. - */ - return( VIPS_FOREIGN_PARTIAL ); + return( VIPS_FOREIGN_SEQUENTIAL ); } static VipsForeignFlags vips_foreign_load_gif_get_flags( VipsForeignLoad *load ) { - return( VIPS_FOREIGN_PARTIAL ); + return( VIPS_FOREIGN_SEQUENTIAL ); } static gboolean @@ -148,6 +148,8 @@ vips_foreign_load_gif_is_a_buffer( const void *buf, size_t len ) { const guchar *str = (const guchar *) buf; + VIPS_DEBUG_MSG( "vips_foreign_load_gif_is_a_buffer:\n" ); + if( len >= 4 && str[0] == 'G' && str[1] == 'I' && @@ -173,7 +175,8 @@ vips_foreign_load_gif_is_a( const char *filename ) static void * vips_foreign_load_gif_bitmap_create( int width, int height ) { - /* ensure a stupidly large bitmap is not created */ + /* ensure a stupidly large bitmap is not created + */ return calloc(width * height, 4); } @@ -225,19 +228,94 @@ static gif_bitmap_callback_vt vips_foreign_load_gif_bitmap_callbacks = { vips_foreign_load_gif_bitmap_modified }; -static int -vips_foreign_load_gif_load( VipsForeignLoad *load ) +static void +print_frame( gif_frame *frame ) { + printf( "frame:\n" ); + printf( " display = %d\n", frame->display ); + printf( " frame_delay = %d\n", frame->frame_delay ); + printf( " virgin = %d\n", frame->virgin ); + printf( " opaque = %d\n", frame->opaque ); + printf( " redraw_required = %d\n", frame->redraw_required ); + printf( " disposal_method = %d\n", frame->disposal_method ); + printf( " transparency = %d\n", frame->transparency ); + printf( " transparency_index = %d\n", frame->transparency_index ); + printf( " redraw_x = %d\n", frame->redraw_x ); + printf( " redraw_y = %d\n", frame->redraw_y ); + printf( " redraw_width = %d\n", frame->redraw_width ); + printf( " redraw_height = %d\n", frame->redraw_height ); +} + +static void +print_animation( gif_animation *anim ) +{ + int i; + + printf( "animation:\n" ); + printf( " width = %d\n", anim->width ); + printf( " height = %d\n", anim->height ); + printf( " frame_count = %d\n", anim->frame_count ); + printf( " frame_count_partial = %d\n", anim->frame_count_partial ); + printf( " decoded_frame = %d\n", anim->decoded_frame ); + printf( " frame_image = %p\n", anim->frame_image ); + printf( " loop_count = %d\n", anim->loop_count ); + printf( " frame_holders = %d\n", anim->frame_holders ); + printf( " background_index = %d\n", anim->background_index ); + printf( " colour_table_size = %d\n", anim->colour_table_size ); + printf( " global_colours = %d\n", anim->global_colours ); + printf( " global_colour_table = %p\n", anim->global_colour_table ); + printf( " local_colour_table = %p\n", anim->local_colour_table ); + + for( i = 0; i < anim->frame_count; i++ ) { + printf( "%d ", i ); + print_frame( &anim->frames[i] ); + } +} + +/* Scan the GIF as quickly as we can and extract transparency, bands, pages, + * etc. + * + * Don't flag any errors unless we have to: we want to work for corrupt or + * malformed GIFs. + */ +static int +vips_foreign_load_gif_scan_header( VipsForeignLoad *load ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( load ), 4 ); gif_result result; - VipsImage *im; - if( !(gif->gif = VIPS_NEW( load, gif_animation )) ) + VIPS_DEBUG_MSG( "vips_foreign_load_gif_scan_header:\n" ); + + if( !(gif->anim = VIPS_NEW( load, gif_animation )) ) return( -1 ); - gif_create( gif->gif, &vips_foreign_load_gif_bitmap_callbacks ); + gif_create( gif->anim, &vips_foreign_load_gif_bitmap_callbacks ); + print_animation( gif->anim ); + + result = gif_initialise( gif->anim, gif->size, gif->data ); + VIPS_DEBUG_MSG( " gif_initialise() = %d\n", result ); + print_animation( gif->anim ); + if( result != GIF_OK && + result != GIF_WORKING ) { + vips_error( class->nickname, "%s", + vips_foreign_load_gif_errstr( result ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +vips_foreign_load_gif_generate( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRect *r = &or->valid; + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a; + + gif_result result; /* Decode entire GIF. * @@ -247,11 +325,17 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) * */ do { - result = gif_initialise( gif->gif, gif->size, gif->data ); - if( result != GIF_OK && - result != GIF_WORKING ) - return 1; - } while( result != GIF_OK ); + result = gif_decode_frame( gif->anim, frame ); + VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n", + frame, result ); + if( result != GIF_OK ) { + vips_error( class->nickname, "%s", + vips_foreign_load_gif_errstr( result ) ); + return( -1 ); + } + + print_animation( gif->anim ); + } while( gif->anim->decoded_frame < gif->anim->frame_count - 1 ); /* Render from libnsgif memory areas into output image. */ @@ -259,6 +343,65 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) return( 0 ); } +static int +vips_foreign_load_gif_load( VipsForeignLoad *load ) +{ + VipsForeignLoadGifClass *class = + (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + VipsImage **t = (VipsImage **) + vips_object_local_array( VIPS_OBJECT( load ), 4 ); + + if( class->open( gif ) ) + return( -1 ); + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); + + /* Make the memory image we accumulate pixels in. We always accumulate + * to RGBA, then trim down to whatever the output image needs on + * _generate. + */ + gif->frame = vips_image_new_memory(); + vips_image_init_fields( gif->frame, + gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, + VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); + if( vips_image_write_prepare( gif->frame ) ) + return( -1 ); + + /* A copy of the previous state of the frame, in case we have to + * process a DISPOSE_PREVIOUS. + */ + gif->previous = vips_image_new_memory(); + vips_image_init_fields( gif->previous, + gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, + VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); + if( vips_image_write_prepare( gif->previous ) ) + return( -1 ); + + /* Make the output pipeline. + */ + t[0] = vips_image_new(); + if( vips_foreign_load_gif_set_header( gif, t[0] ) ) + return( -1 ); + + /* Close input immediately at end of read. + */ + g_signal_connect( t[0], "minimise", + G_CALLBACK( vips_foreign_load_gif_minimise ), gif ); + + /* Strips 8 pixels high to avoid too many tiny regions. + */ + if( vips_image_generate( t[0], + NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) || + vips_sequential( t[0], &t[1], + "tile_height", VIPS__FATSTRIP_HEIGHT, + NULL ) || + vips_image_write( t[1], load->real ) ) + return( -1 ); + + return( 0 ); +} + static void vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) { @@ -306,6 +449,12 @@ typedef struct _VipsForeignLoadGifFile { */ char *filename; + /* mmap here. + */ + int fd; + void *base; + gint64 length; + } VipsForeignLoadGifFile; typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass; @@ -313,26 +462,73 @@ typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass; G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file, vips_foreign_load_gif_get_type() ); +static void +vips_foreign_load_gif_file_dispose( GObject *gobject ) +{ + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; + VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject; + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_dispose:\n" ); + + G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> + dispose( gobject ); + + if( file->fd >= 0 ) { + (void) g_close( file->fd, NULL ); + file->fd = -1; + } + + if( file->base ) { + vips__munmap( file->base, file->length ); + file->base = NULL; + file->length = 0; + } +} + static int vips_foreign_load_gif_file_header( VipsForeignLoad *load ) { + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load; - /* mmap the file. + struct stat st; + mode_t m; + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_header:\n" ); + + /* Open and mmap the file. */ - if( vips_rawload( filename, &t[0], - out->Xsize, out->Ysize, VIPS_IMAGE_SIZEOF_PEL( out ), - "offset", header_offset, - NULL ) || + file->fd = vips__open_read( file->filename ); + if( file->fd == -1 ) { + vips_error_system( errno, class->nickname, + _( "unable to open file \"%s\" for reading" ), + file->filename ); + return( -1 ); + } + if( (file->length = vips_file_length( file->fd )) == -1 ) + return( -1 ); - - if( vips_foreign_load_gif_open( gif, file->filename ) ) + if( fstat( file->fd, &st ) == -1 ) { + vips_error( class->nickname, + "%s", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( !S_ISREG( m ) ) { + vips_error( class->nickname, + "%s", _( "not a regular file" ) ); return( -1 ); + } + + if( !(file->base = vips__mmap( file->fd, 0, file->length, 0 )) ) + return( -1 ); VIPS_SETSTR( load->out->filename, file->filename ); + gif->data = file->base; + gif->size = file->length; - return( vips_foreign_load_gif_load( load ) ); + return( vips_foreign_load_gif_scan_header( load ) ); } static const char *vips_foreign_gif_suffs[] = { @@ -349,6 +545,7 @@ vips_foreign_load_gif_file_class_init( VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + gobject_class->dispose = vips_foreign_load_gif_file_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -372,6 +569,7 @@ vips_foreign_load_gif_file_class_init( static void vips_foreign_load_gif_file_init( VipsForeignLoadGifFile *file ) { + file->fd = -1; } typedef struct _VipsForeignLoadGifBuffer { @@ -393,39 +591,18 @@ typedef VipsForeignLoadGifClass VipsForeignLoadGifBufferClass; G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer, vips_foreign_load_gif_get_type() ); -/* Callback from the gif loader. - * - * Read up to len bytes into buffer, return number of bytes read, 0 for EOF. - */ -static int -vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n ) -{ - VipsForeignLoadGifBuffer *buffer = - (VipsForeignLoadGifBuffer *) file->UserData; - size_t will_read = VIPS_MIN( n, buffer->bytes_to_go ); - - memcpy( buf, buffer->p, will_read ); - buffer->p += will_read; - buffer->bytes_to_go -= will_read; - - return( will_read ); -} - static int vips_foreign_load_gif_buffer_header( VipsForeignLoad *load ) { VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) load; - /* Init the read point. - */ - buffer->p = buffer->buf->data; - buffer->bytes_to_go = buffer->buf->length; + VIPS_DEBUG_MSG( "vips_foreign_load_gif_buffer_header:\n" ); - if( vips_foreign_load_gif_open_buffer( gif, vips_giflib_buffer_read ) ) - return( -1 ); + gif->data = buffer->buf->data; + gif->size = buffer->buf->length; - return( vips_foreign_load_gif_load( load ) ); + return( vips_foreign_load_gif_scan_header( load ) ); } static void From a0086e49cd67d6ec2b29688dcc3e402e958687fc Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 6 Sep 2019 16:47:35 +0100 Subject: [PATCH 03/20] works! but lots of TODO still --- libvips/foreign/gifnsload.c | 202 ++++++++++++++++++++++++++++-------- 1 file changed, 158 insertions(+), 44 deletions(-) diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index 14c5f297..c0549b1d 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -51,10 +51,33 @@ #include #include +/* TODO: + * + * - early close + * - detect greyscale images + * - detect images with transparent pixels + * - libnsgif does not seem to support comment metadata + */ + #ifdef HAVE_LIBNSGIF #include +#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_gif_get_type()) +#define VIPS_FOREIGN_LOAD_GIF( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGif )) +#define VIPS_FOREIGN_LOAD_GIF_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifClass)) +#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, VipsForeignLoadGifClass )) + typedef struct _VipsForeignLoadGif { VipsForeignLoad parent_object; @@ -75,6 +98,14 @@ typedef struct _VipsForeignLoadGif { unsigned char *data; size_t size; + /* Does this GIF have a non-greyscale colourmap? + */ + gboolean has_colour; + + /* Does this GIF have any transparent pixels? + */ + gboolean has_transparency; + } VipsForeignLoadGif; typedef VipsForeignLoadClass VipsForeignLoadGifClass; @@ -86,7 +117,7 @@ static const char * vips_foreign_load_gif_errstr( gif_result result ) { switch( result ) { - case GIF_WORKING: + case GIF_WORKING: return( _( "Working" ) ); case GIF_OK: @@ -118,6 +149,15 @@ vips_foreign_load_gif_errstr( gif_result result ) } } +static void +vips_foreign_load_gif_error( VipsForeignLoadGif *gif, gif_result result ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); + + vips_error( class->nickname, "%s", + vips_foreign_load_gif_errstr( result ) ); +} + static void vips_foreign_load_gif_dispose( GObject *gobject ) { @@ -266,12 +306,43 @@ print_animation( gif_animation *anim ) printf( " global_colour_table = %p\n", anim->global_colour_table ); printf( " local_colour_table = %p\n", anim->local_colour_table ); - for( i = 0; i < anim->frame_count; i++ ) { + for( i = 0; i < anim->frame_holders; i++ ) { printf( "%d ", i ); print_frame( &anim->frames[i] ); } } +static int +vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) +{ + vips_image_init_fields( image, + gif->anim->width, gif->anim->height * gif->n, + (gif->has_colour ? 3 : 1) + (gif->has_transparency ? 1 : 0), + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, + gif->has_colour ? + VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); + + if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) + vips_image_set_int( image, + VIPS_META_PAGE_HEIGHT, gif->anim->height ); + vips_image_set_int( image, VIPS_META_N_PAGES, gif->anim->frame_count ); + vips_image_set_int( image, "gif-loop", gif->anim->loop_count ); + + /* The deprecated gif-delay field is in centiseconds. + */ + vips_image_set_int( image, + "gif-delay", gif->anim->frames[0].frame_delay ); + + /* TODO make and set the "delay" array in ms + vips_image_set_array_int( image, + "delay", gif->delays, gif->n_pages ); + */ + + return( 0 ); +} + /* Scan the GIF as quickly as we can and extract transparency, bands, pages, * etc. * @@ -279,31 +350,56 @@ print_animation( gif_animation *anim ) * malformed GIFs. */ static int -vips_foreign_load_gif_scan_header( VipsForeignLoad *load ) +vips_foreign_load_gif_header( VipsForeignLoad *load ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; - VipsImage **t = (VipsImage **) - vips_object_local_array( VIPS_OBJECT( load ), 4 ); gif_result result; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_scan_header:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gif_header:\n" ); if( !(gif->anim = VIPS_NEW( load, gif_animation )) ) return( -1 ); gif_create( gif->anim, &vips_foreign_load_gif_bitmap_callbacks ); - print_animation( gif->anim ); + /* GIF_INSUFFICIENT_FRAME_DATA means we allow truncated GIFs. + * + * TODO use fail to bail out on truncvated GIFs. + */ result = gif_initialise( gif->anim, gif->size, gif->data ); - VIPS_DEBUG_MSG( " gif_initialise() = %d\n", result ); + VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); print_animation( gif->anim ); if( result != GIF_OK && - result != GIF_WORKING ) { - vips_error( class->nickname, "%s", - vips_foreign_load_gif_errstr( result ) ); + result != GIF_WORKING && + result != GIF_INSUFFICIENT_FRAME_DATA ) { + vips_foreign_load_gif_error( gif, result ); return( -1 ); } + if( !gif->anim->frame_count ) { + vips_error( class->nickname, "%s", _( "no frames in GIF" ) ); + return( -1 ); + } + + /* TODO test for a non-greyscale colourmap. + map = file->Image.ColorMap ? file->Image.ColorMap : file->SColorMap; + if( !gif->has_colour && + map ) { + 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 ) { + gif->has_colour = TRUE; + break; + } + } + */ + + /* TODO test for transparency. + */ + + vips_foreign_load_gif_set_header( gif, load->out ); return( 0 ); } @@ -315,30 +411,43 @@ vips_foreign_load_gif_generate( VipsRegion *or, VipsRect *r = &or->valid; VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a; - gif_result result; + int y; - /* Decode entire GIF. - * - * FIXME ... add progressive decode. - * - * FIXME ... only decode as far as we need for the selected page - * - */ - do { - result = gif_decode_frame( gif->anim, frame ); - VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n", - frame, result ); - if( result != GIF_OK ) { - vips_error( class->nickname, "%s", - vips_foreign_load_gif_errstr( result ) ); - return( -1 ); + VIPS_DEBUG_MSG( "vips_foreign_load_gif_generate: " + "top = %d, height = %d\n", r->top, r->height ); + + for( y = 0; y < r->height; y++ ) { + /* The page for this output line, and the line number in page. + */ + int page = (r->top + y) / gif->anim->height + gif->page; + int line = (r->top + y) % gif->anim->height; + + gif_result result; + VipsPel *p, *q; + + g_assert( line >= 0 && line < gif->anim->height ); + g_assert( page >= 0 && page < gif->anim->frame_count ); + + /* TODO + * + * Do we need to loop to read earlier pages? + */ + if( gif->anim->decoded_frame != page ) { + result = gif_decode_frame( gif->anim, page ); + VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n", + page, result ); + if( result != GIF_OK ) { + vips_foreign_load_gif_error( gif, result ); + return( -1 ); + } + print_animation( gif->anim ); } - print_animation( gif->anim ); - } while( gif->anim->decoded_frame < gif->anim->frame_count - 1 ); - - /* Render from libnsgif memory areas into output image. - */ + p = gif->anim->frame_image + + line * gif->anim->width * sizeof( int ); + q = VIPS_REGION_ADDR( or, 0, r->top + y ); + memcpy( q, p, VIPS_REGION_SIZEOF_LINE( or ) ); + } return( 0 ); } @@ -346,37 +455,32 @@ vips_foreign_load_gif_generate( VipsRegion *or, static int vips_foreign_load_gif_load( VipsForeignLoad *load ) { - VipsForeignLoadGifClass *class = - (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( load ), 4 ); - if( class->open( gif ) ) - return( -1 ); - VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); /* Make the memory image we accumulate pixels in. We always accumulate * to RGBA, then trim down to whatever the output image needs on * _generate. - */ gif->frame = vips_image_new_memory(); vips_image_init_fields( gif->frame, gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); if( vips_image_write_prepare( gif->frame ) ) return( -1 ); + */ /* A copy of the previous state of the frame, in case we have to * process a DISPOSE_PREVIOUS. - */ gif->previous = vips_image_new_memory(); vips_image_init_fields( gif->previous, gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); if( vips_image_write_prepare( gif->previous ) ) return( -1 ); + */ /* Make the output pipeline. */ @@ -384,10 +488,12 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) if( vips_foreign_load_gif_set_header( gif, t[0] ) ) return( -1 ); - /* Close input immediately at end of read. - */ + /* TODO + * + * Close input immediately at end of read. g_signal_connect( t[0], "minimise", G_CALLBACK( vips_foreign_load_gif_minimise ), gif ); + */ /* Strips 8 pixels high to avoid too many tiny regions. */ @@ -419,6 +525,8 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) load_class->get_flags_filename = vips_foreign_load_gif_get_flags_filename; load_class->get_flags = vips_foreign_load_gif_get_flags; + load_class->header = vips_foreign_load_gif_header; + load_class->load = vips_foreign_load_gif_load; VIPS_ARG_INT( class, "page", 10, _( "Page" ), @@ -440,6 +548,11 @@ static void vips_foreign_load_gif_init( VipsForeignLoadGif *gif ) { gif->n = 1; + + /* TODO always read RGBA for now. + */ + gif->has_colour = TRUE; + gif->has_transparency = TRUE; } typedef struct _VipsForeignLoadGifFile { @@ -465,7 +578,6 @@ G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file, static void vips_foreign_load_gif_file_dispose( GObject *gobject ) { - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject; VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_dispose:\n" ); @@ -528,7 +640,8 @@ vips_foreign_load_gif_file_header( VipsForeignLoad *load ) gif->data = file->base; gif->size = file->length; - return( vips_foreign_load_gif_scan_header( load ) ); + return( VIPS_FOREIGN_LOAD_GIF_CLASS( + vips_foreign_load_gif_file_parent_class )->header( load ) ); } static const char *vips_foreign_gif_suffs[] = { @@ -602,7 +715,8 @@ vips_foreign_load_gif_buffer_header( VipsForeignLoad *load ) gif->data = buffer->buf->data; gif->size = buffer->buf->length; - return( vips_foreign_load_gif_scan_header( load ) ); + return( VIPS_FOREIGN_LOAD_GIF_CLASS( + vips_foreign_load_gif_file_parent_class )->header( load ) ); } static void From ae680a8d8cd7506c6afe82bcfd3e80a76cffadd0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 6 Sep 2019 17:24:55 +0100 Subject: [PATCH 04/20] make giflib load less fussy it was refusing to load a few GIFs --- libvips/foreign/gifload.c | 43 ++++++++--------- libvips/foreign/gifnsload.c | 92 ++++++++++++++----------------------- 2 files changed, 53 insertions(+), 82 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 68ef31a2..9a59c1df 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -824,19 +824,18 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif ) for( y = InterlacedOffset[i]; y < file->Image.Height; - y += InterlacedJumps[i] ) { - VipsPel *q = VIPS_IMAGE_ADDR( gif->frame, - file->Image.Left, file->Image.Top + y ); - + y += InterlacedJumps[i] ) if( DGifGetLine( gif->file, gif->line, file->Image.Width ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } + VipsPel *q = VIPS_IMAGE_ADDR( + gif->frame, + file->Image.Left, + file->Image.Top + y ); - vips_foreign_load_gif_render_line( gif, - file->Image.Width, q, gif->line ); - } + vips_foreign_load_gif_render_line( gif, + file->Image.Width, q, + gif->line ); + } } } else { @@ -847,19 +846,15 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif ) file->Image.Width, file->Image.Height, file->Image.Left, file->Image.Top ); - for( y = 0; y < file->Image.Height; y++ ) { - VipsPel *q = VIPS_IMAGE_ADDR( gif->frame, - file->Image.Left, file->Image.Top + y ); - + for( y = 0; y < file->Image.Height; y++ ) if( DGifGetLine( gif->file, gif->line, - file->Image.Width ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } + file->Image.Width ) != GIF_ERROR ) { + VipsPel *q = VIPS_IMAGE_ADDR( gif->frame, + file->Image.Left, file->Image.Top + y ); - vips_foreign_load_gif_render_line( gif, - file->Image.Width, q, gif->line ); - } + vips_foreign_load_gif_render_line( gif, + file->Image.Width, q, gif->line ); + } } return( 0 ); @@ -915,10 +910,8 @@ vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif ) have_read_frame = FALSE; do { - if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } + if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) + continue; switch( record ) { case IMAGE_DESC_RECORD_TYPE: diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index c0549b1d..d4a9fd7c 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -32,6 +32,7 @@ */ /* +#define VERBOSE */ #define VIPS_DEBUG @@ -52,11 +53,23 @@ #include /* TODO: + * + * - look into frame / page match up ... what about optimised GIFs where + * several frames make up each page? * * - early close - * - detect greyscale images - * - detect images with transparent pixels + * * - libnsgif does not seem to support comment metadata + * + * Notes: + * + * - 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 + * + * - don't bother detecting alpha -- if we can't detect RGB, alpha won't help + * much + * */ #ifdef HAVE_LIBNSGIF @@ -98,14 +111,6 @@ typedef struct _VipsForeignLoadGif { unsigned char *data; size_t size; - /* Does this GIF have a non-greyscale colourmap? - */ - gboolean has_colour; - - /* Does this GIF have any transparent pixels? - */ - gboolean has_transparency; - } VipsForeignLoadGif; typedef VipsForeignLoadClass VipsForeignLoadGifClass; @@ -268,6 +273,7 @@ static gif_bitmap_callback_vt vips_foreign_load_gif_bitmap_callbacks = { vips_foreign_load_gif_bitmap_modified }; +#ifdef VERBOSE static void print_frame( gif_frame *frame ) { @@ -311,17 +317,15 @@ print_animation( gif_animation *anim ) print_frame( &anim->frames[i] ); } } +#endif /*VERBOSE*/ static int vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) { vips_image_init_fields( image, - gif->anim->width, gif->anim->height * gif->n, - (gif->has_colour ? 3 : 1) + (gif->has_transparency ? 1 : 0), + gif->anim->width, gif->anim->height * gif->n, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, - gif->has_colour ? - VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_B_W, - 1.0, 1.0 ); + VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) @@ -365,11 +369,13 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) /* GIF_INSUFFICIENT_FRAME_DATA means we allow truncated GIFs. * - * TODO use fail to bail out on truncvated GIFs. + * TODO use fail to bail out on truncated GIFs. */ result = gif_initialise( gif->anim, gif->size, gif->data ); VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); +#ifdef VERBOSE print_animation( gif->anim ); +#endif /*VERBOSE*/ if( result != GIF_OK && result != GIF_WORKING && result != GIF_INSUFFICIENT_FRAME_DATA ) { @@ -381,23 +387,15 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) return( -1 ); } - /* TODO test for a non-greyscale colourmap. - map = file->Image.ColorMap ? file->Image.ColorMap : file->SColorMap; - if( !gif->has_colour && - map ) { - int i; + if( gif->n == -1 ) + gif->n = gif->anim->frame_count - gif->page; - for( i = 0; i < map->ColorCount; i++ ) - if( map->Colors[i].Red != map->Colors[i].Green || - map->Colors[i].Green != map->Colors[i].Blue ) { - gif->has_colour = TRUE; - break; - } + if( gif->page < 0 || + gif->n <= 0 || + gif->page + gif->n > gif->anim->frame_count ) { + vips_error( class->nickname, "%s", _( "bad page number" ) ); + return( -1 ); } - */ - - /* TODO test for transparency. - */ vips_foreign_load_gif_set_header( gif, load->out ); @@ -413,8 +411,10 @@ vips_foreign_load_gif_generate( VipsRegion *or, int y; +#ifdef VERBOSE VIPS_DEBUG_MSG( "vips_foreign_load_gif_generate: " "top = %d, height = %d\n", r->top, r->height ); +#endif /*VERBOSE*/ for( y = 0; y < r->height; y++ ) { /* The page for this output line, and the line number in page. @@ -440,9 +440,13 @@ vips_foreign_load_gif_generate( VipsRegion *or, vips_foreign_load_gif_error( gif, result ); return( -1 ); } +#ifdef VERBOSE print_animation( gif->anim ); +#endif /*VERBOSE*/ } + /* TODO ... handle G, GA, RGB cases as well. + */ p = gif->anim->frame_image + line * gif->anim->width * sizeof( int ); q = VIPS_REGION_ADDR( or, 0, r->top + y ); @@ -461,27 +465,6 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); - /* Make the memory image we accumulate pixels in. We always accumulate - * to RGBA, then trim down to whatever the output image needs on - * _generate. - gif->frame = vips_image_new_memory(); - vips_image_init_fields( gif->frame, - gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, - VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); - if( vips_image_write_prepare( gif->frame ) ) - return( -1 ); - */ - - /* A copy of the previous state of the frame, in case we have to - * process a DISPOSE_PREVIOUS. - gif->previous = vips_image_new_memory(); - vips_image_init_fields( gif->previous, - gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, - VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); - if( vips_image_write_prepare( gif->previous ) ) - return( -1 ); - */ - /* Make the output pipeline. */ t[0] = vips_image_new(); @@ -548,11 +531,6 @@ static void vips_foreign_load_gif_init( VipsForeignLoadGif *gif ) { gif->n = 1; - - /* TODO always read RGBA for now. - */ - gif->has_colour = TRUE; - gif->has_transparency = TRUE; } typedef struct _VipsForeignLoadGifFile { From 28434b4e15a1c029d4092dd5c0c5162979d90846 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 7 Sep 2019 12:13:25 +0100 Subject: [PATCH 05/20] add delay array support to gifnsload --- libvips/foreign/gifnsload.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index d4a9fd7c..f7b93894 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -111,6 +111,10 @@ typedef struct _VipsForeignLoadGif { unsigned char *data; size_t size; + /* Delays between frames (in milliseconds). Array of length @n. + */ + int *delays; + } VipsForeignLoadGif; typedef VipsForeignLoadClass VipsForeignLoadGifClass; @@ -171,6 +175,7 @@ vips_foreign_load_gif_dispose( GObject *gobject ) VIPS_DEBUG_MSG( "vips_foreign_load_gif_dispose:\n" ); VIPS_FREEF( gif_finalise, gif->anim ); + VIPS_FREE( gif->delays ); G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> dispose( gobject ); @@ -339,6 +344,8 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) vips_image_set_int( image, "gif-delay", gif->anim->frames[0].frame_delay ); + vips_image_set_array_int( image, "delay", gif->delays, gif->n ); + /* TODO make and set the "delay" array in ms vips_image_set_array_int( image, "delay", gif->delays, gif->n_pages ); @@ -360,6 +367,7 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; gif_result result; + int i; VIPS_DEBUG_MSG( "vips_foreign_load_gif_header:\n" ); @@ -397,6 +405,14 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) return( -1 ); } + /* In ms, frame_delay in cs. + */ + if( !(gif->delays = VIPS_ARRAY( NULL, gif->n, int )) ) + return( -1 ); + for( i = 0; i < gif->n; i++ ) + gif->delays[i] = + 10 * gif->anim->frames[gif->page + i].frame_delay; + vips_foreign_load_gif_set_header( gif, load->out ); return( 0 ); From 1a801c28388d1d8c1c307e67b07781ccd7cb2f3e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 7 Sep 2019 15:44:50 +0100 Subject: [PATCH 06/20] note early close vfuncs --- libvips/foreign/gifnsload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index f7b93894..3d7f5854 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -57,7 +57,7 @@ * - look into frame / page match up ... what about optimised GIFs where * several frames make up each page? * - * - early close + * - early close ... I guess we need open/close vfuncs. * * - libnsgif does not seem to support comment metadata * From c0aa4153e72766736ac7cb7a6f61119b38b6da15 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 15 Sep 2019 17:08:48 +0100 Subject: [PATCH 07/20] add close early to loadnsgif --- libvips/foreign/gifload.c | 4 +- libvips/foreign/gifnsload.c | 731 ++++++++++++++++++++---------------- 2 files changed, 401 insertions(+), 334 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 9a59c1df..03115a80 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -1053,11 +1053,11 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( load ), 4 ); + VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); + if( class->open( gif ) ) return( -1 ); - VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); - /* Make the memory image we accumulate pixels in. We always accumulate * to RGBA, then trim down to whatever the output image needs on * _generate. diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index 3d7f5854..1f2c013c 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -57,8 +57,6 @@ * - look into frame / page match up ... what about optimised GIFs where * several frames make up each page? * - * - early close ... I guess we need open/close vfuncs. - * * - libnsgif does not seem to support comment metadata * * Notes: @@ -113,11 +111,25 @@ typedef struct _VipsForeignLoadGif { /* Delays between frames (in milliseconds). Array of length @n. */ - int *delays; + int *delay; + + /* A single centisecond value for compatibility. + */ + int gif_delay; } VipsForeignLoadGif; -typedef VipsForeignLoadClass VipsForeignLoadGifClass; +typedef struct _VipsForeignLoadGifClass { + VipsForeignLoadClass parent_class; + + /* Close and reopen. + */ + int (*open)( VipsForeignLoadGif *gif ); + + /* Close any underlying file resource. + */ + void (*close)( VipsForeignLoadGif *gif ); +} VipsForeignLoadGifClass; G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif, VIPS_TYPE_FOREIGN_LOAD ); @@ -171,11 +183,16 @@ static void vips_foreign_load_gif_dispose( GObject *gobject ) { VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; + VipsForeignLoadGifClass *class = VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); VIPS_DEBUG_MSG( "vips_foreign_load_gif_dispose:\n" ); - VIPS_FREEF( gif_finalise, gif->anim ); - VIPS_FREE( gif->delays ); + class->close( gif ); + if( gif->anim ) { + gif_finalise( gif->anim ); + VIPS_FREE( gif->anim ); + } + VIPS_FREE( gif->delay ); G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> dispose( gobject ); @@ -222,6 +239,302 @@ vips_foreign_load_gif_is_a( const char *filename ) return( 0 ); } +#ifdef VERBOSE +static void +print_frame( gif_frame *frame ) +{ + printf( "frame:\n" ); + printf( " display = %d\n", frame->display ); + printf( " frame_delay = %d\n", frame->frame_delay ); + printf( " virgin = %d\n", frame->virgin ); + printf( " opaque = %d\n", frame->opaque ); + printf( " redraw_required = %d\n", frame->redraw_required ); + printf( " disposal_method = %d\n", frame->disposal_method ); + printf( " transparency = %d\n", frame->transparency ); + printf( " transparency_index = %d\n", frame->transparency_index ); + printf( " redraw_x = %d\n", frame->redraw_x ); + printf( " redraw_y = %d\n", frame->redraw_y ); + printf( " redraw_width = %d\n", frame->redraw_width ); + printf( " redraw_height = %d\n", frame->redraw_height ); +} + +static void +print_animation( gif_animation *anim ) +{ + int i; + + printf( "animation:\n" ); + printf( " width = %d\n", anim->width ); + printf( " height = %d\n", anim->height ); + printf( " frame_count = %d\n", anim->frame_count ); + printf( " frame_count_partial = %d\n", anim->frame_count_partial ); + printf( " decoded_frame = %d\n", anim->decoded_frame ); + printf( " frame_image = %p\n", anim->frame_image ); + printf( " loop_count = %d\n", anim->loop_count ); + printf( " frame_holders = %d\n", anim->frame_holders ); + printf( " background_index = %d\n", anim->background_index ); + printf( " colour_table_size = %d\n", anim->colour_table_size ); + printf( " global_colours = %d\n", anim->global_colours ); + printf( " global_colour_table = %p\n", anim->global_colour_table ); + printf( " local_colour_table = %p\n", anim->local_colour_table ); + + for( i = 0; i < anim->frame_holders; i++ ) { + printf( "%d ", i ); + print_frame( &anim->frames[i] ); + } +} +#endif /*VERBOSE*/ + +static int +vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) +{ + VIPS_DEBUG_MSG( "vips_foreign_load_gif_set_header:\n" ); + + vips_image_init_fields( image, + gif->anim->width, gif->anim->height * gif->n, 4, + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, + VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); + vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); + + if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) + vips_image_set_int( image, + VIPS_META_PAGE_HEIGHT, gif->anim->height ); + vips_image_set_int( image, VIPS_META_N_PAGES, gif->anim->frame_count ); + vips_image_set_int( image, "gif-loop", gif->anim->loop_count ); + + vips_image_set_array_int( image, "delay", gif->delay, gif->n ); + + /* The deprecated gif-delay field is in centiseconds. + */ + vips_image_set_int( image, "gif-delay", gif->gif_delay ); + + return( 0 ); +} + +static int +vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); + + gif_result result; + int i; + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_open:\n" ); + + /* GIF_INSUFFICIENT_FRAME_DATA means we allow truncated GIFs. + * + * TODO use fail to bail out on truncated GIFs. + */ + result = gif_initialise( gif->anim, gif->size, gif->data ); + VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); +#ifdef VERBOSE + print_animation( gif->anim ); +#endif /*VERBOSE*/ + if( result != GIF_OK && + result != GIF_WORKING && + result != GIF_INSUFFICIENT_FRAME_DATA ) { + vips_foreign_load_gif_error( gif, result ); + return( -1 ); + } + if( !gif->anim->frame_count ) { + vips_error( class->nickname, "%s", _( "no frames in GIF" ) ); + return( -1 ); + } + + if( gif->n == -1 ) + gif->n = gif->anim->frame_count - gif->page; + + if( gif->page < 0 || + gif->n <= 0 || + gif->page + gif->n > gif->anim->frame_count ) { + vips_error( class->nickname, "%s", _( "bad page number" ) ); + return( -1 ); + } + + /* In ms, frame_delay in cs. + */ + if( !(gif->delay = VIPS_ARRAY( NULL, gif->n, int )) ) + return( -1 ); + for( i = 0; i < gif->n; i++ ) + gif->delay[i] = + 10 * gif->anim->frames[gif->page + i].frame_delay; + + gif->gif_delay = gif->anim->frames[0].frame_delay; + + return( 0 ); +} + +static void +vips_foreign_load_gif_close( VipsForeignLoadGif *gif ) +{ + /* Don't call gif_finalise(): that just frees memory attached to the + * gif struct, and gif_initialise() hates being called again after + * that. + */ +} + +/* Scan the GIF as quickly as we can and extract transparency, bands, pages, + * etc. + * + * Don't flag any errors unless we have to: we want to work for corrupt or + * malformed GIFs. + * + * Close as soon as we can to free up the fd. + */ +static int +vips_foreign_load_gif_header( VipsForeignLoad *load ) +{ + VipsForeignLoadGifClass *class = + VIPS_FOREIGN_LOAD_GIF_GET_CLASS( load ); + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_header:\n" ); + + if( class->open( gif ) ) { + class->close( gif ); + return( -1 ); + } + class->close( gif ); + + vips_foreign_load_gif_set_header( gif, load->out ); + + return( 0 ); +} + +static void +vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif ) +{ + VipsForeignLoadGifClass *class = VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_minimise:\n" ); + + class->close( gif ); +} + +static int +vips_foreign_load_gif_generate( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRect *r = &or->valid; + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a; + + int y; + +#ifdef VERBOSE + VIPS_DEBUG_MSG( "vips_foreign_load_gif_generate: " + "top = %d, height = %d\n", r->top, r->height ); +#endif /*VERBOSE*/ + + for( y = 0; y < r->height; y++ ) { + /* The page for this output line, and the line number in page. + */ + int page = (r->top + y) / gif->anim->height + gif->page; + int line = (r->top + y) % gif->anim->height; + + gif_result result; + VipsPel *p, *q; + + g_assert( line >= 0 && line < gif->anim->height ); + g_assert( page >= 0 && page < gif->anim->frame_count ); + + if( gif->anim->decoded_frame != page ) { + result = gif_decode_frame( gif->anim, page ); + VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n", + page, result ); + if( result != GIF_OK ) { + vips_foreign_load_gif_error( gif, result ); + return( -1 ); + } +#ifdef VERBOSE + print_animation( gif->anim ); +#endif /*VERBOSE*/ + } + + p = gif->anim->frame_image + + line * gif->anim->width * sizeof( int ); + q = VIPS_REGION_ADDR( or, 0, r->top + y ); + memcpy( q, p, VIPS_REGION_SIZEOF_LINE( or ) ); + } + + return( 0 ); +} + +static int +vips_foreign_load_gif_load( VipsForeignLoad *load ) +{ + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + VipsForeignLoadGifClass *class = VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); + VipsImage **t = (VipsImage **) + vips_object_local_array( VIPS_OBJECT( load ), 4 ); + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); + + if( class->open( gif ) ) + return( -1 ); + + /* Make the output pipeline. + */ + t[0] = vips_image_new(); + if( vips_foreign_load_gif_set_header( gif, t[0] ) ) + return( -1 ); + + /* Close immediately at end of read. + */ + g_signal_connect( t[0], "minimise", + G_CALLBACK( vips_foreign_load_gif_minimise ), gif ); + + /* Strips 8 pixels high to avoid too many tiny regions. + */ + if( vips_image_generate( t[0], + NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) || + vips_sequential( t[0], &t[1], + "tile_height", VIPS__FATSTRIP_HEIGHT, + NULL ) || + vips_image_write( t[1], load->real ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) +{ + 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; + load_class->header = vips_foreign_load_gif_header; + load_class->load = vips_foreign_load_gif_load; + + class->open = vips_foreign_load_gif_open; + class->close = vips_foreign_load_gif_close; + + 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_bitmap_create( int width, int height ) { @@ -278,274 +591,11 @@ static gif_bitmap_callback_vt vips_foreign_load_gif_bitmap_callbacks = { vips_foreign_load_gif_bitmap_modified }; -#ifdef VERBOSE -static void -print_frame( gif_frame *frame ) -{ - printf( "frame:\n" ); - printf( " display = %d\n", frame->display ); - printf( " frame_delay = %d\n", frame->frame_delay ); - printf( " virgin = %d\n", frame->virgin ); - printf( " opaque = %d\n", frame->opaque ); - printf( " redraw_required = %d\n", frame->redraw_required ); - printf( " disposal_method = %d\n", frame->disposal_method ); - printf( " transparency = %d\n", frame->transparency ); - printf( " transparency_index = %d\n", frame->transparency_index ); - printf( " redraw_x = %d\n", frame->redraw_x ); - printf( " redraw_y = %d\n", frame->redraw_y ); - printf( " redraw_width = %d\n", frame->redraw_width ); - printf( " redraw_height = %d\n", frame->redraw_height ); -} - -static void -print_animation( gif_animation *anim ) -{ - int i; - - printf( "animation:\n" ); - printf( " width = %d\n", anim->width ); - printf( " height = %d\n", anim->height ); - printf( " frame_count = %d\n", anim->frame_count ); - printf( " frame_count_partial = %d\n", anim->frame_count_partial ); - printf( " decoded_frame = %d\n", anim->decoded_frame ); - printf( " frame_image = %p\n", anim->frame_image ); - printf( " loop_count = %d\n", anim->loop_count ); - printf( " frame_holders = %d\n", anim->frame_holders ); - printf( " background_index = %d\n", anim->background_index ); - printf( " colour_table_size = %d\n", anim->colour_table_size ); - printf( " global_colours = %d\n", anim->global_colours ); - printf( " global_colour_table = %p\n", anim->global_colour_table ); - printf( " local_colour_table = %p\n", anim->local_colour_table ); - - for( i = 0; i < anim->frame_holders; i++ ) { - printf( "%d ", i ); - print_frame( &anim->frames[i] ); - } -} -#endif /*VERBOSE*/ - -static int -vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) -{ - vips_image_init_fields( image, - gif->anim->width, gif->anim->height * gif->n, 4, - VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, - VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); - vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); - - if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) - vips_image_set_int( image, - VIPS_META_PAGE_HEIGHT, gif->anim->height ); - vips_image_set_int( image, VIPS_META_N_PAGES, gif->anim->frame_count ); - vips_image_set_int( image, "gif-loop", gif->anim->loop_count ); - - /* The deprecated gif-delay field is in centiseconds. - */ - vips_image_set_int( image, - "gif-delay", gif->anim->frames[0].frame_delay ); - - vips_image_set_array_int( image, "delay", gif->delays, gif->n ); - - /* TODO make and set the "delay" array in ms - vips_image_set_array_int( image, - "delay", gif->delays, gif->n_pages ); - */ - - return( 0 ); -} - -/* Scan the GIF as quickly as we can and extract transparency, bands, pages, - * etc. - * - * Don't flag any errors unless we have to: we want to work for corrupt or - * malformed GIFs. - */ -static int -vips_foreign_load_gif_header( VipsForeignLoad *load ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; - - gif_result result; - int i; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_header:\n" ); - - if( !(gif->anim = VIPS_NEW( load, gif_animation )) ) - return( -1 ); - gif_create( gif->anim, &vips_foreign_load_gif_bitmap_callbacks ); - - /* GIF_INSUFFICIENT_FRAME_DATA means we allow truncated GIFs. - * - * TODO use fail to bail out on truncated GIFs. - */ - result = gif_initialise( gif->anim, gif->size, gif->data ); - VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); -#ifdef VERBOSE - print_animation( gif->anim ); -#endif /*VERBOSE*/ - if( result != GIF_OK && - result != GIF_WORKING && - result != GIF_INSUFFICIENT_FRAME_DATA ) { - vips_foreign_load_gif_error( gif, result ); - return( -1 ); - } - if( !gif->anim->frame_count ) { - vips_error( class->nickname, "%s", _( "no frames in GIF" ) ); - return( -1 ); - } - - if( gif->n == -1 ) - gif->n = gif->anim->frame_count - gif->page; - - if( gif->page < 0 || - gif->n <= 0 || - gif->page + gif->n > gif->anim->frame_count ) { - vips_error( class->nickname, "%s", _( "bad page number" ) ); - return( -1 ); - } - - /* In ms, frame_delay in cs. - */ - if( !(gif->delays = VIPS_ARRAY( NULL, gif->n, int )) ) - return( -1 ); - for( i = 0; i < gif->n; i++ ) - gif->delays[i] = - 10 * gif->anim->frames[gif->page + i].frame_delay; - - vips_foreign_load_gif_set_header( gif, load->out ); - - return( 0 ); -} - -static int -vips_foreign_load_gif_generate( VipsRegion *or, - void *seq, void *a, void *b, gboolean *stop ) -{ - VipsRect *r = &or->valid; - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a; - - int y; - -#ifdef VERBOSE - VIPS_DEBUG_MSG( "vips_foreign_load_gif_generate: " - "top = %d, height = %d\n", r->top, r->height ); -#endif /*VERBOSE*/ - - for( y = 0; y < r->height; y++ ) { - /* The page for this output line, and the line number in page. - */ - int page = (r->top + y) / gif->anim->height + gif->page; - int line = (r->top + y) % gif->anim->height; - - gif_result result; - VipsPel *p, *q; - - g_assert( line >= 0 && line < gif->anim->height ); - g_assert( page >= 0 && page < gif->anim->frame_count ); - - /* TODO - * - * Do we need to loop to read earlier pages? - */ - if( gif->anim->decoded_frame != page ) { - result = gif_decode_frame( gif->anim, page ); - VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n", - page, result ); - if( result != GIF_OK ) { - vips_foreign_load_gif_error( gif, result ); - return( -1 ); - } -#ifdef VERBOSE - print_animation( gif->anim ); -#endif /*VERBOSE*/ - } - - /* TODO ... handle G, GA, RGB cases as well. - */ - p = gif->anim->frame_image + - line * gif->anim->width * sizeof( int ); - q = VIPS_REGION_ADDR( or, 0, r->top + y ); - memcpy( q, p, VIPS_REGION_SIZEOF_LINE( or ) ); - } - - 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 ); - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); - - /* Make the output pipeline. - */ - t[0] = vips_image_new(); - if( vips_foreign_load_gif_set_header( gif, t[0] ) ) - return( -1 ); - - /* TODO - * - * Close input immediately at end of read. - g_signal_connect( t[0], "minimise", - G_CALLBACK( vips_foreign_load_gif_minimise ), gif ); - */ - - /* Strips 8 pixels high to avoid too many tiny regions. - */ - if( vips_image_generate( t[0], - NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) || - vips_sequential( t[0], &t[1], - "tile_height", VIPS__FATSTRIP_HEIGHT, - NULL ) || - vips_image_write( t[1], load->real ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) -{ - 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; - load_class->header = vips_foreign_load_gif_header; - load_class->load = vips_foreign_load_gif_load; - - 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->anim = g_new0( gif_animation, 1 ); + gif_create( gif->anim, &vips_foreign_load_gif_bitmap_callbacks ); gif->n = 1; } @@ -569,15 +619,79 @@ typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass; G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file, vips_foreign_load_gif_get_type() ); -static void -vips_foreign_load_gif_file_dispose( GObject *gobject ) +static const char *vips_foreign_gif_suffs[] = { + ".gif", + NULL +}; + +static int +vips_foreign_load_gif_file_header( VipsForeignLoad *load ) { - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject; + VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_dispose:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_header:\n" ); - G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> - dispose( gobject ); + VIPS_SETSTR( load->out->filename, file->filename ); + + return( VIPS_FOREIGN_LOAD_CLASS( + vips_foreign_load_gif_file_parent_class )->header( load ) ); +} + +static int +vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); + VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_open:\n" ); + + if( file->fd == -1 ) { + struct stat st; + mode_t m; + + g_assert( gif->filename ); + g_assert( !gif->data ); + g_assert( !gif->size ); + + file->fd = vips__open_read( file->filename ); + if( file->fd == -1 ) { + vips_error_system( errno, class->nickname, + _( "unable to open file \"%s\" for reading" ), + file->filename ); + return( -1 ); + } + if( (file->length = vips_file_length( file->fd )) == -1 ) + return( -1 ); + + if( fstat( file->fd, &st ) == -1 ) { + vips_error( class->nickname, + "%s", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( !S_ISREG( m ) ) { + vips_error( class->nickname, + "%s", _( "not a regular file" ) ); + return( -1 ); + } + + if( !(file->base = vips__mmap( file->fd, 0, file->length, 0 )) ) + return( -1 ); + + gif->data = file->base; + gif->size = file->length; + } + + return( VIPS_FOREIGN_LOAD_GIF_CLASS( + vips_foreign_load_gif_file_parent_class )->open( gif ) ); +} + +static void +vips_foreign_load_gif_file_close( VipsForeignLoadGif *gif ) +{ + VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; + + VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_close:\n" ); if( file->fd >= 0 ) { (void) g_close( file->fd, NULL ); @@ -589,60 +703,11 @@ vips_foreign_load_gif_file_dispose( GObject *gobject ) file->base = NULL; file->length = 0; } + + VIPS_FOREIGN_LOAD_GIF_CLASS( + vips_foreign_load_gif_file_parent_class )->close( gif ); } -static int -vips_foreign_load_gif_file_header( VipsForeignLoad *load ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load; - - struct stat st; - mode_t m; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_header:\n" ); - - /* Open and mmap the file. - */ - file->fd = vips__open_read( file->filename ); - if( file->fd == -1 ) { - vips_error_system( errno, class->nickname, - _( "unable to open file \"%s\" for reading" ), - file->filename ); - return( -1 ); - } - if( (file->length = vips_file_length( file->fd )) == -1 ) - return( -1 ); - - if( fstat( file->fd, &st ) == -1 ) { - vips_error( class->nickname, - "%s", _( "unable to get file status" ) ); - return( -1 ); - } - m = (mode_t) st.st_mode; - if( !S_ISREG( m ) ) { - vips_error( class->nickname, - "%s", _( "not a regular file" ) ); - return( -1 ); - } - - if( !(file->base = vips__mmap( file->fd, 0, file->length, 0 )) ) - return( -1 ); - - VIPS_SETSTR( load->out->filename, file->filename ); - gif->data = file->base; - gif->size = file->length; - - return( VIPS_FOREIGN_LOAD_GIF_CLASS( - vips_foreign_load_gif_file_parent_class )->header( load ) ); -} - -static const char *vips_foreign_gif_suffs[] = { - ".gif", - NULL -}; - static void vips_foreign_load_gif_file_class_init( VipsForeignLoadGifFileClass *class ) @@ -652,7 +717,6 @@ vips_foreign_load_gif_file_class_init( VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; - gobject_class->dispose = vips_foreign_load_gif_file_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -664,6 +728,9 @@ vips_foreign_load_gif_file_class_init( load_class->is_a = vips_foreign_load_gif_is_a; load_class->header = vips_foreign_load_gif_file_header; + class->open = vips_foreign_load_gif_file_open; + class->close = vips_foreign_load_gif_file_close; + VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to load from" ), @@ -709,8 +776,8 @@ vips_foreign_load_gif_buffer_header( VipsForeignLoad *load ) gif->data = buffer->buf->data; gif->size = buffer->buf->length; - return( VIPS_FOREIGN_LOAD_GIF_CLASS( - vips_foreign_load_gif_file_parent_class )->header( load ) ); + return( VIPS_FOREIGN_LOAD_CLASS( + vips_foreign_load_gif_buffer_parent_class )->header( load ) ); } static void From a952de86269d0029b81f63351fbe09284350112b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 15 Sep 2019 17:46:36 +0100 Subject: [PATCH 08/20] add 'fail' handling to gifnsload --- libvips/foreign/gifnsload.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index 1f2c013c..83b5c732 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -33,8 +33,8 @@ /* #define VERBOSE - */ #define VIPS_DEBUG + */ #ifdef HAVE_CONFIG_H #include @@ -315,16 +315,13 @@ static int vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); + VipsForeignLoad *load = (VipsForeignLoad *) gif; gif_result result; int i; VIPS_DEBUG_MSG( "vips_foreign_load_gif_open:\n" ); - /* GIF_INSUFFICIENT_FRAME_DATA means we allow truncated GIFs. - * - * TODO use fail to bail out on truncated GIFs. - */ result = gif_initialise( gif->anim, gif->size, gif->data ); VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); #ifdef VERBOSE @@ -336,6 +333,11 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) vips_foreign_load_gif_error( gif, result ); return( -1 ); } + else if( result == GIF_INSUFFICIENT_FRAME_DATA && + load->fail ) { + vips_error( class->nickname, "%s", _( "truncated GIF" ) ); + return( -1 ); + } if( !gif->anim->frame_count ) { vips_error( class->nickname, "%s", _( "no frames in GIF" ) ); return( -1 ); From 612789d91467b173973ea8409c5d7734316d78fd Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 18 Sep 2019 17:34:49 +0100 Subject: [PATCH 09/20] remove undisplayable frames from gifnsload and make gifload and gifnsload both build for easier testing ... though gifnsload is given higher priority, so it handles vipsheader etc. --- configure.ac | 4 +- libvips/foreign/foreign.c | 6 + libvips/foreign/gifnsload.c | 310 ++++++++++++++++++++---------------- 3 files changed, 180 insertions(+), 140 deletions(-) diff --git a/configure.ac b/configure.ac index 77869c1e..b6f1fb90 100644 --- a/configure.ac +++ b/configure.ac @@ -1166,7 +1166,7 @@ if test x"$with_libnsgif" != x"no"; then fi # giflib if no libnsgif -if test x"$with_libnsgif" != x"yes"; then +#if test x"$with_libnsgif" != x"yes"; then FIND_GIFLIB( [with_giflib="yes (found by search)" ], @@ -1174,7 +1174,7 @@ if test x"$with_libnsgif" != x"yes"; then with_giflib=no ] ) -fi +#fi # look for PNG with pkg-config ... fall back to our tester AC_ARG_WITH([png], diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 016643a0..bb973383 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1910,6 +1910,9 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_load_gif_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_gifns_get_type( void ); + extern GType vips_foreign_load_gifns_file_get_type( void ); + extern GType vips_foreign_load_gifns_buffer_get_type( void ); vips_foreign_load_csv_get_type(); vips_foreign_save_csv_get_type(); @@ -1959,6 +1962,9 @@ vips_foreign_operation_init( void ) vips_foreign_load_gif_get_type(); vips_foreign_load_gif_file_get_type(); vips_foreign_load_gif_buffer_get_type(); + vips_foreign_load_gifns_get_type(); + vips_foreign_load_gifns_file_get_type(); + vips_foreign_load_gifns_buffer_get_type(); #endif /*gif*/ #ifdef HAVE_GSF diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index 83b5c732..32357fc9 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -57,6 +57,8 @@ * - look into frame / page match up ... what about optimised GIFs where * several frames make up each page? * + * see https://en.wikipedia.org/wiki/GIF#/media/File:SmallFullColourGIF.gif + * * - libnsgif does not seem to support comment metadata * * Notes: @@ -74,22 +76,22 @@ #include -#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_gif_get_type()) +#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_gifns_get_type()) #define VIPS_FOREIGN_LOAD_GIF( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGif )) + VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifns )) #define VIPS_FOREIGN_LOAD_GIF_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifClass)) + VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifnsClass)) #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, VipsForeignLoadGifClass )) + VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifnsClass )) -typedef struct _VipsForeignLoadGif { +typedef struct _VipsForeignLoadGifns { VipsForeignLoad parent_object; /* Load this page (frame number). @@ -109,6 +111,10 @@ typedef struct _VipsForeignLoadGif { unsigned char *data; size_t size; + /* The frame_count, after we have removed undisplayable frames. + */ + int frame_count_displayable; + /* Delays between frames (in milliseconds). Array of length @n. */ int *delay; @@ -117,25 +123,25 @@ typedef struct _VipsForeignLoadGif { */ int gif_delay; -} VipsForeignLoadGif; +} VipsForeignLoadGifns; -typedef struct _VipsForeignLoadGifClass { +typedef struct _VipsForeignLoadGifnsClass { VipsForeignLoadClass parent_class; /* Close and reopen. */ - int (*open)( VipsForeignLoadGif *gif ); + int (*open)( VipsForeignLoadGifns *gif ); /* Close any underlying file resource. */ - void (*close)( VipsForeignLoadGif *gif ); -} VipsForeignLoadGifClass; + void (*close)( VipsForeignLoadGifns *gif ); +} VipsForeignLoadGifnsClass; -G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif, +G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGifns, vips_foreign_load_gifns, VIPS_TYPE_FOREIGN_LOAD ); static const char * -vips_foreign_load_gif_errstr( gif_result result ) +vips_foreign_load_gifns_errstr( gif_result result ) { switch( result ) { case GIF_WORKING: @@ -171,21 +177,22 @@ vips_foreign_load_gif_errstr( gif_result result ) } static void -vips_foreign_load_gif_error( VipsForeignLoadGif *gif, gif_result result ) +vips_foreign_load_gifns_error( VipsForeignLoadGifns *gif, gif_result result ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); vips_error( class->nickname, "%s", - vips_foreign_load_gif_errstr( result ) ); + vips_foreign_load_gifns_errstr( result ) ); } static void -vips_foreign_load_gif_dispose( GObject *gobject ) +vips_foreign_load_gifns_dispose( GObject *gobject ) { - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; - VipsForeignLoadGifClass *class = VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) gobject; + VipsForeignLoadGifnsClass *class = + VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); - VIPS_DEBUG_MSG( "vips_foreign_load_gif_dispose:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_dispose:\n" ); class->close( gif ); if( gif->anim ) { @@ -194,28 +201,28 @@ vips_foreign_load_gif_dispose( GObject *gobject ) } VIPS_FREE( gif->delay ); - G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> + G_OBJECT_CLASS( vips_foreign_load_gifns_parent_class )-> dispose( gobject ); } static VipsForeignFlags -vips_foreign_load_gif_get_flags_filename( const char *filename ) +vips_foreign_load_gifns_get_flags_filename( const char *filename ) { return( VIPS_FOREIGN_SEQUENTIAL ); } static VipsForeignFlags -vips_foreign_load_gif_get_flags( VipsForeignLoad *load ) +vips_foreign_load_gifns_get_flags( VipsForeignLoad *load ) { return( VIPS_FOREIGN_SEQUENTIAL ); } static gboolean -vips_foreign_load_gif_is_a_buffer( const void *buf, size_t len ) +vips_foreign_load_gifns_is_a_buffer( const void *buf, size_t len ) { const guchar *str = (const guchar *) buf; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_is_a_buffer:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_is_a_buffer:\n" ); if( len >= 4 && str[0] == 'G' && @@ -228,12 +235,12 @@ vips_foreign_load_gif_is_a_buffer( const void *buf, size_t len ) } static gboolean -vips_foreign_load_gif_is_a( const char *filename ) +vips_foreign_load_gifns_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 ) ) + vips_foreign_load_gifns_is_a_buffer( buf, 4 ) ) return( 1 ); return( 0 ); @@ -286,9 +293,10 @@ print_animation( gif_animation *anim ) #endif /*VERBOSE*/ static int -vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) +vips_foreign_load_gifns_set_header( VipsForeignLoadGifns *gif, + VipsImage *image ) { - VIPS_DEBUG_MSG( "vips_foreign_load_gif_set_header:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_set_header:\n" ); vips_image_init_fields( image, gif->anim->width, gif->anim->height * gif->n, 4, @@ -299,7 +307,7 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) vips_image_set_int( image, VIPS_META_PAGE_HEIGHT, gif->anim->height ); - vips_image_set_int( image, VIPS_META_N_PAGES, gif->anim->frame_count ); + vips_image_set_int( image, VIPS_META_N_PAGES, gif->frame_count_displayable ); vips_image_set_int( image, "gif-loop", gif->anim->loop_count ); vips_image_set_array_int( image, "delay", gif->delay, gif->n ); @@ -312,7 +320,7 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) } static int -vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) +vips_foreign_load_gifns_open( VipsForeignLoadGifns *gif ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); VipsForeignLoad *load = (VipsForeignLoad *) gif; @@ -320,7 +328,7 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) gif_result result; int i; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_open:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_open:\n" ); result = gif_initialise( gif->anim, gif->size, gif->data ); VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); @@ -330,7 +338,7 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) if( result != GIF_OK && result != GIF_WORKING && result != GIF_INSUFFICIENT_FRAME_DATA ) { - vips_foreign_load_gif_error( gif, result ); + vips_foreign_load_gifns_error( gif, result ); return( -1 ); } else if( result == GIF_INSUFFICIENT_FRAME_DATA && @@ -338,17 +346,31 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) vips_error( class->nickname, "%s", _( "truncated GIF" ) ); return( -1 ); } - if( !gif->anim->frame_count ) { + + /* Many GIFs have dead frames at the end. Remove these from our count. + */ + for( i = gif->anim->frame_count - 1; + i >= 0 && !gif->anim->frames[i].display; i-- ) + ; + gif->frame_count_displayable = i + 1; +#ifdef VERBOSE + if( gif->frame_count_displayable != gif->anim->frame_count ) + printf( "vips_foreign_load_gifns_open: " + "removed %d undisplayable frames\n", + gif->anim->frame_count - gif->frame_count_displayable ); +#endif /*VERBOSE*/ + + if( !gif->frame_count_displayable ) { vips_error( class->nickname, "%s", _( "no frames in GIF" ) ); return( -1 ); } if( gif->n == -1 ) - gif->n = gif->anim->frame_count - gif->page; + gif->n = gif->frame_count_displayable - gif->page; if( gif->page < 0 || gif->n <= 0 || - gif->page + gif->n > gif->anim->frame_count ) { + gif->page + gif->n > gif->frame_count_displayable ) { vips_error( class->nickname, "%s", _( "bad page number" ) ); return( -1 ); } @@ -367,7 +389,7 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) } static void -vips_foreign_load_gif_close( VipsForeignLoadGif *gif ) +vips_foreign_load_gifns_close( VipsForeignLoadGifns *gif ) { /* Don't call gif_finalise(): that just frees memory attached to the * gif struct, and gif_initialise() hates being called again after @@ -384,13 +406,13 @@ vips_foreign_load_gif_close( VipsForeignLoadGif *gif ) * Close as soon as we can to free up the fd. */ static int -vips_foreign_load_gif_header( VipsForeignLoad *load ) +vips_foreign_load_gifns_header( VipsForeignLoad *load ) { - VipsForeignLoadGifClass *class = + VipsForeignLoadGifnsClass *class = VIPS_FOREIGN_LOAD_GIF_GET_CLASS( load ); - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_header:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_header:\n" ); if( class->open( gif ) ) { class->close( gif ); @@ -398,32 +420,34 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) } class->close( gif ); - vips_foreign_load_gif_set_header( gif, load->out ); + vips_foreign_load_gifns_set_header( gif, load->out ); return( 0 ); } static void -vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif ) +vips_foreign_load_gifns_minimise( VipsObject *object, + VipsForeignLoadGifns *gif ) { - VipsForeignLoadGifClass *class = VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); + VipsForeignLoadGifnsClass *class = + VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); - VIPS_DEBUG_MSG( "vips_foreign_load_gif_minimise:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_minimise:\n" ); class->close( gif ); } static int -vips_foreign_load_gif_generate( VipsRegion *or, +vips_foreign_load_gifns_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a; + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) a; int y; #ifdef VERBOSE - VIPS_DEBUG_MSG( "vips_foreign_load_gif_generate: " + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_generate: " "top = %d, height = %d\n", r->top, r->height ); #endif /*VERBOSE*/ @@ -437,14 +461,14 @@ vips_foreign_load_gif_generate( VipsRegion *or, VipsPel *p, *q; g_assert( line >= 0 && line < gif->anim->height ); - g_assert( page >= 0 && page < gif->anim->frame_count ); + g_assert( page >= 0 && page < gif->frame_count_displayable ); if( gif->anim->decoded_frame != page ) { result = gif_decode_frame( gif->anim, page ); VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n", page, result ); if( result != GIF_OK ) { - vips_foreign_load_gif_error( gif, result ); + vips_foreign_load_gifns_error( gif, result ); return( -1 ); } #ifdef VERBOSE @@ -462,14 +486,15 @@ vips_foreign_load_gif_generate( VipsRegion *or, } static int -vips_foreign_load_gif_load( VipsForeignLoad *load ) +vips_foreign_load_gifns_load( VipsForeignLoad *load ) { - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; - VipsForeignLoadGifClass *class = VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load; + VipsForeignLoadGifnsClass *class = + VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( load ), 4 ); - VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_load:\n" ); if( class->open( gif ) ) return( -1 ); @@ -477,18 +502,18 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) /* Make the output pipeline. */ t[0] = vips_image_new(); - if( vips_foreign_load_gif_set_header( gif, t[0] ) ) + if( vips_foreign_load_gifns_set_header( gif, t[0] ) ) return( -1 ); /* Close immediately at end of read. */ g_signal_connect( t[0], "minimise", - G_CALLBACK( vips_foreign_load_gif_minimise ), gif ); + G_CALLBACK( vips_foreign_load_gifns_minimise ), gif ); /* Strips 8 pixels high to avoid too many tiny regions. */ if( vips_image_generate( t[0], - NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) || + NULL, vips_foreign_load_gifns_generate, NULL, gif, NULL ) || vips_sequential( t[0], &t[1], "tile_height", VIPS__FATSTRIP_HEIGHT, NULL ) || @@ -499,110 +524,118 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) } static void -vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) +vips_foreign_load_gifns_class_init( VipsForeignLoadGifnsClass *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_gif_dispose; + gobject_class->dispose = vips_foreign_load_gifns_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "gifload_base"; + object_class->nickname = "gifnsload_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; - load_class->header = vips_foreign_load_gif_header; - load_class->load = vips_foreign_load_gif_load; + /* High priority, so that we handle vipsheader etc. + */ + foreign_class->priority = 50; - class->open = vips_foreign_load_gif_open; - class->close = vips_foreign_load_gif_close; + 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; + + class->open = vips_foreign_load_gifns_open; + class->close = vips_foreign_load_gifns_close; VIPS_ARG_INT( class, "page", 10, _( "Page" ), _( "Load this page from the file" ), VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGif, page ), + G_STRUCT_OFFSET( VipsForeignLoadGifns, page ), 0, 100000, 0 ); VIPS_ARG_INT( class, "n", 6, _( "n" ), _( "Load this many pages" ), VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGif, n ), + G_STRUCT_OFFSET( VipsForeignLoadGifns, n ), -1, 100000, 1 ); } static void * -vips_foreign_load_gif_bitmap_create( int width, int height ) +vips_foreign_load_gifns_bitmap_create( int width, int height ) { /* ensure a stupidly large bitmap is not created */ - return calloc(width * height, 4); + return calloc( width * height, 4 ); } static void -vips_foreign_load_gif_bitmap_set_opaque( void *bitmap, bool opaque ) +vips_foreign_load_gifns_bitmap_set_opaque( void *bitmap, bool opaque ) { (void) opaque; /* unused */ (void) bitmap; /* unused */ - g_assert(bitmap); + g_assert( bitmap ); } static bool -vips_foreign_load_gif_bitmap_test_opaque( void *bitmap ) +vips_foreign_load_gifns_bitmap_test_opaque( void *bitmap ) { (void) bitmap; /* unused */ - g_assert(bitmap); - return false; + g_assert( bitmap ); + + return( false ); } static unsigned char * -vips_foreign_load_gif_bitmap_get_buffer( void *bitmap ) +vips_foreign_load_gifns_bitmap_get_buffer( void *bitmap ) { - g_assert(bitmap); - return bitmap; + g_assert( bitmap ); + + return( bitmap ); } static void -vips_foreign_load_gif_bitmap_destroy( void *bitmap ) +vips_foreign_load_gifns_bitmap_destroy( void *bitmap ) { - g_assert(bitmap); - free(bitmap); + g_assert( bitmap ); + free( bitmap ); } static void -vips_foreign_load_gif_bitmap_modified( void *bitmap ) +vips_foreign_load_gifns_bitmap_modified( void *bitmap ) { (void) bitmap; /* unused */ - g_assert(bitmap); + 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 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 void -vips_foreign_load_gif_init( VipsForeignLoadGif *gif ) +vips_foreign_load_gifns_init( VipsForeignLoadGifns *gif ) { gif->anim = g_new0( gif_animation, 1 ); - gif_create( gif->anim, &vips_foreign_load_gif_bitmap_callbacks ); + gif_create( gif->anim, &vips_foreign_load_gifns_bitmap_callbacks ); gif->n = 1; } -typedef struct _VipsForeignLoadGifFile { - VipsForeignLoadGif parent_object; +typedef struct _VipsForeignLoadGifnsFile { + VipsForeignLoadGifns parent_object; /* Filename for load. */ @@ -614,38 +647,38 @@ typedef struct _VipsForeignLoadGifFile { void *base; gint64 length; -} VipsForeignLoadGifFile; +} VipsForeignLoadGifnsFile; -typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass; +typedef VipsForeignLoadGifnsClass VipsForeignLoadGifnsFileClass; -G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file, - vips_foreign_load_gif_get_type() ); +G_DEFINE_TYPE( VipsForeignLoadGifnsFile, vips_foreign_load_gifns_file, + vips_foreign_load_gifns_get_type() ); -static const char *vips_foreign_gif_suffs[] = { +static const char *vips_foreign_gifns_suffs[] = { ".gif", NULL }; static int -vips_foreign_load_gif_file_header( VipsForeignLoad *load ) +vips_foreign_load_gifns_file_header( VipsForeignLoad *load ) { - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load; + VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) load; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_header:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_file_header:\n" ); VIPS_SETSTR( load->out->filename, file->filename ); return( VIPS_FOREIGN_LOAD_CLASS( - vips_foreign_load_gif_file_parent_class )->header( load ) ); + vips_foreign_load_gifns_file_parent_class )->header( load ) ); } static int -vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif ) +vips_foreign_load_gifns_file_open( VipsForeignLoadGifns *gif ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; + VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) gif; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_open:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_file_open:\n" ); if( file->fd == -1 ) { struct stat st; @@ -685,15 +718,15 @@ vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif ) } return( VIPS_FOREIGN_LOAD_GIF_CLASS( - vips_foreign_load_gif_file_parent_class )->open( gif ) ); + vips_foreign_load_gifns_file_parent_class )->open( gif ) ); } static void -vips_foreign_load_gif_file_close( VipsForeignLoadGif *gif ) +vips_foreign_load_gifns_file_close( VipsForeignLoadGifns *gif ) { - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; + VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) gif; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_file_close:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_file_close:\n" ); if( file->fd >= 0 ) { (void) g_close( file->fd, NULL ); @@ -707,12 +740,12 @@ vips_foreign_load_gif_file_close( VipsForeignLoadGif *gif ) } VIPS_FOREIGN_LOAD_GIF_CLASS( - vips_foreign_load_gif_file_parent_class )->close( gif ); + vips_foreign_load_gifns_file_parent_class )->close( gif ); } static void -vips_foreign_load_gif_file_class_init( - VipsForeignLoadGifFileClass *class ) +vips_foreign_load_gifns_file_class_init( + VipsForeignLoadGifnsFileClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; @@ -722,34 +755,34 @@ vips_foreign_load_gif_file_class_init( gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "gifload"; + object_class->nickname = "gifnsload"; object_class->description = _( "load GIF with libnsgif" ); - foreign_class->suffs = vips_foreign_gif_suffs; + foreign_class->suffs = vips_foreign_gifns_suffs; - load_class->is_a = vips_foreign_load_gif_is_a; - load_class->header = vips_foreign_load_gif_file_header; + load_class->is_a = vips_foreign_load_gifns_is_a; + load_class->header = vips_foreign_load_gifns_file_header; - class->open = vips_foreign_load_gif_file_open; - class->close = vips_foreign_load_gif_file_close; + class->open = vips_foreign_load_gifns_file_open; + class->close = vips_foreign_load_gifns_file_close; VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to load from" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGifFile, filename ), + G_STRUCT_OFFSET( VipsForeignLoadGifnsFile, filename ), NULL ); } static void -vips_foreign_load_gif_file_init( VipsForeignLoadGifFile *file ) +vips_foreign_load_gifns_file_init( VipsForeignLoadGifnsFile *file ) { file->fd = -1; } -typedef struct _VipsForeignLoadGifBuffer { - VipsForeignLoadGif parent_object; +typedef struct _VipsForeignLoadGifnsBuffer { + VipsForeignLoadGifns parent_object; /* Load from a buffer. */ @@ -760,31 +793,32 @@ typedef struct _VipsForeignLoadGifBuffer { VipsPel *p; size_t bytes_to_go; -} VipsForeignLoadGifBuffer; +} VipsForeignLoadGifnsBuffer; -typedef VipsForeignLoadGifClass VipsForeignLoadGifBufferClass; +typedef VipsForeignLoadGifnsClass VipsForeignLoadGifnsBufferClass; -G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer, - vips_foreign_load_gif_get_type() ); +G_DEFINE_TYPE( VipsForeignLoadGifnsBuffer, vips_foreign_load_gifns_buffer, + vips_foreign_load_gifns_get_type() ); static int -vips_foreign_load_gif_buffer_header( VipsForeignLoad *load ) +vips_foreign_load_gifns_buffer_header( VipsForeignLoad *load ) { - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; - VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) load; + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load; + VipsForeignLoadGifnsBuffer *buffer = + (VipsForeignLoadGifnsBuffer *) load; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_buffer_header:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_buffer_header:\n" ); gif->data = buffer->buf->data; gif->size = buffer->buf->length; return( VIPS_FOREIGN_LOAD_CLASS( - vips_foreign_load_gif_buffer_parent_class )->header( load ) ); + vips_foreign_load_gifns_buffer_parent_class )->header( load ) ); } static void -vips_foreign_load_gif_buffer_class_init( - VipsForeignLoadGifBufferClass *class ) +vips_foreign_load_gifns_buffer_class_init( + VipsForeignLoadGifnsBufferClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; @@ -793,23 +827,23 @@ vips_foreign_load_gif_buffer_class_init( gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "gifload_buffer"; + object_class->nickname = "gifnsload_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; + load_class->is_a_buffer = vips_foreign_load_gifns_is_a_buffer; + load_class->header = vips_foreign_load_gifns_buffer_header; VIPS_ARG_BOXED( class, "buffer", 1, _( "Buffer" ), _( "Buffer to load from" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGifBuffer, buf ), + G_STRUCT_OFFSET( VipsForeignLoadGifnsBuffer, buf ), VIPS_TYPE_BLOB ); } static void -vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer ) +vips_foreign_load_gifns_buffer_init( VipsForeignLoadGifnsBuffer *buffer ) { } From fe4ec6d11a7f0704f9b59d8989c8d6dfccb1cf7d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 18 Sep 2019 18:06:33 +0100 Subject: [PATCH 10/20] save int arrays to vips files and fix a tiny memleak in loadgifns --- libvips/foreign/gifnsload.c | 11 ++++------- libvips/iofuncs/type.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index 32357fc9..8ed5bf8f 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -53,11 +53,6 @@ #include /* TODO: - * - * - look into frame / page match up ... what about optimised GIFs where - * several frames make up each page? - * - * see https://en.wikipedia.org/wiki/GIF#/media/File:SmallFullColourGIF.gif * * - libnsgif does not seem to support comment metadata * @@ -307,7 +302,8 @@ vips_foreign_load_gifns_set_header( VipsForeignLoadGifns *gif, if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) vips_image_set_int( image, VIPS_META_PAGE_HEIGHT, gif->anim->height ); - vips_image_set_int( image, VIPS_META_N_PAGES, gif->frame_count_displayable ); + vips_image_set_int( image, VIPS_META_N_PAGES, + gif->frame_count_displayable ); vips_image_set_int( image, "gif-loop", gif->anim->loop_count ); vips_image_set_array_int( image, "delay", gif->delay, gif->n ); @@ -328,7 +324,7 @@ vips_foreign_load_gifns_open( VipsForeignLoadGifns *gif ) gif_result result; int i; - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_open:\n" ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_open: %p\n", gif ); result = gif_initialise( gif->anim, gif->size, gif->data ); VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); @@ -377,6 +373,7 @@ vips_foreign_load_gifns_open( VipsForeignLoadGifns *gif ) /* In ms, frame_delay in cs. */ + VIPS_FREE( gif->delay ); if( !(gif->delay = VIPS_ARRAY( NULL, gif->n, int )) ) return( -1 ); for( i = 0; i < gif->n; i++ ) diff --git a/libvips/iofuncs/type.c b/libvips/iofuncs/type.c index d59f4b56..d28282b3 100644 --- a/libvips/iofuncs/type.c +++ b/libvips/iofuncs/type.c @@ -843,6 +843,21 @@ transform_array_int_g_string( const GValue *src_value, GValue *dest_value ) g_value_set_string( dest_value, vips_buf_all( &buf ) ); } +static void +transform_array_int_save_string( const GValue *src_value, GValue *dest_value ) +{ + GValue intermediate = { 0 }; + + g_value_init( &intermediate, G_TYPE_STRING ); + + transform_array_int_g_string( src_value, &intermediate ); + + vips_value_set_save_string( dest_value, + g_value_get_string( &intermediate ) ); + + g_value_unset( &intermediate ); +} + /* It'd be great to be able to write a generic string->array function, but * it doesn't seem possible. */ @@ -894,6 +909,21 @@ transform_g_string_array_int( const GValue *src_value, GValue *dest_value ) g_free( str ); } +static void +transform_save_string_array_int( const GValue *src_value, GValue *dest_value ) +{ + GValue intermediate = { 0 }; + + g_value_init( &intermediate, G_TYPE_STRING ); + + g_value_set_string( &intermediate, + vips_value_get_save_string( src_value ) ); + + transform_g_string_array_int( &intermediate, dest_value ); + + g_value_unset( &intermediate ); +} + /* We need a arrayint, we have an int, make a one-element array. */ static void @@ -949,6 +979,10 @@ vips_array_int_get_type( void ) transform_double_array_int ); g_value_register_transform_func( VIPS_TYPE_ARRAY_DOUBLE, type, transform_array_double_array_int ); + g_value_register_transform_func( type, VIPS_TYPE_SAVE_STRING, + transform_array_int_save_string ); + g_value_register_transform_func( VIPS_TYPE_SAVE_STRING, type, + transform_save_string_array_int ); } return( type ); From 8e3e32957bf6b6dd4735581a29f7cd5585242864 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 3 Jul 2020 11:02:03 +0100 Subject: [PATCH 11/20] final notes and give up on libnsgif --- libvips/foreign/gifnsload.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index 8ed5bf8f..8e4cc358 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -56,6 +56,10 @@ * * - libnsgif does not seem to support comment metadata * + * - dispose_previous is not handled correctly by libnsgif + * + * - it always loads the entire source file into memory + * * Notes: * * - hard to detect mono images -- local_colour_table in libnsgif is only set From e44b781971ce8bf7638e69126b1bfa7d85965091 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 7 Jul 2020 15:50:44 +0100 Subject: [PATCH 12/20] revise gifnsload.c for source API --- libvips/foreign/gifnsload.c | 308 +++++++++++------------------------- libvips/iofuncs/source.c | 2 - 2 files changed, 96 insertions(+), 214 deletions(-) diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/gifnsload.c index 8e4cc358..6852740c 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/gifnsload.c @@ -56,8 +56,6 @@ * * - libnsgif does not seem to support comment metadata * - * - dispose_previous is not handled correctly by libnsgif - * * - it always loads the entire source file into memory * * Notes: @@ -101,6 +99,10 @@ typedef struct _VipsForeignLoadGifns { */ int n; + /* Load from this source (set by subclasses). + */ + VipsSource *source; + /* The animation created by libnsgif. */ gif_animation *anim; @@ -124,17 +126,7 @@ typedef struct _VipsForeignLoadGifns { } VipsForeignLoadGifns; -typedef struct _VipsForeignLoadGifnsClass { - VipsForeignLoadClass parent_class; - - /* Close and reopen. - */ - int (*open)( VipsForeignLoadGifns *gif ); - - /* Close any underlying file resource. - */ - void (*close)( VipsForeignLoadGifns *gif ); -} VipsForeignLoadGifnsClass; +typedef VipsForeignLoadClass VipsForeignLoadGifnsClass; G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGifns, vips_foreign_load_gifns, VIPS_TYPE_FOREIGN_LOAD ); @@ -188,16 +180,14 @@ static void vips_foreign_load_gifns_dispose( GObject *gobject ) { VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) gobject; - VipsForeignLoadGifnsClass *class = - VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); VIPS_DEBUG_MSG( "vips_foreign_load_gifns_dispose:\n" ); - class->close( gif ); if( gif->anim ) { gif_finalise( gif->anim ); VIPS_FREE( gif->anim ); } + VIPS_UNREF( gif->source ); VIPS_FREE( gif->delay ); G_OBJECT_CLASS( vips_foreign_load_gifns_parent_class )-> @@ -217,32 +207,18 @@ vips_foreign_load_gifns_get_flags( VipsForeignLoad *load ) } static gboolean -vips_foreign_load_gifns_is_a_buffer( const void *buf, size_t len ) +vips_foreign_load_gifns_is_a_source( VipsSource *source ) { - const guchar *str = (const guchar *) buf; + const unsigned char *data; - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_is_a_buffer:\n" ); + if( (data = vips_source_sniff( source, 4 )) && + data[0] == 'G' && + data[1] == 'I' && + data[2] == 'F' && + data[3] == '8' ) + return( TRUE ); - if( len >= 4 && - str[0] == 'G' && - str[1] == 'I' && - str[2] == 'F' && - str[3] == '8' ) - return( 1 ); - - return( 0 ); -} - -static gboolean -vips_foreign_load_gifns_is_a( const char *filename ) -{ - unsigned char buf[4]; - - if( vips__get_bytes( filename, buf, 4 ) == 4 && - vips_foreign_load_gifns_is_a_buffer( buf, 4 ) ) - return( 1 ); - - return( 0 ); + return( FALSE ); } #ifdef VERBOSE @@ -319,18 +295,35 @@ vips_foreign_load_gifns_set_header( VipsForeignLoadGifns *gif, return( 0 ); } +/* Scan the GIF as quickly as we can and extract transparency, bands, pages, + * etc. + * + * Don't flag any errors unless we have to: we want to work for corrupt or + * malformed GIFs. + * + * Close as soon as we can to free up the fd. + */ static int -vips_foreign_load_gifns_open( VipsForeignLoadGifns *gif ) +vips_foreign_load_gifns_header( VipsForeignLoad *load ) { - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); - VipsForeignLoad *load = (VipsForeignLoad *) gif; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load; + const void *data; + size_t size; gif_result result; int i; - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_open: %p\n", gif ); + VIPS_DEBUG_MSG( "vips_foreign_load_gifns_header:\n" ); - result = gif_initialise( gif->anim, gif->size, gif->data ); + /* We map in the image, then minimise to close any underlying file + * object. This won't unmap. + */ + if( !(data = vips_source_map( gif->source, &size )) ) + return( -1 ); + vips_source_minimise( gif->source ); + + result = gif_initialise( gif->anim, size, (void *) data ); VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result ); #ifdef VERBOSE print_animation( gif->anim ); @@ -386,58 +379,11 @@ vips_foreign_load_gifns_open( VipsForeignLoadGifns *gif ) gif->gif_delay = gif->anim->frames[0].frame_delay; - return( 0 ); -} - -static void -vips_foreign_load_gifns_close( VipsForeignLoadGifns *gif ) -{ - /* Don't call gif_finalise(): that just frees memory attached to the - * gif struct, and gif_initialise() hates being called again after - * that. - */ -} - -/* Scan the GIF as quickly as we can and extract transparency, bands, pages, - * etc. - * - * Don't flag any errors unless we have to: we want to work for corrupt or - * malformed GIFs. - * - * Close as soon as we can to free up the fd. - */ -static int -vips_foreign_load_gifns_header( VipsForeignLoad *load ) -{ - VipsForeignLoadGifnsClass *class = - VIPS_FOREIGN_LOAD_GIF_GET_CLASS( load ); - VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load; - - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_header:\n" ); - - if( class->open( gif ) ) { - class->close( gif ); - return( -1 ); - } - class->close( gif ); - vips_foreign_load_gifns_set_header( gif, load->out ); return( 0 ); } -static void -vips_foreign_load_gifns_minimise( VipsObject *object, - VipsForeignLoadGifns *gif ) -{ - VipsForeignLoadGifnsClass *class = - VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); - - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_minimise:\n" ); - - class->close( gif ); -} - static int vips_foreign_load_gifns_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) @@ -490,27 +436,17 @@ static int vips_foreign_load_gifns_load( VipsForeignLoad *load ) { VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load; - VipsForeignLoadGifnsClass *class = - VIPS_FOREIGN_LOAD_GIF_GET_CLASS( gif ); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( load ), 4 ); VIPS_DEBUG_MSG( "vips_foreign_load_gifns_load:\n" ); - if( class->open( gif ) ) - return( -1 ); - /* Make the output pipeline. */ t[0] = vips_image_new(); if( vips_foreign_load_gifns_set_header( gif, t[0] ) ) return( -1 ); - /* Close immediately at end of read. - */ - g_signal_connect( t[0], "minimise", - G_CALLBACK( vips_foreign_load_gifns_minimise ), gif ); - /* Strips 8 pixels high to avoid too many tiny regions. */ if( vips_image_generate( t[0], @@ -549,9 +485,6 @@ vips_foreign_load_gifns_class_init( VipsForeignLoadGifnsClass *class ) load_class->header = vips_foreign_load_gifns_header; load_class->load = vips_foreign_load_gifns_load; - class->open = vips_foreign_load_gifns_open; - class->close = vips_foreign_load_gifns_close; - VIPS_ARG_INT( class, "page", 10, _( "Page" ), _( "Load this page from the file" ), @@ -642,12 +575,6 @@ typedef struct _VipsForeignLoadGifnsFile { */ char *filename; - /* mmap here. - */ - int fd; - void *base; - gint64 length; - } VipsForeignLoadGifnsFile; typedef VipsForeignLoadGifnsClass VipsForeignLoadGifnsFileClass; @@ -655,93 +582,41 @@ typedef VipsForeignLoadGifnsClass VipsForeignLoadGifnsFileClass; G_DEFINE_TYPE( VipsForeignLoadGifnsFile, vips_foreign_load_gifns_file, vips_foreign_load_gifns_get_type() ); +static int +vips_foreign_load_gif_file_build( VipsObject *object ) +{ + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) object; + VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) 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 )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + static const char *vips_foreign_gifns_suffs[] = { ".gif", NULL }; -static int -vips_foreign_load_gifns_file_header( VipsForeignLoad *load ) +static gboolean +vips_foreign_load_gifns_file_is_a( const char *filename ) { - VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) load; + VipsSource *source; + gboolean result; - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_file_header:\n" ); + if( !(source = vips_source_new_from_file( filename )) ) + return( FALSE ); + result = vips_foreign_load_gifns_is_a_source( source ); + VIPS_UNREF( source ); - VIPS_SETSTR( load->out->filename, file->filename ); - - return( VIPS_FOREIGN_LOAD_CLASS( - vips_foreign_load_gifns_file_parent_class )->header( load ) ); -} - -static int -vips_foreign_load_gifns_file_open( VipsForeignLoadGifns *gif ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); - VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) gif; - - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_file_open:\n" ); - - if( file->fd == -1 ) { - struct stat st; - mode_t m; - - g_assert( gif->filename ); - g_assert( !gif->data ); - g_assert( !gif->size ); - - file->fd = vips__open_read( file->filename ); - if( file->fd == -1 ) { - vips_error_system( errno, class->nickname, - _( "unable to open file \"%s\" for reading" ), - file->filename ); - return( -1 ); - } - if( (file->length = vips_file_length( file->fd )) == -1 ) - return( -1 ); - - if( fstat( file->fd, &st ) == -1 ) { - vips_error( class->nickname, - "%s", _( "unable to get file status" ) ); - return( -1 ); - } - m = (mode_t) st.st_mode; - if( !S_ISREG( m ) ) { - vips_error( class->nickname, - "%s", _( "not a regular file" ) ); - return( -1 ); - } - - if( !(file->base = vips__mmap( file->fd, 0, file->length, 0 )) ) - return( -1 ); - - gif->data = file->base; - gif->size = file->length; - } - - return( VIPS_FOREIGN_LOAD_GIF_CLASS( - vips_foreign_load_gifns_file_parent_class )->open( gif ) ); -} - -static void -vips_foreign_load_gifns_file_close( VipsForeignLoadGifns *gif ) -{ - VipsForeignLoadGifnsFile *file = (VipsForeignLoadGifnsFile *) gif; - - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_file_close:\n" ); - - if( file->fd >= 0 ) { - (void) g_close( file->fd, NULL ); - file->fd = -1; - } - - if( file->base ) { - vips__munmap( file->base, file->length ); - file->base = NULL; - file->length = 0; - } - - VIPS_FOREIGN_LOAD_GIF_CLASS( - vips_foreign_load_gifns_file_parent_class )->close( gif ); + return( result ); } static void @@ -758,14 +633,11 @@ vips_foreign_load_gifns_file_class_init( object_class->nickname = "gifnsload"; object_class->description = _( "load GIF with libnsgif" ); + object_class->build = vips_foreign_load_gif_file_build; foreign_class->suffs = vips_foreign_gifns_suffs; - load_class->is_a = vips_foreign_load_gifns_is_a; - load_class->header = vips_foreign_load_gifns_file_header; - - class->open = vips_foreign_load_gifns_file_open; - class->close = vips_foreign_load_gifns_file_close; + load_class->is_a = vips_foreign_load_gifns_file_is_a; VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), @@ -779,7 +651,6 @@ vips_foreign_load_gifns_file_class_init( static void vips_foreign_load_gifns_file_init( VipsForeignLoadGifnsFile *file ) { - file->fd = -1; } typedef struct _VipsForeignLoadGifnsBuffer { @@ -787,12 +658,7 @@ typedef struct _VipsForeignLoadGifnsBuffer { /* Load from a buffer. */ - VipsArea *buf; - - /* Current read point, bytes left in buffer. - */ - VipsPel *p; - size_t bytes_to_go; + VipsArea *blob; } VipsForeignLoadGifnsBuffer; @@ -802,19 +668,37 @@ G_DEFINE_TYPE( VipsForeignLoadGifnsBuffer, vips_foreign_load_gifns_buffer, vips_foreign_load_gifns_get_type() ); static int -vips_foreign_load_gifns_buffer_header( VipsForeignLoad *load ) +vips_foreign_load_gifns_buffer_build( VipsObject *object ) { - VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) load; + VipsForeignLoadGifns *gif = (VipsForeignLoadGifns *) object; VipsForeignLoadGifnsBuffer *buffer = - (VipsForeignLoadGifnsBuffer *) load; + (VipsForeignLoadGifnsBuffer *) object; - VIPS_DEBUG_MSG( "vips_foreign_load_gifns_buffer_header:\n" ); + if( buffer->blob && + !(gif->source = vips_source_new_from_memory( + buffer->blob->data, + buffer->blob->length )) ) + return( -1 ); - gif->data = buffer->buf->data; - gif->size = buffer->buf->length; + if( VIPS_OBJECT_CLASS( vips_foreign_load_gifns_buffer_parent_class )-> + build( object ) ) + return( -1 ); - return( VIPS_FOREIGN_LOAD_CLASS( - vips_foreign_load_gifns_buffer_parent_class )->header( load ) ); + return( 0 ); +} + +static gboolean +vips_foreign_load_gifns_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 ); + VIPS_UNREF( source ); + + return( result ); } static void @@ -830,15 +714,15 @@ vips_foreign_load_gifns_buffer_class_init( object_class->nickname = "gifnsload_buffer"; object_class->description = _( "load GIF with libnsgif" ); + object_class->build = vips_foreign_load_gifns_buffer_build; - load_class->is_a_buffer = vips_foreign_load_gifns_is_a_buffer; - load_class->header = vips_foreign_load_gifns_buffer_header; + load_class->is_a_buffer = vips_foreign_load_gifns_buffer_is_a_buffer; VIPS_ARG_BOXED( class, "buffer", 1, _( "Buffer" ), _( "Buffer to load from" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGifnsBuffer, buf ), + G_STRUCT_OFFSET( VipsForeignLoadGifnsBuffer, blob ), VIPS_TYPE_BLOB ); } diff --git a/libvips/iofuncs/source.c b/libvips/iofuncs/source.c index a8d59261..83c3d8d0 100644 --- a/libvips/iofuncs/source.c +++ b/libvips/iofuncs/source.c @@ -545,8 +545,6 @@ vips_source_new_from_options( const char *options ) * * Loaders should call this in response to the minimise signal on their output * image. - * - * Returns: 0 on success, or -1 on error. */ void vips_source_minimise( VipsSource *source ) From b995a6d24439146598ff5ce388c6d6b467f0da4b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 28 Feb 2021 12:20:05 +0000 Subject: [PATCH 13/20] move the nsgif source into the tree and remove the old giflib loader --- README.md | 18 +- configure.ac | 83 +- libvips/Makefile.am | 4 + libvips/colour/Makefile.am | 4 +- libvips/foreign/Makefile.am | 9 +- libvips/foreign/foreign.c | 23 +- libvips/foreign/gifload.c | 1746 ------------------ libvips/foreign/libnsgif/Makefile.am | 13 + libvips/foreign/libnsgif/README-ns | 36 + libvips/foreign/libnsgif/README.md | 18 + libvips/foreign/libnsgif/libnsgif.c | 1164 ++++++++++++ libvips/foreign/libnsgif/libnsgif.h | 183 ++ libvips/foreign/libnsgif/lzw.c | 377 ++++ libvips/foreign/libnsgif/lzw.h | 105 ++ libvips/foreign/libnsgif/utils/log.h | 21 + libvips/foreign/{gifnsload.c => nsgifload.c} | 376 +++- 16 files changed, 2252 insertions(+), 1928 deletions(-) delete mode 100644 libvips/foreign/gifload.c create mode 100644 libvips/foreign/libnsgif/Makefile.am create mode 100644 libvips/foreign/libnsgif/README-ns create mode 100644 libvips/foreign/libnsgif/README.md create mode 100644 libvips/foreign/libnsgif/libnsgif.c create mode 100644 libvips/foreign/libnsgif/libnsgif.h create mode 100644 libvips/foreign/libnsgif/lzw.c create mode 100644 libvips/foreign/libnsgif/lzw.h create mode 100644 libvips/foreign/libnsgif/utils/log.h rename libvips/foreign/{gifnsload.c => nsgifload.c} (59%) diff --git a/README.md b/README.md index 8d362ff7..1e7cc567 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/configure.ac b/configure.ac index a88367f4..b3535d22 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/libvips/Makefile.am b/libvips/Makefile.am index bc05c661..212dc491 100644 --- a/libvips/Makefile.am +++ b/libvips/Makefile.am @@ -9,6 +9,10 @@ else OPTIONAL_DIST_DIR += deprecated endif +if ENABLE_NSGIF +OPTIONAL_LIB += foreign/libnsgif/libnsgif.la +endif + SUBDIRS = \ include \ foreign \ diff --git a/libvips/colour/Makefile.am b/libvips/colour/Makefile.am index b8ced977..ef013ad7 100644 --- a/libvips/colour/Makefile.am +++ b/libvips/colour/Makefile.am @@ -1,7 +1,7 @@ -noinst_LTLIBRARIES = libcolour.la - SUBDIRS = profiles +noinst_LTLIBRARIES = libcolour.la + libcolour_la_SOURCES = \ profiles.c \ profiles.h \ diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index 0fef2c4a..3af243b4 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -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@ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 52e34d6f..4ee11c28 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -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(); diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c deleted file mode 100644 index b520c742..00000000 --- a/libvips/foreign/gifload.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* load a GIF with giflib - * - * 10/2/16 - * - from svgload.c - * 25/4/16 - * - 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 - * 17/8/16 - * - support unicode on win - * 19/8/16 - * - better transparency detection, thanks diegocsandrim - * 25/11/16 - * - support @n, page-height - * 5/10/17 - * - colormap can be missing thanks Kleis - * 21/11/17 - * - add "gif-delay", "gif-loop", "gif-comment" metadata - * - add dispose handling - * 13/8/18 - * - init pages to 0 before load - * 14/2/19 - * - rework as a sequential loader ... simpler, much lower mem use - * 6/7/19 [deftomat] - * - support array of delays - * 24/7/19 - * - close early on minimise - * - close early on error - * 23/8/18 - * - allow GIF read errors during header scan - * - better feof() handling - * 27/8/19 - * - check image and frame bounds, since giflib does not - * 1/9/19 - * - improve early close again - * 30/1/19 - * - rework on top of VipsSource - * - add gifload_source - * 5/2/20 alon-ne - * - fix DISPOSE_BACKGROUND and DISPOSE_PREVIOUS - * 2/7/20 - * - clip out of bounds images against canvas - * - fix PREVIOUS handling, again - * 19/2/21 781545872 - * - read out background, if we can - */ - -/* - - 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 DEBUG_VERBOSE -#define VIPS_DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef HAVE_GIFLIB - -#include - -/* giflib 5 is rather different :-( functions have error returns and there's - * no LastError(). - * - * GIFLIB_MAJOR was introduced in 4.1.6. Use it to test for giflib 5.x. - */ -#ifdef GIFLIB_MAJOR -# if GIFLIB_MAJOR > 4 -# define HAVE_GIFLIB_5 -# endif -#endif - -/* Added in giflib5. - */ -#ifndef HAVE_GIFLIB_5 -#define DISPOSAL_UNSPECIFIED 0 -#define DISPOSE_DO_NOT 1 -#define DISPOSE_BACKGROUND 2 -#define DISPOSE_PREVIOUS 3 -#endif - -#define NO_TRANSPARENT_INDEX -1 -#define TRANSPARENT_MASK 0x01 -#define DISPOSE_MASK 0x07 -#define DISPOSE_SHIFT 2 - -#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_gif_get_type()) -#define VIPS_FOREIGN_LOAD_GIF( obj ) \ - (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGif )) -#define VIPS_FOREIGN_LOAD_GIF_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifClass)) -#define VIPS_FOREIGN_LOAD_GIF_GET_CLASS( obj ) \ - (G_TYPE_INSTANCE_GET_CLASS( (obj), \ - VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadGifClass )) - -typedef struct _VipsForeignLoadGif { - VipsForeignLoad parent_object; - - /* Load from this page (frame number). - */ - int page; - - /* Load this many pages. - */ - int n; - - /* Load from this source (set by subclasses). - */ - VipsSource *source; - - GifFileType *file; - - /* 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; - - /* Delays between frames (in milliseconds). - */ - int *delays; - int delays_length; - - /* Number of times to loop the animation. - */ - int loop; - - /* The GIF comment, if any. - */ - char *comment; - - /* The number of pages (frame) in the image. - */ - int n_pages; - - /* A memory image the size of one frame ... we accumulate to this as - * we scan the image, and copy lines to the output on generate. - */ - VipsImage *frame; - - /* A scratch buffer the size of the largest Image inside the GIF. We - * decompress lines from the GIF to this. - */ - VipsImage *scratch; - - /* A copy of the previous frame, in case we need a DISPOSE_PREVIOUS. - */ - VipsImage *previous; - - /* The position of @frame, in pages. - */ - int current_page; - - /* Decompress lines of the gif file to here. This is large enough to - * hold one line of the widest sub-image in the GIF. - */ - int max_image_width; - GifPixelType *line; - - /* The current dispose method. - */ - int dispose; - - /* Set for EOF detected. - */ - gboolean eof; - - /* The current cmap unpacked to a simple LUT. Each uint32 is really an - * RGBA pixel ready to be blasted into @frame. - */ - guint32 cmap[256]; - - /* As we scan the file, the index of the transparent pixel for this - * frame. - */ - int transparent_index; - -} VipsForeignLoadGif; - -typedef VipsForeignLoadClass VipsForeignLoadGifClass; - -G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif, - VIPS_TYPE_FOREIGN_LOAD ); - -/* From gif2rgb.c ... offsets and jumps for interlaced GIF images. - */ -static int - InterlacedOffset[] = { 0, 4, 2, 1 }, - InterlacedJumps[] = { 8, 8, 4, 2 }; - -/* giflib4 was missing this. - */ -static const char * -vips_foreign_load_gif_errstr( int error_code ) -{ -#ifdef HAVE_GIFLIB_5 - return( GifErrorString( error_code ) ); -#else /*!HAVE_GIFLIB_5*/ - switch( error_code ) { - case D_GIF_ERR_OPEN_FAILED: - return( _( "Failed to open given file" ) ); - - case D_GIF_ERR_READ_FAILED: - return( _( "Failed to read from given file" ) ); - - case D_GIF_ERR_NOT_GIF_FILE: - return( _( "Data is not a GIF file" ) ); - - case D_GIF_ERR_NO_SCRN_DSCR: - return( _( "No screen descriptor detected" ) ); - - case D_GIF_ERR_NO_IMAG_DSCR: - return( _( "No image descriptor detected" ) ); - - case D_GIF_ERR_NO_COLOR_MAP: - return( _( "Neither global nor local color map" ) ); - - case D_GIF_ERR_WRONG_RECORD: - return( _( "Wrong record type detected" ) ); - - case D_GIF_ERR_DATA_TOO_BIG: - return( _( "Number of pixels bigger than width * height" ) ); - - case D_GIF_ERR_NOT_ENOUGH_MEM: - return( _( "Failed to allocate required memory" ) ); - - case D_GIF_ERR_CLOSE_FAILED: - return( _( "Failed to close given file" ) ); - - case D_GIF_ERR_NOT_READABLE: - return( _( "Given file was not opened for read" ) ); - - case D_GIF_ERR_IMAGE_DEFECT: - return( _( "Image is defective, decoding aborted" ) ); - - case D_GIF_ERR_EOF_TOO_SOON: - return( _( "Image EOF detected, before image complete" ) ); - - default: - return( _( "Unknown error" ) ); - } -#endif /*HAVE_GIFLIB_5*/ -} - -static void -vips_foreign_load_gif_error_vips( VipsForeignLoadGif *gif, int error ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); - - const char *message; - - if( (message = vips_foreign_load_gif_errstr( error )) ) - vips_error( class->nickname, "%s", message ); -} - -static void -vips_foreign_load_gif_error( VipsForeignLoadGif *gif ) -{ - int error; - - error = 0; - -#ifdef HAVE_GIFLIB_5 - if( gif->file ) - error = gif->file->Error; -#else - error = GifLastError(); -#endif - - if( error ) - vips_foreign_load_gif_error_vips( gif, error ); -} - -/* Shut down giflib plus any underlying file resource. - */ -static int -vips_foreign_load_gif_close_giflib( VipsForeignLoadGif *gif ) -{ - VIPS_DEBUG_MSG( "vips_foreign_load_gif_close_giflib:\n" ); - -#ifdef HAVE_GIFLIB_5 - if( gif->file ) { - int error; - - if( DGifCloseFile( gif->file, &error ) == GIF_ERROR ) { - vips_foreign_load_gif_error_vips( gif, error ); - gif->file = NULL; - - return( -1 ); - } - gif->file = NULL; - } -#else - if( gif->file ) { - if( DGifCloseFile( gif->file ) == GIF_ERROR ) { - vips_foreign_load_gif_error_vips( gif, GifLastError() ); - gif->file = NULL; - - return( -1 ); - } - gif->file = NULL; - } -#endif - - if( gif->source ) - vips_source_minimise( gif->source ); - - return( 0 ); -} - -/* Callback from the gif loader. - * - * Read up to len bytes into buffer, return number of bytes read. This is - * called by giflib exactly as fread, so it does not distinguish between EOF - * and read error. - */ -static int -vips_giflib_read( GifFileType *file, GifByteType *buf, int n ) -{ - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) file->UserData; - - int to_read; - - to_read = n; - while( to_read > 0 ) { - gint64 bytes_read; - - bytes_read = vips_source_read( gif->source, buf, n ); - if( bytes_read == 0 ) { - gif->eof = TRUE; - return( -1 ); - } - if( bytes_read < 0 ) - return( -1 ); - if( bytes_read > INT_MAX ) - return( -1 ); - - to_read -= bytes_read; - buf += bytes_read; - } - - return( (int) n ); -} - -/* Open any underlying file resource, then giflib. - */ -static int -vips_foreign_load_gif_open_giflib( VipsForeignLoadGif *gif ) -{ - VIPS_DEBUG_MSG( "vips_foreign_load_gif_open_giflib:\n" ); - - g_assert( !gif->file ); - - /* Must always rewind before opening giflib again. - */ - vips_source_rewind( gif->source ); - -#ifdef HAVE_GIFLIB_5 -{ - int error; - - if( !(gif->file = DGifOpen( gif, vips_giflib_read, &error )) ) { - vips_foreign_load_gif_error_vips( gif, error ); - (void) vips_foreign_load_gif_close_giflib( gif ); - return( -1 ); - } -} -#else - if( !(gif->file = DGifOpen( gif, vips_giflib_read )) ) { - vips_foreign_load_gif_error_vips( gif, GifLastError() ); - (void) vips_foreign_load_gif_close_giflib( gif ); - return( -1 ); - } -#endif - - gif->eof = FALSE; - gif->current_page = 0; - gif->max_image_width = 0; - - return( 0 ); -} - -static void -vips_foreign_load_gif_dispose( GObject *gobject ) -{ - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_dispose:\n" ); - - vips_foreign_load_gif_close_giflib( gif ); - - VIPS_UNREF( gif->source ); - VIPS_UNREF( gif->frame ); - VIPS_UNREF( gif->scratch ); - VIPS_UNREF( gif->previous ); - VIPS_FREE( gif->comment ); - VIPS_FREE( gif->line ); - VIPS_FREE( gif->delays ); - - G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )-> - dispose( gobject ); -} - -static VipsForeignFlags -vips_foreign_load_gif_get_flags_filename( const char *filename ) -{ - return( VIPS_FOREIGN_SEQUENTIAL ); -} - -static VipsForeignFlags -vips_foreign_load_gif_get_flags( VipsForeignLoad *load ) -{ - return( VIPS_FOREIGN_SEQUENTIAL ); -} - -static gboolean -vips_foreign_load_gif_is_a_source( VipsSource *source ) -{ - const unsigned char *data; - - if( (data = vips_source_sniff( source, 4 )) && - data[0] == 'G' && - data[1] == 'I' && - data[2] == 'F' && - data[3] == '8' ) - return( TRUE ); - - return( FALSE ); -} - -/* Make sure delays is allocated and large enough. - */ -static void -vips_foreign_load_gif_allocate_delays( VipsForeignLoadGif *gif ) -{ - if( gif->n_pages >= gif->delays_length ) { - int old = gif->delays_length; - int i; - - gif->delays_length = gif->delays_length + gif->n_pages + 64; - gif->delays = (int *) g_realloc( gif->delays, - gif->delays_length * sizeof( int ) ); - for( i = old; i < gif->delays_length; i++ ) - gif->delays[i] = 40; - } -} - -static int -vips_foreign_load_gif_ext_next( VipsForeignLoadGif *gif, - GifByteType **extension ) -{ - if( DGifGetExtensionNext( gif->file, extension ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - -#ifdef DEBUG_VERBOSE - if( *extension ) - printf( "gifload: EXTENSION_NEXT\n" ); -#endif /*DEBUG_VERBOSE*/ - - return( 0 ); -} - -static int -vips_foreign_load_gif_code_next( VipsForeignLoadGif *gif, - GifByteType **extension ) -{ - if( DGifGetCodeNext( gif->file, extension ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - -#ifdef DEBUG_VERBOSE - if( *extension ) - printf( "gifload: CODE_NEXT\n" ); -#endif /*DEBUG_VERBOSE*/ - - return( 0 ); -} - -/* Quickly scan an image record. - */ -static int -vips_foreign_load_gif_scan_image( VipsForeignLoadGif *gif ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); - GifFileType *file = gif->file; - - ColorMapObject *map; - GifByteType *extension; - - if( DGifGetImageDesc( file ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_scan_image: " - "frame of %dx%d pixels at %dx%d\n", - file->Image.Width, file->Image.Height, - file->Image.Left, file->Image.Top ); - - /* giflib does no checking of image dimensions, not even for 0. - */ - if( file->Image.Width <= 0 || - file->Image.Width > VIPS_MAX_COORD || - file->Image.Height <= 0 || - file->Image.Height > VIPS_MAX_COORD ) { - vips_error( class->nickname, - "%s", _( "image size out of bounds" ) ); - return( -1 ); - } - - /* We need to find the max scanline size inside the GIF - * so we can allocate the decompress buffer. - */ - gif->max_image_width = VIPS_MAX( gif->max_image_width, - file->Image.Width ); - - /* Test for a non-greyscale colourmap for this frame. - */ - map = file->Image.ColorMap ? file->Image.ColorMap : file->SColorMap; - if( !gif->has_colour && - map ) { - 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 ) { - gif->has_colour = TRUE; - break; - } - } - - /* Step over compressed image data. - */ - do { - if( vips_foreign_load_gif_code_next( gif, &extension ) ) - return( -1 ); - } while( extension != NULL ); - - return( 0 ); -} - -static int -vips_foreign_load_gif_scan_application_ext( VipsForeignLoadGif *gif, - GifByteType *extension ) -{ - gboolean have_netscape; - - /* The 11-byte NETSCAPE extension. - */ - have_netscape = FALSE; - if( extension[0] == 11 && - (vips_isprefix( "NETSCAPE2.0", - (const char*) (extension + 1) ) || - vips_isprefix( "ANIMEXTS1.0", - (const char*) (extension + 1) )) ) - have_netscape = TRUE; - - while( extension != NULL ) { - if( vips_foreign_load_gif_ext_next( gif, &extension ) ) - return( -1 ); - - if( have_netscape && - extension && - extension[0] == 3 && - extension[1] == 1 ) { - gif->loop = extension[2] | (extension[3] << 8); - if( gif->loop != 0 ) - gif->loop += 1; - } - } - - return( 0 ); -} - -static int -vips_foreign_load_gif_scan_comment_ext( VipsForeignLoadGif *gif, - GifByteType *extension ) -{ - VIPS_DEBUG_MSG( "gifload: type: comment\n" ); - - if( !gif->comment ) { - /* Up to 257 with a NULL terminator. - */ - char comment[257]; - - vips_strncpy( comment, (char *) (extension + 1), 256 ); - comment[extension[0]] = '\0'; - gif->comment = g_strdup( comment ); - } - - while( extension != NULL ) - if( vips_foreign_load_gif_ext_next( gif, &extension ) ) - return( -1 ); - - return( 0 ); -} - -static int -vips_foreign_load_gif_scan_extension( VipsForeignLoadGif *gif ) -{ - GifByteType *extension; - int ext_code; - - if( DGifGetExtension( gif->file, &ext_code, &extension ) == - GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - if( extension ) - switch( ext_code ) { - case GRAPHICS_EXT_FUNC_CODE: - if( extension[0] == 4 && - extension[1] & TRANSPARENT_MASK ) { - VIPS_DEBUG_MSG( "gifload: has transp.\n" ); - gif->has_transparency = TRUE; - } - - /* giflib uses centiseconds, we use ms. - */ - gif->delays[gif->n_pages] = - (extension[2] | (extension[3] << 8)) * 10; - - while( extension != NULL ) - if( vips_foreign_load_gif_ext_next( gif, - &extension ) ) - return( -1 ); - - break; - - case APPLICATION_EXT_FUNC_CODE: - if( vips_foreign_load_gif_scan_application_ext( gif, - extension ) ) - return( -1 ); - break; - - case COMMENT_EXT_FUNC_CODE: - if( vips_foreign_load_gif_scan_comment_ext( gif, - extension ) ) - return( -1 ); - break; - - default: - /* Step over any NEXT blocks for unknown extensions. - */ - while( extension != NULL ) - if( vips_foreign_load_gif_ext_next( gif, - &extension ) ) - return( -1 ); - break; - } - - return( 0 ); -} - -static int -vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) -{ - const gint64 total_height = (gint64) gif->file->SHeight * gif->n; - ColorMapObject *map = gif->file->SColorMap; - - if( total_height <= 0 || - total_height > VIPS_MAX_COORD ) { - vips_error( "gifload", "%s", _( "image size out of bounds" ) ); - return( -1 ); - } - - vips_image_init_fields( image, - gif->file->SWidth, total_height, - (gif->has_colour ? 3 : 1) + (gif->has_transparency ? 1 : 0), - VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, - gif->has_colour ? - VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_B_W, - 1.0, 1.0 ); - vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); - - if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) ) - vips_image_set_int( image, - VIPS_META_PAGE_HEIGHT, gif->file->SHeight ); - vips_image_set_int( image, VIPS_META_N_PAGES, gif->n_pages ); - vips_image_set_int( image, "loop", gif->loop ); - - /* DEPRECATED "gif-loop" - * - * Not the correct behavior as loop=1 became gif-loop=0 - * but we want to keep the old behavior untouched! - */ - vips_image_set_int( image, - "gif-loop", gif->loop == 0 ? 0 : gif->loop - 1 ); - - if( gif->delays ) { - /* The deprecated gif-delay field is in centiseconds. - */ - vips_image_set_int( image, - "gif-delay", VIPS_RINT( gif->delays[0] / 10.0 ) ); - vips_image_set_array_int( image, - "delay", gif->delays, gif->n_pages ); - } - else - vips_image_set_int( image, "gif-delay", 4 ); - - if( gif->comment ) - vips_image_set_string( image, "gif-comment", gif->comment ); - - if( map && - gif->file->SBackGroundColor >= 0 && - gif->file->SBackGroundColor < map->ColorCount ) { - double array[3]; - - array[0] = map->Colors[gif->file->SBackGroundColor].Red; - array[1] = map->Colors[gif->file->SBackGroundColor].Green; - array[2] = map->Colors[gif->file->SBackGroundColor].Blue; - - vips_image_set_array_double( image, "background", array, 3 ); - } - - return( 0 ); -} - -/* Attempt to quickly scan a GIF and discover what we need for our header. We - * need to scan the whole file to get n_pages, transparency, colour etc. - * - * Don't flag errors during header scan. Many GIFs do not follow spec. - */ -static int -vips_foreign_load_gif_scan( VipsForeignLoadGif *gif ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); - - GifRecordType record; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_scan:\n" ); - - gif->n_pages = 0; - - do { - if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) - continue; - - switch( record ) { - case IMAGE_DESC_RECORD_TYPE: - (void) vips_foreign_load_gif_scan_image( gif ); - gif->n_pages += 1; - vips_foreign_load_gif_allocate_delays( gif ); - break; - - case EXTENSION_RECORD_TYPE: - /* We need to fetch the extensions to check for - * cmaps and transparency. - */ - (void) vips_foreign_load_gif_scan_extension( gif ); - break; - - case TERMINATE_RECORD_TYPE: - gif->eof = TRUE; - break; - - case SCREEN_DESC_RECORD_TYPE: - case UNDEFINED_RECORD_TYPE: - break; - - default: - break; - } - } while( !gif->eof ); - - if( gif->n == -1 ) - gif->n = gif->n_pages - gif->page; - - if( gif->page < 0 || - gif->n <= 0 || - gif->page + gif->n > gif->n_pages ) { - vips_error( class->nickname, "%s", _( "bad page number" ) ); - return( -1 ); - } - - if( gif->max_image_width <= 0 || - gif->max_image_width > VIPS_MAX_COORD ) { - vips_error( class->nickname, "%s", _( "bad image size" ) ); - return( -1 ); - } - - return( 0 ); -} - -/* Scan the GIF and set the libvips header. We always close after scan, even - * on an error. - */ -static int -vips_foreign_load_gif_header( VipsForeignLoad *load ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); - VipsForeignLoadGif *gif = VIPS_FOREIGN_LOAD_GIF( load ); - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_header: %p\n", gif ); - - if( vips_foreign_load_gif_open_giflib( gif ) ) - return( -1 ); - - /* giflib does no checking of image dimensions, not even for 0. - */ - if( gif->file->SWidth <= 0 || - gif->file->SWidth > VIPS_MAX_COORD || - gif->file->SHeight <= 0 || - gif->file->SHeight > VIPS_MAX_COORD ) { - vips_error( class->nickname, - "%s", _( "image size out of bounds" ) ); - (void) vips_foreign_load_gif_close_giflib( gif ); - - return( -1 ); - } - - /* Allocate a line buffer now that we have the GIF width. - */ - if( vips_foreign_load_gif_scan( gif ) || - !(gif->line = VIPS_ARRAY( NULL, - gif->max_image_width, GifPixelType )) || - vips_foreign_load_gif_set_header( gif, load->out ) ) { - (void) vips_foreign_load_gif_close_giflib( gif ); - - return( -1 ); - } - - (void) vips_foreign_load_gif_close_giflib( gif ); - - return( 0 ); -} - -static void -vips_foreign_load_gif_build_cmap( VipsForeignLoadGif *gif ) -{ - ColorMapObject *map = gif->file->Image.ColorMap ? - gif->file->Image.ColorMap : gif->file->SColorMap; - - int v; - - for( v = 0; v < 256; v++ ) { - VipsPel *q = (VipsPel *) &gif->cmap[v]; - - if( map && - v < map->ColorCount ) { - q[0] = map->Colors[v].Red; - q[1] = map->Colors[v].Green; - q[2] = map->Colors[v].Blue; - q[3] = 255; - } - else { - /* If there's no map, just save the index. - */ - q[0] = v; - q[1] = v; - q[2] = v; - q[3] = 255; - } - } -} - -/* Paint line y from the image left/top/width/height into scratch, clipping as - * we go. - */ -static void -vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif, - int y, - int left, int top, int width, int height, - VipsPel *line ) -{ - VipsRect canvas; - VipsRect row; - VipsRect overlap; - - /* Many GIFs have frames which lie outside the canvas. We have to - * clip. - */ - canvas.left = 0; - canvas.top = 0; - canvas.width = gif->file->SWidth; - canvas.height = gif->file->SHeight; - row.left = left; - row.top = top + y; - row.width = width; - row.height = height; - vips_rect_intersectrect( &canvas, &row, &overlap ); - - if( !vips_rect_isempty( &overlap ) ) { - VipsPel *dst = VIPS_IMAGE_ADDR( gif->scratch, - overlap.left, overlap.top ); - guint32 * restrict idst = (guint32 *) dst; - VipsPel * restrict src = line + (overlap.left - row.left); - - int x; - - for( x = 0; x < overlap.width; x++ ) { - VipsPel v = src[x]; - - if( v != gif->transparent_index ) - idst[x] = gif->cmap[v]; - } - } -} - -/* Render the current gif frame into an RGBA buffer. GIFs can accumulate, - * depending on the current dispose mode. - */ -static int -vips_foreign_load_gif_render( VipsForeignLoadGif *gif ) -{ - GifFileType *file = gif->file; - - if( DGifGetImageDesc( file ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - /* Update the colour map for this frame. - */ - vips_foreign_load_gif_build_cmap( gif ); - - /* If this is PREVIOUS, then after we're done, we'll need to restore - * the frame to what it was previously. Make a note of the current - * state. - */ - if( gif->dispose == DISPOSE_PREVIOUS ) - memcpy( VIPS_IMAGE_ADDR( gif->previous, 0, 0 ), - VIPS_IMAGE_ADDR( gif->frame, 0, 0 ), - VIPS_IMAGE_SIZEOF_IMAGE( gif->previous ) ); - - if( file->Image.Interlace ) { - int i; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: " - "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++ ) { - int y; - - for( y = InterlacedOffset[i]; y < file->Image.Height; - y += InterlacedJumps[i] ) { - if( DGifGetLine( gif->file, - gif->line, file->Image.Width ) == - GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - vips_foreign_load_gif_render_line( gif, y, - file->Image.Left, - file->Image.Top, - file->Image.Width, - file->Image.Height, - gif->line ); - } - } - } - else { - int y; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: " - "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++ ) { - if( DGifGetLine( gif->file, - gif->line, file->Image.Width ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - vips_foreign_load_gif_render_line( gif, y, - file->Image.Left, - file->Image.Top, - file->Image.Width, - file->Image.Height, - gif->line ); - } - } - - /* Copy the result to frame, ready to be copied to our output. - */ - memcpy( VIPS_IMAGE_ADDR( gif->frame, 0, 0 ), - VIPS_IMAGE_ADDR( gif->scratch, 0, 0 ), - VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) ); - - if( gif->dispose == DISPOSE_BACKGROUND ) { - /* BACKGROUND means we reset the area we just painted to - * transparent. We have to clip against the canvas. - */ - VipsRect canvas; - VipsRect image; - VipsRect overlap; - - canvas.left = 0; - canvas.top = 0; - canvas.width = gif->file->SWidth; - canvas.height = gif->file->SHeight; - image.left = file->Image.Left, - image.top = file->Image.Top, - image.width = file->Image.Width, - image.height = file->Image.Height, - vips_rect_intersectrect( &canvas, &image, &overlap ); - - if( !vips_rect_isempty( &overlap ) ) { - guint32 *q = (guint32 *) VIPS_IMAGE_ADDR( gif->scratch, - overlap.left, overlap.top ); - - /* What we write for transparent pixels. We want RGB - * to be 255, and A to be 0. - */ - guint32 transparent = GUINT32_TO_BE( 0xffffff00 ); - - int x, y; - - /* Generate the first line a pixel at a time, - * memcpy() for subsequent lines. - */ - for( x = 0; x < overlap.width; x++ ) - q[x] = transparent; - - for( y = 1; y < overlap.height; y++ ) - memcpy( q + gif->scratch->Xsize * y, - q, - overlap.width * sizeof( guint32 ) ); - } - } - else if( gif->dispose == DISPOSE_PREVIOUS ) - /* PREVIOUS means we restore the previous state of the scratch - * area. - */ - memcpy( VIPS_IMAGE_ADDR( gif->scratch, 0, 0 ), - VIPS_IMAGE_ADDR( gif->previous, 0, 0 ), - VIPS_IMAGE_SIZEOF_IMAGE( gif->scratch ) ); - - /* Reset values, as the Graphic Control Extension is optional - */ - gif->dispose = DISPOSAL_UNSPECIFIED; - gif->transparent_index = NO_TRANSPARENT_INDEX; - - return( 0 ); -} - -#ifdef VIPS_DEBUG -static const char * -dispose2str( int dispose ) -{ - switch( dispose ) { - case DISPOSAL_UNSPECIFIED: return( "DISPOSAL_UNSPECIFIED" ); - case DISPOSE_DO_NOT: return( "DISPOSE_DO_NOT" ); - case DISPOSE_BACKGROUND: return( "DISPOSE_BACKGROUND" ); - case DISPOSE_PREVIOUS: return( "DISPOSE_PREVIOUS" ); - default: return( "" ); - } -} -#endif /*VIPS_DEBUG*/ - -static int -vips_foreign_load_gif_extension( VipsForeignLoadGif *gif ) -{ - GifByteType *extension; - int ext_code; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension:\n" ); - - if( DGifGetExtension( gif->file, &ext_code, &extension ) == - GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - if( extension && - ext_code == GRAPHICS_EXT_FUNC_CODE && - extension[0] == 4 ) { - int flags = extension[1]; - - /* Bytes are flags, delay low, delay high, transparency. - * Flag bit 1 means transparency is being set. - */ - gif->transparent_index = (flags & TRANSPARENT_MASK) ? - extension[4] : NO_TRANSPARENT_INDEX; - VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: " - "transparency = %d\n", gif->transparent_index ); - - /* Set the current dispose mode. This is read during frame load - * to set the meaning of background and transparent pixels. - */ - gif->dispose = (flags >> DISPOSE_SHIFT) & DISPOSE_MASK; - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: " - "dispose = %s\n", dispose2str( gif->dispose ) ); - } - - while( extension != NULL ) - if( vips_foreign_load_gif_ext_next( gif, &extension ) ) - return( -1 ); - - return( 0 ); -} - -/* Read the next page from the file into @frame. - */ -static int -vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif ) -{ - GifRecordType record; - gboolean have_read_frame; - - have_read_frame = FALSE; - do { - if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - switch( record ) { - case IMAGE_DESC_RECORD_TYPE: - VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: " - "IMAGE_DESC_RECORD_TYPE\n" ); - - if( vips_foreign_load_gif_render( gif ) ) - return( -1 ); - - have_read_frame = TRUE; - - break; - - case EXTENSION_RECORD_TYPE: - if( vips_foreign_load_gif_extension( gif ) ) - return( -1 ); - break; - - case TERMINATE_RECORD_TYPE: - VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: " - "TERMINATE_RECORD_TYPE\n" ); - gif->eof = TRUE; - break; - - case SCREEN_DESC_RECORD_TYPE: - VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: " - "SCREEN_DESC_RECORD_TYPE\n" ); - break; - - case UNDEFINED_RECORD_TYPE: - VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: " - "UNDEFINED_RECORD_TYPE\n" ); - break; - - default: - break; - } - } while( !have_read_frame && - !gif->eof ); - - return( 0 ); -} - -static int -vips_foreign_load_gif_generate( VipsRegion *or, - void *seq, void *a, void *b, gboolean *stop ) -{ - VipsRect *r = &or->valid; - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a; - - int y; - -#ifdef DEBUG_VERBOSE - printf( "vips_foreign_load_gif_generate: %p " - "left = %d, top = %d, width = %d, height = %d\n", - gif, - r->left, r->top, r->width, r->height ); -#endif /*DEBUG_VERBOSE*/ - - for( y = 0; y < r->height; y++ ) { - /* The page for this output line, and the line number in page. - */ - int page = (r->top + y) / gif->file->SHeight + gif->page; - int line = (r->top + y) % gif->file->SHeight; - - VipsPel *p, *q; - int x; - - g_assert( line >= 0 && line < gif->frame->Ysize ); - g_assert( page >= 0 && page < gif->n_pages ); - - /* current_page == 0 means we've not loaded any pages yet. So - * we need to have loaded the page beyond the page we want. - */ - while( gif->current_page <= page ) { - if( vips_foreign_load_gif_next_page( gif ) ) - return( -1 ); - - gif->current_page += 1; - } - - /* @frame is always RGBA, but or may be G, GA, RGB or RGBA. - * We have to pick out the values we want. - */ - p = VIPS_IMAGE_ADDR( gif->frame, 0, line ); - q = VIPS_REGION_ADDR( or, 0, r->top + y ); - switch( or->im->Bands ) { - case 1: - for( x = 0; x < gif->frame->Xsize; x++ ) { - q[0] = p[1]; - - q += 1; - p += 4; - } - break; - - case 2: - for( x = 0; x < gif->frame->Xsize; x++ ) { - q[0] = p[1]; - q[1] = p[3]; - - q += 2; - p += 4; - } - break; - - case 3: - for( x = 0; x < gif->frame->Xsize; x++ ) { - q[0] = p[0]; - q[1] = p[1]; - q[2] = p[2]; - - q += 3; - p += 4; - } - break; - - case 4: - memcpy( q, p, VIPS_IMAGE_SIZEOF_LINE( gif->frame ) ); - break; - - default: - g_assert_not_reached(); - break; - } - } - - return( 0 ); -} - -static void -vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif ) -{ - vips_source_minimise( gif->source ); -} - -static VipsImage * -vips_foreign_load_gif_temp( VipsForeignLoadGif *gif ) -{ - VipsImage *temp; - - temp = vips_image_new_memory(); - vips_image_init_fields( temp, - gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR, - VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); - if( vips_image_write_prepare( temp ) ) { - VIPS_UNREF( temp ); - return( NULL ); - } - - return( temp ); -} - -static int -vips_foreign_load_gif_load( VipsForeignLoad *load ) -{ - VipsForeignLoadGif *gif = VIPS_FOREIGN_LOAD_GIF( load ); - VipsImage **t = (VipsImage **) - vips_object_local_array( VIPS_OBJECT( load ), 4 ); - - VIPS_DEBUG_MSG( "vips_foreign_load_gif_load: %p\n", gif ); - - if( vips_foreign_load_gif_open_giflib( gif ) ) - return( -1 ); - - /* Set of temp images we use during rendering. - */ - if( !(gif->frame = vips_foreign_load_gif_temp( gif )) || - !(gif->scratch = vips_foreign_load_gif_temp( gif )) || - !(gif->previous = vips_foreign_load_gif_temp( gif )) ) - return( -1 ); - - /* Make the output pipeline. - */ - t[0] = vips_image_new(); - if( vips_foreign_load_gif_set_header( gif, t[0] ) ) - return( -1 ); - - /* Close input immediately at end of read. - */ - g_signal_connect( t[0], "minimise", - G_CALLBACK( vips_foreign_load_gif_minimise ), gif ); - - /* Strips 8 pixels high to avoid too many tiny regions. - */ - if( vips_image_generate( t[0], - NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) || - vips_sequential( t[0], &t[1], - "tile_height", VIPS__FATSTRIP_HEIGHT, - NULL ) || - vips_image_write( t[1], load->real ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) -{ - 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 giflib" ); - - load_class->header = vips_foreign_load_gif_header; - load_class->load = vips_foreign_load_gif_load; - 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", 20, - _( "Page" ), - _( "Load this page from the file" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGif, page ), - 0, 100000, 0 ); - - VIPS_ARG_INT( class, "n", 21, - _( "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; - gif->transparent_index = NO_TRANSPARENT_INDEX; - gif->delays = NULL; - gif->delays_length = 0; - gif->loop = 1; - gif->comment = NULL; - gif->dispose = DISPOSAL_UNSPECIFIED; - - vips_foreign_load_gif_allocate_delays( gif ); -} - -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_build( VipsObject *object ) -{ - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) object; - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) object; - - if( file->filename ) - if( !(gif->source = - vips_source_new_from_file( file->filename )) ) - return( -1 ); - - if( VIPS_OBJECT_CLASS( vips_foreign_load_gif_file_parent_class )-> - build( object ) ) - return( -1 ); - - return( 0 ); -} - -static const char *vips_foreign_gif_suffs[] = { - ".gif", - NULL -}; - -static gboolean -vips_foreign_load_gif_file_is_a( const char *filename ) -{ - VipsSource *source; - gboolean result; - - if( !(source = vips_source_new_from_file( filename )) ) - return( FALSE ); - result = vips_foreign_load_gif_is_a_source( source ); - VIPS_UNREF( source ); - - return( result ); -} - -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 giflib" ); - object_class->build = vips_foreign_load_gif_file_build; - - foreign_class->suffs = vips_foreign_gif_suffs; - - load_class->is_a = vips_foreign_load_gif_file_is_a; - - 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 *blob; - -} VipsForeignLoadGifBuffer; - -typedef VipsForeignLoadGifClass VipsForeignLoadGifBufferClass; - -G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer, - vips_foreign_load_gif_get_type() ); - -static int -vips_foreign_load_gif_buffer_build( VipsObject *object ) -{ - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) object; - VipsForeignLoadGifBuffer *buffer = - (VipsForeignLoadGifBuffer *) object; - - if( buffer->blob && - !(gif->source = vips_source_new_from_memory( - VIPS_AREA( buffer->blob )->data, - VIPS_AREA( buffer->blob )->length )) ) - return( -1 ); - - if( VIPS_OBJECT_CLASS( vips_foreign_load_gif_buffer_parent_class )-> - build( object ) ) - return( -1 ); - - return( 0 ); -} - -static gboolean -vips_foreign_load_gif_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_gif_is_a_source( source ); - VIPS_UNREF( source ); - - return( result ); -} - -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 giflib" ); - object_class->build = vips_foreign_load_gif_buffer_build; - - load_class->is_a_buffer = vips_foreign_load_gif_buffer_is_a_buffer; - - VIPS_ARG_BOXED( class, "buffer", 1, - _( "Buffer" ), - _( "Buffer to load from" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGifBuffer, blob ), - VIPS_TYPE_BLOB ); - -} - -static void -vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer ) -{ -} - -typedef struct _VipsForeignLoadGifSource { - VipsForeignLoadGif parent_object; - - /* Load from a source. - */ - VipsSource *source; - -} VipsForeignLoadGifSource; - -typedef VipsForeignLoadGifClass VipsForeignLoadGifSourceClass; - -G_DEFINE_TYPE( VipsForeignLoadGifSource, vips_foreign_load_gif_source, - vips_foreign_load_gif_get_type() ); - -static int -vips_foreign_load_gif_source_build( VipsObject *object ) -{ - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) object; - VipsForeignLoadGifSource *source = - (VipsForeignLoadGifSource *) object; - - if( source->source ) { - gif->source = source->source; - g_object_ref( gif->source ); - } - - if( VIPS_OBJECT_CLASS( vips_foreign_load_gif_source_parent_class )-> - build( object ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_foreign_load_gif_source_class_init( - VipsForeignLoadGifSourceClass *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 with giflib" ); - object_class->build = vips_foreign_load_gif_source_build; - - load_class->is_a_source = vips_foreign_load_gif_is_a_source; - - VIPS_ARG_OBJECT( class, "source", 1, - _( "Source" ), - _( "Source to load from" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadGifSource, source ), - VIPS_TYPE_SOURCE ); - -} - -static void -vips_foreign_load_gif_source_init( VipsForeignLoadGifSource *source ) -{ -} - -#endif /*HAVE_GIFLIB*/ - -/** - * 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 will be 1, 2, 3 or 4 bands for mono, mono plus - * transparency, RGB, or RGB plus transparency. - * - * 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 ); -} - diff --git a/libvips/foreign/libnsgif/Makefile.am b/libvips/foreign/libnsgif/Makefile.am new file mode 100644 index 00000000..631db44f --- /dev/null +++ b/libvips/foreign/libnsgif/Makefile.am @@ -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 + diff --git a/libvips/foreign/libnsgif/README-ns b/libvips/foreign/libnsgif/README-ns new file mode 100644 index 00000000..498ee466 --- /dev/null +++ b/libvips/foreign/libnsgif/README-ns @@ -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'. diff --git a/libvips/foreign/libnsgif/README.md b/libvips/foreign/libnsgif/README.md new file mode 100644 index 00000000..1b241b3d --- /dev/null +++ b/libvips/foreign/libnsgif/README.md @@ -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. diff --git a/libvips/foreign/libnsgif/libnsgif.c b/libvips/foreign/libnsgif/libnsgif.c new file mode 100644 index 00000000..cf478f54 --- /dev/null +++ b/libvips/foreign/libnsgif/libnsgif.c @@ -0,0 +1,1164 @@ +/* + * Copyright 2004 Richard Wilson + * Copyright 2008 Sean Fox + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include "libnsgif.h" +#include "utils/log.h" + +#include "lzw.h" + +/** + * + * \file + * \brief GIF image decoder + * + * The GIF format is thoroughly documented; a full description can be found at + * http://www.w3.org/Graphics/GIF/spec-gif89a.txt + * + * \todo Plain text and comment extensions should be implemented. + */ + + +/** Maximum colour table size */ +#define GIF_MAX_COLOURS 256 + +/** Internal flag that the colour table needs to be processed */ +#define GIF_PROCESS_COLOURS 0xaa000000 + +/** Internal flag that a frame is invalid/unprocessed */ +#define GIF_INVALID_FRAME -1 + +/** Transparent colour */ +#define GIF_TRANSPARENT_COLOUR 0x00 + +/* GIF Flags */ +#define GIF_FRAME_COMBINE 1 +#define GIF_FRAME_CLEAR 2 +#define GIF_FRAME_RESTORE 3 +#define GIF_FRAME_QUIRKS_RESTORE 4 + +#define GIF_IMAGE_SEPARATOR 0x2c +#define GIF_INTERLACE_MASK 0x40 +#define GIF_COLOUR_TABLE_MASK 0x80 +#define GIF_COLOUR_TABLE_SIZE_MASK 0x07 +#define GIF_EXTENSION_INTRODUCER 0x21 +#define GIF_EXTENSION_GRAPHIC_CONTROL 0xf9 +#define GIF_DISPOSAL_MASK 0x1c +#define GIF_TRANSPARENCY_MASK 0x01 +#define GIF_EXTENSION_COMMENT 0xfe +#define GIF_EXTENSION_PLAIN_TEXT 0x01 +#define GIF_EXTENSION_APPLICATION 0xff +#define GIF_BLOCK_TERMINATOR 0x00 +#define GIF_TRAILER 0x3b + +/** standard GIF header size */ +#define GIF_STANDARD_HEADER_SIZE 13 + + +/** + * Updates the sprite memory size + * + * \param gif The animation context + * \param width The width of the sprite + * \param height The height of the sprite + * \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success + */ +static gif_result +gif_initialise_sprite(gif_animation *gif, + unsigned int width, + unsigned int height) +{ + unsigned int max_width; + unsigned int max_height; + struct bitmap *buffer; + + /* Check if we've changed */ + if ((width <= gif->width) && (height <= gif->height)) { + return GIF_OK; + } + + /* Get our maximum values */ + max_width = (width > gif->width) ? width : gif->width; + max_height = (height > gif->height) ? height : gif->height; + + /* Allocate some more memory */ + assert(gif->bitmap_callbacks.bitmap_create); + buffer = gif->bitmap_callbacks.bitmap_create(max_width, max_height); + if (buffer == NULL) { + return GIF_INSUFFICIENT_MEMORY; + } + + assert(gif->bitmap_callbacks.bitmap_destroy); + gif->bitmap_callbacks.bitmap_destroy(gif->frame_image); + gif->frame_image = buffer; + gif->width = max_width; + gif->height = max_height; + + /* Invalidate our currently decoded image */ + gif->decoded_frame = GIF_INVALID_FRAME; + return GIF_OK; +} + + +/** + * Attempts to initialise the frame's extensions + * + * \param gif The animation context + * \param frame The frame number + * @return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the + * frame GIF_OK for successful initialisation. + */ +static gif_result +gif_initialise_frame_extensions(gif_animation *gif, const int frame) +{ + unsigned char *gif_data, *gif_end; + int gif_bytes; + unsigned int block_size; + + /* Get our buffer position etc. */ + gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position); + gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size); + + /* Initialise the extensions */ + while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) { + ++gif_data; + if ((gif_bytes = (gif_end - gif_data)) < 1) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + + /* Switch on extension label */ + switch (gif_data[0]) { + case GIF_EXTENSION_GRAPHIC_CONTROL: + /* 6-byte Graphic Control Extension is: + * + * +0 CHAR Graphic Control Label + * +1 CHAR Block Size + * +2 CHAR __Packed Fields__ + * 3BITS Reserved + * 3BITS Disposal Method + * 1BIT User Input Flag + * 1BIT Transparent Color Flag + * +3 SHORT Delay Time + * +5 CHAR Transparent Color Index + */ + if (gif_bytes < 6) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + + gif->frames[frame].frame_delay = gif_data[3] | (gif_data[4] << 8); + if (gif_data[2] & GIF_TRANSPARENCY_MASK) { + gif->frames[frame].transparency = true; + gif->frames[frame].transparency_index = gif_data[5]; + } + gif->frames[frame].disposal_method = ((gif_data[2] & GIF_DISPOSAL_MASK) >> 2); + /* I have encountered documentation and GIFs in the + * wild that use 0x04 to restore the previous frame, + * rather than the officially documented 0x03. I + * believe some (older?) software may even actually + * export this way. We handle this as a type of + * "quirks" mode. + */ + if (gif->frames[frame].disposal_method == GIF_FRAME_QUIRKS_RESTORE) { + gif->frames[frame].disposal_method = GIF_FRAME_RESTORE; + } + gif_data += (2 + gif_data[1]); + break; + + case GIF_EXTENSION_APPLICATION: + /* 14-byte+ Application Extension is: + * + * +0 CHAR Application Extension Label + * +1 CHAR Block Size + * +2 8CHARS Application Identifier + * +10 3CHARS Appl. Authentication Code + * +13 1-256 Application Data (Data sub-blocks) + */ + if (gif_bytes < 17) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + if ((gif_data[1] == 0x0b) && + (strncmp((const char *) gif_data + 2, + "NETSCAPE2.0", 11) == 0) && + (gif_data[13] == 0x03) && + (gif_data[14] == 0x01)) { + gif->loop_count = gif_data[15] | (gif_data[16] << 8); + } + gif_data += (2 + gif_data[1]); + break; + + case GIF_EXTENSION_COMMENT: + /* Move the pointer to the first data sub-block Skip 1 + * byte for the extension label + */ + ++gif_data; + break; + + default: + /* Move the pointer to the first data sub-block Skip 2 + * bytes for the extension label and size fields Skip + * the extension size itself + */ + if (gif_bytes < 2) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + gif_data += (2 + gif_data[1]); + } + + /* Repeatedly skip blocks until we get a zero block or run out + * of data This data is ignored by this gif decoder + */ + gif_bytes = (gif_end - gif_data); + block_size = 0; + while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) { + block_size = gif_data[0] + 1; + if ((gif_bytes -= block_size) < 0) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + gif_data += block_size; + } + ++gif_data; + } + + /* Set buffer position and return */ + gif->buffer_position = (gif_data - gif->gif_data); + return GIF_OK; +} + + +/** + * Attempts to initialise the next frame + * + * \param gif The animation context + * \return error code + * - GIF_INSUFFICIENT_DATA for insufficient data to do anything + * - GIF_FRAME_DATA_ERROR for GIF frame data error + * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process + * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame + * - GIF_DATA_ERROR for GIF error (invalid frame header) + * - GIF_OK for successful decoding + * - GIF_WORKING for successful decoding if more frames are expected +*/ +static gif_result gif_initialise_frame(gif_animation *gif) +{ + int frame; + gif_frame *temp_buf; + + unsigned char *gif_data, *gif_end; + int gif_bytes; + unsigned int flags = 0; + unsigned int width, height, offset_x, offset_y; + unsigned int block_size, colour_table_size; + bool first_image = true; + gif_result return_value; + + /* Get the frame to decode and our data position */ + frame = gif->frame_count; + + /* Get our buffer position etc. */ + gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position); + gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size); + gif_bytes = (gif_end - gif_data); + + /* Check if we've finished */ + if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) { + return GIF_OK; + } + + /* Check if there is enough data remaining. The shortest block of data + * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif + * trailer + */ + if (gif_bytes < 6) { + return GIF_INSUFFICIENT_DATA; + } + + /* We could theoretically get some junk data that gives us millions of + * frames, so we ensure that we don't have a silly number + */ + if (frame > 4096) { + return GIF_FRAME_DATA_ERROR; + } + + /* Get some memory to store our pointers in etc. */ + if ((int)gif->frame_holders <= frame) { + /* Allocate more memory */ + temp_buf = (gif_frame *)realloc(gif->frames, (frame + 1) * sizeof(gif_frame)); + if (temp_buf == NULL) { + return GIF_INSUFFICIENT_MEMORY; + } + gif->frames = temp_buf; + gif->frame_holders = frame + 1; + } + + /* Store our frame pointer. We would do it when allocating except we + * start off with one frame allocated so we can always use realloc. + */ + gif->frames[frame].frame_pointer = gif->buffer_position; + gif->frames[frame].display = false; + gif->frames[frame].virgin = true; + gif->frames[frame].disposal_method = 0; + gif->frames[frame].transparency = false; + gif->frames[frame].frame_delay = 100; + gif->frames[frame].redraw_required = false; + + /* Invalidate any previous decoding we have of this frame */ + if (gif->decoded_frame == frame) { + gif->decoded_frame = GIF_INVALID_FRAME; + } + + /* We pretend to initialise the frames, but really we just skip over + * all the data contained within. This is all basically a cut down + * version of gif_decode_frame that doesn't have any of the LZW bits in + * it. + */ + + /* Initialise any extensions */ + gif->buffer_position = gif_data - gif->gif_data; + return_value = gif_initialise_frame_extensions(gif, frame); + if (return_value != GIF_OK) { + return return_value; + } + gif_data = (gif->gif_data + gif->buffer_position); + gif_bytes = (gif_end - gif_data); + + /* Check if we've finished */ + if ((gif_bytes = (gif_end - gif_data)) < 1) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + + if (gif_data[0] == GIF_TRAILER) { + gif->buffer_position = (gif_data - gif->gif_data); + gif->frame_count = frame + 1; + return GIF_OK; + } + + /* If we're not done, there should be an image descriptor */ + if (gif_data[0] != GIF_IMAGE_SEPARATOR) { + return GIF_FRAME_DATA_ERROR; + } + + /* Do some simple boundary checking */ + if (gif_bytes < 10) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + offset_x = gif_data[1] | (gif_data[2] << 8); + offset_y = gif_data[3] | (gif_data[4] << 8); + width = gif_data[5] | (gif_data[6] << 8); + height = gif_data[7] | (gif_data[8] << 8); + + /* Set up the redraw characteristics. We have to check for extending + * the area due to multi-image frames. + */ + if (!first_image) { + if (gif->frames[frame].redraw_x > offset_x) { + gif->frames[frame].redraw_width += (gif->frames[frame].redraw_x - offset_x); + gif->frames[frame].redraw_x = offset_x; + } + + if (gif->frames[frame].redraw_y > offset_y) { + gif->frames[frame].redraw_height += (gif->frames[frame].redraw_y - offset_y); + gif->frames[frame].redraw_y = offset_y; + } + + if ((offset_x + width) > (gif->frames[frame].redraw_x + gif->frames[frame].redraw_width)) { + gif->frames[frame].redraw_width = (offset_x + width) - gif->frames[frame].redraw_x; + } + + if ((offset_y + height) > (gif->frames[frame].redraw_y + gif->frames[frame].redraw_height)) { + gif->frames[frame].redraw_height = (offset_y + height) - gif->frames[frame].redraw_y; + } + } else { + first_image = false; + gif->frames[frame].redraw_x = offset_x; + gif->frames[frame].redraw_y = offset_y; + gif->frames[frame].redraw_width = width; + gif->frames[frame].redraw_height = height; + } + + /* if we are clearing the background then we need to redraw enough to + * cover the previous frame too + */ + gif->frames[frame].redraw_required = ((gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) || + (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE)); + + /* Boundary checking - shouldn't ever happen except with junk data */ + if (gif_initialise_sprite(gif, (offset_x + width), (offset_y + height))) { + return GIF_INSUFFICIENT_MEMORY; + } + + /* Decode the flags */ + flags = gif_data[9]; + colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK); + + /* Move our data onwards and remember we've got a bit of this frame */ + gif_data += 10; + gif_bytes = (gif_end - gif_data); + gif->frame_count_partial = frame + 1; + + /* Skip the local colour table */ + if (flags & GIF_COLOUR_TABLE_MASK) { + gif_data += 3 * colour_table_size; + if ((gif_bytes = (gif_end - gif_data)) < 0) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + } + + /* Ensure we have a correct code size */ + if (gif_bytes < 1) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + if (gif_data[0] > LZW_CODE_MAX) { + return GIF_DATA_ERROR; + } + + /* Move our pointer to the actual image data */ + gif_data++; + --gif_bytes; + + /* Repeatedly skip blocks until we get a zero block or run out of data + * These blocks of image data are processed later by gif_decode_frame() + */ + block_size = 0; + while (block_size != 1) { + if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA; + block_size = gif_data[0] + 1; + /* Check if the frame data runs off the end of the file */ + if ((int)(gif_bytes - block_size) < 0) { + /* jcupitt 15/9/19 + * + * There was code here to set a TRAILER tag. But this + * wrote to the input buffer, which will not work for + * libvips, where buffers can be mmaped read only files. + * + * Instead, just signal insufficient frame data. + */ + return GIF_INSUFFICIENT_FRAME_DATA; + } else { + gif_bytes -= block_size; + gif_data += block_size; + } + } + + /* Add the frame and set the display flag */ + gif->buffer_position = gif_data - gif->gif_data; + gif->frame_count = frame + 1; + gif->frames[frame].display = true; + + /* Check if we've finished */ + if (gif_bytes < 1) { + return GIF_INSUFFICIENT_FRAME_DATA; + } else { + if (gif_data[0] == GIF_TRAILER) { + return GIF_OK; + } + } + return GIF_WORKING; +} + + + + +/** + * Skips the frame's extensions (which have been previously initialised) + * + * \param gif The animation context + * \return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the + * frame GIF_OK for successful decoding + */ +static gif_result gif_skip_frame_extensions(gif_animation *gif) +{ + unsigned char *gif_data, *gif_end; + int gif_bytes; + unsigned int block_size; + + /* Get our buffer position etc. */ + gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position); + gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size); + gif_bytes = (gif_end - gif_data); + + /* Skip the extensions */ + while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) { + ++gif_data; + if (gif_data >= gif_end) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + + /* Switch on extension label */ + switch(gif_data[0]) { + case GIF_EXTENSION_COMMENT: + /* Move the pointer to the first data sub-block + * 1 byte for the extension label + */ + ++gif_data; + break; + + default: + /* Move the pointer to the first data sub-block 2 bytes + * for the extension label and size fields Skip the + * extension size itself + */ + if (gif_data + 1 >= gif_end) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + gif_data += (2 + gif_data[1]); + } + + /* Repeatedly skip blocks until we get a zero block or run out + * of data This data is ignored by this gif decoder + */ + gif_bytes = (gif_end - gif_data); + block_size = 0; + while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) { + block_size = gif_data[0] + 1; + if ((gif_bytes -= block_size) < 0) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + gif_data += block_size; + } + ++gif_data; + } + + /* Set buffer position and return */ + gif->buffer_position = (gif_data - gif->gif_data); + return GIF_OK; +} + +static unsigned int gif_interlaced_line(int height, int y) { + if ((y << 3) < height) { + return (y << 3); + } + y -= ((height + 7) >> 3); + if ((y << 3) < (height - 4)) { + return (y << 3) + 4; + } + y -= ((height + 3) >> 3); + if ((y << 2) < (height - 2)) { + return (y << 2) + 2; + } + y -= ((height + 1) >> 2); + return (y << 1) + 1; +} + + +static gif_result gif_error_from_lzw(lzw_result l_res) +{ + static const gif_result g_res[] = { + [LZW_OK] = GIF_OK, + [LZW_OK_EOD] = GIF_END_OF_FRAME, + [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY, + [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA, + [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR, + [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR, + [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR, + }; + return g_res[l_res]; +} + + +/** + * decode a gif frame + * + * \param gif gif animation context. + * \param frame The frame number to decode. + * \param clear_image flag for image data being cleared instead of plotted. + */ +static gif_result +gif_internal_decode_frame(gif_animation *gif, + unsigned int frame, + bool clear_image) +{ + unsigned int index = 0; + unsigned char *gif_data, *gif_end; + int gif_bytes; + unsigned int width, height, offset_x, offset_y; + unsigned int flags, colour_table_size, interlace; + unsigned int *colour_table; + unsigned int *frame_data = 0; // Set to 0 for no warnings + unsigned int *frame_scanline; + unsigned int save_buffer_position; + unsigned int return_value = 0; + unsigned int x, y, decode_y, burst_bytes; + register unsigned char colour; + + /* Ensure this frame is supposed to be decoded */ + if (gif->frames[frame].display == false) { + return GIF_OK; + } + + /* Ensure the frame is in range to decode */ + if (frame > gif->frame_count_partial) { + return GIF_INSUFFICIENT_DATA; + } + + /* done if frame is already decoded */ + if ((!clear_image) && + ((int)frame == gif->decoded_frame)) { + return GIF_OK; + } + + /* Get the start of our frame data and the end of the GIF data */ + gif_data = gif->gif_data + gif->frames[frame].frame_pointer; + gif_end = gif->gif_data + gif->buffer_size; + gif_bytes = (gif_end - gif_data); + + /* + * Ensure there is a minimal amount of data to proceed. The shortest + * block of data is a 10-byte image descriptor + 1-byte gif trailer + */ + if (gif_bytes < 12) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + + /* Save the buffer position */ + save_buffer_position = gif->buffer_position; + gif->buffer_position = gif_data - gif->gif_data; + + /* Skip any extensions because they have allready been processed */ + if ((return_value = gif_skip_frame_extensions(gif)) != GIF_OK) { + goto gif_decode_frame_exit; + } + gif_data = (gif->gif_data + gif->buffer_position); + gif_bytes = (gif_end - gif_data); + + /* Ensure we have enough data for the 10-byte image descriptor + 1-byte + * gif trailer + */ + if (gif_bytes < 12) { + return_value = GIF_INSUFFICIENT_FRAME_DATA; + goto gif_decode_frame_exit; + } + + /* 10-byte Image Descriptor is: + * + * +0 CHAR Image Separator (0x2c) + * +1 SHORT Image Left Position + * +3 SHORT Image Top Position + * +5 SHORT Width + * +7 SHORT Height + * +9 CHAR __Packed Fields__ + * 1BIT Local Colour Table Flag + * 1BIT Interlace Flag + * 1BIT Sort Flag + * 2BITS Reserved + * 3BITS Size of Local Colour Table + */ + if (gif_data[0] != GIF_IMAGE_SEPARATOR) { + return_value = GIF_DATA_ERROR; + goto gif_decode_frame_exit; + } + offset_x = gif_data[1] | (gif_data[2] << 8); + offset_y = gif_data[3] | (gif_data[4] << 8); + width = gif_data[5] | (gif_data[6] << 8); + height = gif_data[7] | (gif_data[8] << 8); + + /* Boundary checking - shouldn't ever happen except unless the data has + * been modified since initialisation. + */ + if ((offset_x + width > gif->width) || + (offset_y + height > gif->height)) { + return_value = GIF_DATA_ERROR; + goto gif_decode_frame_exit; + } + + /* Decode the flags */ + flags = gif_data[9]; + colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK); + interlace = flags & GIF_INTERLACE_MASK; + + /* Advance data pointer to next block either colour table or image + * data. + */ + gif_data += 10; + gif_bytes = (gif_end - gif_data); + + /* Set up the colour table */ + if (flags & GIF_COLOUR_TABLE_MASK) { + if (gif_bytes < (int)(3 * colour_table_size)) { + return_value = GIF_INSUFFICIENT_FRAME_DATA; + goto gif_decode_frame_exit; + } + colour_table = gif->local_colour_table; + if (!clear_image) { + for (index = 0; index < colour_table_size; index++) { + /* Gif colour map contents are r,g,b. + * + * We want to pack them bytewise into the + * colour table, such that the red component + * is in byte 0 and the alpha component is in + * byte 3. + */ + unsigned char *entry = + (unsigned char *) &colour_table[index]; + + entry[0] = gif_data[0]; /* r */ + entry[1] = gif_data[1]; /* g */ + entry[2] = gif_data[2]; /* b */ + entry[3] = 0xff; /* a */ + + gif_data += 3; + } + } else { + gif_data += 3 * colour_table_size; + } + gif_bytes = (gif_end - gif_data); + } else { + colour_table = gif->global_colour_table; + } + + /* Ensure sufficient data remains */ + if (gif_bytes < 1) { + return_value = GIF_INSUFFICIENT_FRAME_DATA; + goto gif_decode_frame_exit; + } + + /* check for an end marker */ + if (gif_data[0] == GIF_TRAILER) { + return_value = GIF_OK; + goto gif_decode_frame_exit; + } + + /* Get the frame data */ + assert(gif->bitmap_callbacks.bitmap_get_buffer); + frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image); + if (!frame_data) { + return GIF_INSUFFICIENT_MEMORY; + } + + /* If we are clearing the image we just clear, if not decode */ + if (!clear_image) { + lzw_result res; + const uint8_t *stack_base; + const uint8_t *stack_pos; + + /* Ensure we have enough data for a 1-byte LZW code size + + * 1-byte gif trailer + */ + if (gif_bytes < 2) { + return_value = GIF_INSUFFICIENT_FRAME_DATA; + goto gif_decode_frame_exit; + } + + /* If we only have a 1-byte LZW code size + 1-byte gif trailer, + * we're finished + */ + if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) { + return_value = GIF_OK; + goto gif_decode_frame_exit; + } + + /* If the previous frame's disposal method requires we restore + * the background colour or this is the first frame, clear + * the frame data + */ + if ((frame == 0) || (gif->decoded_frame == GIF_INVALID_FRAME)) { + memset((char*)frame_data, + GIF_TRANSPARENT_COLOUR, + gif->width * gif->height * sizeof(int)); + gif->decoded_frame = frame; + /* The line below would fill the image with its + * background color, but because GIFs support + * transparency we likely wouldn't want to do that. */ + /* memset((char*)frame_data, colour_table[gif->background_index], gif->width * gif->height * sizeof(int)); */ + } else if ((frame != 0) && + (gif->frames[frame - 1].disposal_method == GIF_FRAME_CLEAR)) { + return_value = gif_internal_decode_frame(gif, + (frame - 1), + true); + if (return_value != GIF_OK) { + goto gif_decode_frame_exit; + } + + } else if ((frame != 0) && + (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) { + /* + * If the previous frame's disposal method requires we + * restore the previous image, find the last image set + * to "do not dispose" and get that frame data + */ + int last_undisposed_frame = frame - 2; + while ((last_undisposed_frame >= 0) && + (gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) { + last_undisposed_frame--; + } + + /* If we don't find one, clear the frame data */ + if (last_undisposed_frame == -1) { + /* see notes above on transparency + * vs. background color + */ + memset((char*)frame_data, + GIF_TRANSPARENT_COLOUR, + gif->width * gif->height * sizeof(int)); + } else { + return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false); + if (return_value != GIF_OK) { + goto gif_decode_frame_exit; + } + /* Get this frame's data */ + assert(gif->bitmap_callbacks.bitmap_get_buffer); + frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image); + if (!frame_data) { + return GIF_INSUFFICIENT_MEMORY; + } + } + } + gif->decoded_frame = frame; + gif->buffer_position = (gif_data - gif->gif_data) + 1; + + /* Initialise the LZW decoding */ + res = lzw_decode_init(gif->lzw_ctx, gif->gif_data, + gif->buffer_size, gif->buffer_position, + gif_data[0], &stack_base, &stack_pos); + if (res != LZW_OK) { + return gif_error_from_lzw(res); + } + + /* Decompress the data */ + for (y = 0; y < height; y++) { + if (interlace) { + decode_y = gif_interlaced_line(height, y) + offset_y; + } else { + decode_y = y + offset_y; + } + frame_scanline = frame_data + offset_x + (decode_y * gif->width); + + /* Rather than decoding pixel by pixel, we try to burst + * out streams of data to remove the need for end-of + * data checks every pixel. + */ + x = width; + while (x > 0) { + burst_bytes = (stack_pos - stack_base); + if (burst_bytes > 0) { + if (burst_bytes > x) { + burst_bytes = x; + } + x -= burst_bytes; + while (burst_bytes-- > 0) { + colour = *--stack_pos; + if (((gif->frames[frame].transparency) && + (colour != gif->frames[frame].transparency_index)) || + (!gif->frames[frame].transparency)) { + *frame_scanline = colour_table[colour]; + } + frame_scanline++; + } + } else { + res = lzw_decode(gif->lzw_ctx, &stack_pos); + if (res != LZW_OK) { + /* Unexpected end of frame, try to recover */ + if (res == LZW_OK_EOD) { + return_value = GIF_OK; + } else { + return_value = gif_error_from_lzw(res); + } + goto gif_decode_frame_exit; + } + } + } + } + } else { + /* Clear our frame */ + if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) { + for (y = 0; y < height; y++) { + frame_scanline = frame_data + offset_x + ((offset_y + y) * gif->width); + if (gif->frames[frame].transparency) { + memset(frame_scanline, + GIF_TRANSPARENT_COLOUR, + width * 4); + } else { + memset(frame_scanline, + colour_table[gif->background_index], + width * 4); + } + } + } + } +gif_decode_frame_exit: + + /* Check if we should test for optimisation */ + if (gif->frames[frame].virgin) { + if (gif->bitmap_callbacks.bitmap_test_opaque) { + gif->frames[frame].opaque = gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image); + } else { + gif->frames[frame].opaque = false; + } + gif->frames[frame].virgin = false; + } + + if (gif->bitmap_callbacks.bitmap_set_opaque) { + gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, gif->frames[frame].opaque); + } + + if (gif->bitmap_callbacks.bitmap_modified) { + gif->bitmap_callbacks.bitmap_modified(gif->frame_image); + } + + /* Restore the buffer position */ + gif->buffer_position = save_buffer_position; + + return return_value; +} + + +/* exported function documented in libnsgif.h */ +void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks) +{ + memset(gif, 0, sizeof(gif_animation)); + gif->bitmap_callbacks = *bitmap_callbacks; + gif->decoded_frame = GIF_INVALID_FRAME; +} + + +/* exported function documented in libnsgif.h */ +gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data) +{ + unsigned char *gif_data; + unsigned int index; + gif_result return_value; + + /* Initialize values */ + gif->buffer_size = size; + gif->gif_data = data; + + if (gif->lzw_ctx == NULL) { + lzw_result res = lzw_context_create( + (struct lzw_ctx **)&gif->lzw_ctx); + if (res != LZW_OK) { + return gif_error_from_lzw(res); + } + } + + /* Check for sufficient data to be a GIF (6-byte header + 7-byte + * logical screen descriptor) + */ + if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) { + return GIF_INSUFFICIENT_DATA; + } + + /* Get our current processing position */ + gif_data = gif->gif_data + gif->buffer_position; + + /* See if we should initialise the GIF */ + if (gif->buffer_position == 0) { + /* We want everything to be NULL before we start so we've no + * chance of freeing bad pointers (paranoia) + */ + gif->frame_image = NULL; + gif->frames = NULL; + gif->local_colour_table = NULL; + gif->global_colour_table = NULL; + + /* The caller may have been lazy and not reset any values */ + gif->frame_count = 0; + gif->frame_count_partial = 0; + gif->decoded_frame = GIF_INVALID_FRAME; + + /* 6-byte GIF file header is: + * + * +0 3CHARS Signature ('GIF') + * +3 3CHARS Version ('87a' or '89a') + */ + if (strncmp((const char *) gif_data, "GIF", 3) != 0) { + return GIF_DATA_ERROR; + } + gif_data += 3; + + /* Ensure GIF reports version 87a or 89a */ + /* + if ((strncmp(gif_data, "87a", 3) != 0) && + (strncmp(gif_data, "89a", 3) != 0)) + LOG(("Unknown GIF format - proceeding anyway")); + */ + gif_data += 3; + + /* 7-byte Logical Screen Descriptor is: + * + * +0 SHORT Logical Screen Width + * +2 SHORT Logical Screen Height + * +4 CHAR __Packed Fields__ + * 1BIT Global Colour Table Flag + * 3BITS Colour Resolution + * 1BIT Sort Flag + * 3BITS Size of Global Colour Table + * +5 CHAR Background Colour Index + * +6 CHAR Pixel Aspect Ratio + */ + gif->width = gif_data[0] | (gif_data[1] << 8); + gif->height = gif_data[2] | (gif_data[3] << 8); + gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK); + gif->colour_table_size = (2 << (gif_data[4] & GIF_COLOUR_TABLE_SIZE_MASK)); + gif->background_index = gif_data[5]; + gif->aspect_ratio = gif_data[6]; + gif->loop_count = 1; + gif_data += 7; + + /* Some broken GIFs report the size as the screen size they + * were created in. As such, we detect for the common cases and + * set the sizes as 0 if they are found which results in the + * GIF being the maximum size of the frames. + */ + if (((gif->width == 640) && (gif->height == 480)) || + ((gif->width == 640) && (gif->height == 512)) || + ((gif->width == 800) && (gif->height == 600)) || + ((gif->width == 1024) && (gif->height == 768)) || + ((gif->width == 1280) && (gif->height == 1024)) || + ((gif->width == 1600) && (gif->height == 1200)) || + ((gif->width == 0) || (gif->height == 0)) || + ((gif->width > 2048) || (gif->height > 2048))) { + gif->width = 1; + gif->height = 1; + } + + /* Allocate some data irrespective of whether we've got any + * colour tables. We always get the maximum size in case a GIF + * is lying to us. It's far better to give the wrong colours + * than to trample over some memory somewhere. + */ + gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int)); + gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int)); + if ((gif->global_colour_table == NULL) || + (gif->local_colour_table == NULL)) { + gif_finalise(gif); + return GIF_INSUFFICIENT_MEMORY; + } + + /* Set the first colour to a value that will never occur in + * reality so we know if we've processed it + */ + gif->global_colour_table[0] = GIF_PROCESS_COLOURS; + + /* Check if the GIF has no frame data (13-byte header + 1-byte + * termination block) Although generally useless, the GIF + * specification does not expressly prohibit this + */ + if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) { + if (gif_data[0] == GIF_TRAILER) { + return GIF_OK; + } else { + return GIF_INSUFFICIENT_DATA; + } + } + + /* Initialise enough workspace for a frame */ + if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) { + gif_finalise(gif); + return GIF_INSUFFICIENT_MEMORY; + } + gif->frame_holders = 1; + + /* Initialise the bitmap header */ + assert(gif->bitmap_callbacks.bitmap_create); + gif->frame_image = gif->bitmap_callbacks.bitmap_create(gif->width, gif->height); + if (gif->frame_image == NULL) { + gif_finalise(gif); + return GIF_INSUFFICIENT_MEMORY; + } + + /* Remember we've done this now */ + gif->buffer_position = gif_data - gif->gif_data; + } + + /* Do the colour map if we haven't already. As the top byte is always + * 0xff or 0x00 depending on the transparency we know if it's been + * filled in. + */ + if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) { + /* Check for a global colour map signified by bit 7 */ + if (gif->global_colours) { + if (gif->buffer_size < (gif->colour_table_size * 3 + GIF_STANDARD_HEADER_SIZE)) { + return GIF_INSUFFICIENT_DATA; + } + for (index = 0; index < gif->colour_table_size; index++) { + /* Gif colour map contents are r,g,b. + * + * We want to pack them bytewise into the + * colour table, such that the red component + * is in byte 0 and the alpha component is in + * byte 3. + */ + unsigned char *entry = (unsigned char *) &gif-> + global_colour_table[index]; + + entry[0] = gif_data[0]; /* r */ + entry[1] = gif_data[1]; /* g */ + entry[2] = gif_data[2]; /* b */ + entry[3] = 0xff; /* a */ + + gif_data += 3; + } + gif->buffer_position = (gif_data - gif->gif_data); + } else { + /* Create a default colour table with the first two + * colours as black and white + */ + unsigned int *entry = gif->global_colour_table; + + entry[0] = 0x00000000; + /* Force Alpha channel to opaque */ + ((unsigned char *) entry)[3] = 0xff; + + entry[1] = 0xffffffff; + } + } + + /* Repeatedly try to initialise frames */ + while ((return_value = gif_initialise_frame(gif)) == GIF_WORKING); + + /* If there was a memory error tell the caller */ + if ((return_value == GIF_INSUFFICIENT_MEMORY) || + (return_value == GIF_DATA_ERROR)) { + return return_value; + } + + /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a + * GIF_INSUFFICIENT_FRAME_DATA + */ + if ((return_value == GIF_INSUFFICIENT_DATA) && + (gif->frame_count_partial > 0)) { + return GIF_INSUFFICIENT_FRAME_DATA; + } + + /* Return how many we got */ + return return_value; +} + + +/* exported function documented in libnsgif.h */ +gif_result gif_decode_frame(gif_animation *gif, unsigned int frame) +{ + return gif_internal_decode_frame(gif, frame, false); +} + + +/* exported function documented in libnsgif.h */ +void gif_finalise(gif_animation *gif) +{ + /* Release all our memory blocks */ + if (gif->frame_image) { + assert(gif->bitmap_callbacks.bitmap_destroy); + gif->bitmap_callbacks.bitmap_destroy(gif->frame_image); + } + + gif->frame_image = NULL; + free(gif->frames); + gif->frames = NULL; + free(gif->local_colour_table); + gif->local_colour_table = NULL; + free(gif->global_colour_table); + gif->global_colour_table = NULL; + + lzw_context_destroy(gif->lzw_ctx); + gif->lzw_ctx = NULL; +} diff --git a/libvips/foreign/libnsgif/libnsgif.h b/libvips/foreign/libnsgif/libnsgif.h new file mode 100644 index 00000000..a819fec0 --- /dev/null +++ b/libvips/foreign/libnsgif/libnsgif.h @@ -0,0 +1,183 @@ +/* + * Copyright 2004 Richard Wilson + * Copyright 2008 Sean Fox + * + * 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 +#include + +/* 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 diff --git a/libvips/foreign/libnsgif/lzw.c b/libvips/foreign/libnsgif/lzw.c new file mode 100644 index 00000000..31cf7d4e --- /dev/null +++ b/libvips/foreign/libnsgif/lzw.c @@ -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 + */ + +#include +#include +#include +#include + +#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; +} diff --git a/libvips/foreign/libnsgif/lzw.h b/libvips/foreign/libnsgif/lzw.h new file mode 100644 index 00000000..385b4255 --- /dev/null +++ b/libvips/foreign/libnsgif/lzw.h @@ -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 + */ + +#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 diff --git a/libvips/foreign/libnsgif/utils/log.h b/libvips/foreign/libnsgif/utils/log.h new file mode 100644 index 00000000..1413374c --- /dev/null +++ b/libvips/foreign/libnsgif/utils/log.h @@ -0,0 +1,21 @@ +/* + * Copyright 2003 James Bursa + * Copyright 2004 John Tytgat + * + * 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 + +#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_ */ diff --git a/libvips/foreign/gifnsload.c b/libvips/foreign/nsgifload.c similarity index 59% rename from libvips/foreign/gifnsload.c rename to libvips/foreign/nsgifload.c index 6852740c..b9168694 100644 --- a/libvips/foreign/gifnsload.c +++ b/libvips/foreign/nsgifload.c @@ -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 -#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 ); +} From 48d9c5d0d440fa0f573f727dfec358d82430e59d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 28 Feb 2021 13:50:04 +0000 Subject: [PATCH 14/20] update test suite for new gif loader --- test/test-suite/images/cogs.png | Bin 4016 -> 3310 bytes test/test-suite/images/dispose-background.png | Bin 15709 -> 15443 bytes test/test-suite/test_foreign.py | 41 +++++------------- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/test/test-suite/images/cogs.png b/test/test-suite/images/cogs.png index 2256a6a81ca3cec95dfe51b6729c5667a60b40a6..e14f2fd2f08aaf0ca9be8070fb3ca7833aa72914 100644 GIT binary patch delta 3286 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 Uqjw_vMgRZ+07*qoM6N<$f(R^Ty8r+H delta 3998 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 diff --git a/test/test-suite/images/dispose-background.png b/test/test-suite/images/dispose-background.png index 0e29eb523ac0896d4743239c3231c5bdf738a326..ba31cc16c4a1e49478d4be8498afb87517985ffe 100644 GIT binary patch literal 15443 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 SK?U9th7@I0WlA0y2mT*=d=NkY literal 15709 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 diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index 31c082cb..e2727cea 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -1,5 +1,4 @@ # vim: set fileencoding=utf-8 : -import filecmp import sys import os import shutil @@ -38,7 +37,7 @@ class TestForeign: cls.cmyk.remove("icc-profile-data") im = pyvips.Image.new_from_file(GIF_FILE) - cls.onebit = im > 128 + cls.onebit = im[1] > 128 @classmethod def teardown_class(cls): @@ -800,10 +799,10 @@ class TestForeign: def test_gifload(self): def gif_valid(im): a = im(10, 10) - assert_almost_equal_objects(a, [33]) + assert_almost_equal_objects(a, [33, 33, 33, 255]) assert im.width == 159 assert im.height == 203 - assert im.bands == 1 + assert im.bands == 4 self.file_loader("gifload", GIF_FILE, gif_valid) self.buffer_loader("gifload_buffer", GIF_FILE, gif_valid) @@ -824,37 +823,21 @@ class TestForeign: x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, page=1, n=-1) assert x2.height == 4 * x1.height - animation = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1) - filename = temp_filename(self.tempdir, '.png') - animation.write_to_file(filename) - # Uncomment to see output file - # animation.write_to_file('cogs.png') - - assert filecmp.cmp(GIF_ANIM_EXPECTED_PNG_FILE, filename, shallow=False) + x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1) + x2 = pyvips.Image.new_from_file(GIF_ANIM_EXPECTED_PNG_FILE) + assert (x1 - x2).abs().max() == 0 @skip_if_no("gifload") def test_gifload_animation_dispose_background(self): - animation = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_BACKGROUND_FILE, n=-1) - - filename = temp_filename(self.tempdir, '.png') - animation.write_to_file(filename) - - # Uncomment to see output file - # animation.write_to_file('dispose-background.png') - - assert filecmp.cmp(GIF_ANIM_DISPOSE_BACKGROUND_EXPECTED_PNG_FILE, filename, shallow=False) + x1 = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_BACKGROUND_FILE, n=-1) + x2 = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_BACKGROUND_EXPECTED_PNG_FILE) + assert (x1 - x2).abs().max() == 0 @skip_if_no("gifload") def test_gifload_animation_dispose_previous(self): - animation = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_PREVIOUS_FILE, n=-1) - - filename = temp_filename(self.tempdir, '.png') - animation.write_to_file(filename) - - # Uncomment to see output file - # animation.write_to_file('dispose-previous.png') - - assert filecmp.cmp(GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE, filename, shallow=False) + x1 = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_PREVIOUS_FILE, n=-1) + x2 = pyvips.Image.new_from_file(GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE) + assert (x1 - x2).abs().max() == 0 @skip_if_no("svgload") def test_svgload(self): From 68332663fb34e93158770b1770d936991022e988 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 28 Feb 2021 14:29:27 +0000 Subject: [PATCH 15/20] update libnsgif from git master though unfortunately libnsgif seems to have broken DISPOSE_PREVIOUS handling again --- libvips/foreign/libnsgif/Makefile.am | 2 + libvips/foreign/libnsgif/README.md | 14 +-- libvips/foreign/libnsgif/libnsgif.c | 103 ++++++++++++++---- libvips/foreign/libnsgif/libnsgif.h | 9 ++ .../libnsgif/patches/no-input-modify.patch | 32 ++++++ libvips/foreign/libnsgif/update.sh | 22 ++++ 6 files changed, 152 insertions(+), 30 deletions(-) create mode 100644 libvips/foreign/libnsgif/patches/no-input-modify.patch create mode 100755 libvips/foreign/libnsgif/update.sh diff --git a/libvips/foreign/libnsgif/Makefile.am b/libvips/foreign/libnsgif/Makefile.am index 631db44f..0ab13f5b 100644 --- a/libvips/foreign/libnsgif/Makefile.am +++ b/libvips/foreign/libnsgif/Makefile.am @@ -1,6 +1,8 @@ EXTRA_DIST = \ README-ns \ README.md \ + patches \ + update.sh \ utils noinst_LTLIBRARIES = libnsgif.la diff --git a/libvips/foreign/libnsgif/README.md b/libvips/foreign/libnsgif/README.md index 1b241b3d..2ee2a0fc 100644 --- a/libvips/foreign/libnsgif/README.md +++ b/libvips/foreign/libnsgif/README.md @@ -1,18 +1,16 @@ # 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. +but within the libvips build system. # To update -When netsurf release a new version: +Run `./update.sh` to update this copy of libnsgif from the upstream repo. It +will also patch libnsgif.c to prevent it modifying the input. -* copy in sources -* reapply any patches from git, eg. no input modification +Last updated 28 Feb 2021. # To do -No attempt made to run tests or build docs. +No attempt made to run tests or build docs. Though the gif loader is tested as +part of the libvips test suite. diff --git a/libvips/foreign/libnsgif/libnsgif.c b/libvips/foreign/libnsgif/libnsgif.c index cf478f54..5550a613 100644 --- a/libvips/foreign/libnsgif/libnsgif.c +++ b/libvips/foreign/libnsgif/libnsgif.c @@ -440,7 +440,7 @@ static gif_result gif_initialise_frame(gif_animation *gif) * There was code here to set a TRAILER tag. But this * wrote to the input buffer, which will not work for * libvips, where buffers can be mmaped read only files. - * + * * Instead, just signal insufficient frame data. */ return GIF_INSUFFICIENT_FRAME_DATA; @@ -565,6 +565,73 @@ static gif_result gif_error_from_lzw(lzw_result l_res) return g_res[l_res]; } +static void gif__record_previous_frame(gif_animation *gif) +{ + bool need_alloc = gif->prev_frame == NULL; + const uint32_t *frame_data; + uint32_t *prev_frame; + + if (gif->decoded_frame == GIF_INVALID_FRAME || + gif->decoded_frame == gif->prev_index) { + /* No frame to copy, or already have this frame recorded. */ + return; + } + + assert(gif->bitmap_callbacks.bitmap_get_buffer); + frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image); + if (!frame_data) { + return; + } + + if (gif->prev_frame != NULL && + gif->width * gif->height > gif->prev_width * gif->prev_height) { + need_alloc = true; + } + + if (need_alloc) { + prev_frame = realloc(gif->prev_frame, + gif->width * gif->height * 4); + if (prev_frame == NULL) { + return; + } + } else { + prev_frame = gif->prev_frame; + } + + memcpy(prev_frame, frame_data, gif->width * gif->height * 4); + + gif->prev_frame = prev_frame; + gif->prev_width = gif->width; + gif->prev_height = gif->height; + gif->prev_index = gif->decoded_frame; +} + +static gif_result gif__recover_previous_frame(const gif_animation *gif) +{ + const uint32_t *prev_frame = gif->prev_frame; + unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height; + unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width; + uint32_t *frame_data; + + if (prev_frame == NULL) { + return GIF_FRAME_DATA_ERROR; + } + + assert(gif->bitmap_callbacks.bitmap_get_buffer); + frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image); + if (!frame_data) { + return GIF_INSUFFICIENT_MEMORY; + } + + for (unsigned y = 0; y < height; y++) { + memcpy(frame_data, prev_frame, width * 4); + + frame_data += gif->width; + prev_frame += gif->prev_width; + } + + return GIF_OK; +} /** * decode a gif frame @@ -578,6 +645,7 @@ gif_internal_decode_frame(gif_animation *gif, unsigned int frame, bool clear_image) { + gif_result err; unsigned int index = 0; unsigned char *gif_data, *gif_end; int gif_bytes; @@ -607,6 +675,11 @@ gif_internal_decode_frame(gif_animation *gif, return GIF_OK; } + if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) { + /* Store the previous frame for later restoration */ + gif__record_previous_frame(gif); + } + /* Get the start of our frame data and the end of the GIF data */ gif_data = gif->gif_data + gif->frames[frame].frame_pointer; gif_end = gif->gif_data + gif->buffer_size; @@ -783,34 +856,16 @@ gif_internal_decode_frame(gif_animation *gif, (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) { /* * If the previous frame's disposal method requires we - * restore the previous image, find the last image set - * to "do not dispose" and get that frame data + * restore the previous image, restore our saved image. */ - int last_undisposed_frame = frame - 2; - while ((last_undisposed_frame >= 0) && - (gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) { - last_undisposed_frame--; - } - - /* If we don't find one, clear the frame data */ - if (last_undisposed_frame == -1) { + err = gif__recover_previous_frame(gif); + if (err != GIF_OK) { /* see notes above on transparency * vs. background color */ memset((char*)frame_data, GIF_TRANSPARENT_COLOUR, gif->width * gif->height * sizeof(int)); - } else { - return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false); - if (return_value != GIF_OK) { - goto gif_decode_frame_exit; - } - /* Get this frame's data */ - assert(gif->bitmap_callbacks.bitmap_get_buffer); - frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image); - if (!frame_data) { - return GIF_INSUFFICIENT_MEMORY; - } } } gif->decoded_frame = frame; @@ -918,6 +973,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks) memset(gif, 0, sizeof(gif_animation)); gif->bitmap_callbacks = *bitmap_callbacks; gif->decoded_frame = GIF_INVALID_FRAME; + gif->prev_index = GIF_INVALID_FRAME; } @@ -1159,6 +1215,9 @@ void gif_finalise(gif_animation *gif) free(gif->global_colour_table); gif->global_colour_table = NULL; + free(gif->prev_frame); + gif->prev_frame = NULL; + lzw_context_destroy(gif->lzw_ctx); gif->lzw_ctx = NULL; } diff --git a/libvips/foreign/libnsgif/libnsgif.h b/libvips/foreign/libnsgif/libnsgif.h index a819fec0..50dc6883 100644 --- a/libvips/foreign/libnsgif/libnsgif.h +++ b/libvips/foreign/libnsgif/libnsgif.h @@ -136,6 +136,15 @@ typedef struct gif_animation { unsigned int *global_colour_table; /** local colour table */ unsigned int *local_colour_table; + + /** previous frame for GIF_FRAME_RESTORE */ + void *prev_frame; + /** previous frame index */ + int prev_index; + /** previous frame width */ + unsigned prev_width; + /** previous frame height */ + unsigned prev_height; } gif_animation; /** diff --git a/libvips/foreign/libnsgif/patches/no-input-modify.patch b/libvips/foreign/libnsgif/patches/no-input-modify.patch new file mode 100644 index 00000000..f358f307 --- /dev/null +++ b/libvips/foreign/libnsgif/patches/no-input-modify.patch @@ -0,0 +1,32 @@ +--- libnsgif-orig.c 2021-02-28 14:10:41.818557190 +0000 ++++ libnsgif.c 2021-02-28 14:11:55.942285930 +0000 +@@ -435,20 +435,15 @@ + block_size = gif_data[0] + 1; + /* Check if the frame data runs off the end of the file */ + if ((int)(gif_bytes - block_size) < 0) { +- /* Try to recover by signaling the end of the gif. +- * Once we get garbage data, there is no logical way to +- * determine where the next frame is. It's probably +- * better to partially load the gif than not at all. +- */ +- if (gif_bytes >= 2) { +- gif_data[0] = 0; +- gif_data[1] = GIF_TRAILER; +- gif_bytes = 1; +- ++gif_data; +- break; +- } else { +- return GIF_INSUFFICIENT_FRAME_DATA; +- } ++ /* jcupitt 15/9/19 ++ * ++ * There was code here to set a TRAILER tag. But this ++ * wrote to the input buffer, which will not work for ++ * libvips, where buffers can be mmaped read only files. ++ * ++ * Instead, just signal insufficient frame data. ++ */ ++ return GIF_INSUFFICIENT_FRAME_DATA; + } else { + gif_bytes -= block_size; + gif_data += block_size; diff --git a/libvips/foreign/libnsgif/update.sh b/libvips/foreign/libnsgif/update.sh new file mode 100755 index 00000000..dde02cf0 --- /dev/null +++ b/libvips/foreign/libnsgif/update.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# attempt to update our copy of libnsgif from the upstream repo + +set -e + +git clone git://git.netsurf-browser.org/libnsgif.git + +echo copying out source files ... +cp libnsgif/src/libnsgif.c . +cp libnsgif/include/libnsgif.h . +cp libnsgif/src/lzw.[ch] . +cp libnsgif/src/utils/log.h utils + +echo applying patches ... +for patch in patches/*.patch; do + patch -p0 <$patch +done + +echo cleaning up ... +rm -rf libnsgif + From 2a087df8fc709c67d69280206b609f2ec22babbb Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 2 Mar 2021 10:19:22 +0000 Subject: [PATCH 16/20] add some printf dbg to linnsgif, trying to find the GIF decode bug --- libvips/foreign/libnsgif/libnsgif.c | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libvips/foreign/libnsgif/libnsgif.c b/libvips/foreign/libnsgif/libnsgif.c index 5550a613..d8d5df19 100644 --- a/libvips/foreign/libnsgif/libnsgif.c +++ b/libvips/foreign/libnsgif/libnsgif.c @@ -571,6 +571,8 @@ static void gif__record_previous_frame(gif_animation *gif) const uint32_t *frame_data; uint32_t *prev_frame; + printf( "gif__record_previous_frame:\n" ); + if (gif->decoded_frame == GIF_INVALID_FRAME || gif->decoded_frame == gif->prev_index) { /* No frame to copy, or already have this frame recorded. */ @@ -613,6 +615,8 @@ static gif_result gif__recover_previous_frame(const gif_animation *gif) unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width; uint32_t *frame_data; + printf( "gif__recover_previous_frame:\n" ); + if (prev_frame == NULL) { return GIF_FRAME_DATA_ERROR; } @@ -659,6 +663,31 @@ gif_internal_decode_frame(gif_animation *gif, unsigned int x, y, decode_y, burst_bytes; register unsigned char colour; + printf( "gif_internal_decode_frame: frame = %d, clear_image = %d\n", + frame, clear_image ); + printf( "gif->frames[frame].disposal_method == " ); + switch( gif->frames[frame].disposal_method ) { + case 0: + printf( "NONE\n" ); + break; + + case GIF_FRAME_COMBINE: + printf( "COMBINE\n" ); + break; + + case GIF_FRAME_CLEAR: + printf( "CLEAR\n" ); + break; + + case GIF_FRAME_RESTORE: + printf( "RESTORE\n" ); + break; + + case GIF_FRAME_QUIRKS_RESTORE: + printf( "QUIRKS_RESTORE\n" ); + break; + } + /* Ensure this frame is supposed to be decoded */ if (gif->frames[frame].display == false) { return GIF_OK; @@ -880,6 +909,7 @@ gif_internal_decode_frame(gif_animation *gif, } /* Decompress the data */ + printf( "... decompressing frame data\n" ); for (y = 0; y < height; y++) { if (interlace) { decode_y = gif_interlaced_line(height, y) + offset_y; @@ -924,6 +954,7 @@ gif_internal_decode_frame(gif_animation *gif, } } } else { + printf( "... clearing frame\n" ); /* Clear our frame */ if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) { for (y = 0; y < height; y++) { From bf522eeffc5aa84a275b1d53e24d0daace59fa3e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 7 Mar 2021 14:37:33 +0000 Subject: [PATCH 17/20] fix libnsgif restore/record ordering --- libvips/foreign/libnsgif/libnsgif.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libvips/foreign/libnsgif/libnsgif.c b/libvips/foreign/libnsgif/libnsgif.c index d8d5df19..a97be035 100644 --- a/libvips/foreign/libnsgif/libnsgif.c +++ b/libvips/foreign/libnsgif/libnsgif.c @@ -704,11 +704,6 @@ gif_internal_decode_frame(gif_animation *gif, return GIF_OK; } - if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) { - /* Store the previous frame for later restoration */ - gif__record_previous_frame(gif); - } - /* Get the start of our frame data and the end of the GIF data */ gif_data = gif->gif_data + gif->frames[frame].frame_pointer; gif_end = gif->gif_data + gif->buffer_size; @@ -897,6 +892,12 @@ gif_internal_decode_frame(gif_animation *gif, gif->width * gif->height * sizeof(int)); } } + + if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) { + /* Store the previous frame for later restoration */ + gif__record_previous_frame(gif); + } + gif->decoded_frame = frame; gif->buffer_position = (gif_data - gif->gif_data) + 1; From f256359f8c20a545d1902b1d64199505507f3bf7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 7 Mar 2021 14:38:16 +0000 Subject: [PATCH 18/20] remove dbg printf --- libvips/foreign/libnsgif/libnsgif.c | 31 ----------------------------- 1 file changed, 31 deletions(-) diff --git a/libvips/foreign/libnsgif/libnsgif.c b/libvips/foreign/libnsgif/libnsgif.c index a97be035..6cce6b6b 100644 --- a/libvips/foreign/libnsgif/libnsgif.c +++ b/libvips/foreign/libnsgif/libnsgif.c @@ -571,8 +571,6 @@ static void gif__record_previous_frame(gif_animation *gif) const uint32_t *frame_data; uint32_t *prev_frame; - printf( "gif__record_previous_frame:\n" ); - if (gif->decoded_frame == GIF_INVALID_FRAME || gif->decoded_frame == gif->prev_index) { /* No frame to copy, or already have this frame recorded. */ @@ -615,8 +613,6 @@ static gif_result gif__recover_previous_frame(const gif_animation *gif) unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width; uint32_t *frame_data; - printf( "gif__recover_previous_frame:\n" ); - if (prev_frame == NULL) { return GIF_FRAME_DATA_ERROR; } @@ -663,31 +659,6 @@ gif_internal_decode_frame(gif_animation *gif, unsigned int x, y, decode_y, burst_bytes; register unsigned char colour; - printf( "gif_internal_decode_frame: frame = %d, clear_image = %d\n", - frame, clear_image ); - printf( "gif->frames[frame].disposal_method == " ); - switch( gif->frames[frame].disposal_method ) { - case 0: - printf( "NONE\n" ); - break; - - case GIF_FRAME_COMBINE: - printf( "COMBINE\n" ); - break; - - case GIF_FRAME_CLEAR: - printf( "CLEAR\n" ); - break; - - case GIF_FRAME_RESTORE: - printf( "RESTORE\n" ); - break; - - case GIF_FRAME_QUIRKS_RESTORE: - printf( "QUIRKS_RESTORE\n" ); - break; - } - /* Ensure this frame is supposed to be decoded */ if (gif->frames[frame].display == false) { return GIF_OK; @@ -910,7 +881,6 @@ gif_internal_decode_frame(gif_animation *gif, } /* Decompress the data */ - printf( "... decompressing frame data\n" ); for (y = 0; y < height; y++) { if (interlace) { decode_y = gif_interlaced_line(height, y) + offset_y; @@ -955,7 +925,6 @@ gif_internal_decode_frame(gif_animation *gif, } } } else { - printf( "... clearing frame\n" ); /* Clear our frame */ if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) { for (y = 0; y < height; y++) { From b51f4dab28083ab87495aec975c1d5570da333fc Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 7 Mar 2021 14:59:31 +0000 Subject: [PATCH 19/20] fix make dist --- configure.ac | 1 + cplusplus/include/vips/vips-operators.h | 3914 +++++++++++++++++++++++ po/POTFILES.in | 5 +- 3 files changed, 3919 insertions(+), 1 deletion(-) create mode 100644 cplusplus/include/vips/vips-operators.h diff --git a/configure.ac b/configure.ac index ba534ecb..3d257d8f 100644 --- a/configure.ac +++ b/configure.ac @@ -1348,6 +1348,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 diff --git a/cplusplus/include/vips/vips-operators.h b/cplusplus/include/vips/vips-operators.h new file mode 100644 index 00000000..e712335a --- /dev/null +++ b/cplusplus/include/vips/vips-operators.h @@ -0,0 +1,3914 @@ +// headers for vips operations +// Sun Mar 7 02:50:00 PM GMT 2021 +// this file is generated automatically, do not edit! + +/** + * Transform lch to cmc. + * @param options Set of options. + * @return Output image. + */ +VImage CMC2LCh( VOption *options = 0 ) const; + +/** + * Transform cmyk to xyz. + * @param options Set of options. + * @return Output image. + */ +VImage CMYK2XYZ( VOption *options = 0 ) const; + +/** + * Transform hsv to srgb. + * @param options Set of options. + * @return Output image. + */ +VImage HSV2sRGB( VOption *options = 0 ) const; + +/** + * Transform lch to cmc. + * @param options Set of options. + * @return Output image. + */ +VImage LCh2CMC( VOption *options = 0 ) const; + +/** + * Transform lch to lab. + * @param options Set of options. + * @return Output image. + */ +VImage LCh2Lab( VOption *options = 0 ) const; + +/** + * Transform lab to lch. + * @param options Set of options. + * @return Output image. + */ +VImage Lab2LCh( VOption *options = 0 ) const; + +/** + * Transform float lab to labq coding. + * @param options Set of options. + * @return Output image. + */ +VImage Lab2LabQ( VOption *options = 0 ) const; + +/** + * Transform float lab to signed short. + * @param options Set of options. + * @return Output image. + */ +VImage Lab2LabS( VOption *options = 0 ) const; + +/** + * Transform cielab to xyz. + * + * **Optional parameters** + * - **temp** -- Color temperature, std::vector. + * + * @param options Set of options. + * @return Output image. + */ +VImage Lab2XYZ( VOption *options = 0 ) const; + +/** + * Unpack a labq image to float lab. + * @param options Set of options. + * @return Output image. + */ +VImage LabQ2Lab( VOption *options = 0 ) const; + +/** + * Unpack a labq image to short lab. + * @param options Set of options. + * @return Output image. + */ +VImage LabQ2LabS( VOption *options = 0 ) const; + +/** + * Convert a labq image to srgb. + * @param options Set of options. + * @return Output image. + */ +VImage LabQ2sRGB( VOption *options = 0 ) const; + +/** + * Transform signed short lab to float. + * @param options Set of options. + * @return Output image. + */ +VImage LabS2Lab( VOption *options = 0 ) const; + +/** + * Transform short lab to labq coding. + * @param options Set of options. + * @return Output image. + */ +VImage LabS2LabQ( VOption *options = 0 ) const; + +/** + * Transform xyz to cmyk. + * @param options Set of options. + * @return Output image. + */ +VImage XYZ2CMYK( VOption *options = 0 ) const; + +/** + * Transform xyz to lab. + * + * **Optional parameters** + * - **temp** -- Colour temperature, std::vector. + * + * @param options Set of options. + * @return Output image. + */ +VImage XYZ2Lab( VOption *options = 0 ) const; + +/** + * Transform xyz to yxy. + * @param options Set of options. + * @return Output image. + */ +VImage XYZ2Yxy( VOption *options = 0 ) const; + +/** + * Transform xyz to scrgb. + * @param options Set of options. + * @return Output image. + */ +VImage XYZ2scRGB( VOption *options = 0 ) const; + +/** + * Transform yxy to xyz. + * @param options Set of options. + * @return Output image. + */ +VImage Yxy2XYZ( VOption *options = 0 ) const; + +/** + * Absolute value of an image. + * @param options Set of options. + * @return Output image. + */ +VImage abs( VOption *options = 0 ) const; + +/** + * Add two images. + * @param right Right-hand image argument. + * @param options Set of options. + * @return Output image. + */ +VImage add( VImage right, VOption *options = 0 ) const; + +/** + * Affine transform of an image. + * + * **Optional parameters** + * - **interpolate** -- Interpolate pixels with this, VInterpolate. + * - **oarea** -- Area of output to generate, std::vector. + * - **odx** -- Horizontal output displacement, double. + * - **ody** -- Vertical output displacement, double. + * - **idx** -- Horizontal input displacement, double. + * - **idy** -- Vertical input displacement, double. + * - **background** -- Background value, std::vector. + * - **premultiplied** -- Images have premultiplied alpha, bool. + * - **extend** -- How to generate the extra pixels, VipsExtend. + * + * @param matrix Transformation matrix. + * @param options Set of options. + * @return Output image. + */ +VImage affine( std::vector matrix, VOption *options = 0 ) const; + +/** + * Load an analyze6 image. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage analyzeload( const char *filename, VOption *options = 0 ); + +/** + * Join an array of images. + * + * **Optional parameters** + * - **across** -- Number of images across grid, int. + * - **shim** -- Pixels between images, int. + * - **background** -- Colour for new pixels, std::vector. + * - **halign** -- Align on the left, centre or right, VipsAlign. + * - **valign** -- Align on the top, centre or bottom, VipsAlign. + * - **hspacing** -- Horizontal spacing between images, int. + * - **vspacing** -- Vertical spacing between images, int. + * + * @param in Array of input images. + * @param options Set of options. + * @return Output image. + */ +static VImage arrayjoin( std::vector in, VOption *options = 0 ); + +/** + * Autorotate image by exif tag. + * @param options Set of options. + * @return Output image. + */ +VImage autorot( VOption *options = 0 ) const; + +/** + * Find image average. + * @param options Set of options. + * @return Output value. + */ +double avg( VOption *options = 0 ) const; + +/** + * Boolean operation across image bands. + * @param boolean boolean to perform. + * @param options Set of options. + * @return Output image. + */ +VImage bandbool( VipsOperationBoolean boolean, VOption *options = 0 ) const; + +/** + * Fold up x axis into bands. + * + * **Optional parameters** + * - **factor** -- Fold by this factor, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage bandfold( VOption *options = 0 ) const; + +/** + * Bandwise join a set of images. + * @param in Array of input images. + * @param options Set of options. + * @return Output image. + */ +static VImage bandjoin( std::vector in, VOption *options = 0 ); + +/** + * Append a constant band to an image. + * @param c Array of constants to add. + * @param options Set of options. + * @return Output image. + */ +VImage bandjoin_const( std::vector c, VOption *options = 0 ) const; + +/** + * Band-wise average. + * @param options Set of options. + * @return Output image. + */ +VImage bandmean( VOption *options = 0 ) const; + +/** + * Band-wise rank of a set of images. + * + * **Optional parameters** + * - **index** -- Select this band element from sorted list, int. + * + * @param in Array of input images. + * @param options Set of options. + * @return Output image. + */ +static VImage bandrank( std::vector in, VOption *options = 0 ); + +/** + * Unfold image bands into x axis. + * + * **Optional parameters** + * - **factor** -- Unfold by this factor, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage bandunfold( VOption *options = 0 ) const; + +/** + * Make a black image. + * + * **Optional parameters** + * - **bands** -- Number of bands in image, int. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage black( int width, int height, VOption *options = 0 ); + +/** + * Boolean operation on two images. + * @param right Right-hand image argument. + * @param boolean boolean to perform. + * @param options Set of options. + * @return Output image. + */ +VImage boolean( VImage right, VipsOperationBoolean boolean, VOption *options = 0 ) const; + +/** + * Boolean operations against a constant. + * @param boolean boolean to perform. + * @param c Array of constants. + * @param options Set of options. + * @return Output image. + */ +VImage boolean_const( VipsOperationBoolean boolean, std::vector c, VOption *options = 0 ) const; + +/** + * Build a look-up table. + * @param options Set of options. + * @return Output image. + */ +VImage buildlut( VOption *options = 0 ) const; + +/** + * Byteswap an image. + * @param options Set of options. + * @return Output image. + */ +VImage byteswap( VOption *options = 0 ) const; + +/** + * Cache an image. + * + * **Optional parameters** + * - **max_tiles** -- Maximum number of tiles to cache, int. + * - **tile_height** -- Tile height in pixels, int. + * - **tile_width** -- Tile width in pixels, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage cache( VOption *options = 0 ) const; + +/** + * Canny edge detector. + * + * **Optional parameters** + * - **sigma** -- Sigma of Gaussian, double. + * - **precision** -- Convolve with this precision, VipsPrecision. + * + * @param options Set of options. + * @return Output image. + */ +VImage canny( VOption *options = 0 ) const; + +/** + * Use pixel values to pick cases from an array of images. + * @param cases Array of case images. + * @param options Set of options. + * @return Output image. + */ +VImage case_image( std::vector cases, VOption *options = 0 ) const; + +/** + * Cast an image. + * + * **Optional parameters** + * - **shift** -- Shift integer values up and down, bool. + * + * @param format Format to cast to. + * @param options Set of options. + * @return Output image. + */ +VImage cast( VipsBandFormat format, VOption *options = 0 ) const; + +/** + * Convert to a new colorspace. + * + * **Optional parameters** + * - **source_space** -- Source color space, VipsInterpretation. + * + * @param space Destination color space. + * @param options Set of options. + * @return Output image. + */ +VImage colourspace( VipsInterpretation space, VOption *options = 0 ) const; + +/** + * Convolve with rotating mask. + * + * **Optional parameters** + * - **times** -- Rotate and convolve this many times, int. + * - **angle** -- Rotate mask by this much between convolutions, VipsAngle45. + * - **combine** -- Combine convolution results like this, VipsCombine. + * - **precision** -- Convolve with this precision, VipsPrecision. + * - **layers** -- Use this many layers in approximation, int. + * - **cluster** -- Cluster lines closer than this in approximation, int. + * + * @param mask Input matrix image. + * @param options Set of options. + * @return Output image. + */ +VImage compass( VImage mask, VOption *options = 0 ) const; + +/** + * Perform a complex operation on an image. + * @param cmplx complex to perform. + * @param options Set of options. + * @return Output image. + */ +VImage complex( VipsOperationComplex cmplx, VOption *options = 0 ) const; + +/** + * Complex binary operations on two images. + * @param right Right-hand image argument. + * @param cmplx binary complex operation to perform. + * @param options Set of options. + * @return Output image. + */ +VImage complex2( VImage right, VipsOperationComplex2 cmplx, VOption *options = 0 ) const; + +/** + * Form a complex image from two real images. + * @param right Right-hand image argument. + * @param options Set of options. + * @return Output image. + */ +VImage complexform( VImage right, VOption *options = 0 ) const; + +/** + * Get a component from a complex image. + * @param get complex to perform. + * @param options Set of options. + * @return Output image. + */ +VImage complexget( VipsOperationComplexget get, VOption *options = 0 ) const; + +/** + * Blend an array of images with an array of blend modes. + * + * **Optional parameters** + * - **x** -- Array of x coordinates to join at, std::vector. + * - **y** -- Array of y coordinates to join at, std::vector. + * - **compositing_space** -- Composite images in this colour space, VipsInterpretation. + * - **premultiplied** -- Images have premultiplied alpha, bool. + * + * @param in Array of input images. + * @param mode Array of VipsBlendMode to join with. + * @param options Set of options. + * @return Output image. + */ +static VImage composite( std::vector in, std::vector mode, VOption *options = 0 ); + +/** + * Blend a pair of images with a blend mode. + * + * **Optional parameters** + * - **x** -- x position of overlay, int. + * - **y** -- y position of overlay, int. + * - **compositing_space** -- Composite images in this colour space, VipsInterpretation. + * - **premultiplied** -- Images have premultiplied alpha, bool. + * + * @param overlay Overlay image. + * @param mode VipsBlendMode to join with. + * @param options Set of options. + * @return Output image. + */ +VImage composite2( VImage overlay, VipsBlendMode mode, VOption *options = 0 ) const; + +/** + * Convolution operation. + * + * **Optional parameters** + * - **precision** -- Convolve with this precision, VipsPrecision. + * - **layers** -- Use this many layers in approximation, int. + * - **cluster** -- Cluster lines closer than this in approximation, int. + * + * @param mask Input matrix image. + * @param options Set of options. + * @return Output image. + */ +VImage conv( VImage mask, VOption *options = 0 ) const; + +/** + * Approximate integer convolution. + * + * **Optional parameters** + * - **layers** -- Use this many layers in approximation, int. + * - **cluster** -- Cluster lines closer than this in approximation, int. + * + * @param mask Input matrix image. + * @param options Set of options. + * @return Output image. + */ +VImage conva( VImage mask, VOption *options = 0 ) const; + +/** + * Approximate separable integer convolution. + * + * **Optional parameters** + * - **layers** -- Use this many layers in approximation, int. + * + * @param mask Input matrix image. + * @param options Set of options. + * @return Output image. + */ +VImage convasep( VImage mask, VOption *options = 0 ) const; + +/** + * Float convolution operation. + * @param mask Input matrix image. + * @param options Set of options. + * @return Output image. + */ +VImage convf( VImage mask, VOption *options = 0 ) const; + +/** + * Int convolution operation. + * @param mask Input matrix image. + * @param options Set of options. + * @return Output image. + */ +VImage convi( VImage mask, VOption *options = 0 ) const; + +/** + * Seperable convolution operation. + * + * **Optional parameters** + * - **precision** -- Convolve with this precision, VipsPrecision. + * - **layers** -- Use this many layers in approximation, int. + * - **cluster** -- Cluster lines closer than this in approximation, int. + * + * @param mask Input matrix image. + * @param options Set of options. + * @return Output image. + */ +VImage convsep( VImage mask, VOption *options = 0 ) const; + +/** + * Copy an image. + * + * **Optional parameters** + * - **swap** -- Swap bytes in image between little and big-endian, bool. + * - **width** -- Image width in pixels, int. + * - **height** -- Image height in pixels, int. + * - **bands** -- Number of bands in image, int. + * - **format** -- Pixel format in image, VipsBandFormat. + * - **coding** -- Pixel coding, VipsCoding. + * - **interpretation** -- Pixel interpretation, VipsInterpretation. + * - **xres** -- Horizontal resolution in pixels/mm, double. + * - **yres** -- Vertical resolution in pixels/mm, double. + * - **xoffset** -- Horizontal offset of origin, int. + * - **yoffset** -- Vertical offset of origin, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage copy( VOption *options = 0 ) const; + +/** + * Count lines in an image. + * @param direction Countlines left-right or up-down. + * @param options Set of options. + * @return Number of lines. + */ +double countlines( VipsDirection direction, VOption *options = 0 ) const; + +/** + * Extract an area from an image. + * @param left Left edge of extract area. + * @param top Top edge of extract area. + * @param width Width of extract area. + * @param height Height of extract area. + * @param options Set of options. + * @return Output image. + */ +VImage crop( int left, int top, int width, int height, VOption *options = 0 ) const; + +/** + * Load csv. + * + * **Optional parameters** + * - **skip** -- Skip this many lines at the start of the file, int. + * - **lines** -- Read this many lines from the file, int. + * - **whitespace** -- Set of whitespace characters, const char *. + * - **separator** -- Set of separator characters, const char *. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage csvload( const char *filename, VOption *options = 0 ); + +/** + * Load csv. + * + * **Optional parameters** + * - **skip** -- Skip this many lines at the start of the file, int. + * - **lines** -- Read this many lines from the file, int. + * - **whitespace** -- Set of whitespace characters, const char *. + * - **separator** -- Set of separator characters, const char *. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage csvload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to csv. + * + * **Optional parameters** + * - **separator** -- Separator characters, const char *. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void csvsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to csv. + * + * **Optional parameters** + * - **separator** -- Separator characters, const char *. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void csvsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Calculate de00. + * @param right Right-hand input image. + * @param options Set of options. + * @return Output image. + */ +VImage dE00( VImage right, VOption *options = 0 ) const; + +/** + * Calculate de76. + * @param right Right-hand input image. + * @param options Set of options. + * @return Output image. + */ +VImage dE76( VImage right, VOption *options = 0 ) const; + +/** + * Calculate decmc. + * @param right Right-hand input image. + * @param options Set of options. + * @return Output image. + */ +VImage dECMC( VImage right, VOption *options = 0 ) const; + +/** + * Find image standard deviation. + * @param options Set of options. + * @return Output value. + */ +double deviate( VOption *options = 0 ) const; + +/** + * Divide two images. + * @param right Right-hand image argument. + * @param options Set of options. + * @return Output image. + */ +VImage divide( VImage right, VOption *options = 0 ) const; + +/** + * Draw a circle on an image. + * + * **Optional parameters** + * - **fill** -- Draw a solid object, bool. + * + * @param ink Color for pixels. + * @param cx Centre of draw_circle. + * @param cy Centre of draw_circle. + * @param radius Radius in pixels. + * @param options Set of options. + */ +void draw_circle( std::vector ink, int cx, int cy, int radius, VOption *options = 0 ) const; + +/** + * Flood-fill an area. + * + * **Optional parameters** + * - **test** -- Test pixels in this image, VImage. + * - **equal** -- DrawFlood while equal to edge, bool. + * + * @param ink Color for pixels. + * @param x DrawFlood start point. + * @param y DrawFlood start point. + * @param options Set of options. + */ +void draw_flood( std::vector ink, int x, int y, VOption *options = 0 ) const; + +/** + * Paint an image into another image. + * + * **Optional parameters** + * - **mode** -- Combining mode, VipsCombineMode. + * + * @param sub Sub-image to insert into main image. + * @param x Draw image here. + * @param y Draw image here. + * @param options Set of options. + */ +void draw_image( VImage sub, int x, int y, VOption *options = 0 ) const; + +/** + * Draw a line on an image. + * @param ink Color for pixels. + * @param x1 Start of draw_line. + * @param y1 Start of draw_line. + * @param x2 End of draw_line. + * @param y2 End of draw_line. + * @param options Set of options. + */ +void draw_line( std::vector ink, int x1, int y1, int x2, int y2, VOption *options = 0 ) const; + +/** + * Draw a mask on an image. + * @param ink Color for pixels. + * @param mask Mask of pixels to draw. + * @param x Draw mask here. + * @param y Draw mask here. + * @param options Set of options. + */ +void draw_mask( std::vector ink, VImage mask, int x, int y, VOption *options = 0 ) const; + +/** + * Paint a rectangle on an image. + * + * **Optional parameters** + * - **fill** -- Draw a solid object, bool. + * + * @param ink Color for pixels. + * @param left Rect to fill. + * @param top Rect to fill. + * @param width Rect to fill. + * @param height Rect to fill. + * @param options Set of options. + */ +void draw_rect( std::vector ink, int left, int top, int width, int height, VOption *options = 0 ) const; + +/** + * Blur a rectangle on an image. + * @param left Rect to fill. + * @param top Rect to fill. + * @param width Rect to fill. + * @param height Rect to fill. + * @param options Set of options. + */ +void draw_smudge( int left, int top, int width, int height, VOption *options = 0 ) const; + +/** + * Save image to deepzoom file. + * + * **Optional parameters** + * - **dirname** -- Directory name to save to, const char *. + * - **basename** -- Base name to save to, const char *. + * - **layout** -- Directory layout, VipsForeignDzLayout. + * - **suffix** -- Filename suffix for tiles, const char *. + * - **overlap** -- Tile overlap in pixels, int. + * - **tile_size** -- Tile size in pixels, int. + * - **tile_height** -- Tile height in pixels, int. + * - **tile_width** -- Tile width in pixels, int. + * - **centre** -- Center image in tile, bool. + * - **depth** -- Pyramid depth, VipsForeignDzDepth. + * - **angle** -- Rotate image during save, VipsAngle. + * - **container** -- Pyramid container type, VipsForeignDzContainer. + * - **properties** -- Write a properties file to the output directory, bool. + * - **compression** -- ZIP deflate compression level, int. + * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. + * - **skip_blanks** -- Skip tiles which are nearly equal to the background, int. + * - **no_strip** -- Don't strip tile metadata, bool. + * - **id** -- Resource ID, const char *. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void dzsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to dz buffer. + * + * **Optional parameters** + * - **dirname** -- Directory name to save to, const char *. + * - **basename** -- Base name to save to, const char *. + * - **layout** -- Directory layout, VipsForeignDzLayout. + * - **suffix** -- Filename suffix for tiles, const char *. + * - **overlap** -- Tile overlap in pixels, int. + * - **tile_size** -- Tile size in pixels, int. + * - **tile_height** -- Tile height in pixels, int. + * - **tile_width** -- Tile width in pixels, int. + * - **centre** -- Center image in tile, bool. + * - **depth** -- Pyramid depth, VipsForeignDzDepth. + * - **angle** -- Rotate image during save, VipsAngle. + * - **container** -- Pyramid container type, VipsForeignDzContainer. + * - **properties** -- Write a properties file to the output directory, bool. + * - **compression** -- ZIP deflate compression level, int. + * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. + * - **skip_blanks** -- Skip tiles which are nearly equal to the background, int. + * - **no_strip** -- Don't strip tile metadata, bool. + * - **id** -- Resource ID, const char *. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *dzsave_buffer( VOption *options = 0 ) const; + +/** + * Embed an image in a larger image. + * + * **Optional parameters** + * - **extend** -- How to generate the extra pixels, VipsExtend. + * - **background** -- Color for background pixels, std::vector. + * + * @param x Left edge of input in output. + * @param y Top edge of input in output. + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +VImage embed( int x, int y, int width, int height, VOption *options = 0 ) const; + +/** + * Extract an area from an image. + * @param left Left edge of extract area. + * @param top Top edge of extract area. + * @param width Width of extract area. + * @param height Height of extract area. + * @param options Set of options. + * @return Output image. + */ +VImage extract_area( int left, int top, int width, int height, VOption *options = 0 ) const; + +/** + * Extract band from an image. + * + * **Optional parameters** + * - **n** -- Number of bands to extract, int. + * + * @param band Band to extract. + * @param options Set of options. + * @return Output image. + */ +VImage extract_band( int band, VOption *options = 0 ) const; + +/** + * Make an image showing the eye's spatial response. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **factor** -- Maximum spatial frequency, double. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage eye( int width, int height, VOption *options = 0 ); + +/** + * False-color an image. + * @param options Set of options. + * @return Output image. + */ +VImage falsecolour( VOption *options = 0 ) const; + +/** + * Fast correlation. + * @param ref Input reference image. + * @param options Set of options. + * @return Output image. + */ +VImage fastcor( VImage ref, VOption *options = 0 ) const; + +/** + * Fill image zeros with nearest non-zero pixel. + * @param options Set of options. + * @return Value of nearest non-zero pixel. + */ +VImage fill_nearest( VOption *options = 0 ) const; + +/** + * Search an image for non-edge areas. + * + * **Optional parameters** + * - **threshold** -- Object threshold, double. + * - **background** -- Color for background pixels, std::vector. + * + * @param top Top edge of extract area. + * @param width Width of extract area. + * @param height Height of extract area. + * @param options Set of options. + * @return Left edge of image. + */ +int find_trim( int *top, int *width, int *height, VOption *options = 0 ) const; + +/** + * Load a fits image. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage fitsload( const char *filename, VOption *options = 0 ); + +/** + * Load fits from a source. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage fitsload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to fits file. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void fitssave( const char *filename, VOption *options = 0 ) const; + +/** + * Flatten alpha out of an image. + * + * **Optional parameters** + * - **background** -- Background value, std::vector. + * - **max_alpha** -- Maximum value of alpha channel, double. + * + * @param options Set of options. + * @return Output image. + */ +VImage flatten( VOption *options = 0 ) const; + +/** + * Flip an image. + * @param direction Direction to flip image. + * @param options Set of options. + * @return Output image. + */ +VImage flip( VipsDirection direction, VOption *options = 0 ) const; + +/** + * Transform float rgb to radiance coding. + * @param options Set of options. + * @return Output image. + */ +VImage float2rad( VOption *options = 0 ) const; + +/** + * Make a fractal surface. + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param fractal_dimension Fractal dimension. + * @param options Set of options. + * @return Output image. + */ +static VImage fractsurf( int width, int height, double fractal_dimension, VOption *options = 0 ); + +/** + * Frequency-domain filtering. + * @param mask Input mask image. + * @param options Set of options. + * @return Output image. + */ +VImage freqmult( VImage mask, VOption *options = 0 ) const; + +/** + * Forward fft. + * @param options Set of options. + * @return Output image. + */ +VImage fwfft( VOption *options = 0 ) const; + +/** + * Gamma an image. + * + * **Optional parameters** + * - **exponent** -- Gamma factor, double. + * + * @param options Set of options. + * @return Output image. + */ +VImage gamma( VOption *options = 0 ) const; + +/** + * Gaussian blur. + * + * **Optional parameters** + * - **min_ampl** -- Minimum amplitude of Gaussian, double. + * - **precision** -- Convolve with this precision, VipsPrecision. + * + * @param sigma Sigma of Gaussian. + * @param options Set of options. + * @return Output image. + */ +VImage gaussblur( double sigma, VOption *options = 0 ) const; + +/** + * Make a gaussian image. + * + * **Optional parameters** + * - **separable** -- Generate separable Gaussian, bool. + * - **integer** -- Generate integer Gaussian, bool. + * - **precision** -- Generate with this precision, VipsPrecision. + * + * @param sigma Sigma of Gaussian. + * @param min_ampl Minimum amplitude of Gaussian. + * @param options Set of options. + * @return Output image. + */ +static VImage gaussmat( double sigma, double min_ampl, VOption *options = 0 ); + +/** + * Make a gaussnoise image. + * + * **Optional parameters** + * - **sigma** -- Standard deviation of pixels in generated image, double. + * - **mean** -- Mean of pixels in generated image, double. + * - **seed** -- Random number seed, int. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage gaussnoise( int width, int height, VOption *options = 0 ); + +/** + * Read a point from an image. + * @param x Point to read. + * @param y Point to read. + * @param options Set of options. + * @return Array of output values. + */ +std::vector getpoint( int x, int y, VOption *options = 0 ) const; + +/** + * Load gif with libnsgif. + * + * **Optional parameters** + * - **n** -- Load this many pages, int. + * - **page** -- Load this page from the file, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage gifload( const char *filename, VOption *options = 0 ); + +/** + * Load gif with libnsgif. + * + * **Optional parameters** + * - **n** -- Load this many pages, int. + * - **page** -- Load this page from the file, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage gifload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load gif from source. + * + * **Optional parameters** + * - **n** -- Load this many pages, int. + * - **page** -- Load this page from the file, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage gifload_source( VSource source, VOption *options = 0 ); + +/** + * Global balance an image mosaic. + * + * **Optional parameters** + * - **gamma** -- Image gamma, double. + * - **int_output** -- Integer output, bool. + * + * @param options Set of options. + * @return Output image. + */ +VImage globalbalance( VOption *options = 0 ) const; + +/** + * Place an image within a larger image with a certain gravity. + * + * **Optional parameters** + * - **extend** -- How to generate the extra pixels, VipsExtend. + * - **background** -- Color for background pixels, std::vector. + * + * @param direction direction to place image within width/height. + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +VImage gravity( VipsCompassDirection direction, int width, int height, VOption *options = 0 ) const; + +/** + * Make a grey ramp image. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage grey( int width, int height, VOption *options = 0 ); + +/** + * Grid an image. + * @param tile_height chop into tiles this high. + * @param across number of tiles across. + * @param down number of tiles down. + * @param options Set of options. + * @return Output image. + */ +VImage grid( int tile_height, int across, int down, VOption *options = 0 ) const; + +/** + * Load a heif image. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **thumbnail** -- Fetch thumbnail image, bool. + * - **autorotate** -- Rotate image using exif orientation, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage heifload( const char *filename, VOption *options = 0 ); + +/** + * Load a heif image. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **thumbnail** -- Fetch thumbnail image, bool. + * - **autorotate** -- Rotate image using exif orientation, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage heifload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load a heif image. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **thumbnail** -- Fetch thumbnail image, bool. + * - **autorotate** -- Rotate image using exif orientation, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage heifload_source( VSource source, VOption *options = 0 ); + +/** + * Save image in heif format. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **lossless** -- Enable lossless compression, bool. + * - **compression** -- Compression format, VipsForeignHeifCompression. + * - **speed** -- CPU effort, int. + * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to load from. + * @param options Set of options. + */ +void heifsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image in heif format. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **lossless** -- Enable lossless compression, bool. + * - **compression** -- Compression format, VipsForeignHeifCompression. + * - **speed** -- CPU effort, int. + * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *heifsave_buffer( VOption *options = 0 ) const; + +/** + * Save image in heif format. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **lossless** -- Enable lossless compression, bool. + * - **compression** -- Compression format, VipsForeignHeifCompression. + * - **speed** -- CPU effort, int. + * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void heifsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Form cumulative histogram. + * @param options Set of options. + * @return Output image. + */ +VImage hist_cum( VOption *options = 0 ) const; + +/** + * Estimate image entropy. + * @param options Set of options. + * @return Output value. + */ +double hist_entropy( VOption *options = 0 ) const; + +/** + * Histogram equalisation. + * + * **Optional parameters** + * - **band** -- Equalise with this band, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage hist_equal( VOption *options = 0 ) const; + +/** + * Find image histogram. + * + * **Optional parameters** + * - **band** -- Find histogram of band, int. + * + * @param options Set of options. + * @return Output histogram. + */ +VImage hist_find( VOption *options = 0 ) const; + +/** + * Find indexed image histogram. + * + * **Optional parameters** + * - **combine** -- Combine bins like this, VipsCombine. + * + * @param index Index image. + * @param options Set of options. + * @return Output histogram. + */ +VImage hist_find_indexed( VImage index, VOption *options = 0 ) const; + +/** + * Find n-dimensional image histogram. + * + * **Optional parameters** + * - **bins** -- Number of bins in each dimension, int. + * + * @param options Set of options. + * @return Output histogram. + */ +VImage hist_find_ndim( VOption *options = 0 ) const; + +/** + * Test for monotonicity. + * @param options Set of options. + * @return true if in is monotonic. + */ +bool hist_ismonotonic( VOption *options = 0 ) const; + +/** + * Local histogram equalisation. + * + * **Optional parameters** + * - **max_slope** -- Maximum slope (CLAHE), int. + * + * @param width Window width in pixels. + * @param height Window height in pixels. + * @param options Set of options. + * @return Output image. + */ +VImage hist_local( int width, int height, VOption *options = 0 ) const; + +/** + * Match two histograms. + * @param ref Reference histogram. + * @param options Set of options. + * @return Output image. + */ +VImage hist_match( VImage ref, VOption *options = 0 ) const; + +/** + * Normalise histogram. + * @param options Set of options. + * @return Output image. + */ +VImage hist_norm( VOption *options = 0 ) const; + +/** + * Plot histogram. + * @param options Set of options. + * @return Output image. + */ +VImage hist_plot( VOption *options = 0 ) const; + +/** + * Find hough circle transform. + * + * **Optional parameters** + * - **scale** -- Scale down dimensions by this factor, int. + * - **min_radius** -- Smallest radius to search for, int. + * - **max_radius** -- Largest radius to search for, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage hough_circle( VOption *options = 0 ) const; + +/** + * Find hough line transform. + * + * **Optional parameters** + * - **width** -- horizontal size of parameter space, int. + * - **height** -- Vertical size of parameter space, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage hough_line( VOption *options = 0 ) const; + +/** + * Output to device with icc profile. + * + * **Optional parameters** + * - **pcs** -- Set Profile Connection Space, VipsPCS. + * - **intent** -- Rendering intent, VipsIntent. + * - **output_profile** -- Filename to load output profile from, const char *. + * - **depth** -- Output device space depth in bits, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage icc_export( VOption *options = 0 ) const; + +/** + * Import from device with icc profile. + * + * **Optional parameters** + * - **pcs** -- Set Profile Connection Space, VipsPCS. + * - **intent** -- Rendering intent, VipsIntent. + * - **embedded** -- Use embedded input profile, if available, bool. + * - **input_profile** -- Filename to load input profile from, const char *. + * + * @param options Set of options. + * @return Output image. + */ +VImage icc_import( VOption *options = 0 ) const; + +/** + * Transform between devices with icc profiles. + * + * **Optional parameters** + * - **pcs** -- Set Profile Connection Space, VipsPCS. + * - **intent** -- Rendering intent, VipsIntent. + * - **embedded** -- Use embedded input profile, if available, bool. + * - **input_profile** -- Filename to load input profile from, const char *. + * - **depth** -- Output device space depth in bits, int. + * + * @param output_profile Filename to load output profile from. + * @param options Set of options. + * @return Output image. + */ +VImage icc_transform( const char *output_profile, VOption *options = 0 ) const; + +/** + * Make a 1d image where pixel values are indexes. + * + * **Optional parameters** + * - **bands** -- Number of bands in LUT, int. + * - **ushort** -- Create a 16-bit LUT, bool. + * - **size** -- Size of 16-bit LUT, int. + * + * @param options Set of options. + * @return Output image. + */ +static VImage identity( VOption *options = 0 ); + +/** + * Ifthenelse an image. + * + * **Optional parameters** + * - **blend** -- Blend smoothly between then and else parts, bool. + * + * @param in1 Source for TRUE pixels. + * @param in2 Source for FALSE pixels. + * @param options Set of options. + * @return Output image. + */ +VImage ifthenelse( VImage in1, VImage in2, VOption *options = 0 ) const; + +/** + * Insert image @sub into @main at @x, @y. + * + * **Optional parameters** + * - **expand** -- Expand output to hold all of both inputs, bool. + * - **background** -- Color for new pixels, std::vector. + * + * @param sub Sub-image to insert into main image. + * @param x Left edge of sub in main. + * @param y Top edge of sub in main. + * @param options Set of options. + * @return Output image. + */ +VImage insert( VImage sub, int x, int y, VOption *options = 0 ) const; + +/** + * Invert an image. + * @param options Set of options. + * @return Output image. + */ +VImage invert( VOption *options = 0 ) const; + +/** + * Build an inverted look-up table. + * + * **Optional parameters** + * - **size** -- LUT size to generate, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage invertlut( VOption *options = 0 ) const; + +/** + * Inverse fft. + * + * **Optional parameters** + * - **real** -- Output only the real part of the transform, bool. + * + * @param options Set of options. + * @return Output image. + */ +VImage invfft( VOption *options = 0 ) const; + +/** + * Join a pair of images. + * + * **Optional parameters** + * - **expand** -- Expand output to hold all of both inputs, bool. + * - **shim** -- Pixels between images, int. + * - **background** -- Colour for new pixels, std::vector. + * - **align** -- Align on the low, centre or high coordinate edge, VipsAlign. + * + * @param in2 Second input image. + * @param direction Join left-right or up-down. + * @param options Set of options. + * @return Output image. + */ +VImage join( VImage in2, VipsDirection direction, VOption *options = 0 ) const; + +/** + * Load jpeg from file. + * + * **Optional parameters** + * - **shrink** -- Shrink factor on load, int. + * - **autorotate** -- Rotate image using exif orientation, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage jpegload( const char *filename, VOption *options = 0 ); + +/** + * Load jpeg from buffer. + * + * **Optional parameters** + * - **shrink** -- Shrink factor on load, int. + * - **autorotate** -- Rotate image using exif orientation, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage jpegload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load image from jpeg source. + * + * **Optional parameters** + * - **shrink** -- Shrink factor on load, int. + * - **autorotate** -- Rotate image using exif orientation, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage jpegload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to jpeg file. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **profile** -- ICC profile to embed, const char *. + * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. + * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. + * - **no_subsample** -- Disable chroma subsample, bool. + * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. + * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. + * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. + * - **quant_table** -- Use predefined quantization table with given index, int. + * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void jpegsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to jpeg buffer. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **profile** -- ICC profile to embed, const char *. + * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. + * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. + * - **no_subsample** -- Disable chroma subsample, bool. + * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. + * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. + * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. + * - **quant_table** -- Use predefined quantization table with given index, int. + * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *jpegsave_buffer( VOption *options = 0 ) const; + +/** + * Save image to jpeg mime. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **profile** -- ICC profile to embed, const char *. + * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. + * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. + * - **no_subsample** -- Disable chroma subsample, bool. + * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. + * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. + * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. + * - **quant_table** -- Use predefined quantization table with given index, int. + * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + */ +void jpegsave_mime( VOption *options = 0 ) const; + +/** + * Save image to jpeg target. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **profile** -- ICC profile to embed, const char *. + * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. + * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. + * - **no_subsample** -- Disable chroma subsample, bool. + * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. + * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. + * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. + * - **quant_table** -- Use predefined quantization table with given index, int. + * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void jpegsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Label regions in an image. + * @param options Set of options. + * @return Mask of region labels. + */ +VImage labelregions( VOption *options = 0 ) const; + +/** + * Calculate (a * in + b). + * + * **Optional parameters** + * - **uchar** -- Output should be uchar, bool. + * + * @param a Multiply by this. + * @param b Add this. + * @param options Set of options. + * @return Output image. + */ +VImage linear( std::vector a, std::vector b, VOption *options = 0 ) const; + +/** + * Cache an image as a set of lines. + * + * **Optional parameters** + * - **tile_height** -- Tile height in pixels, int. + * - **access** -- Expected access pattern, VipsAccess. + * - **threaded** -- Allow threaded access, bool. + * - **persistent** -- Keep cache between evaluations, bool. + * + * @param options Set of options. + * @return Output image. + */ +VImage linecache( VOption *options = 0 ) const; + +/** + * Make a laplacian of gaussian image. + * + * **Optional parameters** + * - **separable** -- Generate separable Logmatian, bool. + * - **integer** -- Generate integer Logmatian, bool. + * - **precision** -- Generate with this precision, VipsPrecision. + * + * @param sigma Radius of Logmatian. + * @param min_ampl Minimum amplitude of Logmatian. + * @param options Set of options. + * @return Output image. + */ +static VImage logmat( double sigma, double min_ampl, VOption *options = 0 ); + +/** + * Load file with imagemagick. + * + * **Optional parameters** + * - **all_frames** -- Read all frames from an image, bool. + * - **density** -- Canvas resolution for rendering vector formats like SVG, const char *. + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage magickload( const char *filename, VOption *options = 0 ); + +/** + * Load buffer with imagemagick. + * + * **Optional parameters** + * - **all_frames** -- Read all frames from an image, bool. + * - **density** -- Canvas resolution for rendering vector formats like SVG, const char *. + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage magickload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Save file with imagemagick. + * + * **Optional parameters** + * - **format** -- Format to save in, const char *. + * - **quality** -- Quality to use, int. + * - **optimize_gif_frames** -- Apply GIF frames optimization, bool. + * - **optimize_gif_transparency** -- Apply GIF transparency optimization, bool. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void magicksave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to magick buffer. + * + * **Optional parameters** + * - **format** -- Format to save in, const char *. + * - **quality** -- Quality to use, int. + * - **optimize_gif_frames** -- Apply GIF frames optimization, bool. + * - **optimize_gif_transparency** -- Apply GIF transparency optimization, bool. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *magicksave_buffer( VOption *options = 0 ) const; + +/** + * Resample with a map image. + * + * **Optional parameters** + * - **interpolate** -- Interpolate pixels with this, VInterpolate. + * + * @param index Index pixels with this. + * @param options Set of options. + * @return Output image. + */ +VImage mapim( VImage index, VOption *options = 0 ) const; + +/** + * Map an image though a lut. + * + * **Optional parameters** + * - **band** -- apply one-band lut to this band of in, int. + * + * @param lut Look-up table image. + * @param options Set of options. + * @return Output image. + */ +VImage maplut( VImage lut, VOption *options = 0 ) const; + +/** + * Make a butterworth filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param order Filter order. + * @param frequency_cutoff Frequency cutoff. + * @param amplitude_cutoff Amplitude cutoff. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_butterworth( int width, int height, double order, double frequency_cutoff, double amplitude_cutoff, VOption *options = 0 ); + +/** + * Make a butterworth_band filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param order Filter order. + * @param frequency_cutoff_x Frequency cutoff x. + * @param frequency_cutoff_y Frequency cutoff y. + * @param radius radius of circle. + * @param amplitude_cutoff Amplitude cutoff. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_butterworth_band( int width, int height, double order, double frequency_cutoff_x, double frequency_cutoff_y, double radius, double amplitude_cutoff, VOption *options = 0 ); + +/** + * Make a butterworth ring filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param order Filter order. + * @param frequency_cutoff Frequency cutoff. + * @param amplitude_cutoff Amplitude cutoff. + * @param ringwidth Ringwidth. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_butterworth_ring( int width, int height, double order, double frequency_cutoff, double amplitude_cutoff, double ringwidth, VOption *options = 0 ); + +/** + * Make fractal filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param fractal_dimension Fractal dimension. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_fractal( int width, int height, double fractal_dimension, VOption *options = 0 ); + +/** + * Make a gaussian filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param frequency_cutoff Frequency cutoff. + * @param amplitude_cutoff Amplitude cutoff. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_gaussian( int width, int height, double frequency_cutoff, double amplitude_cutoff, VOption *options = 0 ); + +/** + * Make a gaussian filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param frequency_cutoff_x Frequency cutoff x. + * @param frequency_cutoff_y Frequency cutoff y. + * @param radius radius of circle. + * @param amplitude_cutoff Amplitude cutoff. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_gaussian_band( int width, int height, double frequency_cutoff_x, double frequency_cutoff_y, double radius, double amplitude_cutoff, VOption *options = 0 ); + +/** + * Make a gaussian ring filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param frequency_cutoff Frequency cutoff. + * @param amplitude_cutoff Amplitude cutoff. + * @param ringwidth Ringwidth. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_gaussian_ring( int width, int height, double frequency_cutoff, double amplitude_cutoff, double ringwidth, VOption *options = 0 ); + +/** + * Make an ideal filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param frequency_cutoff Frequency cutoff. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_ideal( int width, int height, double frequency_cutoff, VOption *options = 0 ); + +/** + * Make an ideal band filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param frequency_cutoff_x Frequency cutoff x. + * @param frequency_cutoff_y Frequency cutoff y. + * @param radius radius of circle. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_ideal_band( int width, int height, double frequency_cutoff_x, double frequency_cutoff_y, double radius, VOption *options = 0 ); + +/** + * Make an ideal ring filter. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **nodc** -- Remove DC component, bool. + * - **reject** -- Invert the sense of the filter, bool. + * - **optical** -- Rotate quadrants to optical space, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param frequency_cutoff Frequency cutoff. + * @param ringwidth Ringwidth. + * @param options Set of options. + * @return Output image. + */ +static VImage mask_ideal_ring( int width, int height, double frequency_cutoff, double ringwidth, VOption *options = 0 ); + +/** + * First-order match of two images. + * + * **Optional parameters** + * - **hwindow** -- Half window size, int. + * - **harea** -- Half area size, int. + * - **search** -- Search to improve tie-points, bool. + * - **interpolate** -- Interpolate pixels with this, VInterpolate. + * + * @param sec Secondary image. + * @param xr1 Position of first reference tie-point. + * @param yr1 Position of first reference tie-point. + * @param xs1 Position of first secondary tie-point. + * @param ys1 Position of first secondary tie-point. + * @param xr2 Position of second reference tie-point. + * @param yr2 Position of second reference tie-point. + * @param xs2 Position of second secondary tie-point. + * @param ys2 Position of second secondary tie-point. + * @param options Set of options. + * @return Output image. + */ +VImage match( VImage sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, VOption *options = 0 ) const; + +/** + * Apply a math operation to an image. + * @param math math to perform. + * @param options Set of options. + * @return Output image. + */ +VImage math( VipsOperationMath math, VOption *options = 0 ) const; + +/** + * Binary math operations. + * @param right Right-hand image argument. + * @param math2 math to perform. + * @param options Set of options. + * @return Output image. + */ +VImage math2( VImage right, VipsOperationMath2 math2, VOption *options = 0 ) const; + +/** + * Binary math operations with a constant. + * @param math2 math to perform. + * @param c Array of constants. + * @param options Set of options. + * @return Output image. + */ +VImage math2_const( VipsOperationMath2 math2, std::vector c, VOption *options = 0 ) const; + +/** + * Load mat from file. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage matload( const char *filename, VOption *options = 0 ); + +/** + * Invert an matrix. + * @param options Set of options. + * @return Output matrix. + */ +VImage matrixinvert( VOption *options = 0 ) const; + +/** + * Load matrix. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage matrixload( const char *filename, VOption *options = 0 ); + +/** + * Load matrix. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage matrixload_source( VSource source, VOption *options = 0 ); + +/** + * Print matrix. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + */ +void matrixprint( VOption *options = 0 ) const; + +/** + * Save image to matrix. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void matrixsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to matrix. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void matrixsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Find image maximum. + * + * **Optional parameters** + * - **size** -- Number of maximum values to find, int. + * + * @param options Set of options. + * @return Output value. + */ +double max( VOption *options = 0 ) const; + +/** + * Measure a set of patches on a color chart. + * + * **Optional parameters** + * - **left** -- Left edge of extract area, int. + * - **top** -- Top edge of extract area, int. + * - **width** -- Width of extract area, int. + * - **height** -- Height of extract area, int. + * + * @param h Number of patches across chart. + * @param v Number of patches down chart. + * @param options Set of options. + * @return Output array of statistics. + */ +VImage measure( int h, int v, VOption *options = 0 ) const; + +/** + * Merge two images. + * + * **Optional parameters** + * - **mblend** -- Maximum blend size, int. + * + * @param sec Secondary image. + * @param direction Horizontal or vertical merge. + * @param dx Horizontal displacement from sec to ref. + * @param dy Vertical displacement from sec to ref. + * @param options Set of options. + * @return Output image. + */ +VImage merge( VImage sec, VipsDirection direction, int dx, int dy, VOption *options = 0 ) const; + +/** + * Find image minimum. + * + * **Optional parameters** + * - **size** -- Number of minimum values to find, int. + * + * @param options Set of options. + * @return Output value. + */ +double min( VOption *options = 0 ) const; + +/** + * Morphology operation. + * @param mask Input matrix image. + * @param morph Morphological operation to perform. + * @param options Set of options. + * @return Output image. + */ +VImage morph( VImage mask, VipsOperationMorphology morph, VOption *options = 0 ) const; + +/** + * Mosaic two images. + * + * **Optional parameters** + * - **hwindow** -- Half window size, int. + * - **harea** -- Half area size, int. + * - **mblend** -- Maximum blend size, int. + * - **bandno** -- Band to search for features on, int. + * + * @param sec Secondary image. + * @param direction Horizontal or vertical mosaic. + * @param xref Position of reference tie-point. + * @param yref Position of reference tie-point. + * @param xsec Position of secondary tie-point. + * @param ysec Position of secondary tie-point. + * @param options Set of options. + * @return Output image. + */ +VImage mosaic( VImage sec, VipsDirection direction, int xref, int yref, int xsec, int ysec, VOption *options = 0 ) const; + +/** + * First-order mosaic of two images. + * + * **Optional parameters** + * - **hwindow** -- Half window size, int. + * - **harea** -- Half area size, int. + * - **search** -- Search to improve tie-points, bool. + * - **interpolate** -- Interpolate pixels with this, VInterpolate. + * - **mblend** -- Maximum blend size, int. + * - **bandno** -- Band to search for features on, int. + * + * @param sec Secondary image. + * @param direction Horizontal or vertical mosaic. + * @param xr1 Position of first reference tie-point. + * @param yr1 Position of first reference tie-point. + * @param xs1 Position of first secondary tie-point. + * @param ys1 Position of first secondary tie-point. + * @param xr2 Position of second reference tie-point. + * @param yr2 Position of second reference tie-point. + * @param xs2 Position of second secondary tie-point. + * @param ys2 Position of second secondary tie-point. + * @param options Set of options. + * @return Output image. + */ +VImage mosaic1( VImage sec, VipsDirection direction, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, VOption *options = 0 ) const; + +/** + * Pick most-significant byte from an image. + * + * **Optional parameters** + * - **band** -- Band to msb, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage msb( VOption *options = 0 ) const; + +/** + * Multiply two images. + * @param right Right-hand image argument. + * @param options Set of options. + * @return Output image. + */ +VImage multiply( VImage right, VOption *options = 0 ) const; + +/** + * Load nifti volume. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage niftiload( const char *filename, VOption *options = 0 ); + +/** + * Load a nifti image. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param options Set of options. + * @return Output image. + */ +static VImage niftiload_base( VOption *options = 0 ); + +/** + * Load nifti volumes. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage niftiload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to nifti file. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void niftisave( const char *filename, VOption *options = 0 ) const; + +/** + * Load an openexr image. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage openexrload( const char *filename, VOption *options = 0 ); + +/** + * Load file with openslide. + * + * **Optional parameters** + * - **attach_associated** -- Attach all associated images, bool. + * - **level** -- Load this level from the file, int. + * - **autocrop** -- Crop to image bounds, bool. + * - **associated** -- Load this associated image, const char *. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage openslideload( const char *filename, VOption *options = 0 ); + +/** + * Load source with openslide. + * + * **Optional parameters** + * - **attach_associated** -- Attach all associated images, bool. + * - **level** -- Load this level from the file, int. + * - **autocrop** -- Crop to image bounds, bool. + * - **associated** -- Load this associated image, const char *. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage openslideload_source( VSource source, VOption *options = 0 ); + +/** + * Load pdf from file. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **dpi** -- Render at this DPI, double. + * - **scale** -- Scale output by this factor, double. + * - **background** -- Background value, std::vector. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage pdfload( const char *filename, VOption *options = 0 ); + +/** + * Load pdf from buffer. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **dpi** -- Render at this DPI, double. + * - **scale** -- Scale output by this factor, double. + * - **background** -- Background value, std::vector. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage pdfload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load pdf from source. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **dpi** -- Render at this DPI, double. + * - **scale** -- Scale output by this factor, double. + * - **background** -- Background value, std::vector. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage pdfload_source( VSource source, VOption *options = 0 ); + +/** + * Find threshold for percent of pixels. + * @param percent Percent of pixels. + * @param options Set of options. + * @return Threshold above which lie percent of pixels. + */ +int percent( double percent, VOption *options = 0 ) const; + +/** + * Make a perlin noise image. + * + * **Optional parameters** + * - **cell_size** -- Size of Perlin cells, int. + * - **uchar** -- Output an unsigned char image, bool. + * - **seed** -- Random number seed, int. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage perlin( int width, int height, VOption *options = 0 ); + +/** + * Calculate phase correlation. + * @param in2 Second input image. + * @param options Set of options. + * @return Output image. + */ +VImage phasecor( VImage in2, VOption *options = 0 ) const; + +/** + * Load png from file. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage pngload( const char *filename, VOption *options = 0 ); + +/** + * Load png from buffer. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage pngload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load png from source. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage pngload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to png file. + * + * **Optional parameters** + * - **compression** -- Compression factor, int. + * - **interlace** -- Interlace image, bool. + * - **profile** -- ICC profile to embed, const char *. + * - **filter** -- libpng row filter flag(s), int. + * - **palette** -- Quantise to 8bpp palette, bool. + * - **colours** -- Max number of palette colours, int. + * - **Q** -- Quantisation quality, int. + * - **dither** -- Amount of dithering, double. + * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void pngsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to png buffer. + * + * **Optional parameters** + * - **compression** -- Compression factor, int. + * - **interlace** -- Interlace image, bool. + * - **profile** -- ICC profile to embed, const char *. + * - **filter** -- libpng row filter flag(s), int. + * - **palette** -- Quantise to 8bpp palette, bool. + * - **colours** -- Max number of palette colours, int. + * - **Q** -- Quantisation quality, int. + * - **dither** -- Amount of dithering, double. + * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *pngsave_buffer( VOption *options = 0 ) const; + +/** + * Save image to target as png. + * + * **Optional parameters** + * - **compression** -- Compression factor, int. + * - **interlace** -- Interlace image, bool. + * - **profile** -- ICC profile to embed, const char *. + * - **filter** -- libpng row filter flag(s), int. + * - **palette** -- Quantise to 8bpp palette, bool. + * - **colours** -- Max number of palette colours, int. + * - **Q** -- Quantisation quality, int. + * - **dither** -- Amount of dithering, double. + * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void pngsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Load ppm from file. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage ppmload( const char *filename, VOption *options = 0 ); + +/** + * Load ppm base class. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage ppmload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to ppm file. + * + * **Optional parameters** + * - **ascii** -- save as ascii, bool. + * - **squash** -- save as one bit, bool. + * - **bitdepth** -- set to 1 to write as a 1 bit image, int. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void ppmsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save to ppm. + * + * **Optional parameters** + * - **ascii** -- save as ascii, bool. + * - **squash** -- save as one bit, bool. + * - **bitdepth** -- set to 1 to write as a 1 bit image, int. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void ppmsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Premultiply image alpha. + * + * **Optional parameters** + * - **max_alpha** -- Maximum value of alpha channel, double. + * + * @param options Set of options. + * @return Output image. + */ +VImage premultiply( VOption *options = 0 ) const; + +/** + * Find image profiles. + * @param rows First non-zero pixel in row. + * @param options Set of options. + * @return First non-zero pixel in column. + */ +VImage profile( VImage *rows, VOption *options = 0 ) const; + +/** + * Load named icc profile. + * @param name Profile name. + * @param options Set of options. + * @return Loaded profile. + */ +static VipsBlob *profile_load( const char *name, VOption *options = 0 ); + +/** + * Find image projections. + * @param rows Sums of rows. + * @param options Set of options. + * @return Sums of columns. + */ +VImage project( VImage *rows, VOption *options = 0 ) const; + +/** + * Resample an image with a quadratic transform. + * + * **Optional parameters** + * - **interpolate** -- Interpolate values with this, VInterpolate. + * + * @param coeff Coefficient matrix. + * @param options Set of options. + * @return Output image. + */ +VImage quadratic( VImage coeff, VOption *options = 0 ) const; + +/** + * Unpack radiance coding to float rgb. + * @param options Set of options. + * @return Output image. + */ +VImage rad2float( VOption *options = 0 ) const; + +/** + * Load a radiance image from a file. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage radload( const char *filename, VOption *options = 0 ); + +/** + * Load rad from buffer. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage radload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load rad from source. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage radload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to radiance file. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void radsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to radiance buffer. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *radsave_buffer( VOption *options = 0 ) const; + +/** + * Save image to radiance target. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void radsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Rank filter. + * @param width Window width in pixels. + * @param height Window height in pixels. + * @param index Select pixel at index. + * @param options Set of options. + * @return Output image. + */ +VImage rank( int width, int height, int index, VOption *options = 0 ) const; + +/** + * Load raw data from a file. + * + * **Optional parameters** + * - **offset** -- Offset in bytes from start of file, guint64. + * - **format** -- Pixel format in image, VipsBandFormat. + * - **interpretation** -- Pixel interpretation, VipsInterpretation. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param bands Number of bands in image. + * @param options Set of options. + * @return Output image. + */ +static VImage rawload( const char *filename, int width, int height, int bands, VOption *options = 0 ); + +/** + * Save image to raw file. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void rawsave( const char *filename, VOption *options = 0 ) const; + +/** + * Write raw image to file descriptor. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param fd File descriptor to write to. + * @param options Set of options. + */ +void rawsave_fd( int fd, VOption *options = 0 ) const; + +/** + * Linear recombination with matrix. + * @param m matrix of coefficients. + * @param options Set of options. + * @return Output image. + */ +VImage recomb( VImage m, VOption *options = 0 ) const; + +/** + * Reduce an image. + * + * **Optional parameters** + * - **kernel** -- Resampling kernel, VipsKernel. + * - **centre** -- Use centre sampling convention, bool. + * + * @param hshrink Horizontal shrink factor. + * @param vshrink Vertical shrink factor. + * @param options Set of options. + * @return Output image. + */ +VImage reduce( double hshrink, double vshrink, VOption *options = 0 ) const; + +/** + * Shrink an image horizontally. + * + * **Optional parameters** + * - **kernel** -- Resampling kernel, VipsKernel. + * - **centre** -- Use centre sampling convention, bool. + * + * @param hshrink Horizontal shrink factor. + * @param options Set of options. + * @return Output image. + */ +VImage reduceh( double hshrink, VOption *options = 0 ) const; + +/** + * Shrink an image vertically. + * + * **Optional parameters** + * - **kernel** -- Resampling kernel, VipsKernel. + * - **centre** -- Use centre sampling convention, bool. + * + * @param vshrink Vertical shrink factor. + * @param options Set of options. + * @return Output image. + */ +VImage reducev( double vshrink, VOption *options = 0 ) const; + +/** + * Relational operation on two images. + * @param right Right-hand image argument. + * @param relational relational to perform. + * @param options Set of options. + * @return Output image. + */ +VImage relational( VImage right, VipsOperationRelational relational, VOption *options = 0 ) const; + +/** + * Relational operations against a constant. + * @param relational relational to perform. + * @param c Array of constants. + * @param options Set of options. + * @return Output image. + */ +VImage relational_const( VipsOperationRelational relational, std::vector c, VOption *options = 0 ) const; + +/** + * Remainder after integer division of two images. + * @param right Right-hand image argument. + * @param options Set of options. + * @return Output image. + */ +VImage remainder( VImage right, VOption *options = 0 ) const; + +/** + * Remainder after integer division of an image and a constant. + * @param c Array of constants. + * @param options Set of options. + * @return Output image. + */ +VImage remainder_const( std::vector c, VOption *options = 0 ) const; + +/** + * Replicate an image. + * @param across Repeat this many times horizontally. + * @param down Repeat this many times vertically. + * @param options Set of options. + * @return Output image. + */ +VImage replicate( int across, int down, VOption *options = 0 ) const; + +/** + * Resize an image. + * + * **Optional parameters** + * - **interpolate** -- Interpolate pixels with this, VInterpolate. + * - **kernel** -- Resampling kernel, VipsKernel. + * - **centre** -- Use centre sampling convention, bool. + * - **vscale** -- Vertical scale image by this factor, double. + * - **idx** -- Horizontal input displacement, double. + * - **idy** -- Vertical input displacement, double. + * + * @param scale Scale image by this factor. + * @param options Set of options. + * @return Output image. + */ +VImage resize( double scale, VOption *options = 0 ) const; + +/** + * Rotate an image. + * @param angle Angle to rotate image. + * @param options Set of options. + * @return Output image. + */ +VImage rot( VipsAngle angle, VOption *options = 0 ) const; + +/** + * Rotate an image. + * + * **Optional parameters** + * - **angle** -- Angle to rotate image, VipsAngle45. + * + * @param options Set of options. + * @return Output image. + */ +VImage rot45( VOption *options = 0 ) const; + +/** + * Rotate an image by a number of degrees. + * + * **Optional parameters** + * - **interpolate** -- Interpolate pixels with this, VInterpolate. + * - **background** -- Background value, std::vector. + * - **odx** -- Horizontal output displacement, double. + * - **ody** -- Vertical output displacement, double. + * - **idx** -- Horizontal input displacement, double. + * - **idy** -- Vertical input displacement, double. + * + * @param angle Rotate anticlockwise by this many degrees. + * @param options Set of options. + * @return Output image. + */ +VImage rotate( double angle, VOption *options = 0 ) const; + +/** + * Perform a round function on an image. + * @param round rounding operation to perform. + * @param options Set of options. + * @return Output image. + */ +VImage round( VipsOperationRound round, VOption *options = 0 ) const; + +/** + * Transform srgb to hsv. + * @param options Set of options. + * @return Output image. + */ +VImage sRGB2HSV( VOption *options = 0 ) const; + +/** + * Convert an srgb image to scrgb. + * @param options Set of options. + * @return Output image. + */ +VImage sRGB2scRGB( VOption *options = 0 ) const; + +/** + * Convert scrgb to bw. + * + * **Optional parameters** + * - **depth** -- Output device space depth in bits, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage scRGB2BW( VOption *options = 0 ) const; + +/** + * Transform scrgb to xyz. + * @param options Set of options. + * @return Output image. + */ +VImage scRGB2XYZ( VOption *options = 0 ) const; + +/** + * Convert an scrgb image to srgb. + * + * **Optional parameters** + * - **depth** -- Output device space depth in bits, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage scRGB2sRGB( VOption *options = 0 ) const; + +/** + * Scale an image to uchar. + * + * **Optional parameters** + * - **exp** -- Exponent for log scale, double. + * - **log** -- Log scale, bool. + * + * @param options Set of options. + * @return Output image. + */ +VImage scale( VOption *options = 0 ) const; + +/** + * Check sequential access. + * + * **Optional parameters** + * - **trace** -- trace pixel requests, bool. + * - **tile_height** -- Tile height in pixels, int. + * - **access** -- Expected access pattern, VipsAccess. + * + * @param options Set of options. + * @return Output image. + */ +VImage sequential( VOption *options = 0 ) const; + +/** + * Unsharp masking for print. + * + * **Optional parameters** + * - **radius** -- radius of Gaussian, int. + * - **sigma** -- Sigma of Gaussian, double. + * - **x1** -- Flat/jaggy threshold, double. + * - **y2** -- Maximum brightening, double. + * - **y3** -- Maximum darkening, double. + * - **m1** -- Slope for flat areas, double. + * - **m2** -- Slope for jaggy areas, double. + * + * @param options Set of options. + * @return Output image. + */ +VImage sharpen( VOption *options = 0 ) const; + +/** + * Shrink an image. + * @param hshrink Horizontal shrink factor. + * @param vshrink Vertical shrink factor. + * @param options Set of options. + * @return Output image. + */ +VImage shrink( double hshrink, double vshrink, VOption *options = 0 ) const; + +/** + * Shrink an image horizontally. + * @param hshrink Horizontal shrink factor. + * @param options Set of options. + * @return Output image. + */ +VImage shrinkh( int hshrink, VOption *options = 0 ) const; + +/** + * Shrink an image vertically. + * @param vshrink Vertical shrink factor. + * @param options Set of options. + * @return Output image. + */ +VImage shrinkv( int vshrink, VOption *options = 0 ) const; + +/** + * Unit vector of pixel. + * @param options Set of options. + * @return Output image. + */ +VImage sign( VOption *options = 0 ) const; + +/** + * Similarity transform of an image. + * + * **Optional parameters** + * - **scale** -- Scale by this factor, double. + * - **angle** -- Rotate anticlockwise by this many degrees, double. + * - **interpolate** -- Interpolate pixels with this, VInterpolate. + * - **background** -- Background value, std::vector. + * - **odx** -- Horizontal output displacement, double. + * - **ody** -- Vertical output displacement, double. + * - **idx** -- Horizontal input displacement, double. + * - **idy** -- Vertical input displacement, double. + * + * @param options Set of options. + * @return Output image. + */ +VImage similarity( VOption *options = 0 ) const; + +/** + * Make a 2d sine wave. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * - **hfreq** -- Horizontal spatial frequency, double. + * - **vfreq** -- Vertical spatial frequency, double. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage sines( int width, int height, VOption *options = 0 ); + +/** + * Extract an area from an image. + * + * **Optional parameters** + * - **interesting** -- How to measure interestingness, VipsInteresting. + * + * @param width Width of extract area. + * @param height Height of extract area. + * @param options Set of options. + * @return Output image. + */ +VImage smartcrop( int width, int height, VOption *options = 0 ) const; + +/** + * Sobel edge detector. + * @param options Set of options. + * @return Output image. + */ +VImage sobel( VOption *options = 0 ) const; + +/** + * Spatial correlation. + * @param ref Input reference image. + * @param options Set of options. + * @return Output image. + */ +VImage spcor( VImage ref, VOption *options = 0 ) const; + +/** + * Make displayable power spectrum. + * @param options Set of options. + * @return Output image. + */ +VImage spectrum( VOption *options = 0 ) const; + +/** + * Find many image stats. + * @param options Set of options. + * @return Output array of statistics. + */ +VImage stats( VOption *options = 0 ) const; + +/** + * Statistical difference. + * + * **Optional parameters** + * - **s0** -- New deviation, double. + * - **b** -- Weight of new deviation, double. + * - **m0** -- New mean, double. + * - **a** -- Weight of new mean, double. + * + * @param width Window width in pixels. + * @param height Window height in pixels. + * @param options Set of options. + * @return Output image. + */ +VImage stdif( int width, int height, VOption *options = 0 ) const; + +/** + * Subsample an image. + * + * **Optional parameters** + * - **point** -- Point sample, bool. + * + * @param xfac Horizontal subsample factor. + * @param yfac Vertical subsample factor. + * @param options Set of options. + * @return Output image. + */ +VImage subsample( int xfac, int yfac, VOption *options = 0 ) const; + +/** + * Subtract two images. + * @param right Right-hand image argument. + * @param options Set of options. + * @return Output image. + */ +VImage subtract( VImage right, VOption *options = 0 ) const; + +/** + * Sum an array of images. + * @param in Array of input images. + * @param options Set of options. + * @return Output image. + */ +static VImage sum( std::vector in, VOption *options = 0 ); + +/** + * Load svg with rsvg. + * + * **Optional parameters** + * - **dpi** -- Render at this DPI, double. + * - **scale** -- Scale output by this factor, double. + * - **unlimited** -- Allow SVG of any size, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage svgload( const char *filename, VOption *options = 0 ); + +/** + * Load svg with rsvg. + * + * **Optional parameters** + * - **dpi** -- Render at this DPI, double. + * - **scale** -- Scale output by this factor, double. + * - **unlimited** -- Allow SVG of any size, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage svgload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load svg from source. + * + * **Optional parameters** + * - **dpi** -- Render at this DPI, double. + * - **scale** -- Scale output by this factor, double. + * - **unlimited** -- Allow SVG of any size, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage svgload_source( VSource source, VOption *options = 0 ); + +/** + * Find the index of the first non-zero pixel in tests. + * @param tests Table of images to test. + * @param options Set of options. + * @return Output image. + */ +static VImage switch_image( std::vector tests, VOption *options = 0 ); + +/** + * Run an external command. + * + * **Optional parameters** + * - **in** -- Array of input images, std::vector. + * - **out_format** -- Format for output filename, const char *. + * - **in_format** -- Format for input filename, const char *. + * + * @param cmd_format Command to run. + * @param options Set of options. + */ +static void system( const char *cmd_format, VOption *options = 0 ); + +/** + * Make a text image. + * + * **Optional parameters** + * - **font** -- Font to render with, const char *. + * - **width** -- Maximum image width in pixels, int. + * - **height** -- Maximum image height in pixels, int. + * - **align** -- Align on the low, centre or high edge, VipsAlign. + * - **dpi** -- DPI to render at, int. + * - **justify** -- Justify lines, bool. + * - **spacing** -- Line spacing, int. + * - **fontfile** -- Load this font file, const char *. + * + * @param text Text to render. + * @param options Set of options. + * @return Output image. + */ +static VImage text( const char *text, VOption *options = 0 ); + +/** + * Generate thumbnail from file. + * + * **Optional parameters** + * - **height** -- Size to this height, int. + * - **size** -- Only upsize, only downsize, or both, VipsSize. + * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. + * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. + * - **linear** -- Reduce in linear light, bool. + * - **import_profile** -- Fallback import profile, const char *. + * - **export_profile** -- Fallback export profile, const char *. + * - **intent** -- Rendering intent, VipsIntent. + * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. + * + * @param filename Filename to read from. + * @param width Size to this width. + * @param options Set of options. + * @return Output image. + */ +static VImage thumbnail( const char *filename, int width, VOption *options = 0 ); + +/** + * Generate thumbnail from buffer. + * + * **Optional parameters** + * - **option_string** -- Options that are passed on to the underlying loader, const char *. + * - **height** -- Size to this height, int. + * - **size** -- Only upsize, only downsize, or both, VipsSize. + * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. + * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. + * - **linear** -- Reduce in linear light, bool. + * - **import_profile** -- Fallback import profile, const char *. + * - **export_profile** -- Fallback export profile, const char *. + * - **intent** -- Rendering intent, VipsIntent. + * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. + * + * @param buffer Buffer to load from. + * @param width Size to this width. + * @param options Set of options. + * @return Output image. + */ +static VImage thumbnail_buffer( VipsBlob *buffer, int width, VOption *options = 0 ); + +/** + * Generate thumbnail from image. + * + * **Optional parameters** + * - **height** -- Size to this height, int. + * - **size** -- Only upsize, only downsize, or both, VipsSize. + * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. + * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. + * - **linear** -- Reduce in linear light, bool. + * - **import_profile** -- Fallback import profile, const char *. + * - **export_profile** -- Fallback export profile, const char *. + * - **intent** -- Rendering intent, VipsIntent. + * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. + * + * @param width Size to this width. + * @param options Set of options. + * @return Output image. + */ +VImage thumbnail_image( int width, VOption *options = 0 ) const; + +/** + * Generate thumbnail from source. + * + * **Optional parameters** + * - **option_string** -- Options that are passed on to the underlying loader, const char *. + * - **height** -- Size to this height, int. + * - **size** -- Only upsize, only downsize, or both, VipsSize. + * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. + * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. + * - **linear** -- Reduce in linear light, bool. + * - **import_profile** -- Fallback import profile, const char *. + * - **export_profile** -- Fallback export profile, const char *. + * - **intent** -- Rendering intent, VipsIntent. + * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. + * + * @param source Source to load from. + * @param width Size to this width. + * @param options Set of options. + * @return Output image. + */ +static VImage thumbnail_source( VSource source, int width, VOption *options = 0 ); + +/** + * Load tiff from file. + * + * **Optional parameters** + * - **page** -- Load this page from the image, int. + * - **subifd** -- Select subifd index, int. + * - **n** -- Load this many pages, int. + * - **autorotate** -- Rotate image using orientation tag, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage tiffload( const char *filename, VOption *options = 0 ); + +/** + * Load tiff from buffer. + * + * **Optional parameters** + * - **page** -- Load this page from the image, int. + * - **subifd** -- Select subifd index, int. + * - **n** -- Load this many pages, int. + * - **autorotate** -- Rotate image using orientation tag, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage tiffload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load tiff from source. + * + * **Optional parameters** + * - **page** -- Load this page from the image, int. + * - **subifd** -- Select subifd index, int. + * - **n** -- Load this many pages, int. + * - **autorotate** -- Rotate image using orientation tag, bool. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage tiffload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to tiff file. + * + * **Optional parameters** + * - **compression** -- Compression for this file, VipsForeignTiffCompression. + * - **Q** -- Q factor, int. + * - **predictor** -- Compression prediction, VipsForeignTiffPredictor. + * - **profile** -- ICC profile to embed, const char *. + * - **tile** -- Write a tiled tiff, bool. + * - **tile_width** -- Tile width in pixels, int. + * - **tile_height** -- Tile height in pixels, int. + * - **pyramid** -- Write a pyramidal tiff, bool. + * - **squash** -- Squash images down to 1 bit, bool. + * - **miniswhite** -- Use 0 for white in 1-bit images, bool. + * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. + * - **resunit** -- Resolution unit, VipsForeignTiffResunit. + * - **xres** -- Horizontal resolution in pixels/mm, double. + * - **yres** -- Vertical resolution in pixels/mm, double. + * - **bigtiff** -- Write a bigtiff image, bool. + * - **rgbjpeg** -- Output RGB JPEG rather than YCbCr, bool. + * - **properties** -- Write a properties document to IMAGEDESCRIPTION, bool. + * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. + * - **level** -- ZSTD compression level, int. + * - **subifd** -- Save pyr layers as sub-IFDs, bool. + * - **lossless** -- Enable WEBP lossless mode, bool. + * - **depth** -- Pyramid depth, VipsForeignDzDepth. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void tiffsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to tiff buffer. + * + * **Optional parameters** + * - **compression** -- Compression for this file, VipsForeignTiffCompression. + * - **Q** -- Q factor, int. + * - **predictor** -- Compression prediction, VipsForeignTiffPredictor. + * - **profile** -- ICC profile to embed, const char *. + * - **tile** -- Write a tiled tiff, bool. + * - **tile_width** -- Tile width in pixels, int. + * - **tile_height** -- Tile height in pixels, int. + * - **pyramid** -- Write a pyramidal tiff, bool. + * - **squash** -- Squash images down to 1 bit, bool. + * - **miniswhite** -- Use 0 for white in 1-bit images, bool. + * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. + * - **resunit** -- Resolution unit, VipsForeignTiffResunit. + * - **xres** -- Horizontal resolution in pixels/mm, double. + * - **yres** -- Vertical resolution in pixels/mm, double. + * - **bigtiff** -- Write a bigtiff image, bool. + * - **rgbjpeg** -- Output RGB JPEG rather than YCbCr, bool. + * - **properties** -- Write a properties document to IMAGEDESCRIPTION, bool. + * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. + * - **level** -- ZSTD compression level, int. + * - **subifd** -- Save pyr layers as sub-IFDs, bool. + * - **lossless** -- Enable WEBP lossless mode, bool. + * - **depth** -- Pyramid depth, VipsForeignDzDepth. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *tiffsave_buffer( VOption *options = 0 ) const; + +/** + * Cache an image as a set of tiles. + * + * **Optional parameters** + * - **tile_width** -- Tile width in pixels, int. + * - **tile_height** -- Tile height in pixels, int. + * - **max_tiles** -- Maximum number of tiles to cache, int. + * - **access** -- Expected access pattern, VipsAccess. + * - **threaded** -- Allow threaded access, bool. + * - **persistent** -- Keep cache between evaluations, bool. + * + * @param options Set of options. + * @return Output image. + */ +VImage tilecache( VOption *options = 0 ) const; + +/** + * Build a look-up table. + * + * **Optional parameters** + * - **in_max** -- Size of LUT to build, int. + * - **out_max** -- Maximum value in output LUT, int. + * - **Lb** -- Lowest value in output, double. + * - **Lw** -- Highest value in output, double. + * - **Ps** -- Position of shadow, double. + * - **Pm** -- Position of mid-tones, double. + * - **Ph** -- Position of highlights, double. + * - **S** -- Adjust shadows by this much, double. + * - **M** -- Adjust mid-tones by this much, double. + * - **H** -- Adjust highlights by this much, double. + * + * @param options Set of options. + * @return Output image. + */ +static VImage tonelut( VOption *options = 0 ); + +/** + * Transpose3d an image. + * + * **Optional parameters** + * - **page_height** -- Height of each input page, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage transpose3d( VOption *options = 0 ) const; + +/** + * Unpremultiply image alpha. + * + * **Optional parameters** + * - **max_alpha** -- Maximum value of alpha channel, double. + * - **alpha_band** -- Unpremultiply with this alpha, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage unpremultiply( VOption *options = 0 ) const; + +/** + * Load vips from file. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage vipsload( const char *filename, VOption *options = 0 ); + +/** + * Load vips from source. + * + * **Optional parameters** + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage vipsload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to file in vips format. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void vipssave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to target in vips format. + * + * **Optional parameters** + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void vipssave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Load webp from file. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **scale** -- Scale factor on load, double. + * - **shrink** -- Shrink factor on load, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param filename Filename to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage webpload( const char *filename, VOption *options = 0 ); + +/** + * Load webp from buffer. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **scale** -- Scale factor on load, double. + * - **shrink** -- Shrink factor on load, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param buffer Buffer to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage webpload_buffer( VipsBlob *buffer, VOption *options = 0 ); + +/** + * Load webp from source. + * + * **Optional parameters** + * - **page** -- Load this page from the file, int. + * - **n** -- Load this many pages, int. + * - **scale** -- Scale factor on load, double. + * - **shrink** -- Shrink factor on load, int. + * - **memory** -- Force open via memory, bool. + * - **access** -- Required access pattern for this file, VipsAccess. + * - **sequential** -- Sequential read only, bool. + * - **fail** -- Fail on first error, bool. + * - **disc** -- Open to disc, bool. + * + * @param source Source to load from. + * @param options Set of options. + * @return Output image. + */ +static VImage webpload_source( VSource source, VOption *options = 0 ); + +/** + * Save image to webp file. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **lossless** -- enable lossless compression, bool. + * - **preset** -- Preset for lossy compression, VipsForeignWebpPreset. + * - **smart_subsample** -- Enable high quality chroma subsampling, bool. + * - **near_lossless** -- Enable preprocessing in lossless mode (uses Q), bool. + * - **alpha_q** -- Change alpha plane fidelity for lossy compression, int. + * - **min_size** -- Optimise for minium size, bool. + * - **kmin** -- Minimum number of frames between key frames, int. + * - **kmax** -- Maximum number of frames between key frames, int. + * - **reduction_effort** -- Level of CPU effort to reduce file size, int. + * - **profile** -- ICC profile to embed, const char *. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param filename Filename to save to. + * @param options Set of options. + */ +void webpsave( const char *filename, VOption *options = 0 ) const; + +/** + * Save image to webp buffer. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **lossless** -- enable lossless compression, bool. + * - **preset** -- Preset for lossy compression, VipsForeignWebpPreset. + * - **smart_subsample** -- Enable high quality chroma subsampling, bool. + * - **near_lossless** -- Enable preprocessing in lossless mode (uses Q), bool. + * - **alpha_q** -- Change alpha plane fidelity for lossy compression, int. + * - **min_size** -- Optimise for minium size, bool. + * - **kmin** -- Minimum number of frames between key frames, int. + * - **kmax** -- Maximum number of frames between key frames, int. + * - **reduction_effort** -- Level of CPU effort to reduce file size, int. + * - **profile** -- ICC profile to embed, const char *. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param options Set of options. + * @return Buffer to save to. + */ +VipsBlob *webpsave_buffer( VOption *options = 0 ) const; + +/** + * Save image to webp target. + * + * **Optional parameters** + * - **Q** -- Q factor, int. + * - **lossless** -- enable lossless compression, bool. + * - **preset** -- Preset for lossy compression, VipsForeignWebpPreset. + * - **smart_subsample** -- Enable high quality chroma subsampling, bool. + * - **near_lossless** -- Enable preprocessing in lossless mode (uses Q), bool. + * - **alpha_q** -- Change alpha plane fidelity for lossy compression, int. + * - **min_size** -- Optimise for minium size, bool. + * - **kmin** -- Minimum number of frames between key frames, int. + * - **kmax** -- Maximum number of frames between key frames, int. + * - **reduction_effort** -- Level of CPU effort to reduce file size, int. + * - **profile** -- ICC profile to embed, const char *. + * - **strip** -- Strip all metadata from image, bool. + * - **background** -- Background value, std::vector. + * - **page_height** -- Set page height for multipage save, int. + * + * @param target Target to save to. + * @param options Set of options. + */ +void webpsave_target( VTarget target, VOption *options = 0 ) const; + +/** + * Make a worley noise image. + * + * **Optional parameters** + * - **cell_size** -- Size of Worley cells, int. + * - **seed** -- Random number seed, int. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage worley( int width, int height, VOption *options = 0 ); + +/** + * Wrap image origin. + * + * **Optional parameters** + * - **x** -- Left edge of input in output, int. + * - **y** -- Top edge of input in output, int. + * + * @param options Set of options. + * @return Output image. + */ +VImage wrap( VOption *options = 0 ) const; + +/** + * Make an image where pixel values are coordinates. + * + * **Optional parameters** + * - **csize** -- Size of third dimension, int. + * - **dsize** -- Size of fourth dimension, int. + * - **esize** -- Size of fifth dimension, int. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage xyz( int width, int height, VOption *options = 0 ); + +/** + * Make a zone plate. + * + * **Optional parameters** + * - **uchar** -- Output an unsigned char image, bool. + * + * @param width Image width in pixels. + * @param height Image height in pixels. + * @param options Set of options. + * @return Output image. + */ +static VImage zone( int width, int height, VOption *options = 0 ); + +/** + * Zoom an image. + * @param xfac Horizontal zoom factor. + * @param yfac Vertical zoom factor. + * @param options Set of options. + * @return Output image. + */ +VImage zoom( int xfac, int yfac, VOption *options = 0 ) const; diff --git a/po/POTFILES.in b/po/POTFILES.in index c3dc94dc..6152da50 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -237,7 +237,6 @@ libvips/foreign/fits.c libvips/foreign/fitsload.c libvips/foreign/fitssave.c libvips/foreign/foreign.c -libvips/foreign/gifload.c libvips/foreign/heifload.c libvips/foreign/heifsave.c libvips/foreign/jpeg2vips.c @@ -254,6 +253,7 @@ libvips/foreign/matrixload.c libvips/foreign/matrixsave.c libvips/foreign/niftiload.c libvips/foreign/niftisave.c +libvips/foreign/nsgifload.c libvips/foreign/openexr2vips.c libvips/foreign/openexrload.c libvips/foreign/openslide2vips.c @@ -285,6 +285,8 @@ libvips/foreign/vipssave.c libvips/foreign/webp2vips.c libvips/foreign/webpload.c libvips/foreign/webpsave.c +libvips/foreign/libnsgif/libnsgif.c +libvips/foreign/libnsgif/lzw.c libvips/freqfilt/freqfilt.c libvips/freqfilt/freqmult.c libvips/freqfilt/fwfft.c @@ -333,6 +335,7 @@ libvips/iofuncs/sinkmemory.c libvips/iofuncs/sinkscreen.c libvips/iofuncs/source.c libvips/iofuncs/sourcecustom.c +libvips/iofuncs/sourceginput.c libvips/iofuncs/system.c libvips/iofuncs/target.c libvips/iofuncs/targetcustom.c From 003a444158fe93cba0559a32602f31930941c3e2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 7 Mar 2021 15:03:13 +0000 Subject: [PATCH 20/20] oop, we no longer need vips-operators.h --- cplusplus/include/vips/Makefile.am | 13 +- cplusplus/include/vips/vips-operators.h | 3914 ----------------------- 2 files changed, 7 insertions(+), 3920 deletions(-) delete mode 100644 cplusplus/include/vips/vips-operators.h diff --git a/cplusplus/include/vips/Makefile.am b/cplusplus/include/vips/Makefile.am index 46c61dcd..d1c0e805 100644 --- a/cplusplus/include/vips/Makefile.am +++ b/cplusplus/include/vips/Makefile.am @@ -5,9 +5,10 @@ pkginclude_HEADERS = \ VConnection8.h \ vips8 -vips-operators.h: - echo "// headers for vips operations" > vips-operators.h; \ - echo -n "// " >> vips-operators.h; \ - date >> vips-operators.h; \ - echo "// this file is generated automatically, do not edit!" >> vips-operators.h; \ - ./../../gen-operators.py -g h >> vips-operators.h +vips-operators: + echo "// headers for vips operations" > vips-operators; \ + echo "// paste this file into VImage8.h, do not leave in repo" > vips-operators; \ + echo -n "// " >> vips-operators; \ + date >> vips-operators; \ + echo "// this file is generated automatically, do not edit!" >> vips-operators; \ + ./../../gen-operators.py -g h >> vips-operators diff --git a/cplusplus/include/vips/vips-operators.h b/cplusplus/include/vips/vips-operators.h deleted file mode 100644 index e712335a..00000000 --- a/cplusplus/include/vips/vips-operators.h +++ /dev/null @@ -1,3914 +0,0 @@ -// headers for vips operations -// Sun Mar 7 02:50:00 PM GMT 2021 -// this file is generated automatically, do not edit! - -/** - * Transform lch to cmc. - * @param options Set of options. - * @return Output image. - */ -VImage CMC2LCh( VOption *options = 0 ) const; - -/** - * Transform cmyk to xyz. - * @param options Set of options. - * @return Output image. - */ -VImage CMYK2XYZ( VOption *options = 0 ) const; - -/** - * Transform hsv to srgb. - * @param options Set of options. - * @return Output image. - */ -VImage HSV2sRGB( VOption *options = 0 ) const; - -/** - * Transform lch to cmc. - * @param options Set of options. - * @return Output image. - */ -VImage LCh2CMC( VOption *options = 0 ) const; - -/** - * Transform lch to lab. - * @param options Set of options. - * @return Output image. - */ -VImage LCh2Lab( VOption *options = 0 ) const; - -/** - * Transform lab to lch. - * @param options Set of options. - * @return Output image. - */ -VImage Lab2LCh( VOption *options = 0 ) const; - -/** - * Transform float lab to labq coding. - * @param options Set of options. - * @return Output image. - */ -VImage Lab2LabQ( VOption *options = 0 ) const; - -/** - * Transform float lab to signed short. - * @param options Set of options. - * @return Output image. - */ -VImage Lab2LabS( VOption *options = 0 ) const; - -/** - * Transform cielab to xyz. - * - * **Optional parameters** - * - **temp** -- Color temperature, std::vector. - * - * @param options Set of options. - * @return Output image. - */ -VImage Lab2XYZ( VOption *options = 0 ) const; - -/** - * Unpack a labq image to float lab. - * @param options Set of options. - * @return Output image. - */ -VImage LabQ2Lab( VOption *options = 0 ) const; - -/** - * Unpack a labq image to short lab. - * @param options Set of options. - * @return Output image. - */ -VImage LabQ2LabS( VOption *options = 0 ) const; - -/** - * Convert a labq image to srgb. - * @param options Set of options. - * @return Output image. - */ -VImage LabQ2sRGB( VOption *options = 0 ) const; - -/** - * Transform signed short lab to float. - * @param options Set of options. - * @return Output image. - */ -VImage LabS2Lab( VOption *options = 0 ) const; - -/** - * Transform short lab to labq coding. - * @param options Set of options. - * @return Output image. - */ -VImage LabS2LabQ( VOption *options = 0 ) const; - -/** - * Transform xyz to cmyk. - * @param options Set of options. - * @return Output image. - */ -VImage XYZ2CMYK( VOption *options = 0 ) const; - -/** - * Transform xyz to lab. - * - * **Optional parameters** - * - **temp** -- Colour temperature, std::vector. - * - * @param options Set of options. - * @return Output image. - */ -VImage XYZ2Lab( VOption *options = 0 ) const; - -/** - * Transform xyz to yxy. - * @param options Set of options. - * @return Output image. - */ -VImage XYZ2Yxy( VOption *options = 0 ) const; - -/** - * Transform xyz to scrgb. - * @param options Set of options. - * @return Output image. - */ -VImage XYZ2scRGB( VOption *options = 0 ) const; - -/** - * Transform yxy to xyz. - * @param options Set of options. - * @return Output image. - */ -VImage Yxy2XYZ( VOption *options = 0 ) const; - -/** - * Absolute value of an image. - * @param options Set of options. - * @return Output image. - */ -VImage abs( VOption *options = 0 ) const; - -/** - * Add two images. - * @param right Right-hand image argument. - * @param options Set of options. - * @return Output image. - */ -VImage add( VImage right, VOption *options = 0 ) const; - -/** - * Affine transform of an image. - * - * **Optional parameters** - * - **interpolate** -- Interpolate pixels with this, VInterpolate. - * - **oarea** -- Area of output to generate, std::vector. - * - **odx** -- Horizontal output displacement, double. - * - **ody** -- Vertical output displacement, double. - * - **idx** -- Horizontal input displacement, double. - * - **idy** -- Vertical input displacement, double. - * - **background** -- Background value, std::vector. - * - **premultiplied** -- Images have premultiplied alpha, bool. - * - **extend** -- How to generate the extra pixels, VipsExtend. - * - * @param matrix Transformation matrix. - * @param options Set of options. - * @return Output image. - */ -VImage affine( std::vector matrix, VOption *options = 0 ) const; - -/** - * Load an analyze6 image. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage analyzeload( const char *filename, VOption *options = 0 ); - -/** - * Join an array of images. - * - * **Optional parameters** - * - **across** -- Number of images across grid, int. - * - **shim** -- Pixels between images, int. - * - **background** -- Colour for new pixels, std::vector. - * - **halign** -- Align on the left, centre or right, VipsAlign. - * - **valign** -- Align on the top, centre or bottom, VipsAlign. - * - **hspacing** -- Horizontal spacing between images, int. - * - **vspacing** -- Vertical spacing between images, int. - * - * @param in Array of input images. - * @param options Set of options. - * @return Output image. - */ -static VImage arrayjoin( std::vector in, VOption *options = 0 ); - -/** - * Autorotate image by exif tag. - * @param options Set of options. - * @return Output image. - */ -VImage autorot( VOption *options = 0 ) const; - -/** - * Find image average. - * @param options Set of options. - * @return Output value. - */ -double avg( VOption *options = 0 ) const; - -/** - * Boolean operation across image bands. - * @param boolean boolean to perform. - * @param options Set of options. - * @return Output image. - */ -VImage bandbool( VipsOperationBoolean boolean, VOption *options = 0 ) const; - -/** - * Fold up x axis into bands. - * - * **Optional parameters** - * - **factor** -- Fold by this factor, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage bandfold( VOption *options = 0 ) const; - -/** - * Bandwise join a set of images. - * @param in Array of input images. - * @param options Set of options. - * @return Output image. - */ -static VImage bandjoin( std::vector in, VOption *options = 0 ); - -/** - * Append a constant band to an image. - * @param c Array of constants to add. - * @param options Set of options. - * @return Output image. - */ -VImage bandjoin_const( std::vector c, VOption *options = 0 ) const; - -/** - * Band-wise average. - * @param options Set of options. - * @return Output image. - */ -VImage bandmean( VOption *options = 0 ) const; - -/** - * Band-wise rank of a set of images. - * - * **Optional parameters** - * - **index** -- Select this band element from sorted list, int. - * - * @param in Array of input images. - * @param options Set of options. - * @return Output image. - */ -static VImage bandrank( std::vector in, VOption *options = 0 ); - -/** - * Unfold image bands into x axis. - * - * **Optional parameters** - * - **factor** -- Unfold by this factor, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage bandunfold( VOption *options = 0 ) const; - -/** - * Make a black image. - * - * **Optional parameters** - * - **bands** -- Number of bands in image, int. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage black( int width, int height, VOption *options = 0 ); - -/** - * Boolean operation on two images. - * @param right Right-hand image argument. - * @param boolean boolean to perform. - * @param options Set of options. - * @return Output image. - */ -VImage boolean( VImage right, VipsOperationBoolean boolean, VOption *options = 0 ) const; - -/** - * Boolean operations against a constant. - * @param boolean boolean to perform. - * @param c Array of constants. - * @param options Set of options. - * @return Output image. - */ -VImage boolean_const( VipsOperationBoolean boolean, std::vector c, VOption *options = 0 ) const; - -/** - * Build a look-up table. - * @param options Set of options. - * @return Output image. - */ -VImage buildlut( VOption *options = 0 ) const; - -/** - * Byteswap an image. - * @param options Set of options. - * @return Output image. - */ -VImage byteswap( VOption *options = 0 ) const; - -/** - * Cache an image. - * - * **Optional parameters** - * - **max_tiles** -- Maximum number of tiles to cache, int. - * - **tile_height** -- Tile height in pixels, int. - * - **tile_width** -- Tile width in pixels, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage cache( VOption *options = 0 ) const; - -/** - * Canny edge detector. - * - * **Optional parameters** - * - **sigma** -- Sigma of Gaussian, double. - * - **precision** -- Convolve with this precision, VipsPrecision. - * - * @param options Set of options. - * @return Output image. - */ -VImage canny( VOption *options = 0 ) const; - -/** - * Use pixel values to pick cases from an array of images. - * @param cases Array of case images. - * @param options Set of options. - * @return Output image. - */ -VImage case_image( std::vector cases, VOption *options = 0 ) const; - -/** - * Cast an image. - * - * **Optional parameters** - * - **shift** -- Shift integer values up and down, bool. - * - * @param format Format to cast to. - * @param options Set of options. - * @return Output image. - */ -VImage cast( VipsBandFormat format, VOption *options = 0 ) const; - -/** - * Convert to a new colorspace. - * - * **Optional parameters** - * - **source_space** -- Source color space, VipsInterpretation. - * - * @param space Destination color space. - * @param options Set of options. - * @return Output image. - */ -VImage colourspace( VipsInterpretation space, VOption *options = 0 ) const; - -/** - * Convolve with rotating mask. - * - * **Optional parameters** - * - **times** -- Rotate and convolve this many times, int. - * - **angle** -- Rotate mask by this much between convolutions, VipsAngle45. - * - **combine** -- Combine convolution results like this, VipsCombine. - * - **precision** -- Convolve with this precision, VipsPrecision. - * - **layers** -- Use this many layers in approximation, int. - * - **cluster** -- Cluster lines closer than this in approximation, int. - * - * @param mask Input matrix image. - * @param options Set of options. - * @return Output image. - */ -VImage compass( VImage mask, VOption *options = 0 ) const; - -/** - * Perform a complex operation on an image. - * @param cmplx complex to perform. - * @param options Set of options. - * @return Output image. - */ -VImage complex( VipsOperationComplex cmplx, VOption *options = 0 ) const; - -/** - * Complex binary operations on two images. - * @param right Right-hand image argument. - * @param cmplx binary complex operation to perform. - * @param options Set of options. - * @return Output image. - */ -VImage complex2( VImage right, VipsOperationComplex2 cmplx, VOption *options = 0 ) const; - -/** - * Form a complex image from two real images. - * @param right Right-hand image argument. - * @param options Set of options. - * @return Output image. - */ -VImage complexform( VImage right, VOption *options = 0 ) const; - -/** - * Get a component from a complex image. - * @param get complex to perform. - * @param options Set of options. - * @return Output image. - */ -VImage complexget( VipsOperationComplexget get, VOption *options = 0 ) const; - -/** - * Blend an array of images with an array of blend modes. - * - * **Optional parameters** - * - **x** -- Array of x coordinates to join at, std::vector. - * - **y** -- Array of y coordinates to join at, std::vector. - * - **compositing_space** -- Composite images in this colour space, VipsInterpretation. - * - **premultiplied** -- Images have premultiplied alpha, bool. - * - * @param in Array of input images. - * @param mode Array of VipsBlendMode to join with. - * @param options Set of options. - * @return Output image. - */ -static VImage composite( std::vector in, std::vector mode, VOption *options = 0 ); - -/** - * Blend a pair of images with a blend mode. - * - * **Optional parameters** - * - **x** -- x position of overlay, int. - * - **y** -- y position of overlay, int. - * - **compositing_space** -- Composite images in this colour space, VipsInterpretation. - * - **premultiplied** -- Images have premultiplied alpha, bool. - * - * @param overlay Overlay image. - * @param mode VipsBlendMode to join with. - * @param options Set of options. - * @return Output image. - */ -VImage composite2( VImage overlay, VipsBlendMode mode, VOption *options = 0 ) const; - -/** - * Convolution operation. - * - * **Optional parameters** - * - **precision** -- Convolve with this precision, VipsPrecision. - * - **layers** -- Use this many layers in approximation, int. - * - **cluster** -- Cluster lines closer than this in approximation, int. - * - * @param mask Input matrix image. - * @param options Set of options. - * @return Output image. - */ -VImage conv( VImage mask, VOption *options = 0 ) const; - -/** - * Approximate integer convolution. - * - * **Optional parameters** - * - **layers** -- Use this many layers in approximation, int. - * - **cluster** -- Cluster lines closer than this in approximation, int. - * - * @param mask Input matrix image. - * @param options Set of options. - * @return Output image. - */ -VImage conva( VImage mask, VOption *options = 0 ) const; - -/** - * Approximate separable integer convolution. - * - * **Optional parameters** - * - **layers** -- Use this many layers in approximation, int. - * - * @param mask Input matrix image. - * @param options Set of options. - * @return Output image. - */ -VImage convasep( VImage mask, VOption *options = 0 ) const; - -/** - * Float convolution operation. - * @param mask Input matrix image. - * @param options Set of options. - * @return Output image. - */ -VImage convf( VImage mask, VOption *options = 0 ) const; - -/** - * Int convolution operation. - * @param mask Input matrix image. - * @param options Set of options. - * @return Output image. - */ -VImage convi( VImage mask, VOption *options = 0 ) const; - -/** - * Seperable convolution operation. - * - * **Optional parameters** - * - **precision** -- Convolve with this precision, VipsPrecision. - * - **layers** -- Use this many layers in approximation, int. - * - **cluster** -- Cluster lines closer than this in approximation, int. - * - * @param mask Input matrix image. - * @param options Set of options. - * @return Output image. - */ -VImage convsep( VImage mask, VOption *options = 0 ) const; - -/** - * Copy an image. - * - * **Optional parameters** - * - **swap** -- Swap bytes in image between little and big-endian, bool. - * - **width** -- Image width in pixels, int. - * - **height** -- Image height in pixels, int. - * - **bands** -- Number of bands in image, int. - * - **format** -- Pixel format in image, VipsBandFormat. - * - **coding** -- Pixel coding, VipsCoding. - * - **interpretation** -- Pixel interpretation, VipsInterpretation. - * - **xres** -- Horizontal resolution in pixels/mm, double. - * - **yres** -- Vertical resolution in pixels/mm, double. - * - **xoffset** -- Horizontal offset of origin, int. - * - **yoffset** -- Vertical offset of origin, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage copy( VOption *options = 0 ) const; - -/** - * Count lines in an image. - * @param direction Countlines left-right or up-down. - * @param options Set of options. - * @return Number of lines. - */ -double countlines( VipsDirection direction, VOption *options = 0 ) const; - -/** - * Extract an area from an image. - * @param left Left edge of extract area. - * @param top Top edge of extract area. - * @param width Width of extract area. - * @param height Height of extract area. - * @param options Set of options. - * @return Output image. - */ -VImage crop( int left, int top, int width, int height, VOption *options = 0 ) const; - -/** - * Load csv. - * - * **Optional parameters** - * - **skip** -- Skip this many lines at the start of the file, int. - * - **lines** -- Read this many lines from the file, int. - * - **whitespace** -- Set of whitespace characters, const char *. - * - **separator** -- Set of separator characters, const char *. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage csvload( const char *filename, VOption *options = 0 ); - -/** - * Load csv. - * - * **Optional parameters** - * - **skip** -- Skip this many lines at the start of the file, int. - * - **lines** -- Read this many lines from the file, int. - * - **whitespace** -- Set of whitespace characters, const char *. - * - **separator** -- Set of separator characters, const char *. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage csvload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to csv. - * - * **Optional parameters** - * - **separator** -- Separator characters, const char *. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void csvsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to csv. - * - * **Optional parameters** - * - **separator** -- Separator characters, const char *. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void csvsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Calculate de00. - * @param right Right-hand input image. - * @param options Set of options. - * @return Output image. - */ -VImage dE00( VImage right, VOption *options = 0 ) const; - -/** - * Calculate de76. - * @param right Right-hand input image. - * @param options Set of options. - * @return Output image. - */ -VImage dE76( VImage right, VOption *options = 0 ) const; - -/** - * Calculate decmc. - * @param right Right-hand input image. - * @param options Set of options. - * @return Output image. - */ -VImage dECMC( VImage right, VOption *options = 0 ) const; - -/** - * Find image standard deviation. - * @param options Set of options. - * @return Output value. - */ -double deviate( VOption *options = 0 ) const; - -/** - * Divide two images. - * @param right Right-hand image argument. - * @param options Set of options. - * @return Output image. - */ -VImage divide( VImage right, VOption *options = 0 ) const; - -/** - * Draw a circle on an image. - * - * **Optional parameters** - * - **fill** -- Draw a solid object, bool. - * - * @param ink Color for pixels. - * @param cx Centre of draw_circle. - * @param cy Centre of draw_circle. - * @param radius Radius in pixels. - * @param options Set of options. - */ -void draw_circle( std::vector ink, int cx, int cy, int radius, VOption *options = 0 ) const; - -/** - * Flood-fill an area. - * - * **Optional parameters** - * - **test** -- Test pixels in this image, VImage. - * - **equal** -- DrawFlood while equal to edge, bool. - * - * @param ink Color for pixels. - * @param x DrawFlood start point. - * @param y DrawFlood start point. - * @param options Set of options. - */ -void draw_flood( std::vector ink, int x, int y, VOption *options = 0 ) const; - -/** - * Paint an image into another image. - * - * **Optional parameters** - * - **mode** -- Combining mode, VipsCombineMode. - * - * @param sub Sub-image to insert into main image. - * @param x Draw image here. - * @param y Draw image here. - * @param options Set of options. - */ -void draw_image( VImage sub, int x, int y, VOption *options = 0 ) const; - -/** - * Draw a line on an image. - * @param ink Color for pixels. - * @param x1 Start of draw_line. - * @param y1 Start of draw_line. - * @param x2 End of draw_line. - * @param y2 End of draw_line. - * @param options Set of options. - */ -void draw_line( std::vector ink, int x1, int y1, int x2, int y2, VOption *options = 0 ) const; - -/** - * Draw a mask on an image. - * @param ink Color for pixels. - * @param mask Mask of pixels to draw. - * @param x Draw mask here. - * @param y Draw mask here. - * @param options Set of options. - */ -void draw_mask( std::vector ink, VImage mask, int x, int y, VOption *options = 0 ) const; - -/** - * Paint a rectangle on an image. - * - * **Optional parameters** - * - **fill** -- Draw a solid object, bool. - * - * @param ink Color for pixels. - * @param left Rect to fill. - * @param top Rect to fill. - * @param width Rect to fill. - * @param height Rect to fill. - * @param options Set of options. - */ -void draw_rect( std::vector ink, int left, int top, int width, int height, VOption *options = 0 ) const; - -/** - * Blur a rectangle on an image. - * @param left Rect to fill. - * @param top Rect to fill. - * @param width Rect to fill. - * @param height Rect to fill. - * @param options Set of options. - */ -void draw_smudge( int left, int top, int width, int height, VOption *options = 0 ) const; - -/** - * Save image to deepzoom file. - * - * **Optional parameters** - * - **dirname** -- Directory name to save to, const char *. - * - **basename** -- Base name to save to, const char *. - * - **layout** -- Directory layout, VipsForeignDzLayout. - * - **suffix** -- Filename suffix for tiles, const char *. - * - **overlap** -- Tile overlap in pixels, int. - * - **tile_size** -- Tile size in pixels, int. - * - **tile_height** -- Tile height in pixels, int. - * - **tile_width** -- Tile width in pixels, int. - * - **centre** -- Center image in tile, bool. - * - **depth** -- Pyramid depth, VipsForeignDzDepth. - * - **angle** -- Rotate image during save, VipsAngle. - * - **container** -- Pyramid container type, VipsForeignDzContainer. - * - **properties** -- Write a properties file to the output directory, bool. - * - **compression** -- ZIP deflate compression level, int. - * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. - * - **skip_blanks** -- Skip tiles which are nearly equal to the background, int. - * - **no_strip** -- Don't strip tile metadata, bool. - * - **id** -- Resource ID, const char *. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void dzsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to dz buffer. - * - * **Optional parameters** - * - **dirname** -- Directory name to save to, const char *. - * - **basename** -- Base name to save to, const char *. - * - **layout** -- Directory layout, VipsForeignDzLayout. - * - **suffix** -- Filename suffix for tiles, const char *. - * - **overlap** -- Tile overlap in pixels, int. - * - **tile_size** -- Tile size in pixels, int. - * - **tile_height** -- Tile height in pixels, int. - * - **tile_width** -- Tile width in pixels, int. - * - **centre** -- Center image in tile, bool. - * - **depth** -- Pyramid depth, VipsForeignDzDepth. - * - **angle** -- Rotate image during save, VipsAngle. - * - **container** -- Pyramid container type, VipsForeignDzContainer. - * - **properties** -- Write a properties file to the output directory, bool. - * - **compression** -- ZIP deflate compression level, int. - * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. - * - **skip_blanks** -- Skip tiles which are nearly equal to the background, int. - * - **no_strip** -- Don't strip tile metadata, bool. - * - **id** -- Resource ID, const char *. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *dzsave_buffer( VOption *options = 0 ) const; - -/** - * Embed an image in a larger image. - * - * **Optional parameters** - * - **extend** -- How to generate the extra pixels, VipsExtend. - * - **background** -- Color for background pixels, std::vector. - * - * @param x Left edge of input in output. - * @param y Top edge of input in output. - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -VImage embed( int x, int y, int width, int height, VOption *options = 0 ) const; - -/** - * Extract an area from an image. - * @param left Left edge of extract area. - * @param top Top edge of extract area. - * @param width Width of extract area. - * @param height Height of extract area. - * @param options Set of options. - * @return Output image. - */ -VImage extract_area( int left, int top, int width, int height, VOption *options = 0 ) const; - -/** - * Extract band from an image. - * - * **Optional parameters** - * - **n** -- Number of bands to extract, int. - * - * @param band Band to extract. - * @param options Set of options. - * @return Output image. - */ -VImage extract_band( int band, VOption *options = 0 ) const; - -/** - * Make an image showing the eye's spatial response. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **factor** -- Maximum spatial frequency, double. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage eye( int width, int height, VOption *options = 0 ); - -/** - * False-color an image. - * @param options Set of options. - * @return Output image. - */ -VImage falsecolour( VOption *options = 0 ) const; - -/** - * Fast correlation. - * @param ref Input reference image. - * @param options Set of options. - * @return Output image. - */ -VImage fastcor( VImage ref, VOption *options = 0 ) const; - -/** - * Fill image zeros with nearest non-zero pixel. - * @param options Set of options. - * @return Value of nearest non-zero pixel. - */ -VImage fill_nearest( VOption *options = 0 ) const; - -/** - * Search an image for non-edge areas. - * - * **Optional parameters** - * - **threshold** -- Object threshold, double. - * - **background** -- Color for background pixels, std::vector. - * - * @param top Top edge of extract area. - * @param width Width of extract area. - * @param height Height of extract area. - * @param options Set of options. - * @return Left edge of image. - */ -int find_trim( int *top, int *width, int *height, VOption *options = 0 ) const; - -/** - * Load a fits image. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage fitsload( const char *filename, VOption *options = 0 ); - -/** - * Load fits from a source. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage fitsload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to fits file. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void fitssave( const char *filename, VOption *options = 0 ) const; - -/** - * Flatten alpha out of an image. - * - * **Optional parameters** - * - **background** -- Background value, std::vector. - * - **max_alpha** -- Maximum value of alpha channel, double. - * - * @param options Set of options. - * @return Output image. - */ -VImage flatten( VOption *options = 0 ) const; - -/** - * Flip an image. - * @param direction Direction to flip image. - * @param options Set of options. - * @return Output image. - */ -VImage flip( VipsDirection direction, VOption *options = 0 ) const; - -/** - * Transform float rgb to radiance coding. - * @param options Set of options. - * @return Output image. - */ -VImage float2rad( VOption *options = 0 ) const; - -/** - * Make a fractal surface. - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param fractal_dimension Fractal dimension. - * @param options Set of options. - * @return Output image. - */ -static VImage fractsurf( int width, int height, double fractal_dimension, VOption *options = 0 ); - -/** - * Frequency-domain filtering. - * @param mask Input mask image. - * @param options Set of options. - * @return Output image. - */ -VImage freqmult( VImage mask, VOption *options = 0 ) const; - -/** - * Forward fft. - * @param options Set of options. - * @return Output image. - */ -VImage fwfft( VOption *options = 0 ) const; - -/** - * Gamma an image. - * - * **Optional parameters** - * - **exponent** -- Gamma factor, double. - * - * @param options Set of options. - * @return Output image. - */ -VImage gamma( VOption *options = 0 ) const; - -/** - * Gaussian blur. - * - * **Optional parameters** - * - **min_ampl** -- Minimum amplitude of Gaussian, double. - * - **precision** -- Convolve with this precision, VipsPrecision. - * - * @param sigma Sigma of Gaussian. - * @param options Set of options. - * @return Output image. - */ -VImage gaussblur( double sigma, VOption *options = 0 ) const; - -/** - * Make a gaussian image. - * - * **Optional parameters** - * - **separable** -- Generate separable Gaussian, bool. - * - **integer** -- Generate integer Gaussian, bool. - * - **precision** -- Generate with this precision, VipsPrecision. - * - * @param sigma Sigma of Gaussian. - * @param min_ampl Minimum amplitude of Gaussian. - * @param options Set of options. - * @return Output image. - */ -static VImage gaussmat( double sigma, double min_ampl, VOption *options = 0 ); - -/** - * Make a gaussnoise image. - * - * **Optional parameters** - * - **sigma** -- Standard deviation of pixels in generated image, double. - * - **mean** -- Mean of pixels in generated image, double. - * - **seed** -- Random number seed, int. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage gaussnoise( int width, int height, VOption *options = 0 ); - -/** - * Read a point from an image. - * @param x Point to read. - * @param y Point to read. - * @param options Set of options. - * @return Array of output values. - */ -std::vector getpoint( int x, int y, VOption *options = 0 ) const; - -/** - * Load gif with libnsgif. - * - * **Optional parameters** - * - **n** -- Load this many pages, int. - * - **page** -- Load this page from the file, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage gifload( const char *filename, VOption *options = 0 ); - -/** - * Load gif with libnsgif. - * - * **Optional parameters** - * - **n** -- Load this many pages, int. - * - **page** -- Load this page from the file, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage gifload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load gif from source. - * - * **Optional parameters** - * - **n** -- Load this many pages, int. - * - **page** -- Load this page from the file, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage gifload_source( VSource source, VOption *options = 0 ); - -/** - * Global balance an image mosaic. - * - * **Optional parameters** - * - **gamma** -- Image gamma, double. - * - **int_output** -- Integer output, bool. - * - * @param options Set of options. - * @return Output image. - */ -VImage globalbalance( VOption *options = 0 ) const; - -/** - * Place an image within a larger image with a certain gravity. - * - * **Optional parameters** - * - **extend** -- How to generate the extra pixels, VipsExtend. - * - **background** -- Color for background pixels, std::vector. - * - * @param direction direction to place image within width/height. - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -VImage gravity( VipsCompassDirection direction, int width, int height, VOption *options = 0 ) const; - -/** - * Make a grey ramp image. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage grey( int width, int height, VOption *options = 0 ); - -/** - * Grid an image. - * @param tile_height chop into tiles this high. - * @param across number of tiles across. - * @param down number of tiles down. - * @param options Set of options. - * @return Output image. - */ -VImage grid( int tile_height, int across, int down, VOption *options = 0 ) const; - -/** - * Load a heif image. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **thumbnail** -- Fetch thumbnail image, bool. - * - **autorotate** -- Rotate image using exif orientation, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage heifload( const char *filename, VOption *options = 0 ); - -/** - * Load a heif image. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **thumbnail** -- Fetch thumbnail image, bool. - * - **autorotate** -- Rotate image using exif orientation, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage heifload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load a heif image. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **thumbnail** -- Fetch thumbnail image, bool. - * - **autorotate** -- Rotate image using exif orientation, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage heifload_source( VSource source, VOption *options = 0 ); - -/** - * Save image in heif format. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **lossless** -- Enable lossless compression, bool. - * - **compression** -- Compression format, VipsForeignHeifCompression. - * - **speed** -- CPU effort, int. - * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to load from. - * @param options Set of options. - */ -void heifsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image in heif format. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **lossless** -- Enable lossless compression, bool. - * - **compression** -- Compression format, VipsForeignHeifCompression. - * - **speed** -- CPU effort, int. - * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *heifsave_buffer( VOption *options = 0 ) const; - -/** - * Save image in heif format. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **lossless** -- Enable lossless compression, bool. - * - **compression** -- Compression format, VipsForeignHeifCompression. - * - **speed** -- CPU effort, int. - * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void heifsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Form cumulative histogram. - * @param options Set of options. - * @return Output image. - */ -VImage hist_cum( VOption *options = 0 ) const; - -/** - * Estimate image entropy. - * @param options Set of options. - * @return Output value. - */ -double hist_entropy( VOption *options = 0 ) const; - -/** - * Histogram equalisation. - * - * **Optional parameters** - * - **band** -- Equalise with this band, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage hist_equal( VOption *options = 0 ) const; - -/** - * Find image histogram. - * - * **Optional parameters** - * - **band** -- Find histogram of band, int. - * - * @param options Set of options. - * @return Output histogram. - */ -VImage hist_find( VOption *options = 0 ) const; - -/** - * Find indexed image histogram. - * - * **Optional parameters** - * - **combine** -- Combine bins like this, VipsCombine. - * - * @param index Index image. - * @param options Set of options. - * @return Output histogram. - */ -VImage hist_find_indexed( VImage index, VOption *options = 0 ) const; - -/** - * Find n-dimensional image histogram. - * - * **Optional parameters** - * - **bins** -- Number of bins in each dimension, int. - * - * @param options Set of options. - * @return Output histogram. - */ -VImage hist_find_ndim( VOption *options = 0 ) const; - -/** - * Test for monotonicity. - * @param options Set of options. - * @return true if in is monotonic. - */ -bool hist_ismonotonic( VOption *options = 0 ) const; - -/** - * Local histogram equalisation. - * - * **Optional parameters** - * - **max_slope** -- Maximum slope (CLAHE), int. - * - * @param width Window width in pixels. - * @param height Window height in pixels. - * @param options Set of options. - * @return Output image. - */ -VImage hist_local( int width, int height, VOption *options = 0 ) const; - -/** - * Match two histograms. - * @param ref Reference histogram. - * @param options Set of options. - * @return Output image. - */ -VImage hist_match( VImage ref, VOption *options = 0 ) const; - -/** - * Normalise histogram. - * @param options Set of options. - * @return Output image. - */ -VImage hist_norm( VOption *options = 0 ) const; - -/** - * Plot histogram. - * @param options Set of options. - * @return Output image. - */ -VImage hist_plot( VOption *options = 0 ) const; - -/** - * Find hough circle transform. - * - * **Optional parameters** - * - **scale** -- Scale down dimensions by this factor, int. - * - **min_radius** -- Smallest radius to search for, int. - * - **max_radius** -- Largest radius to search for, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage hough_circle( VOption *options = 0 ) const; - -/** - * Find hough line transform. - * - * **Optional parameters** - * - **width** -- horizontal size of parameter space, int. - * - **height** -- Vertical size of parameter space, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage hough_line( VOption *options = 0 ) const; - -/** - * Output to device with icc profile. - * - * **Optional parameters** - * - **pcs** -- Set Profile Connection Space, VipsPCS. - * - **intent** -- Rendering intent, VipsIntent. - * - **output_profile** -- Filename to load output profile from, const char *. - * - **depth** -- Output device space depth in bits, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage icc_export( VOption *options = 0 ) const; - -/** - * Import from device with icc profile. - * - * **Optional parameters** - * - **pcs** -- Set Profile Connection Space, VipsPCS. - * - **intent** -- Rendering intent, VipsIntent. - * - **embedded** -- Use embedded input profile, if available, bool. - * - **input_profile** -- Filename to load input profile from, const char *. - * - * @param options Set of options. - * @return Output image. - */ -VImage icc_import( VOption *options = 0 ) const; - -/** - * Transform between devices with icc profiles. - * - * **Optional parameters** - * - **pcs** -- Set Profile Connection Space, VipsPCS. - * - **intent** -- Rendering intent, VipsIntent. - * - **embedded** -- Use embedded input profile, if available, bool. - * - **input_profile** -- Filename to load input profile from, const char *. - * - **depth** -- Output device space depth in bits, int. - * - * @param output_profile Filename to load output profile from. - * @param options Set of options. - * @return Output image. - */ -VImage icc_transform( const char *output_profile, VOption *options = 0 ) const; - -/** - * Make a 1d image where pixel values are indexes. - * - * **Optional parameters** - * - **bands** -- Number of bands in LUT, int. - * - **ushort** -- Create a 16-bit LUT, bool. - * - **size** -- Size of 16-bit LUT, int. - * - * @param options Set of options. - * @return Output image. - */ -static VImage identity( VOption *options = 0 ); - -/** - * Ifthenelse an image. - * - * **Optional parameters** - * - **blend** -- Blend smoothly between then and else parts, bool. - * - * @param in1 Source for TRUE pixels. - * @param in2 Source for FALSE pixels. - * @param options Set of options. - * @return Output image. - */ -VImage ifthenelse( VImage in1, VImage in2, VOption *options = 0 ) const; - -/** - * Insert image @sub into @main at @x, @y. - * - * **Optional parameters** - * - **expand** -- Expand output to hold all of both inputs, bool. - * - **background** -- Color for new pixels, std::vector. - * - * @param sub Sub-image to insert into main image. - * @param x Left edge of sub in main. - * @param y Top edge of sub in main. - * @param options Set of options. - * @return Output image. - */ -VImage insert( VImage sub, int x, int y, VOption *options = 0 ) const; - -/** - * Invert an image. - * @param options Set of options. - * @return Output image. - */ -VImage invert( VOption *options = 0 ) const; - -/** - * Build an inverted look-up table. - * - * **Optional parameters** - * - **size** -- LUT size to generate, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage invertlut( VOption *options = 0 ) const; - -/** - * Inverse fft. - * - * **Optional parameters** - * - **real** -- Output only the real part of the transform, bool. - * - * @param options Set of options. - * @return Output image. - */ -VImage invfft( VOption *options = 0 ) const; - -/** - * Join a pair of images. - * - * **Optional parameters** - * - **expand** -- Expand output to hold all of both inputs, bool. - * - **shim** -- Pixels between images, int. - * - **background** -- Colour for new pixels, std::vector. - * - **align** -- Align on the low, centre or high coordinate edge, VipsAlign. - * - * @param in2 Second input image. - * @param direction Join left-right or up-down. - * @param options Set of options. - * @return Output image. - */ -VImage join( VImage in2, VipsDirection direction, VOption *options = 0 ) const; - -/** - * Load jpeg from file. - * - * **Optional parameters** - * - **shrink** -- Shrink factor on load, int. - * - **autorotate** -- Rotate image using exif orientation, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage jpegload( const char *filename, VOption *options = 0 ); - -/** - * Load jpeg from buffer. - * - * **Optional parameters** - * - **shrink** -- Shrink factor on load, int. - * - **autorotate** -- Rotate image using exif orientation, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage jpegload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load image from jpeg source. - * - * **Optional parameters** - * - **shrink** -- Shrink factor on load, int. - * - **autorotate** -- Rotate image using exif orientation, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage jpegload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to jpeg file. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **profile** -- ICC profile to embed, const char *. - * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. - * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. - * - **no_subsample** -- Disable chroma subsample, bool. - * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. - * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. - * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. - * - **quant_table** -- Use predefined quantization table with given index, int. - * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void jpegsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to jpeg buffer. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **profile** -- ICC profile to embed, const char *. - * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. - * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. - * - **no_subsample** -- Disable chroma subsample, bool. - * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. - * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. - * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. - * - **quant_table** -- Use predefined quantization table with given index, int. - * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *jpegsave_buffer( VOption *options = 0 ) const; - -/** - * Save image to jpeg mime. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **profile** -- ICC profile to embed, const char *. - * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. - * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. - * - **no_subsample** -- Disable chroma subsample, bool. - * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. - * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. - * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. - * - **quant_table** -- Use predefined quantization table with given index, int. - * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - */ -void jpegsave_mime( VOption *options = 0 ) const; - -/** - * Save image to jpeg target. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **profile** -- ICC profile to embed, const char *. - * - **optimize_coding** -- Compute optimal Huffman coding tables, bool. - * - **interlace** -- Generate an interlaced (progressive) jpeg, bool. - * - **no_subsample** -- Disable chroma subsample, bool. - * - **trellis_quant** -- Apply trellis quantisation to each 8x8 block, bool. - * - **overshoot_deringing** -- Apply overshooting to samples with extreme values, bool. - * - **optimize_scans** -- Split spectrum of DCT coefficients into separate scans, bool. - * - **quant_table** -- Use predefined quantization table with given index, int. - * - **subsample_mode** -- Select chroma subsample operation mode, VipsForeignSubsample. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void jpegsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Label regions in an image. - * @param options Set of options. - * @return Mask of region labels. - */ -VImage labelregions( VOption *options = 0 ) const; - -/** - * Calculate (a * in + b). - * - * **Optional parameters** - * - **uchar** -- Output should be uchar, bool. - * - * @param a Multiply by this. - * @param b Add this. - * @param options Set of options. - * @return Output image. - */ -VImage linear( std::vector a, std::vector b, VOption *options = 0 ) const; - -/** - * Cache an image as a set of lines. - * - * **Optional parameters** - * - **tile_height** -- Tile height in pixels, int. - * - **access** -- Expected access pattern, VipsAccess. - * - **threaded** -- Allow threaded access, bool. - * - **persistent** -- Keep cache between evaluations, bool. - * - * @param options Set of options. - * @return Output image. - */ -VImage linecache( VOption *options = 0 ) const; - -/** - * Make a laplacian of gaussian image. - * - * **Optional parameters** - * - **separable** -- Generate separable Logmatian, bool. - * - **integer** -- Generate integer Logmatian, bool. - * - **precision** -- Generate with this precision, VipsPrecision. - * - * @param sigma Radius of Logmatian. - * @param min_ampl Minimum amplitude of Logmatian. - * @param options Set of options. - * @return Output image. - */ -static VImage logmat( double sigma, double min_ampl, VOption *options = 0 ); - -/** - * Load file with imagemagick. - * - * **Optional parameters** - * - **all_frames** -- Read all frames from an image, bool. - * - **density** -- Canvas resolution for rendering vector formats like SVG, const char *. - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage magickload( const char *filename, VOption *options = 0 ); - -/** - * Load buffer with imagemagick. - * - * **Optional parameters** - * - **all_frames** -- Read all frames from an image, bool. - * - **density** -- Canvas resolution for rendering vector formats like SVG, const char *. - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage magickload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Save file with imagemagick. - * - * **Optional parameters** - * - **format** -- Format to save in, const char *. - * - **quality** -- Quality to use, int. - * - **optimize_gif_frames** -- Apply GIF frames optimization, bool. - * - **optimize_gif_transparency** -- Apply GIF transparency optimization, bool. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void magicksave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to magick buffer. - * - * **Optional parameters** - * - **format** -- Format to save in, const char *. - * - **quality** -- Quality to use, int. - * - **optimize_gif_frames** -- Apply GIF frames optimization, bool. - * - **optimize_gif_transparency** -- Apply GIF transparency optimization, bool. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *magicksave_buffer( VOption *options = 0 ) const; - -/** - * Resample with a map image. - * - * **Optional parameters** - * - **interpolate** -- Interpolate pixels with this, VInterpolate. - * - * @param index Index pixels with this. - * @param options Set of options. - * @return Output image. - */ -VImage mapim( VImage index, VOption *options = 0 ) const; - -/** - * Map an image though a lut. - * - * **Optional parameters** - * - **band** -- apply one-band lut to this band of in, int. - * - * @param lut Look-up table image. - * @param options Set of options. - * @return Output image. - */ -VImage maplut( VImage lut, VOption *options = 0 ) const; - -/** - * Make a butterworth filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param order Filter order. - * @param frequency_cutoff Frequency cutoff. - * @param amplitude_cutoff Amplitude cutoff. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_butterworth( int width, int height, double order, double frequency_cutoff, double amplitude_cutoff, VOption *options = 0 ); - -/** - * Make a butterworth_band filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param order Filter order. - * @param frequency_cutoff_x Frequency cutoff x. - * @param frequency_cutoff_y Frequency cutoff y. - * @param radius radius of circle. - * @param amplitude_cutoff Amplitude cutoff. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_butterworth_band( int width, int height, double order, double frequency_cutoff_x, double frequency_cutoff_y, double radius, double amplitude_cutoff, VOption *options = 0 ); - -/** - * Make a butterworth ring filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param order Filter order. - * @param frequency_cutoff Frequency cutoff. - * @param amplitude_cutoff Amplitude cutoff. - * @param ringwidth Ringwidth. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_butterworth_ring( int width, int height, double order, double frequency_cutoff, double amplitude_cutoff, double ringwidth, VOption *options = 0 ); - -/** - * Make fractal filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param fractal_dimension Fractal dimension. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_fractal( int width, int height, double fractal_dimension, VOption *options = 0 ); - -/** - * Make a gaussian filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param frequency_cutoff Frequency cutoff. - * @param amplitude_cutoff Amplitude cutoff. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_gaussian( int width, int height, double frequency_cutoff, double amplitude_cutoff, VOption *options = 0 ); - -/** - * Make a gaussian filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param frequency_cutoff_x Frequency cutoff x. - * @param frequency_cutoff_y Frequency cutoff y. - * @param radius radius of circle. - * @param amplitude_cutoff Amplitude cutoff. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_gaussian_band( int width, int height, double frequency_cutoff_x, double frequency_cutoff_y, double radius, double amplitude_cutoff, VOption *options = 0 ); - -/** - * Make a gaussian ring filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param frequency_cutoff Frequency cutoff. - * @param amplitude_cutoff Amplitude cutoff. - * @param ringwidth Ringwidth. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_gaussian_ring( int width, int height, double frequency_cutoff, double amplitude_cutoff, double ringwidth, VOption *options = 0 ); - -/** - * Make an ideal filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param frequency_cutoff Frequency cutoff. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_ideal( int width, int height, double frequency_cutoff, VOption *options = 0 ); - -/** - * Make an ideal band filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param frequency_cutoff_x Frequency cutoff x. - * @param frequency_cutoff_y Frequency cutoff y. - * @param radius radius of circle. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_ideal_band( int width, int height, double frequency_cutoff_x, double frequency_cutoff_y, double radius, VOption *options = 0 ); - -/** - * Make an ideal ring filter. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **nodc** -- Remove DC component, bool. - * - **reject** -- Invert the sense of the filter, bool. - * - **optical** -- Rotate quadrants to optical space, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param frequency_cutoff Frequency cutoff. - * @param ringwidth Ringwidth. - * @param options Set of options. - * @return Output image. - */ -static VImage mask_ideal_ring( int width, int height, double frequency_cutoff, double ringwidth, VOption *options = 0 ); - -/** - * First-order match of two images. - * - * **Optional parameters** - * - **hwindow** -- Half window size, int. - * - **harea** -- Half area size, int. - * - **search** -- Search to improve tie-points, bool. - * - **interpolate** -- Interpolate pixels with this, VInterpolate. - * - * @param sec Secondary image. - * @param xr1 Position of first reference tie-point. - * @param yr1 Position of first reference tie-point. - * @param xs1 Position of first secondary tie-point. - * @param ys1 Position of first secondary tie-point. - * @param xr2 Position of second reference tie-point. - * @param yr2 Position of second reference tie-point. - * @param xs2 Position of second secondary tie-point. - * @param ys2 Position of second secondary tie-point. - * @param options Set of options. - * @return Output image. - */ -VImage match( VImage sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, VOption *options = 0 ) const; - -/** - * Apply a math operation to an image. - * @param math math to perform. - * @param options Set of options. - * @return Output image. - */ -VImage math( VipsOperationMath math, VOption *options = 0 ) const; - -/** - * Binary math operations. - * @param right Right-hand image argument. - * @param math2 math to perform. - * @param options Set of options. - * @return Output image. - */ -VImage math2( VImage right, VipsOperationMath2 math2, VOption *options = 0 ) const; - -/** - * Binary math operations with a constant. - * @param math2 math to perform. - * @param c Array of constants. - * @param options Set of options. - * @return Output image. - */ -VImage math2_const( VipsOperationMath2 math2, std::vector c, VOption *options = 0 ) const; - -/** - * Load mat from file. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage matload( const char *filename, VOption *options = 0 ); - -/** - * Invert an matrix. - * @param options Set of options. - * @return Output matrix. - */ -VImage matrixinvert( VOption *options = 0 ) const; - -/** - * Load matrix. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage matrixload( const char *filename, VOption *options = 0 ); - -/** - * Load matrix. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage matrixload_source( VSource source, VOption *options = 0 ); - -/** - * Print matrix. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - */ -void matrixprint( VOption *options = 0 ) const; - -/** - * Save image to matrix. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void matrixsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to matrix. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void matrixsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Find image maximum. - * - * **Optional parameters** - * - **size** -- Number of maximum values to find, int. - * - * @param options Set of options. - * @return Output value. - */ -double max( VOption *options = 0 ) const; - -/** - * Measure a set of patches on a color chart. - * - * **Optional parameters** - * - **left** -- Left edge of extract area, int. - * - **top** -- Top edge of extract area, int. - * - **width** -- Width of extract area, int. - * - **height** -- Height of extract area, int. - * - * @param h Number of patches across chart. - * @param v Number of patches down chart. - * @param options Set of options. - * @return Output array of statistics. - */ -VImage measure( int h, int v, VOption *options = 0 ) const; - -/** - * Merge two images. - * - * **Optional parameters** - * - **mblend** -- Maximum blend size, int. - * - * @param sec Secondary image. - * @param direction Horizontal or vertical merge. - * @param dx Horizontal displacement from sec to ref. - * @param dy Vertical displacement from sec to ref. - * @param options Set of options. - * @return Output image. - */ -VImage merge( VImage sec, VipsDirection direction, int dx, int dy, VOption *options = 0 ) const; - -/** - * Find image minimum. - * - * **Optional parameters** - * - **size** -- Number of minimum values to find, int. - * - * @param options Set of options. - * @return Output value. - */ -double min( VOption *options = 0 ) const; - -/** - * Morphology operation. - * @param mask Input matrix image. - * @param morph Morphological operation to perform. - * @param options Set of options. - * @return Output image. - */ -VImage morph( VImage mask, VipsOperationMorphology morph, VOption *options = 0 ) const; - -/** - * Mosaic two images. - * - * **Optional parameters** - * - **hwindow** -- Half window size, int. - * - **harea** -- Half area size, int. - * - **mblend** -- Maximum blend size, int. - * - **bandno** -- Band to search for features on, int. - * - * @param sec Secondary image. - * @param direction Horizontal or vertical mosaic. - * @param xref Position of reference tie-point. - * @param yref Position of reference tie-point. - * @param xsec Position of secondary tie-point. - * @param ysec Position of secondary tie-point. - * @param options Set of options. - * @return Output image. - */ -VImage mosaic( VImage sec, VipsDirection direction, int xref, int yref, int xsec, int ysec, VOption *options = 0 ) const; - -/** - * First-order mosaic of two images. - * - * **Optional parameters** - * - **hwindow** -- Half window size, int. - * - **harea** -- Half area size, int. - * - **search** -- Search to improve tie-points, bool. - * - **interpolate** -- Interpolate pixels with this, VInterpolate. - * - **mblend** -- Maximum blend size, int. - * - **bandno** -- Band to search for features on, int. - * - * @param sec Secondary image. - * @param direction Horizontal or vertical mosaic. - * @param xr1 Position of first reference tie-point. - * @param yr1 Position of first reference tie-point. - * @param xs1 Position of first secondary tie-point. - * @param ys1 Position of first secondary tie-point. - * @param xr2 Position of second reference tie-point. - * @param yr2 Position of second reference tie-point. - * @param xs2 Position of second secondary tie-point. - * @param ys2 Position of second secondary tie-point. - * @param options Set of options. - * @return Output image. - */ -VImage mosaic1( VImage sec, VipsDirection direction, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, VOption *options = 0 ) const; - -/** - * Pick most-significant byte from an image. - * - * **Optional parameters** - * - **band** -- Band to msb, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage msb( VOption *options = 0 ) const; - -/** - * Multiply two images. - * @param right Right-hand image argument. - * @param options Set of options. - * @return Output image. - */ -VImage multiply( VImage right, VOption *options = 0 ) const; - -/** - * Load nifti volume. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage niftiload( const char *filename, VOption *options = 0 ); - -/** - * Load a nifti image. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param options Set of options. - * @return Output image. - */ -static VImage niftiload_base( VOption *options = 0 ); - -/** - * Load nifti volumes. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage niftiload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to nifti file. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void niftisave( const char *filename, VOption *options = 0 ) const; - -/** - * Load an openexr image. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage openexrload( const char *filename, VOption *options = 0 ); - -/** - * Load file with openslide. - * - * **Optional parameters** - * - **attach_associated** -- Attach all associated images, bool. - * - **level** -- Load this level from the file, int. - * - **autocrop** -- Crop to image bounds, bool. - * - **associated** -- Load this associated image, const char *. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage openslideload( const char *filename, VOption *options = 0 ); - -/** - * Load source with openslide. - * - * **Optional parameters** - * - **attach_associated** -- Attach all associated images, bool. - * - **level** -- Load this level from the file, int. - * - **autocrop** -- Crop to image bounds, bool. - * - **associated** -- Load this associated image, const char *. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage openslideload_source( VSource source, VOption *options = 0 ); - -/** - * Load pdf from file. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **dpi** -- Render at this DPI, double. - * - **scale** -- Scale output by this factor, double. - * - **background** -- Background value, std::vector. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage pdfload( const char *filename, VOption *options = 0 ); - -/** - * Load pdf from buffer. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **dpi** -- Render at this DPI, double. - * - **scale** -- Scale output by this factor, double. - * - **background** -- Background value, std::vector. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage pdfload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load pdf from source. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **dpi** -- Render at this DPI, double. - * - **scale** -- Scale output by this factor, double. - * - **background** -- Background value, std::vector. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage pdfload_source( VSource source, VOption *options = 0 ); - -/** - * Find threshold for percent of pixels. - * @param percent Percent of pixels. - * @param options Set of options. - * @return Threshold above which lie percent of pixels. - */ -int percent( double percent, VOption *options = 0 ) const; - -/** - * Make a perlin noise image. - * - * **Optional parameters** - * - **cell_size** -- Size of Perlin cells, int. - * - **uchar** -- Output an unsigned char image, bool. - * - **seed** -- Random number seed, int. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage perlin( int width, int height, VOption *options = 0 ); - -/** - * Calculate phase correlation. - * @param in2 Second input image. - * @param options Set of options. - * @return Output image. - */ -VImage phasecor( VImage in2, VOption *options = 0 ) const; - -/** - * Load png from file. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage pngload( const char *filename, VOption *options = 0 ); - -/** - * Load png from buffer. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage pngload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load png from source. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage pngload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to png file. - * - * **Optional parameters** - * - **compression** -- Compression factor, int. - * - **interlace** -- Interlace image, bool. - * - **profile** -- ICC profile to embed, const char *. - * - **filter** -- libpng row filter flag(s), int. - * - **palette** -- Quantise to 8bpp palette, bool. - * - **colours** -- Max number of palette colours, int. - * - **Q** -- Quantisation quality, int. - * - **dither** -- Amount of dithering, double. - * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void pngsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to png buffer. - * - * **Optional parameters** - * - **compression** -- Compression factor, int. - * - **interlace** -- Interlace image, bool. - * - **profile** -- ICC profile to embed, const char *. - * - **filter** -- libpng row filter flag(s), int. - * - **palette** -- Quantise to 8bpp palette, bool. - * - **colours** -- Max number of palette colours, int. - * - **Q** -- Quantisation quality, int. - * - **dither** -- Amount of dithering, double. - * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *pngsave_buffer( VOption *options = 0 ) const; - -/** - * Save image to target as png. - * - * **Optional parameters** - * - **compression** -- Compression factor, int. - * - **interlace** -- Interlace image, bool. - * - **profile** -- ICC profile to embed, const char *. - * - **filter** -- libpng row filter flag(s), int. - * - **palette** -- Quantise to 8bpp palette, bool. - * - **colours** -- Max number of palette colours, int. - * - **Q** -- Quantisation quality, int. - * - **dither** -- Amount of dithering, double. - * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void pngsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Load ppm from file. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage ppmload( const char *filename, VOption *options = 0 ); - -/** - * Load ppm base class. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage ppmload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to ppm file. - * - * **Optional parameters** - * - **ascii** -- save as ascii, bool. - * - **squash** -- save as one bit, bool. - * - **bitdepth** -- set to 1 to write as a 1 bit image, int. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void ppmsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save to ppm. - * - * **Optional parameters** - * - **ascii** -- save as ascii, bool. - * - **squash** -- save as one bit, bool. - * - **bitdepth** -- set to 1 to write as a 1 bit image, int. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void ppmsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Premultiply image alpha. - * - * **Optional parameters** - * - **max_alpha** -- Maximum value of alpha channel, double. - * - * @param options Set of options. - * @return Output image. - */ -VImage premultiply( VOption *options = 0 ) const; - -/** - * Find image profiles. - * @param rows First non-zero pixel in row. - * @param options Set of options. - * @return First non-zero pixel in column. - */ -VImage profile( VImage *rows, VOption *options = 0 ) const; - -/** - * Load named icc profile. - * @param name Profile name. - * @param options Set of options. - * @return Loaded profile. - */ -static VipsBlob *profile_load( const char *name, VOption *options = 0 ); - -/** - * Find image projections. - * @param rows Sums of rows. - * @param options Set of options. - * @return Sums of columns. - */ -VImage project( VImage *rows, VOption *options = 0 ) const; - -/** - * Resample an image with a quadratic transform. - * - * **Optional parameters** - * - **interpolate** -- Interpolate values with this, VInterpolate. - * - * @param coeff Coefficient matrix. - * @param options Set of options. - * @return Output image. - */ -VImage quadratic( VImage coeff, VOption *options = 0 ) const; - -/** - * Unpack radiance coding to float rgb. - * @param options Set of options. - * @return Output image. - */ -VImage rad2float( VOption *options = 0 ) const; - -/** - * Load a radiance image from a file. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage radload( const char *filename, VOption *options = 0 ); - -/** - * Load rad from buffer. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage radload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load rad from source. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage radload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to radiance file. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void radsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to radiance buffer. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *radsave_buffer( VOption *options = 0 ) const; - -/** - * Save image to radiance target. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void radsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Rank filter. - * @param width Window width in pixels. - * @param height Window height in pixels. - * @param index Select pixel at index. - * @param options Set of options. - * @return Output image. - */ -VImage rank( int width, int height, int index, VOption *options = 0 ) const; - -/** - * Load raw data from a file. - * - * **Optional parameters** - * - **offset** -- Offset in bytes from start of file, guint64. - * - **format** -- Pixel format in image, VipsBandFormat. - * - **interpretation** -- Pixel interpretation, VipsInterpretation. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param bands Number of bands in image. - * @param options Set of options. - * @return Output image. - */ -static VImage rawload( const char *filename, int width, int height, int bands, VOption *options = 0 ); - -/** - * Save image to raw file. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void rawsave( const char *filename, VOption *options = 0 ) const; - -/** - * Write raw image to file descriptor. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param fd File descriptor to write to. - * @param options Set of options. - */ -void rawsave_fd( int fd, VOption *options = 0 ) const; - -/** - * Linear recombination with matrix. - * @param m matrix of coefficients. - * @param options Set of options. - * @return Output image. - */ -VImage recomb( VImage m, VOption *options = 0 ) const; - -/** - * Reduce an image. - * - * **Optional parameters** - * - **kernel** -- Resampling kernel, VipsKernel. - * - **centre** -- Use centre sampling convention, bool. - * - * @param hshrink Horizontal shrink factor. - * @param vshrink Vertical shrink factor. - * @param options Set of options. - * @return Output image. - */ -VImage reduce( double hshrink, double vshrink, VOption *options = 0 ) const; - -/** - * Shrink an image horizontally. - * - * **Optional parameters** - * - **kernel** -- Resampling kernel, VipsKernel. - * - **centre** -- Use centre sampling convention, bool. - * - * @param hshrink Horizontal shrink factor. - * @param options Set of options. - * @return Output image. - */ -VImage reduceh( double hshrink, VOption *options = 0 ) const; - -/** - * Shrink an image vertically. - * - * **Optional parameters** - * - **kernel** -- Resampling kernel, VipsKernel. - * - **centre** -- Use centre sampling convention, bool. - * - * @param vshrink Vertical shrink factor. - * @param options Set of options. - * @return Output image. - */ -VImage reducev( double vshrink, VOption *options = 0 ) const; - -/** - * Relational operation on two images. - * @param right Right-hand image argument. - * @param relational relational to perform. - * @param options Set of options. - * @return Output image. - */ -VImage relational( VImage right, VipsOperationRelational relational, VOption *options = 0 ) const; - -/** - * Relational operations against a constant. - * @param relational relational to perform. - * @param c Array of constants. - * @param options Set of options. - * @return Output image. - */ -VImage relational_const( VipsOperationRelational relational, std::vector c, VOption *options = 0 ) const; - -/** - * Remainder after integer division of two images. - * @param right Right-hand image argument. - * @param options Set of options. - * @return Output image. - */ -VImage remainder( VImage right, VOption *options = 0 ) const; - -/** - * Remainder after integer division of an image and a constant. - * @param c Array of constants. - * @param options Set of options. - * @return Output image. - */ -VImage remainder_const( std::vector c, VOption *options = 0 ) const; - -/** - * Replicate an image. - * @param across Repeat this many times horizontally. - * @param down Repeat this many times vertically. - * @param options Set of options. - * @return Output image. - */ -VImage replicate( int across, int down, VOption *options = 0 ) const; - -/** - * Resize an image. - * - * **Optional parameters** - * - **interpolate** -- Interpolate pixels with this, VInterpolate. - * - **kernel** -- Resampling kernel, VipsKernel. - * - **centre** -- Use centre sampling convention, bool. - * - **vscale** -- Vertical scale image by this factor, double. - * - **idx** -- Horizontal input displacement, double. - * - **idy** -- Vertical input displacement, double. - * - * @param scale Scale image by this factor. - * @param options Set of options. - * @return Output image. - */ -VImage resize( double scale, VOption *options = 0 ) const; - -/** - * Rotate an image. - * @param angle Angle to rotate image. - * @param options Set of options. - * @return Output image. - */ -VImage rot( VipsAngle angle, VOption *options = 0 ) const; - -/** - * Rotate an image. - * - * **Optional parameters** - * - **angle** -- Angle to rotate image, VipsAngle45. - * - * @param options Set of options. - * @return Output image. - */ -VImage rot45( VOption *options = 0 ) const; - -/** - * Rotate an image by a number of degrees. - * - * **Optional parameters** - * - **interpolate** -- Interpolate pixels with this, VInterpolate. - * - **background** -- Background value, std::vector. - * - **odx** -- Horizontal output displacement, double. - * - **ody** -- Vertical output displacement, double. - * - **idx** -- Horizontal input displacement, double. - * - **idy** -- Vertical input displacement, double. - * - * @param angle Rotate anticlockwise by this many degrees. - * @param options Set of options. - * @return Output image. - */ -VImage rotate( double angle, VOption *options = 0 ) const; - -/** - * Perform a round function on an image. - * @param round rounding operation to perform. - * @param options Set of options. - * @return Output image. - */ -VImage round( VipsOperationRound round, VOption *options = 0 ) const; - -/** - * Transform srgb to hsv. - * @param options Set of options. - * @return Output image. - */ -VImage sRGB2HSV( VOption *options = 0 ) const; - -/** - * Convert an srgb image to scrgb. - * @param options Set of options. - * @return Output image. - */ -VImage sRGB2scRGB( VOption *options = 0 ) const; - -/** - * Convert scrgb to bw. - * - * **Optional parameters** - * - **depth** -- Output device space depth in bits, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage scRGB2BW( VOption *options = 0 ) const; - -/** - * Transform scrgb to xyz. - * @param options Set of options. - * @return Output image. - */ -VImage scRGB2XYZ( VOption *options = 0 ) const; - -/** - * Convert an scrgb image to srgb. - * - * **Optional parameters** - * - **depth** -- Output device space depth in bits, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage scRGB2sRGB( VOption *options = 0 ) const; - -/** - * Scale an image to uchar. - * - * **Optional parameters** - * - **exp** -- Exponent for log scale, double. - * - **log** -- Log scale, bool. - * - * @param options Set of options. - * @return Output image. - */ -VImage scale( VOption *options = 0 ) const; - -/** - * Check sequential access. - * - * **Optional parameters** - * - **trace** -- trace pixel requests, bool. - * - **tile_height** -- Tile height in pixels, int. - * - **access** -- Expected access pattern, VipsAccess. - * - * @param options Set of options. - * @return Output image. - */ -VImage sequential( VOption *options = 0 ) const; - -/** - * Unsharp masking for print. - * - * **Optional parameters** - * - **radius** -- radius of Gaussian, int. - * - **sigma** -- Sigma of Gaussian, double. - * - **x1** -- Flat/jaggy threshold, double. - * - **y2** -- Maximum brightening, double. - * - **y3** -- Maximum darkening, double. - * - **m1** -- Slope for flat areas, double. - * - **m2** -- Slope for jaggy areas, double. - * - * @param options Set of options. - * @return Output image. - */ -VImage sharpen( VOption *options = 0 ) const; - -/** - * Shrink an image. - * @param hshrink Horizontal shrink factor. - * @param vshrink Vertical shrink factor. - * @param options Set of options. - * @return Output image. - */ -VImage shrink( double hshrink, double vshrink, VOption *options = 0 ) const; - -/** - * Shrink an image horizontally. - * @param hshrink Horizontal shrink factor. - * @param options Set of options. - * @return Output image. - */ -VImage shrinkh( int hshrink, VOption *options = 0 ) const; - -/** - * Shrink an image vertically. - * @param vshrink Vertical shrink factor. - * @param options Set of options. - * @return Output image. - */ -VImage shrinkv( int vshrink, VOption *options = 0 ) const; - -/** - * Unit vector of pixel. - * @param options Set of options. - * @return Output image. - */ -VImage sign( VOption *options = 0 ) const; - -/** - * Similarity transform of an image. - * - * **Optional parameters** - * - **scale** -- Scale by this factor, double. - * - **angle** -- Rotate anticlockwise by this many degrees, double. - * - **interpolate** -- Interpolate pixels with this, VInterpolate. - * - **background** -- Background value, std::vector. - * - **odx** -- Horizontal output displacement, double. - * - **ody** -- Vertical output displacement, double. - * - **idx** -- Horizontal input displacement, double. - * - **idy** -- Vertical input displacement, double. - * - * @param options Set of options. - * @return Output image. - */ -VImage similarity( VOption *options = 0 ) const; - -/** - * Make a 2d sine wave. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - **hfreq** -- Horizontal spatial frequency, double. - * - **vfreq** -- Vertical spatial frequency, double. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage sines( int width, int height, VOption *options = 0 ); - -/** - * Extract an area from an image. - * - * **Optional parameters** - * - **interesting** -- How to measure interestingness, VipsInteresting. - * - * @param width Width of extract area. - * @param height Height of extract area. - * @param options Set of options. - * @return Output image. - */ -VImage smartcrop( int width, int height, VOption *options = 0 ) const; - -/** - * Sobel edge detector. - * @param options Set of options. - * @return Output image. - */ -VImage sobel( VOption *options = 0 ) const; - -/** - * Spatial correlation. - * @param ref Input reference image. - * @param options Set of options. - * @return Output image. - */ -VImage spcor( VImage ref, VOption *options = 0 ) const; - -/** - * Make displayable power spectrum. - * @param options Set of options. - * @return Output image. - */ -VImage spectrum( VOption *options = 0 ) const; - -/** - * Find many image stats. - * @param options Set of options. - * @return Output array of statistics. - */ -VImage stats( VOption *options = 0 ) const; - -/** - * Statistical difference. - * - * **Optional parameters** - * - **s0** -- New deviation, double. - * - **b** -- Weight of new deviation, double. - * - **m0** -- New mean, double. - * - **a** -- Weight of new mean, double. - * - * @param width Window width in pixels. - * @param height Window height in pixels. - * @param options Set of options. - * @return Output image. - */ -VImage stdif( int width, int height, VOption *options = 0 ) const; - -/** - * Subsample an image. - * - * **Optional parameters** - * - **point** -- Point sample, bool. - * - * @param xfac Horizontal subsample factor. - * @param yfac Vertical subsample factor. - * @param options Set of options. - * @return Output image. - */ -VImage subsample( int xfac, int yfac, VOption *options = 0 ) const; - -/** - * Subtract two images. - * @param right Right-hand image argument. - * @param options Set of options. - * @return Output image. - */ -VImage subtract( VImage right, VOption *options = 0 ) const; - -/** - * Sum an array of images. - * @param in Array of input images. - * @param options Set of options. - * @return Output image. - */ -static VImage sum( std::vector in, VOption *options = 0 ); - -/** - * Load svg with rsvg. - * - * **Optional parameters** - * - **dpi** -- Render at this DPI, double. - * - **scale** -- Scale output by this factor, double. - * - **unlimited** -- Allow SVG of any size, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage svgload( const char *filename, VOption *options = 0 ); - -/** - * Load svg with rsvg. - * - * **Optional parameters** - * - **dpi** -- Render at this DPI, double. - * - **scale** -- Scale output by this factor, double. - * - **unlimited** -- Allow SVG of any size, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage svgload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load svg from source. - * - * **Optional parameters** - * - **dpi** -- Render at this DPI, double. - * - **scale** -- Scale output by this factor, double. - * - **unlimited** -- Allow SVG of any size, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage svgload_source( VSource source, VOption *options = 0 ); - -/** - * Find the index of the first non-zero pixel in tests. - * @param tests Table of images to test. - * @param options Set of options. - * @return Output image. - */ -static VImage switch_image( std::vector tests, VOption *options = 0 ); - -/** - * Run an external command. - * - * **Optional parameters** - * - **in** -- Array of input images, std::vector. - * - **out_format** -- Format for output filename, const char *. - * - **in_format** -- Format for input filename, const char *. - * - * @param cmd_format Command to run. - * @param options Set of options. - */ -static void system( const char *cmd_format, VOption *options = 0 ); - -/** - * Make a text image. - * - * **Optional parameters** - * - **font** -- Font to render with, const char *. - * - **width** -- Maximum image width in pixels, int. - * - **height** -- Maximum image height in pixels, int. - * - **align** -- Align on the low, centre or high edge, VipsAlign. - * - **dpi** -- DPI to render at, int. - * - **justify** -- Justify lines, bool. - * - **spacing** -- Line spacing, int. - * - **fontfile** -- Load this font file, const char *. - * - * @param text Text to render. - * @param options Set of options. - * @return Output image. - */ -static VImage text( const char *text, VOption *options = 0 ); - -/** - * Generate thumbnail from file. - * - * **Optional parameters** - * - **height** -- Size to this height, int. - * - **size** -- Only upsize, only downsize, or both, VipsSize. - * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. - * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. - * - **linear** -- Reduce in linear light, bool. - * - **import_profile** -- Fallback import profile, const char *. - * - **export_profile** -- Fallback export profile, const char *. - * - **intent** -- Rendering intent, VipsIntent. - * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. - * - * @param filename Filename to read from. - * @param width Size to this width. - * @param options Set of options. - * @return Output image. - */ -static VImage thumbnail( const char *filename, int width, VOption *options = 0 ); - -/** - * Generate thumbnail from buffer. - * - * **Optional parameters** - * - **option_string** -- Options that are passed on to the underlying loader, const char *. - * - **height** -- Size to this height, int. - * - **size** -- Only upsize, only downsize, or both, VipsSize. - * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. - * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. - * - **linear** -- Reduce in linear light, bool. - * - **import_profile** -- Fallback import profile, const char *. - * - **export_profile** -- Fallback export profile, const char *. - * - **intent** -- Rendering intent, VipsIntent. - * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. - * - * @param buffer Buffer to load from. - * @param width Size to this width. - * @param options Set of options. - * @return Output image. - */ -static VImage thumbnail_buffer( VipsBlob *buffer, int width, VOption *options = 0 ); - -/** - * Generate thumbnail from image. - * - * **Optional parameters** - * - **height** -- Size to this height, int. - * - **size** -- Only upsize, only downsize, or both, VipsSize. - * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. - * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. - * - **linear** -- Reduce in linear light, bool. - * - **import_profile** -- Fallback import profile, const char *. - * - **export_profile** -- Fallback export profile, const char *. - * - **intent** -- Rendering intent, VipsIntent. - * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. - * - * @param width Size to this width. - * @param options Set of options. - * @return Output image. - */ -VImage thumbnail_image( int width, VOption *options = 0 ) const; - -/** - * Generate thumbnail from source. - * - * **Optional parameters** - * - **option_string** -- Options that are passed on to the underlying loader, const char *. - * - **height** -- Size to this height, int. - * - **size** -- Only upsize, only downsize, or both, VipsSize. - * - **no_rotate** -- Don't use orientation tags to rotate image upright, bool. - * - **crop** -- Reduce to fill target rectangle, then crop, VipsInteresting. - * - **linear** -- Reduce in linear light, bool. - * - **import_profile** -- Fallback import profile, const char *. - * - **export_profile** -- Fallback export profile, const char *. - * - **intent** -- Rendering intent, VipsIntent. - * - **auto_rotate** -- Use orientation tags to rotate image upright, bool. - * - * @param source Source to load from. - * @param width Size to this width. - * @param options Set of options. - * @return Output image. - */ -static VImage thumbnail_source( VSource source, int width, VOption *options = 0 ); - -/** - * Load tiff from file. - * - * **Optional parameters** - * - **page** -- Load this page from the image, int. - * - **subifd** -- Select subifd index, int. - * - **n** -- Load this many pages, int. - * - **autorotate** -- Rotate image using orientation tag, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage tiffload( const char *filename, VOption *options = 0 ); - -/** - * Load tiff from buffer. - * - * **Optional parameters** - * - **page** -- Load this page from the image, int. - * - **subifd** -- Select subifd index, int. - * - **n** -- Load this many pages, int. - * - **autorotate** -- Rotate image using orientation tag, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage tiffload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load tiff from source. - * - * **Optional parameters** - * - **page** -- Load this page from the image, int. - * - **subifd** -- Select subifd index, int. - * - **n** -- Load this many pages, int. - * - **autorotate** -- Rotate image using orientation tag, bool. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage tiffload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to tiff file. - * - * **Optional parameters** - * - **compression** -- Compression for this file, VipsForeignTiffCompression. - * - **Q** -- Q factor, int. - * - **predictor** -- Compression prediction, VipsForeignTiffPredictor. - * - **profile** -- ICC profile to embed, const char *. - * - **tile** -- Write a tiled tiff, bool. - * - **tile_width** -- Tile width in pixels, int. - * - **tile_height** -- Tile height in pixels, int. - * - **pyramid** -- Write a pyramidal tiff, bool. - * - **squash** -- Squash images down to 1 bit, bool. - * - **miniswhite** -- Use 0 for white in 1-bit images, bool. - * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. - * - **resunit** -- Resolution unit, VipsForeignTiffResunit. - * - **xres** -- Horizontal resolution in pixels/mm, double. - * - **yres** -- Vertical resolution in pixels/mm, double. - * - **bigtiff** -- Write a bigtiff image, bool. - * - **rgbjpeg** -- Output RGB JPEG rather than YCbCr, bool. - * - **properties** -- Write a properties document to IMAGEDESCRIPTION, bool. - * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. - * - **level** -- ZSTD compression level, int. - * - **subifd** -- Save pyr layers as sub-IFDs, bool. - * - **lossless** -- Enable WEBP lossless mode, bool. - * - **depth** -- Pyramid depth, VipsForeignDzDepth. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void tiffsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to tiff buffer. - * - * **Optional parameters** - * - **compression** -- Compression for this file, VipsForeignTiffCompression. - * - **Q** -- Q factor, int. - * - **predictor** -- Compression prediction, VipsForeignTiffPredictor. - * - **profile** -- ICC profile to embed, const char *. - * - **tile** -- Write a tiled tiff, bool. - * - **tile_width** -- Tile width in pixels, int. - * - **tile_height** -- Tile height in pixels, int. - * - **pyramid** -- Write a pyramidal tiff, bool. - * - **squash** -- Squash images down to 1 bit, bool. - * - **miniswhite** -- Use 0 for white in 1-bit images, bool. - * - **bitdepth** -- Write as a 1, 2, 4 or 8 bit image, int. - * - **resunit** -- Resolution unit, VipsForeignTiffResunit. - * - **xres** -- Horizontal resolution in pixels/mm, double. - * - **yres** -- Vertical resolution in pixels/mm, double. - * - **bigtiff** -- Write a bigtiff image, bool. - * - **rgbjpeg** -- Output RGB JPEG rather than YCbCr, bool. - * - **properties** -- Write a properties document to IMAGEDESCRIPTION, bool. - * - **region_shrink** -- Method to shrink regions, VipsRegionShrink. - * - **level** -- ZSTD compression level, int. - * - **subifd** -- Save pyr layers as sub-IFDs, bool. - * - **lossless** -- Enable WEBP lossless mode, bool. - * - **depth** -- Pyramid depth, VipsForeignDzDepth. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *tiffsave_buffer( VOption *options = 0 ) const; - -/** - * Cache an image as a set of tiles. - * - * **Optional parameters** - * - **tile_width** -- Tile width in pixels, int. - * - **tile_height** -- Tile height in pixels, int. - * - **max_tiles** -- Maximum number of tiles to cache, int. - * - **access** -- Expected access pattern, VipsAccess. - * - **threaded** -- Allow threaded access, bool. - * - **persistent** -- Keep cache between evaluations, bool. - * - * @param options Set of options. - * @return Output image. - */ -VImage tilecache( VOption *options = 0 ) const; - -/** - * Build a look-up table. - * - * **Optional parameters** - * - **in_max** -- Size of LUT to build, int. - * - **out_max** -- Maximum value in output LUT, int. - * - **Lb** -- Lowest value in output, double. - * - **Lw** -- Highest value in output, double. - * - **Ps** -- Position of shadow, double. - * - **Pm** -- Position of mid-tones, double. - * - **Ph** -- Position of highlights, double. - * - **S** -- Adjust shadows by this much, double. - * - **M** -- Adjust mid-tones by this much, double. - * - **H** -- Adjust highlights by this much, double. - * - * @param options Set of options. - * @return Output image. - */ -static VImage tonelut( VOption *options = 0 ); - -/** - * Transpose3d an image. - * - * **Optional parameters** - * - **page_height** -- Height of each input page, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage transpose3d( VOption *options = 0 ) const; - -/** - * Unpremultiply image alpha. - * - * **Optional parameters** - * - **max_alpha** -- Maximum value of alpha channel, double. - * - **alpha_band** -- Unpremultiply with this alpha, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage unpremultiply( VOption *options = 0 ) const; - -/** - * Load vips from file. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage vipsload( const char *filename, VOption *options = 0 ); - -/** - * Load vips from source. - * - * **Optional parameters** - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage vipsload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to file in vips format. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void vipssave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to target in vips format. - * - * **Optional parameters** - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void vipssave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Load webp from file. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **scale** -- Scale factor on load, double. - * - **shrink** -- Shrink factor on load, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param filename Filename to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage webpload( const char *filename, VOption *options = 0 ); - -/** - * Load webp from buffer. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **scale** -- Scale factor on load, double. - * - **shrink** -- Shrink factor on load, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param buffer Buffer to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage webpload_buffer( VipsBlob *buffer, VOption *options = 0 ); - -/** - * Load webp from source. - * - * **Optional parameters** - * - **page** -- Load this page from the file, int. - * - **n** -- Load this many pages, int. - * - **scale** -- Scale factor on load, double. - * - **shrink** -- Shrink factor on load, int. - * - **memory** -- Force open via memory, bool. - * - **access** -- Required access pattern for this file, VipsAccess. - * - **sequential** -- Sequential read only, bool. - * - **fail** -- Fail on first error, bool. - * - **disc** -- Open to disc, bool. - * - * @param source Source to load from. - * @param options Set of options. - * @return Output image. - */ -static VImage webpload_source( VSource source, VOption *options = 0 ); - -/** - * Save image to webp file. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **lossless** -- enable lossless compression, bool. - * - **preset** -- Preset for lossy compression, VipsForeignWebpPreset. - * - **smart_subsample** -- Enable high quality chroma subsampling, bool. - * - **near_lossless** -- Enable preprocessing in lossless mode (uses Q), bool. - * - **alpha_q** -- Change alpha plane fidelity for lossy compression, int. - * - **min_size** -- Optimise for minium size, bool. - * - **kmin** -- Minimum number of frames between key frames, int. - * - **kmax** -- Maximum number of frames between key frames, int. - * - **reduction_effort** -- Level of CPU effort to reduce file size, int. - * - **profile** -- ICC profile to embed, const char *. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param filename Filename to save to. - * @param options Set of options. - */ -void webpsave( const char *filename, VOption *options = 0 ) const; - -/** - * Save image to webp buffer. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **lossless** -- enable lossless compression, bool. - * - **preset** -- Preset for lossy compression, VipsForeignWebpPreset. - * - **smart_subsample** -- Enable high quality chroma subsampling, bool. - * - **near_lossless** -- Enable preprocessing in lossless mode (uses Q), bool. - * - **alpha_q** -- Change alpha plane fidelity for lossy compression, int. - * - **min_size** -- Optimise for minium size, bool. - * - **kmin** -- Minimum number of frames between key frames, int. - * - **kmax** -- Maximum number of frames between key frames, int. - * - **reduction_effort** -- Level of CPU effort to reduce file size, int. - * - **profile** -- ICC profile to embed, const char *. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param options Set of options. - * @return Buffer to save to. - */ -VipsBlob *webpsave_buffer( VOption *options = 0 ) const; - -/** - * Save image to webp target. - * - * **Optional parameters** - * - **Q** -- Q factor, int. - * - **lossless** -- enable lossless compression, bool. - * - **preset** -- Preset for lossy compression, VipsForeignWebpPreset. - * - **smart_subsample** -- Enable high quality chroma subsampling, bool. - * - **near_lossless** -- Enable preprocessing in lossless mode (uses Q), bool. - * - **alpha_q** -- Change alpha plane fidelity for lossy compression, int. - * - **min_size** -- Optimise for minium size, bool. - * - **kmin** -- Minimum number of frames between key frames, int. - * - **kmax** -- Maximum number of frames between key frames, int. - * - **reduction_effort** -- Level of CPU effort to reduce file size, int. - * - **profile** -- ICC profile to embed, const char *. - * - **strip** -- Strip all metadata from image, bool. - * - **background** -- Background value, std::vector. - * - **page_height** -- Set page height for multipage save, int. - * - * @param target Target to save to. - * @param options Set of options. - */ -void webpsave_target( VTarget target, VOption *options = 0 ) const; - -/** - * Make a worley noise image. - * - * **Optional parameters** - * - **cell_size** -- Size of Worley cells, int. - * - **seed** -- Random number seed, int. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage worley( int width, int height, VOption *options = 0 ); - -/** - * Wrap image origin. - * - * **Optional parameters** - * - **x** -- Left edge of input in output, int. - * - **y** -- Top edge of input in output, int. - * - * @param options Set of options. - * @return Output image. - */ -VImage wrap( VOption *options = 0 ) const; - -/** - * Make an image where pixel values are coordinates. - * - * **Optional parameters** - * - **csize** -- Size of third dimension, int. - * - **dsize** -- Size of fourth dimension, int. - * - **esize** -- Size of fifth dimension, int. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage xyz( int width, int height, VOption *options = 0 ); - -/** - * Make a zone plate. - * - * **Optional parameters** - * - **uchar** -- Output an unsigned char image, bool. - * - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param options Set of options. - * @return Output image. - */ -static VImage zone( int width, int height, VOption *options = 0 ); - -/** - * Zoom an image. - * @param xfac Horizontal zoom factor. - * @param yfac Vertical zoom factor. - * @param options Set of options. - * @return Output image. - */ -VImage zoom( int xfac, int yfac, VOption *options = 0 ) const;