webp in tiff

This commit is contained in:
Norman Barker 2018-09-05 17:49:28 -06:00
parent 981e43ecae
commit 9eacd59fec
13 changed files with 792 additions and 4 deletions

View File

@ -92,6 +92,7 @@ include(GNUInstallDirs)
include(CheckCCompilerFlag)
include(CheckCSourceCompiles)
include(CheckIncludeFile)
include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckSymbolExists)
enable_testing()
@ -546,6 +547,21 @@ if(ZSTD_FOUND)
set(ZSTD_SUPPORT 1)
endif()
# libwebp
option(webp "use libwebp (required for WEBP compression)" ON)
if (webp)
find_path(WEBP_INCLUDE_DIR /webp/decode.h)
find_library(WEBP_LIBRARY NAMES webp)
endif()
set(WEBP_SUPPORT 0)
set(WEBP_FOUND FALSE)
if (WEBP_INCLUDE_DIR AND WEBP_LIBRARY)
set(WEBP_SUPPORT 1)
set(WEBP_FOUND TRUE)
set(WEBP_LIBRARIES ${WEBP_LIBRARY})
message(STATUS "Found WEBP library: ${WEBP_LIBRARY}")
endif()
# 8/12-bit jpeg mode
option(jpeg12 "enable libjpeg 8/12-bit dual mode (requires separate
12-bit libjpeg build)" ON)
@ -659,6 +675,9 @@ endif()
if(ZSTD_INCLUDE_DIR)
list(APPEND TIFF_INCLUDES ${ZSTD_INCLUDE_DIR})
endif()
if(WEBP_INCLUDE_DIR)
list(APPEND TIFF_INCLUDES ${WEBP_INCLUDE_DIR})
endif()
# Libraries required by libtiff
set(TIFF_LIBRARY_DEPS)
@ -683,6 +702,9 @@ endif()
if(ZSTD_LIBRARIES)
list(APPEND TIFF_LIBRARY_DEPS ${ZSTD_LIBRARIES})
endif()
if(WEBP_LIBRARIES)
list(APPEND TIFF_LIBRARY_DEPS ${WEBP_LIBRARIES})
endif()
#report_values(TIFF_INCLUDES TIFF_LIBRARY_DEPS)
@ -727,6 +749,7 @@ message(STATUS " JPEG 8/12 bit dual mode: ${jpeg12} (requested) ${JP
message(STATUS " ISO JBIG support: ${jbig} (requested) ${JBIG_FOUND} (availability)")
message(STATUS " LZMA2 support: ${lzma} (requested) ${LIBLZMA_FOUND} (availability)")
message(STATUS " ZSTD support: ${zstd} (requested) ${ZSTD_FOUND} (availability)")
message(STATUS " WEBP support: ${webp} (requested) ${WEBP_FOUND} (availability)")
message(STATUS "")
message(STATUS " C++ support: ${cxx} (requested) ${CXX_SUPPORT} (availability)")
message(STATUS "")

View File

@ -13,7 +13,7 @@ autoconf_build()
mkdir autoconf-build
cd autoconf-build
echo "Running ../configure --prefix=$(pwd)/../autoconf-install) ${opts}"
../configure --prefix=$(pwd)/../autoconf-install --with-zstd-include-dir=/tmp/include --with-zstd-lib-dir=/tmp/lib ${opts}
../configure --prefix=$(pwd)/../autoconf-install --with-zstd-include-dir=/tmp/include --with-zstd-lib-dir=/tmp/lib --with-webp-include-dir=/tmp/include --with-webp-lib-dir=/tmp/lib ${opts}
make
make install
make check
@ -29,8 +29,8 @@ cmake_build()
fi
mkdir cmake-build
cd cmake-build
echo "Running cmake -G "$1" -DCMAKE_BUILD_TYPE="$2" -DCMAKE_INSTALL_PREFIX=../autoconf-install -DZSTD_INCLUDE_DIR=/tmp/include -DZSTD_LIBRARY=/tmp/lib/libzstd.so ${opts} .."
cmake -G "$1" -DCMAKE_BUILD_TYPE="$2" -DCMAKE_INSTALL_PREFIX=../autoconf-install -DZSTD_INCLUDE_DIR=/tmp/include -DZSTD_LIBRARY=/tmp/lib/libzstd.so ${opts} ..
echo "Running cmake -G "$1" -DCMAKE_BUILD_TYPE="$2" -DCMAKE_INSTALL_PREFIX=../autoconf-install -DZSTD_INCLUDE_DIR=/tmp/include -DZSTD_LIBRARY=/tmp/lib/libzstd.so -DWEBP_INCLUDE_DIR=/tmp/include -DZWEBP_LIBRARY=/tmp/lib/libwebp.so ${opts} .."
cmake -G "$1" -DCMAKE_BUILD_TYPE="$2" -DCMAKE_INSTALL_PREFIX=../autoconf-install -DZSTD_INCLUDE_DIR=/tmp/include -DZSTD_LIBRARY=/tmp/lib/libzstd.so -DWEBP_INCLUDE_DIR=/tmp/include -DWEBP_LIBRARY=/tmp/lib/libwebp.so ${opts} ..
cmake --build .
cmake --build . --target install
ctest -V
@ -48,6 +48,16 @@ make -j3 PREFIX=/tmp ZSTD_LEGACY_SUPPORT=0 CFLAGS=-O1
make install PREFIX=/tmp ZSTD_LEGACY_SUPPORT=0 CFLAGS=-O1
cd ../..
rm -rf zstd-1.3.3
# Build webp
wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.0.tar.gz
tar xvzf libwebp-1.0.0.tar.gz
cd libwebp-1.0.0
./configure --prefix=/tmp
make && make install
cd ..
rm -rf libwebp-1.0.0
export LD_LIBRARY_PATH=/tmp/lib
case $build in

View File

@ -867,6 +867,60 @@ fi
AM_CONDITIONAL(HAVE_ZSTD, test "$HAVE_ZSTD" = 'yes')
dnl ---------------------------------------------------------------------------
dnl Check for libwebp.
dnl ---------------------------------------------------------------------------
HAVE_WEBP=no
AC_ARG_ENABLE(webp,
AS_HELP_STRING([--disable-webp],
[disable libwebp usage (required for webp compression, enabled by default)]),,)
AC_ARG_WITH(webp-include-dir,
AS_HELP_STRING([--with-webp-include-dir=DIR],
[location of libwebp headers]),,)
AC_ARG_WITH(webp-lib-dir,
AS_HELP_STRING([--with-webp-lib-dir=DIR],
[location of libwebp library binary]),,)
if test "x$enable_webp" != "xno" ; then
if test "x$with_webp_lib_dir" != "x" ; then
LDFLAGS="-L$with_webp_lib_dir $LDFLAGS"
fi
AC_CHECK_LIB(webp, WebPDecode, [webp_lib=yes], [webp_lib=no],)
if test "$webp_lib" = "no" -a "x$with_webp_lib_dir" != "x"; then
AC_MSG_ERROR([webp library not found at $with_webp_lib_dir])
fi
if test "x$with_webp_include_dir" != "x" ; then
CPPFLAGS="-I$with_webp_include_dir $CPPFLAGS"
fi
AC_CHECK_HEADER(webp/decode.h, [webp_h=yes], [webp_h=no])
if test "$webp_h" = "no" -a "x$with_webp_include_dir" != "x" ; then
AC_MSG_ERROR([Libwebp headers not found at $with_webp_include_dir])
fi
if test "$webp_lib" = "yes" -a "$webp_h" = "yes" ; then
HAVE_WEBP=yes
fi
fi
if test "$HAVE_WEBP" = "yes" ; then
AC_DEFINE(WEBP_SUPPORT,1,[Support webp compression])
LIBS="-lwebp $LIBS"
tiff_libs_private="-lwebp ${tiff_libs_private}"
if test "$HAVE_RPATH" = "yes" -a "x$with_webp_lib_dir" != "x" ; then
LIBDIR="-R $with_webp_lib_dir $LIBDIR"
fi
fi
AM_CONDITIONAL(HAVE_WEBP, test "$HAVE_WEBP" = 'yes')
dnl ---------------------------------------------------------------------------
dnl Should 8/12 bit jpeg mode be enabled?
dnl ---------------------------------------------------------------------------
@ -1145,6 +1199,7 @@ LOC_MSG([ JPEG 8/12 bit dual mode: ${HAVE_JPEG12}])
LOC_MSG([ ISO JBIG support: ${HAVE_JBIG}])
LOC_MSG([ LZMA2 support: ${HAVE_LZMA}])
LOC_MSG([ ZSTD support: ${HAVE_ZSTD}])
LOC_MSG([ WEBP support: ${HAVE_WEBP}])
LOC_MSG()
LOC_MSG([ C++ support: ${HAVE_CXX}])
LOC_MSG()

View File

@ -93,6 +93,7 @@ set(tiff_SOURCES
tif_tile.c
tif_version.c
tif_warning.c
tif_webp.c
tif_write.c
tif_zip.c
tif_zstd.c)

View File

@ -98,6 +98,7 @@ libtiff_la_SOURCES = \
tif_tile.c \
tif_version.c \
tif_warning.c \
tif_webp.c \
tif_write.c \
tif_zip.c \
tif_zstd.c

View File

@ -73,6 +73,9 @@ static int NotConfigured(TIFF*, int);
#ifndef ZSTD_SUPPORT
#define TIFFInitZSTD NotConfigured
#endif
#ifndef WEBP_SUPPORT
#define TIFFInitWebP NotConfigured
#endif
/*
* Compression schemes statically built into the library.
@ -101,6 +104,7 @@ TIFFCodec _TIFFBuiltinCODECS[] = {
{ "SGILog24", COMPRESSION_SGILOG24, TIFFInitSGILog },
{ "LZMA", COMPRESSION_LZMA, TIFFInitLZMA },
{ "ZSTD", COMPRESSION_ZSTD, TIFFInitZSTD },
{ "WEBP", COMPRESSION_WEBP, TIFFInitWebP },
{ NULL, 0, NULL }
};

View File

@ -116,6 +116,9 @@
/* Support ZSTD compression */
#cmakedefine ZSTD_SUPPORT 1
/* Support WEBP compression */
#cmakedefine WEBP_SUPPORT 1
/* Name of package */
#define PACKAGE "@PACKAGE_NAME@"

View File

@ -335,6 +335,9 @@
/* Support zstd compression */
#undef ZSTD_SUPPORT
/* Support webp compression */
#undef WEBP_SUPPORT
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1

View File

@ -1062,6 +1062,10 @@ _TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag)
if (tag == TIFFTAG_LERC_PARAMETERS)
return 1;
break;
case COMPRESSION_WEBP:
if (tag == TIFFTAG_PREDICTOR)
return 1;
break;
}
return 0;
}

