Commit 9eacd59f authored by Norman Barker's avatar Norman Barker
Browse files

webp in tiff

parent 981e43ec
......@@ -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
......
......@@ -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;
}
......
/*
* 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;