summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/tinyexr/SCsub3
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/tinyexr/tinyexr.h588
3 files changed, 428 insertions, 165 deletions
diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub
index e7fd44fabd..84b3b4015b 100644
--- a/modules/tinyexr/SCsub
+++ b/modules/tinyexr/SCsub
@@ -15,6 +15,9 @@ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_tinyexr.Prepend(CPPPATH=[thirdparty_dir])
+# Enable threaded loading with C++11.
+env_tinyexr.Append(CPPDEFINES=["TINYEXR_USE_THREAD"])
+
env_thirdparty = env_tinyexr.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 77cc93fdac..ad9d443ec2 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -528,7 +528,7 @@ comments and a patch is provided in the squish/ folder.
## tinyexr
- Upstream: https://github.com/syoyo/tinyexr
-- Version: git (656bb61, 2019)
+- Version: git (4dbd05a22f51a2d7462311569b8b0cba0bbe2ac5, 2020)
- License: BSD-3-Clause
Files extracted from upstream source:
diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h
index bfc52b51a5..7e8956f7d3 100644
--- a/thirdparty/tinyexr/tinyexr.h
+++ b/thirdparty/tinyexr/tinyexr.h
@@ -105,6 +105,19 @@ extern "C" {
// http://computation.llnl.gov/projects/floating-point-compression
#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_ERROR_INVALID_MAGIC_NUMBER (-1)
#define TINYEXR_ERROR_INVALID_EXR_VERSION (-2)
@@ -118,6 +131,7 @@ extern "C" {
#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-10)
#define TINYEXR_ERROR_CANT_WRITE_FILE (-11)
#define TINYEXR_ERROR_SERIALZATION_FAILED (-12)
+#define TINYEXR_ERROR_LAYER_NOT_FOUND (-13)
// @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf }
@@ -263,7 +277,7 @@ typedef struct _DeepImage {
int pad0;
} 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
// alpha) or RGB(A) channels.
// 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,
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. }
// Simple wrapper API for ParseEXRHeaderFromFile.
// 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 <sstream>
-//#include <iostream> // debug
+// #include <iostream> // debug
#include <limits>
#include <string>
@@ -481,9 +516,15 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
#if __cplusplus > 199711L
// C++11
#include <cstdint>
+
+#if TINYEXR_USE_THREAD
+#include <atomic>
+#include <thread>
+#endif
+
#endif // __cplusplus > 199711L
-#ifdef _OPENMP
+#if TINYEXR_USE_OPENMP
#include <omp.h>
#endif
@@ -6917,6 +6958,10 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename,
}
#endif
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
#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
#endif
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
} // namespace miniz
#else
@@ -9954,9 +9996,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
return false;
}
- if (!tinyexr::DecompressRle(reinterpret_cast<unsigned char *>(&outBuf.at(0)),
- dstLen, data_ptr,
- static_cast<unsigned long>(data_len))) {
+ if (!tinyexr::DecompressRle(
+ reinterpret_cast<unsigned char *>(&outBuf.at(0)), dstLen, data_ptr,
+ static_cast<unsigned long>(data_len))) {
return false;
}
@@ -10272,7 +10314,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
return true;
}
-static void DecodeTiledPixelData(
+static bool DecodeTiledPixelData(
unsigned char **out_images, int *width, int *height,
const int *requested_pixel_types, const unsigned char *data_ptr,
size_t data_len, int compression_type, int line_order, int data_width,
@@ -10298,11 +10340,11 @@ static void DecodeTiledPixelData(
}
// Image size = tile size.
- DecodePixelData(out_images, requested_pixel_types, data_ptr, data_len,
- compression_type, line_order, (*width), tile_size_y,
- /* stride */ tile_size_x, /* y */ 0, /* line_no */ 0,
- (*height), pixel_data_size, num_attributes, attributes,
- num_channels, channels, channel_offset_list);
+ return DecodePixelData(out_images, requested_pixel_types, data_ptr, data_len,
+ compression_type, line_order, (*width), tile_size_y,
+ /* stride */ tile_size_x, /* y */ 0, /* line_no */ 0,
+ (*height), pixel_data_size, num_attributes, attributes,
+ num_channels, channels, 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 *>(
calloc(sizeof(EXRTile), static_cast<size_t>(num_tiles)));
+ int err_code = TINYEXR_SUCCESS;
+
+#if (__cplusplus > 199711L) && (TINYEXR_USE_THREAD > 0)
+
+ std::vector<std::thread> workers;
+ std::atomic<size_t> tile_count(0);
+
+ int num_threads = std::max(1, int(std::thread::hardware_concurrency()));
+ if (num_threads > int(num_tiles)) {
+ num_threads = 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++) {
- // 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) {
- if (err) {
- (*err) += "Insufficient data size.\n";
- }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+#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]);
+ 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 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]));
+ int data_len;
+ memcpy(&data_len, data_ptr + 16,
+ sizeof(int)); // 16 = sizeof(tile_coordinates)
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
- // @todo{ LoD }
- if (tile_coordinates[2] != 0) {
- return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
- }
- if (tile_coordinates[3] != 0) {
- return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
- }
+ 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;
+ }
- int data_len;
- memcpy(&data_len, data_ptr + 16,
- sizeof(int)); // 16 = sizeof(tile_coordinates)
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
+ // 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;
+ }
- if (data_len < 4 || size_t(data_len) > data_size) {
- if (err) {
- (*err) += "Insufficient data length.\n";
+ 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)
}
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ }));
+ } // num_thread loop
- // 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 (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
// Don't allow too large image(256GB * pixel_data_size or more). Workaround
// for #104.
size_t total_data_len =
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;
- if ((total_data_len == 0) || total_data_len_overflown ) {
+ const bool total_data_len_overflown =
+ sizeof(void *) == 8 ? (total_data_len >= 0x4000000000) : false;
+ if ((total_data_len == 0) || total_data_len_overflown) {
if (err) {
std::stringstream ss;
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,
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
#endif
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) {
- invalid_data = true;
- } 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;
- 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;
+#endif
+ size_t y_idx = static_cast<size_t>(y);
- if (num_lines <= 0) {
+ if (offsets[y_idx] + sizeof(int) * 2 > size) {
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];
- }
+ // 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;
+ 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;
- 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;
} 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)) {
+ // 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;
+ } 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
+#endif
}
if (invalid_data) {
@@ -11219,6 +11350,9 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
tinyexr::SetErrorMessage(e, err);
}
+#if 1
+ FreeEXRImage(exr_image);
+#else
// release memory(if exists)
if ((exr_header->num_channels > 0) && exr_image && exr_image->images) {
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);
exr_image->images = NULL;
}
+#endif
}
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
+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,
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) {
tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err);
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);
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;
}
@@ -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);
if (ret != TINYEXR_SUCCESS) {
@@ -11294,19 +11529,40 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
int idxG = -1;
int idxB = -1;
int idxA = -1;
- for (int c = 0; c < exr_header.num_channels; c++) {
- if (strcmp(exr_header.channels[c].name, "R") == 0) {
- idxR = c;
- } else if (strcmp(exr_header.channels[c].name, "G") == 0) {
- idxG = c;
- } else if (strcmp(exr_header.channels[c].name, "B") == 0) {
- idxB = c;
- } else if (strcmp(exr_header.channels[c].name, "A") == 0) {
- idxA = c;
+
+ std::vector<std::string> layer_names;
+ tinyexr::GetLayers(exr_header, layer_names);
+
+ std::vector<tinyexr::LayerChannel> channels;
+ tinyexr::ChannelsInLayer(exr_header, layername == NULL ? "" : std::string(layername), channels);
+
+ if (channels.size() < 1) {
+ 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.
(*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;
unsigned char **src = exr_image.tiles[it].images;
(*out_rgba)[4 * idx + 0] =
- reinterpret_cast<float **>(src)[0][srcIdx];
+ reinterpret_cast<float **>(src)[chIdx][srcIdx];
(*out_rgba)[4 * idx + 1] =
- reinterpret_cast<float **>(src)[0][srcIdx];
+ reinterpret_cast<float **>(src)[chIdx][srcIdx];
(*out_rgba)[4 * idx + 2] =
- reinterpret_cast<float **>(src)[0][srcIdx];
+ reinterpret_cast<float **>(src)[chIdx][srcIdx];
(*out_rgba)[4 * idx + 3] =
- reinterpret_cast<float **>(src)[0][srcIdx];
+ reinterpret_cast<float **>(src)[chIdx][srcIdx];
}
}
}
} else {
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 + 1] = 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) {
tinyexr::SetErrorMessage("R channel not found", err);
- // @todo { free exr_image }
FreeEXRHeader(&exr_header);
+ FreeEXRImage(&exr_image);
return TINYEXR_ERROR_INVALID_DATA;
}
if (idxG == -1) {
tinyexr::SetErrorMessage("G channel not found", err);
- // @todo { free exr_image }
FreeEXRHeader(&exr_header);
+ FreeEXRImage(&exr_image);
return TINYEXR_ERROR_INVALID_DATA;
}
if (idxB == -1) {
tinyexr::SetErrorMessage("B channel not found", err);
- // @todo { free exr_image }
FreeEXRHeader(&exr_header);
+ FreeEXRImage(&exr_image);
return TINYEXR_ERROR_INVALID_DATA;
}
@@ -11446,7 +11702,7 @@ int IsEXR(const char *filename) {
int ret = ParseEXRVersionFromFile(&exr_version, filename);
if (ret != TINYEXR_SUCCESS) {
- return TINYEXR_ERROR_INVALID_HEADER;
+ return ret;
}
return TINYEXR_SUCCESS;
@@ -11509,7 +11765,9 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
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;
}
@@ -11967,9 +12225,11 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
}
#endif
+ // TOOD(LTE): C++11 thread
+
// Use signed int since some OpenMP compiler doesn't allow unsigned type for
// `parallel for`
-#ifdef _OPENMP
+#if TINYEXR_USE_OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < num_blocks; i++) {