diff --git a/ChangeLog b/ChangeLog index 9702a107..e69f71ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ - add "unlimited" flag to svgload - disable webp alpha output if all frames fill the canvas and are solid - add "compression" option to heifsave [lovell] +- support webp and zstd compression in tiff 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/configure.ac b/configure.ac index fbf34be9..638c9016 100644 --- a/configure.ac +++ b/configure.ac @@ -1250,6 +1250,20 @@ if test x"$with_tiff" != x"no"; then ) fi +# WEBP in TIFF added in libtiff 4.0.10 +if test x"$with_tiff" != x"no"; then + save_INCLUDES="$INCLUDES" + INCLUDES="$INCLUDES $TIFF_INCLUDES" + AC_CHECK_DECL(COMPRESSION_WEBP,[ + AC_DEFINE(HAVE_TIFF_COMPRESSION_WEBP,1,[define if your libtiff has webp.]) + ],[ + ],[ + [#include ] + ] + ) + INCLUDES="$save_INCLUDES" +fi + # giflib FIND_GIFLIB( [with_giflib="yes (found by search)" diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 0f2c304f..40f79e26 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -28,8 +28,8 @@ */ -#ifndef VIPS_PARITHMETIC_H -#define VIPS_PARITHMETIC_H +#ifndef VIPS_PFOREIGN_H +#define VIPS_PFOREIGN_H #ifdef __cplusplus extern "C" { @@ -60,7 +60,8 @@ int vips__tiff_write( VipsImage *in, const char *filename, gboolean rgbjpeg, gboolean properties, gboolean strip, - VipsRegionShrink region_shrink ); + VipsRegionShrink region_shrink, + int level, gboolean lossless ); int vips__tiff_write_buf( VipsImage *in, void **obuf, size_t *olen, @@ -75,7 +76,8 @@ int vips__tiff_write_buf( VipsImage *in, gboolean bigtiff, gboolean rgbjpeg, gboolean properties, gboolean strip, - VipsRegionShrink region_shrink ); + VipsRegionShrink region_shrink, + int level, gboolean lossless ); int vips__tiff_read_header( const char *filename, VipsImage *out, int page, int n, gboolean autorotate ); @@ -281,6 +283,6 @@ void vips__heif_error( struct heif_error *error ); } #endif /*__cplusplus*/ -#endif /*VIPS_PARITHMETIC_H*/ +#endif /*VIPS_PFOREIGN_H*/ diff --git a/libvips/foreign/tiffsave.c b/libvips/foreign/tiffsave.c index d9d76124..b1e45d85 100644 --- a/libvips/foreign/tiffsave.c +++ b/libvips/foreign/tiffsave.c @@ -14,6 +14,9 @@ * - predictor defaults to horizontal, reducing file size, usually * 13/6/18 * - add region_shrink + * 8/7/19 + * - add webp and zstd support + * - add @level and @lossless */ /* @@ -88,6 +91,8 @@ typedef struct _VipsForeignSaveTiff { gboolean rgbjpeg; gboolean properties; VipsRegionShrink region_shrink; + int level; + gboolean lossless; } VipsForeignSaveTiff; typedef VipsForeignSaveClass VipsForeignSaveTiffClass; @@ -303,6 +308,20 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class ) G_STRUCT_OFFSET( VipsForeignSaveTiff, region_shrink ), VIPS_TYPE_REGION_SHRINK, VIPS_REGION_SHRINK_MEAN ); + VIPS_ARG_INT( class, "level", 23, + _( "Level" ), + _( "ZSTD compression level" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveTiff, level ), + 1, 22, 10 ); + + VIPS_ARG_BOOL( class, "lossless", 24, + _( "lossless" ), + _( "Enable WEBP lossless mode" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveTiff, lossless ), + FALSE ); + } static void @@ -317,6 +336,8 @@ vips_foreign_save_tiff_init( VipsForeignSaveTiff *tiff ) tiff->xres = 1.0; tiff->yres = 1.0; tiff->region_shrink = VIPS_REGION_SHRINK_MEAN; + tiff->level = 10; + tiff->lossless = FALSE; } typedef struct _VipsForeignSaveTiffFile { @@ -353,7 +374,9 @@ vips_foreign_save_tiff_file_build( VipsObject *object ) tiff->rgbjpeg, tiff->properties, save->strip, - tiff->region_shrink ) ) + tiff->region_shrink, + tiff->level, + tiff->lossless ) ) return( -1 ); return( 0 ); @@ -422,7 +445,9 @@ vips_foreign_save_tiff_buffer_build( VipsObject *object ) tiff->rgbjpeg, tiff->properties, save->strip, - tiff->region_shrink ) ) + tiff->region_shrink, + tiff->level, + tiff->lossless ) ) return( -1 ); /* vips__tiff_write_buf() makes a buffer that needs g_free(), not @@ -488,7 +513,9 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer ) * * @properties: set %TRUE to write an IMAGEDESCRIPTION tag * * @strip: set %TRUE to block metadata save * * @page_height: %gint for page height for multi-page save - * * @shrink_region: #VipsRegionShrink How to shrink each 2x2 region. + * * @region_shrink: #VipsRegionShrink How to shrink each 2x2 region. + * * @level: %gint, Zstd compression level + * * @lossless: set %TRUE for WebP losssless mode * * Write a VIPS image to a file as TIFF. * @@ -497,13 +524,17 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer ) * written as series of pages, each #VIPS_META_PAGE_HEIGHT pixels high. * * Use @compression to set the tiff compression. Currently jpeg, packbits, - * fax4, lzw, none and deflate are supported. The default is no compression. + * fax4, lzw, none, deflate, webp and zstd are supported. The default is no + * compression. * JPEG compression is a good lossy compressor for photographs, packbits is * good for 1-bit images, and deflate is the best lossless compression TIFF * can do. * * Use @Q to set the JPEG compression factor. Default 75. * + * User @level to set the ZSTD compression level. Use @lossless to + * set WEBP lossless mode on. Use @Q to set the WEBP compression level. + * * Use @predictor to set the predictor for lzw and deflate compression. It * defaults to #VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL, meaning horizontal * differencing. Please refer to the libtiff @@ -522,7 +553,7 @@ vips_foreign_save_tiff_buffer_init( VipsForeignSaveTiffBuffer *buffer ) * is 128 by 128. * * Set @pyramid to write the image as a set of images, one per page, of - * decreasing size. Use @shrink_region to set how images will be shrunk: by + * decreasing size. Use @region_shrink to set how images will be shrunk: by * default each 2x2 block is just averaged, but you can set MODE or MEDIAN as * well. * @@ -601,7 +632,9 @@ vips_tiffsave( VipsImage *in, const char *filename, ... ) * * @properties: set %TRUE to write an IMAGEDESCRIPTION tag * * @strip: set %TRUE to block metadata save * * @page_height: %gint for page height for multi-page save - * * @shrink_region: #VipsRegionShrink How to shrink each 2x2 region. + * * @region_shrink: #VipsRegionShrink How to shrink each 2x2 region. + * * @level: %gint, Zstd compression level + * * @lossless: set %TRUE for WebP losssless mode * * As vips_tiffsave(), but save to a memory buffer. * diff --git a/libvips/foreign/vips2tiff.c b/libvips/foreign/vips2tiff.c index 5f51164d..782f0197 100644 --- a/libvips/foreign/vips2tiff.c +++ b/libvips/foreign/vips2tiff.c @@ -182,6 +182,9 @@ * - copy EXTRASAMPLES to pyramid layers * 21/12/18 * - stop pyr layers if width or height drop to 1 + * 8/7/19 + * - add webp and zstd support + * - add @level and @lossless */ /* @@ -300,7 +303,7 @@ struct _Wtiff { int tls; /* Tile line size */ int compression; /* Compression type */ - int jpqual; /* JPEG q-factor */ + int Q; /* JPEG q-factor, webp level */ int predictor; /* Predictor value */ int tile; /* Tile or not */ int tilew, tileh; /* Tile size */ @@ -316,6 +319,8 @@ struct _Wtiff { int properties; /* Set to save XML props */ int strip; /* Don't write metadata */ VipsRegionShrink region_shrink; /* How to shrink regions */ + int level; /* zstd compression level */ + gboolean lossless; /* webp lossless mode */ /* True if we've detected a toilet-roll image, plus the page height, * which has been checked to be a factor of im->Ysize. @@ -583,7 +588,16 @@ wtiff_write_header( Wtiff *wtiff, Layer *layer ) TIFFSetField( tif, TIFFTAG_COMPRESSION, wtiff->compression ); if( wtiff->compression == COMPRESSION_JPEG ) - TIFFSetField( tif, TIFFTAG_JPEGQUALITY, wtiff->jpqual ); + TIFFSetField( tif, TIFFTAG_JPEGQUALITY, wtiff->Q ); + +#ifdef HAVE_TIFF_COMPRESSION_WEBP + if( wtiff->compression == COMPRESSION_WEBP ) { + TIFFSetField( tif, TIFFTAG_WEBP_LEVEL, wtiff->Q ); + TIFFSetField( tif, TIFFTAG_WEBP_LOSSLESS, wtiff->lossless ); + } + if( wtiff->compression == COMPRESSION_ZSTD ) + TIFFSetField( tif, TIFFTAG_ZSTD_LEVEL, wtiff->level ); +#endif /*HAVE_TIFF_COMPRESSION_WEBP*/ if( (wtiff->compression == VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE || wtiff->compression == VIPS_FOREIGN_TIFF_COMPRESSION_LZW) && @@ -666,7 +680,7 @@ wtiff_write_header( Wtiff *wtiff, Layer *layer ) else if( wtiff->compression == COMPRESSION_JPEG && wtiff->im->Bands == 3 && wtiff->im->BandFmt == VIPS_FORMAT_UCHAR && - (!wtiff->rgbjpeg && wtiff->jpqual < 90) ) { + (!wtiff->rgbjpeg && wtiff->Q < 90) ) { /* This signals to libjpeg that it can do * YCbCr chrominance subsampling from RGB, not * that we will supply the image as YCbCr. @@ -876,14 +890,16 @@ get_compression( VipsForeignTiffCompression compression ) return( COMPRESSION_CCITTFAX4 ); case VIPS_FOREIGN_TIFF_COMPRESSION_LZW: return( COMPRESSION_LZW ); +#ifdef HAVE_TIFF_COMPRESSION_WEBP + case VIPS_FOREIGN_TIFF_COMPRESSION_WEBP: + return( COMPRESSION_WEBP ); + case VIPS_FOREIGN_TIFF_COMPRESSION_ZSTD: + return( COMPRESSION_ZSTD ); +#endif /*HAVE_TIFF_COMPRESSION_WEBP*/ default: - g_assert_not_reached(); + return( COMPRESSION_NONE ); } - - /* Keep -Wall happy. - */ - return( -1 ); } static int @@ -918,7 +934,8 @@ wtiff_new( VipsImage *im, const char *filename, gboolean rgbjpeg, gboolean properties, gboolean strip, - VipsRegionShrink region_shrink ) + VipsRegionShrink region_shrink, + int level, gboolean lossless ) { Wtiff *wtiff; @@ -930,7 +947,7 @@ wtiff_new( VipsImage *im, const char *filename, wtiff->layer = NULL; wtiff->tbuf = NULL; wtiff->compression = get_compression( compression ); - wtiff->jpqual = Q; + wtiff->Q = Q; wtiff->predictor = predictor; wtiff->tile = tile; wtiff->tilew = tile_width; @@ -947,6 +964,8 @@ wtiff_new( VipsImage *im, const char *filename, wtiff->properties = properties; wtiff->strip = strip; wtiff->region_shrink = region_shrink; + wtiff->level = level; + wtiff->lossless = lossless; wtiff->toilet_roll = FALSE; wtiff->page_height = vips_image_get_page_height( im ); wtiff->image_height = im->Ysize; @@ -1030,6 +1049,21 @@ wtiff_new( VipsImage *im, const char *filename, wtiff->miniswhite = FALSE; } + /* lossless is for webp only. + */ +#ifdef HAVE_TIFF_COMPRESSION_WEBP + if( wtiff->lossless ) { + if( wtiff->compression == COMPRESSION_NONE ) + wtiff->compression = COMPRESSION_WEBP; + + if( wtiff->compression != COMPRESSION_WEBP ) { + g_warning( "%s", + _( "lossless is for WEBP compression only" ) ); + wtiff->lossless = FALSE; + } + } +#endif /*HAVE_TIFF_COMPRESSION_WEBP*/ + /* Sizeof a line of bytes in the TIFF tile. */ if( im->Coding == VIPS_CODING_LABQ ) @@ -1603,7 +1637,7 @@ wtiff_copy_tiff( Wtiff *wtiff, TIFF *out, TIFF *in ) * Set explicitly from Wtiff. */ if( wtiff->compression == COMPRESSION_JPEG ) { - TIFFSetField( out, TIFFTAG_JPEGQUALITY, wtiff->jpqual ); + TIFFSetField( out, TIFFTAG_JPEGQUALITY, wtiff->Q ); /* Only for three-band, 8-bit images. */ @@ -1612,7 +1646,7 @@ wtiff_copy_tiff( Wtiff *wtiff, TIFF *out, TIFF *in ) /* Enable rgb->ycbcr conversion in the jpeg write. */ if( !wtiff->rgbjpeg && - wtiff->jpqual < 90 ) + wtiff->Q < 90 ) TIFFSetField( out, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB ); @@ -1626,6 +1660,17 @@ wtiff_copy_tiff( Wtiff *wtiff, TIFF *out, TIFF *in ) } } +#ifdef HAVE_TIFF_COMPRESSION_WEBP + /* More pseudotags we can't copy. + */ + if( wtiff->compression == COMPRESSION_WEBP ) { + TIFFSetField( out, TIFFTAG_WEBP_LEVEL, wtiff->Q ); + TIFFSetField( out, TIFFTAG_WEBP_LOSSLESS, wtiff->lossless ); + } + if( wtiff->compression == COMPRESSION_ZSTD ) + TIFFSetField( out, TIFFTAG_ZSTD_LEVEL, wtiff->level ); +#endif /*HAVE_TIFF_COMPRESSION_WEBP*/ + /* We can't copy profiles or xmp :( Set again from Wtiff. */ if( !wtiff->strip ) @@ -1785,7 +1830,8 @@ vips__tiff_write( VipsImage *in, const char *filename, gboolean bigtiff, gboolean rgbjpeg, gboolean properties, gboolean strip, - VipsRegionShrink region_shrink ) + VipsRegionShrink region_shrink, + int level, gboolean lossless ) { Wtiff *wtiff; @@ -1802,7 +1848,7 @@ vips__tiff_write( VipsImage *in, const char *filename, compression, Q, predictor, profile, tile, tile_width, tile_height, pyramid, squash, miniswhite, resunit, xres, yres, bigtiff, rgbjpeg, - properties, strip, region_shrink )) ) + properties, strip, region_shrink, level, lossless )) ) return( -1 ); if( wtiff_write_image( wtiff ) ) { @@ -1829,7 +1875,8 @@ vips__tiff_write_buf( VipsImage *in, gboolean bigtiff, gboolean rgbjpeg, gboolean properties, gboolean strip, - VipsRegionShrink region_shrink ) + VipsRegionShrink region_shrink, + int level, gboolean lossless ) { Wtiff *wtiff; @@ -1842,7 +1889,7 @@ vips__tiff_write_buf( VipsImage *in, compression, Q, predictor, profile, tile, tile_width, tile_height, pyramid, squash, miniswhite, resunit, xres, yres, bigtiff, rgbjpeg, - properties, strip, region_shrink )) ) + properties, strip, region_shrink, level, lossless )) ) return( -1 ); wtiff->obuf = obuf; diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index eb8a425d..130adfa4 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -406,12 +406,18 @@ int vips_webpsave_mime( VipsImage *in, ... ) * @VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS: packbits compression * @VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4: fax4 compression * @VIPS_FOREIGN_TIFF_COMPRESSION_LZW: LZW compression + * @VIPS_FOREIGN_TIFF_COMPRESSION_WEBP: WEBP compression + * @VIPS_FOREIGN_TIFF_COMPRESSION_ZSTD: ZSTD compression * * The compression types supported by the tiff writer. * * Use @Q to set the jpeg compression level, default 75. * * Use @prediction to set the lzw or deflate prediction, default none. + * + * Use @lossless to set WEBP lossless compression. + * + * Use @level to set webp and zstd compression level. */ typedef enum { VIPS_FOREIGN_TIFF_COMPRESSION_NONE, @@ -420,6 +426,8 @@ typedef enum { VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS, VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4, VIPS_FOREIGN_TIFF_COMPRESSION_LZW, + VIPS_FOREIGN_TIFF_COMPRESSION_WEBP, + VIPS_FOREIGN_TIFF_COMPRESSION_ZSTD, VIPS_FOREIGN_TIFF_COMPRESSION_LAST } VipsForeignTiffCompression; diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 6628e3c2..a79b0bb8 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -533,6 +533,8 @@ vips_foreign_tiff_compression_get_type( void ) {VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS, "VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS", "packbits"}, {VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4, "VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4", "ccittfax4"}, {VIPS_FOREIGN_TIFF_COMPRESSION_LZW, "VIPS_FOREIGN_TIFF_COMPRESSION_LZW", "lzw"}, + {VIPS_FOREIGN_TIFF_COMPRESSION_WEBP, "VIPS_FOREIGN_TIFF_COMPRESSION_WEBP", "webp"}, + {VIPS_FOREIGN_TIFF_COMPRESSION_ZSTD, "VIPS_FOREIGN_TIFF_COMPRESSION_ZSTD", "zstd"}, {VIPS_FOREIGN_TIFF_COMPRESSION_LAST, "VIPS_FOREIGN_TIFF_COMPRESSION_LAST", "last"}, {0, NULL, NULL} };