|
|
@@ -116,6 +116,8 @@ extern "C" {
|
|
|
#define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-7)
|
|
|
#define TINYEXR_ERROR_INVALID_HEADER (-8)
|
|
|
#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-9)
|
|
|
+#define TINYEXR_ERROR_CANT_WRITE_FILE (-10)
|
|
|
+#define TINYEXR_ERROR_SERIALZATION_FAILED (-11)
|
|
|
|
|
|
// @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf }
|
|
|
|
|
|
@@ -279,9 +281,12 @@ extern int LoadEXR(float **out_rgba, int *width, int *height,
|
|
|
// Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero
|
|
|
// value.
|
|
|
// Save image as fp32(FLOAT) format when `save_as_fp16` is 0.
|
|
|
+// Use ZIP compression by default.
|
|
|
+// Returns negative value and may set error string in `err` when there's an
|
|
|
+// error
|
|
|
extern int SaveEXR(const float *data, const int width, const int height,
|
|
|
const int components, const int save_as_fp16,
|
|
|
- const char *filename);
|
|
|
+ const char *filename, const char **err);
|
|
|
|
|
|
// Initialize EXRHeader struct
|
|
|
extern void InitEXRHeader(EXRHeader *exr_header);
|
|
|
@@ -400,9 +405,9 @@ extern int SaveEXRImageToFile(const EXRImage *image,
|
|
|
|
|
|
// Saves multi-channel, single-frame OpenEXR image to a memory.
|
|
|
// Image is compressed using EXRImage.compression value.
|
|
|
-// Return the number of bytes if succes.
|
|
|
-// Returns negative value and may set error string in `err` when there's an
|
|
|
-// error
|
|
|
+// Return the number of bytes if success.
|
|
|
+// Return zero and will set error string in `err` when there's an
|
|
|
+// error.
|
|
|
// When there was an error message, Application must free `err` with
|
|
|
// FreeEXRErrorMessage()
|
|
|
extern size_t SaveEXRImageToMemory(const EXRImage *image,
|
|
|
@@ -529,15 +534,23 @@ namespace miniz {
|
|
|
#if __has_warning("-Wcomma")
|
|
|
#pragma clang diagnostic ignored "-Wcomma"
|
|
|
#endif
|
|
|
+
|
|
|
#if __has_warning("-Wmacro-redefined")
|
|
|
#pragma clang diagnostic ignored "-Wmacro-redefined"
|
|
|
#endif
|
|
|
+
|
|
|
#if __has_warning("-Wcast-qual")
|
|
|
#pragma clang diagnostic ignored "-Wcast-qual"
|
|
|
#endif
|
|
|
+
|
|
|
#if __has_warning("-Wzero-as-null-pointer-constant")
|
|
|
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
|
|
#endif
|
|
|
+
|
|
|
+#if __has_warning("-Wtautological-constant-compare")
|
|
|
+#pragma clang diagnostic ignored "-Wtautological-constant-compare"
|
|
|
+#endif
|
|
|
+
|
|
|
#endif
|
|
|
|
|
|
/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP
|
|
|
@@ -7253,7 +7266,21 @@ static bool ReadAttribute(std::string *name, std::string *type,
|
|
|
tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
|
|
|
|
|
|
if (data_len == 0) {
|
|
|
- return false;
|
|
|
+ if ((*type).compare("string") == 0) {
|
|
|
+ // Accept empty string attribute.
|
|
|
+
|
|
|
+ marker += sizeof(uint32_t);
|
|
|
+ size -= sizeof(uint32_t);
|
|
|
+
|
|
|
+ *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t);
|
|
|
+
|
|
|
+ data->resize(1);
|
|
|
+ (*data)[0] = '\0';
|
|
|
+
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
marker += sizeof(uint32_t);
|
|
|
@@ -9587,6 +9614,7 @@ bool CompressZfp(std::vector<unsigned char> *outBuf, unsigned int *outSize,
|
|
|
// -----------------------------------------------------------------
|
|
|
//
|
|
|
|
|
|
+// TODO(syoyo): Refactor function arguments.
|
|
|
static bool DecodePixelData(/* out */ unsigned char **out_images,
|
|
|
const int *requested_pixel_types,
|
|
|
const unsigned char *data_ptr, size_t data_len,
|
|
|
@@ -9636,6 +9664,8 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
|
|
|
FP16 hf;
|
|
|
|
|
|
// hf.u = line_ptr[u];
|
|
|
+ // use `cpy` to avoid unaligned memory access when compiler's
|
|
|
+ // optimization is on.
|
|
|
tinyexr::cpy2(&(hf.u), line_ptr + u);
|
|
|
|
|
|
tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
|
|
|
@@ -10070,37 +10100,76 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
|
|
|
#endif
|
|
|
} else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
|
|
|
for (size_t c = 0; c < num_channels; c++) {
|
|
|
- if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
|
|
|
- const unsigned short *line_ptr =
|
|
|
- reinterpret_cast<const unsigned short *>(
|
|
|
- data_ptr +
|
|
|
- c * static_cast<size_t>(width) * sizeof(unsigned short));
|
|
|
-
|
|
|
- if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
|
|
|
- unsigned short *outLine =
|
|
|
- reinterpret_cast<unsigned short *>(out_images[c]);
|
|
|
- if (line_order == 0) {
|
|
|
- outLine += y * x_stride;
|
|
|
- } else {
|
|
|
- outLine += (height - 1 - y) * x_stride;
|
|
|
- }
|
|
|
+ for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
|
|
|
+ if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
|
|
|
+ const unsigned short *line_ptr =
|
|
|
+ reinterpret_cast<const unsigned short *>(
|
|
|
+ data_ptr + v * pixel_data_size * size_t(width) +
|
|
|
+ channel_offset_list[c] * static_cast<size_t>(width));
|
|
|
+
|
|
|
+ if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
|
|
|
+ unsigned short *outLine =
|
|
|
+ reinterpret_cast<unsigned short *>(out_images[c]);
|
|
|
+ if (line_order == 0) {
|
|
|
+ outLine += (size_t(y) + v) * size_t(x_stride);
|
|
|
+ } else {
|
|
|
+ outLine +=
|
|
|
+ (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
|
|
|
+ }
|
|
|
|
|
|
- for (int u = 0; u < width; u++) {
|
|
|
- tinyexr::FP16 hf;
|
|
|
+ for (int u = 0; u < width; u++) {
|
|
|
+ tinyexr::FP16 hf;
|
|
|
|
|
|
- // hf.u = line_ptr[u];
|
|
|
- tinyexr::cpy2(&(hf.u), line_ptr + u);
|
|
|
+ // hf.u = line_ptr[u];
|
|
|
+ tinyexr::cpy2(&(hf.u), line_ptr + u);
|
|
|
|
|
|
- tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
|
|
|
+ tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
|
|
|
|
|
|
- outLine[u] = hf.u;
|
|
|
+ outLine[u] = hf.u;
|
|
|
+ }
|
|
|
+ } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
|
|
|
+ float *outLine = reinterpret_cast<float *>(out_images[c]);
|
|
|
+ if (line_order == 0) {
|
|
|
+ outLine += (size_t(y) + v) * size_t(x_stride);
|
|
|
+ } else {
|
|
|
+ outLine +=
|
|
|
+ (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
|
|
|
+ (data_ptr + data_len)) {
|
|
|
+ // Insufficient data size
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int u = 0; u < width; u++) {
|
|
|
+ tinyexr::FP16 hf;
|
|
|
+
|
|
|
+ // address may not be aliged. use byte-wise copy for safety.#76
|
|
|
+ // hf.u = line_ptr[u];
|
|
|
+ tinyexr::cpy2(&(hf.u), line_ptr + u);
|
|
|
+
|
|
|
+ tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
|
|
|
+
|
|
|
+ tinyexr::FP32 f32 = half_to_float(hf);
|
|
|
+
|
|
|
+ outLine[u] = f32.f;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ assert(0);
|
|
|
+ return false;
|
|
|
}
|
|
|
- } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
|
|
|
+ } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
|
|
|
+ const float *line_ptr = reinterpret_cast<const float *>(
|
|
|
+ data_ptr + v * pixel_data_size * size_t(width) +
|
|
|
+ channel_offset_list[c] * static_cast<size_t>(width));
|
|
|
+
|
|
|
float *outLine = reinterpret_cast<float *>(out_images[c]);
|
|
|
if (line_order == 0) {
|
|
|
- outLine += y * x_stride;
|
|
|
+ outLine += (size_t(y) + v) * size_t(x_stride);
|
|
|
} else {
|
|
|
- outLine += (height - 1 - y) * x_stride;
|
|
|
+ outLine +=
|
|
|
+ (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
|
|
|
}
|
|
|
|
|
|
if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
|
|
|
@@ -10110,71 +10179,41 @@ static bool DecodePixelData(/* out */ unsigned char **out_images,
|
|
|
}
|
|
|
|
|
|
for (int u = 0; u < width; u++) {
|
|
|
- tinyexr::FP16 hf;
|
|
|
-
|
|
|
- // address may not be aliged. use byte-wise copy for safety.#76
|
|
|
- // hf.u = line_ptr[u];
|
|
|
- tinyexr::cpy2(&(hf.u), line_ptr + u);
|
|
|
-
|
|
|
- tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
|
|
|
+ float val;
|
|
|
+ tinyexr::cpy4(&val, line_ptr + u);
|
|
|
|
|
|
- tinyexr::FP32 f32 = half_to_float(hf);
|
|
|
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
|
|
|
|
|
|
- outLine[u] = f32.f;
|
|
|
+ outLine[u] = val;
|
|
|
}
|
|
|
- } else {
|
|
|
- assert(0);
|
|
|
- return false;
|
|
|
- }
|
|
|
- } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
|
|
|
- const float *line_ptr = reinterpret_cast<const float *>(
|
|
|
- data_ptr + c * static_cast<size_t>(width) * sizeof(float));
|
|
|
+ } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
|
|
|
+ const unsigned int *line_ptr = reinterpret_cast<const unsigned int *>(
|
|
|
+ data_ptr + v * pixel_data_size * size_t(width) +
|
|
|
+ channel_offset_list[c] * static_cast<size_t>(width));
|
|
|
|
|
|
- float *outLine = reinterpret_cast<float *>(out_images[c]);
|
|
|
- if (line_order == 0) {
|
|
|
- outLine += y * x_stride;
|
|
|
- } else {
|
|
|
- outLine += (height - 1 - y) * x_stride;
|
|
|
- }
|
|
|
-
|
|
|
- if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
|
|
|
- (data_ptr + data_len)) {
|
|
|
- // Insufficient data size
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- for (int u = 0; u < width; u++) {
|
|
|
- float val;
|
|
|
- tinyexr::cpy4(&val, line_ptr + u);
|
|
|
+ unsigned int *outLine =
|
|
|
+ reinterpret_cast<unsigned int *>(out_images[c]);
|
|
|
+ if (line_order == 0) {
|
|
|
+ outLine += (size_t(y) + v) * size_t(x_stride);
|
|
|
+ } else {
|
|
|
+ outLine +=
|
|
|
+ (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
|
|
|
+ }
|
|
|
|
|
|
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
|
|
|
+ for (int u = 0; u < width; u++) {
|
|
|
+ if (reinterpret_cast<const unsigned char *>(line_ptr + u) >=
|
|
|
+ (data_ptr + data_len)) {
|
|
|
+ // Corrupsed data?
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- outLine[u] = val;
|
|
|
- }
|
|
|
- } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
|
|
|
- const unsigned int *line_ptr = reinterpret_cast<const unsigned int *>(
|
|
|
- data_ptr + c * static_cast<size_t>(width) * sizeof(unsigned int));
|
|
|
+ unsigned int val;
|
|
|
+ tinyexr::cpy4(&val, line_ptr + u);
|
|
|
|
|
|
- unsigned int *outLine = reinterpret_cast<unsigned int *>(out_images[c]);
|
|
|
- if (line_order == 0) {
|
|
|
- outLine += y * x_stride;
|
|
|
- } else {
|
|
|
- outLine += (height - 1 - y) * x_stride;
|
|
|
- }
|
|
|
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
|
|
|
|
|
|
- for (int u = 0; u < width; u++) {
|
|
|
- if (reinterpret_cast<const unsigned char *>(line_ptr + u) >=
|
|
|
- (data_ptr + data_len)) {
|
|
|
- // Corrupsed data?
|
|
|
- return false;
|
|
|
+ outLine[u] = val;
|
|
|
}
|
|
|
-
|
|
|
- unsigned int val;
|
|
|
- tinyexr::cpy4(&val, line_ptr + u);
|
|
|
-
|
|
|
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
|
|
|
-
|
|
|
- outLine[u] = val;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -10680,7 +10719,8 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) {
|
|
|
|
|
|
static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
|
|
|
const std::vector<tinyexr::tinyexr_uint64> &offsets,
|
|
|
- const unsigned char *head, const size_t size, std::string *err) {
|
|
|
+ const unsigned char *head, const size_t size,
|
|
|
+ std::string *err) {
|
|
|
int num_channels = exr_header->num_channels;
|
|
|
|
|
|
int num_scanline_blocks = 1;
|
|
|
@@ -11045,7 +11085,6 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
|
|
|
free(exr_image->images);
|
|
|
exr_image->images = NULL;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
@@ -11240,9 +11279,7 @@ int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version,
|
|
|
}
|
|
|
|
|
|
if (size < tinyexr::kEXRVersionSize) {
|
|
|
- tinyexr::SetErrorMessage(
|
|
|
- "Insufficient header/data size.\n",
|
|
|
- err);
|
|
|
+ tinyexr::SetErrorMessage("Insufficient header/data size.\n", err);
|
|
|
return TINYEXR_ERROR_INVALID_DATA;
|
|
|
}
|
|
|
|
|
|
@@ -11347,18 +11384,53 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
|
|
|
malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
|
|
|
static_cast<size_t>(exr_image.height)));
|
|
|
|
|
|
- for (int i = 0; i < exr_image.width * exr_image.height; i++) {
|
|
|
- (*out_rgba)[4 * i + 0] =
|
|
|
- reinterpret_cast<float **>(exr_image.images)[idxR][i];
|
|
|
- (*out_rgba)[4 * i + 1] =
|
|
|
- reinterpret_cast<float **>(exr_image.images)[idxG][i];
|
|
|
- (*out_rgba)[4 * i + 2] =
|
|
|
- reinterpret_cast<float **>(exr_image.images)[idxB][i];
|
|
|
- if (idxA != -1) {
|
|
|
- (*out_rgba)[4 * i + 3] =
|
|
|
- reinterpret_cast<float **>(exr_image.images)[idxA][i];
|
|
|
- } else {
|
|
|
- (*out_rgba)[4 * i + 3] = 1.0;
|
|
|
+ if (exr_header.tiled) {
|
|
|
+ for (int it = 0; it < exr_image.num_tiles; it++) {
|
|
|
+ for (int j = 0; j < exr_header.tile_size_y; j++)
|
|
|
+ for (int i = 0; i < exr_header.tile_size_x; i++) {
|
|
|
+ const int ii =
|
|
|
+ exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
|
|
|
+ const int jj =
|
|
|
+ exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
|
|
|
+ const int idx = ii + jj * exr_image.width;
|
|
|
+
|
|
|
+ // out of region check.
|
|
|
+ if (ii >= exr_image.width) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (jj >= exr_image.height) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ 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)[idxR][srcIdx];
|
|
|
+ (*out_rgba)[4 * idx + 1] =
|
|
|
+ reinterpret_cast<float **>(src)[idxG][srcIdx];
|
|
|
+ (*out_rgba)[4 * idx + 2] =
|
|
|
+ reinterpret_cast<float **>(src)[idxB][srcIdx];
|
|
|
+ if (idxA != -1) {
|
|
|
+ (*out_rgba)[4 * idx + 3] =
|
|
|
+ reinterpret_cast<float **>(src)[idxA][srcIdx];
|
|
|
+ } else {
|
|
|
+ (*out_rgba)[4 * idx + 3] = 1.0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (int i = 0; i < exr_image.width * exr_image.height; i++) {
|
|
|
+ (*out_rgba)[4 * i + 0] =
|
|
|
+ reinterpret_cast<float **>(exr_image.images)[idxR][i];
|
|
|
+ (*out_rgba)[4 * i + 1] =
|
|
|
+ reinterpret_cast<float **>(exr_image.images)[idxG][i];
|
|
|
+ (*out_rgba)[4 * i + 2] =
|
|
|
+ reinterpret_cast<float **>(exr_image.images)[idxB][i];
|
|
|
+ if (idxA != -1) {
|
|
|
+ (*out_rgba)[4 * i + 3] =
|
|
|
+ reinterpret_cast<float **>(exr_image.images)[idxA][i];
|
|
|
+ } else {
|
|
|
+ (*out_rgba)[4 * i + 3] = 1.0;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -11396,7 +11468,8 @@ int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header,
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
|
|
if (filesize < 16) {
|
|
|
- tinyexr::SetErrorMessage("File size too short " + std::string(filename), err);
|
|
|
+ tinyexr::SetErrorMessage("File size too short " + std::string(filename),
|
|
|
+ err);
|
|
|
return TINYEXR_ERROR_INVALID_FILE;
|
|
|
}
|
|
|
|
|
|
@@ -11442,7 +11515,7 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
|
|
|
if (exr_image == NULL || memory_out == NULL ||
|
|
|
exr_header->compression_type < 0) {
|
|
|
tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToMemory", err);
|
|
|
- return 0; // @fixme
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
#if !TINYEXR_USE_PIZ
|
|
|
@@ -11613,8 +11686,6 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
|
|
|
sizeof(
|
|
|
tinyexr::tinyexr_int64); // sizeof(header) + sizeof(offsetTable)
|
|
|
|
|
|
- std::vector<unsigned char> data;
|
|
|
-
|
|
|
std::vector<std::vector<unsigned char> > data_list(
|
|
|
static_cast<size_t>(num_blocks));
|
|
|
std::vector<size_t> channel_offset_list(
|
|
|
@@ -11853,9 +11924,9 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
|
|
|
} else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
|
|
|
#if TINYEXR_USE_PIZ
|
|
|
unsigned int bufLen =
|
|
|
- 1024 + static_cast<unsigned int>(
|
|
|
- 1.2 * static_cast<unsigned int>(
|
|
|
- buf.size())); // @fixme { compute good bound. }
|
|
|
+ 8192 + static_cast<unsigned int>(
|
|
|
+ 2 * static_cast<unsigned int>(
|
|
|
+ buf.size())); // @fixme { compute good bound. }
|
|
|
std::vector<unsigned char> block(bufLen);
|
|
|
unsigned int outSize = static_cast<unsigned int>(block.size());
|
|
|
|
|
|
@@ -11914,13 +11985,12 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
|
|
|
} // omp parallel
|
|
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(num_blocks); i++) {
|
|
|
- data.insert(data.end(), data_list[i].begin(), data_list[i].end());
|
|
|
-
|
|
|
offsets[i] = offset;
|
|
|
tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64 *>(&offsets[i]));
|
|
|
offset += data_list[i].size();
|
|
|
}
|
|
|
|
|
|
+ size_t totalSize = static_cast<size_t>(offset);
|
|
|
{
|
|
|
memory.insert(
|
|
|
memory.end(), reinterpret_cast<unsigned char *>(&offsets.at(0)),
|
|
|
@@ -11928,14 +11998,21 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image,
|
|
|
sizeof(tinyexr::tinyexr_uint64) * static_cast<size_t>(num_blocks));
|
|
|
}
|
|
|
|
|
|
- { memory.insert(memory.end(), data.begin(), data.end()); }
|
|
|
-
|
|
|
- assert(memory.size() > 0);
|
|
|
+ if ( memory.size() == 0 ) {
|
|
|
+ tinyexr::SetErrorMessage("Output memory size is zero", err);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
- (*memory_out) = static_cast<unsigned char *>(malloc(memory.size()));
|
|
|
+ (*memory_out) = static_cast<unsigned char *>(malloc(totalSize));
|
|
|
memcpy((*memory_out), &memory.at(0), memory.size());
|
|
|
+ unsigned char *memory_ptr = *memory_out + memory.size();
|
|
|
+
|
|
|
+ for (size_t i = 0; i < static_cast<size_t>(num_blocks); i++) {
|
|
|
+ memcpy(memory_ptr, &data_list[i].at(0), data_list[i].size());
|
|
|
+ memory_ptr += data_list[i].size();
|
|
|
+ }
|
|
|
|
|
|
- return memory.size(); // OK
|
|
|
+ return totalSize; // OK
|
|
|
}
|
|
|
|
|
|
int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
|
|
|
@@ -11950,7 +12027,7 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
|
|
|
if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
|
|
|
tinyexr::SetErrorMessage("PIZ compression is not supported in this build",
|
|
|
err);
|
|
|
- return 0;
|
|
|
+ return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
@@ -11958,7 +12035,7 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
|
|
|
if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
|
|
|
tinyexr::SetErrorMessage("ZFP compression is not supported in this build",
|
|
|
err);
|
|
|
- return 0;
|
|
|
+ return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
@@ -11970,19 +12047,28 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
|
|
|
#endif
|
|
|
if (!fp) {
|
|
|
tinyexr::SetErrorMessage("Cannot write a file", err);
|
|
|
- return TINYEXR_ERROR_CANT_OPEN_FILE;
|
|
|
+ return TINYEXR_ERROR_CANT_WRITE_FILE;
|
|
|
}
|
|
|
|
|
|
unsigned char *mem = NULL;
|
|
|
size_t mem_size = SaveEXRImageToMemory(exr_image, exr_header, &mem, err);
|
|
|
+ if (mem_size == 0) {
|
|
|
+ return TINYEXR_ERROR_SERIALZATION_FAILED;
|
|
|
+ }
|
|
|
|
|
|
+ size_t written_size = 0;
|
|
|
if ((mem_size > 0) && mem) {
|
|
|
- fwrite(mem, 1, mem_size, fp);
|
|
|
+ written_size = fwrite(mem, 1, mem_size, fp);
|
|
|
}
|
|
|
free(mem);
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
+ if (written_size != mem_size) {
|
|
|
+ tinyexr::SetErrorMessage("Cannot write a file", err);
|
|
|
+ return TINYEXR_ERROR_CANT_WRITE_FILE;
|
|
|
+ }
|
|
|
+
|
|
|
return TINYEXR_SUCCESS;
|
|
|
}
|
|
|
|
|
|
@@ -12515,8 +12601,7 @@ int ParseEXRMultipartHeaderFromMemory(EXRHeader ***exr_headers,
|
|
|
}
|
|
|
|
|
|
if (size < tinyexr::kEXRVersionSize) {
|
|
|
- tinyexr::SetErrorMessage(
|
|
|
- "Data size too short", err);
|
|
|
+ tinyexr::SetErrorMessage("Data size too short", err);
|
|
|
return TINYEXR_ERROR_INVALID_DATA;
|
|
|
}
|
|
|
|
|
|
@@ -12852,20 +12937,27 @@ int LoadEXRMultipartImageFromFile(EXRImage *exr_images,
|
|
|
}
|
|
|
|
|
|
int SaveEXR(const float *data, int width, int height, int components,
|
|
|
- const int save_as_fp16, const char *outfilename) {
|
|
|
+ const int save_as_fp16, const char *outfilename, const char **err) {
|
|
|
if ((components == 1) || components == 3 || components == 4) {
|
|
|
// OK
|
|
|
} else {
|
|
|
+ std::stringstream ss;
|
|
|
+ ss << "Unsupported component value : " << components << std::endl;
|
|
|
+
|
|
|
+ tinyexr::SetErrorMessage(ss.str(), err);
|
|
|
return TINYEXR_ERROR_INVALID_ARGUMENT;
|
|
|
}
|
|
|
|
|
|
- // Assume at least 16x16 pixels.
|
|
|
- if (width < 16) return TINYEXR_ERROR_INVALID_ARGUMENT;
|
|
|
- if (height < 16) return TINYEXR_ERROR_INVALID_ARGUMENT;
|
|
|
-
|
|
|
EXRHeader header;
|
|
|
InitEXRHeader(&header);
|
|
|
|
|
|
+ if ((width < 16) && (height < 16)) {
|
|
|
+ // No compression for small image.
|
|
|
+ header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE;
|
|
|
+ } else {
|
|
|
+ header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
|
|
|
+ }
|
|
|
+
|
|
|
EXRImage image;
|
|
|
InitEXRImage(&image);
|
|
|
|
|
|
@@ -12971,8 +13063,7 @@ int SaveEXR(const float *data, int width, int height, int components,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const char *err;
|
|
|
- int ret = SaveEXRImageToFile(&image, &header, outfilename, &err);
|
|
|
+ int ret = SaveEXRImageToFile(&image, &header, outfilename, err);
|
|
|
if (ret != TINYEXR_SUCCESS) {
|
|
|
return ret;
|
|
|
}
|