tinyexr: Sync with upstream 1.0.4

(cherry picked from commit ca55c455ad)
This commit is contained in:
bitsawer 2023-06-05 12:15:30 +03:00 committed by Rémi Verschelde
parent 3bc6822caa
commit 4746eaba83
No known key found for this signature in database
GPG key ID: C3336907360768E1
2 changed files with 298 additions and 110 deletions

View file

@ -536,7 +536,7 @@ comments and a patch is provided in the squish/ folder.
## tinyexr
- Upstream: https://github.com/syoyo/tinyexr
- Version: 1.0.2 (02310c77e5156c36fedf6cf810c4071e3f83906f, 2023)
- Version: 1.0.4 (7c92b8cd86a378ba5cb7b6d39a336457728dfb82, 2023)
- License: BSD-3-Clause
Files extracted from upstream source:

View file

@ -619,7 +619,6 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
#endif
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@ -684,6 +683,27 @@ extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len,
#endif
// cond: conditional expression
// msg: std::string
// err: std::string*
#define TINYEXR_CHECK_AND_RETURN_MSG(cond, msg, err) do { \
if (!(cond)) { \
if (!err) { \
std::ostringstream ss_e; \
ss_e << __func__ << "():" << __LINE__ << msg << "\n"; \
(*err) += ss_e.str(); \
} \
return false;\
} \
} while(0)
// no error message.
#define TINYEXR_CHECK_AND_RETURN_C(cond, retcode) do { \
if (!(cond)) { \
return retcode; \
} \
} while(0)
namespace tinyexr {
#if __cplusplus > 199711L
@ -1558,7 +1578,7 @@ static int rleUncompress(int inLength, int maxLength, const signed char in[],
// End of RLE code from OpenEXR -----------------------------------
static void CompressRle(unsigned char *dst,
static bool CompressRle(unsigned char *dst,
tinyexr::tinyexr_uint64 &compressedSize,
const unsigned char *src, unsigned long src_size) {
std::vector<unsigned char> tmpBuf(src_size);
@ -1613,7 +1633,7 @@ static void CompressRle(unsigned char *dst,
int outSize = rleCompress(static_cast<int>(src_size),
reinterpret_cast<const char *>(&tmpBuf.at(0)),
reinterpret_cast<signed char *>(dst));
assert(outSize > 0);
TINYEXR_CHECK_AND_RETURN_C(outSize > 0, false);
compressedSize = static_cast<tinyexr::tinyexr_uint64>(outSize);
@ -1623,6 +1643,8 @@ static void CompressRle(unsigned char *dst,
compressedSize = src_size;
memcpy(dst, src, src_size);
}
return true;
}
static bool DecompressRle(unsigned char *dst,
@ -2162,7 +2184,7 @@ struct FHeapCompare {
bool operator()(long long *a, long long *b) { return *a > *b; }
};
static void hufBuildEncTable(
static bool hufBuildEncTable(
long long *frq, // io: input frequencies [HUF_ENCSIZE], output table
int *im, // o: min frq index
int *iM) // o: max frq index
@ -2290,7 +2312,7 @@ static void hufBuildEncTable(
for (int j = m;; j = hlink[j]) {
scode[j]++;
assert(scode[j] <= 58);
TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false);
if (hlink[j] == j) {
//
@ -2309,7 +2331,7 @@ static void hufBuildEncTable(
for (int j = mm;; j = hlink[j]) {
scode[j]++;
assert(scode[j] <= 58);
TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false);
if (hlink[j] == j) break;
}
@ -2323,6 +2345,8 @@ static void hufBuildEncTable(
hufCanonicalCodeTable(scode.data());
memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE);
return true;
}
//
@ -3034,7 +3058,6 @@ static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize,
#if !TINYEXR_LITTLE_ENDIAN
// @todo { PIZ compression on BigEndian architecture. }
assert(0);
return false;
#endif
@ -3160,7 +3183,6 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
#if !TINYEXR_LITTLE_ENDIAN
// @todo { PIZ compression on BigEndian architecture. }
assert(0);
return false;
#endif
@ -3200,8 +3222,14 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
ptr += maxNonZero - minNonZero + 1;
readLen += maxNonZero - minNonZero + 1;
} else {
// Issue 194
if ((minNonZero == (BITMAP_SIZE - 1)) && (maxNonZero == 0)) {
// OK. all pixels are zero. And no need to read `bitmap` data.
} else {
// invalid minNonZero/maxNonZero combination.
return false;
}
}
std::vector<unsigned short> lut(USHORT_RANGE);
memset(lut.data(), 0, sizeof(unsigned short) * USHORT_RANGE);
@ -3211,12 +3239,12 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
// Huffman decoding
//
int length;
if ((readLen + 4) > inLen) {
return false;
}
int length=0;
// length = *(reinterpret_cast<const int *>(ptr));
tinyexr::cpy4(&length, reinterpret_cast<const int *>(ptr));
ptr += sizeof(int);
@ -3396,8 +3424,8 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines,
zfp_stream *zfp = NULL;
zfp_field *field = NULL;
assert((dst_width % 4) == 0);
assert((dst_num_lines % 4) == 0);
TINYEXR_CHECK_AND_RETURN_C((dst_width % 4) == 0, false);
TINYEXR_CHECK_AND_RETURN_C((dst_num_lines % 4) == 0, false);
if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) {
return false;
@ -3418,7 +3446,7 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines,
} else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
zfp_stream_set_accuracy(zfp, param.tolerance);
} else {
assert(0);
return false;
}
size_t buf_size = zfp_stream_maximum_size(zfp, field);
@ -3462,8 +3490,8 @@ static bool CompressZfp(std::vector<unsigned char> *outBuf,
zfp_stream *zfp = NULL;
zfp_field *field = NULL;
assert((width % 4) == 0);
assert((num_lines % 4) == 0);
TINYEXR_CHECK_AND_RETURN_C((width % 4) == 0, false);
TINYEXR_CHECK_AND_RETURN_C((num_lines % 4) == 0, false);
if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) {
return false;
@ -3483,7 +3511,7 @@ static bool CompressZfp(std::vector<unsigned char> *outBuf,
} else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
zfp_stream_set_accuracy(zfp, param.tolerance);
} else {
assert(0);
return false;
}
size_t buf_size = zfp_stream_maximum_size(zfp, field);
@ -3620,7 +3648,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT);
TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false);
for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const unsigned int *line_ptr = reinterpret_cast<unsigned int *>(
@ -3649,7 +3677,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT);
TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>(&outBuf.at(
v * pixel_data_size * static_cast<size_t>(width) +
@ -3676,11 +3704,10 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else {
assert(0);
return false;
}
}
#else
assert(0 && "PIZ is disabled in this build");
return false;
#endif
@ -3692,7 +3719,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
pixel_data_size);
unsigned long dstLen = static_cast<unsigned long>(outBuf.size());
assert(dstLen > 0);
TINYEXR_CHECK_AND_RETURN_C(dstLen > 0, false);
if (!tinyexr::DecompressZip(
reinterpret_cast<unsigned char *>(&outBuf.at(0)), &dstLen, data_ptr,
static_cast<unsigned long>(data_len))) {
@ -3759,7 +3786,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT);
TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false);
for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const unsigned int *line_ptr = reinterpret_cast<unsigned int *>(
@ -3788,7 +3815,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT);
TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>(
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
@ -3815,7 +3842,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else {
assert(0);
return false;
}
}
@ -3893,7 +3919,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT);
TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false);
for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const unsigned int *line_ptr = reinterpret_cast<unsigned int *>(
@ -3922,7 +3948,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT);
TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>(
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
@ -3949,7 +3975,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else {
assert(0);
return false;
}
}
@ -3960,7 +3985,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes,
int(num_attributes), &e)) {
// This code path should not be reachable.
assert(0);
return false;
}
@ -3970,7 +3994,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
pixel_data_size);
unsigned long dstLen = outBuf.size();
assert(dstLen > 0);
TINYEXR_CHECK_AND_RETURN_C(dstLen > 0, false);
tinyexr::DecompressZfp(reinterpret_cast<float *>(&outBuf.at(0)), width,
num_lines, num_channels, data_ptr,
static_cast<unsigned long>(data_len),
@ -3987,9 +4011,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
// pixel sample data for channel n for scanline 1
// ...
for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
assert(channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT);
TINYEXR_CHECK_AND_RETURN_C(channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT, false);
if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT);
TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>(
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
@ -4015,7 +4039,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
}
}
} else {
assert(0);
return false;
}
}
@ -4023,7 +4046,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
(void)attributes;
(void)num_attributes;
(void)num_channels;
assert(0);
return false;
#endif
} else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
@ -4084,7 +4106,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
outLine[u] = f32.f;
}
} else {
assert(0);
return false;
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
@ -4245,7 +4266,9 @@ static unsigned char **AllocateImage(int num_channels,
images[c] = reinterpret_cast<unsigned char *>(
static_cast<float *>(malloc(sizeof(float) * data_len)));
} else {
assert(0);
images[c] = NULL; // just in case.
valid = false;
break;
}
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
// pixel_data_size += sizeof(float);
@ -4400,7 +4423,6 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header,
return TINYEXR_ERROR_INVALID_DATA;
}
assert(data.size() == 9);
memcpy(&x_size, &data.at(0), sizeof(int));
memcpy(&y_size, &data.at(4), sizeof(int));
tile_mode = data[8];
@ -4787,6 +4809,7 @@ struct OffsetData {
int num_y_levels;
};
// -1 = error
static int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) {
switch (tile_level_mode) {
case TINYEXR_TILE_ONE_LEVEL:
@ -4799,13 +4822,15 @@ static int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) {
return lx + ly * num_x_levels;
default:
assert(false);
return -1;
}
return 0;
}
static int LevelSize(int toplevel_size, int level, int tile_rounding_mode) {
assert(level >= 0);
if (level < 0) {
return -1;
}
int b = static_cast<int>(1u << static_cast<unsigned int>(level));
int level_size = toplevel_size / b;
@ -4826,9 +4851,13 @@ static int DecodeTiledLevel(EXRImage* exr_image, const EXRHeader* exr_header,
int level_index = LevelIndex(exr_image->level_x, exr_image->level_y, exr_header->tile_level_mode, offset_data.num_x_levels);
int num_y_tiles = int(offset_data.offsets[size_t(level_index)].size());
assert(num_y_tiles);
if (num_y_tiles < 1) {
return TINYEXR_ERROR_INVALID_DATA;
}
int num_x_tiles = int(offset_data.offsets[size_t(level_index)][0].size());
assert(num_x_tiles);
if (num_x_tiles < 1) {
return TINYEXR_ERROR_INVALID_DATA;
}
int num_tiles = num_x_tiles * num_y_tiles;
int err_code = TINYEXR_SUCCESS;
@ -5026,10 +5055,24 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
return TINYEXR_ERROR_INVALID_DATA;
}
int data_width =
exr_header->data_window.max_x - exr_header->data_window.min_x + 1;
int data_height =
exr_header->data_window.max_y - exr_header->data_window.min_y + 1;
tinyexr_int64 data_width =
static_cast<tinyexr_int64>(exr_header->data_window.max_x) - static_cast<tinyexr_int64>(exr_header->data_window.min_x) + static_cast<tinyexr_int64>(1);
tinyexr_int64 data_height =
static_cast<tinyexr_int64>(exr_header->data_window.max_y) - static_cast<tinyexr_int64>(exr_header->data_window.min_y) + static_cast<tinyexr_int64>(1);
if (data_width <= 0) {
if (err) {
(*err) += "Invalid data window width.\n";
}
return TINYEXR_ERROR_INVALID_DATA;
}
if (data_height <= 0) {
if (err) {
(*err) += "Invalid data window height.\n";
}
return TINYEXR_ERROR_INVALID_DATA;
}
// Do not allow too large data_width and data_height. header invalid?
{
@ -5109,8 +5152,17 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
}
level_image->width =
LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level, exr_header->tile_rounding_mode);
if (level_image->width < 1) {
return TINYEXR_ERROR_INVALID_DATA;
}
level_image->height =
LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level, exr_header->tile_rounding_mode);
if (level_image->height < 1) {
return TINYEXR_ERROR_INVALID_DATA;
}
level_image->level_x = level;
level_image->level_y = level;
@ -5136,8 +5188,16 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
level_image->width =
LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level_x, exr_header->tile_rounding_mode);
if (level_image->width < 1) {
return TINYEXR_ERROR_INVALID_DATA;
}
level_image->height =
LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level_y, exr_header->tile_rounding_mode);
if (level_image->height < 1) {
return TINYEXR_ERROR_INVALID_DATA;
}
level_image->level_x = level_x;
level_image->level_y = level_y;
@ -5171,7 +5231,7 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
bool alloc_success = false;
exr_image->images = tinyexr::AllocateImage(
num_channels, exr_header->channels, exr_header->requested_pixel_types,
data_width, data_height, &alloc_success);
int(data_width), int(data_height), &alloc_success);
if (!alloc_success) {
if (err) {
@ -5271,7 +5331,7 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
exr_image->images, exr_header->requested_pixel_types,
data_ptr, static_cast<size_t>(data_len),
exr_header->compression_type, exr_header->line_order,
data_width, data_height, data_width, y, line_no,
int(data_width), int(data_height), int(data_width), y, line_no,
num_lines, static_cast<size_t>(pixel_data_size),
static_cast<size_t>(
exr_header->num_custom_attributes),
@ -5323,8 +5383,8 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
{
exr_image->num_channels = num_channels;
exr_image->width = data_width;
exr_image->height = data_height;
exr_image->width = int(data_width);
exr_image->height = int(data_height);
}
return TINYEXR_SUCCESS;
@ -5333,8 +5393,12 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
static bool ReconstructLineOffsets(
std::vector<tinyexr::tinyexr_uint64> *offsets, size_t n,
const unsigned char *head, const unsigned char *marker, const size_t size) {
assert(head < marker);
assert(offsets->size() == n);
if (head >= marker) {
return false;
}
if (offsets->size() != n) {
return false;
}
for (size_t i = 0; i < n; i++) {
size_t offset = static_cast<size_t>(marker - head);
@ -5430,7 +5494,7 @@ static int CalculateNumXLevels(const EXRHeader* exr_header) {
default:
assert(false);
return -1;
}
return num;
@ -5468,25 +5532,29 @@ static int CalculateNumYLevels(const EXRHeader* exr_header) {
default:
assert(false);
return -1;
}
return num;
}
static void CalculateNumTiles(std::vector<int>& numTiles,
static bool CalculateNumTiles(std::vector<int>& numTiles,
int toplevel_size,
int size,
int tile_rounding_mode) {
for (unsigned i = 0; i < numTiles.size(); i++) {
int l = LevelSize(toplevel_size, int(i), tile_rounding_mode);
assert(l <= std::numeric_limits<int>::max() - size + 1);
if (l < 0) {
return false;
}
TINYEXR_CHECK_AND_RETURN_C(l <= std::numeric_limits<int>::max() - size + 1, false);
numTiles[i] = (l + size - 1) / size;
}
return true;
}
static void PrecalculateTileInfo(std::vector<int>& num_x_tiles,
static bool PrecalculateTileInfo(std::vector<int>& num_x_tiles,
std::vector<int>& num_y_tiles,
const EXRHeader* exr_header) {
int min_x = exr_header->data_window.min_x;
@ -5495,20 +5563,35 @@ static void PrecalculateTileInfo(std::vector<int>& num_x_tiles,
int max_y = exr_header->data_window.max_y;
int num_x_levels = CalculateNumXLevels(exr_header);
if (num_x_levels < 0) {
return false;
}
int num_y_levels = CalculateNumYLevels(exr_header);
if (num_y_levels < 0) {
return false;
}
num_x_tiles.resize(size_t(num_x_levels));
num_y_tiles.resize(size_t(num_y_levels));
CalculateNumTiles(num_x_tiles,
if (!CalculateNumTiles(num_x_tiles,
max_x - min_x + 1,
exr_header->tile_size_x,
exr_header->tile_rounding_mode);
exr_header->tile_rounding_mode)) {
return false;
}
CalculateNumTiles(num_y_tiles,
if (!CalculateNumTiles(num_y_tiles,
max_y - min_y + 1,
exr_header->tile_size_y,
exr_header->tile_rounding_mode);
exr_header->tile_rounding_mode)) {
return false;
}
return true;
}
static void InitSingleResolutionOffsets(OffsetData& offset_data, size_t num_blocks) {
@ -5520,6 +5603,7 @@ static void InitSingleResolutionOffsets(OffsetData& offset_data, size_t num_bloc
}
// Return sum of tile blocks.
// 0 = error
static int InitTileOffsets(OffsetData& offset_data,
const EXRHeader* exr_header,
const std::vector<int>& num_x_tiles,
@ -5530,7 +5614,7 @@ static int InitTileOffsets(OffsetData& offset_data,
switch (exr_header->tile_level_mode) {
case TINYEXR_TILE_ONE_LEVEL:
case TINYEXR_TILE_MIPMAP_LEVELS:
assert(offset_data.num_x_levels == offset_data.num_y_levels);
TINYEXR_CHECK_AND_RETURN_C(offset_data.num_x_levels == offset_data.num_y_levels, 0);
offset_data.offsets.resize(size_t(offset_data.num_x_levels));
for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
@ -5561,7 +5645,7 @@ static int InitTileOffsets(OffsetData& offset_data,
break;
default:
assert(false);
return 0;
}
return num_tile_blocks;
}
@ -5629,7 +5713,7 @@ static bool isValidTile(const EXRHeader* exr_header,
return false;
}
static void ReconstructTileOffsets(OffsetData& offset_data,
static bool ReconstructTileOffsets(OffsetData& offset_data,
const EXRHeader* exr_header,
const unsigned char* head, const unsigned char* marker, const size_t /*size*/,
bool isMultiPartFile,
@ -5689,14 +5773,19 @@ static void ReconstructTileOffsets(OffsetData& offset_data,
}
if (!isValidTile(exr_header, offset_data,
tileX, tileY, levelX, levelY))
return;
tileX, tileY, levelX, levelY)) {
return false;
}
int level_idx = LevelIndex(levelX, levelY, exr_header->tile_level_mode, numXLevels);
if (level_idx < 0) {
return false;
}
offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset;
}
}
}
return true;
}
// marker output is also
@ -5754,8 +5843,12 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
tinyexr::SetErrorMessage("Invalid data width value", err);
return TINYEXR_ERROR_INVALID_DATA;
}
int data_width =
exr_header->data_window.max_x - exr_header->data_window.min_x + 1;
tinyexr_int64 data_width =
static_cast<tinyexr_int64>(exr_header->data_window.max_x) - static_cast<tinyexr_int64>(exr_header->data_window.min_x) + static_cast<tinyexr_int64>(1);
if (data_width <= 0) {
tinyexr::SetErrorMessage("Invalid data window width value", err);
return TINYEXR_ERROR_INVALID_DATA;
}
if (exr_header->data_window.max_y < exr_header->data_window.min_y ||
exr_header->data_window.max_y - exr_header->data_window.min_y ==
@ -5763,8 +5856,13 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
tinyexr::SetErrorMessage("Invalid data height value", err);
return TINYEXR_ERROR_INVALID_DATA;
}
int data_height =
exr_header->data_window.max_y - exr_header->data_window.min_y + 1;
tinyexr_int64 data_height =
static_cast<tinyexr_int64>(exr_header->data_window.max_y) - static_cast<tinyexr_int64>(exr_header->data_window.min_y) + static_cast<tinyexr_int64>(1);
if (data_height <= 0) {
tinyexr::SetErrorMessage("Invalid data window height value", err);
return TINYEXR_ERROR_INVALID_DATA;
}
// Do not allow too large data_width and data_height. header invalid?
{
@ -5797,7 +5895,10 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
if (exr_header->tiled) {
{
std::vector<int> num_x_tiles, num_y_tiles;
PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_header);
if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_header)) {
tinyexr::SetErrorMessage("Failed to precalculate tile info.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
num_blocks = size_t(InitTileOffsets(offset_data, exr_header, num_x_tiles, num_y_tiles));
if (exr_header->chunk_count > 0) {
if (exr_header->chunk_count != static_cast<int>(num_blocks)) {
@ -5810,9 +5911,13 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
int ret = ReadOffsets(offset_data, head, marker, size, err);
if (ret != TINYEXR_SUCCESS) return ret;
if (IsAnyOffsetsAreInvalid(offset_data)) {
ReconstructTileOffsets(offset_data, exr_header,
if (!ReconstructTileOffsets(offset_data, exr_header,
head, marker, size,
exr_header->multipart, exr_header->non_image);
exr_header->multipart, exr_header->non_image)) {
tinyexr::SetErrorMessage("Invalid Tile Offsets data.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
}
} else if (exr_header->chunk_count > 0) {
// Use `chunkCount` attribute.
@ -6638,6 +6743,7 @@ struct MemoryMappedFile {
data = reinterpret_cast<unsigned char *>(
mmap(0, size, PROT_READ, MAP_SHARED, posix_descriptor, 0));
if (data == MAP_FAILED) {
data = nullptr;
return;
}
#else
@ -6662,20 +6768,26 @@ struct MemoryMappedFile {
size = static_cast<size_t>(ftell_result);
if (fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp);
size = 0;
return;
}
data = reinterpret_cast<unsigned char *>(malloc(size));
if (!data) {
size = 0;
fclose(fp);
return;
}
size_t read_bytes = fread(data, 1, size, fp);
assert(read_bytes == size);
if (read_bytes != size) {
// TODO: Try to read data until reading `size` bytes.
fclose(fp);
size = 0;
data = nullptr;
return;
}
fclose(fp);
(void)read_bytes;
#endif
assert(valid());
}
// MemoryMappedFile's destructor closes all its handles.
@ -6966,9 +7078,14 @@ static bool EncodePixelData(/* out */ std::vector<unsigned char>& out_data,
tinyexr::tinyexr_uint64 outSize = block.size();
tinyexr::CompressRle(&block.at(0), outSize,
if (!tinyexr::CompressRle(&block.at(0), outSize,
reinterpret_cast<const unsigned char *>(&buf.at(0)),
static_cast<unsigned long>(buf.size()));
static_cast<unsigned long>(buf.size()))) {
if (err) {
(*err) += "RLE compresssion failed.\n";
}
return false;
}
// 4 byte: scan line
// 4 byte: data size
@ -6985,9 +7102,14 @@ static bool EncodePixelData(/* out */ std::vector<unsigned char>& out_data,
std::vector<unsigned char> block(bufLen);
unsigned int outSize = static_cast<unsigned int>(block.size());
CompressPiz(&block.at(0), &outSize,
if (!CompressPiz(&block.at(0), &outSize,
reinterpret_cast<const unsigned char *>(&buf.at(0)),
buf.size(), channels, width, num_lines);
buf.size(), channels, width, num_lines)) {
if (err) {
(*err) += "PIZ compresssion failed.\n";
}
return false;
}
// 4 byte: scan line
// 4 byte: data size
@ -7041,7 +7163,12 @@ static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_he
const void* compression_param, // must be set if zfp compression is enabled
std::string* err) {
int num_tiles = num_x_tiles * num_y_tiles;
assert(num_tiles == level_image->num_tiles);
if (num_tiles != level_image->num_tiles) {
if (err) {
(*err) += "Invalid number of tiles in argument.\n";
}
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
if ((exr_header->tile_size_x > level_image->width || exr_header->tile_size_y > level_image->height) &&
level_image->level_x == 0 && level_image->level_y == 0) {
@ -7111,7 +7238,11 @@ static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_he
invalid_data = true;
continue;
}
assert(data_list[data_idx].size() > data_header_size);
if (data_list[data_idx].size() <= data_header_size) {
invalid_data = true;
continue;
}
int data_len = static_cast<int>(data_list[data_idx].size() - data_header_size);
//tileX, tileY, levelX, levelY // pixel_data_size(int)
memcpy(&data_list[data_idx][0], &x_tile, sizeof(int));
@ -7191,7 +7322,10 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header,
pixel_data_size += sizeof(unsigned int);
channel_offset += sizeof(unsigned int);
} else {
assert(0);
if (err) {
(*err) += "Invalid requested_pixel_type.\n";
}
return TINYEXR_ERROR_INVALID_DATA;
}
}
}
@ -7236,6 +7370,13 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header,
int level_index_from_image = LevelIndex(level_image->level_x, level_image->level_y,
exr_header->tile_level_mode, offset_data.num_x_levels);
if (level_index_from_image < 0) {
if (err) {
(*err) += "Invalid tile level mode\n";
}
return TINYEXR_ERROR_INVALID_DATA;
}
if (level_index_from_image != level_index) {
if (err) {
(*err) += "Incorrect level ordering in tiled image\n";
@ -7243,9 +7384,20 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header,
return TINYEXR_ERROR_INVALID_DATA;
}
int num_y_tiles = int(offset_data.offsets[level_index].size());
assert(num_y_tiles);
if (num_y_tiles <= 0) {
if (err) {
(*err) += "Invalid Y tile size\n";
}
return TINYEXR_ERROR_INVALID_DATA;
}
int num_x_tiles = int(offset_data.offsets[level_index][0].size());
assert(num_x_tiles);
if (num_x_tiles <= 0) {
if (err) {
(*err) += "Invalid X tile size\n";
}
return TINYEXR_ERROR_INVALID_DATA;
}
std::string e;
int ret = EncodeTiledLevel(level_image,
@ -7276,7 +7428,7 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header,
}
level_image = level_image->next_level;
}
assert(static_cast<int>(block_idx) == num_blocks);
TINYEXR_CHECK_AND_RETURN_C(static_cast<int>(block_idx) == num_blocks, TINYEXR_ERROR_INVALID_DATA);
total_size = offset;
} else { // scanlines
std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0];
@ -7329,7 +7481,10 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header,
invalid_data = true;
continue; // "break" cannot be used with OpenMP
}
assert(data_list[i].size() > data_header_size);
if (data_list[i].size() <= data_header_size) {
invalid_data = true;
continue; // "break" cannot be used with OpenMP
}
int data_len = static_cast<int>(data_list[i].size() - data_header_size);
memcpy(&data_list[i][0], &start_y, sizeof(int));
memcpy(&data_list[i][4], &data_len, sizeof(int));
@ -7455,9 +7610,20 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
} else {
{
std::vector<int> num_x_tiles, num_y_tiles;
PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i]);
chunk_count[i] =
InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles);
if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) {
SetErrorMessage("Failed to precalculate Tile info",
err);
return TINYEXR_ERROR_INVALID_DATA;
}
int ntiles = InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles);
if (ntiles > 0) {
chunk_count[i] = ntiles;
} else {
SetErrorMessage("Failed to compute Tile offsets",
err);
return TINYEXR_ERROR_INVALID_DATA;
}
total_chunk_count += chunk_count[i];
}
}
@ -7657,7 +7823,7 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
// Allocating required memory
if (total_size == 0) { // something went wrong
tinyexr::SetErrorMessage("Output memory size is zero", err);
return 0;
return TINYEXR_ERROR_INVALID_DATA;
}
(*memory_out) = static_cast<unsigned char*>(malloc(size_t(total_size)));
@ -7676,7 +7842,11 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
for (size_t j = 0; j < offset_data[i].offsets[level_index].size(); ++j) {
size_t num_bytes = sizeof(tinyexr_uint64) * offset_data[i].offsets[level_index][j].size();
sum += num_bytes;
assert(sum <= total_size);
if (sum > total_size) {
tinyexr::SetErrorMessage("Invalid offset bytes in Tiled Part image.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
memcpy(memory_ptr,
reinterpret_cast<unsigned char*>(&offset_data[i].offsets[level_index][j][0]),
num_bytes);
@ -7687,7 +7857,10 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
} else {
size_t num_bytes = sizeof(tinyexr::tinyexr_uint64) * static_cast<size_t>(chunk_count[i]);
sum += num_bytes;
assert(sum <= total_size);
if (sum > total_size) {
tinyexr::SetErrorMessage("Invalid offset bytes in Part image.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data[i].offsets[0][0];
memcpy(memory_ptr, reinterpret_cast<unsigned char*>(&offsets[0]), num_bytes);
memory_ptr += num_bytes;
@ -7699,19 +7872,30 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
for (size_t j = 0; j < static_cast<size_t>(chunk_count[i]); ++j) {
if (num_parts > 1) {
sum += 4;
assert(sum <= total_size);
if (sum > total_size) {
tinyexr::SetErrorMessage("Buffer overrun in reading Part image chunk data.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
unsigned int part_number = i;
swap4(&part_number);
memcpy(memory_ptr, &part_number, 4);
memory_ptr += 4;
}
sum += data_lists[i][j].size();
assert(sum <= total_size);
if (sum > total_size) {
tinyexr::SetErrorMessage("Buffer overrun in reading Part image chunk data.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
memcpy(memory_ptr, &data_lists[i][j][0], data_lists[i][j].size());
memory_ptr += data_lists[i][j].size();
}
}
assert(sum == total_size);
if (sum != total_size) {
tinyexr::SetErrorMessage("Corrupted Part image chunk data.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
return size_t(total_size); // OK
}
@ -8004,11 +8188,11 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
}
}
assert(dx >= 0);
assert(dy >= 0);
assert(dw >= 0);
assert(dh >= 0);
assert(num_channels >= 1);
TINYEXR_CHECK_AND_RETURN_C(dx >= 0, TINYEXR_ERROR_INVALID_DATA);
TINYEXR_CHECK_AND_RETURN_C(dy >= 0, TINYEXR_ERROR_INVALID_DATA);
TINYEXR_CHECK_AND_RETURN_C(dw >= 0, TINYEXR_ERROR_INVALID_DATA);
TINYEXR_CHECK_AND_RETURN_C(dh >= 0, TINYEXR_ERROR_INVALID_DATA);
TINYEXR_CHECK_AND_RETURN_C(num_channels >= 1, TINYEXR_ERROR_INVALID_DATA);
int data_width = dw - dx + 1;
int data_height = dh - dy + 1;
@ -8106,7 +8290,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
return false;
}
assert(dstLen == pixelOffsetTable.size() * sizeof(int));
TINYEXR_CHECK_AND_RETURN_C(dstLen == pixelOffsetTable.size() * sizeof(int), TINYEXR_ERROR_INVALID_DATA);
for (size_t i = 0; i < static_cast<size_t>(data_width); i++) {
deep_image->offset_table[y][i] = pixelOffsetTable[i];
}
@ -8125,7 +8309,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
static_cast<unsigned long>(packedSampleDataSize))) {
return false;
}
assert(dstLen == static_cast<unsigned long>(unpackedSampleDataSize));
TINYEXR_CHECK_AND_RETURN_C(dstLen == static_cast<unsigned long>(unpackedSampleDataSize), TINYEXR_ERROR_INVALID_DATA);
}
}
@ -8144,16 +8328,17 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
TINYEXR_PIXELTYPE_FLOAT) { // float
channel_offset += 4;
} else {
assert(0);
tinyexr::SetErrorMessage("Invalid pixel_type in chnnels.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
}
sampleSize = channel_offset;
}
assert(sampleSize >= 2);
TINYEXR_CHECK_AND_RETURN_C(sampleSize >= 2, TINYEXR_ERROR_INVALID_DATA);
assert(static_cast<size_t>(
TINYEXR_CHECK_AND_RETURN_C(static_cast<size_t>(
pixelOffsetTable[static_cast<size_t>(data_width - 1)] *
sampleSize) == sample_data.size());
sampleSize) == sample_data.size(), TINYEXR_ERROR_INVALID_DATA);
int samples_per_line = static_cast<int>(sample_data.size()) / sampleSize;
//
@ -8656,7 +8841,10 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images,
} else {
{
std::vector<int> num_x_tiles, num_y_tiles;
tinyexr::PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i]);
if (!tinyexr::PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) {
tinyexr::SetErrorMessage("Invalid tile info.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
int num_blocks = InitTileOffsets(offset_data, exr_headers[i], num_x_tiles, num_y_tiles);
if (num_blocks != exr_headers[i]->chunk_count) {
tinyexr::SetErrorMessage("Invalid offset table size.", err);