webp in tiff
This commit is contained in:
parent
981e43ecae
commit
9eacd59fec
@ -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 "")
|
||||
|
@ -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
|
||||
|
55
configure.ac
55
configure.ac
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
@ -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@"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
666
libtiff/tif_webp.c
Normal 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, ¤t_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 */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user