diff --git a/ChangeLog b/ChangeLog index b0d2ce2f..e31d008e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,6 +29,13 @@ - better upsizing with vips_resize() - added vips_worley(), generate Worley noise - added vips_perlin(), generate Perlin noise +- gif loader can write 1, 2, 3, or 4 bands depending on file contents +- support --strip for pngsave +- add svgz support [Felix Bünemann] + +30/7/16 started 8.3.3 +- fix performance regression in 8.3.2, thanks Lovell +- yet more robust vips file reading 18/5/16 started 8.3.2 - more robust vips image reading diff --git a/TODO b/TODO index 4ead5e91..c9790b1c 100644 --- a/TODO +++ b/TODO @@ -1,14 +1,14 @@ +- try: + + $ vips avg broken.jpg[fail] + + about 50% of the time it'll trigger a range of out-of-order reads and lock + for 10s or so while seq times out + - add more webp tests to py suite - try moving some more of the CLI tests to py -- the gif tests in the suite sometimes fail with giflib5 because of an - uninitialized struct in giflib, see - - https://sourceforge.net/p/giflib/bugs/94/ - - sadly ubuntu 16.04 only comes with giflib5, and giflib5 is currently broken - - I like the new int mask creator in reducev, can we use it in im_vips2imask() as well? @@ -18,8 +18,6 @@ - could load pdf thumbnails? -- still not happy about float->int mask conversion in im_vips2mask.c - - colour needs to split _build() into preprocess / process / postprocess phases diff --git a/configure.ac b/configure.ac index 0c4fed37..c77b7a9c 100644 --- a/configure.ac +++ b/configure.ac @@ -38,7 +38,7 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date` # binary interface changes not backwards compatible?: reset age to 0 LIBRARY_CURRENT=46 -LIBRARY_REVISION=2 +LIBRARY_REVISION=3 LIBRARY_AGE=4 # patched into include/vips/version.h @@ -627,6 +627,29 @@ if test x"$with_rsvg" != x"no"; then ) fi +# zlib +# some platforms, like macosx, are missing the .pc files for zlib, so +# we fall back to FIND_ZLIB +AC_ARG_WITH([zlib], + AS_HELP_STRING([--without-zlib], [build without zlib (default: test)])) + +if test x"$with_zlib" != "xno"; then + PKG_CHECK_MODULES(ZLIB, zlib >= 0.4, + [AC_DEFINE(HAVE_ZLIB,1,[define if you have zlib installed.]) + with_zlib=yes + PACKAGES_USED="$PACKAGES_USED zlib" + ], + [FIND_ZLIB( + [with_zlib="yes (found by search)" + ], + [AC_MSG_WARN([zlib not found; disabling SVGZ buffer support]) + with_zlib=no + ] + ) + ] + ) +fi + # OpenSlide AC_ARG_WITH([openslide], AS_HELP_STRING([--without-openslide], @@ -787,15 +810,6 @@ fi AM_CONDITIONAL(ENABLE_PYVIPS8, test x"$enable_pyvips8" = x"yes") -# hmm, these don't have .pc files on ubuntu 5.10, how odd -FIND_ZIP( - [with_zip=yes - ], - [AC_MSG_WARN([libz not found; disabling ZIP support]) - with_zip=no - ] -) - # look for TIFF with pkg-config ... fall back to our tester # pkgconfig support for libtiff starts with libtiff-4 AC_ARG_WITH([tiff], @@ -944,14 +958,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 $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS +VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS do echo $i done | sort -ru` VIPS_CFLAGS=`echo $VIPS_CFLAGS` VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS" -VIPS_INCLUDES="$PNG_INCLUDES $TIFF_INCLUDES $ZIP_INCLUDES $JPEG_INCLUDES" -VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $MATIO_LIBS $EXIF_LIBS -lm" +VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES" +VIPS_LIBS="$ZLIB_LIBS $MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $MATIO_LIBS $EXIF_LIBS -lm" AC_SUBST(VIPS_LIBDIR) @@ -1047,6 +1061,7 @@ PDF import with poppler-glib: $with_poppler (requires poppler-glib 0.16.0 or later) SVG import 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 (requires libwebp-0.1.3 or later) diff --git a/cplusplus/VImage.cpp b/cplusplus/VImage.cpp index 92511694..9e798347 100644 --- a/cplusplus/VImage.cpp +++ b/cplusplus/VImage.cpp @@ -461,7 +461,7 @@ VImage::call_option_string( const char *operation_name, { VipsOperation *operation; - VIPS_DEBUG_MSG( "vips_call_by_name: starting for %s ...\n", + VIPS_DEBUG_MSG( "call_option_string: starting for %s ...\n", operation_name ); if( !(operation = vips_operation_new( operation_name )) ) { @@ -489,6 +489,7 @@ VImage::call_option_string( const char *operation_name, */ if( vips_cache_operation_buildp( &operation ) ) { vips_object_unref_outputs( VIPS_OBJECT( operation ) ); + g_object_unref( operation ); delete options; throw( VError() ); } diff --git a/libvips/conversion/tilecache.c b/libvips/conversion/tilecache.c index 39942b22..d7299d0e 100644 --- a/libvips/conversion/tilecache.c +++ b/libvips/conversion/tilecache.c @@ -703,11 +703,8 @@ vips_tile_cache_gen( VipsRegion *or, "error on tile %p\n", tile ); vips_warn( class->nickname, - _( "error reading tile %dx%d: " - "%s" ), - tile->pos.left, tile->pos.top, - vips_error_buffer() ); - vips_error_clear(); + _( "error in tile %d x %d" ), + tile->pos.left, tile->pos.top ); vips_region_black( tile->region ); diff --git a/libvips/create/text.c b/libvips/create/text.c index 90344c6c..464ab59b 100644 --- a/libvips/create/text.c +++ b/libvips/create/text.c @@ -341,11 +341,11 @@ vips_text_init( VipsText *text ) * * Optional arguments: * - * * @font: font to render with - * * @width: render within this many pixels across - * * @alignment: left/centre/right alignment - * * @dpi: render at this resolution - * * @spacing: space lines by this in points + * * @font: %gchararray, font to render with + * * @width: %gint, render within this many pixels across + * * @align: #VipsAlign, left/centre/right alignment + * * @dpi: %gint, render at this resolution + * * @spacing: %gint, space lines by this in points * * Draw the string @text to an image. @out is a one-band 8-bit * unsigned char image, with 0 for no text and 255 for text. Values inbetween @@ -359,7 +359,7 @@ vips_text_init( VipsText *text ) * * @width is the maximum number of pixels across to draw within. If the * generated text is wider than this, it will wrap to a new line. In this - * case, @alignment can be used to set the alignment style for multi-line + * case, @align can be used to set the alignment style for multi-line * text. * * @dpi sets the resolution to render at. "sans 12" at 72 dpi draws characters diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index abe3f89f..c6264266 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -4,6 +4,9 @@ * - 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 */ /* @@ -86,6 +89,14 @@ typedef struct _VipsForeignLoadGif { */ GifPixelType *line; + /* We decompress the whole thing to a huge RGBA memory image, and + * as we render, watch for bands and transparency. At the end of + * loading, we copy 1 or 3 bands, with or without transparency to + * output. + */ + gboolean has_transparency; + gboolean has_colour; + } VipsForeignLoadGif; typedef VipsForeignLoadClass VipsForeignLoadGifClass; @@ -302,39 +313,11 @@ vips_foreign_load_gif_is_a( const char *filename ) return( 0 ); } -static void -vips_foreign_load_gif_parse( VipsForeignLoadGif *gif, - VipsImage *out ) -{ - vips_image_init_fields( out, - gif->file->SWidth, gif->file->SHeight, - 4, VIPS_FORMAT_UCHAR, - VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); - - /* We will have the whole GIF frame in memory, so we can render any - * area. - */ - vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL ); - - /* We need a line buffer to decompress to. - */ - gif->line = VIPS_ARRAY( gif, gif->file->SWidth, GifPixelType ); -} - -static int -vips_foreign_load_gif_header( VipsForeignLoad *load ) -{ - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; - - vips_foreign_load_gif_parse( gif, load->out ); - - return( 0 ); -} - static void vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif, int width, VipsPel * restrict q, VipsPel * restrict p ) { + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); ColorMapObject *map = gif->file->Image.ColorMap ? gif->file->Image.ColorMap : gif->file->SColorMap; @@ -343,8 +326,13 @@ vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif, for( x = 0; x < width; x++ ) { VipsPel v = p[x]; - if( v != gif->transparency && - v < map->ColorCount ) { + if( v >= map->ColorCount ) { + vips_warn( class->nickname, + "%s", _( "pixel value out of range" ) ); + continue; + } + + if( v != gif->transparency ) { q[0] = map->Colors[v].Red; q[1] = map->Colors[v].Green; q[2] = map->Colors[v].Blue; @@ -370,6 +358,8 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); GifFileType *file = gif->file; + ColorMapObject *map = file->Image.ColorMap ? + file->Image.ColorMap : file->SColorMap; /* Check that the frame lies within our image. */ @@ -382,10 +372,27 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out ) return( -1 ); } + /* Check if we have a non-greyscale colourmap for this frame. + */ + if( !gif->has_colour ) { + int i; + + for( i = 0; i < map->ColorCount; i++ ) + if( map->Colors[i].Red != map->Colors[i].Green || + map->Colors[i].Green != map->Colors[i].Blue ) { + VIPS_DEBUG_MSG( "gifload: not mono\n" ); + gif->has_colour = TRUE; + break; + } + } + if( file->Image.Interlace ) { int i; - VIPS_DEBUG_MSG( "gifload: interlaced frame\n" ); + VIPS_DEBUG_MSG( "gifload: interlaced frame of " + "%d x %d pixels at %d x %d\n", + file->Image.Width, file->Image.Height, + file->Image.Left, file->Image.Top ); for( i = 0; i < 4; i++ ) { int y; @@ -410,6 +417,11 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out ) else { int y; + VIPS_DEBUG_MSG( "gifload: non-interlaced frame of " + "%d x %d pixels at %d x %d\n", + file->Image.Width, file->Image.Height, + file->Image.Left, file->Image.Top ); + for( y = 0; y < file->Image.Height; y++ ) { VipsPel *q = VIPS_IMAGE_ADDR( out, file->Image.Left, file->Image.Top + y ); @@ -429,20 +441,31 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif, VipsImage *out ) } static int -vips_foreign_load_gif_load( VipsForeignLoad *load ) +vips_foreign_load_gif_to_memory( VipsForeignLoadGif *gif, VipsImage *out ) { - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); int frame_n; GifRecordType record; - vips_foreign_load_gif_parse( gif, load->real ); + vips_image_init_fields( out, + gif->file->SWidth, gif->file->SHeight, + 4, VIPS_FORMAT_UCHAR, + VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); + + /* We will have the whole GIF frame in memory, so we can render any + * area. + */ + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL ); + + /* We need a line buffer to decompress to. + */ + gif->line = VIPS_ARRAY( gif, gif->file->SWidth, GifPixelType ); /* Turn out into a memory image which we then render the GIF frames * into. */ - if( vips_image_write_prepare( load->real ) ) + if( vips_image_write_prepare( out ) ) return( -1 ); /* Scan the GIF until we have enough to have completely rendered the @@ -467,7 +490,7 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) return( -1 ); } - if( vips_foreign_load_gif_render( gif, load->real ) ) + if( vips_foreign_load_gif_render( gif, out ) ) return( -1 ); frame_n += 1; @@ -495,6 +518,7 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) * transparency. */ gif->transparency = extension[4]; + gif->has_transparency = TRUE; VIPS_DEBUG_MSG( "gifload: " "seen transparency %d\n", @@ -549,6 +573,62 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) return( 0 ); } +static int +vips_foreign_load_gif_load( VipsForeignLoad *load ) +{ + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + VipsImage **t = (VipsImage **) + vips_object_local_array( VIPS_OBJECT( load ), 4 ); + + VipsImage *im; + + /* Render to a memory image. + */ + im = t[0] = vips_image_new_memory(); + if( vips_foreign_load_gif_to_memory( gif, im ) ) + return( -1 ); + + /* Depending on what we found, transform and write to load->real. + */ + if( gif->has_colour && + gif->has_transparency ) { + /* Nothing to do. + */ + } + else if( gif->has_colour ) { + /* RGB. + */ + if( vips_extract_band( im, &t[1], 0, + "n", 3, + NULL ) ) + return( -1 ); + im = t[1]; + } + else if( gif->has_transparency ) { + /* GA. + */ + if( vips_extract_band( im, &t[1], 0, NULL ) || + vips_extract_band( im, &t[2], 3, NULL ) || + vips_bandjoin2( t[1], t[2], &t[3], NULL ) ) + return( -1 ); + im = t[3]; + im->Type = VIPS_INTERPRETATION_B_W; + } + else { + /* G. + */ + if( vips_extract_band( im, &t[1], 0, NULL ) ) + return( -1 ); + im = t[1]; + im->Type = VIPS_INTERPRETATION_B_W; + } + + if( vips_image_write( im, load->out ) ) + return( -1 ); + + return( 0 ); +} + static void vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) { @@ -566,7 +646,6 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) load_class->get_flags_filename = vips_foreign_load_gif_get_flags_filename; load_class->get_flags = vips_foreign_load_gif_get_flags; - load_class->load = vips_foreign_load_gif_load; VIPS_ARG_INT( class, "page", 10, _( "Page" ), @@ -580,6 +659,7 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) static void vips_foreign_load_gif_init( VipsForeignLoadGif *gif ) { + gif->transparency = -1; } typedef struct _VipsForeignLoadGifFile { @@ -607,7 +687,7 @@ vips_foreign_load_gif_file_header( VipsForeignLoad *load ) VIPS_SETSTR( load->out->filename, file->filename ); - return( vips_foreign_load_gif_header( load ) ); + return( vips_foreign_load_gif_load( load ) ); } static const char *vips_foreign_gif_suffs[] = { @@ -702,7 +782,7 @@ vips_foreign_load_gif_buffer_header( VipsForeignLoad *load ) vips_foreign_load_gif_buffer_read ) ) return( -1 ); - return( vips_foreign_load_gif_header( load ) ); + return( vips_foreign_load_gif_load( load ) ); } static void @@ -752,8 +832,8 @@ vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer ) * * Use @page to set page number (frame number) to read. * - * The whole GIF is parsed and read into memory on header access, the whole - * GIF is rendered on first pixel access. + * The whole GIF is rendered into memory on header access. The output image + * will be 1, 2, 3 or 4 bands depending on what the reader finds in the file. * * See also: vips_image_new_from_file(). * diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 1a1e42df..79d120a2 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -977,27 +977,29 @@ read_jpeg_generate( VipsRegion *or, * a vips_sequential(). */ if( r->top != jpeg->y_pos ) { + VIPS_GATE_STOP( "read_jpeg_generate: work" ); vips_error( "VipsJpeg", _( "out of order read at line %d" ), jpeg->y_pos ); + return( -1 ); } /* Here for longjmp() from vips__new_error_exit(). */ - if( setjmp( jpeg->eman.jmp ) ) + if( setjmp( jpeg->eman.jmp ) ) { + VIPS_GATE_STOP( "read_jpeg_generate: work" ); + return( -1 ); + } /* If --fail is set, we make read fail on any warnings. This will stop * on any errors from the previous jpeg_read_scanlines(). */ if( jpeg->eman.pub.num_warnings > 0 && jpeg->fail ) { - vips_error( "VipsJpeg", - _( "read gave %ld warnings" ), - jpeg->eman.pub.num_warnings ); - vips_error( NULL, "%s", vips_error_buffer() ); + VIPS_GATE_STOP( "read_jpeg_generate: work" ); - /* Make the message only appear once. + /* Only fail once. */ jpeg->eman.pub.num_warnings = 0; diff --git a/libvips/foreign/pngsave.c b/libvips/foreign/pngsave.c index 5b9e533b..959c270a 100644 --- a/libvips/foreign/pngsave.c +++ b/libvips/foreign/pngsave.c @@ -164,8 +164,9 @@ vips_foreign_save_png_file_build( VipsObject *object ) build( object ) ) return( -1 ); - if( vips__png_write( save->ready, png_file->filename, - png->compression, png->interlace, png->profile, png->filter ) ) + if( vips__png_write( save->ready, + png_file->filename, png->compression, png->interlace, + png->profile, png->filter, save->strip ) ) return( -1 ); return( 0 ); @@ -223,7 +224,8 @@ vips_foreign_save_png_buffer_build( VipsObject *object ) return( -1 ); if( vips__png_write_buf( save->ready, &obuf, &olen, - png->compression, png->interlace, png->profile, png->filter ) ) + png->compression, png->interlace, png->profile, png->filter, + save->strip ) ) return( -1 ); /* vips__png_write_buf() makes a buffer that needs g_free(), not diff --git a/libvips/foreign/svgload.c b/libvips/foreign/svgload.c index 9bae681a..aa055506 100644 --- a/libvips/foreign/svgload.c +++ b/libvips/foreign/svgload.c @@ -2,6 +2,8 @@ * * 7/2/16 * - from svgload.c + * 1/8/16 felixbuenemann + * - add svgz support */ /* @@ -55,6 +57,23 @@ #include #include +/* Old librsvg versions don't include librsvg-features.h by default. + * Newer versions deprecate direct inclusion. + */ +#ifndef LIBRSVG_FEATURES_H +#include +#endif + +/* A handy #define for we-will-handle-svgz. + */ +#if LIBRSVG_CHECK_FEATURE(SVGZ) && defined(HAVE_ZLIB) +#define HANDLE_SVGZ +#endif + +#ifdef HANDLE_SVGZ +#include +#endif + typedef struct _VipsForeignLoadSvg { VipsForeignLoad parent_object; @@ -315,6 +334,12 @@ vips_foreign_load_svg_file_header( VipsForeignLoad *load ) static const char *vips_foreign_svg_suffs[] = { ".svg", + /* librsvg supports svgz directly, no need to check for zlib here. + */ +#if LIBRSVG_CHECK_FEATURE(SVGZ) + ".svgz", + ".svg.gz", +#endif NULL }; @@ -364,13 +389,78 @@ typedef VipsForeignLoadSvgClass VipsForeignLoadSvgBufferClass; G_DEFINE_TYPE( VipsForeignLoadSvgBuffer, vips_foreign_load_svg_buffer, vips_foreign_load_svg_get_type() ); +#ifdef HANDLE_SVGZ +static void * +vips_foreign_load_svg_zalloc( void *opaque, unsigned items, unsigned size ) +{ + return( g_malloc0_n( items, size ) ); +} + +static void +vips_foreign_load_svg_zfree( void *opaque, void *ptr ) +{ + return( g_free( ptr ) ); +} +#endif /*HANDLE_SVGZ*/ + static gboolean vips_foreign_load_svg_is_a_buffer( const void *buf, size_t len ) { - char *str = (char *) buf; + char *str; + +#ifdef HANDLE_SVGZ + /* If the buffer looks like a zip, deflate to here and then search + * that for = 18 && + str[0] == '\037' && + str[1] == '\213' ) { + z_stream zs; + size_t opos; + + zs.zalloc = (alloc_func) vips_foreign_load_svg_zalloc; + zs.zfree = (free_func) vips_foreign_load_svg_zfree; + zs.opaque = Z_NULL; + zs.next_in = (unsigned char *) str; + zs.avail_in = len; + + /* There isn't really an error return from is_a_buffer() + */ + if( inflateInit2( &zs, 15 | 32 ) != Z_OK ) + return( FALSE ); + + opos = 0; + do { + zs.avail_out = sizeof( obuf ) - opos; + zs.next_out = (unsigned char *) obuf + opos; + if( inflate( &zs, Z_NO_FLUSH ) < Z_OK ) + return( FALSE ); + opos = sizeof( obuf ) - zs.avail_out; + } while( opos < sizeof( obuf ) && + zs.avail_in > 0 ); + + inflateEnd( &zs ); + + str = obuf; + len = opos; + } +#endif /*HANDLE_SVGZ*/ + /* SVG documents are very freeform. They normally look like: * * @@ -390,19 +480,13 @@ vips_foreign_load_svg_is_a_buffer( const void *buf, size_t len ) return( 0 ); for( i = 0; i < 24; i++ ) if( !isascii( str[i] ) ) - return( 0 ); + return( FALSE ); - for( i = 0; i < 200 && i < len - 5; i++ ) { - char txt[5]; + for( i = 0; i < 200 && i < len - 5; i++ ) + if( g_ascii_strncasecmp( str + i, "in; @@ -883,7 +887,8 @@ write_vips( Write *write, int compress, int interlace, const char *profile, /* Set ICC Profile. */ - if( profile ) { + if( profile && + !strip ) { if( strcmp( profile, "none" ) != 0 ) { void *data; size_t length; @@ -902,7 +907,8 @@ write_vips( Write *write, int compress, int interlace, const char *profile, PNG_COMPRESSION_TYPE_BASE, data, length ); } } - else if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { + else if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) && + !strip ) { void *data; size_t length; @@ -951,7 +957,7 @@ write_vips( Write *write, int compress, int interlace, const char *profile, int vips__png_write( VipsImage *in, const char *filename, int compress, int interlace, const char *profile, - VipsForeignPngFilter filter ) + VipsForeignPngFilter filter, gboolean strip ) { Write *write; @@ -970,7 +976,8 @@ vips__png_write( VipsImage *in, const char *filename, /* Convert it! */ - if( write_vips( write, compress, interlace, profile, filter ) ) { + if( write_vips( write, + compress, interlace, profile, filter, strip ) ) { vips_error( "vips2png", _( "unable to write \"%s\"" ), filename ); @@ -1026,7 +1033,7 @@ user_write_data( png_structp png_ptr, png_bytep data, png_size_t length ) int vips__png_write_buf( VipsImage *in, void **obuf, size_t *olen, int compression, int interlace, - const char *profile, VipsForeignPngFilter filter ) + const char *profile, VipsForeignPngFilter filter, gboolean strip ) { Write *write; @@ -1037,7 +1044,8 @@ vips__png_write_buf( VipsImage *in, /* Convert it! */ - if( write_vips( write, compression, interlace, profile, filter ) ) { + if( write_vips( write, + compression, interlace, profile, filter, strip ) ) { vips_error( "vips2png", "%s", _( "unable to write to buffer" ) ); diff --git a/libvips/foreign/vipspng.h b/libvips/foreign/vipspng.h index 053aad6b..62fe38ba 100644 --- a/libvips/foreign/vipspng.h +++ b/libvips/foreign/vipspng.h @@ -48,10 +48,10 @@ int vips__png_header_buffer( const void *buffer, size_t length, int vips__png_write( VipsImage *in, const char *filename, int compress, int interlace, const char *profile, - VipsForeignPngFilter filter ); + VipsForeignPngFilter filter, gboolean strip ); int vips__png_write_buf( VipsImage *in, void **obuf, size_t *olen, int compression, int interlace, - const char *profile, VipsForeignPngFilter filter ); + const char *profile, VipsForeignPngFilter filter, gboolean strip ); #ifdef __cplusplus } diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 2530adf7..b60e1739 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -134,6 +134,7 @@ extern "C" { #define VIPS_META_ORIENTATION "orientation" guint64 vips_format_sizeof( VipsBandFormat format ); +guint64 vips_format_sizeof_unsafe( VipsBandFormat format ); int vips_image_get_width( const VipsImage *image ); int vips_image_get_height( const VipsImage *image ); diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index 3fedba71..60e8f59b 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -363,10 +363,11 @@ GType vips_image_get_type(void); /* Has to be guint64 and not size_t/off_t since we have to be able to address * huge images on platforms with 32-bit files. */ + /* Pixel address calculation macros. */ #define VIPS_IMAGE_SIZEOF_ELEMENT( I ) \ - (vips_format_sizeof((I)->BandFmt)) + (vips_format_sizeof_unsafe((I)->BandFmt)) #define VIPS_IMAGE_SIZEOF_PEL( I ) \ (VIPS_IMAGE_SIZEOF_ELEMENT( I ) * (I)->Bands) #define VIPS_IMAGE_SIZEOF_LINE( I ) \ diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index f08e71f5..01cd8091 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -207,6 +207,7 @@ void *vips_hash_table_map( GHashTable *hash, char *vips_strncpy( char *dest, const char *src, int n ); char *vips_strrstr( const char *haystack, const char *needle ); gboolean vips_ispostfix( const char *a, const char *b ); +gboolean vips_iscasepostfix( const char *a, const char *b ); gboolean vips_isprefix( const char *a, const char *b ); char *vips_break_token( char *str, const char *brk ); diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index 2b5a6aa3..2ee3b7d8 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -201,6 +201,23 @@ vips_format_sizeof( VipsBandFormat format ) return( vips__image_sizeof_bandformat[format] ); } +/** + * vips_format_sizeof_unsafe: (skip) + * @format: format type + * + * A fast but dangerous version of vips_format_sizeof(). You must have + * previously range-checked @format or you'll crash. + * + * Returns: number of bytes for a band format. + */ +guint64 +vips_format_sizeof_unsafe( VipsBandFormat format ) +{ + g_assert( 0 <= format && format <= VIPS_FORMAT_DPCOMPLEX ); + + return( vips__image_sizeof_bandformat[format] ); +} + #ifdef DEBUG /* Check that this meta is on the hash table. */ diff --git a/libvips/iofuncs/region.c b/libvips/iofuncs/region.c index ad7035e2..766f2104 100644 --- a/libvips/iofuncs/region.c +++ b/libvips/iofuncs/region.c @@ -231,9 +231,12 @@ vips__region_start( VipsRegion *region ) g_mutex_unlock( image->sslock ); if( !region->seq ) { - vips_error( "vips__region_start", - _( "start function failed for image %s" ), +#ifdef DEBUG + printf( "vips__region_start: " + "start function failed for image %s", image->filename ); +#endif /*DEBUG*/ + return( -1 ); } } @@ -890,7 +893,7 @@ vips_region_fill( VipsRegion *reg, VipsRect *r, VipsRegionFillFn fn, void *a ) * the pixels we need. If it does, we could copy them and only * generate the new ones. * - * However, we usually have neighboring regions on different threads, + * However, we usually have neighbouring regions on different threads, * so from the point of view of this thread, we will get no overlaps * on successive prepare requests. */ diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index 9838da20..48158410 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -319,6 +319,20 @@ vips_ispostfix( const char *a, const char *b ) return( strcmp( a + m - n, b ) == 0 ); } +/* Case-insensitive test for string b ends string a. ASCII strings only. + */ +gboolean +vips_iscasepostfix( const char *a, const char *b ) +{ + int m = strlen( a ); + int n = strlen( b ); + + if( n > m ) + return( FALSE ); + + return( strcasecmp( a + m - n, b ) == 0 ); +} + /* Test for string a starts string b. a is a known-good string, b may be * random data. */ @@ -461,30 +475,22 @@ int vips_filename_suffix_match( const char *path, const char *suffixes[] ) { char *basename; - char *suffix; char *q; - const char **p; int result; + const char **p; - /* Drop any directory components, we want ignore any '.' in there. + /* Drop any directory components. */ basename = g_path_get_basename( path ); - /* Zap any trailing options. + /* Zap any trailing [] options. */ if( (q = (char *) vips__find_rightmost_brackets( basename )) ) *q = '\0'; - /* And select just the '.' and to the right. - */ - if( (q = strrchr( basename, '.' )) ) - suffix = q; - else - suffix = basename; - result = 0; - for( p = suffixes; *p; p++ ) - if( g_ascii_strcasecmp( suffix, *p ) == 0 ) { + for( p = suffixes; *p; p++ ) + if( vips_iscasepostfix( basename, *p ) ) { result = 1; break; } diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 9ed6b814..92dfb144 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -1,4 +1,4 @@ -/* Read and write a vips file +/* Read and write a vips file. * * 22/5/08 * - from im_open.c, im_openin.c, im_desc_hd.c, im_readhist.c, @@ -330,6 +330,18 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from ) im->Xres = im->Xres_float; im->Yres = im->Yres_float; + /* Some protection against malicious files. We also check predicted + * (based on these values) against real file length, see below. + */ + im->Xsize = VIPS_CLIP( 1, im->Xsize, VIPS_MAX_COORD ); + im->Ysize = VIPS_CLIP( 1, im->Ysize, VIPS_MAX_COORD ); + im->Bands = VIPS_CLIP( 1, im->Bands, VIPS_MAX_COORD ); + im->BandFmt = VIPS_CLIP( 0, im->BandFmt, VIPS_FORMAT_LAST - 1 ); + + /* Type, Coding, Offset, Res, etc. don't affect vips file layout, just + * pixel interpretation, don't clip them. + */ + return( 0 ); } diff --git a/libvips/resample/reduceh.cpp b/libvips/resample/reduceh.cpp index d887240f..6ca45ab5 100644 --- a/libvips/resample/reduceh.cpp +++ b/libvips/resample/reduceh.cpp @@ -321,15 +321,30 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq, VIPS_GATE_START( "vips_reduceh_gen: work" ); for( int y = 0; y < r->height; y ++ ) { + VipsPel *p0; VipsPel *q; + double X; q = VIPS_REGION_ADDR( out_region, r->left, r->top + y ); + X = r->left * reduceh->xshrink; + /* We want p0 to be the start (ie. x == 0) of the input + * scanline we are reading from. We can then calculate the p we + * need for each pixel with a single mul and avoid calling ADDR + * for each pixel. + * + * We can't get p0 directly with ADDR since it could be outside + * valid, so get the leftmost pixel in valid and subtract a + * bit. + */ + p0 = VIPS_REGION_ADDR( ir, ir->valid.left, r->top + y ) - + ir->valid.left * ps; + for( int x = 0; x < r->width; x++ ) { int ix = (int) X; - VipsPel *p = VIPS_REGION_ADDR( ir, ix, r->top + y ); + VipsPel *p = p0 + ix * ps; const int sx = X * VIPS_TRANSFORM_SCALE * 2; const int six = sx & (VIPS_TRANSFORM_SCALE * 2 - 1); const int tx = (six + 1) >> 1; diff --git a/m4/zip.m4 b/m4/zip.m4 deleted file mode 100644 index d956a738..00000000 --- a/m4/zip.m4 +++ /dev/null @@ -1,117 +0,0 @@ -dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding -dnl -dnl FIND_ZIP[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] -dnl ------------------------------------------------ -dnl -dnl Find ZIP libraries and headers -dnl -dnl Put includes stuff in ZIP_INCLUDES -dnl Put link stuff in ZIP_LIBS -dnl Define HAVE_ZIP if found -dnl -AC_DEFUN([FIND_ZIP], [ -AC_REQUIRE([AC_PATH_XTRA]) - -ZIP_INCLUDES="" -ZIP_LIBS="" - -AC_ARG_WITH(zip, - AS_HELP_STRING([--without-zip], [build without libx (default: test)])) -# Treat --without-zip like --without-zip-includes --without-zip-libraries. -if test "$with_zip" = "no"; then - ZIP_INCLUDES=no - ZIP_LIBS=no -fi - -AC_ARG_WITH(zip-includes, - AS_HELP_STRING([--with-zip-includes=DIR], [libz includes are in DIR]), - ZIP_INCLUDES="-I$withval") -AC_ARG_WITH(zip-libraries, - AS_HELP_STRING([--with-zip-libraries=DIR], [libz libraries are in DIR]), - ZIP_LIBS="-L$withval -lz") - -AC_MSG_CHECKING(for ZIP) - -# Look for zlib.h -if test "$ZIP_INCLUDES" = ""; then - # Check the standard search path - AC_TRY_COMPILE([#include ],[int a;],[ - ZIP_INCLUDES="" - ], [ - # zlib.h is not in the standard search path, try - # $prefix - zip_save_INCLUDES="$INCLUDES" - - INCLUDES="-I${prefix}/include $INCLUDES" - - AC_TRY_COMPILE([#include ],[int a;],[ - ZIP_INCLUDES="-I${prefix}/include" - ], [ - ZIP_INCLUDES="no" - ]) - - INCLUDES=$zip_save_INCLUDES - ]) -fi - -# Now for the libraries -if test "$ZIP_LIBS" = ""; then - zip_save_LIBS="$LIBS" - zip_save_INCLUDES="$INCLUDES" - - LIBS="-lz $LIBS" - INCLUDES="$ZIP_INCLUDES $INCLUDES" - - # Try the standard search path first - AC_TRY_LINK([#include ],[zlibVersion()], [ - ZIP_LIBS="-lz" - ], [ - # libz is not in the standard search path, try $prefix - - LIBS="-L${prefix}/lib $LIBS" - - AC_TRY_LINK([#include ],[zlibVersion()], [ - ZIP_LIBS="-L${prefix}/lib -lz" - ], [ - ZIP_LIBS=no - ]) - ]) - - LIBS="$zip_save_LIBS" - INCLUDES="$zip_save_INCLUDES" -fi - -AC_SUBST(ZIP_LIBS) -AC_SUBST(ZIP_INCLUDES) - -# Print a helpful message -zip_libraries_result="$ZIP_LIBS" -zip_includes_result="$ZIP_INCLUDES" - -if test x"$zip_libraries_result" = x""; then - zip_libraries_result="in default path" -fi -if test x"$zip_includes_result" = x""; then - zip_includes_result="in default path" -fi - -if test "$zip_libraries_result" = "no"; then - zip_libraries_result="(none)" -fi -if test "$zip_includes_result" = "no"; then - zip_includes_result="(none)" -fi - -AC_MSG_RESULT([libraries $zip_libraries_result, headers $zip_includes_result]) - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test "$ZIP_INCLUDES" != "no" && test "$ZIP_LIBS" != "no"; then - AC_DEFINE(HAVE_ZIP,1,[Define if you have libz libraries and header files.]) - $1 -else - ZIP_LIBS="" - ZIP_INCLUDES="" - $2 -fi - -])dnl diff --git a/m4/zlib.m4 b/m4/zlib.m4 new file mode 100644 index 00000000..eaeb1c39 --- /dev/null +++ b/m4/zlib.m4 @@ -0,0 +1,124 @@ +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_ZLIB[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find zlib libraries and headers ... useful for platforms which are missing +dnl the zlib .pc file +dnl +dnl Put compile stuff in ZLIB_INCLUDES +dnl Put link stuff in ZLIB_LIBS +dnl Define HAVE_ZLIB if found +dnl +AC_DEFUN([FIND_ZLIB], [ +AC_REQUIRE([AC_PATH_XTRA]) + +ZLIB_INCLUDES="" +ZLIB_LIBS="" + +AC_ARG_WITH(zlib, + AS_HELP_STRING([--without-zlib], [build without zlib (default: test)])) +# Treat --without-zlib like --without-zlib-includes --without-zlib-libraries. +if test "$with_zlib" = "no"; then + ZLIB_INCLUDES=no + ZLIB_LIBS=no +fi + +AC_ARG_WITH(zlib-includes, + AS_HELP_STRING([--with-zlib-includes=DIR], [libz includes are in DIR]), + ZLIB_INCLUDES="-I$withval") +AC_ARG_WITH(zlib-libraries, + AS_HELP_STRING([--with-zlib-libraries=DIR], [libz libraries are in DIR]), + ZLIB_LIBS="-L$withval -lz") + +AC_MSG_CHECKING(for ZLIB) + +# Look for zlib.h +if test "$ZLIB_INCLUDES" = ""; then + # Check the standard search path + AC_TRY_COMPILE([#include + #include ],[int a;],[ + ZLIB_INCLUDES="" + ], [ + # zlib.h is not in the standard search path, try + # $prefix + zlib_save_INCLUDES="$INCLUDES" + + INCLUDES="-I${prefix}/include $INCLUDES" + + AC_TRY_COMPILE([#include + #include ],[int a;],[ + ZLIB_INCLUDES="-I${prefix}/include" + ], [ + ZLIB_INCLUDES="no" + ]) + + INCLUDES=$zlib_save_INCLUDES + ]) +fi + +# Now for the libraries +if test "$ZLIB_LIBS" = ""; then + zlib_save_LIBS="$LIBS" + zlib_save_INCLUDES="$INCLUDES" + + LIBS="-lz $LIBS" + INCLUDES="$ZLIB_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([#include + #include + ],[z_stream zs;inflateInit2(&zs, 15 | 32)], [ + ZLIB_LIBS="-lz" + ], [ + # libz is not in the standard search path, try $prefix + + LIBS="-L${prefix}/lib $LIBS" + + AC_TRY_LINK([#include + #include + ],[z_stream zs;inflateInit2(&zs, 15 | 32)], [ + ZLIB_LIBS="-L${prefix}/lib -lz" + ], [ + ZLIB_LIBS=no + ]) + ]) + + LIBS="$zlib_save_LIBS" + INCLUDES="$zlib_save_INCLUDES" +fi + +AC_SUBST(ZLIB_LIBS) +AC_SUBST(ZLIB_INCLUDES) + +# Print a helpful message +zlib_libraries_result="$ZLIB_LIBS" +zlib_includes_result="$ZLIB_INCLUDES" + +if test x"$zlib_libraries_result" = x""; then + zlib_libraries_result="in default path" +fi +if test x"$zlib_includes_result" = x""; then + zlib_includes_result="in default path" +fi + +if test "$zlib_libraries_result" = "no"; then + zlib_libraries_result="(none)" +fi +if test "$zlib_includes_result" = "no"; then + zlib_includes_result="(none)" +fi + +AC_MSG_RESULT([libraries $zlib_libraries_result, headers $zlib_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$ZLIB_INCLUDES" != "no" && test "$ZLIB_LIBS" != "no"; then + AC_DEFINE(HAVE_ZLIB,1,[Define if you have zlib libraries and header files.]) + $1 +else + ZLIB_INCLUDES="" + ZLIB_LIBS="" + $2 +fi + +])dnl diff --git a/python/packages/gi/overrides/Vips.py b/python/packages/gi/overrides/Vips.py index b015779e..2da074d0 100644 --- a/python/packages/gi/overrides/Vips.py +++ b/python/packages/gi/overrides/Vips.py @@ -176,7 +176,7 @@ class Error(Exception): """ def __init__(self, message, detail = None): self.message = message - if detail == None: + if detail == None or detail == "": detail = Vips.error_buffer() Vips.error_clear() self.detail = detail diff --git a/test/images/vips-profile.svg.gz b/test/images/vips-profile.svg.gz new file mode 100644 index 00000000..8ccb610f Binary files /dev/null and b/test/images/vips-profile.svg.gz differ diff --git a/test/images/vips-profile.svgz b/test/images/vips-profile.svgz new file mode 100644 index 00000000..8ccb610f Binary files /dev/null and b/test/images/vips-profile.svgz differ diff --git a/test/test_foreign.py b/test/test_foreign.py index a4e904df..edbf9802 100755 --- a/test/test_foreign.py +++ b/test/test_foreign.py @@ -50,6 +50,8 @@ class TestForeign(unittest.TestCase): self.pdf_file = "images/ISO_12233-reschart.pdf" self.cmyk_pdf_file = "images/cmyktest.pdf" self.svg_file = "images/vips-profile.svg" + self.svgz_file = "images/vips-profile.svgz" + self.svg_gz_file = "images/vips-profile.svg.gz" self.colour = Vips.Image.jpegload(self.jpeg_file) self.mono = self.colour.extract_band(1) @@ -58,9 +60,7 @@ class TestForeign(unittest.TestCase): self.cmyk = self.cmyk.copy(interpretation = Vips.Interpretation.CMYK) im = Vips.Image.new_from_file(self.gif_file) - # some libMagick will load this mono image as RGB, some as mono ... test - # band 0 to be safe - self.onebit = im[0] > 128 + self.onebit = im > 128 # we have test files for formats which have a clear standard def file_loader(self, loader, test_file, validate): @@ -475,10 +475,10 @@ class TestForeign(unittest.TestCase): def gif_valid(self, im): a = im(10, 10) - self.assertAlmostEqualObjects(a, [33, 33, 33, 255]) + self.assertAlmostEqualObjects(a, [33]) self.assertEqual(im.width, 159) self.assertEqual(im.height, 203) - self.assertEqual(im.bands, 4) + self.assertEqual(im.bands, 1) self.file_loader("gifload", self.gif_file, gif_valid) self.buffer_loader("gifload_buffer", self.gif_file, gif_valid) @@ -499,6 +499,11 @@ class TestForeign(unittest.TestCase): self.file_loader("svgload", self.svg_file, svg_valid) self.buffer_loader("svgload_buffer", self.svg_file, svg_valid) + self.file_loader("svgload", self.svgz_file, svg_valid) + self.buffer_loader("svgload_buffer", self.svgz_file, svg_valid) + + self.file_loader("svgload", self.svg_gz_file, svg_valid) + im = Vips.Image.new_from_file(self.svg_file) x = Vips.Image.new_from_file(self.svg_file, scale = 2) self.assertLess(abs(im.width * 2 - x.width), 2) diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index 23b53dea..b6199d87 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -81,6 +81,8 @@ * - restore BandFmt after unpremultiply * 23/5/16 * - no need to guess max-alpha now premultiply does this for us + * 1/8/16 + * - use scRGB as the working space in linear mode */ #ifdef HAVE_CONFIG_H @@ -357,7 +359,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in ) { VipsImage **t = (VipsImage **) vips_object_local_array( process, 10 ); VipsInterpretation interpretation = linear_processing ? - VIPS_INTERPRETATION_XYZ : VIPS_INTERPRETATION_sRGB; + VIPS_INTERPRETATION_scRGB : VIPS_INTERPRETATION_sRGB; /* TRUE if we've done the import of an ICC transform and still need to * export.