666
libtiff/tif_webp.c Normal file
View File

@ -0,0 +1,666 @@
/*
* Copyright (c) 2018, Mapbox
* Author: <norman.barker at mapbox.com>
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that (i) the above copyright notices and this permission notice appear in
* all copies of the software and related documentation, and (ii) the names of
* Sam Leffler and Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Sam Leffler and Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
* ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include "tiffiop.h"
#ifdef WEBP_SUPPORT
/*
* TIFF Library.
*
* WEBP Compression Support
*
*/
#include "webp/decode.h"
#include "webp/encode.h"
#include <stdio.h>
#define LSTATE_INIT_DECODE 0x01
#define LSTATE_INIT_ENCODE 0x02
/*
* State block for each open TIFF
* file using WEBP compression/decompression.
*/
typedef struct {
uint16 nSamples; /* number of samples per pixel */
int lossless; /* lossy/lossless compression */
int quality_level; /* compression level */
WebPPicture sPicture; /* WebP Picture */
WebPConfig sEncoderConfig; /* WebP encoder config */
uint8* pBuffer; /* buffer to hold raw data on encoding */
unsigned int buffer_offset; /* current offset into the buffer */
unsigned int buffer_size;
WebPIDecoder* psDecoder; /* WebPIDecoder */
WebPDecBuffer sDecBuffer; /* Decoder buffer */
int last_y; /* Last row decoded */
int state; /* state flags */
TIFFVGetMethod vgetparent; /* super-class method */
TIFFVSetMethod vsetparent; /* super-class method */
} WebPState;
#define LState(tif) ((WebPState*) (tif)->tif_data)
#define DecoderState(tif) LState(tif)
#define EncoderState(tif) LState(tif)
static int TWebPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s);
static int TWebPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s);
static
int TWebPDatasetWriter(const uint8_t* data, size_t data_size,
const WebPPicture* const picture)
{
static const char module[] = "TWebPDatasetWriter";
TIFF* tif = (TIFF*)(picture->custom_ptr);
if ( (tif->tif_rawcc + (tmsize_t)data_size) > tif->tif_rawdatasize ) {
TIFFErrorExt(tif->tif_clientdata, module,
"Buffer too small by %lu bytes.",
tif->tif_rawcc + data_size - tif->tif_rawdatasize);
return 0;
} else {
_TIFFmemcpy(tif->tif_rawcp, data, data_size);
tif->tif_rawcc += data_size;
tif->tif_rawcp += data_size;
return 1;
}
}
/*
* Encode a chunk of pixels.
*/
static int
TWebPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
{
static const char module[] = "TWebPEncode";
WebPState *sp = EncoderState(tif);
(void) s;
assert(sp != NULL);
assert(sp->state == LSTATE_INIT_ENCODE);
if( (uint64)sp->buffer_offset +
(uint64)cc > sp->buffer_size )
{
TIFFErrorExt(tif->tif_clientdata, module,
"Too many bytes to be written");
return 0;
}
memcpy(sp->pBuffer + sp->buffer_offset,
bp, cc);
sp->buffer_offset += (unsigned)cc;
return 1;
}
static int
TWebPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
{
static const char module[] = "WebPDecode";
VP8StatusCode status = VP8_STATUS_OK;
WebPState *sp = DecoderState(tif);
(void) s;
assert(sp != NULL);
assert(sp->state == LSTATE_INIT_DECODE);
if (occ % sp->sDecBuffer.u.RGBA.stride)
{
TIFFErrorExt(tif->tif_clientdata, module,
"Fractional scanlines cannot be read");
return 0;
}
status = WebPIAppend(sp->psDecoder, tif->tif_rawcp, tif->tif_rawcc);
if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
if (status == VP8_STATUS_INVALID_PARAM) {
TIFFErrorExt(tif->tif_clientdata, module,
"Invalid parameter used.");
} else if (status == VP8_STATUS_OUT_OF_MEMORY) {
TIFFErrorExt(tif->tif_clientdata, module,
"Out of memory.");
} else {
TIFFErrorExt(tif->tif_clientdata, module,
"Unrecognized error.");
}
return 0;
} else {
int current_y, stride;
uint8_t* buf;
// Returns the RGB/A image decoded so far
buf = WebPIDecGetRGB(sp->psDecoder, &current_y, NULL, NULL, &stride);
if ((buf != NULL) && (current_y > sp->last_y)) {
memcpy(op,
buf + (sp->last_y * stride),
occ);
tif->tif_rawcp += occ;
tif->tif_rawcc -= occ;
sp->last_y = current_y;
return 1;
} else {
TIFFErrorExt(tif->tif_clientdata, module, "Unable to decode WebP data.");
return 0;
}
}
}
static int
TWebPFixupTags(TIFF* tif)
{
(void) tif;
if (tif->tif_dir.td_planarconfig != PLANARCONFIG_CONTIG) {
static const char module[] = "TWebPFixupTags";
TIFFErrorExt(tif->tif_clientdata, module,
"TIFF WEBP requires data to be stored contiguously in RGB e.g. RGBRGBRGB "
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
"or RGBARGBARGBA"
#endif
);
return 0;
}
return 1;
}
static int
TWebPSetupDecode(TIFF* tif)
{
static const char module[] = "WebPSetupDecode";
uint16 nBitsPerSample = tif->tif_dir.td_bitspersample;
uint16 sampleFormat = tif->tif_dir.td_sampleformat;
WebPState* sp = DecoderState(tif);
assert(sp != NULL);
// check band count
if ( sp->nSamples != 3
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
&& sp->nSamples != 4
#endif
)
{
TIFFErrorExt(tif->tif_clientdata, module,
"WEBP driver doesn't support %d bands. Must be 3 (RGB) "
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
"or 4 (RGBA) "
#endif
"bands.",
sp->nSamples );
return 0;
}
// check bits per sample and data type
if ((nBitsPerSample != 8) && (sampleFormat != 1)) {
TIFFErrorExt(tif->tif_clientdata, module,
"WEBP driver requires 8 bit unsigned data");
return 0;
}
/* if we were last encoding, terminate this mode */
if (sp->state & LSTATE_INIT_ENCODE) {
WebPPictureFree(&sp->sPicture);
if (sp->pBuffer != NULL) {
_TIFFfree(sp->pBuffer);
sp->pBuffer = NULL;
}
sp->buffer_offset = 0;
sp->state = 0;
}
sp->state |= LSTATE_INIT_DECODE;
return 1;
}
/*
* Setup state for decoding a strip.
*/
static int
TWebPPreDecode(TIFF* tif, uint16 s)
{
static const char module[] = "TWebPPreDecode";
uint32 segment_width, segment_height;
WebPState* sp = DecoderState(tif);
TIFFDirectory* td = &tif->tif_dir;
(void) s;
assert(sp != NULL);
if (isTiled(tif)) {
segment_width = td->td_tilewidth;
segment_height = td->td_tilelength;
} else {
segment_width = td->td_imagewidth;
segment_height = td->td_imagelength - tif->tif_row;
if (segment_height > td->td_rowsperstrip)
segment_height = td->td_rowsperstrip;
}
if( (sp->state & LSTATE_INIT_DECODE) == 0 )
tif->tif_setupdecode(tif);
if (sp->psDecoder != NULL) {
WebPIDelete(sp->psDecoder);
WebPFreeDecBuffer(&sp->sDecBuffer);
sp->psDecoder = NULL;
}
sp->last_y = 0;
WebPInitDecBuffer(&sp->sDecBuffer);
sp->sDecBuffer.is_external_memory = 0;
sp->sDecBuffer.width = segment_width;
sp->sDecBuffer.height = segment_height;
sp->sDecBuffer.u.RGBA.stride = segment_width * sp->nSamples;
sp->sDecBuffer.u.RGBA.size = segment_width * sp->nSamples * segment_height;
if (sp->nSamples > 3) {
sp->sDecBuffer.colorspace = MODE_RGBA;
} else {
sp->sDecBuffer.colorspace = MODE_RGB;
}
sp->psDecoder = WebPINewDecoder(&sp->sDecBuffer);
if (sp->psDecoder == NULL) {
TIFFErrorExt(tif->tif_clientdata, module,
"Unable to allocate WebP decoder.");
return 0;
}
return 1;
}
static int
TWebPSetupEncode(TIFF* tif)
{
static const char module[] = "WebPSetupEncode";
uint16 nBitsPerSample = tif->tif_dir.td_bitspersample;
uint16 sampleFormat = tif->tif_dir.td_sampleformat;
WebPState* sp = EncoderState(tif);
assert(sp != NULL);
// check band count
if ( sp->nSamples != 3
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
&& sp->nSamples != 4
#endif
)
{
TIFFErrorExt(tif->tif_clientdata, module,
"WEBP driver doesn't support %d bands. Must be 3 (RGB) "
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
"or 4 (RGBA) "
#endif
"bands.",
sp->nSamples );
return 0;
}
// check bits per sample and data type
if ((nBitsPerSample != 8) && (sampleFormat != 1)) {
TIFFErrorExt(tif->tif_clientdata, module,
"WEBP driver requires 8 bit unsigned data");
return 0;
}
if (sp->state & LSTATE_INIT_DECODE) {
WebPIDelete(sp->psDecoder);
WebPFreeDecBuffer(&sp->sDecBuffer);
sp->psDecoder = NULL;
sp->last_y = 0;
sp->state = 0;
}
sp->state |= LSTATE_INIT_ENCODE;
if (!WebPConfigInitInternal(&sp->sEncoderConfig, WEBP_PRESET_DEFAULT,
sp->quality_level,
WEBP_ENCODER_ABI_VERSION)) {
TIFFErrorExt(tif->tif_clientdata, module,
"Error creating WebP encoder configuration.");
return 0;
}
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
sp->sEncoderConfig.lossless = sp->lossless;
#endif
if (!WebPValidateConfig(&sp->sEncoderConfig)) {
TIFFErrorExt(tif->tif_clientdata, module,
"Error with WebP encoder configuration.");
return 0;
}
if (!WebPPictureInit(&sp->sPicture)) {
TIFFErrorExt(tif->tif_clientdata, module,
"Error initializing WebP picture.");
return 0;
}
return 1;
}
/*
* Reset encoding state at the start of a strip.
*/
static int
TWebPPreEncode(TIFF* tif, uint16 s)
{
uint32 segment_width, segment_height;
WebPState *sp = EncoderState(tif);
TIFFDirectory* td = &tif->tif_dir;
(void) s;
assert(sp != NULL);
if( sp->state != LSTATE_INIT_ENCODE )
tif->tif_setupencode(tif);
/*
* Set encoding parameters for this strip/tile.
*/
if (isTiled(tif)) {
segment_width = td->td_tilewidth;
segment_height = td->td_tilelength;
} else {
segment_width = td->td_imagewidth;
segment_height = td->td_imagelength - tif->tif_row;
if (segment_height > td->td_rowsperstrip)
segment_height = td->td_rowsperstrip;
}
// set up buffer for raw data
sp->buffer_size = segment_width * segment_height * sp->nSamples;
sp->pBuffer = _TIFFmalloc(sp->buffer_size);
sp->buffer_offset = 0;
sp->sPicture.width = segment_width;
sp->sPicture.height = segment_height;
sp->sPicture.writer = TWebPDatasetWriter;
sp->sPicture.custom_ptr = tif;
return 1;
}
/*
* Finish off an encoded strip by flushing it.
*/
static int
TWebPPostEncode(TIFF* tif)
{
static const char module[] = "WebPPostEncode";
int64_t stride;
WebPState *sp = EncoderState(tif);
assert(sp != NULL);
assert(sp->state == LSTATE_INIT_ENCODE);
stride = (int64_t)sp->sPicture.width * sp->nSamples;
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
if (sp->nSamples == 4) {
if (!WebPPictureImportRGBA(&sp->sPicture, sp->pBuffer, (int)stride)) {
TIFFErrorExt(tif->tif_clientdata, module,
"WebPPictureImportRGBA() failed" );
return 0;
}
}
else
#endif
if (!WebPPictureImportRGB(&sp->sPicture, sp->pBuffer, (int)stride)) {
TIFFErrorExt(tif->tif_clientdata, module,
"WebPPictureImportRGB() failed");
return 0;
}
if (!WebPEncode(&sp->sEncoderConfig, &sp->sPicture)) {
// copied from webpdataset.cpp
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
const char* pszErrorMsg = NULL;
switch(sp->sPicture.error_code) {
case VP8_ENC_ERROR_OUT_OF_MEMORY:
pszErrorMsg = "Out of memory"; break;
case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
pszErrorMsg = "Out of memory while flushing bits"; break;
case VP8_ENC_ERROR_NULL_PARAMETER:
pszErrorMsg = "A pointer parameter is NULL"; break;
case VP8_ENC_ERROR_INVALID_CONFIGURATION:
pszErrorMsg = "Configuration is invalid"; break;
case VP8_ENC_ERROR_BAD_DIMENSION:
pszErrorMsg = "Picture has invalid width/height"; break;
case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
pszErrorMsg = "Partition is bigger than 512k. Try using less "
"SEGMENTS, or increase PARTITION_LIMIT value";
break;
case VP8_ENC_ERROR_PARTITION_OVERFLOW:
pszErrorMsg = "Partition is bigger than 16M";
break;
case VP8_ENC_ERROR_BAD_WRITE:
pszErrorMsg = "Error while fludshing bytes"; break;
case VP8_ENC_ERROR_FILE_TOO_BIG:
pszErrorMsg = "File is bigger than 4G"; break;
case VP8_ENC_ERROR_USER_ABORT:
pszErrorMsg = "User interrupted";
break;
default:
TIFFErrorExt(tif->tif_clientdata, module,
"WebPEncode returned an unknown error code: %d",
sp->sPicture.error_code);
pszErrorMsg = "Unknown WebP error type.";
break;
}
TIFFErrorExt(tif->tif_clientdata, module,
"WebPEncode() failed : %s", pszErrorMsg);
#else
TIFFErrorExt(tif->tif_clientdata, module,
"Error in WebPEncode()");
#endif
return 0;
}
sp->sPicture.custom_ptr = NULL;
if (!TIFFFlushData1(tif))
{
TIFFErrorExt(tif->tif_clientdata, module,
"Error flushing TIFF WebP encoder.");
return 0;
}
return 1;
}
static void
TWebPCleanup(TIFF* tif)
{
WebPState* sp = LState(tif);
assert(sp != 0);
tif->tif_tagmethods.vgetfield = sp->vgetparent;
tif->tif_tagmethods.vsetfield = sp->vsetparent;
if (sp->state & LSTATE_INIT_ENCODE) {
WebPPictureFree(&sp->sPicture);
}
if (sp->psDecoder != NULL) {
WebPIDelete(sp->psDecoder);
WebPFreeDecBuffer(&sp->sDecBuffer);
sp->psDecoder = NULL;
sp->last_y = 0;
}
if (sp->pBuffer != NULL) {
_TIFFfree(sp->pBuffer);
sp->pBuffer = NULL;
}
if (tif->tif_data) {
_TIFFfree(tif->tif_data);
tif->tif_data = NULL;
}
_TIFFSetDefaultCompressionState(tif);
}
static int
TWebPVSetField(TIFF* tif, uint32 tag, va_list ap)
{
static const char module[] = "WebPVSetField";
WebPState* sp = LState(tif);
switch (tag) {
case TIFFTAG_WEBP_LEVEL:
sp->quality_level = (int) va_arg(ap, int);
if( sp->quality_level <= 0 ||
sp->quality_level > 100.0f ) {
TIFFWarningExt(tif->tif_clientdata, module,
"WEBP_LEVEL should be between 1 and 100");
}
return 1;
case TIFFTAG_WEBP_LOSSLESS:
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
sp->lossless = va_arg(ap, int);
return 1;
#else
TIFFErrorExt(tif->tif_clientdata, module,
"Need to upgrade WEBP driver, this version doesn't support "
"lossless compression.");
return 0;
#endif
default:
return (*sp->vsetparent)(tif, tag, ap);
}
/*NOTREACHED*/
}
static int
TWebPVGetField(TIFF* tif, uint32 tag, va_list ap)
{
WebPState* sp = LState(tif);
switch (tag) {
case TIFFTAG_WEBP_LEVEL:
*va_arg(ap, int*) = sp->quality_level;
break;
case TIFFTAG_WEBP_LOSSLESS:
*va_arg(ap, int*) = sp->lossless;
default:
return (*sp->vgetparent)(tif, tag, ap);
}
return 1;
}
static const TIFFField TWebPFields[] = {
{ TIFFTAG_WEBP_LEVEL, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
TIFF_SETGET_UNDEFINED,
FIELD_PSEUDO, TRUE, FALSE, "WEBP quality", NULL },
{ TIFFTAG_WEBP_LOSSLESS, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
TIFF_SETGET_UNDEFINED,
FIELD_PSEUDO, TRUE, FALSE, "WEBP lossless/lossy", NULL
},
};
int
TIFFInitWebP(TIFF* tif, int scheme)
{
static const char module[] = "TIFFInitWebP";
uint16 nSamples = tif->tif_dir.td_samplesperpixel;
WebPState* sp;
assert( scheme == COMPRESSION_WEBP );
/*
* Merge codec-specific tag information.
*/
if ( !_TIFFMergeFields(tif, TWebPFields, TIFFArrayCount(TWebPFields)) ) {
TIFFErrorExt(tif->tif_clientdata, module,
"Merging WebP codec-specific tags failed");
return 0;
}
/*
* Allocate state block so tag methods have storage to record values.
*/
tif->tif_data = (uint8*) _TIFFmalloc(sizeof(WebPState));
if (tif->tif_data == NULL)
goto bad;
sp = LState(tif);
/*
* Override parent get/set field methods.
*/
sp->vgetparent = tif->tif_tagmethods.vgetfield;
tif->tif_tagmethods.vgetfield = TWebPVGetField; /* hook for codec tags */
sp->vsetparent = tif->tif_tagmethods.vsetfield;
tif->tif_tagmethods.vsetfield = TWebPVSetField; /* hook for codec tags */
/* Default values for codec-specific fields */
sp->quality_level = 75.0f; /* default comp. level */
sp->lossless = 0; // default to false
sp->state = 0;
sp->nSamples = nSamples;
sp->psDecoder = NULL;
sp->last_y = 0;
sp->buffer_offset = 0;
sp->pBuffer = NULL;
/*
* Install codec methods.
* Notes:
* encoderow is not supported
*/
tif->tif_fixuptags = TWebPFixupTags;
tif->tif_setupdecode = TWebPSetupDecode;
tif->tif_predecode = TWebPPreDecode;
tif->tif_decoderow = TWebPDecode;
tif->tif_decodestrip = TWebPDecode;
tif->tif_decodetile = TWebPDecode;
tif->tif_setupencode = TWebPSetupEncode;
tif->tif_preencode = TWebPPreEncode;
tif->tif_postencode = TWebPPostEncode;
tif->tif_encodestrip = TWebPEncode;
tif->tif_encodetile = TWebPEncode;
tif->tif_cleanup = TWebPCleanup;
return 1;
bad:
TIFFErrorExt(tif->tif_clientdata, module,
"No space for WebP state block");
return 0;
}
#endif /* WEBP_SUPPORT */

