Update TinyEXR.
diff --git a/examples/common/tinyexr.h b/examples/common/tinyexr.h
index 925f0d4..0b79797 100644
--- a/examples/common/tinyexr.h
+++ b/examples/common/tinyexr.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2014 - 2016, Syoyo Fujita
+Copyright (c) 2014 - 2018, Syoyo Fujita and many contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -9,7 +9,7 @@
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- * Neither the name of the <organization> nor the
+ * Neither the name of the Syoyo Fujita nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
@@ -115,6 +115,9 @@
#define TINYEXR_ERROR_CANT_OPEN_FILE (-6)
#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 }
@@ -123,7 +126,8 @@
#define TINYEXR_PIXELTYPE_HALF (1)
#define TINYEXR_PIXELTYPE_FLOAT (2)
-#define TINYEXR_MAX_ATTRIBUTES (128)
+#define TINYEXR_MAX_HEADER_ATTRIBUTES (1024)
+#define TINYEXR_MAX_CUSTOM_ATTRIBUTES (128)
#define TINYEXR_COMPRESSIONTYPE_NONE (0)
#define TINYEXR_COMPRESSIONTYPE_RLE (1)
@@ -205,7 +209,8 @@
// Custom attributes(exludes required attributes(e.g. `channels`,
// `compression`, etc)
int num_custom_attributes;
- EXRAttribute custom_attributes[TINYEXR_MAX_ATTRIBUTES];
+ EXRAttribute *custom_attributes; // array of EXRAttribute. size =
+ // `num_custom_attributes`.
EXRChannelInfo *channels; // [num_channels]
@@ -259,7 +264,8 @@
} DeepImage;
// @deprecated { to be removed. }
-// Loads single-frame OpenEXR image. Assume EXR image contains RGB(A) channels.
+// 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`
// 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
@@ -268,11 +274,25 @@
const char *filename, const char **err);
// @deprecated { to be removed. }
+// Simple wrapper API for ParseEXRHeaderFromFile.
+// checking given file is a EXR file(by just look up header)
+// @return TINYEXR_SUCCEES for EXR image, TINYEXR_ERROR_INVALID_HEADER for others
+extern int IsEXR(const char *filename);
+
+// @deprecated { to be removed. }
// Saves single-frame OpenEXR image. Assume EXR image contains RGB(A) channels.
-// components must be 3(RGB) or 4(RGBA).
-// Result image format is: float x RGB(A) x width x hight
-extern int SaveEXR(const float *data, int width, int height, int components,
- const char *filename);
+// components must be 1(Grayscale), 3(RGB) or 4(RGBA).
+// Input image format is: `float x width x height`, or `float x RGB(A) x width x
+// hight`
+// 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 **err);
// Initialize EXRHeader struct
extern void InitEXRHeader(EXRHeader *exr_header);
@@ -286,6 +306,9 @@
// Free's internal data of EXRImage struct
extern int FreeEXRImage(EXRImage *exr_image);
+// Free's error message
+extern void FreeEXRErrorMessage(const char *msg);
+
// Parse EXR version header of a file.
extern int ParseEXRVersionFromFile(EXRVersion *version, const char *filename);
@@ -294,10 +317,14 @@
const unsigned char *memory, size_t size);
// Parse single-part OpenEXR header from a file and initialize `EXRHeader`.
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int ParseEXRHeaderFromFile(EXRHeader *header, const EXRVersion *version,
const char *filename, const char **err);
// Parse single-part OpenEXR header from a memory and initialize `EXRHeader`.
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int ParseEXRHeaderFromMemory(EXRHeader *header,
const EXRVersion *version,
const unsigned char *memory, size_t size,
@@ -305,6 +332,8 @@
// Parse multi-part OpenEXR headers from a file and initialize `EXRHeader*`
// array.
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int ParseEXRMultipartHeaderFromFile(EXRHeader ***headers,
int *num_headers,
const EXRVersion *version,
@@ -313,6 +342,8 @@
// Parse multi-part OpenEXR headers from a memory and initialize `EXRHeader*`
// array
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int ParseEXRMultipartHeaderFromMemory(EXRHeader ***headers,
int *num_headers,
const EXRVersion *version,
@@ -324,6 +355,8 @@
// Application can free EXRImage using `FreeEXRImage`
// Returns negative value and may set error string in `err` when there's an
// error
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int LoadEXRImageFromFile(EXRImage *image, const EXRHeader *header,
const char *filename, const char **err);
@@ -333,9 +366,11 @@
// Application can free EXRImage using `FreeEXRImage`
// Returns negative value and may set error string in `err` when there's an
// error
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int LoadEXRImageFromMemory(EXRImage *image, const EXRHeader *header,
const unsigned char *memory,
- const char **err);
+ const size_t size, const char **err);
// Loads multi-part OpenEXR image from a file.
// Application must setup `ParseEXRMultipartHeaderFromFile` before calling this
@@ -343,6 +378,8 @@
// Application can free EXRImage using `FreeEXRImage`
// Returns negative value and may set error string in `err` when there's an
// error
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int LoadEXRMultipartImageFromFile(EXRImage *images,
const EXRHeader **headers,
unsigned int num_parts,
@@ -355,24 +392,30 @@
// Application can free EXRImage using `FreeEXRImage`
// Returns negative value and may set error string in `err` when there's an
// error
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int LoadEXRMultipartImageFromMemory(EXRImage *images,
const EXRHeader **headers,
unsigned int num_parts,
const unsigned char *memory,
- const char **err);
+ const size_t size, const char **err);
// Saves multi-channel, single-frame OpenEXR image to a file.
// Returns negative value and may set error string in `err` when there's an
// error
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int SaveEXRImageToFile(const EXRImage *image,
const EXRHeader *exr_header, const char *filename,
const char **err);
// 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,
const EXRHeader *exr_header,
unsigned char **memory, const char **err);
@@ -381,6 +424,8 @@
// Application must free memory of variables in DeepImage(image, offset_table)
// Returns negative value and may set error string in `err` when there's an
// error
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
extern int LoadDeepEXR(DeepImage *out_image, const char *filename,
const char **err);
@@ -401,30 +446,40 @@
// For emscripten.
// Loads single-frame OpenEXR image from memory. Assume EXR image contains
// RGB(A) channels.
-// `out_rgba` must have enough memory(at least sizeof(float) x 4(RGBA) x width x
-// hight)
// Returns negative value and may set error string in `err` when there's an
// error
-extern int LoadEXRFromMemory(float *out_rgba, const unsigned char *memory,
- size_t size, const char **err);
+// When there was an error message, Application must free `err` with
+// FreeEXRErrorMessage()
+extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
+ const unsigned char *memory, size_t size,
+ const char **err);
#ifdef __cplusplus
}
#endif
+#endif // TINYEXR_H_
+
#ifdef TINYEXR_IMPLEMENTATION
+#ifndef TINYEXR_IMPLEMENTATION_DEIFNED
+#define TINYEXR_IMPLEMENTATION_DEIFNED
+
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <iostream>
#include <sstream>
+#include <limits>
#include <string>
#include <vector>
-// @todo { remove including tinyexr.h }
-#include "tinyexr.h"
+#if __cplusplus > 199711L
+// C++11
+#include <cstdint>
+#endif // __cplusplus > 199711L
#ifdef _OPENMP
#include <omp.h>
@@ -432,7 +487,9 @@
#if TINYEXR_USE_MINIZ
#else
-#include "zlib.h"
+// Issue #46. Please include your own zlib-compatible API header before
+// including `tinyexr.h`
+//#include "zlib.h"
#endif
#if TINYEXR_USE_ZFP
@@ -443,7 +500,6 @@
#if __cplusplus > 199711L
// C++11
-#include <cstdint>
typedef uint64_t tinyexr_uint64;
typedef int64_t tinyexr_int64;
#else
@@ -472,6 +528,30 @@
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wc++11-extensions"
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wunused-function"
+#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#pragma clang diagnostic ignored "-Wundef"
+
+#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
@@ -731,7 +811,7 @@
#ifndef MINIZ_HEADER_INCLUDED
#define MINIZ_HEADER_INCLUDED
-#include <stdlib.h>
+//#include <stdlib.h>
// Defines to completely disable specific portions of miniz.c:
// If all macros here are defined the only functionality remaining will be
@@ -749,7 +829,7 @@
#define MINIZ_NO_TIME
// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
-//#define MINIZ_NO_ARCHIVE_APIS
+#define MINIZ_NO_ARCHIVE_APIS
// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive
// API's.
@@ -779,7 +859,7 @@
#endif
#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
-#include <time.h>
+//#include <time.h>
#endif
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
@@ -1800,8 +1880,8 @@
typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1];
typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
-#include <assert.h>
-#include <string.h>
+//#include <assert.h>
+//#include <string.h>
#define MZ_ASSERT(x) assert(x)
@@ -1901,11 +1981,11 @@
(void)opaque, (void)address;
MZ_FREE(address);
}
-static void *def_realloc_func(void *opaque, void *address, size_t items,
- size_t size) {
- (void)opaque, (void)address, (void)items, (void)size;
- return MZ_REALLOC(address, items * size);
-}
+// static void *def_realloc_func(void *opaque, void *address, size_t items,
+// size_t size) {
+// (void)opaque, (void)address, (void)items, (void)size;
+// return MZ_REALLOC(address, items * size);
+//}
const char *mz_version(void) { return MZ_VERSION; }
@@ -4362,9 +4442,8 @@
// C and C99, so no big deal)
#pragma warning(disable : 4244) // 'initializing': conversion from '__int64' to
// 'int', possible loss of data
-#pragma warning( \
- disable : 4267) // 'argument': conversion from '__int64' to 'int',
- // possible loss of data
+#pragma warning(disable : 4267) // 'argument': conversion from '__int64' to
+ // 'int', possible loss of data
#pragma warning(disable : 4996) // 'strdup': The POSIX name for this item is
// deprecated. Instead, use the ISO C and C++
// conformant name: _strdup.
@@ -4494,6 +4573,7 @@
// ------------------- .ZIP archive reading
#ifndef MINIZ_NO_ARCHIVE_APIS
+#error "No arvhive APIs"
#ifdef MINIZ_NO_STDIO
#define MZ_FILE void *
@@ -6862,7 +6942,11 @@
#ifdef __clang__
#pragma clang diagnostic pop
#endif
-}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+} // namespace miniz
#else
// Reuse MINIZ_LITTE_ENDIAN macro
@@ -6887,6 +6971,26 @@
// return bint.c[0] == 1;
//}
+static void SetErrorMessage(const std::string &msg, const char **err) {
+ if (err) {
+#ifdef _WIN32
+ (*err) = _strdup(msg.c_str());
+#else
+ (*err) = strdup(msg.c_str());
+#endif
+ }
+}
+
+static const int kEXRVersionSize = 8;
+
+static void cpy2(unsigned short *dst_val, const unsigned short *src_val) {
+ unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
+ const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+}
+
static void swap2(unsigned short *val) {
#ifdef MINIZ_LITTLE_ENDIAN
(void)val;
@@ -6900,6 +7004,43 @@
#endif
}
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+static void cpy4(int *dst_val, const int *src_val) {
+ unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
+ const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+}
+
+static void cpy4(unsigned int *dst_val, const unsigned int *src_val) {
+ unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
+ const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+}
+
+static void cpy4(float *dst_val, const float *src_val) {
+ unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
+ const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
static void swap4(unsigned int *val) {
#ifdef MINIZ_LITTLE_ENDIAN
(void)val;
@@ -6915,6 +7056,22 @@
#endif
}
+#if 0
+static void cpy8(tinyexr::tinyexr_uint64 *dst_val, const tinyexr::tinyexr_uint64 *src_val) {
+ unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
+ const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ dst[4] = src[4];
+ dst[5] = src[5];
+ dst[6] = src[6];
+ dst[7] = src[7];
+}
+#endif
+
static void swap8(tinyexr::tinyexr_uint64 *val) {
#ifdef MINIZ_LITTLE_ENDIAN
(void)val;
@@ -7050,11 +7207,27 @@
// #define IMF_B44_COMPRESSION 6
// #define IMF_B44A_COMPRESSION 7
-static const char *ReadString(std::string *s, const char *ptr) {
+#ifdef __clang__
+#pragma clang diagnostic push
+
+#if __has_warning("-Wzero-as-null-pointer-constant")
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif
+
+#endif
+
+static const char *ReadString(std::string *s, const char *ptr, size_t len) {
// Read untile NULL(\0).
const char *p = ptr;
const char *q = ptr;
- while ((*q) != 0) q++;
+ while ((size_t(q - ptr) < len) && (*q) != 0) {
+ q++;
+ }
+
+ if (size_t(q - ptr) >= len) {
+ (*s) = std::string();
+ return NULL;
+ }
(*s) = std::string(p, q);
@@ -7091,6 +7264,24 @@
memcpy(&data_len, marker, sizeof(uint32_t));
tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
+ if (data_len == 0) {
+ 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);
size -= sizeof(uint32_t);
@@ -7181,7 +7372,7 @@
}
} HeaderInfo;
-static void ReadChannelInfo(std::vector<ChannelInfo> &channels,
+static bool ReadChannelInfo(std::vector<ChannelInfo> &channels,
const std::vector<unsigned char> &data) {
const char *p = reinterpret_cast<const char *>(&data.at(0));
@@ -7190,7 +7381,24 @@
break;
}
ChannelInfo info;
- p = ReadString(&info.name, p);
+
+ tinyexr_int64 data_len = static_cast<tinyexr_int64>(data.size()) -
+ (p - reinterpret_cast<const char *>(data.data()));
+ if (data_len < 0) {
+ return false;
+ }
+
+ p = ReadString(&info.name, p, size_t(data_len));
+ if ((p == NULL) && (info.name.empty())) {
+ // Buffer overrun. Issue #51.
+ return false;
+ }
+
+ const unsigned char *data_end =
+ reinterpret_cast<const unsigned char *>(p) + 16;
+ if (data_end >= (data.data() + data.size())) {
+ return false;
+ }
memcpy(&info.pixel_type, p, sizeof(int));
p += 4;
@@ -7207,6 +7415,8 @@
channels.push_back(info);
}
+
+ return true;
}
static void WriteChannelInfo(std::vector<unsigned char> &data,
@@ -7323,22 +7533,36 @@
compressedSize = outSize;
#endif
+
+ // Use uncompressed data when compressed data is larger than uncompressed.
+ // (Issue 40)
+ if (compressedSize >= src_size) {
+ compressedSize = src_size;
+ memcpy(dst, src, src_size);
+ }
}
-static void DecompressZip(unsigned char *dst,
+static bool DecompressZip(unsigned char *dst,
unsigned long *uncompressed_size /* inout */,
const unsigned char *src, unsigned long src_size) {
+ if ((*uncompressed_size) == src_size) {
+ // Data is not compressed(Issue 40).
+ memcpy(dst, src, src_size);
+ return true;
+ }
std::vector<unsigned char> tmpBuf(*uncompressed_size);
#if TINYEXR_USE_MINIZ
int ret =
miniz::mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
- assert(ret == miniz::MZ_OK);
- (void)ret;
+ if (miniz::MZ_OK != ret) {
+ return false;
+ }
#else
int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
- assert(ret == Z_OK);
- (void)ret;
+ if (Z_OK != ret) {
+ return false;
+ }
#endif
//
@@ -7378,6 +7602,8 @@
break;
}
}
+
+ return true;
}
// RLE code from OpenEXR --------------------------------------
@@ -7387,6 +7613,20 @@
#pragma clang diagnostic ignored "-Wsign-conversion"
#endif
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4204) // nonstandard extension used : non-constant
+ // aggregate initializer (also supported by GNU
+ // C and C99, so no big deal)
+#pragma warning(disable : 4244) // 'initializing': conversion from '__int64' to
+ // 'int', possible loss of data
+#pragma warning(disable : 4267) // 'argument': conversion from '__int64' to
+ // 'int', possible loss of data
+#pragma warning(disable : 4996) // 'strdup': The POSIX name for this item is
+ // deprecated. Instead, use the ISO C and C++
+ // conformant name: _strdup.
+#endif
+
const int MIN_RUN_LENGTH = 3;
const int MAX_RUN_LENGTH = 127;
@@ -7479,6 +7719,7 @@
#ifdef __clang__
#pragma clang diagnostic pop
#endif
+
// End of RLE code from OpenEXR -----------------------------------
static void CompressRle(unsigned char *dst,
@@ -7488,7 +7729,7 @@
//
// Apply EXR-specific? postprocess. Grabbed from OpenEXR's
- // ImfZipCompressor.cpp
+ // ImfRleCompressor.cpp
//
//
@@ -7539,23 +7780,36 @@
assert(outSize > 0);
compressedSize = static_cast<tinyexr::tinyexr_uint64>(outSize);
+
+ // Use uncompressed data when compressed data is larger than uncompressed.
+ // (Issue 40)
+ if (compressedSize >= src_size) {
+ compressedSize = src_size;
+ memcpy(dst, src, src_size);
+ }
}
static void DecompressRle(unsigned char *dst,
const unsigned long uncompressed_size,
const unsigned char *src, unsigned long src_size) {
+ if (uncompressed_size == src_size) {
+ // Data is not compressed(Issue 40).
+ memcpy(dst, src, src_size);
+ return;
+ }
+
std::vector<unsigned char> tmpBuf(uncompressed_size);
int ret = rleUncompress(static_cast<int>(src_size),
static_cast<int>(uncompressed_size),
reinterpret_cast<const signed char *>(src),
- reinterpret_cast<char *>(dst));
+ reinterpret_cast<char *>(&tmpBuf.at(0)));
assert(ret == static_cast<int>(uncompressed_size));
(void)ret;
//
// Apply EXR-specific? postprocess. Grabbed from OpenEXR's
- // ImfZipCompressor.cpp
+ // ImfRleCompressor.cpp
//
// Predictor.
@@ -7602,6 +7856,12 @@
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wc++11-extensions"
#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+
+#if __has_warning("-Wcast-qual")
+#pragma clang diagnostic ignored "-Wcast-qual"
+#endif
+
#endif
//
@@ -8079,8 +8339,8 @@
// for all array entries.
//
- int hlink[HUF_ENCSIZE];
- long long *fHeap[HUF_ENCSIZE];
+ std::vector<int> hlink(HUF_ENCSIZE);
+ std::vector<long long *> fHeap(HUF_ENCSIZE);
*im = 0;
@@ -8139,8 +8399,8 @@
std::make_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
- long long scode[HUF_ENCSIZE];
- memset(scode, 0, sizeof(long long) * HUF_ENCSIZE);
+ std::vector<long long> scode(HUF_ENCSIZE);
+ memset(scode.data(), 0, sizeof(long long) * HUF_ENCSIZE);
while (nf > 1) {
//
@@ -8212,8 +8472,8 @@
// code table from scode into frq.
//
- hufCanonicalCodeTable(scode);
- memcpy(frq, scode, sizeof(long long) * HUF_ENCSIZE);
+ hufCanonicalCodeTable(scode.data());
+ memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE);
}
//
@@ -8549,26 +8809,63 @@
lc += 8; \
}
-#define getCode(po, rlc, c, lc, in, out, oe) \
- { \
- if (po == rlc) { \
- if (lc < 8) getChar(c, lc, in); \
- \
- lc -= 8; \
- \
- unsigned char cs = (c >> lc); \
- \
- if (out + cs > oe) return false; \
- \
- unsigned short s = out[-1]; \
- \
- while (cs-- > 0) *out++ = s; \
- } else if (out < oe) { \
- *out++ = po; \
- } else { \
- return false; \
- } \
+#if 0
+#define getCode(po, rlc, c, lc, in, out, ob, oe) \
+ { \
+ if (po == rlc) { \
+ if (lc < 8) getChar(c, lc, in); \
+ \
+ lc -= 8; \
+ \
+ unsigned char cs = (c >> lc); \
+ \
+ if (out + cs > oe) return false; \
+ \
+ /* TinyEXR issue 78 */ \
+ unsigned short s = out[-1]; \
+ \
+ while (cs-- > 0) *out++ = s; \
+ } else if (out < oe) { \
+ *out++ = po; \
+ } else { \
+ return false; \
+ } \
}
+#else
+static bool getCode(int po, int rlc, long long &c, int &lc, const char *&in,
+ const char *in_end, unsigned short *&out,
+ const unsigned short *ob, const unsigned short *oe) {
+ (void)ob;
+ if (po == rlc) {
+ if (lc < 8) {
+ /* TinyEXR issue 78 */
+ if ((in + 1) >= in_end) {
+ return false;
+ }
+
+ getChar(c, lc, in);
+ }
+
+ lc -= 8;
+
+ unsigned char cs = (c >> lc);
+
+ if (out + cs > oe) return false;
+
+ // Bounds check for safety
+ // Issue 100.
+ if ((out - 1) < ob) return false;
+ unsigned short s = out[-1];
+
+ while (cs-- > 0) *out++ = s;
+ } else if (out < oe) {
+ *out++ = po;
+ } else {
+ return false;
+ }
+ return true;
+}
+#endif
//
// Decode (uncompress) ni bits based on encoding & decoding tables:
@@ -8584,8 +8881,8 @@
{
long long c = 0;
int lc = 0;
- unsigned short *outb = out;
- unsigned short *oe = out + no;
+ unsigned short *outb = out; // begin
+ unsigned short *oe = out + no; // end
const char *ie = in + (ni + 7) / 8; // input byte size
//
@@ -8608,7 +8905,16 @@
//
lc -= pl.len;
- getCode(pl.lit, rlc, c, lc, in, out, oe);
+ // std::cout << "lit = " << pl.lit << std::endl;
+ // std::cout << "rlc = " << rlc << std::endl;
+ // std::cout << "c = " << c << std::endl;
+ // std::cout << "lc = " << lc << std::endl;
+ // std::cout << "in = " << in << std::endl;
+ // std::cout << "out = " << out << std::endl;
+ // std::cout << "oe = " << oe << std::endl;
+ if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) {
+ return false;
+ }
} else {
if (!pl.p) {
return false;
@@ -8635,7 +8941,9 @@
//
lc -= l;
- getCode(pl.p[j], rlc, c, lc, in, out, oe);
+ if (!getCode(pl.p[j], rlc, c, lc, in, ie, out, outb, oe)) {
+ return false;
+ }
break;
}
}
@@ -8662,7 +8970,9 @@
if (pl.len) {
lc -= pl.len;
- getCode(pl.lit, rlc, c, lc, in, out, oe);
+ if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) {
+ return false;
+ }
} else {
return false;
// invalidCode(); // wrong (long) code
@@ -8677,7 +8987,7 @@
return true;
}
-static void countFrequencies(long long freq[HUF_ENCSIZE],
+static void countFrequencies(std::vector<long long> &freq,
const unsigned short data[/*n*/], int n) {
for (int i = 0; i < HUF_ENCSIZE; ++i) freq[i] = 0;
@@ -8708,21 +9018,21 @@
char compressed[]) {
if (nRaw == 0) return 0;
- long long freq[HUF_ENCSIZE];
+ std::vector<long long> freq(HUF_ENCSIZE);
countFrequencies(freq, raw, nRaw);
int im = 0;
int iM = 0;
- hufBuildEncTable(freq, &im, &iM);
+ hufBuildEncTable(freq.data(), &im, &iM);
char *tableStart = compressed + 20;
char *tableEnd = tableStart;
- hufPackEncTable(freq, im, iM, &tableEnd);
+ hufPackEncTable(freq.data(), im, iM, &tableEnd);
int tableLength = tableEnd - tableStart;
char *dataStart = tableEnd;
- int nBits = hufEncode(freq, raw, nRaw, iM, dataStart);
+ int nBits = hufEncode(freq.data(), raw, nRaw, iM, dataStart);
int data_length = (nBits + 7) / 8;
writeUInt(compressed, im);
@@ -8735,9 +9045,9 @@
}
static bool hufUncompress(const char compressed[], int nCompressed,
- unsigned short raw[], int nRaw) {
+ std::vector<unsigned short> *raw) {
if (nCompressed == 0) {
- if (nRaw != 0) return false;
+ if (raw->size() != 0) return false;
return false;
}
@@ -8778,7 +9088,8 @@
}
hufBuildDecTable(&freq.at(0), im, iM, &hdec.at(0));
- hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, nRaw, raw);
+ hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, raw->size(),
+ raw->data());
}
// catch (...)
//{
@@ -8859,11 +9170,15 @@
#pragma clang diagnostic pop
#endif // __clang__
-static bool CompressPiz(unsigned char *outPtr, unsigned int &outSize,
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize,
const unsigned char *inPtr, size_t inSize,
const std::vector<ChannelInfo> &channelInfo,
int data_width, int num_lines) {
- unsigned char bitmap[BITMAP_SIZE];
+ std::vector<unsigned char> bitmap(BITMAP_SIZE);
unsigned short minNonZero;
unsigned short maxNonZero;
@@ -8914,12 +9229,12 @@
}
}
- bitmapFromData(&tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()), bitmap,
- minNonZero, maxNonZero);
+ bitmapFromData(&tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()),
+ bitmap.data(), minNonZero, maxNonZero);
- unsigned short lut[USHORT_RANGE];
- unsigned short maxValue = forwardLutFromBitmap(bitmap, lut);
- applyLut(lut, &tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()));
+ std::vector<unsigned short> lut(USHORT_RANGE);
+ unsigned short maxValue = forwardLutFromBitmap(bitmap.data(), lut.data());
+ applyLut(lut.data(), &tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()));
//
// Store range compression info in _outBuffer
@@ -8966,17 +9281,30 @@
hufCompress(&tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()), buf);
memcpy(lengthPtr, &length, sizeof(int));
- outSize = static_cast<unsigned int>(
+ (*outSize) = static_cast<unsigned int>(
(reinterpret_cast<unsigned char *>(buf) - outPtr) +
static_cast<unsigned int>(length));
+
+ // Use uncompressed data when compressed data is larger than uncompressed.
+ // (Issue 40)
+ if ((*outSize) >= inSize) {
+ (*outSize) = static_cast<unsigned int>(inSize);
+ memcpy(outPtr, inPtr, inSize);
+ }
return true;
}
static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
- size_t tmpBufSize, int num_channels,
+ size_t tmpBufSize, size_t inLen, int num_channels,
const EXRChannelInfo *channels, int data_width,
int num_lines) {
- unsigned char bitmap[BITMAP_SIZE];
+ if (inLen == tmpBufSize) {
+ // Data is not compressed(Issue 40).
+ memcpy(outPtr, inPtr, inLen);
+ return true;
+ }
+
+ std::vector<unsigned char> bitmap(BITMAP_SIZE);
unsigned short minNonZero;
unsigned short maxNonZero;
@@ -8986,11 +9314,13 @@
return false;
#endif
- memset(bitmap, 0, BITMAP_SIZE);
+ memset(bitmap.data(), 0, BITMAP_SIZE);
const unsigned char *ptr = inPtr;
- minNonZero = *(reinterpret_cast<const unsigned short *>(ptr));
- maxNonZero = *(reinterpret_cast<const unsigned short *>(ptr + 2));
+ // minNonZero = *(reinterpret_cast<const unsigned short *>(ptr));
+ tinyexr::cpy2(&minNonZero, reinterpret_cast<const unsigned short *>(ptr));
+ // maxNonZero = *(reinterpret_cast<const unsigned short *>(ptr + 2));
+ tinyexr::cpy2(&maxNonZero, reinterpret_cast<const unsigned short *>(ptr + 2));
ptr += 4;
if (maxNonZero >= BITMAP_SIZE) {
@@ -9003,9 +9333,9 @@
ptr += maxNonZero - minNonZero + 1;
}
- unsigned short lut[USHORT_RANGE];
- memset(lut, 0, sizeof(unsigned short) * USHORT_RANGE);
- unsigned short maxValue = reverseLutFromBitmap(bitmap, lut);
+ std::vector<unsigned short> lut(USHORT_RANGE);
+ memset(lut.data(), 0, sizeof(unsigned short) * USHORT_RANGE);
+ unsigned short maxValue = reverseLutFromBitmap(bitmap.data(), lut.data());
//
// Huffman decoding
@@ -9013,12 +9343,12 @@
int length;
- length = *(reinterpret_cast<const int *>(ptr));
+ // length = *(reinterpret_cast<const int *>(ptr));
+ tinyexr::cpy4(&length, reinterpret_cast<const int *>(ptr));
ptr += sizeof(int);
std::vector<unsigned short> tmpBuffer(tmpBufSize);
- hufUncompress(reinterpret_cast<const char *>(ptr), length, &tmpBuffer.at(0),
- static_cast<int>(tmpBufSize));
+ hufUncompress(reinterpret_cast<const char *>(ptr), length, &tmpBuffer);
//
// Wavelet decoding
@@ -9059,7 +9389,7 @@
// Expand the pixel data to their original range
//
- applyLut(lut, &tmpBuffer.at(0), static_cast<int>(tmpBufSize));
+ applyLut(lut.data(), &tmpBuffer.at(0), static_cast<int>(tmpBufSize));
for (int y = 0; y < num_lines; y++) {
for (size_t i = 0; i < channelData.size(); ++i) {
@@ -9150,6 +9480,11 @@
const ZFPCompressionParam ¶m) {
size_t uncompressed_size = dst_width * dst_num_lines * num_channels;
+ if (uncompressed_size == src_size) {
+ // Data is not compressed(Issue 40).
+ memcpy(dst, src, src_size);
+ }
+
zfp_stream *zfp = NULL;
zfp_field *field = NULL;
@@ -9279,7 +9614,8 @@
// -----------------------------------------------------------------
//
-static void DecodePixelData(/* out */ unsigned char **out_images,
+// 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,
int compression_type, int line_order, int width,
@@ -9291,17 +9627,22 @@
const std::vector<size_t> &channel_offset_list) {
if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { // PIZ
#if TINYEXR_USE_PIZ
+ if ((width == 0) || (num_lines == 0) || (pixel_data_size == 0)) {
+ // Invalid input #90
+ return false;
+ }
+
// Allocate original data size.
std::vector<unsigned char> outBuf(static_cast<size_t>(
static_cast<size_t>(width * num_lines) * pixel_data_size));
- size_t tmpBufLen = static_cast<size_t>(
- static_cast<size_t>(width * num_lines) * pixel_data_size);
+ size_t tmpBufLen = outBuf.size();
bool ret = tinyexr::DecompressPiz(
reinterpret_cast<unsigned char *>(&outBuf.at(0)), data_ptr, tmpBufLen,
- static_cast<int>(num_channels), channels, width, num_lines);
+ data_len, static_cast<int>(num_channels), channels, width, num_lines);
assert(ret);
+ (void)ret;
// For PIZ_COMPRESSION:
// pixel sample data for channel 0 for scanline 0
@@ -9322,7 +9663,10 @@
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
FP16 hf;
- hf.u = line_ptr[u];
+ // 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));
@@ -9365,7 +9709,9 @@
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
channel_offset_list[c] * static_cast<size_t>(width)));
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
- unsigned int val = line_ptr[u];
+ unsigned int val;
+ // val = line_ptr[u];
+ tinyexr::cpy4(&val, line_ptr + u);
tinyexr::swap4(&val);
@@ -9391,7 +9737,9 @@
v * pixel_data_size * static_cast<size_t>(x_stride) +
channel_offset_list[c] * static_cast<size_t>(x_stride)));
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
- float val = line_ptr[u];
+ float val;
+ // val = line_ptr[u];
+ tinyexr::cpy4(&val, line_ptr + u);
tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
@@ -9415,6 +9763,7 @@
}
#else
assert(0 && "PIZ is enabled in this build");
+ return false;
#endif
} else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS ||
@@ -9424,11 +9773,13 @@
static_cast<size_t>(num_lines) *
pixel_data_size);
- unsigned long dstLen = outBuf.size();
+ unsigned long dstLen = static_cast<unsigned long>(outBuf.size());
assert(dstLen > 0);
- tinyexr::DecompressZip(reinterpret_cast<unsigned char *>(&outBuf.at(0)),
- &dstLen, data_ptr,
- static_cast<unsigned long>(data_len));
+ if (!tinyexr::DecompressZip(
+ reinterpret_cast<unsigned char *>(&outBuf.at(0)), &dstLen, data_ptr,
+ static_cast<unsigned long>(data_len))) {
+ return false;
+ }
// For ZIP_COMPRESSION:
// pixel sample data for channel 0 for scanline 0
@@ -9450,7 +9801,8 @@
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
tinyexr::FP16 hf;
- 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));
@@ -9493,7 +9845,9 @@
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
channel_offset_list[c] * static_cast<size_t>(width)));
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
- unsigned int val = line_ptr[u];
+ unsigned int val;
+ // val = line_ptr[u];
+ tinyexr::cpy4(&val, line_ptr + u);
tinyexr::swap4(&val);
@@ -9519,7 +9873,9 @@
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
channel_offset_list[c] * static_cast<size_t>(width)));
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
- float val = line_ptr[u];
+ float val;
+ // val = line_ptr[u];
+ tinyexr::cpy4(&val, line_ptr + u);
tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
@@ -9539,6 +9895,7 @@
}
} else {
assert(0);
+ return false;
}
}
} else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) {
@@ -9547,13 +9904,13 @@
static_cast<size_t>(num_lines) *
pixel_data_size);
- unsigned long dstLen = outBuf.size();
+ unsigned long dstLen = static_cast<unsigned long>(outBuf.size());
assert(dstLen > 0);
tinyexr::DecompressRle(reinterpret_cast<unsigned char *>(&outBuf.at(0)),
dstLen, data_ptr,
static_cast<unsigned long>(data_len));
- // For ZIP_COMPRESSION:
+ // For RLE_COMPRESSION:
// pixel sample data for channel 0 for scanline 0
// pixel sample data for channel 1 for scanline 0
// pixel sample data for channel ... for scanline 0
@@ -9573,7 +9930,8 @@
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
tinyexr::FP16 hf;
- 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));
@@ -9616,7 +9974,9 @@
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
channel_offset_list[c] * static_cast<size_t>(width)));
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
- unsigned int val = line_ptr[u];
+ unsigned int val;
+ // val = line_ptr[u];
+ tinyexr::cpy4(&val, line_ptr + u);
tinyexr::swap4(&val);
@@ -9642,7 +10002,9 @@
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
channel_offset_list[c] * static_cast<size_t>(width)));
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
- float val = line_ptr[u];
+ float val;
+ // val = line_ptr[u];
+ tinyexr::cpy4(&val, line_ptr + u);
tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
@@ -9662,6 +10024,7 @@
}
} else {
assert(0);
+ return false;
}
}
} else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
@@ -9670,7 +10033,7 @@
if (!FindZFPCompressionParam(&zfp_compression_param, attributes,
num_attributes)) {
assert(0);
- return;
+ return false;
}
// Allocate original data size.
@@ -9704,7 +10067,8 @@
&outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
channel_offset_list[c] * static_cast<size_t>(width)));
for (size_t u = 0; u < static_cast<size_t>(width); u++) {
- float val = line_ptr[u];
+ float val;
+ tinyexr::cpy4(&val, line_ptr + u);
tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
@@ -9724,6 +10088,7 @@
}
} else {
assert(0);
+ return false;
}
}
#else
@@ -9731,94 +10096,130 @@
(void)num_attributes;
(void)num_channels;
assert(0);
+ return false;
#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));
+ 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 += y * x_stride;
+ 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;
+
+ // hf.u = line_ptr[u];
+ tinyexr::cpy2(&(hf.u), line_ptr + u);
+
+ tinyexr::swap2(reinterpret_cast<unsigned short *>(&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 {
- outLine += (height - 1 - y) * x_stride;
+ assert(0);
+ return false;
}
+ } 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));
- for (int u = 0; u < width; u++) {
- tinyexr::FP16 hf;
-
- hf.u = line_ptr[u];
-
- tinyexr::swap2(reinterpret_cast<unsigned short *>(&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 += 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) >
+ (data_ptr + data_len)) {
+ // Insufficient data size
+ return false;
}
for (int u = 0; u < width; u++) {
- tinyexr::FP16 hf;
+ float val;
+ tinyexr::cpy4(&val, line_ptr + u);
- hf.u = line_ptr[u];
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
- tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
-
- tinyexr::FP32 f32 = half_to_float(hf);
-
- outLine[u] = f32.f;
+ outLine[u] = val;
}
- } else {
- assert(0);
- }
- } 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;
- }
+ 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);
+ }
- for (int u = 0; u < width; u++) {
- float val = line_ptr[u];
+ for (int u = 0; u < width; u++) {
+ if (reinterpret_cast<const unsigned char *>(line_ptr + u) >=
+ (data_ptr + data_len)) {
+ // Corrupsed data?
+ return false;
+ }
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
+ unsigned int val;
+ tinyexr::cpy4(&val, line_ptr + u);
- 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));
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
- 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;
- }
-
- for (int u = 0; u < width; u++) {
- unsigned int val = line_ptr[u];
-
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
-
- outLine[u] = val;
+ outLine[u] = val;
+ }
}
}
}
}
+
+ return true;
}
static void DecodeTiledPixelData(
@@ -9854,7 +10255,7 @@
num_channels, channels, channel_offset_list);
}
-static void ComputeChannelLayout(std::vector<size_t> *channel_offset_list,
+static bool ComputeChannelLayout(std::vector<size_t> *channel_offset_list,
int *pixel_data_size, size_t *channel_offset,
int num_channels,
const EXRChannelInfo *channels) {
@@ -9875,9 +10276,11 @@
(*pixel_data_size) += sizeof(unsigned int);
(*channel_offset) += sizeof(unsigned int);
} else {
- assert(0);
+ // ???
+ return false;
}
}
+ return true;
}
static unsigned char **AllocateImage(int num_channels,
@@ -9985,8 +10388,11 @@
// Read attributes
size_t orig_size = size;
- for (;;) {
+ for (size_t nattr = 0; nattr < TINYEXR_MAX_HEADER_ATTRIBUTES; nattr++) {
if (0 == size) {
+ if (err) {
+ (*err) += "Insufficient data size for attributes.\n";
+ }
return TINYEXR_ERROR_INVALID_DATA;
} else if (marker[0] == '\0') {
size--;
@@ -9999,6 +10405,9 @@
size_t marker_size;
if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size,
marker, size)) {
+ if (err) {
+ (*err) += "Failed to read attribute.\n";
+ }
return TINYEXR_ERROR_INVALID_DATA;
}
marker += marker_size;
@@ -10023,8 +10432,7 @@
} else if (attr_name.compare("compression") == 0) {
bool ok = false;
- if ((data[0] >= TINYEXR_COMPRESSIONTYPE_NONE) &&
- (data[0] < TINYEXR_COMPRESSIONTYPE_PIZ)) {
+ if (data[0] < TINYEXR_COMPRESSIONTYPE_PIZ) {
ok = true;
}
@@ -10068,11 +10476,16 @@
// xSampling: int
// ySampling: int
- ReadChannelInfo(info->channels, data);
+ if (!ReadChannelInfo(info->channels, data)) {
+ if (err) {
+ (*err) += "Failed to parse channel info.\n";
+ }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
if (info->channels.size() < 1) {
if (err) {
- (*err) = "# of channels is zero.";
+ (*err) += "# of channels is zero.\n";
}
return TINYEXR_ERROR_INVALID_DATA;
}
@@ -10080,63 +10493,81 @@
has_channels = true;
} else if (attr_name.compare("dataWindow") == 0) {
- memcpy(&info->data_window[0], &data.at(0), sizeof(int));
- memcpy(&info->data_window[1], &data.at(4), sizeof(int));
- memcpy(&info->data_window[2], &data.at(8), sizeof(int));
- memcpy(&info->data_window[3], &data.at(12), sizeof(int));
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[0]));
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[1]));
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[2]));
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[3]));
-
- has_data_window = true;
+ if (data.size() >= 16) {
+ memcpy(&info->data_window[0], &data.at(0), sizeof(int));
+ memcpy(&info->data_window[1], &data.at(4), sizeof(int));
+ memcpy(&info->data_window[2], &data.at(8), sizeof(int));
+ memcpy(&info->data_window[3], &data.at(12), sizeof(int));
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[0]));
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[1]));
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[2]));
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->data_window[3]));
+ has_data_window = true;
+ }
} else if (attr_name.compare("displayWindow") == 0) {
- memcpy(&info->display_window[0], &data.at(0), sizeof(int));
- memcpy(&info->display_window[1], &data.at(4), sizeof(int));
- memcpy(&info->display_window[2], &data.at(8), sizeof(int));
- memcpy(&info->display_window[3], &data.at(12), sizeof(int));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->display_window[0]));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->display_window[1]));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->display_window[2]));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->display_window[3]));
+ if (data.size() >= 16) {
+ memcpy(&info->display_window[0], &data.at(0), sizeof(int));
+ memcpy(&info->display_window[1], &data.at(4), sizeof(int));
+ memcpy(&info->display_window[2], &data.at(8), sizeof(int));
+ memcpy(&info->display_window[3], &data.at(12), sizeof(int));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->display_window[0]));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->display_window[1]));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->display_window[2]));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->display_window[3]));
- has_display_window = true;
+ has_display_window = true;
+ }
} else if (attr_name.compare("lineOrder") == 0) {
- info->line_order = static_cast<int>(data[0]);
- has_line_order = true;
+ if (data.size() >= 1) {
+ info->line_order = static_cast<int>(data[0]);
+ has_line_order = true;
+ }
} else if (attr_name.compare("pixelAspectRatio") == 0) {
- memcpy(&info->pixel_aspect_ratio, &data.at(0), sizeof(float));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->pixel_aspect_ratio));
- has_pixel_aspect_ratio = true;
+ if (data.size() >= sizeof(float)) {
+ memcpy(&info->pixel_aspect_ratio, &data.at(0), sizeof(float));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->pixel_aspect_ratio));
+ has_pixel_aspect_ratio = true;
+ }
} else if (attr_name.compare("screenWindowCenter") == 0) {
- memcpy(&info->screen_window_center[0], &data.at(0), sizeof(float));
- memcpy(&info->screen_window_center[1], &data.at(4), sizeof(float));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->screen_window_center[0]));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->screen_window_center[1]));
- has_screen_window_center = true;
+ if (data.size() >= 8) {
+ memcpy(&info->screen_window_center[0], &data.at(0), sizeof(float));
+ memcpy(&info->screen_window_center[1], &data.at(4), sizeof(float));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->screen_window_center[0]));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->screen_window_center[1]));
+ has_screen_window_center = true;
+ }
} else if (attr_name.compare("screenWindowWidth") == 0) {
- memcpy(&info->screen_window_width, &data.at(0), sizeof(float));
- tinyexr::swap4(
- reinterpret_cast<unsigned int *>(&info->screen_window_width));
+ if (data.size() >= sizeof(float)) {
+ memcpy(&info->screen_window_width, &data.at(0), sizeof(float));
+ tinyexr::swap4(
+ reinterpret_cast<unsigned int *>(&info->screen_window_width));
- has_screen_window_width = true;
+ has_screen_window_width = true;
+ }
} else if (attr_name.compare("chunkCount") == 0) {
- memcpy(&info->chunk_count, &data.at(0), sizeof(int));
- tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->chunk_count));
+ if (data.size() >= sizeof(int)) {
+ memcpy(&info->chunk_count, &data.at(0), sizeof(int));
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->chunk_count));
+ }
} else {
- // Custom attribute(up to TINYEXR_MAX_ATTRIBUTES)
- if (info->attributes.size() < TINYEXR_MAX_ATTRIBUTES) {
+ // Custom attribute(up to TINYEXR_MAX_CUSTOM_ATTRIBUTES)
+ if (info->attributes.size() < TINYEXR_MAX_CUSTOM_ATTRIBUTES) {
EXRAttribute attrib;
+#ifdef _MSC_VER
+ strncpy_s(attrib.name, attr_name.c_str(), 255);
+ strncpy_s(attrib.type, attr_type.c_str(), 255);
+#else
strncpy(attrib.name, attr_name.c_str(), 255);
- attrib.name[255] = '\0';
strncpy(attrib.type, attr_type.c_str(), 255);
+#endif
+ attrib.name[255] = '\0';
attrib.type[255] = '\0';
attrib.size = static_cast<int>(data.size());
attrib.value = static_cast<unsigned char *>(malloc(data.size()));
@@ -10170,7 +10601,7 @@
}
if (!has_data_window) {
- ss_err << "\"dataWindow\" attribute not found in the header."
+ ss_err << "\"dataWindow\" attribute not found in the header or invalid."
<< std::endl;
}
@@ -10230,7 +10661,11 @@
exr_header->channels = static_cast<EXRChannelInfo *>(malloc(
sizeof(EXRChannelInfo) * static_cast<size_t>(exr_header->num_channels)));
for (size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
+#ifdef _MSC_VER
+ strncpy_s(exr_header->channels[c].name, info.channels[c].name.c_str(), 255);
+#else
strncpy(exr_header->channels[c].name, info.channels[c].name.c_str(), 255);
+#endif
// manually add '\0' for safety.
exr_header->channels[c].name[255] = '\0';
@@ -10253,15 +10688,30 @@
exr_header->requested_pixel_types[c] = info.channels[c].pixel_type;
}
- assert(info.attributes.size() < TINYEXR_MAX_ATTRIBUTES);
exr_header->num_custom_attributes = static_cast<int>(info.attributes.size());
- for (size_t i = 0; i < info.attributes.size(); i++) {
- memcpy(exr_header->custom_attributes[i].name, info.attributes[i].name, 256);
- memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type, 256);
- exr_header->custom_attributes[i].size = info.attributes[i].size;
- // Just copy poiner
- exr_header->custom_attributes[i].value = info.attributes[i].value;
+ if (exr_header->num_custom_attributes > 0) {
+ // TODO(syoyo): Report warning when # of attributes exceeds
+ // `TINYEXR_MAX_CUSTOM_ATTRIBUTES`
+ if (exr_header->num_custom_attributes > TINYEXR_MAX_CUSTOM_ATTRIBUTES) {
+ exr_header->num_custom_attributes = TINYEXR_MAX_CUSTOM_ATTRIBUTES;
+ }
+
+ exr_header->custom_attributes = static_cast<EXRAttribute *>(malloc(
+ sizeof(EXRAttribute) * size_t(exr_header->num_custom_attributes)));
+
+ for (size_t i = 0; i < info.attributes.size(); i++) {
+ memcpy(exr_header->custom_attributes[i].name, info.attributes[i].name,
+ 256);
+ memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type,
+ 256);
+ exr_header->custom_attributes[i].size = info.attributes[i].size;
+ // Just copy poiner
+ exr_header->custom_attributes[i].value = info.attributes[i].value;
+ }
+
+ } else {
+ exr_header->custom_attributes = NULL;
}
exr_header->header_len = info.header_len;
@@ -10269,7 +10719,8 @@
static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
const std::vector<tinyexr::tinyexr_uint64> &offsets,
- const unsigned char *head) {
+ const unsigned char *head, const size_t size,
+ std::string *err) {
int num_channels = exr_header->num_channels;
int num_scanline_blocks = 1;
@@ -10284,30 +10735,54 @@
int data_width = exr_header->data_window[2] - exr_header->data_window[0] + 1;
int data_height = exr_header->data_window[3] - exr_header->data_window[1] + 1;
+ if ((data_width < 0) || (data_height < 0)) {
+ if (err) {
+ std::stringstream ss;
+ ss << "Invalid data width or data height: " << data_width << ", " << data_height << std::endl;
+ (*err) += ss.str();
+ }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
size_t num_blocks = offsets.size();
std::vector<size_t> channel_offset_list;
int pixel_data_size = 0;
size_t channel_offset = 0;
- tinyexr::ComputeChannelLayout(&channel_offset_list, &pixel_data_size,
- &channel_offset, num_channels,
- exr_header->channels);
+ if (!tinyexr::ComputeChannelLayout(&channel_offset_list, &pixel_data_size,
+ &channel_offset, num_channels,
+ exr_header->channels)) {
+ if (err) {
+ (*err) += "Failed to compute channel layout.\n";
+ }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ bool invalid_data = false; // TODO(LTE): Use atomic lock for MT safety.
if (exr_header->tiled) {
size_t num_tiles = offsets.size(); // = # of blocks
exr_image->tiles = static_cast<EXRTile *>(
- malloc(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++) {
// Allocate memory for each tile.
exr_image->tiles[tile_idx].images = tinyexr::AllocateImage(
num_channels, exr_header->channels, exr_header->requested_pixel_types,
- data_width, data_height);
+ 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;
+ }
+
+ 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]);
@@ -10319,14 +10794,24 @@
tinyexr::swap4(reinterpret_cast<unsigned int *>(&tile_coordinates[3]));
// @todo{ LoD }
- assert(tile_coordinates[2] == 0);
- assert(tile_coordinates[3] == 0);
+ 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));
- assert(data_len >= 4);
+
+ 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;
@@ -10354,6 +10839,17 @@
}
} 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);
+ if ((total_data_len == 0) || (total_data_len >= 0x4000000000)) {
+ if (err) {
+ std::stringstream ss;
+ ss << "Image data size is zero or too large: width = " << data_width << ", height = " << data_height << ", channels = " << num_channels << std::endl;
+ (*err) += ss.str();
+ }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
exr_image->images = tinyexr::AllocateImage(
num_channels, exr_header->channels, exr_header->requested_pixel_types,
data_width, data_height);
@@ -10363,42 +10859,68 @@
#endif
for (int y = 0; y < static_cast<int>(num_blocks); y++) {
size_t y_idx = static_cast<size_t>(y);
- const unsigned char *data_ptr =
- reinterpret_cast<const unsigned char *>(head + offsets[y_idx]);
- // 4 byte: scan line
- // 4 byte: data size
- // ~ : pixel data(uncompressed or compressed)
- 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));
- int end_line_no = (std::min)(line_no + num_scanline_blocks,
- (exr_header->data_window[3] + 1));
+ 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 num_lines = end_line_no - line_no;
- assert(num_lines > 0);
+ 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));
- // Move to data addr: 8 = 4 + 4;
- data_ptr += 8;
+ if (size_t(data_len) > data_size) {
+ invalid_data = true;
+ } else {
+ int end_line_no = (std::min)(line_no + num_scanline_blocks,
+ (exr_header->data_window[3] + 1));
- // Adjust line_no with data_window.bmin.y
- line_no -= exr_header->data_window[1];
+ int num_lines = end_line_no - line_no;
+ // assert(num_lines > 0);
- 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);
+ 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
+ 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;
+ }
+ }
+ }
+ }
+ }
} // omp parallel
}
+ if (invalid_data) {
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
// Overwrite `pixel_type` with `requested_pixel_type`.
{
for (int c = 0; c < exr_header->num_channels; c++) {
@@ -10416,14 +10938,47 @@
return TINYEXR_SUCCESS;
}
+static bool ReconstructLineOffsets(
+ std::vector<tinyexr::tinyexr_uint64> *offsets, size_t n,
+ const unsigned char *head, const unsigned char *marker, const size_t size) {
+ assert(head < marker);
+ assert(offsets->size() == n);
+
+ for (size_t i = 0; i < n; i++) {
+ size_t offset = static_cast<size_t>(marker - head);
+ // Offset should not exceed whole EXR file/data size.
+ if ((offset + sizeof(tinyexr::tinyexr_uint64)) >= size) {
+ return false;
+ }
+
+ int y;
+ unsigned int data_len;
+
+ memcpy(&y, marker, sizeof(int));
+ memcpy(&data_len, marker + 4, sizeof(unsigned int));
+
+ if (data_len >= size) {
+ return false;
+ }
+
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&y));
+ tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
+
+ (*offsets)[i] = offset;
+
+ marker += data_len + 8; // 8 = 4 bytes(y) + 4 bytes(data_len)
+ }
+
+ return true;
+}
+
static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
const unsigned char *head,
- const unsigned char *marker, const char **err) {
+ const unsigned char *marker, const size_t size,
+ const char **err) {
if (exr_image == NULL || exr_header == NULL || head == NULL ||
- marker == NULL) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ marker == NULL || (size <= tinyexr::kEXRVersionSize)) {
+ tinyexr::SetErrorMessage("Invalid argument for DecodeEXRImage().", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -10436,11 +10991,28 @@
num_scanline_blocks = 16;
}
- int data_width = exr_header->data_window[2] - exr_header->data_window[0] + 1;
- int data_height = exr_header->data_window[3] - exr_header->data_window[1] + 1;
+ int data_width = exr_header->data_window[2] - exr_header->data_window[0];
+ if (data_width >= std::numeric_limits<int>::max()) {
+ // Issue 63
+ tinyexr::SetErrorMessage("Invalid data window value", err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+ data_width++;
+
+ int data_height = exr_header->data_window[3] - exr_header->data_window[1];
+ if (data_height >= std::numeric_limits<int>::max()) {
+ tinyexr::SetErrorMessage("Invalid data height value", err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+ data_height++;
+
+ if ((data_width < 0) || (data_height < 0)) {
+ tinyexr::SetErrorMessage("data window or data height is negative.", err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
// Read offset tables.
- size_t num_blocks;
+ size_t num_blocks = 0;
if (exr_header->chunk_count > 0) {
// Use `chunkCount` attribute.
@@ -10474,13 +11046,69 @@
for (size_t y = 0; y < num_blocks; y++) {
tinyexr::tinyexr_uint64 offset;
+ // Issue #81
+ if ((marker + sizeof(tinyexr_uint64)) >= (head + size)) {
+ tinyexr::SetErrorMessage("Insufficient data size in offset table.", err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64));
tinyexr::swap8(&offset);
+ if (offset >= size) {
+ tinyexr::SetErrorMessage("Invalid offset value in DecodeEXRImage.", err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
marker += sizeof(tinyexr::tinyexr_uint64); // = 8
offsets[y] = offset;
}
- return DecodeChunk(exr_image, exr_header, offsets, head);
+ // If line offsets are invalid, we try to reconstruct it.
+ // See OpenEXR/IlmImf/ImfScanLineInputFile.cpp::readLineOffsets() for details.
+ for (size_t y = 0; y < num_blocks; y++) {
+ if (offsets[y] <= 0) {
+ // TODO(syoyo) Report as warning?
+ // if (err) {
+ // stringstream ss;
+ // ss << "Incomplete lineOffsets." << std::endl;
+ // (*err) += ss.str();
+ //}
+ bool ret =
+ ReconstructLineOffsets(&offsets, num_blocks, head, marker, size);
+ if (ret) {
+ // OK
+ break;
+ } else {
+ tinyexr::SetErrorMessage(
+ "Cannot reconstruct lineOffset table in DecodeEXRImage.", err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+ }
+ }
+
+ {
+ std::string e;
+ int ret = DecodeChunk(exr_image, exr_header, offsets, head, size, &e);
+
+ if (ret != TINYEXR_SUCCESS) {
+ if (!e.empty()) {
+ tinyexr::SetErrorMessage(e, err);
+ }
+
+ // 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++) {
+ if (exr_image->images[c]) {
+ free(exr_image->images[c]);
+ exr_image->images[c] = NULL;
+ }
+ }
+ free(exr_image->images);
+ exr_image->images = NULL;
+ }
+ }
+
+ return ret;
+ }
}
} // namespace tinyexr
@@ -10488,9 +11116,7 @@
int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
const char **err) {
if (out_rgba == NULL) {
- if (err) {
- (*err) = "Invalid argument.\n";
- }
+ tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -10503,13 +11129,14 @@
{
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) {
- if (err) {
- (*err) = "Loading multipart or DeepImage is not supported yet.\n";
- }
+ tinyexr::SetErrorMessage(
+ "Loading multipart or DeepImage is not supported in LoadEXR() API",
+ err);
return TINYEXR_ERROR_INVALID_DATA; // @fixme.
}
}
@@ -10517,6 +11144,7 @@
{
int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err);
if (ret != TINYEXR_SUCCESS) {
+ FreeEXRHeader(&exr_header);
return ret;
}
}
@@ -10531,6 +11159,7 @@
{
int ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename, err);
if (ret != TINYEXR_SUCCESS) {
+ FreeEXRHeader(&exr_header);
return ret;
}
}
@@ -10552,46 +11181,130 @@
}
}
- if (idxR == -1) {
- if (err) {
- (*err) = "R channel not found\n";
- }
+ if (exr_header.num_channels == 1) {
+ // Grayscale channel only.
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ (*out_rgba) = reinterpret_cast<float *>(
+ malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
+ static_cast<size_t>(exr_image.height)));
- if (idxG == -1) {
- if (err) {
- (*err) = "G channel not found\n";
- }
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ if (exr_header.tiled) {
- if (idxB == -1) {
- if (err) {
- (*err) = "B channel not found\n";
- }
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ 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_rgba) = reinterpret_cast<float *>(
- 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];
+ // 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)[0][srcIdx];
+ (*out_rgba)[4 * idx + 1] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 2] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 3] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ }
+ }
+ }
} else {
- (*out_rgba)[4 * i + 3] = 1.0;
+ for (int i = 0; i < exr_image.width * exr_image.height; i++) {
+ const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
+ (*out_rgba)[4 * i + 0] = val;
+ (*out_rgba)[4 * i + 1] = val;
+ (*out_rgba)[4 * i + 2] = val;
+ (*out_rgba)[4 * i + 3] = val;
+ }
+ }
+ } else {
+ // Assume RGB(A)
+
+ if (idxR == -1) {
+ tinyexr::SetErrorMessage("R channel not found", err);
+
+ // @todo { free exr_image }
+ FreeEXRHeader(&exr_header);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ if (idxG == -1) {
+ tinyexr::SetErrorMessage("G channel not found", err);
+ // @todo { free exr_image }
+ FreeEXRHeader(&exr_header);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ if (idxB == -1) {
+ tinyexr::SetErrorMessage("B channel not found", err);
+ // @todo { free exr_image }
+ FreeEXRHeader(&exr_header);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ (*out_rgba) = reinterpret_cast<float *>(
+ malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
+ static_cast<size_t>(exr_image.height)));
+ 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;
+ }
+ }
}
}
@@ -10604,26 +11317,37 @@
return TINYEXR_SUCCESS;
}
-static const int kVersionSize = 8;
+int IsEXR(const char *filename) {
+ EXRVersion exr_version;
+
+ int ret = ParseEXRVersionFromFile(&exr_version, filename);
+ if (ret != TINYEXR_SUCCESS) {
+ return TINYEXR_ERROR_INVALID_HEADER;
+ }
+
+ return TINYEXR_SUCCESS;
+}
int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version,
const unsigned char *memory, size_t size,
const char **err) {
if (memory == NULL || exr_header == NULL) {
- if (err) {
- (*err) = "Invalid argument.\n";
- }
+ tinyexr::SetErrorMessage(
+ "Invalid argument. `memory` or `exr_header` argument is null in "
+ "ParseEXRHeaderFromMemory()",
+ err);
// Invalid argument
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
- if (size < kVersionSize) {
+ if (size < tinyexr::kEXRVersionSize) {
+ tinyexr::SetErrorMessage("Insufficient header/data size.\n", err);
return TINYEXR_ERROR_INVALID_DATA;
}
- const unsigned char *marker = memory + kVersionSize;
- size_t marker_size = size - kVersionSize;
+ const unsigned char *marker = memory + tinyexr::kEXRVersionSize;
+ size_t marker_size = size - tinyexr::kEXRVersionSize;
tinyexr::HeaderInfo info;
info.clear();
@@ -10633,7 +11357,7 @@
if (ret != TINYEXR_SUCCESS) {
if (err && !err_str.empty()) {
- (*err) = strdup(err_str.c_str()); // May leak
+ tinyexr::SetErrorMessage(err_str, err);
}
}
@@ -10645,12 +11369,11 @@
return ret;
}
-int LoadEXRFromMemory(float *out_rgba, const unsigned char *memory, size_t size,
+int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
+ const unsigned char *memory, size_t size,
const char **err) {
if (out_rgba == NULL || memory == NULL) {
- if (err) {
- (*err) = "Invalid argument.\n";
- }
+ tinyexr::SetErrorMessage("Invalid argument for LoadEXRFromMemory", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -10662,6 +11385,7 @@
int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
if (ret != TINYEXR_SUCCESS) {
+ tinyexr::SetErrorMessage("Failed to parse EXR version", err);
return ret;
}
@@ -10670,8 +11394,15 @@
return ret;
}
+ // Read HALF channel as FLOAT.
+ for (int i = 0; i < exr_header.num_channels; i++) {
+ if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
+ exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
+ }
+ }
+
InitEXRImage(&exr_image);
- ret = LoadEXRImageFromMemory(&exr_image, &exr_header, memory, err);
+ ret = LoadEXRImageFromMemory(&exr_image, &exr_header, memory, size, err);
if (ret != TINYEXR_SUCCESS) {
return ret;
}
@@ -10693,53 +11424,145 @@
}
}
- if (idxR == -1) {
- if (err) {
- (*err) = "R channel not found\n";
- }
+ // TODO(syoyo): Refactor removing same code as used in LoadEXR().
+ if (exr_header.num_channels == 1) {
+ // Grayscale channel only.
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ (*out_rgba) = reinterpret_cast<float *>(
+ malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
+ static_cast<size_t>(exr_image.height)));
- if (idxG == -1) {
- if (err) {
- (*err) = "G channel not found\n";
- }
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ if (exr_header.tiled) {
- if (idxB == -1) {
- if (err) {
- (*err) = "B channel not found\n";
- }
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ 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;
- // Assume `out_rgba` have enough memory allocated.
- 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 > 0) {
- out_rgba[4 * i + 3] =
- reinterpret_cast<float **>(exr_image.images)[idxA][i];
+ // 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)[0][srcIdx];
+ (*out_rgba)[4 * idx + 1] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 2] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 3] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ }
+ }
+ }
} else {
- out_rgba[4 * i + 3] = 1.0;
+ for (int i = 0; i < exr_image.width * exr_image.height; i++) {
+ const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
+ (*out_rgba)[4 * i + 0] = val;
+ (*out_rgba)[4 * i + 1] = val;
+ (*out_rgba)[4 * i + 2] = val;
+ (*out_rgba)[4 * i + 3] = val;
+ }
+ }
+
+ } else {
+ // TODO(syoyo): Support non RGBA image.
+
+ if (idxR == -1) {
+ tinyexr::SetErrorMessage("R channel not found", err);
+
+ // @todo { free exr_image }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ if (idxG == -1) {
+ tinyexr::SetErrorMessage("G channel not found", err);
+ // @todo { free exr_image }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ if (idxB == -1) {
+ tinyexr::SetErrorMessage("B channel not found", err);
+ // @todo { free exr_image }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ (*out_rgba) = reinterpret_cast<float *>(
+ malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
+ static_cast<size_t>(exr_image.height)));
+
+ 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;
+ }
+ }
}
}
+ (*width) = exr_image.width;
+ (*height) = exr_image.height;
+
+ FreeEXRHeader(&exr_header);
+ FreeEXRImage(&exr_image);
+
return TINYEXR_SUCCESS;
}
int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header,
const char *filename, const char **err) {
if (exr_image == NULL) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromFile", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -10750,9 +11573,7 @@
FILE *fp = fopen(filename, "rb");
#endif
if (!fp) {
- if (err) {
- (*err) = "Cannot read file.";
- }
+ tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
return TINYEXR_ERROR_CANT_OPEN_FILE;
}
@@ -10762,6 +11583,12 @@
filesize = static_cast<size_t>(ftell(fp));
fseek(fp, 0, SEEK_SET);
+ if (filesize < 16) {
+ tinyexr::SetErrorMessage("File size too short " + std::string(filename),
+ err);
+ return TINYEXR_ERROR_INVALID_FILE;
+ }
+
std::vector<unsigned char> buf(filesize); // @todo { use mmap }
{
size_t ret;
@@ -10771,22 +11598,22 @@
(void)ret;
}
- return LoadEXRImageFromMemory(exr_image, exr_header, &buf.at(0), err);
+ return LoadEXRImageFromMemory(exr_image, exr_header, &buf.at(0), filesize,
+ err);
}
int LoadEXRImageFromMemory(EXRImage *exr_image, const EXRHeader *exr_header,
- const unsigned char *memory, const char **err) {
- if (exr_image == NULL || memory == NULL) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ const unsigned char *memory, const size_t size,
+ const char **err) {
+ if (exr_image == NULL || memory == NULL ||
+ (size < tinyexr::kEXRVersionSize)) {
+ tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromMemory",
+ err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
if (exr_header->header_len == 0) {
- if (err) {
- (*err) = "EXRHeader is not initialized.";
- }
+ tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -10794,7 +11621,8 @@
const unsigned char *marker = reinterpret_cast<const unsigned char *>(
memory + exr_header->header_len +
8); // +8 for magic number + version header.
- return tinyexr::DecodeEXRImage(exr_image, exr_header, head, marker, err);
+ return tinyexr::DecodeEXRImage(exr_image, exr_header, head, marker, size,
+ err);
}
size_t SaveEXRImageToMemory(const EXRImage *exr_image,
@@ -10802,26 +11630,22 @@
unsigned char **memory_out, const char **err) {
if (exr_image == NULL || memory_out == NULL ||
exr_header->compression_type < 0) {
- if (err) {
- (*err) = "Invalid argument.";
- }
- return 0; // @fixme
+ tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToMemory", err);
+ return 0;
}
#if !TINYEXR_USE_PIZ
if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
- if (err) {
- (*err) = "PIZ compression is not supported in this build.";
- }
+ tinyexr::SetErrorMessage("PIZ compression is not supported in this build",
+ err);
return 0;
}
#endif
#if !TINYEXR_USE_ZFP
if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
- if (err) {
- (*err) = "ZFP compression is not supported in this build.";
- }
+ tinyexr::SetErrorMessage("ZFP compression is not supported in this build",
+ err);
return 0;
}
#endif
@@ -10829,9 +11653,8 @@
#if TINYEXR_USE_ZFP
for (size_t i = 0; i < static_cast<size_t>(exr_header->num_channels); i++) {
if (exr_header->requested_pixel_types[i] != TINYEXR_PIXELTYPE_FLOAT) {
- if (err) {
- (*err) = "Pixel type must be FLOAT for ZFP compression.";
- }
+ tinyexr::SetErrorMessage("Pixel type must be FLOAT for ZFP compression",
+ err);
return 0;
}
}
@@ -10979,8 +11802,6 @@
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(
@@ -11041,6 +11862,11 @@
if (exr_header->pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
if (exr_header->requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
for (int y = 0; y < h; y++) {
+ // Assume increasing Y
+ float *line_ptr = reinterpret_cast<float *>(&buf.at(
+ static_cast<size_t>(pixel_data_size * y * exr_image->width) +
+ channel_offset_list[c] *
+ static_cast<size_t>(exr_image->width)));
for (int x = 0; x < exr_image->width; x++) {
tinyexr::FP16 h16;
h16.u = reinterpret_cast<unsigned short **>(
@@ -11050,30 +11876,27 @@
tinyexr::swap4(reinterpret_cast<unsigned int *>(&f32.f));
- // Assume increasing Y
- float *line_ptr = reinterpret_cast<float *>(&buf.at(
- static_cast<size_t>(pixel_data_size * y * exr_image->width) +
- channel_offset_list[c] *
- static_cast<size_t>(exr_image->width)));
- line_ptr[x] = f32.f;
+ // line_ptr[x] = f32.f;
+ tinyexr::cpy4(line_ptr + x, &(f32.f));
}
}
} else if (exr_header->requested_pixel_types[c] ==
TINYEXR_PIXELTYPE_HALF) {
for (int y = 0; y < h; y++) {
+ // Assume increasing Y
+ unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
+ &buf.at(static_cast<size_t>(pixel_data_size * y *
+ exr_image->width) +
+ channel_offset_list[c] *
+ static_cast<size_t>(exr_image->width)));
for (int x = 0; x < exr_image->width; x++) {
unsigned short val = reinterpret_cast<unsigned short **>(
exr_image->images)[c][(y + start_y) * exr_image->width + x];
tinyexr::swap2(&val);
- // Assume increasing Y
- unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
- &buf.at(static_cast<size_t>(pixel_data_size * y *
- exr_image->width) +
- channel_offset_list[c] *
- static_cast<size_t>(exr_image->width)));
- line_ptr[x] = val;
+ // line_ptr[x] = val;
+ tinyexr::cpy2(line_ptr + x, &val);
}
}
} else {
@@ -11083,6 +11906,12 @@
} else if (exr_header->pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
if (exr_header->requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
for (int y = 0; y < h; y++) {
+ // Assume increasing Y
+ unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
+ &buf.at(static_cast<size_t>(pixel_data_size * y *
+ exr_image->width) +
+ channel_offset_list[c] *
+ static_cast<size_t>(exr_image->width)));
for (int x = 0; x < exr_image->width; x++) {
tinyexr::FP32 f32;
f32.f = reinterpret_cast<float **>(
@@ -11093,30 +11922,26 @@
tinyexr::swap2(reinterpret_cast<unsigned short *>(&h16.u));
- // Assume increasing Y
- unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
- &buf.at(static_cast<size_t>(pixel_data_size * y *
- exr_image->width) +
- channel_offset_list[c] *
- static_cast<size_t>(exr_image->width)));
- line_ptr[x] = h16.u;
+ // line_ptr[x] = h16.u;
+ tinyexr::cpy2(line_ptr + x, &(h16.u));
}
}
} else if (exr_header->requested_pixel_types[c] ==
TINYEXR_PIXELTYPE_FLOAT) {
for (int y = 0; y < h; y++) {
+ // Assume increasing Y
+ float *line_ptr = reinterpret_cast<float *>(&buf.at(
+ static_cast<size_t>(pixel_data_size * y * exr_image->width) +
+ channel_offset_list[c] *
+ static_cast<size_t>(exr_image->width)));
for (int x = 0; x < exr_image->width; x++) {
float val = reinterpret_cast<float **>(
exr_image->images)[c][(y + start_y) * exr_image->width + x];
tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
- // Assume increasing Y
- float *line_ptr = reinterpret_cast<float *>(&buf.at(
- static_cast<size_t>(pixel_data_size * y * exr_image->width) +
- channel_offset_list[c] *
- static_cast<size_t>(exr_image->width)));
- line_ptr[x] = val;
+ // line_ptr[x] = val;
+ tinyexr::cpy4(line_ptr + x, &val);
}
}
} else {
@@ -11124,18 +11949,18 @@
}
} else if (exr_header->pixel_types[c] == TINYEXR_PIXELTYPE_UINT) {
for (int y = 0; y < h; y++) {
+ // Assume increasing Y
+ unsigned int *line_ptr = reinterpret_cast<unsigned int *>(&buf.at(
+ static_cast<size_t>(pixel_data_size * y * exr_image->width) +
+ channel_offset_list[c] * static_cast<size_t>(exr_image->width)));
for (int x = 0; x < exr_image->width; x++) {
unsigned int val = reinterpret_cast<unsigned int **>(
exr_image->images)[c][(y + start_y) * exr_image->width + x];
tinyexr::swap4(&val);
- // Assume increasing Y
- unsigned int *line_ptr = reinterpret_cast<unsigned int *>(&buf.at(
- static_cast<size_t>(pixel_data_size * y * exr_image->width) +
- channel_offset_list[c] *
- static_cast<size_t>(exr_image->width)));
- line_ptr[x] = val;
+ // line_ptr[x] = val;
+ tinyexr::cpy4(line_ptr + x, &val);
}
}
}
@@ -11160,16 +11985,17 @@
} else if ((exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
(exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) {
#if TINYEXR_USE_MINIZ
- std::vector<unsigned char> block(
- tinyexr::miniz::mz_compressBound(buf.size()));
+ std::vector<unsigned char> block(tinyexr::miniz::mz_compressBound(
+ static_cast<unsigned long>(buf.size())));
#else
- std::vector<unsigned char> block(compressBound(buf.size()));
+ std::vector<unsigned char> block(
+ compressBound(static_cast<uLong>(buf.size())));
#endif
tinyexr::tinyexr_uint64 outSize = block.size();
tinyexr::CompressZip(&block.at(0), outSize,
reinterpret_cast<const unsigned char *>(&buf.at(0)),
- buf.size());
+ static_cast<unsigned long>(buf.size()));
// 4 byte: scan line
// 4 byte: data size
@@ -11194,7 +12020,7 @@
tinyexr::CompressRle(&block.at(0), outSize,
reinterpret_cast<const unsigned char *>(&buf.at(0)),
- buf.size());
+ static_cast<unsigned long>(buf.size()));
// 4 byte: scan line
// 4 byte: data size
@@ -11214,13 +12040,13 @@
} 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());
- CompressPiz(&block.at(0), outSize,
+ CompressPiz(&block.at(0), &outSize,
reinterpret_cast<const unsigned char *>(&buf.at(0)),
buf.size(), channels, exr_image->width, h);
@@ -11275,13 +12101,12 @@
} // 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)),
@@ -11289,41 +12114,44 @@
sizeof(tinyexr::tinyexr_uint64) * static_cast<size_t>(num_blocks));
}
- { memory.insert(memory.end(), data.begin(), data.end()); }
+ if ( memory.size() == 0 ) {
+ tinyexr::SetErrorMessage("Output memory size is zero", err);
+ return 0;
+ }
- assert(memory.size() > 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();
- return memory.size(); // OK
+ 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 totalSize; // OK
}
int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
const char *filename, const char **err) {
if (exr_image == NULL || filename == NULL ||
exr_header->compression_type < 0) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToFile", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
#if !TINYEXR_USE_PIZ
if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
- if (err) {
- (*err) = "PIZ compression is not supported in this build.";
- }
- return 0;
+ tinyexr::SetErrorMessage("PIZ compression is not supported in this build",
+ err);
+ return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
}
#endif
#if !TINYEXR_USE_ZFP
if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
- if (err) {
- (*err) = "ZFP compression is not supported in this build.";
- }
- return 0;
+ tinyexr::SetErrorMessage("ZFP compression is not supported in this build",
+ err);
+ return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
}
#endif
@@ -11334,40 +12162,54 @@
FILE *fp = fopen(filename, "wb");
#endif
if (!fp) {
- if (err) {
- (*err) = "Cannot write a file.";
- }
- return TINYEXR_ERROR_CANT_OPEN_FILE;
+ tinyexr::SetErrorMessage("Cannot write a file", err);
+ 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;
}
int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
if (deep_image == NULL) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ tinyexr::SetErrorMessage("Invalid argument for LoadDeepEXR", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
- FILE *fp = fopen(filename, "rb");
- if (!fp) {
- if (err) {
- (*err) = "Cannot read file.";
- }
+#ifdef _MSC_VER
+ FILE *fp = NULL;
+ errno_t errcode = fopen_s(&fp, filename, "rb");
+ if ((0 != errcode) || (!fp)) {
+ tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename),
+ err);
return TINYEXR_ERROR_CANT_OPEN_FILE;
}
+#else
+ FILE *fp = fopen(filename, "rb");
+ if (!fp) {
+ tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename),
+ err);
+ return TINYEXR_ERROR_CANT_OPEN_FILE;
+ }
+#endif
size_t filesize;
// Compute size
@@ -11377,9 +12219,8 @@
if (filesize == 0) {
fclose(fp);
- if (err) {
- (*err) = "File size is zero.";
- }
+ tinyexr::SetErrorMessage("File size is zero : " + std::string(filename),
+ err);
return TINYEXR_ERROR_INVALID_FILE;
}
@@ -11400,9 +12241,7 @@
const char header[] = {0x76, 0x2f, 0x31, 0x01};
if (memcmp(marker, header, 4) != 0) {
- if (err) {
- (*err) = "Invalid magic number.";
- }
+ tinyexr::SetErrorMessage("Invalid magic number", err);
return TINYEXR_ERROR_INVALID_MAGIC_NUMBER;
}
marker += 4;
@@ -11413,9 +12252,7 @@
// ver 2.0, scanline, deep bit on(0x800)
// must be [2, 0, 0, 0]
if (marker[0] != 2 || marker[1] != 8 || marker[2] != 0 || marker[3] != 0) {
- if (err) {
- (*err) = "Unsupported version or scanline.";
- }
+ tinyexr::SetErrorMessage("Unsupported version or scanline", err);
return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
}
@@ -11432,11 +12269,12 @@
std::vector<tinyexr::ChannelInfo> channels;
// Read attributes
- size_t size = filesize - kVersionSize;
+ size_t size = filesize - tinyexr::kEXRVersionSize;
for (;;) {
if (0 == size) {
return TINYEXR_ERROR_INVALID_DATA;
} else if (marker[0] == '\0') {
+ marker++;
size--;
break;
}
@@ -11455,9 +12293,9 @@
if (attr_name.compare("compression") == 0) {
compression_type = data[0];
if (compression_type > TINYEXR_COMPRESSIONTYPE_PIZ) {
- if (err) {
- (*err) = "Unsupported compression type.";
- }
+ std::stringstream ss;
+ ss << "Unsupported compression type : " << compression_type;
+ tinyexr::SetErrorMessage(ss.str(), err);
return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
}
@@ -11473,14 +12311,15 @@
// xSampling: int
// ySampling: int
- tinyexr::ReadChannelInfo(channels, data);
+ if (!tinyexr::ReadChannelInfo(channels, data)) {
+ tinyexr::SetErrorMessage("Failed to parse channel info", err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
num_channels = static_cast<int>(channels.size());
if (num_channels < 1) {
- if (err) {
- (*err) = "Invalid channels format.";
- }
+ tinyexr::SetErrorMessage("Invalid channels format", err);
return TINYEXR_ERROR_INVALID_DATA;
}
@@ -11552,9 +12391,7 @@
#endif
// OK
} else {
- if (err) {
- (*err) = "Unsupported format.";
- }
+ tinyexr::SetErrorMessage("Unsupported compression format", err);
return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
}
@@ -11608,10 +12445,14 @@
// decode pixel offset table.
{
- unsigned long dstLen = pixelOffsetTable.size() * sizeof(int);
- tinyexr::DecompressZip(
- reinterpret_cast<unsigned char *>(&pixelOffsetTable.at(0)), &dstLen,
- data_ptr + 28, static_cast<size_t>(packedOffsetTableSize));
+ unsigned long dstLen =
+ static_cast<unsigned long>(pixelOffsetTable.size() * sizeof(int));
+ if (!tinyexr::DecompressZip(
+ reinterpret_cast<unsigned char *>(&pixelOffsetTable.at(0)),
+ &dstLen, data_ptr + 28,
+ static_cast<unsigned long>(packedOffsetTableSize))) {
+ return false;
+ }
assert(dstLen == pixelOffsetTable.size() * sizeof(int));
for (size_t i = 0; i < static_cast<size_t>(data_width); i++) {
@@ -11625,11 +12466,15 @@
// decode sample data.
{
unsigned long dstLen = static_cast<unsigned long>(unpackedSampleDataSize);
- tinyexr::DecompressZip(
- reinterpret_cast<unsigned char *>(&sample_data.at(0)), &dstLen,
- data_ptr + 28 + packedOffsetTableSize,
- static_cast<size_t>(packedSampleDataSize));
- assert(dstLen == static_cast<unsigned long>(unpackedSampleDataSize));
+ if (dstLen) {
+ if (!tinyexr::DecompressZip(
+ reinterpret_cast<unsigned char *>(&sample_data.at(0)), &dstLen,
+ data_ptr + 28 + packedOffsetTableSize,
+ static_cast<unsigned long>(packedSampleDataSize))) {
+ return false;
+ }
+ assert(dstLen == static_cast<unsigned long>(unpackedSampleDataSize));
+ }
}
// decode sample
@@ -11674,8 +12519,10 @@
if (channels[c].pixel_type == 0) { // UINT
for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
- unsigned int ui = *reinterpret_cast<unsigned int *>(
- &sample_data.at(data_offset + x * sizeof(int)));
+ unsigned int ui;
+ unsigned int *src_ptr = reinterpret_cast<unsigned int *>(
+ &sample_data.at(size_t(data_offset) + x * sizeof(int)));
+ tinyexr::cpy4(&ui, src_ptr);
deep_image->image[c][y][x] = static_cast<float>(ui); // @fixme
}
data_offset +=
@@ -11683,16 +12530,19 @@
} else if (channels[c].pixel_type == 1) { // half
for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
tinyexr::FP16 f16;
- f16.u = *reinterpret_cast<unsigned short *>(
- &sample_data.at(data_offset + x * sizeof(short)));
+ const unsigned short *src_ptr = reinterpret_cast<unsigned short *>(
+ &sample_data.at(size_t(data_offset) + x * sizeof(short)));
+ tinyexr::cpy2(&(f16.u), src_ptr);
tinyexr::FP32 f32 = half_to_float(f16);
deep_image->image[c][y][x] = f32.f;
}
data_offset += sizeof(short) * static_cast<size_t>(samples_per_line);
} else { // float
for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
- float f = *reinterpret_cast<float *>(
- &sample_data.at(data_offset + x * sizeof(float)));
+ float f;
+ const float *src_ptr = reinterpret_cast<float *>(
+ &sample_data.at(size_t(data_offset) + x * sizeof(float)));
+ tinyexr::cpy4(&f, src_ptr);
deep_image->image[c][y][x] = f;
}
data_offset += sizeof(float) * static_cast<size_t>(samples_per_line);
@@ -11729,6 +12579,15 @@
exr_image->images = NULL;
exr_image->tiles = NULL;
+
+ exr_image->num_tiles = 0;
+}
+
+void FreeEXRErrorMessage(const char *msg) {
+ if (msg) {
+ free(reinterpret_cast<void *>(const_cast<char *>(msg)));
+ }
+ return;
}
void InitEXRHeader(EXRHeader *exr_header) {
@@ -11762,6 +12621,10 @@
}
}
+ if (exr_header->custom_attributes) {
+ free(exr_header->custom_attributes);
+ }
+
return TINYEXR_SUCCESS;
}
@@ -11791,6 +12654,7 @@
free(exr_image->tiles[tid].images);
}
}
+ free(exr_image->tiles);
}
return TINYEXR_SUCCESS;
@@ -11799,9 +12663,8 @@
int ParseEXRHeaderFromFile(EXRHeader *exr_header, const EXRVersion *exr_version,
const char *filename, const char **err) {
if (exr_header == NULL || exr_version == NULL || filename == NULL) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ tinyexr::SetErrorMessage("Invalid argument for ParseEXRHeaderFromFile",
+ err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -11812,9 +12675,7 @@
FILE *fp = fopen(filename, "rb");
#endif
if (!fp) {
- if (err) {
- (*err) = "Cannot read file.";
- }
+ tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
return TINYEXR_ERROR_CANT_OPEN_FILE;
}
@@ -11832,9 +12693,8 @@
fclose(fp);
if (ret != filesize) {
- if (err) {
- (*err) = "fread error.";
- }
+ tinyexr::SetErrorMessage("fread() error on " + std::string(filename),
+ err);
return TINYEXR_ERROR_INVALID_FILE;
}
}
@@ -11851,15 +12711,18 @@
if (memory == NULL || exr_headers == NULL || num_headers == NULL ||
exr_version == NULL) {
// Invalid argument
+ tinyexr::SetErrorMessage(
+ "Invalid argument for ParseEXRMultipartHeaderFromMemory", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
- if (size < kVersionSize) {
+ if (size < tinyexr::kEXRVersionSize) {
+ tinyexr::SetErrorMessage("Data size too short", err);
return TINYEXR_ERROR_INVALID_DATA;
}
- const unsigned char *marker = memory + kVersionSize;
- size_t marker_size = size - kVersionSize;
+ const unsigned char *marker = memory + tinyexr::kEXRVersionSize;
+ size_t marker_size = size - tinyexr::kEXRVersionSize;
std::vector<tinyexr::HeaderInfo> infos;
@@ -11873,9 +12736,7 @@
marker, marker_size);
if (ret != TINYEXR_SUCCESS) {
- if (err) {
- (*err) = strdup(err_str.c_str()); // may leak
- }
+ tinyexr::SetErrorMessage(err_str, err);
return ret;
}
@@ -11886,9 +12747,8 @@
// `chunkCount` must exist in the header.
if (info.chunk_count == 0) {
- if (err) {
- (*err) = "`chunkCount' attribute is not found in the header.";
- }
+ tinyexr::SetErrorMessage(
+ "`chunkCount' attribute is not found in the header.", err);
return TINYEXR_ERROR_INVALID_DATA;
}
@@ -11923,9 +12783,8 @@
const char *filename, const char **err) {
if (exr_headers == NULL || num_headers == NULL || exr_version == NULL ||
filename == NULL) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ tinyexr::SetErrorMessage(
+ "Invalid argument for ParseEXRMultipartHeaderFromFile()", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -11936,9 +12795,7 @@
FILE *fp = fopen(filename, "rb");
#endif
if (!fp) {
- if (err) {
- (*err) = "Cannot read file.";
- }
+ tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
return TINYEXR_ERROR_CANT_OPEN_FILE;
}
@@ -11956,9 +12813,7 @@
fclose(fp);
if (ret != filesize) {
- if (err) {
- (*err) = "fread error.";
- }
+ tinyexr::SetErrorMessage("`fread' error. file may be corrupted.", err);
return TINYEXR_ERROR_INVALID_FILE;
}
}
@@ -11973,7 +12828,7 @@
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
- if (size < kVersionSize) {
+ if (size < tinyexr::kEXRVersionSize) {
return TINYEXR_ERROR_INVALID_DATA;
}
@@ -12045,31 +12900,30 @@
file_size = static_cast<size_t>(ftell(fp));
fseek(fp, 0, SEEK_SET);
- if (file_size < kVersionSize) {
+ if (file_size < tinyexr::kEXRVersionSize) {
return TINYEXR_ERROR_INVALID_FILE;
}
- unsigned char buf[kVersionSize];
- size_t ret = fread(&buf[0], 1, kVersionSize, fp);
+ unsigned char buf[tinyexr::kEXRVersionSize];
+ size_t ret = fread(&buf[0], 1, tinyexr::kEXRVersionSize, fp);
fclose(fp);
- if (ret != kVersionSize) {
+ if (ret != tinyexr::kEXRVersionSize) {
return TINYEXR_ERROR_INVALID_FILE;
}
- return ParseEXRVersionFromMemory(version, buf, kVersionSize);
+ return ParseEXRVersionFromMemory(version, buf, tinyexr::kEXRVersionSize);
}
int LoadEXRMultipartImageFromMemory(EXRImage *exr_images,
const EXRHeader **exr_headers,
unsigned int num_parts,
const unsigned char *memory,
- const char **err) {
+ const size_t size, const char **err) {
if (exr_images == NULL || exr_headers == NULL || num_parts == 0 ||
- memory == NULL) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ memory == NULL || (size <= tinyexr::kEXRVersionSize)) {
+ tinyexr::SetErrorMessage(
+ "Invalid argument for LoadEXRMultipartImageFromMemory()", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -12077,9 +12931,7 @@
size_t total_header_size = 0;
for (unsigned int i = 0; i < num_parts; i++) {
if (exr_headers[i]->header_len == 0) {
- if (err) {
- (*err) = "EXRHeader is not initialized.";
- }
+ tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -12113,6 +12965,12 @@
memcpy(&offset, marker, 8);
tinyexr::swap8(&offset);
+ if (offset >= size) {
+ tinyexr::SetErrorMessage("Invalid offset size in EXR header chunks.",
+ err);
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
offset_table[c] = offset + 4; // +4 to skip 'part number'
marker += 8;
}
@@ -12134,14 +12992,19 @@
tinyexr::swap4(&part_no);
if (part_no != i) {
- assert(0);
+ tinyexr::SetErrorMessage("Invalid `part number' in EXR header chunks.",
+ err);
return TINYEXR_ERROR_INVALID_DATA;
}
}
+ std::string e;
int ret = tinyexr::DecodeChunk(&exr_images[i], exr_headers[i], offset_table,
- memory);
+ memory, size, &e);
if (ret != TINYEXR_SUCCESS) {
+ if (!e.empty()) {
+ tinyexr::SetErrorMessage(e, err);
+ }
return ret;
}
}
@@ -12154,9 +13017,8 @@
unsigned int num_parts, const char *filename,
const char **err) {
if (exr_images == NULL || exr_headers == NULL || num_parts == 0) {
- if (err) {
- (*err) = "Invalid argument.";
- }
+ tinyexr::SetErrorMessage(
+ "Invalid argument for LoadEXRMultipartImageFromFile", err);
return TINYEXR_ERROR_INVALID_ARGUMENT;
}
@@ -12167,9 +13029,7 @@
FILE *fp = fopen(filename, "rb");
#endif
if (!fp) {
- if (err) {
- (*err) = "Cannot read file.";
- }
+ tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
return TINYEXR_ERROR_CANT_OPEN_FILE;
}
@@ -12189,42 +13049,55 @@
}
return LoadEXRMultipartImageFromMemory(exr_images, exr_headers, num_parts,
- &buf.at(0), err);
+ &buf.at(0), filesize, err);
}
int SaveEXR(const float *data, int width, int height, int components,
- const char *outfilename) {
- if (components == 3 || components == 4) {
+ 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);
image.num_channels = components;
std::vector<float> images[4];
- images[0].resize(static_cast<size_t>(width * height));
- images[1].resize(static_cast<size_t>(width * height));
- images[2].resize(static_cast<size_t>(width * height));
- images[3].resize(static_cast<size_t>(width * height));
- // Split RGB(A)RGB(A)RGB(A)... into R, G and B(and A) layers
- for (size_t i = 0; i < static_cast<size_t>(width * height); i++) {
- images[0][i] = data[static_cast<size_t>(components) * i + 0];
- images[1][i] = data[static_cast<size_t>(components) * i + 1];
- images[2][i] = data[static_cast<size_t>(components) * i + 2];
- if (components == 4) {
- images[3][i] = data[static_cast<size_t>(components) * i + 3];
+ if (components == 1) {
+ images[0].resize(static_cast<size_t>(width * height));
+ memcpy(images[0].data(), data, sizeof(float) * size_t(width * height));
+ } else {
+ images[0].resize(static_cast<size_t>(width * height));
+ images[1].resize(static_cast<size_t>(width * height));
+ images[2].resize(static_cast<size_t>(width * height));
+ images[3].resize(static_cast<size_t>(width * height));
+
+ // Split RGB(A)RGB(A)RGB(A)... into R, G and B(and A) layers
+ for (size_t i = 0; i < static_cast<size_t>(width * height); i++) {
+ images[0][i] = data[static_cast<size_t>(components) * i + 0];
+ images[1][i] = data[static_cast<size_t>(components) * i + 1];
+ images[2][i] = data[static_cast<size_t>(components) * i + 2];
+ if (components == 4) {
+ images[3][i] = data[static_cast<size_t>(components) * i + 3];
+ }
}
}
@@ -12234,10 +13107,12 @@
image_ptr[1] = &(images[2].at(0)); // B
image_ptr[2] = &(images[1].at(0)); // G
image_ptr[3] = &(images[0].at(0)); // R
- } else {
+ } else if (components == 3) {
image_ptr[0] = &(images[2].at(0)); // B
image_ptr[1] = &(images[1].at(0)); // G
image_ptr[2] = &(images[0].at(0)); // R
+ } else if (components == 1) {
+ image_ptr[0] = &(images[0].at(0)); // A
}
image.images = reinterpret_cast<unsigned char **>(image_ptr);
@@ -12249,21 +13124,41 @@
sizeof(EXRChannelInfo) * static_cast<size_t>(header.num_channels)));
// Must be (A)BGR order, since most of EXR viewers expect this channel order.
if (components == 4) {
+#ifdef _MSC_VER
+ strncpy_s(header.channels[0].name, "A", 255);
+ strncpy_s(header.channels[1].name, "B", 255);
+ strncpy_s(header.channels[2].name, "G", 255);
+ strncpy_s(header.channels[3].name, "R", 255);
+#else
strncpy(header.channels[0].name, "A", 255);
- header.channels[0].name[strlen("A")] = '\0';
strncpy(header.channels[1].name, "B", 255);
- header.channels[1].name[strlen("B")] = '\0';
strncpy(header.channels[2].name, "G", 255);
- header.channels[2].name[strlen("G")] = '\0';
strncpy(header.channels[3].name, "R", 255);
+#endif
+ header.channels[0].name[strlen("A")] = '\0';
+ header.channels[1].name[strlen("B")] = '\0';
+ header.channels[2].name[strlen("G")] = '\0';
header.channels[3].name[strlen("R")] = '\0';
- } else {
+ } else if (components == 3) {
+#ifdef _MSC_VER
+ strncpy_s(header.channels[0].name, "B", 255);
+ strncpy_s(header.channels[1].name, "G", 255);
+ strncpy_s(header.channels[2].name, "R", 255);
+#else
strncpy(header.channels[0].name, "B", 255);
- header.channels[0].name[strlen("B")] = '\0';
strncpy(header.channels[1].name, "G", 255);
- header.channels[1].name[strlen("G")] = '\0';
strncpy(header.channels[2].name, "R", 255);
+#endif
+ header.channels[0].name[strlen("B")] = '\0';
+ header.channels[1].name[strlen("G")] = '\0';
header.channels[2].name[strlen("R")] = '\0';
+ } else {
+#ifdef _MSC_VER
+ strncpy_s(header.channels[0].name, "A", 255);
+#else
+ strncpy(header.channels[0].name, "A", 255);
+#endif
+ header.channels[0].name[strlen("A")] = '\0';
}
header.pixel_types = static_cast<int *>(
@@ -12273,13 +13168,18 @@
for (int i = 0; i < header.num_channels; i++) {
header.pixel_types[i] =
TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
- header.requested_pixel_types[i] =
- TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in
- // .EXR
+
+ if (save_as_fp16 > 0) {
+ header.requested_pixel_types[i] =
+ TINYEXR_PIXELTYPE_HALF; // save with half(fp16) pixel format
+ } else {
+ header.requested_pixel_types[i] =
+ TINYEXR_PIXELTYPE_FLOAT; // save with float(fp32) pixel format(i.e.
+ // no precision reduction)
+ }
}
- const char *err;
- int ret = SaveEXRImageToFile(&image, &header, outfilename, &err);
+ int ret = SaveEXRImageToFile(&image, &header, outfilename, err);
if (ret != TINYEXR_SUCCESS) {
return ret;
}
@@ -12291,10 +13191,10 @@
return ret;
}
-#ifdef _MSC_VER
-#pragma warning(pop)
+#ifdef __clang__
+// zero-as-null-ppinter-constant
+#pragma clang diagnostic pop
#endif
-#endif
-
-#endif // TINYEXR_H_
+#endif // TINYEXR_IMPLEMENTATION_DEIFNED
+#endif // TINYEXR_IMPLEMENTATION