diff options
Diffstat (limited to 'tiff/libtiff/tif_lerc.c')
-rw-r--r-- | tiff/libtiff/tif_lerc.c | 1277 |
1 files changed, 1277 insertions, 0 deletions
diff --git a/tiff/libtiff/tif_lerc.c b/tiff/libtiff/tif_lerc.c new file mode 100644 index 00000000..a4aeb4a1 --- /dev/null +++ b/tiff/libtiff/tif_lerc.c @@ -0,0 +1,1277 @@ +/* +* Copyright (c) 2018, Even Rouault +* Author: <even.rouault at spatialys.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 LERC_SUPPORT +/* +* TIFF Library. +* +* LERC Compression Support +* +*/ + +#include "Lerc_c_api.h" +#include "zlib.h" +#ifdef ZSTD_SUPPORT +#include "zstd.h" +#endif + +#if LIBDEFLATE_SUPPORT +#include "libdeflate.h" +#endif +#define LIBDEFLATE_MAX_COMPRESSION_LEVEL 12 + +#include <assert.h> + +#define LSTATE_INIT_DECODE 0x01 +#define LSTATE_INIT_ENCODE 0x02 + +#ifndef LERC_AT_LEAST_VERSION +#define LERC_AT_LEAST_VERSION(maj,min,patch) 0 +#endif + +/* +* State block for each open TIFF file using LERC compression/decompression. +*/ +typedef struct { + double maxzerror; /* max z error */ + int lerc_version; + int additional_compression; + int zstd_compress_level; /* zstd */ + int zipquality; /* deflate */ + int state; /* state flags */ + + uint32_t segment_width; + uint32_t segment_height; + + unsigned int uncompressed_size; + unsigned int uncompressed_alloc; + uint8_t *uncompressed_buffer; + unsigned int uncompressed_offset; + + unsigned int mask_size; + uint8_t *mask_buffer; + + unsigned int compressed_size; + void *compressed_buffer; + +#if LIBDEFLATE_SUPPORT + struct libdeflate_decompressor* libdeflate_dec; + struct libdeflate_compressor* libdeflate_enc; +#endif + + TIFFVGetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ +} LERCState; + +#define LState(tif) ((LERCState*) (tif)->tif_data) +#define DecoderState(tif) LState(tif) +#define EncoderState(tif) LState(tif) + +static int LERCEncode(TIFF* tif, uint8_t* bp, tmsize_t cc, uint16_t s); +static int LERCDecode(TIFF* tif, uint8_t* op, tmsize_t occ, uint16_t s); + +static int +LERCFixupTags(TIFF* tif) +{ + (void) tif; + return 1; +} + +static int +LERCSetupDecode(TIFF* tif) +{ + LERCState* sp = DecoderState(tif); + + assert(sp != NULL); + + /* if we were last encoding, terminate this mode */ + if (sp->state & LSTATE_INIT_ENCODE) { + sp->state = 0; + } + + sp->state |= LSTATE_INIT_DECODE; + return 1; +} + +static int GetLercDataType(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + static const char module[] = "GetLercDataType"; + + if( td->td_sampleformat == SAMPLEFORMAT_INT && + td->td_bitspersample == 8 ) + { + return 0; + } + + if( td->td_sampleformat == SAMPLEFORMAT_UINT && + td->td_bitspersample == 8 ) + { + return 1; + } + + if( td->td_sampleformat == SAMPLEFORMAT_INT && + td->td_bitspersample == 16 ) + { + return 2; + } + + if( td->td_sampleformat == SAMPLEFORMAT_UINT && + td->td_bitspersample == 16 ) + { + return 3; + } + + if( td->td_sampleformat == SAMPLEFORMAT_INT && + td->td_bitspersample == 32 ) + { + return 4; + } + + if( td->td_sampleformat == SAMPLEFORMAT_UINT && + td->td_bitspersample == 32 ) + { + return 5; + } + + if( td->td_sampleformat == SAMPLEFORMAT_IEEEFP && + td->td_bitspersample == 32 ) + { + return 6; + } + + if( td->td_sampleformat == SAMPLEFORMAT_IEEEFP && + td->td_bitspersample == 64 ) + { + return 7; + } + + TIFFErrorExt(tif->tif_clientdata, module, + "Unsupported combination of SampleFormat and td_bitspersample"); + return -1; +} + +static int SetupUncompressedBuffer(TIFF* tif, LERCState* sp, + const char* module) +{ + TIFFDirectory *td = &tif->tif_dir; + uint64_t new_size_64; + uint64_t new_alloc_64; + unsigned int new_size; + unsigned int new_alloc; + + sp->uncompressed_offset = 0; + + if (isTiled(tif)) { + sp->segment_width = td->td_tilewidth; + sp->segment_height = td->td_tilelength; + } else { + sp->segment_width = td->td_imagewidth; + sp->segment_height = td->td_imagelength - tif->tif_row; + if (sp->segment_height > td->td_rowsperstrip) + sp->segment_height = td->td_rowsperstrip; + } + + new_size_64 = (uint64_t)sp->segment_width * sp->segment_height * + (td->td_bitspersample / 8); + if( td->td_planarconfig == PLANARCONFIG_CONTIG ) + { + new_size_64 *= td->td_samplesperpixel; + } + + new_size = (unsigned int)new_size_64; + sp->uncompressed_size = new_size; + + /* add some margin as we are going to use it also to store deflate/zstd compressed data */ + new_alloc_64 = 100 + new_size_64 + new_size_64 / 3; +#ifdef ZSTD_SUPPORT + { + size_t zstd_max = ZSTD_compressBound((size_t)new_size_64); + if( new_alloc_64 < zstd_max ) + { + new_alloc_64 = zstd_max; + } + } +#endif + new_alloc = (unsigned int)new_alloc_64; + if( new_alloc != new_alloc_64 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Too large uncompressed strip/tile"); + _TIFFfree(sp->uncompressed_buffer); + sp->uncompressed_buffer = 0; + sp->uncompressed_alloc = 0; + return 0; + } + + if( sp->uncompressed_alloc < new_alloc ) + { + _TIFFfree(sp->uncompressed_buffer); + sp->uncompressed_buffer = _TIFFmalloc(new_alloc); + if( !sp->uncompressed_buffer ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot allocate buffer"); + _TIFFfree(sp->uncompressed_buffer); + sp->uncompressed_buffer = 0; + sp->uncompressed_alloc = 0; + return 0; + } + sp->uncompressed_alloc = new_alloc; + } + + if( (td->td_planarconfig == PLANARCONFIG_CONTIG && + td->td_extrasamples > 0 && + td->td_sampleinfo[td->td_extrasamples-1] == EXTRASAMPLE_UNASSALPHA && + GetLercDataType(tif) == 1 ) || + (td->td_sampleformat == SAMPLEFORMAT_IEEEFP && + (td->td_planarconfig == PLANARCONFIG_SEPARATE || + td->td_samplesperpixel == 1) && + (td->td_bitspersample == 32 || td->td_bitspersample == 64 )) ) + { + unsigned int mask_size = sp->segment_width * sp->segment_height; + if( sp->mask_size < mask_size ) + { + void* mask_buffer = _TIFFrealloc(sp->mask_buffer, mask_size); + if( mask_buffer == NULL ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot allocate buffer"); + sp->mask_size = 0; + _TIFFfree(sp->uncompressed_buffer); + sp->uncompressed_buffer = 0; + sp->uncompressed_alloc = 0; + return 0; + } + sp->mask_buffer = (uint8_t*)mask_buffer; + sp->mask_size = mask_size; + } + } + + return 1; +} + +/* +* Setup state for decoding a strip. +*/ +static int +LERCPreDecode(TIFF* tif, uint16_t s) +{ + static const char module[] = "LERCPreDecode"; + lerc_status lerc_ret; + TIFFDirectory *td = &tif->tif_dir; + LERCState* sp = DecoderState(tif); + int lerc_data_type; + unsigned int infoArray[8]; + unsigned nomask_bands = td->td_samplesperpixel; + int ndims; + int use_mask = 0; + uint8_t* lerc_data = tif->tif_rawcp; + unsigned int lerc_data_size = (unsigned int)tif->tif_rawcc; + + (void) s; + assert(sp != NULL); + if( sp->state != LSTATE_INIT_DECODE ) + tif->tif_setupdecode(tif); + + lerc_data_type = GetLercDataType(tif); + if( lerc_data_type < 0 ) + return 0; + + if( !SetupUncompressedBuffer(tif, sp, module) ) + return 0; + + if( sp->additional_compression != LERC_ADD_COMPRESSION_NONE ) + { + if( sp->compressed_size < sp->uncompressed_alloc ) + { + _TIFFfree(sp->compressed_buffer); + sp->compressed_buffer = _TIFFmalloc(sp->uncompressed_alloc); + if( !sp->compressed_buffer ) + { + sp->compressed_size = 0; + return 0; + } + sp->compressed_size = sp->uncompressed_alloc; + } + } + + if( sp->additional_compression == LERC_ADD_COMPRESSION_DEFLATE ) + { +#if LIBDEFLATE_SUPPORT + enum libdeflate_result res; + size_t lerc_data_sizet = 0; + if( sp->libdeflate_dec == NULL ) + { + sp->libdeflate_dec = libdeflate_alloc_decompressor(); + if( sp->libdeflate_dec == NULL ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot allocate decompressor"); + return 0; + } + } + + res = libdeflate_zlib_decompress( + sp->libdeflate_dec, tif->tif_rawcp, (size_t)tif->tif_rawcc, + sp->compressed_buffer, sp->compressed_size, + &lerc_data_sizet); + if( res != LIBDEFLATE_SUCCESS ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Decoding error at scanline %lu", + (unsigned long) tif->tif_row); + return 0; + } + assert( lerc_data_sizet == (unsigned int)lerc_data_sizet ); + lerc_data = sp->compressed_buffer; + lerc_data_size = (unsigned int)lerc_data_sizet; +#else + z_stream strm; + int zlib_ret; + + memset(&strm, 0, sizeof(strm)); + strm.zalloc = NULL; + strm.zfree = NULL; + strm.opaque = NULL; + zlib_ret = inflateInit(&strm); + if( zlib_ret != Z_OK ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "inflateInit() failed"); + inflateEnd(&strm); + return 0; + } + + strm.avail_in = (uInt)tif->tif_rawcc; + strm.next_in = tif->tif_rawcp; + strm.avail_out = sp->compressed_size; + strm.next_out = sp->compressed_buffer; + zlib_ret = inflate(&strm, Z_FINISH); + if( zlib_ret != Z_STREAM_END && zlib_ret != Z_OK ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "inflate() failed"); + inflateEnd(&strm); + return 0; + } + lerc_data = sp->compressed_buffer; + lerc_data_size = sp->compressed_size - strm.avail_out; + inflateEnd(&strm); +#endif + } + else if( sp->additional_compression == LERC_ADD_COMPRESSION_ZSTD ) + { +#ifdef ZSTD_SUPPORT + size_t zstd_ret; + + zstd_ret = ZSTD_decompress(sp->compressed_buffer, + sp->compressed_size, + tif->tif_rawcp, + tif->tif_rawcc); + if( ZSTD_isError(zstd_ret) ) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error in ZSTD_decompress(): %s", + ZSTD_getErrorName(zstd_ret)); + return 0; + } + + lerc_data = sp->compressed_buffer; + lerc_data_size = (unsigned int)zstd_ret; +#else + TIFFErrorExt(tif->tif_clientdata, module, "ZSTD support missing"); + return 0; +#endif + } + else if( sp->additional_compression != LERC_ADD_COMPRESSION_NONE ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unhandled additional compression"); + return 0; + } + + lerc_ret = lerc_getBlobInfo( + lerc_data, + lerc_data_size, + infoArray, + NULL, + 8, + 0); + if( lerc_ret != 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "lerc_getBlobInfo() failed"); + return 0; + } + + /* If the configuration is compatible of a LERC mask, and that the */ + /* LERC info has dim == samplesperpixel - 1, then there is a LERC */ + /* mask. */ + if( td->td_planarconfig == PLANARCONFIG_CONTIG && + td->td_extrasamples > 0 && + td->td_sampleinfo[td->td_extrasamples-1] == EXTRASAMPLE_UNASSALPHA && + GetLercDataType(tif) == 1 && + infoArray[2] == td->td_samplesperpixel - 1U ) + { + use_mask = 1; + nomask_bands --; + } + else if( td->td_sampleformat == SAMPLEFORMAT_IEEEFP && + (td->td_planarconfig == PLANARCONFIG_SEPARATE || + td->td_samplesperpixel == 1) && + (td->td_bitspersample == 32 || td->td_bitspersample == 64) ) + { + use_mask = 1; + } + + ndims = td->td_planarconfig == PLANARCONFIG_CONTIG ? + nomask_bands : 1; + + /* Info returned in infoArray is { version, dataType, nDim, nCols, + nRows, nBands, nValidPixels, blobSize } */ + if( infoArray[0] != (unsigned)sp->lerc_version ) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Unexpected version number: %d. Expected: %d", + infoArray[0], sp->lerc_version); + } + if( infoArray[1] != (unsigned)lerc_data_type ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unexpected dataType: %d. Expected: %d", + infoArray[1], lerc_data_type); + return 0; + } + if( infoArray[2] != (unsigned)ndims ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unexpected nDim: %d. Expected: %d", + infoArray[2], ndims); + return 0; + } + if( infoArray[3] != sp->segment_width ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unexpected nCols: %d. Expected: %du", + infoArray[3], sp->segment_width); + return 0; + } + if( infoArray[4] != sp->segment_height ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unexpected nRows: %d. Expected: %u", + infoArray[4], sp->segment_height); + return 0; + } + if( infoArray[5] != 1 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unexpected nBands: %d. Expected: %d", + infoArray[5], 1); + return 0; + } + if( infoArray[7] != lerc_data_size ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unexpected blobSize: %d. Expected: %u", + infoArray[7], + lerc_data_size); + return 0; + } + + lerc_ret = lerc_decode( + lerc_data, + lerc_data_size, +#if LERC_AT_LEAST_VERSION(3,0,0) + use_mask ? 1 : 0, +#endif + use_mask ? sp->mask_buffer : NULL, + ndims, + sp->segment_width, + sp->segment_height, + 1, + lerc_data_type, + sp->uncompressed_buffer); + if( lerc_ret != 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "lerc_decode() failed"); + return 0; + } + + /* Interleave alpha mask with other samples. */ + if( use_mask && GetLercDataType(tif) == 1 ) + { + unsigned src_stride = + (td->td_samplesperpixel - 1) * (td->td_bitspersample / 8); + unsigned dst_stride = + td->td_samplesperpixel * (td->td_bitspersample / 8); + unsigned i = sp->segment_width * sp->segment_height; + /* Operate from end to begin to be able to move in place */ + while( i > 0 && i > nomask_bands ) + { + i --; + sp->uncompressed_buffer[ + i * dst_stride + td->td_samplesperpixel - 1] = + 255 * sp->mask_buffer[i]; + memcpy( sp->uncompressed_buffer + i * dst_stride, + sp->uncompressed_buffer + i * src_stride, + src_stride ); + } + /* First pixels must use memmove due to overlapping areas */ + while( i > 0 ) + { + i --; + sp->uncompressed_buffer[ + i * dst_stride + td->td_samplesperpixel - 1] = + 255 * sp->mask_buffer[i]; + memmove( sp->uncompressed_buffer + i * dst_stride, + sp->uncompressed_buffer + i * src_stride, + src_stride ); + } + } + else if( use_mask && td->td_sampleformat == SAMPLEFORMAT_IEEEFP ) + { + const unsigned nb_pixels = sp->segment_width * sp->segment_height; + unsigned i; +#if HOST_BIGENDIAN + const unsigned char nan_bytes[] = { 0x7f, 0xc0, 0, 0 }; +#else + const unsigned char nan_bytes[] = { 0, 0, 0xc0, 0x7f }; +#endif + float nan_float32; + memcpy(&nan_float32, nan_bytes, 4); + + if( td->td_bitspersample == 32 ) + { + for( i = 0; i < nb_pixels; i++ ) + { + if( sp->mask_buffer[i] == 0 ) + ((float*)sp->uncompressed_buffer)[i] = nan_float32; + } + } + else + { + const double nan_float64 = nan_float32; + for( i = 0; i < nb_pixels; i++ ) + { + if( sp->mask_buffer[i] == 0 ) + ((double*)sp->uncompressed_buffer)[i] = nan_float64; + } + } + } + + return 1; +} + +/* +* Decode a strip, tile or scanline. +*/ +static int +LERCDecode(TIFF* tif, uint8_t* op, tmsize_t occ, uint16_t s) +{ + static const char module[] = "LERCDecode"; + LERCState* sp = DecoderState(tif); + + (void) s; + assert(sp != NULL); + assert(sp->state == LSTATE_INIT_DECODE); + + if( sp->uncompressed_buffer == 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Uncompressed buffer not allocated"); + return 0; + } + + if( (uint64_t)sp->uncompressed_offset + + (uint64_t)occ > sp->uncompressed_size ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Too many bytes read"); + return 0; + } + + memcpy(op, + sp->uncompressed_buffer + sp->uncompressed_offset, + occ); + sp->uncompressed_offset += (unsigned)occ; + + return 1; +} + +static int +LERCSetupEncode(TIFF* tif) +{ + LERCState* sp = EncoderState(tif); + + assert(sp != NULL); + if (sp->state & LSTATE_INIT_DECODE) { + sp->state = 0; + } + + sp->state |= LSTATE_INIT_ENCODE; + + return 1; +} + +/* +* Reset encoding state at the start of a strip. +*/ +static int +LERCPreEncode(TIFF* tif, uint16_t s) +{ + static const char module[] = "LERCPreEncode"; + LERCState *sp = EncoderState(tif); + int lerc_data_type; + + (void) s; + assert(sp != NULL); + if( sp->state != LSTATE_INIT_ENCODE ) + tif->tif_setupencode(tif); + + lerc_data_type = GetLercDataType(tif); + if( lerc_data_type < 0 ) + return 0; + + if( !SetupUncompressedBuffer(tif, sp, module) ) + return 0; + + return 1; +} + +/* +* Encode a chunk of pixels. +*/ +static int +LERCEncode(TIFF* tif, uint8_t* bp, tmsize_t cc, uint16_t s) +{ + static const char module[] = "LERCEncode"; + LERCState *sp = EncoderState(tif); + + (void)s; + assert(sp != NULL); + assert(sp->state == LSTATE_INIT_ENCODE); + + if( (uint64_t)sp->uncompressed_offset + + (uint64_t)cc > sp->uncompressed_size ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Too many bytes written"); + return 0; + } + + memcpy(sp->uncompressed_buffer + sp->uncompressed_offset, + bp, cc); + sp->uncompressed_offset += (unsigned)cc; + + return 1; +} + +/* +* Finish off an encoded strip by flushing it. +*/ +static int +LERCPostEncode(TIFF* tif) +{ + lerc_status lerc_ret; + static const char module[] = "LERCPostEncode"; + LERCState *sp = EncoderState(tif); + unsigned int numBytes = 0; + unsigned int numBytesWritten = 0; + TIFFDirectory *td = &tif->tif_dir; + int use_mask = 0; + unsigned dst_nbands = td->td_samplesperpixel; + + if( sp->uncompressed_offset != sp->uncompressed_size ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unexpected number of bytes in the buffer"); + return 0; + } + + /* Extract alpha mask (if containing only 0 and 255 values, */ + /* and compact array of regular bands */ + if( td->td_planarconfig == PLANARCONFIG_CONTIG && + td->td_extrasamples > 0 && + td->td_sampleinfo[td->td_extrasamples-1] == EXTRASAMPLE_UNASSALPHA && + GetLercDataType(tif) == 1 ) + { + const unsigned dst_stride = (td->td_samplesperpixel - 1) * + (td->td_bitspersample / 8); + const unsigned src_stride = td->td_samplesperpixel * + (td->td_bitspersample / 8); + unsigned i = 0; + const unsigned nb_pixels = sp->segment_width * sp->segment_height; + + use_mask = 1; + for( i = 0 ; i < nb_pixels; i++) + { + int v = sp->uncompressed_buffer[ + i * src_stride + td->td_samplesperpixel - 1]; + if( v != 0 && v != 255 ) + { + use_mask = 0; + break; + } + } + + if( use_mask ) + { + dst_nbands --; + /* First pixels must use memmove due to overlapping areas */ + for( i = 0 ;i < dst_nbands && i < nb_pixels; i++) + { + memmove( sp->uncompressed_buffer + i * dst_stride, + sp->uncompressed_buffer + i * src_stride, + dst_stride ); + sp->mask_buffer[i] = sp->uncompressed_buffer[ + i * src_stride + td->td_samplesperpixel - 1]; + } + for(; i < nb_pixels; i++) + { + memcpy( sp->uncompressed_buffer + i * dst_stride, + sp->uncompressed_buffer + i * src_stride, + dst_stride ); + sp->mask_buffer[i] = sp->uncompressed_buffer[ + i * src_stride + td->td_samplesperpixel - 1]; + } + } + } + else if( td->td_sampleformat == SAMPLEFORMAT_IEEEFP && + (td->td_planarconfig == PLANARCONFIG_SEPARATE || + dst_nbands == 1) && + (td->td_bitspersample == 32 || td->td_bitspersample == 64 ) ) + { + /* Check for NaN values */ + unsigned i; + const unsigned nb_pixels = sp->segment_width * sp->segment_height; + if( td->td_bitspersample == 32 ) + { + for( i = 0; i < nb_pixels; i++ ) + { + const float val = ((float*)sp->uncompressed_buffer)[i]; + if( val != val ) + { + use_mask = 1; + break; + } + } + } + else + { + for( i = 0; i < nb_pixels; i++ ) + { + const double val = ((double*)sp->uncompressed_buffer)[i]; + if( val != val ) + { + use_mask = 1; + break; + } + } + } + + if( use_mask ) + { + if( td->td_bitspersample == 32 ) + { + for( i = 0; i < nb_pixels; i++ ) + { + const float val = ((float*)sp->uncompressed_buffer)[i]; + sp->mask_buffer[i] = ( val == val ) ? 255 : 0; + } + } + else + { + for( i = 0; i < nb_pixels; i++ ) + { + const double val = ((double*)sp->uncompressed_buffer)[i]; + sp->mask_buffer[i] = ( val == val ) ? 255 : 0; + } + } + } + } + + +#if 0 + lerc_ret = lerc_computeCompressedSize( + sp->uncompressed_buffer, + sp->lerc_version, + GetLercDataType(tif), + td->td_planarconfig == PLANARCONFIG_CONTIG ? + dst_nbands : 1, + sp->segment_width, + sp->segment_height, + 1, + use_mask ? sp->mask_buffer : NULL, + sp->maxzerror, + &numBytes); + if( lerc_ret != 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "lerc_computeCompressedSize() failed"); + return 0; + } +#else + numBytes = sp->uncompressed_alloc; +#endif + + if( sp->compressed_size < numBytes ) + { + _TIFFfree(sp->compressed_buffer); + sp->compressed_buffer = _TIFFmalloc(numBytes); + if( !sp->compressed_buffer ) + { + sp->compressed_size = 0; + return 0; + } + sp->compressed_size = numBytes; + } + + lerc_ret = lerc_encodeForVersion( + sp->uncompressed_buffer, + sp->lerc_version, + GetLercDataType(tif), + td->td_planarconfig == PLANARCONFIG_CONTIG ? + dst_nbands : 1, + sp->segment_width, + sp->segment_height, + 1, +#if LERC_AT_LEAST_VERSION(3,0,0) + use_mask ? 1 : 0, +#endif + use_mask ? sp->mask_buffer : NULL, + sp->maxzerror, + sp->compressed_buffer, + sp->compressed_size, + &numBytesWritten); + if( lerc_ret != 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "lerc_encode() failed"); + return 0; + } + assert( numBytesWritten < numBytes ); + + if( sp->additional_compression == LERC_ADD_COMPRESSION_DEFLATE ) + { +#if LIBDEFLATE_SUPPORT + if( sp->libdeflate_enc == NULL ) + { + /* To get results as good as zlib, we ask 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"); + return 0; + } + } + + /* Should not happen normally */ + if( libdeflate_zlib_compress_bound(sp->libdeflate_enc, numBytesWritten) > + sp->uncompressed_alloc ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Output buffer for libdeflate too small"); + return 0; + } + + tif->tif_rawcc = libdeflate_zlib_compress( + sp->libdeflate_enc, + sp->compressed_buffer, numBytesWritten, + sp->uncompressed_buffer, sp->uncompressed_alloc); + + if( tif->tif_rawcc == 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Encoder error at scanline %lu", + (unsigned long) tif->tif_row); + return 0; + } +#else + z_stream strm; + int zlib_ret; + int cappedQuality = sp->zipquality; + if( cappedQuality > Z_BEST_COMPRESSION ) + cappedQuality = Z_BEST_COMPRESSION; + + memset(&strm, 0, sizeof(strm)); + strm.zalloc = NULL; + strm.zfree = NULL; + strm.opaque = NULL; + zlib_ret = deflateInit(&strm, cappedQuality); + if( zlib_ret != Z_OK ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "deflateInit() failed"); + return 0; + } + + strm.avail_in = numBytesWritten; + strm.next_in = sp->compressed_buffer; + strm.avail_out = sp->uncompressed_alloc; + strm.next_out = sp->uncompressed_buffer; + zlib_ret = deflate(&strm, Z_FINISH); + if( zlib_ret == Z_STREAM_END ) + { + tif->tif_rawcc = sp->uncompressed_alloc - strm.avail_out; + } + deflateEnd(&strm); + if( zlib_ret != Z_STREAM_END ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "deflate() failed"); + return 0; + } +#endif + { + int ret; + uint8_t* tif_rawdata_backup = tif->tif_rawdata; + tif->tif_rawdata = sp->uncompressed_buffer; + ret = TIFFFlushData1(tif); + tif->tif_rawdata = tif_rawdata_backup; + if( !ret ) + { + return 0; + } + } + } + else if( sp->additional_compression == LERC_ADD_COMPRESSION_ZSTD ) + { +#ifdef ZSTD_SUPPORT + size_t zstd_ret = ZSTD_compress( sp->uncompressed_buffer, + sp->uncompressed_alloc, + sp->compressed_buffer, + numBytesWritten, + sp->zstd_compress_level ); + if( ZSTD_isError(zstd_ret) ) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error in ZSTD_compress(): %s", + ZSTD_getErrorName(zstd_ret)); + return 0; + } + + { + int ret; + uint8_t* tif_rawdata_backup = tif->tif_rawdata; + tif->tif_rawdata = sp->uncompressed_buffer; + tif->tif_rawcc = zstd_ret; + ret = TIFFFlushData1(tif); + tif->tif_rawdata = tif_rawdata_backup; + if( !ret ) + { + return 0; + } + } +#else + TIFFErrorExt(tif->tif_clientdata, module, "ZSTD support missing"); + return 0; +#endif + } + else if( sp->additional_compression != LERC_ADD_COMPRESSION_NONE ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unhandled additional compression"); + return 0; + } + else + { + int ret; + uint8_t* tif_rawdata_backup = tif->tif_rawdata; + tif->tif_rawdata = sp->compressed_buffer; + tif->tif_rawcc = numBytesWritten; + ret = TIFFFlushData1(tif); + tif->tif_rawdata = tif_rawdata_backup; + if( !ret ) + return 0; + } + + return 1; +} + +static void +LERCCleanup(TIFF* tif) +{ + LERCState* sp = LState(tif); + + assert(sp != 0); + + tif->tif_tagmethods.vgetfield = sp->vgetparent; + tif->tif_tagmethods.vsetfield = sp->vsetparent; + + _TIFFfree(sp->uncompressed_buffer); + _TIFFfree(sp->compressed_buffer); + _TIFFfree(sp->mask_buffer); + +#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; + + _TIFFSetDefaultCompressionState(tif); +} + +static const TIFFField LERCFields[] = { + { TIFFTAG_LERC_PARAMETERS, TIFF_VARIABLE2, TIFF_VARIABLE2, + TIFF_LONG, 0, TIFF_SETGET_C32_UINT32, TIFF_SETGET_UNDEFINED, + FIELD_CUSTOM, FALSE, TRUE, "LercParameters", NULL }, + { TIFFTAG_LERC_MAXZERROR, 0, 0, TIFF_ANY, 0, TIFF_SETGET_DOUBLE, + TIFF_SETGET_UNDEFINED, + FIELD_PSEUDO, TRUE, FALSE, "LercMaximumError", NULL }, + { TIFFTAG_LERC_VERSION, 0, 0, TIFF_ANY, 0, TIFF_SETGET_UINT32, + TIFF_SETGET_UNDEFINED, + FIELD_PSEUDO, FALSE, FALSE, "LercVersion", NULL }, + { TIFFTAG_LERC_ADD_COMPRESSION, 0, 0, TIFF_ANY, 0, TIFF_SETGET_UINT32, + TIFF_SETGET_UNDEFINED, + FIELD_PSEUDO, FALSE, FALSE, "LercAdditionalCompression", NULL }, + { TIFFTAG_ZSTD_LEVEL, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, + TIFF_SETGET_UNDEFINED, + FIELD_PSEUDO, TRUE, FALSE, "ZSTD zstd_compress_level", NULL }, + { TIFFTAG_ZIPQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, + TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL }, +}; + +static int LERCVSetFieldBase(TIFF* tif, uint32_t tag, ...) +{ + LERCState* sp = LState(tif); + int ret; + va_list ap; + va_start(ap, tag); + ret = (*sp->vsetparent)(tif, tag, ap); + va_end(ap); + return ret; +} + +static int +LERCVSetField(TIFF* tif, uint32_t tag, va_list ap) +{ + static const char module[] = "LERCVSetField"; + LERCState* sp = LState(tif); + + switch (tag) { + case TIFFTAG_LERC_PARAMETERS: + { + uint32_t count = va_arg(ap, int); + int* params = va_arg(ap, int*); + if( count < 2 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid count for LercParameters: %u", count); + return 0; + } + sp->lerc_version = params[0]; + sp->additional_compression = params[1]; + return LERCVSetFieldBase(tif, TIFFTAG_LERC_PARAMETERS, + count, params); + } + case TIFFTAG_LERC_MAXZERROR: + sp->maxzerror = va_arg(ap, double); + return 1; + case TIFFTAG_LERC_VERSION: + { + int params[2] = {0, 0}; + int version = va_arg(ap, int); + if( version != LERC_VERSION_2_4 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid value for LercVersion: %d", version); + return 0; + } + sp->lerc_version = version; + params[0] = sp->lerc_version; + params[1] = sp->additional_compression; + return LERCVSetFieldBase(tif, TIFFTAG_LERC_PARAMETERS, + 2, params); + } + case TIFFTAG_LERC_ADD_COMPRESSION: + { + int params[2] = {0, 0}; + int additional_compression = va_arg(ap, int); +#ifndef ZSTD_SUPPORT + if( additional_compression == LERC_ADD_COMPRESSION_ZSTD ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "LERC_ZSTD requested, but ZSTD not available"); + return 0; + } +#endif + if( additional_compression != LERC_ADD_COMPRESSION_NONE && + additional_compression != LERC_ADD_COMPRESSION_DEFLATE && + additional_compression != LERC_ADD_COMPRESSION_ZSTD ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid value for LercAdditionalCompression: %d", + additional_compression); + return 0; + } + sp->additional_compression = additional_compression; + params[0] = sp->lerc_version; + params[1] = sp->additional_compression; + return LERCVSetFieldBase(tif, TIFFTAG_LERC_PARAMETERS, + 2, params); + } +#ifdef ZSTD_SUPPORT + case TIFFTAG_ZSTD_LEVEL: + { + sp->zstd_compress_level = (int) va_arg(ap, int); + if( sp->zstd_compress_level <= 0 || + sp->zstd_compress_level > ZSTD_maxCLevel() ) + { + TIFFWarningExt(tif->tif_clientdata, module, + "ZSTD_LEVEL should be between 1 and %d", + ZSTD_maxCLevel()); + } + return 1; + } +#endif + case TIFFTAG_ZIPQUALITY: + { + sp->zipquality = (int) va_arg(ap, int); + 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 LIBDEFLATE_SUPPORT + if( sp->libdeflate_enc ) + { + libdeflate_free_compressor(sp->libdeflate_enc); + sp->libdeflate_enc = NULL; + } +#endif + + return (1); + } + default: + return (*sp->vsetparent)(tif, tag, ap); + } + /*NOTREACHED*/ +} + +static int +LERCVGetField(TIFF* tif, uint32_t tag, va_list ap) +{ + LERCState* sp = LState(tif); + + switch (tag) { + case TIFFTAG_LERC_MAXZERROR: + *va_arg(ap, double*) = sp->maxzerror; + break; + case TIFFTAG_LERC_VERSION: + *va_arg(ap, int*) = sp->lerc_version; + break; + case TIFFTAG_LERC_ADD_COMPRESSION: + *va_arg(ap, int*) = sp->additional_compression; + break; + case TIFFTAG_ZSTD_LEVEL: + *va_arg(ap, int*) = sp->zstd_compress_level; + break; + case TIFFTAG_ZIPQUALITY: + *va_arg(ap, int*) = sp->zipquality; + break; + default: + return (*sp->vgetparent)(tif, tag, ap); + } + return 1; +} + +int TIFFInitLERC(TIFF* tif, int scheme) +{ + static const char module[] = "TIFFInitLERC"; + LERCState* sp; + + (void) scheme; + assert( scheme == COMPRESSION_LERC ); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, LERCFields, TIFFArrayCount(LERCFields))) { + TIFFErrorExt(tif->tif_clientdata, module, + "Merging LERC codec-specific tags failed"); + return 0; + } + + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8_t*) _TIFFcalloc(1, sizeof(LERCState)); + 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 = LERCVGetField; /* hook for codec tags */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = LERCVSetField; /* hook for codec tags */ + + /* + * Install codec methods. + */ + tif->tif_fixuptags = LERCFixupTags; + tif->tif_setupdecode = LERCSetupDecode; + tif->tif_predecode = LERCPreDecode; + tif->tif_decoderow = LERCDecode; + tif->tif_decodestrip = LERCDecode; + tif->tif_decodetile = LERCDecode; + tif->tif_setupencode = LERCSetupEncode; + tif->tif_preencode = LERCPreEncode; + tif->tif_postencode = LERCPostEncode; + tif->tif_encoderow = LERCEncode; + tif->tif_encodestrip = LERCEncode; + tif->tif_encodetile = LERCEncode; + tif->tif_cleanup = LERCCleanup; + + /* Default values for codec-specific fields */ + TIFFSetField(tif, TIFFTAG_LERC_VERSION, LERC_VERSION_2_4); + TIFFSetField(tif, TIFFTAG_LERC_ADD_COMPRESSION, LERC_ADD_COMPRESSION_NONE); + sp->maxzerror = 0.0; + sp->zstd_compress_level = 9; /* default comp. level */ + sp->zipquality = Z_DEFAULT_COMPRESSION; /* default comp. level */ + sp->state = 0; + + return 1; +bad: + TIFFErrorExt(tif->tif_clientdata, module, + "No space for LERC state block"); + return 0; +} +#endif /* LERC_SUPPORT */ |