tinyexr: Sync with upstream 1.0.4

This commit is contained in:
bitsawer 2023-06-05 12:15:30 +03:00
parent 543750a1b3
commit ca55c455ad
2 changed files with 298 additions and 110 deletions

View File

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

View File

@ -619,7 +619,6 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
#endif #endif
#include <algorithm> #include <algorithm>
#include <cassert>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -684,6 +683,27 @@ extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len,
#endif #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 { namespace tinyexr {
#if __cplusplus > 199711L #if __cplusplus > 199711L
@ -1558,7 +1578,7 @@ static int rleUncompress(int inLength, int maxLength, const signed char in[],
// End of RLE code from OpenEXR ----------------------------------- // End of RLE code from OpenEXR -----------------------------------
static void CompressRle(unsigned char *dst, static bool CompressRle(unsigned char *dst,
tinyexr::tinyexr_uint64 &compressedSize, tinyexr::tinyexr_uint64 &compressedSize,
const unsigned char *src, unsigned long src_size) { const unsigned char *src, unsigned long src_size) {
std::vector<unsigned char> tmpBuf(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), int outSize = rleCompress(static_cast<int>(src_size),
reinterpret_cast<const char *>(&tmpBuf.at(0)), reinterpret_cast<const char *>(&tmpBuf.at(0)),
reinterpret_cast<signed char *>(dst)); reinterpret_cast<signed char *>(dst));
assert(outSize > 0); TINYEXR_CHECK_AND_RETURN_C(outSize > 0, false);
compressedSize = static_cast<tinyexr::tinyexr_uint64>(outSize); compressedSize = static_cast<tinyexr::tinyexr_uint64>(outSize);
@ -1623,6 +1643,8 @@ static void CompressRle(unsigned char *dst,
compressedSize = src_size; compressedSize = src_size;
memcpy(dst, src, src_size); memcpy(dst, src, src_size);
} }
return true;
} }
static bool DecompressRle(unsigned char *dst, static bool DecompressRle(unsigned char *dst,
@ -2162,7 +2184,7 @@ struct FHeapCompare {
bool operator()(long long *a, long long *b) { return *a > *b; } 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 long long *frq, // io: input frequencies [HUF_ENCSIZE], output table
int *im, // o: min frq index int *im, // o: min frq index
int *iM) // o: max frq index int *iM) // o: max frq index
@ -2290,7 +2312,7 @@ static void hufBuildEncTable(
for (int j = m;; j = hlink[j]) { for (int j = m;; j = hlink[j]) {
scode[j]++; scode[j]++;
assert(scode[j] <= 58); TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false);
if (hlink[j] == j) { if (hlink[j] == j) {
// //
@ -2309,7 +2331,7 @@ static void hufBuildEncTable(
for (int j = mm;; j = hlink[j]) { for (int j = mm;; j = hlink[j]) {
scode[j]++; scode[j]++;
assert(scode[j] <= 58); TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false);
if (hlink[j] == j) break; if (hlink[j] == j) break;
} }
@ -2323,6 +2345,8 @@ static void hufBuildEncTable(
hufCanonicalCodeTable(scode.data()); hufCanonicalCodeTable(scode.data());
memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE); 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 #if !TINYEXR_LITTLE_ENDIAN
// @todo { PIZ compression on BigEndian architecture. } // @todo { PIZ compression on BigEndian architecture. }
assert(0);
return false; return false;
#endif #endif
@ -3160,7 +3183,6 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
#if !TINYEXR_LITTLE_ENDIAN #if !TINYEXR_LITTLE_ENDIAN
// @todo { PIZ compression on BigEndian architecture. } // @todo { PIZ compression on BigEndian architecture. }
assert(0);
return false; return false;
#endif #endif
@ -3200,7 +3222,13 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
ptr += maxNonZero - minNonZero + 1; ptr += maxNonZero - minNonZero + 1;
readLen += maxNonZero - minNonZero + 1; readLen += maxNonZero - minNonZero + 1;
} else { } else {
return false; // 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); std::vector<unsigned short> lut(USHORT_RANGE);
@ -3211,12 +3239,12 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
// Huffman decoding // Huffman decoding
// //
int length;
if ((readLen + 4) > inLen) { if ((readLen + 4) > inLen) {
return false; return false;
} }
int length=0;
// length = *(reinterpret_cast<const int *>(ptr)); // length = *(reinterpret_cast<const int *>(ptr));
tinyexr::cpy4(&length, reinterpret_cast<const int *>(ptr)); tinyexr::cpy4(&length, reinterpret_cast<const int *>(ptr));
ptr += sizeof(int); ptr += sizeof(int);
@ -3396,8 +3424,8 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines,
zfp_stream *zfp = NULL; zfp_stream *zfp = NULL;
zfp_field *field = NULL; zfp_field *field = NULL;
assert((dst_width % 4) == 0); TINYEXR_CHECK_AND_RETURN_C((dst_width % 4) == 0, false);
assert((dst_num_lines % 4) == 0); TINYEXR_CHECK_AND_RETURN_C((dst_num_lines % 4) == 0, false);
if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) { if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) {
return false; 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) { } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
zfp_stream_set_accuracy(zfp, param.tolerance); zfp_stream_set_accuracy(zfp, param.tolerance);
} else { } else {
assert(0); return false;
} }
size_t buf_size = zfp_stream_maximum_size(zfp, field); 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_stream *zfp = NULL;
zfp_field *field = NULL; zfp_field *field = NULL;
assert((width % 4) == 0); TINYEXR_CHECK_AND_RETURN_C((width % 4) == 0, false);
assert((num_lines % 4) == 0); TINYEXR_CHECK_AND_RETURN_C((num_lines % 4) == 0, false);
if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) { if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) {
return false; return false;
@ -3483,7 +3511,7 @@ static bool CompressZfp(std::vector<unsigned char> *outBuf,
} else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
zfp_stream_set_accuracy(zfp, param.tolerance); zfp_stream_set_accuracy(zfp, param.tolerance);
} else { } else {
assert(0); return false;
} }
size_t buf_size = zfp_stream_maximum_size(zfp, field); 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) { } 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++) { for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const unsigned int *line_ptr = reinterpret_cast<unsigned int *>( 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) { } 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++) { for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>(&outBuf.at( const float *line_ptr = reinterpret_cast<float *>(&outBuf.at(
v * pixel_data_size * static_cast<size_t>(width) + v * pixel_data_size * static_cast<size_t>(width) +
@ -3676,11 +3704,10 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
} }
} }
} else { } else {
assert(0); return false;
} }
} }
#else #else
assert(0 && "PIZ is disabled in this build");
return false; return false;
#endif #endif
@ -3692,7 +3719,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
pixel_data_size); pixel_data_size);
unsigned long dstLen = static_cast<unsigned long>(outBuf.size()); unsigned long dstLen = static_cast<unsigned long>(outBuf.size());
assert(dstLen > 0); TINYEXR_CHECK_AND_RETURN_C(dstLen > 0, false);
if (!tinyexr::DecompressZip( if (!tinyexr::DecompressZip(
reinterpret_cast<unsigned char *>(&outBuf.at(0)), &dstLen, data_ptr, reinterpret_cast<unsigned char *>(&outBuf.at(0)), &dstLen, data_ptr,
static_cast<unsigned long>(data_len))) { 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) { } 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++) { for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const unsigned int *line_ptr = reinterpret_cast<unsigned int *>( 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) { } 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++) { for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>( const float *line_ptr = reinterpret_cast<float *>(
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
@ -3815,7 +3842,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
} }
} }
} else { } else {
assert(0);
return false; return false;
} }
} }
@ -3893,7 +3919,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
} }
} }
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { } 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++) { for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const unsigned int *line_ptr = reinterpret_cast<unsigned int *>( 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) { } 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++) { for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>( const float *line_ptr = reinterpret_cast<float *>(
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
@ -3949,7 +3975,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
} }
} }
} else { } else {
assert(0);
return false; return false;
} }
} }
@ -3960,7 +3985,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes, if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes,
int(num_attributes), &e)) { int(num_attributes), &e)) {
// This code path should not be reachable. // This code path should not be reachable.
assert(0);
return false; return false;
} }
@ -3970,7 +3994,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
pixel_data_size); pixel_data_size);
unsigned long dstLen = outBuf.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, tinyexr::DecompressZfp(reinterpret_cast<float *>(&outBuf.at(0)), width,
num_lines, num_channels, data_ptr, num_lines, num_channels, data_ptr,
static_cast<unsigned long>(data_len), 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 // pixel sample data for channel n for scanline 1
// ... // ...
for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) { 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) { 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++) { for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
const float *line_ptr = reinterpret_cast<float *>( const float *line_ptr = reinterpret_cast<float *>(
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
@ -4015,7 +4039,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
} }
} }
} else { } else {
assert(0);
return false; return false;
} }
} }
@ -4023,7 +4046,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
(void)attributes; (void)attributes;
(void)num_attributes; (void)num_attributes;
(void)num_channels; (void)num_channels;
assert(0);
return false; return false;
#endif #endif
} else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) { } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
@ -4084,7 +4106,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
outLine[u] = f32.f; outLine[u] = f32.f;
} }
} else { } else {
assert(0);
return false; return false;
} }
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { } 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 *>( images[c] = reinterpret_cast<unsigned char *>(
static_cast<float *>(malloc(sizeof(float) * data_len))); static_cast<float *>(malloc(sizeof(float) * data_len)));
} else { } else {
assert(0); images[c] = NULL; // just in case.
valid = false;
break;
} }
} else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
// pixel_data_size += sizeof(float); // pixel_data_size += sizeof(float);
@ -4400,7 +4423,6 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header,
return TINYEXR_ERROR_INVALID_DATA; return TINYEXR_ERROR_INVALID_DATA;
} }
assert(data.size() == 9);
memcpy(&x_size, &data.at(0), sizeof(int)); memcpy(&x_size, &data.at(0), sizeof(int));
memcpy(&y_size, &data.at(4), sizeof(int)); memcpy(&y_size, &data.at(4), sizeof(int));
tile_mode = data[8]; tile_mode = data[8];
@ -4787,6 +4809,7 @@ struct OffsetData {
int num_y_levels; int num_y_levels;
}; };
// -1 = error
static int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) { static int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) {
switch (tile_level_mode) { switch (tile_level_mode) {
case TINYEXR_TILE_ONE_LEVEL: 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; return lx + ly * num_x_levels;
default: default:
assert(false); return -1;
} }
return 0; return 0;
} }
static int LevelSize(int toplevel_size, int level, int tile_rounding_mode) { 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 b = static_cast<int>(1u << static_cast<unsigned int>(level));
int level_size = toplevel_size / b; 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 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()); 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()); 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 num_tiles = num_x_tiles * num_y_tiles;
int err_code = TINYEXR_SUCCESS; int err_code = TINYEXR_SUCCESS;
@ -5026,10 +5055,24 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
return TINYEXR_ERROR_INVALID_DATA; return TINYEXR_ERROR_INVALID_DATA;
} }
int data_width = tinyexr_int64 data_width =
exr_header->data_window.max_x - exr_header->data_window.min_x + 1; 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);
int data_height = tinyexr_int64 data_height =
exr_header->data_window.max_y - exr_header->data_window.min_y + 1; 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? // 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 = level_image->width =
LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level, exr_header->tile_rounding_mode); 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 = level_image->height =
LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level, exr_header->tile_rounding_mode); 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_x = level;
level_image->level_y = level; level_image->level_y = level;
@ -5136,8 +5188,16 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
level_image->width = level_image->width =
LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level_x, exr_header->tile_rounding_mode); 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 = level_image->height =
LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level_y, exr_header->tile_rounding_mode); 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_x = level_x;
level_image->level_y = level_y; level_image->level_y = level_y;
@ -5171,7 +5231,7 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
bool alloc_success = false; bool alloc_success = false;
exr_image->images = tinyexr::AllocateImage( exr_image->images = tinyexr::AllocateImage(
num_channels, exr_header->channels, exr_header->requested_pixel_types, 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 (!alloc_success) {
if (err) { if (err) {
@ -5271,7 +5331,7 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
exr_image->images, exr_header->requested_pixel_types, exr_image->images, exr_header->requested_pixel_types,
data_ptr, static_cast<size_t>(data_len), data_ptr, static_cast<size_t>(data_len),
exr_header->compression_type, exr_header->line_order, 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), num_lines, static_cast<size_t>(pixel_data_size),
static_cast<size_t>( static_cast<size_t>(
exr_header->num_custom_attributes), 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->num_channels = num_channels;
exr_image->width = data_width; exr_image->width = int(data_width);
exr_image->height = data_height; exr_image->height = int(data_height);
} }
return TINYEXR_SUCCESS; return TINYEXR_SUCCESS;
@ -5333,8 +5393,12 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
static bool ReconstructLineOffsets( static bool ReconstructLineOffsets(
std::vector<tinyexr::tinyexr_uint64> *offsets, size_t n, std::vector<tinyexr::tinyexr_uint64> *offsets, size_t n,
const unsigned char *head, const unsigned char *marker, const size_t size) { const unsigned char *head, const unsigned char *marker, const size_t size) {
assert(head < marker); if (head >= marker) {
assert(offsets->size() == n); return false;
}
if (offsets->size() != n) {
return false;
}
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
size_t offset = static_cast<size_t>(marker - head); size_t offset = static_cast<size_t>(marker - head);
@ -5430,7 +5494,7 @@ static int CalculateNumXLevels(const EXRHeader* exr_header) {
default: default:
assert(false); return -1;
} }
return num; return num;
@ -5468,25 +5532,29 @@ static int CalculateNumYLevels(const EXRHeader* exr_header) {
default: default:
assert(false); return -1;
} }
return num; return num;
} }
static void CalculateNumTiles(std::vector<int>& numTiles, static bool CalculateNumTiles(std::vector<int>& numTiles,
int toplevel_size, int toplevel_size,
int size, int size,
int tile_rounding_mode) { int tile_rounding_mode) {
for (unsigned i = 0; i < numTiles.size(); i++) { for (unsigned i = 0; i < numTiles.size(); i++) {
int l = LevelSize(toplevel_size, int(i), tile_rounding_mode); 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; 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, std::vector<int>& num_y_tiles,
const EXRHeader* exr_header) { const EXRHeader* exr_header) {
int min_x = exr_header->data_window.min_x; 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 max_y = exr_header->data_window.max_y;
int num_x_levels = CalculateNumXLevels(exr_header); int num_x_levels = CalculateNumXLevels(exr_header);
if (num_x_levels < 0) {
return false;
}
int num_y_levels = CalculateNumYLevels(exr_header); int num_y_levels = CalculateNumYLevels(exr_header);
if (num_y_levels < 0) {
return false;
}
num_x_tiles.resize(size_t(num_x_levels)); num_x_tiles.resize(size_t(num_x_levels));
num_y_tiles.resize(size_t(num_y_levels)); num_y_tiles.resize(size_t(num_y_levels));
CalculateNumTiles(num_x_tiles, if (!CalculateNumTiles(num_x_tiles,
max_x - min_x + 1, max_x - min_x + 1,
exr_header->tile_size_x, 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, max_y - min_y + 1,
exr_header->tile_size_y, 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) { 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. // Return sum of tile blocks.
// 0 = error
static int InitTileOffsets(OffsetData& offset_data, static int InitTileOffsets(OffsetData& offset_data,
const EXRHeader* exr_header, const EXRHeader* exr_header,
const std::vector<int>& num_x_tiles, const std::vector<int>& num_x_tiles,
@ -5530,7 +5614,7 @@ static int InitTileOffsets(OffsetData& offset_data,
switch (exr_header->tile_level_mode) { switch (exr_header->tile_level_mode) {
case TINYEXR_TILE_ONE_LEVEL: case TINYEXR_TILE_ONE_LEVEL:
case TINYEXR_TILE_MIPMAP_LEVELS: 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)); offset_data.offsets.resize(size_t(offset_data.num_x_levels));
for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) { for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
@ -5561,7 +5645,7 @@ static int InitTileOffsets(OffsetData& offset_data,
break; break;
default: default:
assert(false); return 0;
} }
return num_tile_blocks; return num_tile_blocks;
} }
@ -5629,7 +5713,7 @@ static bool isValidTile(const EXRHeader* exr_header,
return false; return false;
} }
static void ReconstructTileOffsets(OffsetData& offset_data, static bool ReconstructTileOffsets(OffsetData& offset_data,
const EXRHeader* exr_header, const EXRHeader* exr_header,
const unsigned char* head, const unsigned char* marker, const size_t /*size*/, const unsigned char* head, const unsigned char* marker, const size_t /*size*/,
bool isMultiPartFile, bool isMultiPartFile,
@ -5689,14 +5773,19 @@ static void ReconstructTileOffsets(OffsetData& offset_data,
} }
if (!isValidTile(exr_header, offset_data, if (!isValidTile(exr_header, offset_data,
tileX, tileY, levelX, levelY)) tileX, tileY, levelX, levelY)) {
return; return false;
}
int level_idx = LevelIndex(levelX, levelY, exr_header->tile_level_mode, numXLevels); 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; offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset;
} }
} }
} }
return true;
} }
// marker output is also // 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); tinyexr::SetErrorMessage("Invalid data width value", err);
return TINYEXR_ERROR_INVALID_DATA; return TINYEXR_ERROR_INVALID_DATA;
} }
int data_width = tinyexr_int64 data_width =
exr_header->data_window.max_x - exr_header->data_window.min_x + 1; 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 || 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 == 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); tinyexr::SetErrorMessage("Invalid data height value", err);
return TINYEXR_ERROR_INVALID_DATA; return TINYEXR_ERROR_INVALID_DATA;
} }
int data_height = tinyexr_int64 data_height =
exr_header->data_window.max_y - exr_header->data_window.min_y + 1; 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? // 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) { if (exr_header->tiled) {
{ {
std::vector<int> num_x_tiles, num_y_tiles; 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)); 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 > 0) {
if (exr_header->chunk_count != static_cast<int>(num_blocks)) { 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); int ret = ReadOffsets(offset_data, head, marker, size, err);
if (ret != TINYEXR_SUCCESS) return ret; if (ret != TINYEXR_SUCCESS) return ret;
if (IsAnyOffsetsAreInvalid(offset_data)) { if (IsAnyOffsetsAreInvalid(offset_data)) {
ReconstructTileOffsets(offset_data, exr_header, if (!ReconstructTileOffsets(offset_data, exr_header,
head, marker, size, 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) { } else if (exr_header->chunk_count > 0) {
// Use `chunkCount` attribute. // Use `chunkCount` attribute.
@ -6638,6 +6743,7 @@ struct MemoryMappedFile {
data = reinterpret_cast<unsigned char *>( data = reinterpret_cast<unsigned char *>(
mmap(0, size, PROT_READ, MAP_SHARED, posix_descriptor, 0)); mmap(0, size, PROT_READ, MAP_SHARED, posix_descriptor, 0));
if (data == MAP_FAILED) { if (data == MAP_FAILED) {
data = nullptr;
return; return;
} }
#else #else
@ -6662,20 +6768,26 @@ struct MemoryMappedFile {
size = static_cast<size_t>(ftell_result); size = static_cast<size_t>(ftell_result);
if (fseek(fp, 0, SEEK_SET) != 0) { if (fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp); fclose(fp);
size = 0;
return; return;
} }
data = reinterpret_cast<unsigned char *>(malloc(size)); data = reinterpret_cast<unsigned char *>(malloc(size));
if (!data) { if (!data) {
size = 0;
fclose(fp); fclose(fp);
return; return;
} }
size_t read_bytes = fread(data, 1, size, fp); 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); fclose(fp);
(void)read_bytes;
#endif #endif
assert(valid());
} }
// MemoryMappedFile's destructor closes all its handles. // 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::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)), 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: scan line
// 4 byte: data size // 4 byte: data size
@ -6985,9 +7102,14 @@ static bool EncodePixelData(/* out */ std::vector<unsigned char>& out_data,
std::vector<unsigned char> block(bufLen); std::vector<unsigned char> block(bufLen);
unsigned int outSize = static_cast<unsigned int>(block.size()); 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)), 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: scan line
// 4 byte: data size // 4 byte: data size
@ -7041,14 +7163,19 @@ static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_he
const void* compression_param, // must be set if zfp compression is enabled const void* compression_param, // must be set if zfp compression is enabled
std::string* err) { std::string* err) {
int num_tiles = num_x_tiles * num_y_tiles; 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) && 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) { level_image->level_x == 0 && level_image->level_y == 0) {
if (err) { if (err) {
(*err) += "Failed to encode tile data.\n"; (*err) += "Failed to encode tile data.\n";
} }
return TINYEXR_ERROR_INVALID_DATA; return TINYEXR_ERROR_INVALID_DATA;
} }
@ -7111,7 +7238,11 @@ static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_he
invalid_data = true; invalid_data = true;
continue; 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); int data_len = static_cast<int>(data_list[data_idx].size() - data_header_size);
//tileX, tileY, levelX, levelY // pixel_data_size(int) //tileX, tileY, levelX, levelY // pixel_data_size(int)
memcpy(&data_list[data_idx][0], &x_tile, sizeof(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); pixel_data_size += sizeof(unsigned int);
channel_offset += sizeof(unsigned int); channel_offset += sizeof(unsigned int);
} else { } 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, int level_index_from_image = LevelIndex(level_image->level_x, level_image->level_y,
exr_header->tile_level_mode, offset_data.num_x_levels); 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 (level_index_from_image != level_index) {
if (err) { if (err) {
(*err) += "Incorrect level ordering in tiled image\n"; (*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; return TINYEXR_ERROR_INVALID_DATA;
} }
int num_y_tiles = int(offset_data.offsets[level_index].size()); 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()); 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; std::string e;
int ret = EncodeTiledLevel(level_image, 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; 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; total_size = offset;
} else { // scanlines } else { // scanlines
std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0]; 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; invalid_data = true;
continue; // "break" cannot be used with OpenMP 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); 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][0], &start_y, sizeof(int));
memcpy(&data_list[i][4], &data_len, sizeof(int)); memcpy(&data_list[i][4], &data_len, sizeof(int));
@ -7455,9 +7610,20 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
} else { } else {
{ {
std::vector<int> num_x_tiles, num_y_tiles; std::vector<int> num_x_tiles, num_y_tiles;
PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i]); if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) {
chunk_count[i] = SetErrorMessage("Failed to precalculate Tile info",
InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles); 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]; total_chunk_count += chunk_count[i];
} }
} }
@ -7657,7 +7823,7 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
// Allocating required memory // Allocating required memory
if (total_size == 0) { // something went wrong if (total_size == 0) { // something went wrong
tinyexr::SetErrorMessage("Output memory size is zero", err); 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))); (*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) { 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(); size_t num_bytes = sizeof(tinyexr_uint64) * offset_data[i].offsets[level_index][j].size();
sum += num_bytes; 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, memcpy(memory_ptr,
reinterpret_cast<unsigned char*>(&offset_data[i].offsets[level_index][j][0]), reinterpret_cast<unsigned char*>(&offset_data[i].offsets[level_index][j][0]),
num_bytes); num_bytes);
@ -7687,7 +7857,10 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
} else { } else {
size_t num_bytes = sizeof(tinyexr::tinyexr_uint64) * static_cast<size_t>(chunk_count[i]); size_t num_bytes = sizeof(tinyexr::tinyexr_uint64) * static_cast<size_t>(chunk_count[i]);
sum += num_bytes; 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]; std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data[i].offsets[0][0];
memcpy(memory_ptr, reinterpret_cast<unsigned char*>(&offsets[0]), num_bytes); memcpy(memory_ptr, reinterpret_cast<unsigned char*>(&offsets[0]), num_bytes);
memory_ptr += 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) { for (size_t j = 0; j < static_cast<size_t>(chunk_count[i]); ++j) {
if (num_parts > 1) { if (num_parts > 1) {
sum += 4; 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; unsigned int part_number = i;
swap4(&part_number); swap4(&part_number);
memcpy(memory_ptr, &part_number, 4); memcpy(memory_ptr, &part_number, 4);
memory_ptr += 4; memory_ptr += 4;
} }
sum += data_lists[i][j].size(); 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()); memcpy(memory_ptr, &data_lists[i][j][0], data_lists[i][j].size());
memory_ptr += 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 return size_t(total_size); // OK
} }
@ -8004,11 +8188,11 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
} }
} }
assert(dx >= 0); TINYEXR_CHECK_AND_RETURN_C(dx >= 0, TINYEXR_ERROR_INVALID_DATA);
assert(dy >= 0); TINYEXR_CHECK_AND_RETURN_C(dy >= 0, TINYEXR_ERROR_INVALID_DATA);
assert(dw >= 0); TINYEXR_CHECK_AND_RETURN_C(dw >= 0, TINYEXR_ERROR_INVALID_DATA);
assert(dh >= 0); TINYEXR_CHECK_AND_RETURN_C(dh >= 0, TINYEXR_ERROR_INVALID_DATA);
assert(num_channels >= 1); TINYEXR_CHECK_AND_RETURN_C(num_channels >= 1, TINYEXR_ERROR_INVALID_DATA);
int data_width = dw - dx + 1; int data_width = dw - dx + 1;
int data_height = dh - dy + 1; int data_height = dh - dy + 1;
@ -8106,7 +8290,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
return false; 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++) { for (size_t i = 0; i < static_cast<size_t>(data_width); i++) {
deep_image->offset_table[y][i] = pixelOffsetTable[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))) { static_cast<unsigned long>(packedSampleDataSize))) {
return false; 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 TINYEXR_PIXELTYPE_FLOAT) { // float
channel_offset += 4; channel_offset += 4;
} else { } else {
assert(0); tinyexr::SetErrorMessage("Invalid pixel_type in chnnels.", err);
return TINYEXR_ERROR_INVALID_DATA;
} }
} }
sampleSize = channel_offset; 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)] * 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; int samples_per_line = static_cast<int>(sample_data.size()) / sampleSize;
// //
@ -8656,7 +8841,10 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images,
} else { } else {
{ {
std::vector<int> num_x_tiles, num_y_tiles; 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); int num_blocks = InitTileOffsets(offset_data, exr_headers[i], num_x_tiles, num_y_tiles);
if (num_blocks != exr_headers[i]->chunk_count) { if (num_blocks != exr_headers[i]->chunk_count) {
tinyexr::SetErrorMessage("Invalid offset table size.", err); tinyexr::SetErrorMessage("Invalid offset table size.", err);