tinyexr: Sync with upstream 4dbd05a
This commit is contained in:
parent
d29514acce
commit
3a80fce8be
|
@ -533,7 +533,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: git (656bb61, 2019)
|
- Version: git (4dbd05a22f51a2d7462311569b8b0cba0bbe2ac5, 2020)
|
||||||
- License: BSD-3-Clause
|
- License: BSD-3-Clause
|
||||||
|
|
||||||
Files extracted from upstream source:
|
Files extracted from upstream source:
|
||||||
|
|
|
@ -105,6 +105,19 @@ extern "C" {
|
||||||
// http://computation.llnl.gov/projects/floating-point-compression
|
// http://computation.llnl.gov/projects/floating-point-compression
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef TINYEXR_USE_THREAD
|
||||||
|
#define TINYEXR_USE_THREAD (0) // No threaded loading.
|
||||||
|
// http://computation.llnl.gov/projects/floating-point-compression
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TINYEXR_USE_OPENMP
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#define TINYEXR_USE_OPENMP (1)
|
||||||
|
#else
|
||||||
|
#define TINYEXR_USE_OPENMP (0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TINYEXR_SUCCESS (0)
|
#define TINYEXR_SUCCESS (0)
|
||||||
#define TINYEXR_ERROR_INVALID_MAGIC_NUMBER (-1)
|
#define TINYEXR_ERROR_INVALID_MAGIC_NUMBER (-1)
|
||||||
#define TINYEXR_ERROR_INVALID_EXR_VERSION (-2)
|
#define TINYEXR_ERROR_INVALID_EXR_VERSION (-2)
|
||||||
|
@ -118,6 +131,7 @@ extern "C" {
|
||||||
#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-10)
|
#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-10)
|
||||||
#define TINYEXR_ERROR_CANT_WRITE_FILE (-11)
|
#define TINYEXR_ERROR_CANT_WRITE_FILE (-11)
|
||||||
#define TINYEXR_ERROR_SERIALZATION_FAILED (-12)
|
#define TINYEXR_ERROR_SERIALZATION_FAILED (-12)
|
||||||
|
#define TINYEXR_ERROR_LAYER_NOT_FOUND (-13)
|
||||||
|
|
||||||
// @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf }
|
// @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf }
|
||||||
|
|
||||||
|
@ -263,7 +277,7 @@ typedef struct _DeepImage {
|
||||||
int pad0;
|
int pad0;
|
||||||
} DeepImage;
|
} DeepImage;
|
||||||
|
|
||||||
// @deprecated { to be removed. }
|
// @deprecated { For backward compatibility. Not recommended to use. }
|
||||||
// Loads single-frame OpenEXR image. Assume EXR image contains A(single channel
|
// Loads single-frame OpenEXR image. Assume EXR image contains A(single channel
|
||||||
// alpha) or RGB(A) channels.
|
// alpha) or RGB(A) channels.
|
||||||
// Application must free image data as returned by `out_rgba`
|
// Application must free image data as returned by `out_rgba`
|
||||||
|
@ -273,6 +287,27 @@ typedef struct _DeepImage {
|
||||||
extern int LoadEXR(float **out_rgba, int *width, int *height,
|
extern int LoadEXR(float **out_rgba, int *width, int *height,
|
||||||
const char *filename, const char **err);
|
const char *filename, const char **err);
|
||||||
|
|
||||||
|
// Loads single-frame OpenEXR image by specifing layer name. Assume EXR image contains A(single channel
|
||||||
|
// alpha) or RGB(A) channels.
|
||||||
|
// Application must free image data as returned by `out_rgba`
|
||||||
|
// Result image format is: float x RGBA x width x hight
|
||||||
|
// Returns negative value and may set error string in `err` when there's an
|
||||||
|
// error
|
||||||
|
// When the specified layer name is not found in the EXR file, the function will return `TINYEXR_ERROR_LAYER_NOT_FOUND`.
|
||||||
|
extern int LoadEXRWithLayer(float **out_rgba, int *width, int *height,
|
||||||
|
const char *filename, const char *layer_name, const char **err);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get layer infos from EXR file.
|
||||||
|
//
|
||||||
|
// @param[out] layer_names List of layer names. Application must free memory after using this.
|
||||||
|
// @param[out] num_layers The number of layers
|
||||||
|
// @param[out] err Error string(wll be filled when the function returns error code). Free it using FreeEXRErrorMessage after using this value.
|
||||||
|
//
|
||||||
|
// @return TINYEXR_SUCCEES upon success.
|
||||||
|
//
|
||||||
|
extern int EXRLayers(const char *filename, const char **layer_names[], int *num_layers, const char **err);
|
||||||
|
|
||||||
// @deprecated { to be removed. }
|
// @deprecated { to be removed. }
|
||||||
// Simple wrapper API for ParseEXRHeaderFromFile.
|
// Simple wrapper API for ParseEXRHeaderFromFile.
|
||||||
// checking given file is a EXR file(by just look up header)
|
// checking given file is a EXR file(by just look up header)
|
||||||
|
@ -472,7 +507,7 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
//#include <iostream> // debug
|
// #include <iostream> // debug
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -481,9 +516,15 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
|
||||||
#if __cplusplus > 199711L
|
#if __cplusplus > 199711L
|
||||||
// C++11
|
// C++11
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if TINYEXR_USE_THREAD
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // __cplusplus > 199711L
|
#endif // __cplusplus > 199711L
|
||||||
|
|
||||||
#ifdef _OPENMP
|
#if TINYEXR_USE_OPENMP
|
||||||
#include <omp.h>
|
#include <omp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -6917,6 +6958,10 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // MINIZ_HEADER_FILE_ONLY
|
#endif // MINIZ_HEADER_FILE_ONLY
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -6952,9 +6997,6 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename,
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
} // namespace miniz
|
} // namespace miniz
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
@ -9954,9 +9996,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tinyexr::DecompressRle(reinterpret_cast<unsigned char *>(&outBuf.at(0)),
|
if (!tinyexr::DecompressRle(
|
||||||
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))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10272,7 +10314,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DecodeTiledPixelData(
|
static bool DecodeTiledPixelData(
|
||||||
unsigned char **out_images, int *width, int *height,
|
unsigned char **out_images, int *width, int *height,
|
||||||
const int *requested_pixel_types, const unsigned char *data_ptr,
|
const int *requested_pixel_types, const unsigned char *data_ptr,
|
||||||
size_t data_len, int compression_type, int line_order, int data_width,
|
size_t data_len, int compression_type, int line_order, int data_width,
|
||||||
|
@ -10298,11 +10340,11 @@ static void DecodeTiledPixelData(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image size = tile size.
|
// Image size = tile size.
|
||||||
DecodePixelData(out_images, requested_pixel_types, data_ptr, data_len,
|
return DecodePixelData(out_images, requested_pixel_types, data_ptr, data_len,
|
||||||
compression_type, line_order, (*width), tile_size_y,
|
compression_type, line_order, (*width), tile_size_y,
|
||||||
/* stride */ tile_size_x, /* y */ 0, /* line_no */ 0,
|
/* stride */ tile_size_x, /* y */ 0, /* line_no */ 0,
|
||||||
(*height), pixel_data_size, num_attributes, attributes,
|
(*height), pixel_data_size, num_attributes, attributes,
|
||||||
num_channels, channels, channel_offset_list);
|
num_channels, channels, channel_offset_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ComputeChannelLayout(std::vector<size_t> *channel_offset_list,
|
static bool ComputeChannelLayout(std::vector<size_t> *channel_offset_list,
|
||||||
|
@ -10851,85 +10893,141 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
|
||||||
exr_image->tiles = static_cast<EXRTile *>(
|
exr_image->tiles = static_cast<EXRTile *>(
|
||||||
calloc(sizeof(EXRTile), static_cast<size_t>(num_tiles)));
|
calloc(sizeof(EXRTile), static_cast<size_t>(num_tiles)));
|
||||||
|
|
||||||
for (size_t tile_idx = 0; tile_idx < num_tiles; tile_idx++) {
|
int err_code = TINYEXR_SUCCESS;
|
||||||
// Allocate memory for each tile.
|
|
||||||
exr_image->tiles[tile_idx].images = tinyexr::AllocateImage(
|
|
||||||
num_channels, exr_header->channels, exr_header->requested_pixel_types,
|
|
||||||
exr_header->tile_size_x, exr_header->tile_size_y);
|
|
||||||
|
|
||||||
// 16 byte: tile coordinates
|
#if (__cplusplus > 199711L) && (TINYEXR_USE_THREAD > 0)
|
||||||
// 4 byte : data size
|
|
||||||
// ~ : data(uncompressed or compressed)
|
|
||||||
if (offsets[tile_idx] + sizeof(int) * 5 > size) {
|
|
||||||
if (err) {
|
|
||||||
(*err) += "Insufficient data size.\n";
|
|
||||||
}
|
|
||||||
return TINYEXR_ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t data_size = size_t(size - (offsets[tile_idx] + sizeof(int) * 5));
|
std::vector<std::thread> workers;
|
||||||
const unsigned char *data_ptr =
|
std::atomic<size_t> tile_count(0);
|
||||||
reinterpret_cast<const unsigned char *>(head + offsets[tile_idx]);
|
|
||||||
|
|
||||||
int tile_coordinates[4];
|
int num_threads = std::max(1, int(std::thread::hardware_concurrency()));
|
||||||
memcpy(tile_coordinates, data_ptr, sizeof(int) * 4);
|
if (num_threads > int(num_tiles)) {
|
||||||
tinyexr::swap4(reinterpret_cast<unsigned int *>(&tile_coordinates[0]));
|
num_threads = int(num_tiles);
|
||||||
tinyexr::swap4(reinterpret_cast<unsigned int *>(&tile_coordinates[1]));
|
|
||||||
tinyexr::swap4(reinterpret_cast<unsigned int *>(&tile_coordinates[2]));
|
|
||||||
tinyexr::swap4(reinterpret_cast<unsigned int *>(&tile_coordinates[3]));
|
|
||||||
|
|
||||||
// @todo{ LoD }
|
|
||||||
if (tile_coordinates[2] != 0) {
|
|
||||||
return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
|
|
||||||
}
|
|
||||||
if (tile_coordinates[3] != 0) {
|
|
||||||
return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int data_len;
|
|
||||||
memcpy(&data_len, data_ptr + 16,
|
|
||||||
sizeof(int)); // 16 = sizeof(tile_coordinates)
|
|
||||||
tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
|
|
||||||
|
|
||||||
if (data_len < 4 || size_t(data_len) > data_size) {
|
|
||||||
if (err) {
|
|
||||||
(*err) += "Insufficient data length.\n";
|
|
||||||
}
|
|
||||||
return TINYEXR_ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to data addr: 20 = 16 + 4;
|
|
||||||
data_ptr += 20;
|
|
||||||
|
|
||||||
tinyexr::DecodeTiledPixelData(
|
|
||||||
exr_image->tiles[tile_idx].images,
|
|
||||||
&(exr_image->tiles[tile_idx].width),
|
|
||||||
&(exr_image->tiles[tile_idx].height),
|
|
||||||
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, tile_coordinates[0],
|
|
||||||
tile_coordinates[1], exr_header->tile_size_x, exr_header->tile_size_y,
|
|
||||||
static_cast<size_t>(pixel_data_size),
|
|
||||||
static_cast<size_t>(exr_header->num_custom_attributes),
|
|
||||||
exr_header->custom_attributes,
|
|
||||||
static_cast<size_t>(exr_header->num_channels), exr_header->channels,
|
|
||||||
channel_offset_list);
|
|
||||||
|
|
||||||
exr_image->tiles[tile_idx].offset_x = tile_coordinates[0];
|
|
||||||
exr_image->tiles[tile_idx].offset_y = tile_coordinates[1];
|
|
||||||
exr_image->tiles[tile_idx].level_x = tile_coordinates[2];
|
|
||||||
exr_image->tiles[tile_idx].level_y = tile_coordinates[3];
|
|
||||||
|
|
||||||
exr_image->num_tiles = static_cast<int>(num_tiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int t = 0; t < num_threads; t++) {
|
||||||
|
workers.emplace_back(std::thread([&]() {
|
||||||
|
size_t tile_idx = 0;
|
||||||
|
while ((tile_idx = tile_count++) < num_tiles) {
|
||||||
|
|
||||||
|
#else
|
||||||
|
for (size_t tile_idx = 0; tile_idx < num_tiles; tile_idx++) {
|
||||||
|
#endif
|
||||||
|
// Allocate memory for each tile.
|
||||||
|
exr_image->tiles[tile_idx].images = tinyexr::AllocateImage(
|
||||||
|
num_channels, exr_header->channels,
|
||||||
|
exr_header->requested_pixel_types, exr_header->tile_size_x,
|
||||||
|
exr_header->tile_size_y);
|
||||||
|
|
||||||
|
// 16 byte: tile coordinates
|
||||||
|
// 4 byte : data size
|
||||||
|
// ~ : data(uncompressed or compressed)
|
||||||
|
if (offsets[tile_idx] + sizeof(int) * 5 > size) {
|
||||||
|
// TODO(LTE): atomic
|
||||||
|
if (err) {
|
||||||
|
(*err) += "Insufficient data size.\n";
|
||||||
|
}
|
||||||
|
err_code = TINYEXR_ERROR_INVALID_DATA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t data_size =
|
||||||
|
size_t(size - (offsets[tile_idx] + sizeof(int) * 5));
|
||||||
|
const unsigned char *data_ptr =
|
||||||
|
reinterpret_cast<const unsigned char *>(head + offsets[tile_idx]);
|
||||||
|
|
||||||
|
int tile_coordinates[4];
|
||||||
|
memcpy(tile_coordinates, data_ptr, sizeof(int) * 4);
|
||||||
|
tinyexr::swap4(
|
||||||
|
reinterpret_cast<unsigned int *>(&tile_coordinates[0]));
|
||||||
|
tinyexr::swap4(
|
||||||
|
reinterpret_cast<unsigned int *>(&tile_coordinates[1]));
|
||||||
|
tinyexr::swap4(
|
||||||
|
reinterpret_cast<unsigned int *>(&tile_coordinates[2]));
|
||||||
|
tinyexr::swap4(
|
||||||
|
reinterpret_cast<unsigned int *>(&tile_coordinates[3]));
|
||||||
|
|
||||||
|
// @todo{ LoD }
|
||||||
|
if (tile_coordinates[2] != 0) {
|
||||||
|
err_code = TINYEXR_ERROR_UNSUPPORTED_FEATURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tile_coordinates[3] != 0) {
|
||||||
|
err_code = TINYEXR_ERROR_UNSUPPORTED_FEATURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int data_len;
|
||||||
|
memcpy(&data_len, data_ptr + 16,
|
||||||
|
sizeof(int)); // 16 = sizeof(tile_coordinates)
|
||||||
|
tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
|
||||||
|
|
||||||
|
if (data_len < 4 || size_t(data_len) > data_size) {
|
||||||
|
// TODO(LTE): atomic
|
||||||
|
if (err) {
|
||||||
|
(*err) += "Insufficient data length.\n";
|
||||||
|
}
|
||||||
|
err_code = TINYEXR_ERROR_INVALID_DATA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to data addr: 20 = 16 + 4;
|
||||||
|
data_ptr += 20;
|
||||||
|
|
||||||
|
bool ret = tinyexr::DecodeTiledPixelData(
|
||||||
|
exr_image->tiles[tile_idx].images,
|
||||||
|
&(exr_image->tiles[tile_idx].width),
|
||||||
|
&(exr_image->tiles[tile_idx].height),
|
||||||
|
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,
|
||||||
|
tile_coordinates[0], tile_coordinates[1], exr_header->tile_size_x,
|
||||||
|
exr_header->tile_size_y, static_cast<size_t>(pixel_data_size),
|
||||||
|
static_cast<size_t>(exr_header->num_custom_attributes),
|
||||||
|
exr_header->custom_attributes,
|
||||||
|
static_cast<size_t>(exr_header->num_channels),
|
||||||
|
exr_header->channels, channel_offset_list);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
// TODO(LTE): atomic
|
||||||
|
if (err) {
|
||||||
|
(*err) += "Failed to decode tile data.\n";
|
||||||
|
}
|
||||||
|
err_code = TINYEXR_ERROR_INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
exr_image->tiles[tile_idx].offset_x = tile_coordinates[0];
|
||||||
|
exr_image->tiles[tile_idx].offset_y = tile_coordinates[1];
|
||||||
|
exr_image->tiles[tile_idx].level_x = tile_coordinates[2];
|
||||||
|
exr_image->tiles[tile_idx].level_y = tile_coordinates[3];
|
||||||
|
|
||||||
|
#if (__cplusplus > 199711L) && (TINYEXR_USE_THREAD > 0)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} // num_thread loop
|
||||||
|
|
||||||
|
for (auto &t : workers) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (err_code != TINYEXR_SUCCESS) {
|
||||||
|
return err_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
exr_image->num_tiles = static_cast<int>(num_tiles);
|
||||||
} else { // scanline format
|
} else { // scanline format
|
||||||
|
|
||||||
// Don't allow too large image(256GB * pixel_data_size or more). Workaround
|
// Don't allow too large image(256GB * pixel_data_size or more). Workaround
|
||||||
// for #104.
|
// for #104.
|
||||||
size_t total_data_len =
|
size_t total_data_len =
|
||||||
size_t(data_width) * size_t(data_height) * size_t(num_channels);
|
size_t(data_width) * size_t(data_height) * size_t(num_channels);
|
||||||
const bool total_data_len_overflown = sizeof(void*) == 8 ? (total_data_len >= 0x4000000000) : false;
|
const bool total_data_len_overflown =
|
||||||
if ((total_data_len == 0) || total_data_len_overflown ) {
|
sizeof(void *) == 8 ? (total_data_len >= 0x4000000000) : false;
|
||||||
|
if ((total_data_len == 0) || total_data_len_overflown) {
|
||||||
if (err) {
|
if (err) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Image data size is zero or too large: width = " << data_width
|
ss << "Image data size is zero or too large: width = " << data_width
|
||||||
|
@ -10944,85 +11042,118 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
|
||||||
num_channels, exr_header->channels, exr_header->requested_pixel_types,
|
num_channels, exr_header->channels, exr_header->requested_pixel_types,
|
||||||
data_width, data_height);
|
data_width, data_height);
|
||||||
|
|
||||||
#ifdef _OPENMP
|
#if (__cplusplus > 199711L) && (TINYEXR_USE_THREAD > 0)
|
||||||
|
std::vector<std::thread> workers;
|
||||||
|
std::atomic<int> y_count(0);
|
||||||
|
|
||||||
|
int num_threads = std::max(1, int(std::thread::hardware_concurrency()));
|
||||||
|
if (num_threads > int(num_blocks)) {
|
||||||
|
num_threads = int(num_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int t = 0; t < num_threads; t++) {
|
||||||
|
workers.emplace_back(std::thread([&]() {
|
||||||
|
int y = 0;
|
||||||
|
while ((y = y_count++) < int(num_blocks)) {
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if TINYEXR_USE_OPENMP
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
#endif
|
#endif
|
||||||
for (int y = 0; y < static_cast<int>(num_blocks); y++) {
|
for (int y = 0; y < static_cast<int>(num_blocks); y++) {
|
||||||
size_t y_idx = static_cast<size_t>(y);
|
|
||||||
|
|
||||||
if (offsets[y_idx] + sizeof(int) * 2 > size) {
|
#endif
|
||||||
invalid_data = true;
|
size_t y_idx = static_cast<size_t>(y);
|
||||||
} else {
|
|
||||||
// 4 byte: scan line
|
|
||||||
// 4 byte: data size
|
|
||||||
// ~ : pixel data(uncompressed or compressed)
|
|
||||||
size_t data_size = size_t(size - (offsets[y_idx] + sizeof(int) * 2));
|
|
||||||
const unsigned char *data_ptr =
|
|
||||||
reinterpret_cast<const unsigned char *>(head + offsets[y_idx]);
|
|
||||||
|
|
||||||
int line_no;
|
if (offsets[y_idx] + sizeof(int) * 2 > size) {
|
||||||
memcpy(&line_no, data_ptr, sizeof(int));
|
|
||||||
int data_len;
|
|
||||||
memcpy(&data_len, data_ptr + 4, sizeof(int));
|
|
||||||
tinyexr::swap4(reinterpret_cast<unsigned int *>(&line_no));
|
|
||||||
tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
|
|
||||||
|
|
||||||
if (size_t(data_len) > data_size) {
|
|
||||||
invalid_data = true;
|
|
||||||
|
|
||||||
} else if ((line_no > (2 << 20)) || (line_no < -(2 << 20))) {
|
|
||||||
// Too large value. Assume this is invalid
|
|
||||||
// 2**20 = 1048576 = heuristic value.
|
|
||||||
invalid_data = true;
|
|
||||||
} else if (data_len == 0) {
|
|
||||||
// TODO(syoyo): May be ok to raise the threshold for example `data_len
|
|
||||||
// < 4`
|
|
||||||
invalid_data = true;
|
|
||||||
} else {
|
|
||||||
// line_no may be negative.
|
|
||||||
int end_line_no = (std::min)(line_no + num_scanline_blocks,
|
|
||||||
(exr_header->data_window[3] + 1));
|
|
||||||
|
|
||||||
int num_lines = end_line_no - line_no;
|
|
||||||
|
|
||||||
if (num_lines <= 0) {
|
|
||||||
invalid_data = true;
|
invalid_data = true;
|
||||||
} else {
|
} else {
|
||||||
// Move to data addr: 8 = 4 + 4;
|
// 4 byte: scan line
|
||||||
data_ptr += 8;
|
// 4 byte: data size
|
||||||
|
// ~ : pixel data(uncompressed or compressed)
|
||||||
|
size_t data_size =
|
||||||
|
size_t(size - (offsets[y_idx] + sizeof(int) * 2));
|
||||||
|
const unsigned char *data_ptr =
|
||||||
|
reinterpret_cast<const unsigned char *>(head + offsets[y_idx]);
|
||||||
|
|
||||||
// Adjust line_no with data_window.bmin.y
|
int line_no;
|
||||||
|
memcpy(&line_no, data_ptr, sizeof(int));
|
||||||
|
int data_len;
|
||||||
|
memcpy(&data_len, data_ptr + 4, sizeof(int));
|
||||||
|
tinyexr::swap4(reinterpret_cast<unsigned int *>(&line_no));
|
||||||
|
tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
|
||||||
|
|
||||||
// overflow check
|
if (size_t(data_len) > data_size) {
|
||||||
tinyexr_int64 lno = static_cast<tinyexr_int64>(line_no) - static_cast<tinyexr_int64>(exr_header->data_window[1]);
|
invalid_data = true;
|
||||||
if (lno > std::numeric_limits<int>::max()) {
|
|
||||||
line_no = -1; // invalid
|
|
||||||
} else if (lno < -std::numeric_limits<int>::max()) {
|
|
||||||
line_no = -1; // invalid
|
|
||||||
} else {
|
|
||||||
line_no -= exr_header->data_window[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line_no < 0) {
|
} else if ((line_no > (2 << 20)) || (line_no < -(2 << 20))) {
|
||||||
|
// Too large value. Assume this is invalid
|
||||||
|
// 2**20 = 1048576 = heuristic value.
|
||||||
|
invalid_data = true;
|
||||||
|
} else if (data_len == 0) {
|
||||||
|
// TODO(syoyo): May be ok to raise the threshold for example
|
||||||
|
// `data_len < 4`
|
||||||
invalid_data = true;
|
invalid_data = true;
|
||||||
} else {
|
} else {
|
||||||
if (!tinyexr::DecodePixelData(
|
// line_no may be negative.
|
||||||
exr_image->images, exr_header->requested_pixel_types,
|
int end_line_no = (std::min)(line_no + num_scanline_blocks,
|
||||||
data_ptr, static_cast<size_t>(data_len),
|
(exr_header->data_window[3] + 1));
|
||||||
exr_header->compression_type, exr_header->line_order,
|
|
||||||
data_width, data_height, data_width, y, line_no,
|
int num_lines = end_line_no - line_no;
|
||||||
num_lines, static_cast<size_t>(pixel_data_size),
|
|
||||||
static_cast<size_t>(exr_header->num_custom_attributes),
|
if (num_lines <= 0) {
|
||||||
exr_header->custom_attributes,
|
|
||||||
static_cast<size_t>(exr_header->num_channels),
|
|
||||||
exr_header->channels, channel_offset_list)) {
|
|
||||||
invalid_data = true;
|
invalid_data = true;
|
||||||
|
} else {
|
||||||
|
// Move to data addr: 8 = 4 + 4;
|
||||||
|
data_ptr += 8;
|
||||||
|
|
||||||
|
// Adjust line_no with data_window.bmin.y
|
||||||
|
|
||||||
|
// overflow check
|
||||||
|
tinyexr_int64 lno =
|
||||||
|
static_cast<tinyexr_int64>(line_no) -
|
||||||
|
static_cast<tinyexr_int64>(exr_header->data_window[1]);
|
||||||
|
if (lno > std::numeric_limits<int>::max()) {
|
||||||
|
line_no = -1; // invalid
|
||||||
|
} else if (lno < -std::numeric_limits<int>::max()) {
|
||||||
|
line_no = -1; // invalid
|
||||||
|
} else {
|
||||||
|
line_no -= exr_header->data_window[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line_no < 0) {
|
||||||
|
invalid_data = true;
|
||||||
|
} else {
|
||||||
|
if (!tinyexr::DecodePixelData(
|
||||||
|
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,
|
||||||
|
num_lines, static_cast<size_t>(pixel_data_size),
|
||||||
|
static_cast<size_t>(
|
||||||
|
exr_header->num_custom_attributes),
|
||||||
|
exr_header->custom_attributes,
|
||||||
|
static_cast<size_t>(exr_header->num_channels),
|
||||||
|
exr_header->channels, channel_offset_list)) {
|
||||||
|
invalid_data = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (__cplusplus > 199711L) && (TINYEXR_USE_THREAD > 0)
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : workers) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
#else
|
||||||
} // omp parallel
|
} // omp parallel
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invalid_data) {
|
if (invalid_data) {
|
||||||
|
@ -11219,6 +11350,9 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
|
||||||
tinyexr::SetErrorMessage(e, err);
|
tinyexr::SetErrorMessage(e, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
FreeEXRImage(exr_image);
|
||||||
|
#else
|
||||||
// release memory(if exists)
|
// release memory(if exists)
|
||||||
if ((exr_header->num_channels > 0) && exr_image && exr_image->images) {
|
if ((exr_header->num_channels > 0) && exr_image && exr_image->images) {
|
||||||
for (size_t c = 0; c < size_t(exr_header->num_channels); c++) {
|
for (size_t c = 0; c < size_t(exr_header->num_channels); c++) {
|
||||||
|
@ -11230,16 +11364,114 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
|
||||||
free(exr_image->images);
|
free(exr_image->images);
|
||||||
exr_image->images = NULL;
|
exr_image->images = NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void GetLayers(const EXRHeader& exr_header, std::vector<std::string>& layer_names) {
|
||||||
|
// Naive implementation
|
||||||
|
// Group channels by layers
|
||||||
|
// go over all channel names, split by periods
|
||||||
|
// collect unique names
|
||||||
|
layer_names.clear();
|
||||||
|
for (int c = 0; c < exr_header.num_channels; c++) {
|
||||||
|
std::string full_name(exr_header.channels[c].name);
|
||||||
|
const size_t pos = full_name.find_last_of('.');
|
||||||
|
if (pos != std::string::npos && pos != 0 && pos + 1 < full_name.size()) {
|
||||||
|
full_name.erase(pos);
|
||||||
|
if (std::find(layer_names.begin(), layer_names.end(), full_name) == layer_names.end())
|
||||||
|
layer_names.push_back(full_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LayerChannel {
|
||||||
|
explicit LayerChannel (size_t i, std::string n)
|
||||||
|
: index(i)
|
||||||
|
, name(n)
|
||||||
|
{}
|
||||||
|
size_t index;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ChannelsInLayer(const EXRHeader& exr_header, const std::string layer_name, std::vector<LayerChannel>& channels) {
|
||||||
|
channels.clear();
|
||||||
|
for (int c = 0; c < exr_header.num_channels; c++) {
|
||||||
|
std::string ch_name(exr_header.channels[c].name);
|
||||||
|
if (layer_name.empty()) {
|
||||||
|
const size_t pos = ch_name.find_last_of('.');
|
||||||
|
if (pos != std::string::npos && pos < ch_name.size()) {
|
||||||
|
ch_name = ch_name.substr(pos + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const size_t pos = ch_name.find(layer_name + '.');
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
continue;
|
||||||
|
if (pos == 0) {
|
||||||
|
ch_name = ch_name.substr(layer_name.size() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayerChannel ch(size_t(c), ch_name);
|
||||||
|
channels.push_back(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tinyexr
|
} // namespace tinyexr
|
||||||
|
|
||||||
|
int EXRLayers(const char *filename, const char **layer_names[], int *num_layers, const char **err) {
|
||||||
|
EXRVersion exr_version;
|
||||||
|
EXRHeader exr_header;
|
||||||
|
InitEXRHeader(&exr_header);
|
||||||
|
|
||||||
|
{
|
||||||
|
int ret = ParseEXRVersionFromFile(&exr_version, filename);
|
||||||
|
if (ret != TINYEXR_SUCCESS) {
|
||||||
|
tinyexr::SetErrorMessage("Invalid EXR header.", err);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exr_version.multipart || exr_version.non_image) {
|
||||||
|
tinyexr::SetErrorMessage(
|
||||||
|
"Loading multipart or DeepImage is not supported in LoadEXR() API",
|
||||||
|
err);
|
||||||
|
return TINYEXR_ERROR_INVALID_DATA; // @fixme.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err);
|
||||||
|
if (ret != TINYEXR_SUCCESS) {
|
||||||
|
FreeEXRHeader(&exr_header);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> layer_vec;
|
||||||
|
tinyexr::GetLayers(exr_header, layer_vec);
|
||||||
|
|
||||||
|
(*num_layers) = int(layer_vec.size());
|
||||||
|
(*layer_names) = static_cast<const char **>(
|
||||||
|
malloc(sizeof(const char *) * static_cast<size_t>(layer_vec.size())));
|
||||||
|
for (size_t c = 0; c < static_cast<size_t>(layer_vec.size()); c++) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
(*layer_names)[c] = _strdup(layer_vec[c].c_str());
|
||||||
|
#else
|
||||||
|
(*layer_names)[c] = strdup(layer_vec[c].c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeEXRHeader(&exr_header);
|
||||||
|
return TINYEXR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
|
int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
|
||||||
const char **err) {
|
const char **err) {
|
||||||
|
return LoadEXRWithLayer(out_rgba, width, height, filename, /* layername */NULL, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LoadEXRWithLayer(float **out_rgba, int *width, int *height, const char *filename, const char *layername,
|
||||||
|
const char **err) {
|
||||||
if (out_rgba == NULL) {
|
if (out_rgba == NULL) {
|
||||||
tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err);
|
tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err);
|
||||||
return TINYEXR_ERROR_INVALID_ARGUMENT;
|
return TINYEXR_ERROR_INVALID_ARGUMENT;
|
||||||
|
@ -11254,7 +11486,9 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
|
||||||
{
|
{
|
||||||
int ret = ParseEXRVersionFromFile(&exr_version, filename);
|
int ret = ParseEXRVersionFromFile(&exr_version, filename);
|
||||||
if (ret != TINYEXR_SUCCESS) {
|
if (ret != TINYEXR_SUCCESS) {
|
||||||
tinyexr::SetErrorMessage("Invalid EXR header.", err);
|
std::stringstream ss;
|
||||||
|
ss << "Failed to open EXR file or read version info from EXR file. code(" << ret << ")";
|
||||||
|
tinyexr::SetErrorMessage(ss.str(), err);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11281,6 +11515,7 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Probably limit loading to layers (channels) selected by layer index
|
||||||
{
|
{
|
||||||
int ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename, err);
|
int ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename, err);
|
||||||
if (ret != TINYEXR_SUCCESS) {
|
if (ret != TINYEXR_SUCCESS) {
|
||||||
|
@ -11294,19 +11529,40 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
|
||||||
int idxG = -1;
|
int idxG = -1;
|
||||||
int idxB = -1;
|
int idxB = -1;
|
||||||
int idxA = -1;
|
int idxA = -1;
|
||||||
for (int c = 0; c < exr_header.num_channels; c++) {
|
|
||||||
if (strcmp(exr_header.channels[c].name, "R") == 0) {
|
std::vector<std::string> layer_names;
|
||||||
idxR = c;
|
tinyexr::GetLayers(exr_header, layer_names);
|
||||||
} else if (strcmp(exr_header.channels[c].name, "G") == 0) {
|
|
||||||
idxG = c;
|
std::vector<tinyexr::LayerChannel> channels;
|
||||||
} else if (strcmp(exr_header.channels[c].name, "B") == 0) {
|
tinyexr::ChannelsInLayer(exr_header, layername == NULL ? "" : std::string(layername), channels);
|
||||||
idxB = c;
|
|
||||||
} else if (strcmp(exr_header.channels[c].name, "A") == 0) {
|
if (channels.size() < 1) {
|
||||||
idxA = c;
|
tinyexr::SetErrorMessage("Layer Not Found", err);
|
||||||
|
FreeEXRHeader(&exr_header);
|
||||||
|
FreeEXRImage(&exr_image);
|
||||||
|
return TINYEXR_ERROR_LAYER_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ch_count = channels.size() < 4 ? channels.size() : 4;
|
||||||
|
for (size_t c = 0; c < ch_count; c++) {
|
||||||
|
const tinyexr::LayerChannel &ch = channels[c];
|
||||||
|
|
||||||
|
if (ch.name == "R") {
|
||||||
|
idxR = int(ch.index);
|
||||||
|
}
|
||||||
|
else if (ch.name == "G") {
|
||||||
|
idxG = int(ch.index);
|
||||||
|
}
|
||||||
|
else if (ch.name == "B") {
|
||||||
|
idxB = int(ch.index);
|
||||||
|
}
|
||||||
|
else if (ch.name == "A") {
|
||||||
|
idxA = int(ch.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exr_header.num_channels == 1) {
|
if (channels.size() == 1) {
|
||||||
|
int chIdx = int(channels.front().index);
|
||||||
// Grayscale channel only.
|
// Grayscale channel only.
|
||||||
|
|
||||||
(*out_rgba) = reinterpret_cast<float *>(
|
(*out_rgba) = reinterpret_cast<float *>(
|
||||||
|
@ -11333,19 +11589,19 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
|
||||||
const int srcIdx = i + j * exr_header.tile_size_x;
|
const int srcIdx = i + j * exr_header.tile_size_x;
|
||||||
unsigned char **src = exr_image.tiles[it].images;
|
unsigned char **src = exr_image.tiles[it].images;
|
||||||
(*out_rgba)[4 * idx + 0] =
|
(*out_rgba)[4 * idx + 0] =
|
||||||
reinterpret_cast<float **>(src)[0][srcIdx];
|
reinterpret_cast<float **>(src)[chIdx][srcIdx];
|
||||||
(*out_rgba)[4 * idx + 1] =
|
(*out_rgba)[4 * idx + 1] =
|
||||||
reinterpret_cast<float **>(src)[0][srcIdx];
|
reinterpret_cast<float **>(src)[chIdx][srcIdx];
|
||||||
(*out_rgba)[4 * idx + 2] =
|
(*out_rgba)[4 * idx + 2] =
|
||||||
reinterpret_cast<float **>(src)[0][srcIdx];
|
reinterpret_cast<float **>(src)[chIdx][srcIdx];
|
||||||
(*out_rgba)[4 * idx + 3] =
|
(*out_rgba)[4 * idx + 3] =
|
||||||
reinterpret_cast<float **>(src)[0][srcIdx];
|
reinterpret_cast<float **>(src)[chIdx][srcIdx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < exr_image.width * exr_image.height; i++) {
|
for (int i = 0; i < exr_image.width * exr_image.height; i++) {
|
||||||
const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
|
const float val = reinterpret_cast<float **>(exr_image.images)[chIdx][i];
|
||||||
(*out_rgba)[4 * i + 0] = val;
|
(*out_rgba)[4 * i + 0] = val;
|
||||||
(*out_rgba)[4 * i + 1] = val;
|
(*out_rgba)[4 * i + 1] = val;
|
||||||
(*out_rgba)[4 * i + 2] = val;
|
(*out_rgba)[4 * i + 2] = val;
|
||||||
|
@ -11358,22 +11614,22 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
|
||||||
if (idxR == -1) {
|
if (idxR == -1) {
|
||||||
tinyexr::SetErrorMessage("R channel not found", err);
|
tinyexr::SetErrorMessage("R channel not found", err);
|
||||||
|
|
||||||
// @todo { free exr_image }
|
|
||||||
FreeEXRHeader(&exr_header);
|
FreeEXRHeader(&exr_header);
|
||||||
|
FreeEXRImage(&exr_image);
|
||||||
return TINYEXR_ERROR_INVALID_DATA;
|
return TINYEXR_ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idxG == -1) {
|
if (idxG == -1) {
|
||||||
tinyexr::SetErrorMessage("G channel not found", err);
|
tinyexr::SetErrorMessage("G channel not found", err);
|
||||||
// @todo { free exr_image }
|
|
||||||
FreeEXRHeader(&exr_header);
|
FreeEXRHeader(&exr_header);
|
||||||
|
FreeEXRImage(&exr_image);
|
||||||
return TINYEXR_ERROR_INVALID_DATA;
|
return TINYEXR_ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idxB == -1) {
|
if (idxB == -1) {
|
||||||
tinyexr::SetErrorMessage("B channel not found", err);
|
tinyexr::SetErrorMessage("B channel not found", err);
|
||||||
// @todo { free exr_image }
|
|
||||||
FreeEXRHeader(&exr_header);
|
FreeEXRHeader(&exr_header);
|
||||||
|
FreeEXRImage(&exr_image);
|
||||||
return TINYEXR_ERROR_INVALID_DATA;
|
return TINYEXR_ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11446,7 +11702,7 @@ int IsEXR(const char *filename) {
|
||||||
|
|
||||||
int ret = ParseEXRVersionFromFile(&exr_version, filename);
|
int ret = ParseEXRVersionFromFile(&exr_version, filename);
|
||||||
if (ret != TINYEXR_SUCCESS) {
|
if (ret != TINYEXR_SUCCESS) {
|
||||||
return TINYEXR_ERROR_INVALID_HEADER;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TINYEXR_SUCCESS;
|
return TINYEXR_SUCCESS;
|
||||||
|
@ -11509,7 +11765,9 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
|
||||||
|
|
||||||
int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
|
int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
|
||||||
if (ret != TINYEXR_SUCCESS) {
|
if (ret != TINYEXR_SUCCESS) {
|
||||||
tinyexr::SetErrorMessage("Failed to parse EXR version", err);
|
std::stringstream ss;
|
||||||
|
ss << "Failed to parse EXR version. code(" << ret << ")";
|
||||||
|
tinyexr::SetErrorMessage(ss.str(), err);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11967,9 +12225,11 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TOOD(LTE): C++11 thread
|
||||||
|
|
||||||
// Use signed int since some OpenMP compiler doesn't allow unsigned type for
|
// Use signed int since some OpenMP compiler doesn't allow unsigned type for
|
||||||
// `parallel for`
|
// `parallel for`
|
||||||
#ifdef _OPENMP
|
#if TINYEXR_USE_OPENMP
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
#endif
|
#endif
|
||||||
for (int i = 0; i < num_blocks; i++) {
|
for (int i = 0; i < num_blocks; i++) {
|
||||||
|
|
Loading…
Reference in New Issue