add webp and zstd support to tiffsave

needs some tests still
This commit is contained in:
John Cupitt 2019-07-08 10:39:44 +01:00
parent 063234fde1
commit 52ee3b083f
7 changed files with 134 additions and 27 deletions

View File

@ -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

View File

@ -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 <tiffio.h>]
]
)
INCLUDES="$save_INCLUDES"
fi
# giflib
FIND_GIFLIB(
[with_giflib="yes (found by search)"

View File

@ -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*/

View File

@ -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.
*

View File

@ -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;

View File

@ -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;

View File

@ -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}
};