From 3a2de853a932bd0ea79d1677d319841edb736c38 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 3 Oct 2020 18:56:46 +0200 Subject: [PATCH 1/2] Add support for optional building against libdeflate for faster Zip/Deflate compression/decompression. So we can have 2 kind of builds with the Zip/Deflate codec: - zlib only - zlib + libdeflate Speed improvements in the 35%-50% range can be expected when libdeflate is used. Compression level up to 12 is now supported (capped to 9 when zlib is used). Still requires zlib for situations where libdeflate cannot be used (that is for scanline access, since libdeflate has no streaming mode) Pseudo-tag TIFFTAG_DEFLATE_SUBCODEC=DEFLATE_SUBCODEC_ZLIB/DEFLATE_SUBCODEC_LIBDEFLATE is added to control which subcodec (zlib or libdeflate) should be used (it defaults of course to libdeflate, when it is available). This is mostly aimed at being used on the writing side, to be able to reproduce output of previous libtiff versions at a binary level, in situations where this would be really needed. Or as a safety belt in case there would be unforeseen issues with using libdeflate. It can be used to know when libdeflate is available at runtime (DEFLATE_SUBCODEC_LIBDEFLATE will be the default value in that situation). Of course, deflate codestreams produced by libdeflate can be read by zlib, and vice-versa. --- .gitlab-ci.yml | 5 +- CMakeLists.txt | 34 +++++ configure.ac | 58 ++++++++ libtiff/tif_write.c | 4 + libtiff/tif_zip.c | 271 +++++++++++++++++++++++++++++++++--- libtiff/tiff.h | 3 + libtiff/tiffconf.h.cmake.in | 3 + libtiff/tiffconf.h.in | 3 + man/tiffcp.1 | 11 ++ tools/tiffcp.c | 17 +++ 10 files changed, 391 insertions(+), 18 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c8a89403..7efe145b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ -image: ubuntu:16.04 +image: ubuntu:20.04 + before_script: - - apt-get update -qq && apt-get install -y -qq autoconf automake build-essential cmake libtool libjpeg8-dev libjbig-dev liblzma-dev ninja-build zlib1g-dev zip wget + - apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq autoconf automake build-essential cmake libtool libjpeg8-dev libjbig-dev liblzma-dev ninja-build zlib1g-dev libdeflate-dev zip wget stages: - build diff --git a/CMakeLists.txt b/CMakeLists.txt index a1f964da..6669b706 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -457,6 +457,33 @@ if(ZLIB_FOUND) set(ZLIB_SUPPORT 1) endif() set(ZIP_SUPPORT ${ZLIB_SUPPORT}) + + +# libdeflate +option(libdeflate "use libdeflate (optional for faster Deflate support, still requires zlib)" ON) +if (libdeflate) + set(DEFLATE_FOUND 0) + find_path(DEFLATE_INCLUDE_DIR libdeflate.h) + set(DEFLATE_NAMES ${DEFLATE_NAMES} libdeflate) + find_library(DEFLATE_LIBRARY NAMES ${DEFLATE_NAMES}) + if (DEFLATE_INCLUDE_DIR AND DEFLATE_LIBRARY) + set(DEFLATE_FOUND 1) + set(DEFLATE_LIBRARIES ${DEFLATE_LIBRARY}) + endif() +endif() +set(LIBDEFLATE_SUPPORT FALSE) +if(DEFLATE_FOUND) + set(LIBDEFLATE_SUPPORT TRUE) +endif() + +if(LIBDEFLATE_SUPPORT AND NOT ZIP_SUPPORT) + message(WARNING "libdeflate available but zlib is not. libdeflate cannot be used") + set(LIBDEFLATE_SUPPORT FALSE) +endif() + +set(LIBDEFLATE_SUPPORT ${LIBDEFLATE_SUPPORT}) + + # Option for Pixar log-format algorithm # Pixar log format @@ -655,6 +682,9 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libtiff-4.pc if(ZLIB_INCLUDE_DIRS) list(APPEND TIFF_INCLUDES ${ZLIB_INCLUDE_DIRS}) endif() +if(DEFLATE_INCLUDE_DIR) + list(APPEND TIFF_INCLUDES ${DEFLATE_INCLUDE_DIR}) +endif() if(JPEG_INCLUDE_DIR) list(APPEND TIFF_INCLUDES ${JPEG_INCLUDE_DIR}) endif() @@ -682,6 +712,9 @@ endif() if(ZLIB_LIBRARIES) list(APPEND TIFF_LIBRARY_DEPS ${ZLIB_LIBRARIES}) endif() +if(DEFLATE_LIBRARIES) + list(APPEND TIFF_LIBRARY_DEPS ${DEFLATE_LIBRARIES}) +endif() if(JPEG_LIBRARIES) list(APPEND TIFF_LIBRARY_DEPS ${JPEG_LIBRARIES}) endif() @@ -737,6 +770,7 @@ message(STATUS " LogLuv high dynamic range encoding: ${logluv}") message(STATUS "") message(STATUS " Support for external codecs:") message(STATUS " ZLIB support: ${zlib} (requested) ${ZLIB_FOUND} (availability)") +message(STATUS " libdeflate support: ${libdeflate} (requested) ${LIBDEFLATE_SUPPORT} (availability)") message(STATUS " Pixar log-format algorithm: ${pixarlog} (requested) ${PIXARLOG_SUPPORT} (availability)") message(STATUS " JPEG support: ${jpeg} (requested) ${JPEG_FOUND} (availability)") message(STATUS " Old JPEG support: ${old-jpeg} (requested) ${JPEG_FOUND} (availability)") diff --git a/configure.ac b/configure.ac index eecb8e99..cfba5118 100644 --- a/configure.ac +++ b/configure.ac @@ -614,6 +614,63 @@ if test "$HAVE_ZLIB" = "yes" ; then fi +dnl --------------------------------------------------------------------------- +dnl Check for libdeflate. +dnl --------------------------------------------------------------------------- + +HAVE_LIBDEFLATE=no + +AC_ARG_ENABLE(libdeflate, + AS_HELP_STRING([--disable-libdeflate], + [disable libdeflate usage (optional for faster Deflate support (still requires zlib), enabled by default)]),,) +AC_ARG_WITH(libdeflate-include-dir, + AS_HELP_STRING([--with-libdeflate-include-dir=DIR], + [location of libdeflate headers]),,) +AC_ARG_WITH(libdeflate-lib-dir, + AS_HELP_STRING([--with-libdeflate-lib-dir=DIR], + [location of libdeflate library binary]),,) + +if test "x$enable_libdeflate" != "xno" ; then + + if test "x$with_libdeflate_lib_dir" != "x" ; then + LDFLAGS="-L$with_libdeflate_lib_dir $LDFLAGS" + fi + + AC_CHECK_LIB(deflate, libdeflate_zlib_decompress, [libdeflate_lib=yes], [libdeflate_lib=no],) + if test "$libdeflate_lib" = "no" -a "x$with_libdeflate_lib_dir" != "x"; then + AC_MSG_ERROR([libdeflate library not found at $with_libdeflate_lib_dir]) + fi + + if test "x$with_libdeflate_include_dir" != "x" ; then + CPPFLAGS="-I$with_libdeflate_include_dir $CPPFLAGS" + fi + AC_CHECK_HEADER(libdeflate.h, [libdeflate_h=yes], [libdeflate_h=no]) + if test "$libdeflate_h" = "no" -a "x$with_libdeflate_include_dir" != "x" ; then + AC_MSG_ERROR([libdeflate headers not found at $with_libdeflate_include_dir]) + fi + + if test "$libdeflate_lib" = "yes" -a "$libdeflate_h" = "yes" ; then + HAVE_LIBDEFLATE=yes + fi + +fi + +if test "$HAVE_LIBDEFLATE" = "yes" -a "$HAVE_ZLIB" = "no" ; then + AC_MSG_WARN([libdeflate available but zlib is not. libdeflate cannot be used]) + HAVE_LIBDEFLATE=no +fi + +if test "$HAVE_LIBDEFLATE" = "yes" ; then + AC_DEFINE(LIBDEFLATE_SUPPORT,1,[Support libdeflate enhanced compression]) + LIBS="-ldeflate $LIBS" + tiff_libs_private="-ldeflate ${tiff_libs_private}" + + if test "$HAVE_RPATH" = "yes" -a "x$with_libdeflate_lib_dir" != "x" ; then + LIBDIR="-R $with_libdeflate_lib_dir $LIBDIR" + fi + +fi + dnl --------------------------------------------------------------------------- dnl Check for Pixar log-format algorithm. dnl --------------------------------------------------------------------------- @@ -1191,6 +1248,7 @@ LOC_MSG([ LogLuv high dynamic range encoding: ${HAVE_LOGLUV}]) LOC_MSG() LOC_MSG([ Support for external codecs:]) LOC_MSG([ ZLIB support: ${HAVE_ZLIB}]) +LOC_MSG([ libdeflate support: ${HAVE_LIBDEFLATE}]) LOC_MSG([ Pixar log-format algorithm: ${HAVE_PIXARLOG}]) LOC_MSG([ JPEG support: ${HAVE_JPEG}]) LOC_MSG([ Old JPEG support: ${HAVE_OJPEG}]) diff --git a/libtiff/tif_write.c b/libtiff/tif_write.c index f79330e9..3af69ab4 100644 --- a/libtiff/tif_write.c +++ b/libtiff/tif_write.c @@ -668,6 +668,10 @@ TIFFWriteBufferSetup(TIFF* tif, void* bp, tmsize_t size) if (size == (tmsize_t)(-1)) { size = (isTiled(tif) ? tif->tif_tilesize : TIFFStripSize(tif)); + + /* Adds 10% margin for cases where compression would expand a bit */ + if( size < TIFF_TMSIZE_T_MAX - size / 10 ) + size += size / 10; /* * Make raw data buffer at least 8K */ diff --git a/libtiff/tif_zip.c b/libtiff/tif_zip.c index d8fe919b..0e6e3bf3 100644 --- a/libtiff/tif_zip.c +++ b/libtiff/tif_zip.c @@ -29,24 +29,22 @@ * * ZIP (aka Deflate) Compression Support * - * This file is simply an interface to the zlib library written by + * This file is an interface to the zlib library written by * Jean-loup Gailly and Mark Adler. You must use version 1.0 or later - * of the library: this code assumes the 1.0 API and also depends on - * the ability to write the zlib header multiple times (one per strip) - * which was not possible with versions prior to 0.95. Note also that - * older versions of this codec avoided this bug by suppressing the header - * entirely. This means that files written with the old library cannot - * be read; they should be converted to a different compression scheme - * and then reconverted. + * of the library. * - * The data format used by the zlib library is described in the files - * zlib-3.1.doc, deflate-1.1.doc and gzip-4.1.doc, available in the - * directory ftp://ftp.uu.net/pub/archiving/zip/doc. The library was - * last found at ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib-0.99.tar.gz. + * Optionnaly, libdeflate (https://github.com/ebiggers/libdeflate) may be used + * to do the compression and decompression, but only for whole strips and tiles. + * For scanline access, zlib will be sued as a fallback. */ #include "tif_predict.h" #include "zlib.h" +#if LIBDEFLATE_SUPPORT +#include "libdeflate.h" +#endif +#define LIBDEFLATE_MAX_COMPRESSION_LEVEL 12 + #include /* @@ -70,6 +68,12 @@ typedef struct { z_stream stream; int zipquality; /* compression level */ int state; /* state flags */ + int subcodec; /* DEFLATE_SUBCODEC_ZLIB or DEFLATE_SUBCODEC_LIBDEFLATE */ +#if LIBDEFLATE_SUPPORT + int libdeflate_state; /* -1 = until first time ZIPEncode() / ZIPDecode() is called, 0 = use zlib, 1 = use libdeflate */ + struct libdeflate_decompressor* libdeflate_dec; + struct libdeflate_compressor* libdeflate_enc; +#endif #define ZSTATE_INIT_DECODE 0x01 #define ZSTATE_INIT_ENCODE 0x02 @@ -132,6 +136,9 @@ ZIPPreDecode(TIFF* tif, uint16 s) if( (sp->state & ZSTATE_INIT_DECODE) == 0 ) tif->tif_setupdecode( tif ); +#if LIBDEFLATE_SUPPORT + sp->libdeflate_state = -1; +#endif sp->stream.next_in = tif->tif_rawdata; assert(sizeof(sp->stream.avail_in)==4); /* if this assert gets raised, we need to simplify this code to reflect a ZLib that is likely updated @@ -151,6 +158,77 @@ ZIPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) assert(sp != NULL); assert(sp->state == ZSTATE_INIT_DECODE); +#if LIBDEFLATE_SUPPORT + if( sp->libdeflate_state == 1 ) + return 0; + + /* If we have libdeflate support and we are asked to read a whole */ + /* strip/tile, then go for using it */ + do { + TIFFDirectory *td = &tif->tif_dir; + + if( sp->libdeflate_state == 0 ) + break; + if( sp->subcodec == DEFLATE_SUBCODEC_ZLIB ) + break; + + /* Check if we are in the situation where we can use libdeflate */ + if (isTiled(tif)) { + if( TIFFTileSize64(tif) != (uint64)occ ) + break; + } else { + uint32 strip_height = td->td_imagelength - tif->tif_row; + if (strip_height > td->td_rowsperstrip) + strip_height = td->td_rowsperstrip; + if( TIFFVStripSize64(tif, strip_height) != (uint64)occ ) + break; + } + + /* Check for overflow */ + if( (size_t)tif->tif_rawcc != (uint64)tif->tif_rawcc ) + break; + if( (size_t)occ != (uint64)occ ) + break; + + /* Go for decompression using libdeflate */ + { + enum libdeflate_result res; + if( sp->libdeflate_dec == NULL ) + { + sp->libdeflate_dec = libdeflate_alloc_decompressor(); + if( sp->libdeflate_dec == NULL ) + { + break; + } + } + + sp->libdeflate_state = 1; + + res = libdeflate_zlib_decompress( + sp->libdeflate_dec, tif->tif_rawcp, (size_t)tif->tif_rawcc, op, (size_t)occ, NULL); + + tif->tif_rawcp += tif->tif_rawcc; + tif->tif_rawcc = 0; + + /* We accept LIBDEFLATE_INSUFFICIENT_SPACE has a return */ + /* There are odd files in the wild where the last strip, when */ + /* it is smaller in height than td_rowsperstrip, actually contains */ + /* data for td_rowsperstrip lines. Just ignore that silently. */ + if( res != LIBDEFLATE_SUCCESS && + res != LIBDEFLATE_INSUFFICIENT_SPACE ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Decoding error at scanline %lu", + (unsigned long) tif->tif_row); + return 0; + } + + return 1; + } + } while(0); + sp->libdeflate_state = 0; +#endif /* LIBDEFLATE_SUPPORT */ + sp->stream.next_in = tif->tif_rawcp; sp->stream.next_out = op; @@ -198,6 +276,7 @@ ZIPSetupEncode(TIFF* tif) { static const char module[] = "ZIPSetupEncode"; ZIPState* sp = EncoderState(tif); + int cappedQuality; assert(sp != NULL); if (sp->state & ZSTATE_INIT_DECODE) { @@ -205,7 +284,11 @@ ZIPSetupEncode(TIFF* tif) sp->state = 0; } - if (deflateInit(&sp->stream, sp->zipquality) != Z_OK) { + cappedQuality = sp->zipquality; + if( cappedQuality > Z_BEST_COMPRESSION ) + cappedQuality = Z_BEST_COMPRESSION; + + if (deflateInit(&sp->stream, cappedQuality) != Z_OK) { TIFFErrorExt(tif->tif_clientdata, module, "%s", SAFE_MSG(sp)); return (0); } else { @@ -227,6 +310,9 @@ ZIPPreEncode(TIFF* tif, uint16 s) if( sp->state != ZSTATE_INIT_ENCODE ) tif->tif_setupencode( tif ); +#if LIBDEFLATE_SUPPORT + sp->libdeflate_state = -1; +#endif sp->stream.next_out = tif->tif_rawdata; assert(sizeof(sp->stream.avail_out)==4); /* if this assert gets raised, we need to simplify this code to reflect a ZLib that is likely updated @@ -249,6 +335,95 @@ ZIPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) assert(sp->state == ZSTATE_INIT_ENCODE); (void) s; + +#if LIBDEFLATE_SUPPORT + if( sp->libdeflate_state == 1 ) + return 0; + + /* If we have libdeflate support and we are asked to write a whole */ + /* strip/tile, then go for using it */ + do { + TIFFDirectory *td = &tif->tif_dir; + + if( sp->libdeflate_state == 0 ) + break; + if( sp->subcodec == DEFLATE_SUBCODEC_ZLIB ) + break; + + /* Libdeflate does not support the 0-compression level */ + if( sp->zipquality == Z_NO_COMPRESSION ) + break; + + /* Check if we are in the situation where we can use libdeflate */ + if (isTiled(tif)) { + if( TIFFTileSize64(tif) != (uint64)cc ) + break; + } else { + uint32 strip_height = td->td_imagelength - tif->tif_row; + if (strip_height > td->td_rowsperstrip) + strip_height = td->td_rowsperstrip; + if( TIFFVStripSize64(tif, strip_height) != (uint64)cc ) + break; + } + + /* Check for overflow */ + if( (size_t)tif->tif_rawdatasize != (uint64)tif->tif_rawdatasize ) + break; + if( (size_t)cc != (uint64)cc ) + break; + + /* Go for compression using libdeflate */ + { + size_t nCompressedBytes; + if( sp->libdeflate_enc == NULL ) + { + /* To get results as good as zlib, we asked for an extra */ + /* level of compression */ + sp->libdeflate_enc = libdeflate_alloc_compressor( + sp->zipquality == Z_DEFAULT_COMPRESSION ? 7 : + sp->zipquality >= 6 && sp->zipquality <= 9 ? sp->zipquality + 1 : + sp->zipquality); + if( sp->libdeflate_enc == NULL ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot allocate compressor"); + break; + } + } + + /* Make sure the output buffer is large enough for the worse case. */ + /* In TIFFWriteBufferSetup(), when libtiff allocates the buffer */ + /* we've taken a 10% margin over the uncompressed size, which should */ + /* be large enough even for the the worse case scenario. */ + if( libdeflate_zlib_compress_bound(sp->libdeflate_enc, (size_t)cc) > + (size_t)tif->tif_rawdatasize) + { + break; + } + + sp->libdeflate_state = 1; + nCompressedBytes = libdeflate_zlib_compress( + sp->libdeflate_enc, bp, (size_t)cc, tif->tif_rawdata, (size_t)tif->tif_rawdatasize); + + if( nCompressedBytes == 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Encoder error at scanline %lu", + (unsigned long) tif->tif_row); + return 0; + } + + tif->tif_rawcc = nCompressedBytes; + + if( !TIFFFlushData1(tif) ) + return 0; + + return 1; + } + } while(0); + sp->libdeflate_state = 0; +#endif /* LIBDEFLATE_SUPPORT */ + sp->stream.next_in = bp; assert(sizeof(sp->stream.avail_in)==4); /* if this assert gets raised, we need to simplify this code to reflect a ZLib that is likely updated @@ -286,6 +461,11 @@ ZIPPostEncode(TIFF* tif) ZIPState *sp = EncoderState(tif); int state; +#if LIBDEFLATE_SUPPORT + if( sp->libdeflate_state == 1 ) + return 1; +#endif + sp->stream.avail_in = 0; do { state = deflate(&sp->stream, Z_FINISH); @@ -329,6 +509,14 @@ ZIPCleanup(TIFF* tif) inflateEnd(&sp->stream); sp->state = 0; } + +#if LIBDEFLATE_SUPPORT + if( sp->libdeflate_dec ) + libdeflate_free_decompressor(sp->libdeflate_dec); + if( sp->libdeflate_enc ) + libdeflate_free_compressor(sp->libdeflate_enc); +#endif + _TIFFfree(sp); tif->tif_data = NULL; @@ -344,15 +532,55 @@ ZIPVSetField(TIFF* tif, uint32 tag, va_list ap) switch (tag) { case TIFFTAG_ZIPQUALITY: sp->zipquality = (int) va_arg(ap, int); - if ( sp->state&ZSTATE_INIT_ENCODE ) { + if( sp->zipquality < Z_DEFAULT_COMPRESSION || + sp->zipquality > LIBDEFLATE_MAX_COMPRESSION_LEVEL ) { + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid ZipQuality value. Should be in [-1,%d] range", + LIBDEFLATE_MAX_COMPRESSION_LEVEL); + return 0; + } + + if ( sp->state&ZSTATE_INIT_ENCODE ) { + int cappedQuality = sp->zipquality; + if( cappedQuality > Z_BEST_COMPRESSION ) + cappedQuality = Z_BEST_COMPRESSION; if (deflateParams(&sp->stream, - sp->zipquality, Z_DEFAULT_STRATEGY) != Z_OK) { + cappedQuality, Z_DEFAULT_STRATEGY) != Z_OK) { TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s", SAFE_MSG(sp)); return (0); } } + +#if LIBDEFLATE_SUPPORT + if( sp->libdeflate_enc ) + { + libdeflate_free_compressor(sp->libdeflate_enc); + sp->libdeflate_enc = NULL; + } +#endif + return (1); + + case TIFFTAG_DEFLATE_SUBCODEC: + sp->subcodec = (int) va_arg(ap, int); + if( sp->subcodec != DEFLATE_SUBCODEC_ZLIB && + sp->subcodec != DEFLATE_SUBCODEC_LIBDEFLATE ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid DeflateCodec value."); + return 0; + } +#if !LIBDEFLATE_SUPPORT + if( sp->subcodec == DEFLATE_SUBCODEC_LIBDEFLATE ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "DeflateCodec = DEFLATE_SUBCODEC_LIBDEFLATE unsupported in this build"); + return 0; + } +#endif + return 1; + default: return (*sp->vsetparent)(tif, tag, ap); } @@ -368,6 +596,11 @@ ZIPVGetField(TIFF* tif, uint32 tag, va_list ap) case TIFFTAG_ZIPQUALITY: *va_arg(ap, int*) = sp->zipquality; break; + + case TIFFTAG_DEFLATE_SUBCODEC: + *va_arg(ap, int*) = sp->subcodec; + break; + default: return (*sp->vgetparent)(tif, tag, ap); } @@ -376,6 +609,7 @@ ZIPVGetField(TIFF* tif, uint32 tag, va_list ap) static const TIFFField zipFields[] = { { TIFFTAG_ZIPQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL }, + { TIFFTAG_DEFLATE_SUBCODEC, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL }, }; int @@ -402,7 +636,7 @@ TIFFInitZIP(TIFF* tif, int scheme) /* * Allocate state block so tag methods have storage to record values. */ - tif->tif_data = (uint8*) _TIFFmalloc(sizeof (ZIPState)); + tif->tif_data = (uint8*) _TIFFcalloc(sizeof (ZIPState), 1); if (tif->tif_data == NULL) goto bad; sp = ZState(tif); @@ -422,6 +656,11 @@ TIFFInitZIP(TIFF* tif, int scheme) /* Default values for codec-specific fields */ sp->zipquality = Z_DEFAULT_COMPRESSION; /* default comp. level */ sp->state = 0; +#if LIBDEFLATE_SUPPORT + sp->subcodec = DEFLATE_SUBCODEC_LIBDEFLATE; +#else + sp->subcodec = DEFLATE_SUBCODEC_ZLIB; +#endif /* * Install codec methods. diff --git a/libtiff/tiff.h b/libtiff/tiff.h index def9855d..eaf392e3 100644 --- a/libtiff/tiff.h +++ b/libtiff/tiff.h @@ -658,6 +658,9 @@ typedef enum { #define TIFFTAG_LERC_MAXZERROR 65567 /* LERC maximum error */ #define TIFFTAG_WEBP_LEVEL 65568 /* WebP compression level: WARNING not registered in Adobe-maintained registry */ #define TIFFTAG_WEBP_LOSSLESS 65569 /* WebP lossless/lossy : WARNING not registered in Adobe-maintained registry */ +#define TIFFTAG_DEFLATE_SUBCODEC 65570 /* ZIP codec: to get/set the sub-codec to use. Will default to libdeflate when available */ +#define DEFLATE_SUBCODEC_ZLIB 0 +#define DEFLATE_SUBCODEC_LIBDEFLATE 1 /* * EXIF tags diff --git a/libtiff/tiffconf.h.cmake.in b/libtiff/tiffconf.h.cmake.in index 59542f1e..9b4b0328 100644 --- a/libtiff/tiffconf.h.cmake.in +++ b/libtiff/tiffconf.h.cmake.in @@ -87,6 +87,9 @@ /* Support Deflate compression */ #cmakedefine ZIP_SUPPORT 1 +/* Support libdeflate enhanced compression */ +#cmakedefine LIBDEFLATE_SUPPORT 1 + /* Support strip chopping (whether or not to convert single-strip uncompressed images to mutiple strips of ~8Kb to reduce memory usage) */ #cmakedefine STRIPCHOP_DEFAULT 1 diff --git a/libtiff/tiffconf.h.in b/libtiff/tiffconf.h.in index 5de30c9b..9bd6a2de 100644 --- a/libtiff/tiffconf.h.in +++ b/libtiff/tiffconf.h.in @@ -84,6 +84,9 @@ /* Support Deflate compression */ #undef ZIP_SUPPORT +/* Support libdeflate enhanced compression */ +#undef LIBDEFLATE_SUPPORT + /* Support strip chopping (whether or not to convert single-strip uncompressed images to mutiple strips of ~8Kb to reduce memory usage) */ #undef STRIPCHOP_DEFAULT diff --git a/man/tiffcp.1 b/man/tiffcp.1 index ec1875d2..6bfee348 100644 --- a/man/tiffcp.1 +++ b/man/tiffcp.1 @@ -151,6 +151,17 @@ e.g. for .SM Deflate encoding with maximum compression level and floating point predictor. +.IP +For the +.SM Deflate +codec, and in a libtiff build with libdeflate enabled, ``p12`` is +actually the maximum level. +.IP +For the +.SM Deflate +codec, and in a libtiff build with libdeflate enabled, ``s0`` can be used to +require zlib to be used, and ``s1`` for libdeflate (defaults to libdeflate when +it is available). .TP .B \-f Specify the bit fill order to use in writing output data. diff --git a/tools/tiffcp.c b/tools/tiffcp.c index 9dbcf67a..e56b1c10 100644 --- a/tools/tiffcp.c +++ b/tools/tiffcp.c @@ -97,6 +97,7 @@ static int jpegcolormode = JPEGCOLORMODE_RGB; static uint16 defcompression = (uint16) -1; static uint16 defpredictor = (uint16) -1; static int defpreset = -1; +static int subcodec = -1; static int tiffcp(TIFF*, TIFF*); static int processCompressOptions(char*); @@ -361,6 +362,8 @@ processZIPOptions(char* cp) defpredictor = atoi(cp); else if (*cp == 'p') defpreset = atoi(++cp); + else if (*cp == 's') + subcodec = atoi(++cp); else usage(EXIT_FAILURE); } while( (cp = strchr(cp, ':')) ); @@ -494,6 +497,9 @@ static const char* stuff[] = { "LZW, Deflate (ZIP), LZMA2, ZSTD and WEBP options:", " # set predictor value", " p# set compression level (preset)", +#if LIBDEFLATE_SUPPORT +" s# set subcodec (0=zlib, 1=libdeflate) (only for Deflate/ZIP)", +#endif "For example, -c lzw:2 to get LZW-encoded data with horizontal differencing,", "-c zip:3:p9 for Deflate encoding with maximum compression level and floating", "point predictor.", @@ -780,6 +786,17 @@ tiffcp(TIFF* in, TIFF* out) TIFFSetField(out, TIFFTAG_PREDICTOR, predictor); else CopyField(TIFFTAG_PREDICTOR, predictor); + if( compression == COMPRESSION_ADOBE_DEFLATE || + compression == COMPRESSION_DEFLATE ) + { + if( subcodec != -1 ) + { + if( TIFFSetField(out, TIFFTAG_DEFLATE_SUBCODEC, subcodec) != 1 ) + { + return FALSE; + } + } + } /*fallthrough*/ case COMPRESSION_WEBP: if (preset != -1) { From b9fa1f6e4aac121cbc3a3e1f59eecda065f9da4b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 12 Oct 2020 19:29:43 +0200 Subject: [PATCH 2/2] test: add testdeflatelaststripextradata.sh --- test/CMakeLists.txt | 3 +- test/Makefile.am | 7 ++- test/images/README.txt | 3 ++ .../images/deflate-last-strip-extra-data.tiff | Bin 0 -> 12789 bytes .../refs/o-deflate-last-strip-extra-data.tiff | Bin 0 -> 12560 bytes test/testdeflatelaststripextradata.sh | 43 ++++++++++++++++++ 6 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 test/images/deflate-last-strip-extra-data.tiff create mode 100644 test/refs/o-deflate-last-strip-extra-data.tiff create mode 100755 test/testdeflatelaststripextradata.sh diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5a1a7420..bffb8bf7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -109,7 +109,8 @@ set(TESTSCRIPTS tiff2rgba-ojpeg_zackthecat_subsamp22_single_strip.sh tiff2rgba-ojpeg_chewey_subsamp21_multi_strip.sh tiff2rgba-ojpeg_single_strip_no_rowsperstrip.sh - testfax4.sh) + testfax4.sh + testdeflatelaststripextradata.sh) # This list should contain all of the TIFF files in the 'images' # subdirectory which are intended to be used as input images for diff --git a/test/Makefile.am b/test/Makefile.am index 42e9f593..4973b614 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -150,6 +150,7 @@ TESTSCRIPTS = \ tiff2rgba-rgb-3c-16b.sh \ tiff2rgba-rgb-3c-8b.sh \ testfax4.sh \ + testdeflatelaststripextradata.sh \ $(JPEG_DEPENDENT_TESTSCRIPTS) # This list should contain the references files @@ -159,7 +160,8 @@ REFFILES = \ refs/o-tiff2ps-PS1.ps \ refs/o-tiff2ps-PS2.ps \ refs/o-tiff2ps-PS3.ps \ - refs/o-testfax4.tiff + refs/o-testfax4.tiff \ + refs/o-deflate-last-strip-extra-data.tiff # This list should contain all of the TIFF files in the 'images' # subdirectory which are intended to be used as input images for @@ -181,7 +183,8 @@ TIFFIMAGES = \ images/ojpeg_zackthecat_subsamp22_single_strip.tiff \ images/ojpeg_chewey_subsamp21_multi_strip.tiff \ images/ojpeg_single_strip_no_rowsperstrip.tiff \ - images/testfax4.tiff + images/testfax4.tiff \ + images/deflate-last-strip-extra-data.tiff PNMIMAGES = \ images/minisblack-1c-8b.pgm \ diff --git a/test/images/README.txt b/test/images/README.txt index b6447d8e..4a7ad829 100644 --- a/test/images/README.txt +++ b/test/images/README.txt @@ -30,3 +30,6 @@ PNM files: G3 Fax files : miniswhite-1c-1b.g3 + +deflate-last-strip-extra-data.tiff is +https://github.com/OSGeo/gdal/blob/master/autotest/alg/data/utmsmall_near.tiff diff --git a/test/images/deflate-last-strip-extra-data.tiff b/test/images/deflate-last-strip-extra-data.tiff new file mode 100644 index 0000000000000000000000000000000000000000..e3ee9dcc07ea506251fa083cfbb11159b672be46 GIT binary patch literal 12789 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%0ANG@qVFOA@SG9==%)ezcrOD0NNTdKXaRsbbO1o- z6##&To_sw603dq}04QW4>xdZu=wKnwgN^(@J6R?N06@bu Q8vFpjgaCPtLIA-30Tt?o+W-In literal 0 HcmV?d00001 diff --git a/test/testdeflatelaststripextradata.sh b/test/testdeflatelaststripextradata.sh new file mode 100755 index 00000000..115eb226 --- /dev/null +++ b/test/testdeflatelaststripextradata.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# +# check decoding of a deflate compressed file whose last strip which should +# contain data for only 4 lines has more in it. +. ${srcdir:-.}/common.sh +infile="${IMAGES}/deflate-last-strip-extra-data.tiff" +outfile="o-deflate-last-strip-extra-data.tiff" +rm -f $outfile +echo "$MEMCHECK ${TIFFCP} -c zip $infile $outfile" +eval "$MEMCHECK ${TIFFCP} -c zip $infile $outfile" +status=$? +if [ $status != 0 ] ; then + echo "Returned failed status $status!" + echo "Output (if any) is in \"${outfile}\"." + exit $status +fi +echo "$MEMCHECK ${TIFFCMP} $outfile ${REFS}/$outfile" +eval "$MEMCHECK ${TIFFCMP} $outfile ${REFS}/$outfile" +status=$? +if [ $status != 0 ] ; then + echo "Returned failed status $status!" + echo "\"${outfile}\" differs from reference file." + exit $status +fi + +outfile="o-deflate-last-strip-extra-data-tiled.tiff" +rm -f $outfile +echo "$MEMCHECK ${TIFFCP} -c zip -t -w 256 -l 256 $infile $outfile" +eval "$MEMCHECK ${TIFFCP} -c zip -t -w 256 -l 256 $infile $outfile" +status=$? +if [ $status != 0 ] ; then + echo "Returned failed status $status!" + echo "Output (if any) is in \"${outfile}\"." + exit $status +fi +echo "$MEMCHECK ${TIFFCMP} $outfile ${REFS}/o-deflate-last-strip-extra-data.tiff" +eval "$MEMCHECK ${TIFFCMP} $outfile ${REFS}/o-deflate-last-strip-extra-data.tiff" +status=$? +if [ $status != 0 ] ; then + echo "Returned failed status $status!" + echo "\"${outfile}\" differs from reference file." + exit $status +fi