mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1206 lines
36 KiB
1206 lines
36 KiB
/* |
|
* 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; |
|
} |
|
|
|
TIFFErrorExtR( |
|
tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, module, "Too large uncompressed strip/tile"); |
|
_TIFFfreeExt(tif, sp->uncompressed_buffer); |
|
sp->uncompressed_buffer = 0; |
|
sp->uncompressed_alloc = 0; |
|
return 0; |
|
} |
|
|
|
if (sp->uncompressed_alloc < new_alloc) |
|
{ |
|
_TIFFfreeExt(tif, sp->uncompressed_buffer); |
|
sp->uncompressed_buffer = _TIFFmallocExt(tif, new_alloc); |
|
if (!sp->uncompressed_buffer) |
|
{ |
|
TIFFErrorExtR(tif, module, "Cannot allocate buffer"); |
|
_TIFFfreeExt(tif, 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 = |
|
_TIFFreallocExt(tif, sp->mask_buffer, mask_size); |
|
if (mask_buffer == NULL) |
|
{ |
|
TIFFErrorExtR(tif, module, "Cannot allocate buffer"); |
|
sp->mask_size = 0; |
|
_TIFFfreeExt(tif, 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) |
|
{ |
|
_TIFFfreeExt(tif, sp->compressed_buffer); |
|
sp->compressed_buffer = _TIFFmallocExt(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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)) |
|
{ |
|
TIFFErrorExtR(tif, 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 |
|
TIFFErrorExtR(tif, module, "ZSTD support missing"); |
|
return 0; |
|
#endif |
|
} |
|
else if (sp->additional_compression != LERC_ADD_COMPRESSION_NONE) |
|
{ |
|
TIFFErrorExtR(tif, module, "Unhandled additional compression"); |
|
return 0; |
|
} |
|
|
|
lerc_ret = |
|
lerc_getBlobInfo(lerc_data, lerc_data_size, infoArray, NULL, 8, 0); |
|
if (lerc_ret != 0) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFWarningExtR(tif, module, |
|
"Unexpected version number: %d. Expected: %d", |
|
infoArray[0], sp->lerc_version); |
|
} |
|
if (infoArray[1] != (unsigned)lerc_data_type) |
|
{ |
|
TIFFErrorExtR(tif, module, "Unexpected dataType: %d. Expected: %d", |
|
infoArray[1], lerc_data_type); |
|
return 0; |
|
} |
|
if (infoArray[2] != (unsigned)ndims) |
|
{ |
|
TIFFErrorExtR(tif, module, "Unexpected nDim: %d. Expected: %d", |
|
infoArray[2], ndims); |
|
return 0; |
|
} |
|
if (infoArray[3] != sp->segment_width) |
|
{ |
|
TIFFErrorExtR(tif, module, "Unexpected nCols: %d. Expected: %du", |
|
infoArray[3], sp->segment_width); |
|
return 0; |
|
} |
|
if (infoArray[4] != sp->segment_height) |
|
{ |
|
TIFFErrorExtR(tif, module, "Unexpected nRows: %d. Expected: %u", |
|
infoArray[4], sp->segment_height); |
|
return 0; |
|
} |
|
if (infoArray[5] != 1) |
|
{ |
|
TIFFErrorExtR(tif, module, "Unexpected nBands: %d. Expected: %d", |
|
infoArray[5], 1); |
|
return 0; |
|
} |
|
if (infoArray[7] != lerc_data_size) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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 WORDS_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) |
|
{ |
|
TIFFErrorExtR(tif, module, "Uncompressed buffer not allocated"); |
|
return 0; |
|
} |
|
|
|
if ((uint64_t)sp->uncompressed_offset + (uint64_t)occ > |
|
sp->uncompressed_size) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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 ) |
|
{ |
|
TIFFErrorExtR(tif, module, |
|
"lerc_computeCompressedSize() failed"); |
|
return 0; |
|
} |
|
#else |
|
numBytes = sp->uncompressed_alloc; |
|
#endif |
|
|
|
if (sp->compressed_size < numBytes) |
|
{ |
|
_TIFFfreeExt(tif, sp->compressed_buffer); |
|
sp->compressed_buffer = _TIFFmallocExt(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, module, "Cannot allocate compressor"); |
|
return 0; |
|
} |
|
} |
|
|
|
/* Should not happen normally */ |
|
if (libdeflate_zlib_compress_bound( |
|
sp->libdeflate_enc, numBytesWritten) > sp->uncompressed_alloc) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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)) |
|
{ |
|
TIFFErrorExtR(tif, 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 |
|
TIFFErrorExtR(tif, module, "ZSTD support missing"); |
|
return 0; |
|
#endif |
|
} |
|
else if (sp->additional_compression != LERC_ADD_COMPRESSION_NONE) |
|
{ |
|
TIFFErrorExtR(tif, 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; |
|
|
|
_TIFFfreeExt(tif, sp->uncompressed_buffer); |
|
_TIFFfreeExt(tif, sp->compressed_buffer); |
|
_TIFFfreeExt(tif, 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 |
|
|
|
_TIFFfreeExt(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR(tif, 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()) |
|
{ |
|
TIFFWarningExtR(tif, 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) |
|
{ |
|
TIFFErrorExtR( |
|
tif, 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))) |
|
{ |
|
TIFFErrorExtR(tif, 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 *)_TIFFcallocExt(tif, 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: |
|
TIFFErrorExtR(tif, module, "No space for LERC state block"); |
|
return 0; |
|
} |
|
#endif /* LERC_SUPPORT */
|
|
|