View File

@ -191,6 +191,7 @@ typedef enum {
/* compression codes 34887-34889 are reserved for ESRI */
#define COMPRESSION_LZMA 34925 /* LZMA2 */
#define COMPRESSION_ZSTD 34926 /* ZSTD: WARNING not registered in Adobe-maintained registry */
#define COMPRESSION_WEBP 34927 /* WEBP: WARNING not registered in Adobe-maintained registry */
#define TIFFTAG_PHOTOMETRIC 262 /* photometric interpretation */
#define PHOTOMETRIC_MINISWHITE 0 /* min value is white */
#define PHOTOMETRIC_MINISBLACK 1 /* min value is black */
@ -614,6 +615,8 @@ typedef enum {
#define LERC_ADD_COMPRESSION_DEFLATE 1
#define LERC_ADD_COMPRESSION_ZSTD 2
#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 */
/*
* EXIF tags

View File

@ -429,6 +429,9 @@ extern int TIFFInitLZMA(TIFF*, int);
#ifdef ZSTD_SUPPORT
extern int TIFFInitZSTD(TIFF*, int);
#endif
#ifdef WEBP_SUPPORT
extern int TIFFInitWebP(TIFF*, int);
#endif
#ifdef VMS
extern const TIFFCodec _TIFFBuiltinCODECS[];
#else

View File

@ -392,6 +392,9 @@ processCompressOptions(char* opt)
} else if (strneq(opt, "zstd", 4)) {
processZIPOptions(opt);
defcompression = COMPRESSION_ZSTD;
} else if (strneq(opt, "webp", 4)) {
processZIPOptions(opt);
defcompression = COMPRESSION_WEBP;
} else if (strneq(opt, "jbig", 4)) {
defcompression = COMPRESSION_JBIG;
} else if (strneq(opt, "sgilog", 6)) {
@ -431,6 +434,7 @@ char* stuff[] = {
" -c zip[:opts] compress output with deflate encoding",
" -c lzma[:opts] compress output with LZMA2 encoding",
" -c zstd[:opts] compress output with ZSTD encoding",
" -c webp[:opts] compress output with WEBP encoding",
" -c jpeg[:opts] compress output with JPEG encoding",
" -c jbig compress output with ISO JBIG encoding",
" -c packbits compress output with packbits encoding",
@ -450,7 +454,7 @@ char* stuff[] = {
" r output color image as RGB rather than YCbCr",
"For example, -c jpeg:r:50 to get JPEG-encoded RGB data with 50% comp. quality",
"",
"LZW, Deflate (ZIP), LZMA2 and ZSTD options:",
"LZW, Deflate (ZIP), LZMA2, ZSTD and WEBP options:",
" # set predictor value",
" p# set compression level (preset)",
"For example, -c lzw:2 to get LZW-encoded data with horizontal differencing,",
@ -736,6 +740,7 @@ tiffcp(TIFF* in, TIFF* out)
case COMPRESSION_DEFLATE:
case COMPRESSION_LZMA:
case COMPRESSION_ZSTD:
case COMPRESSION_WEBP:
if (predictor != (uint16)-1)
TIFFSetField(out, TIFFTAG_PREDICTOR, predictor);
else
@ -748,6 +753,13 @@ tiffcp(TIFF* in, TIFF* out)
TIFFSetField(out, TIFFTAG_LZMAPRESET, preset);
else if (compression == COMPRESSION_ZSTD)
TIFFSetField(out, TIFFTAG_ZSTD_LEVEL, preset);
else if (compression == COMPRESSION_WEBP) {
if (preset == 100) {
TIFFSetField(out, TIFFTAG_WEBP_LOSSLESS, TRUE);
} else {
TIFFSetField(out, TIFFTAG_WEBP_LEVEL, preset);
}
}
}
break;
case COMPRESSION_CCITTFAX3: