92#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
93 defined(__i386) || defined(__i486__) || defined(__i486) || \
94 defined(i386) || defined(__ia64__) || defined(__x86_64__)
95#define TINYEXR_X86_OR_X64_CPU 1
97#define TINYEXR_X86_OR_X64_CPU 0
100#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || TINYEXR_X86_OR_X64_CPU
101#define TINYEXR_LITTLE_ENDIAN 1
103#define TINYEXR_LITTLE_ENDIAN 0
108#ifndef TINYEXR_USE_MINIZ
109#define TINYEXR_USE_MINIZ (1)
113#ifndef TINYEXR_USE_STB_ZLIB
114#define TINYEXR_USE_STB_ZLIB (0)
118#ifndef TINYEXR_USE_NANOZLIB
119#define TINYEXR_USE_NANOZLIB (0)
123#ifndef TINYEXR_USE_PIZ
124#define TINYEXR_USE_PIZ (1)
127#ifndef TINYEXR_USE_ZFP
128#define TINYEXR_USE_ZFP (0)
132#ifndef TINYEXR_USE_THREAD
133#define TINYEXR_USE_THREAD (0)
136#ifndef TINYEXR_MAX_THREADS
137#define TINYEXR_MAX_THREADS (0)
141#ifndef TINYEXR_USE_OPENMP
143#define TINYEXR_USE_OPENMP (1)
145#define TINYEXR_USE_OPENMP (0)
149#ifndef TINYEXR_USE_COMPILER_FP16
150#define TINYEXR_USE_COMPILER_FP16 (0)
153#if TINYEXR_USE_COMPILER_FP16
155#if defined( __GNUC__ ) || defined( __clang__ )
156#if defined( __SSE2__ )
157#if ( __GNUC__ > 11 ) || ( __clang_major__ > 14 )
158#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
159#define __STDC_WANT_IEC_60559_TYPES_EXT__
163#define TINYEXR_FP16_COMPILER_TYPE _Float16
166#if defined( __ARM_NEON__ ) || defined( __ARM_NEON )
167#define TINYEXR_FP16_COMPILER_TYPE __fp16
171#if (defined(_M_IX86) || defined(_M_X64)) && defined(__AVX2__)
173#define TINYEXR_FP16_COMPILER_TYPE uint16_t
178#ifdef TINYEXR_FP16_COMPILER_TYPE
179#define TINYEXR_HAS_FP16_COMPILER_TYPE (1)
181#define TINYEXR_HAS_FP16_COMPILER_TYPE (0)
184#define TINYEXR_SUCCESS (0)
185#define TINYEXR_ERROR_INVALID_MAGIC_NUMBER (-1)
186#define TINYEXR_ERROR_INVALID_EXR_VERSION (-2)
187#define TINYEXR_ERROR_INVALID_ARGUMENT (-3)
188#define TINYEXR_ERROR_INVALID_DATA (-4)
189#define TINYEXR_ERROR_INVALID_FILE (-5)
190#define TINYEXR_ERROR_INVALID_PARAMETER (-6)
191#define TINYEXR_ERROR_CANT_OPEN_FILE (-7)
192#define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-8)
193#define TINYEXR_ERROR_INVALID_HEADER (-9)
194#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-10)
195#define TINYEXR_ERROR_CANT_WRITE_FILE (-11)
196#define TINYEXR_ERROR_SERIALIZATION_FAILED (-12)
197#define TINYEXR_ERROR_LAYER_NOT_FOUND (-13)
198#define TINYEXR_ERROR_DATA_TOO_LARGE (-14)
203#define TINYEXR_PIXELTYPE_UINT (0)
204#define TINYEXR_PIXELTYPE_HALF (1)
205#define TINYEXR_PIXELTYPE_FLOAT (2)
207#define TINYEXR_MAX_HEADER_ATTRIBUTES (1024)
208#define TINYEXR_MAX_CUSTOM_ATTRIBUTES (128)
210#define TINYEXR_COMPRESSIONTYPE_NONE (0)
211#define TINYEXR_COMPRESSIONTYPE_RLE (1)
212#define TINYEXR_COMPRESSIONTYPE_ZIPS (2)
213#define TINYEXR_COMPRESSIONTYPE_ZIP (3)
214#define TINYEXR_COMPRESSIONTYPE_PIZ (4)
215#define TINYEXR_COMPRESSIONTYPE_ZFP (128)
217#define TINYEXR_ZFP_COMPRESSIONTYPE_RATE (0)
218#define TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION (1)
219#define TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY (2)
221#define TINYEXR_TILE_ONE_LEVEL (0)
222#define TINYEXR_TILE_MIPMAP_LEVELS (1)
223#define TINYEXR_TILE_RIPMAP_LEVELS (2)
225#define TINYEXR_TILE_ROUND_DOWN (0)
226#define TINYEXR_TILE_ROUND_UP (1)
243 unsigned char *value;
253 unsigned char p_linear;
254 unsigned char pad[3];
266 unsigned char **images;
277 float pixel_aspect_ratio;
281 float screen_window_center[2];
282 float screen_window_width;
291 int tile_rounding_mode;
298 unsigned int header_len;
302 int num_custom_attributes;
313 int compression_type;
314 int *requested_pixel_types;
338 unsigned char **images;
356 const char **channel_names;
372extern int LoadEXR(
float **out_rgba,
int *width,
int *height,
373 const char *filename,
const char **err);
381extern int LoadEXRWithLayer(
float **out_rgba,
int *width,
int *height,
382 const char *filename,
const char *layer_name,
396extern int EXRLayers(
const char *filename,
const char **layer_names[],
397 int *num_layers,
const char **err);
404extern int IsEXR(
const char *filename);
410extern int IsEXRFromMemory(
const unsigned char *memory,
size_t size);
426extern int SaveEXRToMemory(
const float *data,
const int width,
const int height,
427 const int components,
const int save_as_fp16,
428 unsigned char **buffer,
const char **err);
442extern int SaveEXR(
const float *data,
const int width,
const int height,
443 const int components,
const int save_as_fp16,
444 const char *filename,
const char **err);
447extern int EXRNumLevels(
const EXRImage* exr_image);
450extern void InitEXRHeader(
EXRHeader *exr_header);
453extern void EXRSetNameAttr(
EXRHeader *exr_header,
const char* name);
456extern void InitEXRImage(
EXRImage *exr_image);
459extern int FreeEXRHeader(
EXRHeader *exr_header);
462extern int FreeEXRImage(
EXRImage *exr_image);
465extern void FreeEXRErrorMessage(
const char *msg);
468extern int ParseEXRVersionFromFile(
EXRVersion *version,
const char *filename);
471extern int ParseEXRVersionFromMemory(
EXRVersion *version,
472 const unsigned char *memory,
size_t size);
478 const char *filename,
const char **err);
483extern int ParseEXRHeaderFromMemory(
EXRHeader *header,
485 const unsigned char *memory,
size_t size,
492extern int ParseEXRMultipartHeaderFromFile(
EXRHeader ***headers,
495 const char *filename,
502extern int ParseEXRMultipartHeaderFromMemory(
EXRHeader ***headers,
505 const unsigned char *memory,
506 size_t size,
const char **err);
516 const char *filename,
const char **err);
527 const unsigned char *memory,
528 const size_t size,
const char **err);
538extern int LoadEXRMultipartImageFromFile(
EXRImage *images,
540 unsigned int num_parts,
541 const char *filename,
552extern int LoadEXRMultipartImageFromMemory(
EXRImage *images,
554 unsigned int num_parts,
555 const unsigned char *memory,
556 const size_t size,
const char **err);
563extern int SaveEXRImageToFile(
const EXRImage *image,
564 const EXRHeader *exr_header,
const char *filename,
574extern size_t SaveEXRImageToMemory(
const EXRImage *image,
576 unsigned char **memory,
const char **err);
585extern int SaveEXRMultipartImageToFile(
const EXRImage *images,
587 unsigned int num_parts,
588 const char *filename,
const char **err);
598extern size_t SaveEXRMultipartImageToMemory(
const EXRImage *images,
600 unsigned int num_parts,
601 unsigned char **memory,
const char **err);
608extern int LoadDeepEXR(
DeepImage *out_image,
const char *filename,
632extern int LoadEXRFromMemory(
float **out_rgba,
int *width,
int *height,
633 const unsigned char *memory,
size_t size,
642#ifdef TINYEXR_IMPLEMENTATION
643#ifndef TINYEXR_IMPLEMENTATION_DEFINED
644#define TINYEXR_IMPLEMENTATION_DEFINED
648#ifndef WIN32_LEAN_AND_MEAN
649#define WIN32_LEAN_AND_MEAN
656#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
657#define TINYEXR_USE_WIN32_MMAP (1)
660#elif defined(__linux__) || defined(__unix__)
665#define TINYEXR_USE_POSIX_MMAP (1)
682#if __cplusplus > 199711L || (defined(_MSC_VER) && _MSC_VER >= 1900)
683#define TINYEXR_HAS_CXX11 (1)
687#if TINYEXR_USE_THREAD
693#define TINYEXR_HAS_CXX11 (0)
696#if TINYEXR_USE_OPENMP
700#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
708#if defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
709#define NANOZLIB_IMPLEMENTATION
713#if TINYEXR_USE_STB_ZLIB
718extern "C" int stbi_zlib_decode_buffer(
char *obuffer,
int olen,
const char *ibuffer,
int ilen);
720extern "C" unsigned char *stbi_zlib_compress(
unsigned char *data,
int data_len,
int *out_len,
int quality);
727#pragma clang diagnostic push
728#pragma clang diagnostic ignored "-Weverything"
734#pragma clang diagnostic pop
742#define TINYEXR_CHECK_AND_RETURN_MSG(cond, msg, err) do { \
745 std::ostringstream ss_e; \
746 ss_e << __func__ << "():" << __LINE__ << msg << "\n"; \
747 (*err) += ss_e.str(); \
754#define TINYEXR_CHECK_AND_RETURN_C(cond, retcode) do { \
762#if __cplusplus > 199711L
770#pragma clang diagnostic push
771#pragma clang diagnostic ignored "-Wc++11-long-long"
773typedef unsigned long long tinyexr_uint64;
774typedef long long tinyexr_int64;
776#pragma clang diagnostic pop
789static void SetErrorMessage(
const std::string &msg,
const char **err) {
792 (*err) = _strdup(msg.c_str());
794 (*err) = strdup(msg.c_str());
800static void SetWarningMessage(
const std::string &msg,
const char **warn) {
803 (*warn) = _strdup(msg.c_str());
805 (*warn) = strdup(msg.c_str());
811static const int kEXRVersionSize = 8;
813static void inline cpy2(
unsigned short *dst_val,
const unsigned short *src_val) {
814 unsigned char *dst =
reinterpret_cast<unsigned char *
>(dst_val);
815 const unsigned char *src =
reinterpret_cast<const unsigned char *
>(src_val);
821static void inline swap2(
unsigned short *val) {
822#if TINYEXR_LITTLE_ENDIAN
825 unsigned short tmp = *val;
826 unsigned char *dst =
reinterpret_cast<unsigned char *
>(val);
827 unsigned char *src =
reinterpret_cast<unsigned char *
>(&tmp);
835#pragma clang diagnostic push
836#pragma clang diagnostic ignored "-Wunused-function"
840#pragma GCC diagnostic push
841#pragma GCC diagnostic ignored "-Wunused-function"
843static void inline cpy4(
int *dst_val,
const int *src_val) {
844 unsigned char *dst =
reinterpret_cast<unsigned char *
>(dst_val);
845 const unsigned char *src =
reinterpret_cast<const unsigned char *
>(src_val);
853static void inline cpy4(
unsigned int *dst_val,
const unsigned int *src_val) {
854 unsigned char *dst =
reinterpret_cast<unsigned char *
>(dst_val);
855 const unsigned char *src =
reinterpret_cast<const unsigned char *
>(src_val);
863static void inline cpy4(
float *dst_val,
const float *src_val) {
864 unsigned char *dst =
reinterpret_cast<unsigned char *
>(dst_val);
865 const unsigned char *src =
reinterpret_cast<const unsigned char *
>(src_val);
873#pragma clang diagnostic pop
877#pragma GCC diagnostic pop
880static void inline swap4(
unsigned int *val) {
881#if TINYEXR_LITTLE_ENDIAN
884 unsigned int tmp = *val;
885 unsigned char *dst =
reinterpret_cast<unsigned char *
>(val);
886 unsigned char *src =
reinterpret_cast<unsigned char *
>(&tmp);
895static void inline swap4(
int *val) {
896#if TINYEXR_LITTLE_ENDIAN
900 unsigned char *dst =
reinterpret_cast<unsigned char *
>(val);
901 unsigned char *src =
reinterpret_cast<unsigned char *
>(&tmp);
910static void inline swap4(
float *val) {
911#if TINYEXR_LITTLE_ENDIAN
915 unsigned char *dst =
reinterpret_cast<unsigned char *
>(val);
916 unsigned char *src =
reinterpret_cast<unsigned char *
>(&tmp);
926static void inline cpy8(tinyexr::tinyexr_uint64 *dst_val,
const tinyexr::tinyexr_uint64 *src_val) {
927 unsigned char *dst =
reinterpret_cast<unsigned char *
>(dst_val);
928 const unsigned char *src =
reinterpret_cast<const unsigned char *
>(src_val);
941static void inline swap8(tinyexr::tinyexr_uint64 *val) {
942#if TINYEXR_LITTLE_ENDIAN
945 tinyexr::tinyexr_uint64 tmp = (*val);
946 unsigned char *dst =
reinterpret_cast<unsigned char *
>(val);
947 unsigned char *src =
reinterpret_cast<unsigned char *
>(&tmp);
961#if TINYEXR_HAS_FP16_COMPILER_TYPE && (TINYEXR_USE_COMPILER_FP16 > 0)
970#if TINYEXR_LITTLE_ENDIAN
971 unsigned int Mantissa : 23;
972 unsigned int Exponent : 8;
973 unsigned int Sign : 1;
975 unsigned int Sign : 1;
976 unsigned int Exponent : 8;
977 unsigned int Mantissa : 23;
984#pragma clang diagnostic push
985#pragma clang diagnostic ignored "-Wpadded"
988#if TINYEXR_HAS_FP16_COMPILER_TYPE && (TINYEXR_USE_COMPILER_FP16 > 0)
990 TINYEXR_FP16_COMPILER_TYPE f;
999#if TINYEXR_LITTLE_ENDIAN
1000 unsigned int Mantissa : 10;
1001 unsigned int Exponent : 5;
1002 unsigned int Sign : 1;
1004 unsigned int Sign : 1;
1005 unsigned int Exponent : 5;
1006 unsigned int Mantissa : 10;
1013#pragma clang diagnostic pop
1016#if TINYEXR_HAS_FP16_COMPILER_TYPE && (TINYEXR_USE_COMPILER_FP16 > 0)
1017static inline FP32 half_to_float(FP16 h) {
1019#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) && defined(__AVX2__)
1020 o.f =_mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(
static_cast<int> (h.u))));
1022 o.f =
static_cast<float> (h.f);
1026static inline FP16 float_to_half_full(FP32 f) {
1028#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) && defined(__AVX2__)
1029 o.f =
static_cast<TINYEXR_FP16_COMPILER_TYPE
> (_mm_cvtsi128_si32(_mm_cvtps_ph(_mm_set_ss(f.f), _MM_FROUND_CUR_DIRECTION)));
1031 o.f =
static_cast<TINYEXR_FP16_COMPILER_TYPE
> (f.f);
1036static FP32 half_to_float(FP16 h) {
1037 static const FP32 magic = {113 << 23};
1038 static const unsigned int shifted_exp = 0x7c00
1042 o.u = (h.u & 0x7fffU) << 13U;
1043 unsigned int exp_ = shifted_exp & o.u;
1044 o.u += (127 - 15) << 23;
1047 if (exp_ == shifted_exp)
1048 o.u += (128 - 16) << 23;
1055 o.u |= (h.u & 0x8000U) << 16U;
1059static FP16 float_to_half_full(FP32 f) {
1063 if (f.s.Exponent == 0)
1065 else if (f.s.Exponent == 255)
1068 o.s.Mantissa = f.s.Mantissa ? 0x200 : 0;
1072 int newexp = f.s.Exponent - 127 + 15;
1075 else if (newexp <= 0)
1077 if ((14 - newexp) <= 24)
1079 unsigned int mant = f.s.Mantissa | 0x800000;
1080 o.s.Mantissa = mant >> (14 - newexp);
1081 if ((mant >> (13 - newexp)) & 1)
1085 o.s.Exponent =
static_cast<unsigned int>(newexp);
1086 o.s.Mantissa = f.s.Mantissa >> 13;
1087 if (f.s.Mantissa & 0x1000)
1092 o.s.Sign = f.s.Sign;
1111#pragma clang diagnostic push
1113#if __has_warning("-Wzero-as-null-pointer-constant")
1114#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1119static const char *ReadString(std::string *s,
const char *ptr,
size_t len) {
1121 const char *p = ptr;
1122 const char *q = ptr;
1123 while ((
size_t(q - ptr) < len) && (*q) != 0) {
1127 if (
size_t(q - ptr) >= len) {
1132 (*s) = std::string(p, q);
1137static bool ReadAttribute(std::string *name, std::string *type,
1138 std::vector<unsigned char> *data,
size_t *marker_size,
1139 const char *marker,
size_t size) {
1140 size_t name_len = strnlen(marker, size);
1141 if (name_len == size) {
1145 *name = std::string(marker, name_len);
1147 marker += name_len + 1;
1148 size -= name_len + 1;
1150 size_t type_len = strnlen(marker, size);
1151 if (type_len == size) {
1154 *type = std::string(marker, type_len);
1156 marker += type_len + 1;
1157 size -= type_len + 1;
1159 if (size <
sizeof(uint32_t)) {
1164 memcpy(&data_len, marker,
sizeof(uint32_t));
1165 tinyexr::swap4(
reinterpret_cast<unsigned int *
>(&data_len));
1167 if (data_len == 0) {
1168 if ((*type).compare(
"string") == 0) {
1174 *marker_size = name_len + 1 + type_len + 1 +
sizeof(
uint32_t);
1188 if (size < data_len) {
1192 data->resize(
static_cast<size_t>(data_len));
1193 memcpy(&data->at(0), marker,
static_cast<size_t>(data_len));
1195 *marker_size = name_len + 1 + type_len + 1 +
sizeof(
uint32_t) + data_len;
1199static void WriteAttributeToMemory(std::vector<unsigned char> *out,
1200 const char *name,
const char *type,
1201 const unsigned char *data,
int len) {
1202 out->insert(out->end(), name, name + strlen(name) + 1);
1203 out->insert(out->end(), type, type + strlen(type) + 1);
1206 tinyexr::swap4(&outLen);
1207 out->insert(out->end(),
reinterpret_cast<unsigned char *
>(&outLen),
1208 reinterpret_cast<unsigned char *
>(&outLen) +
sizeof(
int));
1209 out->insert(out->end(), data, data + len);
1212typedef struct TChannelInfo {
1215 int requested_pixel_type;
1218 unsigned char p_linear;
1219 unsigned char pad[3];
1230 std::vector<tinyexr::ChannelInfo> channels;
1231 std::vector<EXRAttribute> attributes;
1233 Box2iInfo data_window;
1235 Box2iInfo display_window;
1236 float screen_window_center[2];
1237 float screen_window_width;
1238 float pixel_aspect_ratio;
1246 int tile_level_mode;
1247 int tile_rounding_mode;
1249 unsigned int header_len;
1251 int compression_type;
1262 data_window.min_x = 0;
1263 data_window.min_y = 0;
1264 data_window.max_x = 0;
1265 data_window.max_y = 0;
1267 display_window.min_x = 0;
1268 display_window.min_y = 0;
1269 display_window.max_x = 0;
1270 display_window.max_y = 0;
1271 screen_window_center[0] = 0.0f;
1272 screen_window_center[1] = 0.0f;
1273 screen_window_width = 0.0f;
1274 pixel_aspect_ratio = 0.0f;
1282 tile_level_mode = 0;
1283 tile_rounding_mode = 0;
1286 compression_type = 0;
1293static bool ReadChannelInfo(std::vector<ChannelInfo> &channels,
1294 const std::vector<unsigned char> &data) {
1295 const char *p =
reinterpret_cast<const char *
>(&data.at(0));
1302 info.requested_pixel_type = 0;
1304 tinyexr_int64 data_len =
static_cast<tinyexr_int64
>(data.size()) -
1305 (p -
reinterpret_cast<const char *
>(data.data()));
1310 p = ReadString(&info.name, p,
size_t(data_len));
1311 if ((p == NULL) && (info.name.empty())) {
1316 const unsigned char *data_end =
1317 reinterpret_cast<const unsigned char *
>(p) + 16;
1318 if (data_end >= (data.data() + data.size())) {
1322 memcpy(&info.pixel_type, p,
sizeof(
int));
1324 info.p_linear =
static_cast<unsigned char>(p[0]);
1326 memcpy(&info.x_sampling, p,
sizeof(
int));
1328 memcpy(&info.y_sampling, p,
sizeof(
int));
1331 tinyexr::swap4(&info.pixel_type);
1332 tinyexr::swap4(&info.x_sampling);
1333 tinyexr::swap4(&info.y_sampling);
1335 channels.push_back(info);
1341static void WriteChannelInfo(std::vector<unsigned char> &data,
1342 const std::vector<ChannelInfo> &channels) {
1346 for (
size_t c = 0; c < channels.size(); c++) {
1347 sz += channels[c].name.length() + 1;
1350 data.resize(sz + 1);
1352 unsigned char *p = &data.at(0);
1354 for (
size_t c = 0; c < channels.size(); c++) {
1355 memcpy(p, channels[c].name.c_str(), channels[c].name.length());
1356 p += channels[c].name.length();
1360 int pixel_type = channels[c].requested_pixel_type;
1361 int x_sampling = channels[c].x_sampling;
1362 int y_sampling = channels[c].y_sampling;
1363 tinyexr::swap4(&pixel_type);
1364 tinyexr::swap4(&x_sampling);
1365 tinyexr::swap4(&y_sampling);
1367 memcpy(p, &pixel_type,
sizeof(
int));
1370 (*p) = channels[c].p_linear;
1373 memcpy(p, &x_sampling,
sizeof(
int));
1376 memcpy(p, &y_sampling,
sizeof(
int));
1383static bool CompressZip(
unsigned char *dst,
1384 tinyexr::tinyexr_uint64 &compressedSize,
1385 const unsigned char *src,
unsigned long src_size) {
1386 std::vector<unsigned char> tmpBuf(src_size);
1397 const char *srcPtr =
reinterpret_cast<const char *
>(src);
1400 char *t1 =
reinterpret_cast<char *
>(&tmpBuf.at(0));
1401 char *t2 =
reinterpret_cast<char *
>(&tmpBuf.at(0)) + (src_size + 1) / 2;
1402 const char *stop = srcPtr + src_size;
1406 *(t1++) = *(srcPtr++);
1411 *(t2++) = *(srcPtr++);
1422 unsigned char *t = &tmpBuf.at(0) + 1;
1423 unsigned char *stop = &tmpBuf.at(0) + src_size;
1427 int d = int(t[0]) - p + (128 + 256);
1429 t[0] =
static_cast<unsigned char>(d);
1434#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
1439 mz_ulong outSize = mz_compressBound(src_size);
1440 int ret = mz_compress(
1441 dst, &outSize,
static_cast<const unsigned char *
>(&tmpBuf.at(0)),
1447 compressedSize = outSize;
1448#elif defined(TINYEXR_USE_STB_ZLIB) && (TINYEXR_USE_STB_ZLIB==1)
1450 unsigned char* ret = stbi_zlib_compress(
const_cast<unsigned char*
>(&tmpBuf.at(0)), src_size, &outSize, 8);
1454 memcpy(dst, ret, outSize);
1457 compressedSize = outSize;
1458#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
1461 unsigned char *ret = nanoz_compress(&tmpBuf.at(0), src_size, &outSize, 8);
1466 memcpy(dst, ret, outSize);
1469 compressedSize = outSize;
1471 uLong outSize = compressBound(
static_cast<uLong
>(src_size));
1472 int ret = compress(dst, &outSize,
static_cast<const Bytef *
>(&tmpBuf.at(0)),
1478 compressedSize = outSize;
1483 if (compressedSize >= src_size) {
1484 compressedSize = src_size;
1485 memcpy(dst, src, src_size);
1491static bool DecompressZip(
unsigned char *dst,
1492 unsigned long *uncompressed_size ,
1493 const unsigned char *src,
unsigned long src_size) {
1494 if ((*uncompressed_size) == src_size) {
1496 memcpy(dst, src, src_size);
1499 std::vector<unsigned char> tmpBuf(*uncompressed_size);
1501#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
1503 mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
1507#elif TINYEXR_USE_STB_ZLIB
1508 int ret = stbi_zlib_decode_buffer(
reinterpret_cast<char*
>(&tmpBuf.at(0)),
1509 *uncompressed_size,
reinterpret_cast<const char*
>(src), src_size);
1513#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
1514 uint64_t dest_size = (*uncompressed_size);
1516 nanoz_status_t ret =
1517 nanoz_uncompress(src, src_size, dest_size, &tmpBuf.at(0), &uncomp_size);
1518 if (NANOZ_SUCCESS != ret) {
1521 if ((*uncompressed_size) != uncomp_size) {
1525 int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
1538 unsigned char *t = &tmpBuf.at(0) + 1;
1539 unsigned char *stop = &tmpBuf.at(0) + (*uncompressed_size);
1542 int d = int(t[-1]) + int(t[0]) - 128;
1543 t[0] =
static_cast<unsigned char>(d);
1550 const char *t1 =
reinterpret_cast<const char *
>(&tmpBuf.at(0));
1551 const char *t2 =
reinterpret_cast<const char *
>(&tmpBuf.at(0)) +
1552 (*uncompressed_size + 1) / 2;
1553 char *s =
reinterpret_cast<char *
>(dst);
1554 char *stop = s + (*uncompressed_size);
1575#pragma clang diagnostic push
1576#pragma clang diagnostic ignored "-Wsign-conversion"
1577#if __has_warning("-Wextra-semi-stmt")
1578#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1583#pragma warning(push)
1584#pragma warning(disable : 4204)
1587#pragma warning(disable : 4244)
1589#pragma warning(disable : 4267)
1591#pragma warning(disable : 4996)
1596const int MIN_RUN_LENGTH = 3;
1597const int MAX_RUN_LENGTH = 127;
1604static int rleCompress(
int inLength,
const char in[],
signed char out[]) {
1605 const char *inEnd = in + inLength;
1606 const char *runStart = in;
1607 const char *runEnd = in + 1;
1608 signed char *outWrite = out;
1610 while (runStart < inEnd) {
1611 while (runEnd < inEnd && *runStart == *runEnd &&
1612 runEnd - runStart - 1 < MAX_RUN_LENGTH) {
1616 if (runEnd - runStart >= MIN_RUN_LENGTH) {
1621 *outWrite++ =
static_cast<char>(runEnd - runStart) - 1;
1622 *outWrite++ = *(
reinterpret_cast<const signed char *
>(runStart));
1629 while (runEnd < inEnd &&
1630 ((runEnd + 1 >= inEnd || *runEnd != *(runEnd + 1)) ||
1631 (runEnd + 2 >= inEnd || *(runEnd + 1) != *(runEnd + 2))) &&
1632 runEnd - runStart < MAX_RUN_LENGTH) {
1636 *outWrite++ =
static_cast<char>(runStart - runEnd);
1638 while (runStart < runEnd) {
1639 *outWrite++ = *(
reinterpret_cast<const signed char *
>(runStart++));
1646 return static_cast<int>(outWrite - out);
1655static int rleUncompress(
int inLength,
int maxLength,
const signed char in[],
1657 char *outStart = out;
1659 while (inLength > 0) {
1661 int count = -(
static_cast<int>(*in++));
1662 inLength -= count + 1;
1665 if ((0 > (maxLength -= count)) || (inLength < 0))
return 0;
1667 memcpy(out, in, count);
1674 if ((0 > (maxLength -= count + 1)) || (inLength < 0))
return 0;
1676 memset(out, *
reinterpret_cast<const char *
>(in), count + 1);
1683 return static_cast<int>(out - outStart);
1687#pragma clang diagnostic pop
1692static bool CompressRle(
unsigned char *dst,
1693 tinyexr::tinyexr_uint64 &compressedSize,
1694 const unsigned char *src,
unsigned long src_size) {
1695 std::vector<unsigned char> tmpBuf(src_size);
1706 const char *srcPtr =
reinterpret_cast<const char *
>(src);
1709 char *t1 =
reinterpret_cast<char *
>(&tmpBuf.at(0));
1710 char *t2 =
reinterpret_cast<char *
>(&tmpBuf.at(0)) + (src_size + 1) / 2;
1711 const char *stop = srcPtr + src_size;
1715 *(t1++) = *(srcPtr++);
1720 *(t2++) = *(srcPtr++);
1731 unsigned char *t = &tmpBuf.at(0) + 1;
1732 unsigned char *stop = &tmpBuf.at(0) + src_size;
1736 int d = int(t[0]) - p + (128 + 256);
1738 t[0] =
static_cast<unsigned char>(d);
1744 int outSize = rleCompress(
static_cast<int>(src_size),
1745 reinterpret_cast<const char *
>(&tmpBuf.at(0)),
1746 reinterpret_cast<signed char *
>(dst));
1747 TINYEXR_CHECK_AND_RETURN_C(outSize > 0,
false);
1749 compressedSize =
static_cast<tinyexr::tinyexr_uint64
>(outSize);
1753 if (compressedSize >= src_size) {
1754 compressedSize = src_size;
1755 memcpy(dst, src, src_size);
1761static bool DecompressRle(
unsigned char *dst,
1762 const unsigned long uncompressed_size,
1763 const unsigned char *src,
unsigned long src_size) {
1764 if (uncompressed_size == src_size) {
1766 memcpy(dst, src, src_size);
1772 if (src_size <= 2) {
1776 std::vector<unsigned char> tmpBuf(uncompressed_size);
1778 int ret = rleUncompress(
static_cast<int>(src_size),
1779 static_cast<int>(uncompressed_size),
1780 reinterpret_cast<const signed char *
>(src),
1781 reinterpret_cast<char *
>(&tmpBuf.at(0)));
1782 if (ret !=
static_cast<int>(uncompressed_size)) {
1793 unsigned char *t = &tmpBuf.at(0) + 1;
1794 unsigned char *stop = &tmpBuf.at(0) + uncompressed_size;
1797 int d = int(t[-1]) + int(t[0]) - 128;
1798 t[0] =
static_cast<unsigned char>(d);
1805 const char *t1 =
reinterpret_cast<const char *
>(&tmpBuf.at(0));
1806 const char *t2 =
reinterpret_cast<const char *
>(&tmpBuf.at(0)) +
1807 (uncompressed_size + 1) / 2;
1808 char *s =
reinterpret_cast<char *
>(dst);
1809 char *stop = s + uncompressed_size;
1830#pragma clang diagnostic push
1831#pragma clang diagnostic ignored "-Wc++11-long-long"
1832#pragma clang diagnostic ignored "-Wold-style-cast"
1833#pragma clang diagnostic ignored "-Wpadded"
1834#pragma clang diagnostic ignored "-Wsign-conversion"
1835#pragma clang diagnostic ignored "-Wc++11-extensions"
1836#pragma clang diagnostic ignored "-Wconversion"
1837#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1839#if __has_warning("-Wcast-qual")
1840#pragma clang diagnostic ignored "-Wcast-qual"
1843#if __has_warning("-Wextra-semi-stmt")
1844#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1858struct PIZChannelData {
1859 unsigned short *start;
1860 unsigned short *end;
1884inline void wenc14(
unsigned short a,
unsigned short b,
unsigned short &l,
1885 unsigned short &h) {
1886 short as =
static_cast<short>(a);
1887 short bs =
static_cast<short>(b);
1889 short ms = (as + bs) >> 1;
1892 l =
static_cast<unsigned short>(ms);
1893 h =
static_cast<unsigned short>(ds);
1896inline void wdec14(
unsigned short l,
unsigned short h,
unsigned short &a,
1897 unsigned short &b) {
1898 short ls =
static_cast<short>(l);
1899 short hs =
static_cast<short>(h);
1902 int ai = ls + (hi & 1) + (hi >> 1);
1904 short as =
static_cast<short>(ai);
1905 short bs =
static_cast<short>(ai - hi);
1907 a =
static_cast<unsigned short>(as);
1908 b =
static_cast<unsigned short>(bs);
1917const int NBITS = 16;
1918const int A_OFFSET = 1 << (NBITS - 1);
1919const int M_OFFSET = 1 << (NBITS - 1);
1920const int MOD_MASK = (1 << NBITS) - 1;
1922inline void wenc16(
unsigned short a,
unsigned short b,
unsigned short &l,
1923 unsigned short &h) {
1924 int ao = (a + A_OFFSET) & MOD_MASK;
1925 int m = ((ao + b) >> 1);
1928 if (d < 0) m = (m + M_OFFSET) & MOD_MASK;
1932 l =
static_cast<unsigned short>(m);
1933 h =
static_cast<unsigned short>(d);
1936inline void wdec16(
unsigned short l,
unsigned short h,
unsigned short &a,
1937 unsigned short &b) {
1940 int bb = (m - (d >> 1)) & MOD_MASK;
1941 int aa = (d + bb - A_OFFSET) & MOD_MASK;
1942 b =
static_cast<unsigned short>(bb);
1943 a =
static_cast<unsigned short>(aa);
1950static void wav2Encode(
1958 bool w14 = (mx < (1 << 14));
1959 int n = (nx > ny) ? ny : nx;
1968 unsigned short *py = in;
1969 unsigned short *ey = in + oy * (ny - p2);
1974 unsigned short i00, i01, i10, i11;
1980 for (; py <= ey; py += oy2) {
1981 unsigned short *px = py;
1982 unsigned short *ex = py + ox * (nx - p2);
1988 for (; px <= ex; px += ox2) {
1989 unsigned short *p01 = px + ox1;
1990 unsigned short *p10 = px + oy1;
1991 unsigned short *p11 = p10 + ox1;
1998 wenc14(*px, *p01, i00, i01);
1999 wenc14(*p10, *p11, i10, i11);
2000 wenc14(i00, i10, *px, *p10);
2001 wenc14(i01, i11, *p01, *p11);
2003 wenc16(*px, *p01, i00, i01);
2004 wenc16(*p10, *p11, i10, i11);
2005 wenc16(i00, i10, *px, *p10);
2006 wenc16(i01, i11, *p01, *p11);
2015 unsigned short *p10 = px + oy1;
2018 wenc14(*px, *p10, i00, *p10);
2020 wenc16(*px, *p10, i00, *p10);
2031 unsigned short *px = py;
2032 unsigned short *ex = py + ox * (nx - p2);
2034 for (; px <= ex; px += ox2) {
2035 unsigned short *p01 = px + ox1;
2038 wenc14(*px, *p01, i00, *p01);
2040 wenc16(*px, *p01, i00, *p01);
2059static void wav2Decode(
2067 bool w14 = (mx < (1 << 14));
2068 int n = (nx > ny) ? ny : nx;
2076 while (p <= n) p <<= 1;
2087 unsigned short *py = in;
2088 unsigned short *ey = in + oy * (ny - p2);
2093 unsigned short i00, i01, i10, i11;
2099 for (; py <= ey; py += oy2) {
2100 unsigned short *px = py;
2101 unsigned short *ex = py + ox * (nx - p2);
2107 for (; px <= ex; px += ox2) {
2108 unsigned short *p01 = px + ox1;
2109 unsigned short *p10 = px + oy1;
2110 unsigned short *p11 = p10 + ox1;
2117 wdec14(*px, *p10, i00, i10);
2118 wdec14(*p01, *p11, i01, i11);
2119 wdec14(i00, i01, *px, *p01);
2120 wdec14(i10, i11, *p10, *p11);
2122 wdec16(*px, *p10, i00, i10);
2123 wdec16(*p01, *p11, i01, i11);
2124 wdec16(i00, i01, *px, *p01);
2125 wdec16(i10, i11, *p10, *p11);
2134 unsigned short *p10 = px + oy1;
2137 wdec14(*px, *p10, i00, *p10);
2139 wdec16(*px, *p10, i00, *p10);
2150 unsigned short *px = py;
2151 unsigned short *ex = py + ox * (nx - p2);
2153 for (; px <= ex; px += ox2) {
2154 unsigned short *p01 = px + ox1;
2157 wdec14(*px, *p01, i00, *p01);
2159 wdec16(*px, *p01, i00, *p01);
2186const int HUF_ENCBITS = 16;
2187const int HUF_DECBITS = 14;
2189const int HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1;
2190const int HUF_DECSIZE = 1 << HUF_DECBITS;
2191const int HUF_DECMASK = HUF_DECSIZE - 1;
2195 unsigned int len : 8;
2196 unsigned int lit : 24;
2200inline long long hufLength(
long long code) {
return code & 63; }
2202inline long long hufCode(
long long code) {
return code >> 6; }
2204inline void outputBits(
int nBits,
long long bits,
long long &c,
int &lc,
2211 while (lc >= 8) *out++ =
static_cast<char>((c >> (lc -= 8)));
2214inline long long getBits(
int nBits,
long long &c,
int &lc,
const char *&in) {
2215 while (lc < nBits) {
2216 c = (c << 8) | *(reinterpret_cast<const unsigned char *>(in++));
2221 return (c >> lc) & ((1 << nBits) - 1);
2244static void hufCanonicalCodeTable(
long long hcode[HUF_ENCSIZE]) {
2253 for (
int i = 0; i <= 58; ++i) n[i] = 0;
2255 for (
int i = 0; i < HUF_ENCSIZE; ++i) n[hcode[i]] += 1;
2265 for (
int i = 58; i > 0; --i) {
2266 long long nc = ((c + n[i]) >> 1);
2278 for (
int i = 0; i < HUF_ENCSIZE; ++i) {
2279 int l =
static_cast<int>(hcode[i]);
2281 if (l > 0) hcode[i] = l | (n[l]++ << 6);
2294struct FHeapCompare {
2295 bool operator()(
long long *a,
long long *b) {
return *a > *b; }
2298static bool hufBuildEncTable(
2324 std::vector<int> hlink(HUF_ENCSIZE);
2325 std::vector<long long *> fHeap(HUF_ENCSIZE);
2329 while (!frq[*im]) (*im)++;
2333 for (
int i = *im; i < HUF_ENCSIZE; i++) {
2337 fHeap[nf] = &frq[i];
2351 fHeap[nf] = &frq[*iM];
2382 std::make_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2384 std::vector<long long> scode(HUF_ENCSIZE);
2385 memset(scode.data(), 0,
sizeof(
long long) * HUF_ENCSIZE);
2394 int mm = fHeap[0] - frq;
2395 std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2398 int m = fHeap[0] - frq;
2399 std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2402 std::push_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2423 for (
int j = m;; j = hlink[j]) {
2426 TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58,
false);
2428 if (hlink[j] == j) {
2442 for (
int j = mm;; j = hlink[j]) {
2445 TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58,
false);
2447 if (hlink[j] == j)
break;
2457 hufCanonicalCodeTable(scode.data());
2458 memcpy(frq, scode.data(),
sizeof(
long long) * HUF_ENCSIZE);
2478const int SHORT_ZEROCODE_RUN = 59;
2479const int LONG_ZEROCODE_RUN = 63;
2480const int SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
2481const int LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN;
2483static void hufPackEncTable(
2484 const long long *hcode,
2493 for (; im <= iM; im++) {
2494 int l = hufLength(hcode[im]);
2499 while ((im < iM) && (zerun < LONGEST_LONG_RUN)) {
2500 if (hufLength(hcode[im + 1]) > 0)
break;
2506 if (zerun >= SHORTEST_LONG_RUN) {
2507 outputBits(6, LONG_ZEROCODE_RUN, c, lc, p);
2508 outputBits(8, zerun - SHORTEST_LONG_RUN, c, lc, p);
2510 outputBits(6, SHORT_ZEROCODE_RUN + zerun - 2, c, lc, p);
2516 outputBits(6, l, c, lc, p);
2519 if (lc > 0) *p++ = (
unsigned char)(c << (8 - lc));
2528static bool hufUnpackEncTable(
2535 memset(hcode, 0,
sizeof(
long long) * HUF_ENCSIZE);
2537 const char *p = *pcode;
2541 for (; im <= iM; im++) {
2542 if (p - *pcode >= ni) {
2546 long long l = hcode[im] = getBits(6, c, lc, p);
2548 if (l == (
long long)LONG_ZEROCODE_RUN) {
2549 if (p - *pcode > ni) {
2553 int zerun = getBits(8, c, lc, p) + SHORTEST_LONG_RUN;
2555 if (im + zerun > iM + 1) {
2559 while (zerun--) hcode[im++] = 0;
2562 }
else if (l >= (
long long)SHORT_ZEROCODE_RUN) {
2563 int zerun = l - SHORT_ZEROCODE_RUN + 2;
2565 if (im + zerun > iM + 1) {
2569 while (zerun--) hcode[im++] = 0;
2575 *pcode =
const_cast<char *
>(p);
2577 hufCanonicalCodeTable(hcode);
2590static void hufClearDecTable(HufDec *hdecod)
2593 for (
int i = 0; i < HUF_DECSIZE; i++) {
2609static bool hufBuildDecTable(
const long long *hcode,
2620 for (; im <= iM; im++) {
2621 long long c = hufCode(hcode[im]);
2622 int l = hufLength(hcode[im]);
2635 if (l > HUF_DECBITS) {
2640 HufDec *pl = hdecod + (c >> (l - HUF_DECBITS));
2655 unsigned int *p = pl->p;
2656 pl->p =
new unsigned int[pl->lit];
2658 for (
unsigned int i = 0; i < pl->lit - 1u; ++i) pl->p[i] = p[i];
2662 pl->p =
new unsigned int[1];
2665 pl->p[pl->lit - 1] = im;
2671 HufDec *pl = hdecod + (c << (HUF_DECBITS - l));
2673 for (
long long i = 1ULL << (HUF_DECBITS - l); i > 0; i--, pl++) {
2674 if (pl->len || pl->p) {
2697static void hufFreeDecTable(HufDec *hdecod)
2699 for (
int i = 0; i < HUF_DECSIZE; i++) {
2701 delete[] hdecod[i].p;
2711inline void outputCode(
long long code,
long long &c,
int &lc,
char *&out) {
2712 outputBits(hufLength(code), hufCode(code), c, lc, out);
2715inline void sendCode(
long long sCode,
int runCount,
long long runCode,
2716 long long &c,
int &lc,
char *&out) {
2724 if (hufLength(sCode) + hufLength(runCode) + 8 < hufLength(sCode) * runCount) {
2725 outputCode(sCode, c, lc, out);
2726 outputCode(runCode, c, lc, out);
2727 outputBits(8, runCount, c, lc, out);
2729 while (runCount-- >= 0) outputCode(sCode, c, lc, out);
2738 (
const long long *hcode,
2739 const unsigned short *in,
2744 char *outStart = out;
2754 for (
int i = 1; i < ni; i++) {
2759 if (s == in[i] && cs < 255) {
2762 sendCode(hcode[s], cs, hcode[rlc], c, lc, out);
2773 sendCode(hcode[s], cs, hcode[rlc], c, lc, out);
2775 if (lc) *out = (c << (8 - lc)) & 0xff;
2777 return (out - outStart) * 8 + lc;
2790#define getChar(c, lc, in) \
2792 c = (c << 8) | *(unsigned char *)(in++); \
2797#define getCode(po, rlc, c, lc, in, out, ob, oe) \
2800 if (lc < 8) getChar(c, lc, in); \
2804 unsigned char cs = (c >> lc); \
2806 if (out + cs > oe) return false; \
2809 unsigned short s = out[-1]; \
2811 while (cs-- > 0) *out++ = s; \
2812 } else if (out < oe) { \
2819static bool getCode(
int po,
int rlc,
long long &c,
int &lc,
const char *&in,
2820 const char *in_end,
unsigned short *&out,
2821 const unsigned short *ob,
const unsigned short *oe) {
2836 unsigned char cs = (c >> lc);
2838 if (out + cs > oe)
return false;
2842 if ((out - 1) < ob)
return false;
2843 unsigned short s = out[-1];
2845 while (cs-- > 0) *out++ = s;
2846 }
else if (out < oe) {
2859static bool hufDecode(
const long long *hcode,
2860 const HufDec *hdecod,
2865 unsigned short *out)
2869 unsigned short *outb = out;
2870 unsigned short *oe = out + no;
2871 const char *ie = in + (ni + 7) / 8;
2884 while (lc >= HUF_DECBITS) {
2885 const HufDec pl = hdecod[(c >> (lc - HUF_DECBITS)) & HUF_DECMASK];
2900 if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) {
2915 for (j = 0; j < pl.lit; j++) {
2916 int l = hufLength(hcode[pl.p[j]]);
2918 while (lc < l && in < ie)
2922 if (hufCode(hcode[pl.p[j]]) ==
2923 ((c >> (lc - l)) & (((
long long)(1) << l) - 1))) {
2929 if (!getCode(pl.p[j], rlc, c, lc, in, ie, out, outb, oe)) {
2949 int i = (8 - ni) & 7;
2954 const HufDec pl = hdecod[(c << (HUF_DECBITS - lc)) & HUF_DECMASK];
2958 if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) {
2967 if (out - outb != no) {
2975static void countFrequencies(std::vector<long long> &freq,
2976 const unsigned short data[],
int n) {
2977 for (
int i = 0; i < HUF_ENCSIZE; ++i) freq[i] = 0;
2979 for (
int i = 0; i < n; ++i) ++freq[data[i]];
2982static void writeUInt(
char buf[4],
unsigned int i) {
2983 unsigned char *b = (
unsigned char *)buf;
2991static unsigned int readUInt(
const char buf[4]) {
2992 const unsigned char *b = (
const unsigned char *)buf;
2994 return (b[0] & 0x000000ff) | ((b[1] << 8) & 0x0000ff00) |
2995 ((b[2] << 16) & 0x00ff0000) | ((b[3] << 24) & 0xff000000);
3002static int hufCompress(
const unsigned short raw[],
int nRaw,
3003 char compressed[]) {
3004 if (nRaw == 0)
return 0;
3006 std::vector<long long> freq(HUF_ENCSIZE);
3008 countFrequencies(freq, raw, nRaw);
3012 hufBuildEncTable(freq.data(), &im, &iM);
3014 char *tableStart = compressed + 20;
3015 char *tableEnd = tableStart;
3016 hufPackEncTable(freq.data(), im, iM, &tableEnd);
3017 int tableLength = tableEnd - tableStart;
3019 char *dataStart = tableEnd;
3020 int nBits = hufEncode(freq.data(), raw, nRaw, iM, dataStart);
3021 int data_length = (nBits + 7) / 8;
3023 writeUInt(compressed, im);
3024 writeUInt(compressed + 4, iM);
3025 writeUInt(compressed + 8, tableLength);
3026 writeUInt(compressed + 12, nBits);
3027 writeUInt(compressed + 16, 0);
3029 return dataStart + data_length - compressed;
3032static bool hufUncompress(
const char compressed[],
int nCompressed,
3033 std::vector<unsigned short> *raw) {
3034 if (nCompressed == 0) {
3035 if (raw->size() != 0)
return false;
3040 int im = readUInt(compressed);
3041 int iM = readUInt(compressed + 4);
3043 int nBits = readUInt(compressed + 12);
3045 if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE)
return false;
3047 const char *ptr = compressed + 20;
3062 std::vector<long long> freq(HUF_ENCSIZE);
3063 std::vector<HufDec> hdec(HUF_DECSIZE);
3065 hufClearDecTable(&hdec.at(0));
3067 hufUnpackEncTable(&ptr, nCompressed - (ptr - compressed), im, iM,
3071 if (nBits > 8 * (nCompressed - (ptr - compressed))) {
3075 hufBuildDecTable(&freq.at(0), im, iM, &hdec.at(0));
3076 hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, raw->size(),
3085 hufFreeDecTable(&hdec.at(0));
3095const int USHORT_RANGE = (1 << 16);
3096const int BITMAP_SIZE = (USHORT_RANGE >> 3);
3098static void bitmapFromData(
const unsigned short data[],
int nData,
3099 unsigned char bitmap[BITMAP_SIZE],
3100 unsigned short &minNonZero,
3101 unsigned short &maxNonZero) {
3102 for (
int i = 0; i < BITMAP_SIZE; ++i) bitmap[i] = 0;
3104 for (
int i = 0; i < nData; ++i) bitmap[data[i] >> 3] |= (1 << (data[i] & 7));
3109 minNonZero = BITMAP_SIZE - 1;
3112 for (
int i = 0; i < BITMAP_SIZE; ++i) {
3114 if (minNonZero > i) minNonZero = i;
3115 if (maxNonZero < i) maxNonZero = i;
3120static unsigned short forwardLutFromBitmap(
3121 const unsigned char bitmap[BITMAP_SIZE],
unsigned short lut[USHORT_RANGE]) {
3124 for (
int i = 0; i < USHORT_RANGE; ++i) {
3125 if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))))
3134static unsigned short reverseLutFromBitmap(
3135 const unsigned char bitmap[BITMAP_SIZE],
unsigned short lut[USHORT_RANGE]) {
3138 for (
int i = 0; i < USHORT_RANGE; ++i) {
3139 if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) lut[k++] = i;
3144 while (k < USHORT_RANGE) lut[k++] = 0;
3149static void applyLut(
const unsigned short lut[USHORT_RANGE],
3150 unsigned short data[],
int nData) {
3151 for (
int i = 0; i < nData; ++i) data[i] = lut[data[i]];
3155#pragma clang diagnostic pop
3162static bool CompressPiz(
unsigned char *outPtr,
unsigned int *outSize,
3163 const unsigned char *inPtr,
size_t inSize,
3164 const std::vector<ChannelInfo> &channelInfo,
3165 int data_width,
int num_lines) {
3166 std::vector<unsigned char> bitmap(BITMAP_SIZE);
3167 unsigned short minNonZero;
3168 unsigned short maxNonZero;
3170#if !TINYEXR_LITTLE_ENDIAN
3176 std::vector<unsigned short> tmpBuffer(inSize /
sizeof(
unsigned short));
3178 std::vector<PIZChannelData> channelData(channelInfo.size());
3179 unsigned short *tmpBufferEnd = &tmpBuffer.at(0);
3181 for (
size_t c = 0; c < channelData.size(); c++) {
3182 PIZChannelData &cd = channelData[c];
3184 cd.start = tmpBufferEnd;
3191 size_t pixelSize =
sizeof(int);
3192 if (channelInfo[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
3193 pixelSize =
sizeof(short);
3196 cd.size =
static_cast<int>(pixelSize /
sizeof(short));
3198 tmpBufferEnd += cd.nx * cd.ny * cd.size;
3201 const unsigned char *ptr = inPtr;
3202 for (
int y = 0; y < num_lines; ++y) {
3203 for (
size_t i = 0; i < channelData.size(); ++i) {
3204 PIZChannelData &cd = channelData[i];
3209 size_t n =
static_cast<size_t>(cd.nx * cd.size);
3210 memcpy(cd.end, ptr, n *
sizeof(
unsigned short));
3211 ptr += n *
sizeof(
unsigned short);
3216 bitmapFromData(&tmpBuffer.at(0),
static_cast<int>(tmpBuffer.size()),
3217 bitmap.data(), minNonZero, maxNonZero);
3219 std::vector<unsigned short> lut(USHORT_RANGE);
3220 unsigned short maxValue = forwardLutFromBitmap(bitmap.data(), lut.data());
3221 applyLut(lut.data(), &tmpBuffer.at(0),
static_cast<int>(tmpBuffer.size()));
3227 char *buf =
reinterpret_cast<char *
>(outPtr);
3229 memcpy(buf, &minNonZero,
sizeof(
unsigned short));
3230 buf +=
sizeof(
unsigned short);
3231 memcpy(buf, &maxNonZero,
sizeof(
unsigned short));
3232 buf +=
sizeof(
unsigned short);
3234 if (minNonZero <= maxNonZero) {
3235 memcpy(buf,
reinterpret_cast<char *
>(&bitmap[0] + minNonZero),
3236 maxNonZero - minNonZero + 1);
3237 buf += maxNonZero - minNonZero + 1;
3244 for (
size_t i = 0; i < channelData.size(); ++i) {
3245 PIZChannelData &cd = channelData[i];
3247 for (
int j = 0; j < cd.size; ++j) {
3248 wav2Encode(cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size,
3259 char *lengthPtr = buf;
3261 memcpy(buf, &zero,
sizeof(
int));
3265 hufCompress(&tmpBuffer.at(0),
static_cast<int>(tmpBuffer.size()), buf);
3266 memcpy(lengthPtr, &length,
sizeof(
int));
3268 (*outSize) =
static_cast<unsigned int>(
3269 (
reinterpret_cast<unsigned char *
>(buf) - outPtr) +
3270 static_cast<unsigned int>(length));
3274 if ((*outSize) >= inSize) {
3275 (*outSize) =
static_cast<unsigned int>(inSize);
3276 memcpy(outPtr, inPtr, inSize);
3281static bool DecompressPiz(
unsigned char *outPtr,
const unsigned char *inPtr,
3282 size_t tmpBufSizeInBytes,
size_t inLen,
int num_channels,
3285 if (inLen == tmpBufSizeInBytes) {
3287 memcpy(outPtr, inPtr, inLen);
3291 std::vector<unsigned char> bitmap(BITMAP_SIZE);
3292 unsigned short minNonZero;
3293 unsigned short maxNonZero;
3295#if !TINYEXR_LITTLE_ENDIAN
3300 memset(bitmap.data(), 0, BITMAP_SIZE);
3308 const unsigned char *ptr = inPtr;
3310 tinyexr::cpy2(&minNonZero,
reinterpret_cast<const unsigned short *
>(ptr));
3312 tinyexr::cpy2(&maxNonZero,
reinterpret_cast<const unsigned short *
>(ptr + 2));
3316 if (maxNonZero >= BITMAP_SIZE) {
3325 if (minNonZero <= maxNonZero) {
3326 if (((maxNonZero - minNonZero + 1) + readLen) > inLen) {
3331 memcpy(
reinterpret_cast<char *
>(&bitmap[0] + minNonZero), ptr,
3332 maxNonZero - minNonZero + 1);
3333 ptr += maxNonZero - minNonZero + 1;
3334 readLen += maxNonZero - minNonZero + 1;
3337 if ((minNonZero == (BITMAP_SIZE - 1)) && (maxNonZero == 0)) {
3345 std::vector<unsigned short> lut(USHORT_RANGE);
3346 memset(lut.data(), 0,
sizeof(
unsigned short) * USHORT_RANGE);
3347 unsigned short maxValue = reverseLutFromBitmap(bitmap.data(), lut.data());
3353 if ((readLen + 4) > inLen) {
3360 tinyexr::cpy4(&length,
reinterpret_cast<const int *
>(ptr));
3363 if (
size_t((ptr - inPtr) + length) > inLen) {
3367 std::vector<unsigned short> tmpBuffer(tmpBufSizeInBytes /
sizeof(
unsigned short));
3368 hufUncompress(
reinterpret_cast<const char *
>(ptr), length, &tmpBuffer);
3374 std::vector<PIZChannelData> channelData(
static_cast<size_t>(num_channels));
3376 unsigned short *tmpBufferEnd = &tmpBuffer.at(0);
3378 for (
size_t i = 0; i < static_cast<size_t>(num_channels); ++i) {
3381 size_t pixelSize =
sizeof(int);
3382 if (chan.pixel_type == TINYEXR_PIXELTYPE_HALF) {
3383 pixelSize =
sizeof(short);
3386 channelData[i].start = tmpBufferEnd;
3387 channelData[i].end = channelData[i].start;
3388 channelData[i].nx = data_width;
3389 channelData[i].ny = num_lines;
3391 channelData[i].size =
static_cast<int>(pixelSize /
sizeof(short));
3393 tmpBufferEnd += channelData[i].nx * channelData[i].ny * channelData[i].size;
3396 for (
size_t i = 0; i < channelData.size(); ++i) {
3397 PIZChannelData &cd = channelData[i];
3399 for (
int j = 0; j < cd.size; ++j) {
3400 wav2Decode(cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size,
3409 applyLut(lut.data(), &tmpBuffer.at(0),
static_cast<int>(tmpBufSizeInBytes /
sizeof(
unsigned short)));
3411 for (
int y = 0; y < num_lines; y++) {
3412 for (
size_t i = 0; i < channelData.size(); ++i) {
3413 PIZChannelData &cd = channelData[i];
3418 size_t n =
static_cast<size_t>(cd.nx * cd.size);
3419 memcpy(outPtr, cd.end,
static_cast<size_t>(n *
sizeof(
unsigned short)));
3420 outPtr += n *
sizeof(
unsigned short);
3431struct ZFPCompressionParam {
3433 unsigned int precision;
3434 unsigned int __pad0;
3437 unsigned int __pad1;
3439 ZFPCompressionParam() {
3440 type = TINYEXR_ZFP_COMPRESSIONTYPE_RATE;
3447static bool FindZFPCompressionParam(ZFPCompressionParam *param,
3449 int num_attributes, std::string *err) {
3450 bool foundType =
false;
3452 for (
int i = 0; i < num_attributes; i++) {
3453 if ((strcmp(attributes[i].name,
"zfpCompressionType") == 0)) {
3454 if (attributes[i].size == 1) {
3455 param->type =
static_cast<int>(attributes[i].value[0]);
3461 "zfpCompressionType attribute must be uchar(1 byte) type.\n";
3470 (*err) +=
"`zfpCompressionType` attribute not found.\n";
3475 if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
3476 for (
int i = 0; i < num_attributes; i++) {
3477 if ((strcmp(attributes[i].name,
"zfpCompressionRate") == 0) &&
3478 (attributes[i].size == 8)) {
3479 param->rate = *(
reinterpret_cast<double *
>(attributes[i].value));
3485 (*err) +=
"`zfpCompressionRate` attribute not found.\n";
3488 }
else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
3489 for (
int i = 0; i < num_attributes; i++) {
3490 if ((strcmp(attributes[i].name,
"zfpCompressionPrecision") == 0) &&
3491 (attributes[i].size == 4)) {
3492 param->rate = *(
reinterpret_cast<int *
>(attributes[i].value));
3498 (*err) +=
"`zfpCompressionPrecision` attribute not found.\n";
3501 }
else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
3502 for (
int i = 0; i < num_attributes; i++) {
3503 if ((strcmp(attributes[i].name,
"zfpCompressionTolerance") == 0) &&
3504 (attributes[i].size == 8)) {
3505 param->tolerance = *(
reinterpret_cast<double *
>(attributes[i].value));
3511 (*err) +=
"`zfpCompressionTolerance` attribute not found.\n";
3515 (*err) +=
"Unknown value specified for `zfpCompressionType`.\n";
3523static bool DecompressZfp(
float *dst,
int dst_width,
int dst_num_lines,
3524 size_t num_channels,
const unsigned char *src,
3525 unsigned long src_size,
3526 const ZFPCompressionParam ¶m) {
3527 size_t uncompressed_size =
3528 size_t(dst_width) * size_t(dst_num_lines) * num_channels;
3530 if (uncompressed_size == src_size) {
3532 memcpy(dst, src, src_size);
3535 zfp_stream *zfp = NULL;
3536 zfp_field *field = NULL;
3538 TINYEXR_CHECK_AND_RETURN_C((dst_width % 4) == 0,
false);
3539 TINYEXR_CHECK_AND_RETURN_C((dst_num_lines % 4) == 0,
false);
3541 if ((
size_t(dst_width) & 3U) || (
size_t(dst_num_lines) & 3U)) {
3546 zfp_field_2d(
reinterpret_cast<void *
>(
const_cast<unsigned char *
>(src)),
3547 zfp_type_float,
static_cast<unsigned int>(dst_width),
3548 static_cast<unsigned int>(dst_num_lines) *
3549 static_cast<unsigned int>(num_channels));
3550 zfp = zfp_stream_open(NULL);
3552 if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
3553 zfp_stream_set_rate(zfp, param.rate, zfp_type_float, 2,
3555 }
else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
3556 zfp_stream_set_precision(zfp, param.precision);
3557 }
else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
3558 zfp_stream_set_accuracy(zfp, param.tolerance);
3563 size_t buf_size = zfp_stream_maximum_size(zfp, field);
3564 std::vector<unsigned char> buf(buf_size);
3565 memcpy(&buf.at(0), src, src_size);
3567 bitstream *stream = stream_open(&buf.at(0), buf_size);
3568 zfp_stream_set_bit_stream(zfp, stream);
3569 zfp_stream_rewind(zfp);
3571 size_t image_size = size_t(dst_width) * size_t(dst_num_lines);
3573 for (
size_t c = 0; c < size_t(num_channels); c++) {
3575 for (
size_t y = 0; y < size_t(dst_num_lines); y += 4) {
3576 for (
size_t x = 0; x < size_t(dst_width); x += 4) {
3578 zfp_decode_block_float_2(zfp, fblock);
3579 for (
size_t j = 0; j < 4; j++) {
3580 for (
size_t i = 0; i < 4; i++) {
3581 dst[c * image_size + ((y + j) *
size_t(dst_width) + (x + i))] =
3589 zfp_field_free(field);
3590 zfp_stream_close(zfp);
3591 stream_close(stream);
3597static bool CompressZfp(std::vector<unsigned char> *outBuf,
3598 unsigned int *outSize,
const float *inPtr,
int width,
3599 int num_lines,
int num_channels,
3600 const ZFPCompressionParam ¶m) {
3601 zfp_stream *zfp = NULL;
3602 zfp_field *field = NULL;
3604 TINYEXR_CHECK_AND_RETURN_C((width % 4) == 0,
false);
3605 TINYEXR_CHECK_AND_RETURN_C((num_lines % 4) == 0,
false);
3607 if ((
size_t(width) & 3U) || (
size_t(num_lines) & 3U)) {
3612 field = zfp_field_2d(
reinterpret_cast<void *
>(
const_cast<float *
>(inPtr)),
3613 zfp_type_float,
static_cast<unsigned int>(width),
3614 static_cast<unsigned int>(num_lines * num_channels));
3616 zfp = zfp_stream_open(NULL);
3618 if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
3619 zfp_stream_set_rate(zfp, param.rate, zfp_type_float, 2, 0);
3620 }
else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
3621 zfp_stream_set_precision(zfp, param.precision);
3622 }
else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
3623 zfp_stream_set_accuracy(zfp, param.tolerance);
3628 size_t buf_size = zfp_stream_maximum_size(zfp, field);
3630 outBuf->resize(buf_size);
3632 bitstream *stream = stream_open(&outBuf->at(0), buf_size);
3633 zfp_stream_set_bit_stream(zfp, stream);
3634 zfp_field_free(field);
3636 size_t image_size = size_t(width) * size_t(num_lines);
3638 for (
size_t c = 0; c < size_t(num_channels); c++) {
3640 for (
size_t y = 0; y < size_t(num_lines); y += 4) {
3641 for (
size_t x = 0; x < size_t(width); x += 4) {
3643 for (
size_t j = 0; j < 4; j++) {
3644 for (
size_t i = 0; i < 4; i++) {
3646 inPtr[c * image_size + ((y + j) *
size_t(width) + (x + i))];
3649 zfp_encode_block_float_2(zfp, fblock);
3654 zfp_stream_flush(zfp);
3655 (*outSize) =
static_cast<unsigned int>(zfp_stream_compressed_size(zfp));
3657 zfp_stream_close(zfp);
3669#define TINYEXR_DIMENSION_THRESHOLD (1024 * 8192)
3672static bool DecodePixelData(
unsigned char **out_images,
3673 const int *requested_pixel_types,
3674 const unsigned char *data_ptr,
size_t data_len,
3675 int compression_type,
int line_order,
int width,
3676 int height,
int x_stride,
int y,
int line_no,
3677 int num_lines,
size_t pixel_data_size,
3678 size_t num_attributes,
3681 const std::vector<size_t> &channel_offset_list) {
3682 if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
3684 if ((width == 0) || (num_lines == 0) || (pixel_data_size == 0)) {
3690 std::vector<unsigned char> outBuf(
static_cast<size_t>(
3691 static_cast<size_t>(width * num_lines) * pixel_data_size));
3692 size_t tmpBufLen = outBuf.size();
3694 bool ret = tinyexr::DecompressPiz(
3695 reinterpret_cast<unsigned char *
>(&outBuf.at(0)), data_ptr, tmpBufLen,
3696 data_len,
static_cast<int>(num_channels), channels, width, num_lines);
3712 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
3713 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
3714 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3715 const unsigned short *line_ptr =
reinterpret_cast<unsigned short *
>(
3716 &outBuf.at(v * pixel_data_size *
static_cast<size_t>(width) +
3717 channel_offset_list[c] *
static_cast<size_t>(width)));
3718 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
3724 tinyexr::cpy2(&(hf.u), line_ptr + u);
3726 tinyexr::swap2(
reinterpret_cast<unsigned short *
>(&hf.u));
3728 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
3729 unsigned short *image =
3730 reinterpret_cast<unsigned short **
>(out_images)[c];
3731 if (line_order == 0) {
3732 image += (
static_cast<size_t>(line_no) + v) *
3733 static_cast<size_t>(x_stride) +
3736 image +=
static_cast<size_t>(
3737 (height - 1 - (line_no +
static_cast<int>(v)))) *
3738 static_cast<size_t>(x_stride) +
3743 FP32
f32 = half_to_float(hf);
3744 float *image =
reinterpret_cast<float **
>(out_images)[c];
3746 if (line_order == 0) {
3747 offset = (
static_cast<size_t>(line_no) + v) *
3748 static_cast<size_t>(x_stride) +
3751 offset =
static_cast<size_t>(
3752 (height - 1 - (line_no +
static_cast<int>(v)))) *
3753 static_cast<size_t>(x_stride) +
3761 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
3762 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT,
false);
3764 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3765 const unsigned int *line_ptr =
reinterpret_cast<unsigned int *
>(
3766 &outBuf.at(v * pixel_data_size *
static_cast<size_t>(width) +
3767 channel_offset_list[c] *
static_cast<size_t>(width)));
3768 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
3771 tinyexr::cpy4(&val, line_ptr + u);
3773 tinyexr::swap4(&val);
3775 unsigned int *image =
3776 reinterpret_cast<unsigned int **
>(out_images)[c];
3777 if (line_order == 0) {
3778 image += (
static_cast<size_t>(line_no) + v) *
3779 static_cast<size_t>(x_stride) +
3782 image +=
static_cast<size_t>(
3783 (height - 1 - (line_no +
static_cast<int>(v)))) *
3784 static_cast<size_t>(x_stride) +
3790 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
3791 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT,
false);
3792 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3793 const float *line_ptr =
reinterpret_cast<float *
>(&outBuf.at(
3794 v * pixel_data_size *
static_cast<size_t>(width) +
3795 channel_offset_list[c] *
static_cast<size_t>(width)));
3796 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
3799 tinyexr::cpy4(&val, line_ptr + u);
3801 tinyexr::swap4(
reinterpret_cast<unsigned int *
>(&val));
3803 float *image =
reinterpret_cast<float **
>(out_images)[c];
3804 if (line_order == 0) {
3805 image += (
static_cast<size_t>(line_no) + v) *
3806 static_cast<size_t>(x_stride) +
3809 image +=
static_cast<size_t>(
3810 (height - 1 - (line_no +
static_cast<int>(v)))) *
3811 static_cast<size_t>(x_stride) +
3825 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS ||
3826 compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
3828 std::vector<unsigned char> outBuf(
static_cast<size_t>(width) *
3829 static_cast<size_t>(num_lines) *
3832 unsigned long dstLen =
static_cast<unsigned long>(outBuf.size());
3833 TINYEXR_CHECK_AND_RETURN_C(dstLen > 0,
false);
3834 if (!tinyexr::DecompressZip(
3835 reinterpret_cast<unsigned char *
>(&outBuf.at(0)), &dstLen, data_ptr,
3836 static_cast<unsigned long>(data_len))) {
3850 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
3851 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
3852 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3853 const unsigned short *line_ptr =
reinterpret_cast<unsigned short *
>(
3854 &outBuf.at(v *
static_cast<size_t>(pixel_data_size) *
3855 static_cast<size_t>(width) +
3856 channel_offset_list[c] *
static_cast<size_t>(width)));
3857 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
3861 tinyexr::cpy2(&(hf.u), line_ptr + u);
3863 tinyexr::swap2(
reinterpret_cast<unsigned short *
>(&hf.u));
3865 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
3866 unsigned short *image =
3867 reinterpret_cast<unsigned short **
>(out_images)[c];
3868 if (line_order == 0) {
3869 image += (
static_cast<size_t>(line_no) + v) *
3870 static_cast<size_t>(x_stride) +
3873 image += (
static_cast<size_t>(height) - 1U -
3874 (
static_cast<size_t>(line_no) + v)) *
3875 static_cast<size_t>(x_stride) +
3880 tinyexr::FP32
f32 = half_to_float(hf);
3881 float *image =
reinterpret_cast<float **
>(out_images)[c];
3883 if (line_order == 0) {
3884 offset = (
static_cast<size_t>(line_no) + v) *
3885 static_cast<size_t>(x_stride) +
3888 offset = (
static_cast<size_t>(height) - 1U -
3889 (
static_cast<size_t>(line_no) + v)) *
3890 static_cast<size_t>(x_stride) +
3899 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
3900 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT,
false);
3902 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3903 const unsigned int *line_ptr =
reinterpret_cast<unsigned int *
>(
3904 &outBuf.at(v * pixel_data_size *
static_cast<size_t>(width) +
3905 channel_offset_list[c] *
static_cast<size_t>(width)));
3906 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
3909 tinyexr::cpy4(&val, line_ptr + u);
3911 tinyexr::swap4(&val);
3913 unsigned int *image =
3914 reinterpret_cast<unsigned int **
>(out_images)[c];
3915 if (line_order == 0) {
3916 image += (
static_cast<size_t>(line_no) + v) *
3917 static_cast<size_t>(x_stride) +
3920 image += (
static_cast<size_t>(height) - 1U -
3921 (
static_cast<size_t>(line_no) + v)) *
3922 static_cast<size_t>(x_stride) +
3928 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
3929 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT,
false);
3930 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3931 const float *line_ptr =
reinterpret_cast<float *
>(
3932 &outBuf.at(v * pixel_data_size *
static_cast<size_t>(width) +
3933 channel_offset_list[c] *
static_cast<size_t>(width)));
3934 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
3937 tinyexr::cpy4(&val, line_ptr + u);
3939 tinyexr::swap4(
reinterpret_cast<unsigned int *
>(&val));
3941 float *image =
reinterpret_cast<float **
>(out_images)[c];
3942 if (line_order == 0) {
3943 image += (
static_cast<size_t>(line_no) + v) *
3944 static_cast<size_t>(x_stride) +
3947 image += (
static_cast<size_t>(height) - 1U -
3948 (
static_cast<size_t>(line_no) + v)) *
3949 static_cast<size_t>(x_stride) +
3959 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) {
3961 std::vector<unsigned char> outBuf(
static_cast<size_t>(width) *
3962 static_cast<size_t>(num_lines) *
3965 unsigned long dstLen =
static_cast<unsigned long>(outBuf.size());
3970 if (!tinyexr::DecompressRle(
3971 reinterpret_cast<unsigned char *
>(&outBuf.at(0)), dstLen, data_ptr,
3972 static_cast<unsigned long>(data_len))) {
3986 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
3987 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
3988 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3989 const unsigned short *line_ptr =
reinterpret_cast<unsigned short *
>(
3990 &outBuf.at(v *
static_cast<size_t>(pixel_data_size) *
3991 static_cast<size_t>(width) +
3992 channel_offset_list[c] *
static_cast<size_t>(width)));
3993 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
3997 tinyexr::cpy2(&(hf.u), line_ptr + u);
3999 tinyexr::swap2(
reinterpret_cast<unsigned short *
>(&hf.u));
4001 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
4002 unsigned short *image =
4003 reinterpret_cast<unsigned short **
>(out_images)[c];
4004 if (line_order == 0) {
4005 image += (
static_cast<size_t>(line_no) + v) *
4006 static_cast<size_t>(x_stride) +
4009 image += (
static_cast<size_t>(height) - 1U -
4010 (
static_cast<size_t>(line_no) + v)) *
4011 static_cast<size_t>(x_stride) +
4016 tinyexr::FP32
f32 = half_to_float(hf);
4017 float *image =
reinterpret_cast<float **
>(out_images)[c];
4018 if (line_order == 0) {
4019 image += (
static_cast<size_t>(line_no) + v) *
4020 static_cast<size_t>(x_stride) +
4023 image += (
static_cast<size_t>(height) - 1U -
4024 (
static_cast<size_t>(line_no) + v)) *
4025 static_cast<size_t>(x_stride) +
4032 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
4033 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT,
false);
4035 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
4036 const unsigned int *line_ptr =
reinterpret_cast<unsigned int *
>(
4037 &outBuf.at(v * pixel_data_size *
static_cast<size_t>(width) +
4038 channel_offset_list[c] *
static_cast<size_t>(width)));
4039 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
4042 tinyexr::cpy4(&val, line_ptr + u);
4044 tinyexr::swap4(&val);
4046 unsigned int *image =
4047 reinterpret_cast<unsigned int **
>(out_images)[c];
4048 if (line_order == 0) {
4049 image += (
static_cast<size_t>(line_no) + v) *
4050 static_cast<size_t>(x_stride) +
4053 image += (
static_cast<size_t>(height) - 1U -
4054 (
static_cast<size_t>(line_no) + v)) *
4055 static_cast<size_t>(x_stride) +
4061 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4062 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT,
false);
4063 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
4064 const float *line_ptr =
reinterpret_cast<float *
>(
4065 &outBuf.at(v * pixel_data_size *
static_cast<size_t>(width) +
4066 channel_offset_list[c] *
static_cast<size_t>(width)));
4067 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
4070 tinyexr::cpy4(&val, line_ptr + u);
4072 tinyexr::swap4(
reinterpret_cast<unsigned int *
>(&val));
4074 float *image =
reinterpret_cast<float **
>(out_images)[c];
4075 if (line_order == 0) {
4076 image += (
static_cast<size_t>(line_no) + v) *
4077 static_cast<size_t>(x_stride) +
4080 image += (
static_cast<size_t>(height) - 1U -
4081 (
static_cast<size_t>(line_no) + v)) *
4082 static_cast<size_t>(x_stride) +
4092 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
4094 tinyexr::ZFPCompressionParam zfp_compression_param;
4096 if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes,
4097 int(num_attributes), &e)) {
4103 std::vector<unsigned char> outBuf(
static_cast<size_t>(width) *
4104 static_cast<size_t>(num_lines) *
4107 unsigned long dstLen = outBuf.size();
4108 TINYEXR_CHECK_AND_RETURN_C(dstLen > 0,
false);
4109 tinyexr::DecompressZfp(
reinterpret_cast<float *
>(&outBuf.at(0)), width,
4110 num_lines, num_channels, data_ptr,
4111 static_cast<unsigned long>(data_len),
4112 zfp_compression_param);
4124 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4125 TINYEXR_CHECK_AND_RETURN_C(channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT,
false);
4126 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4127 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT,
false);
4128 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
4129 const float *line_ptr =
reinterpret_cast<float *
>(
4130 &outBuf.at(v * pixel_data_size *
static_cast<size_t>(width) +
4131 channel_offset_list[c] *
static_cast<size_t>(width)));
4132 for (
size_t u = 0; u < static_cast<size_t>(width); u++) {
4134 tinyexr::cpy4(&val, line_ptr + u);
4136 tinyexr::swap4(
reinterpret_cast<unsigned int *
>(&val));
4138 float *image =
reinterpret_cast<float **
>(out_images)[c];
4139 if (line_order == 0) {
4140 image += (
static_cast<size_t>(line_no) + v) *
4141 static_cast<size_t>(x_stride) +
4144 image += (
static_cast<size_t>(height) - 1U -
4145 (
static_cast<size_t>(line_no) + v)) *
4146 static_cast<size_t>(x_stride) +
4158 (void)num_attributes;
4162 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
4163 for (
size_t c = 0; c < num_channels; c++) {
4164 for (
size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
4165 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
4166 const unsigned short *line_ptr =
4167 reinterpret_cast<const unsigned short *
>(
4168 data_ptr + v * pixel_data_size * size_t(width) +
4169 channel_offset_list[c] *
static_cast<size_t>(width));
4171 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
4172 unsigned short *outLine =
4173 reinterpret_cast<unsigned short *
>(out_images[c]);
4174 if (line_order == 0) {
4175 outLine += (size_t(y) + v) *
size_t(x_stride);
4178 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4181 for (
int u = 0; u < width; u++) {
4185 tinyexr::cpy2(&(hf.u), line_ptr + u);
4187 tinyexr::swap2(
reinterpret_cast<unsigned short *
>(&hf.u));
4191 }
else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
4192 float *outLine =
reinterpret_cast<float *
>(out_images[c]);
4193 if (line_order == 0) {
4194 outLine += (size_t(y) + v) *
size_t(x_stride);
4197 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4200 if (
reinterpret_cast<const unsigned char *
>(line_ptr + width) >
4201 (data_ptr + data_len)) {
4206 for (
int u = 0; u < width; u++) {
4211 tinyexr::cpy2(&(hf.u), line_ptr + u);
4213 tinyexr::swap2(
reinterpret_cast<unsigned short *
>(&hf.u));
4215 tinyexr::FP32
f32 = half_to_float(hf);
4222 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4223 const float *line_ptr =
reinterpret_cast<const float *
>(
4224 data_ptr + v * pixel_data_size * size_t(width) +
4225 channel_offset_list[c] *
static_cast<size_t>(width));
4227 float *outLine =
reinterpret_cast<float *
>(out_images[c]);
4228 if (line_order == 0) {
4229 outLine += (size_t(y) + v) *
size_t(x_stride);
4232 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4235 if (
reinterpret_cast<const unsigned char *
>(line_ptr + width) >
4236 (data_ptr + data_len)) {
4241 for (
int u = 0; u < width; u++) {
4243 tinyexr::cpy4(&val, line_ptr + u);
4245 tinyexr::swap4(
reinterpret_cast<unsigned int *
>(&val));
4249 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
4250 const unsigned int *line_ptr =
reinterpret_cast<const unsigned int *
>(
4251 data_ptr + v * pixel_data_size * size_t(width) +
4252 channel_offset_list[c] *
static_cast<size_t>(width));
4254 unsigned int *outLine =
4255 reinterpret_cast<unsigned int *
>(out_images[c]);
4256 if (line_order == 0) {
4257 outLine += (size_t(y) + v) *
size_t(x_stride);
4260 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4263 if (
reinterpret_cast<const unsigned char *
>(line_ptr + width) >
4264 (data_ptr + data_len)) {
4269 for (
int u = 0; u < width; u++) {
4272 tinyexr::cpy4(&val, line_ptr + u);
4274 tinyexr::swap4(
reinterpret_cast<unsigned int *
>(&val));
4286static bool DecodeTiledPixelData(
4287 unsigned char **out_images,
int *width,
int *height,
4288 const int *requested_pixel_types,
const unsigned char *data_ptr,
4289 size_t data_len,
int compression_type,
int line_order,
int data_width,
4290 int data_height,
int tile_offset_x,
int tile_offset_y,
int tile_size_x,
4291 int tile_size_y,
size_t pixel_data_size,
size_t num_attributes,
4294 const std::vector<size_t> &channel_offset_list) {
4296 if (tile_size_x * tile_offset_x > data_width ||
4297 tile_size_y * tile_offset_y > data_height) {
4302 if ((tile_offset_x + 1) * tile_size_x >= data_width) {
4303 (*width) = data_width - (tile_offset_x * tile_size_x);
4305 (*width) = tile_size_x;
4308 if ((tile_offset_y + 1) * tile_size_y >= data_height) {
4309 (*height) = data_height - (tile_offset_y * tile_size_y);
4311 (*height) = tile_size_y;
4315 return DecodePixelData(out_images, requested_pixel_types, data_ptr, data_len,
4316 compression_type, line_order, (*width), tile_size_y,
4318 (*height), pixel_data_size, num_attributes, attributes,
4319 num_channels, channels, channel_offset_list);
4322static bool ComputeChannelLayout(std::vector<size_t> *channel_offset_list,
4323 int *pixel_data_size,
size_t *channel_offset,
4326 channel_offset_list->resize(
static_cast<size_t>(num_channels));
4328 (*pixel_data_size) = 0;
4329 (*channel_offset) = 0;
4331 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4332 (*channel_offset_list)[c] = (*channel_offset);
4333 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
4334 (*pixel_data_size) +=
sizeof(
unsigned short);
4335 (*channel_offset) +=
sizeof(
unsigned short);
4336 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4337 (*pixel_data_size) +=
sizeof(float);
4338 (*channel_offset) +=
sizeof(float);
4339 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
4340 (*pixel_data_size) +=
sizeof(
unsigned int);
4341 (*channel_offset) +=
sizeof(
unsigned int);
4351static unsigned char **AllocateImage(
int num_channels,
4353 const int *requested_pixel_types,
4354 int data_width,
int data_height,
bool *success) {
4355 unsigned char **images =
4356 reinterpret_cast<unsigned char **
>(
static_cast<float **
>(
4357 malloc(
sizeof(
float *) *
static_cast<size_t>(num_channels))));
4359 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4365 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4367 static_cast<size_t>(data_width) *
static_cast<size_t>(data_height);
4368 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
4372 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
4374 reinterpret_cast<unsigned char *
>(
static_cast<unsigned short *
>(
4375 malloc(
sizeof(
unsigned short) * data_len)));
4376 }
else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
4377 images[c] =
reinterpret_cast<unsigned char *
>(
4378 static_cast<float *
>(malloc(
sizeof(
float) * data_len)));
4384 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4387 images[c] =
reinterpret_cast<unsigned char *
>(
4388 static_cast<float *
>(malloc(
sizeof(
float) * data_len)));
4389 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
4392 images[c] =
reinterpret_cast<unsigned char *
>(
4393 static_cast<unsigned int *
>(malloc(
sizeof(
unsigned int) * data_len)));
4402 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4422static inline std::wstring UTF8ToWchar(
const std::string &str) {
4424 MultiByteToWideChar(CP_UTF8, 0, str.data(), (
int)str.size(), NULL, 0);
4425 std::wstring wstr(wstr_size, 0);
4426 MultiByteToWideChar(CP_UTF8, 0, str.data(), (
int)str.size(), &wstr[0],
4433static int ParseEXRHeader(HeaderInfo *info,
bool *empty_header,
4435 const unsigned char *buf,
size_t size) {
4436 const char *marker =
reinterpret_cast<const char *
>(&buf[0]);
4439 (*empty_header) =
false;
4442 if (version->multipart) {
4443 if (size > 0 && marker[0] ==
'\0') {
4446 (*empty_header) =
true;
4448 return TINYEXR_SUCCESS;
4463 bool has_channels =
false;
4464 bool has_compression =
false;
4465 bool has_data_window =
false;
4466 bool has_display_window =
false;
4467 bool has_line_order =
false;
4468 bool has_pixel_aspect_ratio =
false;
4469 bool has_screen_window_center =
false;
4470 bool has_screen_window_width =
false;
4471 bool has_name =
false;
4472 bool has_type =
false;
4477 info->data_window.min_x = 0;
4478 info->data_window.min_y = 0;
4479 info->data_window.max_x = 0;
4480 info->data_window.max_y = 0;
4481 info->line_order = 0;
4482 info->display_window.min_x = 0;
4483 info->display_window.min_y = 0;
4484 info->display_window.max_x = 0;
4485 info->display_window.max_y = 0;
4486 info->screen_window_center[0] = 0.0f;
4487 info->screen_window_center[1] = 0.0f;
4488 info->screen_window_width = -1.0f;
4489 info->pixel_aspect_ratio = -1.0f;
4492 info->tile_size_x = -1;
4493 info->tile_size_y = -1;
4494 info->tile_level_mode = -1;
4495 info->tile_rounding_mode = -1;
4497 info->attributes.clear();
4500 size_t orig_size = size;
4501 for (
size_t nattr = 0; nattr < TINYEXR_MAX_HEADER_ATTRIBUTES; nattr++) {
4504 (*err) +=
"Insufficient data size for attributes.\n";
4506 return TINYEXR_ERROR_INVALID_DATA;
4507 }
else if (marker[0] ==
'\0') {
4512 std::string attr_name;
4513 std::string attr_type;
4514 std::vector<unsigned char> data;
4516 if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size,
4519 (*err) +=
"Failed to read attribute.\n";
4521 return TINYEXR_ERROR_INVALID_DATA;
4523 marker += marker_size;
4524 size -= marker_size;
4527 if ((version->tiled || version->multipart || version->non_image) && attr_name.compare(
"tiles") == 0) {
4528 unsigned int x_size, y_size;
4529 unsigned char tile_mode;
4530 if (data.size() != 9) {
4532 (*err) +=
"(ParseEXRHeader) Invalid attribute data size. Attribute data size must be 9.\n";
4534 return TINYEXR_ERROR_INVALID_DATA;
4537 memcpy(&x_size, &data.at(0),
sizeof(
int));
4538 memcpy(&y_size, &data.at(4),
sizeof(
int));
4539 tile_mode = data[8];
4540 tinyexr::swap4(&x_size);
4541 tinyexr::swap4(&y_size);
4543 if (x_size >
static_cast<unsigned int>(std::numeric_limits<int>::max()) ||
4544 y_size >
static_cast<unsigned int>(std::numeric_limits<int>::max())) {
4546 (*err) =
"Tile sizes were invalid.";
4548 return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
4551 info->tile_size_x =
static_cast<int>(x_size);
4552 info->tile_size_y =
static_cast<int>(y_size);
4555 info->tile_level_mode = tile_mode & 0x3;
4556 info->tile_rounding_mode = (tile_mode >> 4) & 0x1;
4558 }
else if (attr_name.compare(
"compression") == 0) {
4560 if (data[0] < TINYEXR_COMPRESSIONTYPE_PIZ) {
4564 if (data[0] == TINYEXR_COMPRESSIONTYPE_PIZ) {
4569 (*err) =
"PIZ compression is not supported.";
4571 return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
4575 if (data[0] == TINYEXR_COMPRESSIONTYPE_ZFP) {
4580 (*err) =
"ZFP compression is not supported.";
4582 return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
4588 (*err) =
"Unknown compression type.";
4590 return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
4593 info->compression_type =
static_cast<int>(data[0]);
4594 has_compression =
true;
4596 }
else if (attr_name.compare(
"channels") == 0) {
4604 if (!ReadChannelInfo(info->channels, data)) {
4606 (*err) +=
"Failed to parse channel info.\n";
4608 return TINYEXR_ERROR_INVALID_DATA;
4611 if (info->channels.size() < 1) {
4613 (*err) +=
"# of channels is zero.\n";
4615 return TINYEXR_ERROR_INVALID_DATA;
4618 has_channels =
true;
4620 }
else if (attr_name.compare(
"dataWindow") == 0) {
4621 if (data.size() >= 16) {
4622 memcpy(&info->data_window.min_x, &data.at(0),
sizeof(
int));
4623 memcpy(&info->data_window.min_y, &data.at(4),
sizeof(
int));
4624 memcpy(&info->data_window.max_x, &data.at(8),
sizeof(
int));
4625 memcpy(&info->data_window.max_y, &data.at(12),
sizeof(
int));
4626 tinyexr::swap4(&info->data_window.min_x);
4627 tinyexr::swap4(&info->data_window.min_y);
4628 tinyexr::swap4(&info->data_window.max_x);
4629 tinyexr::swap4(&info->data_window.max_y);
4630 has_data_window =
true;
4632 }
else if (attr_name.compare(
"displayWindow") == 0) {
4633 if (data.size() >= 16) {
4634 memcpy(&info->display_window.min_x, &data.at(0),
sizeof(
int));
4635 memcpy(&info->display_window.min_y, &data.at(4),
sizeof(
int));
4636 memcpy(&info->display_window.max_x, &data.at(8),
sizeof(
int));
4637 memcpy(&info->display_window.max_y, &data.at(12),
sizeof(
int));
4638 tinyexr::swap4(&info->display_window.min_x);
4639 tinyexr::swap4(&info->display_window.min_y);
4640 tinyexr::swap4(&info->display_window.max_x);
4641 tinyexr::swap4(&info->display_window.max_y);
4643 has_display_window =
true;
4645 }
else if (attr_name.compare(
"lineOrder") == 0) {
4646 if (data.size() >= 1) {
4647 info->line_order =
static_cast<int>(data[0]);
4648 has_line_order =
true;
4650 }
else if (attr_name.compare(
"pixelAspectRatio") == 0) {
4651 if (data.size() >=
sizeof(float)) {
4652 memcpy(&info->pixel_aspect_ratio, &data.at(0),
sizeof(
float));
4653 tinyexr::swap4(&info->pixel_aspect_ratio);
4654 has_pixel_aspect_ratio =
true;
4656 }
else if (attr_name.compare(
"screenWindowCenter") == 0) {
4657 if (data.size() >= 8) {
4658 memcpy(&info->screen_window_center[0], &data.at(0),
sizeof(
float));
4659 memcpy(&info->screen_window_center[1], &data.at(4),
sizeof(
float));
4660 tinyexr::swap4(&info->screen_window_center[0]);
4661 tinyexr::swap4(&info->screen_window_center[1]);
4662 has_screen_window_center =
true;
4664 }
else if (attr_name.compare(
"screenWindowWidth") == 0) {
4665 if (data.size() >=
sizeof(
float)) {
4666 memcpy(&info->screen_window_width, &data.at(0),
sizeof(
float));
4667 tinyexr::swap4(&info->screen_window_width);
4669 has_screen_window_width =
true;
4671 }
else if (attr_name.compare(
"chunkCount") == 0) {
4672 if (data.size() >=
sizeof(
int)) {
4673 memcpy(&info->chunk_count, &data.at(0),
sizeof(
int));
4674 tinyexr::swap4(&info->chunk_count);
4676 }
else if (attr_name.compare(
"name") == 0) {
4677 if (!data.empty() && data[0]) {
4679 size_t len = strlen(
reinterpret_cast<const char*
>(&data[0]));
4680 info->name.resize(len);
4681 info->name.assign(
reinterpret_cast<const char*
>(&data[0]), len);
4684 }
else if (attr_name.compare(
"type") == 0) {
4685 if (!data.empty() && data[0]) {
4687 size_t len = strlen(
reinterpret_cast<const char*
>(&data[0]));
4688 info->type.resize(len);
4689 info->type.assign(
reinterpret_cast<const char*
>(&data[0]), len);
4694 if (info->attributes.size() < TINYEXR_MAX_CUSTOM_ATTRIBUTES) {
4697 strncpy_s(attrib.name, attr_name.c_str(), 255);
4698 strncpy_s(attrib.type, attr_type.c_str(), 255);
4700 strncpy(attrib.name, attr_name.c_str(), 255);
4701 strncpy(attrib.type, attr_type.c_str(), 255);
4703 attrib.name[255] =
'\0';
4704 attrib.type[255] =
'\0';
4706 attrib.size =
static_cast<int>(data.size());
4707 attrib.value =
static_cast<unsigned char *
>(malloc(data.size()));
4708 memcpy(
reinterpret_cast<char *
>(attrib.value), &data.at(0),
4710 info->attributes.push_back(attrib);
4717 std::stringstream ss_err;
4719 if (!has_compression) {
4720 ss_err <<
"\"compression\" attribute not found in the header."
4724 if (!has_channels) {
4725 ss_err <<
"\"channels\" attribute not found in the header." << std::endl;
4728 if (!has_line_order) {
4729 ss_err <<
"\"lineOrder\" attribute not found in the header." << std::endl;
4732 if (!has_display_window) {
4733 ss_err <<
"\"displayWindow\" attribute not found in the header."
4737 if (!has_data_window) {
4738 ss_err <<
"\"dataWindow\" attribute not found in the header or invalid."
4742 if (!has_pixel_aspect_ratio) {
4743 ss_err <<
"\"pixelAspectRatio\" attribute not found in the header."
4747 if (!has_screen_window_width) {
4748 ss_err <<
"\"screenWindowWidth\" attribute not found in the header."
4752 if (!has_screen_window_center) {
4753 ss_err <<
"\"screenWindowCenter\" attribute not found in the header."
4757 if (version->multipart || version->non_image) {
4759 ss_err <<
"\"name\" attribute not found in the header."
4763 ss_err <<
"\"type\" attribute not found in the header."
4768 if (!(ss_err.str().empty())) {
4770 (*err) += ss_err.str();
4773 return TINYEXR_ERROR_INVALID_HEADER;
4777 info->header_len =
static_cast<unsigned int>(orig_size - size);
4779 return TINYEXR_SUCCESS;
4783static bool ConvertHeader(
EXRHeader *exr_header,
const HeaderInfo &info, std::string *warn, std::string *err) {
4784 exr_header->pixel_aspect_ratio = info.pixel_aspect_ratio;
4785 exr_header->screen_window_center[0] = info.screen_window_center[0];
4786 exr_header->screen_window_center[1] = info.screen_window_center[1];
4787 exr_header->screen_window_width = info.screen_window_width;
4788 exr_header->chunk_count = info.chunk_count;
4789 exr_header->display_window.min_x = info.display_window.min_x;
4790 exr_header->display_window.min_y = info.display_window.min_y;
4791 exr_header->display_window.max_x = info.display_window.max_x;
4792 exr_header->display_window.max_y = info.display_window.max_y;
4793 exr_header->data_window.min_x = info.data_window.min_x;
4794 exr_header->data_window.min_y = info.data_window.min_y;
4795 exr_header->data_window.max_x = info.data_window.max_x;
4796 exr_header->data_window.max_y = info.data_window.max_y;
4797 exr_header->line_order = info.line_order;
4798 exr_header->compression_type = info.compression_type;
4799 exr_header->tiled = info.tiled;
4800 exr_header->tile_size_x = info.tile_size_x;
4801 exr_header->tile_size_y = info.tile_size_y;
4802 exr_header->tile_level_mode = info.tile_level_mode;
4803 exr_header->tile_rounding_mode = info.tile_rounding_mode;
4805 EXRSetNameAttr(exr_header, info.name.c_str());
4808 if (!info.type.empty()) {
4810 if (info.type ==
"scanlineimage") {
4811 if (exr_header->tiled) {
4813 (*err) +=
"(ConvertHeader) tiled bit must be off for `scanlineimage` type.\n";
4817 }
else if (info.type ==
"tiledimage") {
4818 if (!exr_header->tiled) {
4820 (*err) +=
"(ConvertHeader) tiled bit must be on for `tiledimage` type.\n";
4824 }
else if (info.type ==
"deeptile") {
4825 exr_header->non_image = 1;
4826 if (!exr_header->tiled) {
4828 (*err) +=
"(ConvertHeader) tiled bit must be on for `deeptile` type.\n";
4832 }
else if (info.type ==
"deepscanline") {
4833 exr_header->non_image = 1;
4834 if (exr_header->tiled) {
4836 (*err) +=
"(ConvertHeader) tiled bit must be off for `deepscanline` type.\n";
4842 std::stringstream ss;
4843 ss <<
"(ConvertHeader) Unsupported or unknown info.type: " << info.type <<
"\n";
4844 (*warn) += ss.str();
4853 exr_header->num_channels =
static_cast<int>(info.channels.size());
4856 sizeof(
EXRChannelInfo) *
static_cast<size_t>(exr_header->num_channels)));
4857 for (
size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
4859 strncpy_s(exr_header->channels[c].name, info.channels[c].name.c_str(), 255);
4861 strncpy(exr_header->channels[c].name, info.channels[c].name.c_str(), 255);
4864 exr_header->channels[c].name[255] =
'\0';
4866 exr_header->channels[c].pixel_type = info.channels[c].pixel_type;
4867 exr_header->channels[c].p_linear = info.channels[c].p_linear;
4868 exr_header->channels[c].x_sampling = info.channels[c].x_sampling;
4869 exr_header->channels[c].y_sampling = info.channels[c].y_sampling;
4872 exr_header->pixel_types =
static_cast<int *
>(
4873 malloc(
sizeof(
int) *
static_cast<size_t>(exr_header->num_channels)));
4874 for (
size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
4875 exr_header->pixel_types[c] = info.channels[c].pixel_type;
4879 exr_header->requested_pixel_types =
static_cast<int *
>(
4880 malloc(
sizeof(
int) *
static_cast<size_t>(exr_header->num_channels)));
4881 for (
size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
4882 exr_header->requested_pixel_types[c] = info.channels[c].pixel_type;
4885 exr_header->num_custom_attributes =
static_cast<int>(info.attributes.size());
4887 if (exr_header->num_custom_attributes > 0) {
4890 if (exr_header->num_custom_attributes > TINYEXR_MAX_CUSTOM_ATTRIBUTES) {
4891 exr_header->num_custom_attributes = TINYEXR_MAX_CUSTOM_ATTRIBUTES;
4894 exr_header->custom_attributes =
static_cast<EXRAttribute *
>(malloc(
4895 sizeof(
EXRAttribute) *
size_t(exr_header->num_custom_attributes)));
4897 for (
size_t i = 0; i < size_t(exr_header->num_custom_attributes); i++) {
4898 memcpy(exr_header->custom_attributes[i].name, info.attributes[i].name,
4900 memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type,
4902 exr_header->custom_attributes[i].size = info.attributes[i].size;
4904 exr_header->custom_attributes[i].value = info.attributes[i].value;
4908 exr_header->custom_attributes = NULL;
4911 exr_header->header_len = info.header_len;
4917 OffsetData() : num_x_levels(0), num_y_levels(0) {}
4918 std::vector<std::vector<std::vector <tinyexr::tinyexr_uint64> > > offsets;
4924static int LevelIndex(
int lx,
int ly,
int tile_level_mode,
int num_x_levels) {
4925 switch (tile_level_mode) {
4926 case TINYEXR_TILE_ONE_LEVEL:
4929 case TINYEXR_TILE_MIPMAP_LEVELS:
4932 case TINYEXR_TILE_RIPMAP_LEVELS:
4933 return lx + ly * num_x_levels;
4941static int LevelSize(
int toplevel_size,
int level,
int tile_rounding_mode) {
4946 int b =
static_cast<int>(1u <<
static_cast<unsigned int>(level));
4947 int level_size = toplevel_size / b;
4949 if (tile_rounding_mode == TINYEXR_TILE_ROUND_UP && level_size * b < toplevel_size)
4952 return std::max(level_size, 1);
4956 const OffsetData& offset_data,
4957 const std::vector<size_t>& channel_offset_list,
4958 int pixel_data_size,
4959 const unsigned char* head,
const size_t size,
4961 int num_channels = exr_header->num_channels;
4963 int level_index = LevelIndex(exr_image->level_x, exr_image->level_y, exr_header->tile_level_mode, offset_data.num_x_levels);
4964 int num_y_tiles = int(offset_data.offsets[
size_t(level_index)].size());
4965 if (num_y_tiles < 1) {
4966 return TINYEXR_ERROR_INVALID_DATA;
4968 int num_x_tiles = int(offset_data.offsets[
size_t(level_index)][0].size());
4969 if (num_x_tiles < 1) {
4970 return TINYEXR_ERROR_INVALID_DATA;
4972 int num_tiles = num_x_tiles * num_y_tiles;
4974 int err_code = TINYEXR_SUCCESS;
4978 EF_INVALID_DATA = 1,
4979 EF_INSUFFICIENT_DATA = 2,
4980 EF_FAILED_TO_DECODE = 4
4982#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
4983 std::atomic<unsigned> error_flag(EF_SUCCESS);
4985 unsigned error_flag(EF_SUCCESS);
4991 if ((exr_header->tile_size_x > exr_image->width || exr_header->tile_size_y > exr_image->height) &&
4992 exr_image->level_x == 0 && exr_image->level_y == 0) {
4994 (*err) +=
"Failed to decode tile data.\n";
4996 err_code = TINYEXR_ERROR_INVALID_DATA;
4999 exr_image->tiles =
static_cast<EXRTile*
>(
5000 calloc(
static_cast<size_t>(num_tiles),
sizeof(
EXRTile)));
5002#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5003 std::vector<std::thread> workers;
5004 std::atomic<int> tile_count(0);
5006 int num_threads = std::max(1,
int(std::thread::hardware_concurrency()));
5007#if (TINYEXR_MAX_THREADS > 0)
5008 num_threads = std::min(num_threads,TINYEXR_MAX_THREADS);
5010 if (num_threads >
int(num_tiles)) {
5011 num_threads = int(num_tiles);
5013 for (
int t = 0; t < num_threads; t++) {
5014 workers.emplace_back(std::thread([&]()
5017 while ((tile_idx = tile_count++) < num_tiles) {
5020#if TINYEXR_USE_OPENMP
5021#pragma omp parallel for
5023 for (
int tile_idx = 0; tile_idx < num_tiles; tile_idx++) {
5026 bool alloc_success =
false;
5027 exr_image->tiles[tile_idx].images = tinyexr::AllocateImage(
5028 num_channels, exr_header->channels,
5029 exr_header->requested_pixel_types, exr_header->tile_size_x,
5030 exr_header->tile_size_y, &alloc_success);
5032 if (!alloc_success) {
5033 error_flag |= EF_INVALID_DATA;
5037 int x_tile = tile_idx % num_x_tiles;
5038 int y_tile = tile_idx / num_x_tiles;
5042 tinyexr::tinyexr_uint64 offset = offset_data.offsets[size_t(level_index)][size_t(y_tile)][size_t(x_tile)];
5043 if (offset +
sizeof(
int) * 5 > size) {
5045 error_flag |= EF_INSUFFICIENT_DATA;
5050 size_t(size - (offset +
sizeof(
int) * 5));
5051 const unsigned char* data_ptr =
5052 reinterpret_cast<const unsigned char*
>(head + offset);
5054 int tile_coordinates[4];
5055 memcpy(tile_coordinates, data_ptr,
sizeof(
int) * 4);
5056 tinyexr::swap4(&tile_coordinates[0]);
5057 tinyexr::swap4(&tile_coordinates[1]);
5058 tinyexr::swap4(&tile_coordinates[2]);
5059 tinyexr::swap4(&tile_coordinates[3]);
5061 if (tile_coordinates[2] != exr_image->level_x) {
5063 error_flag |= EF_INVALID_DATA;
5066 if (tile_coordinates[3] != exr_image->level_y) {
5068 error_flag |= EF_INVALID_DATA;
5073 memcpy(&data_len, data_ptr + 16,
5075 tinyexr::swap4(&data_len);
5077 if (data_len < 2 ||
size_t(data_len) > data_size) {
5079 error_flag |= EF_INSUFFICIENT_DATA;
5085 bool ret = tinyexr::DecodeTiledPixelData(
5086 exr_image->tiles[tile_idx].images,
5087 &(exr_image->tiles[tile_idx].width),
5088 &(exr_image->tiles[tile_idx].height),
5089 exr_header->requested_pixel_types, data_ptr,
5090 static_cast<size_t>(data_len), exr_header->compression_type,
5091 exr_header->line_order,
5092 exr_image->width, exr_image->height,
5093 tile_coordinates[0], tile_coordinates[1], exr_header->tile_size_x,
5094 exr_header->tile_size_y,
static_cast<size_t>(pixel_data_size),
5095 static_cast<size_t>(exr_header->num_custom_attributes),
5096 exr_header->custom_attributes,
5097 static_cast<size_t>(exr_header->num_channels),
5098 exr_header->channels, channel_offset_list);
5102 error_flag |= EF_FAILED_TO_DECODE;
5105 exr_image->tiles[tile_idx].offset_x = tile_coordinates[0];
5106 exr_image->tiles[tile_idx].offset_y = tile_coordinates[1];
5107 exr_image->tiles[tile_idx].level_x = tile_coordinates[2];
5108 exr_image->tiles[tile_idx].level_y = tile_coordinates[3];
5110#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5115 for (
auto& t : workers) {
5124 exr_image->num_channels = num_channels;
5125 exr_image->num_tiles =
static_cast<int>(num_tiles);
5127 if (error_flag) err_code = TINYEXR_ERROR_INVALID_DATA;
5129 if (error_flag & EF_INSUFFICIENT_DATA) {
5130 (*err) +=
"Insufficient data length.\n";
5132 if (error_flag & EF_FAILED_TO_DECODE) {
5133 (*err) +=
"Failed to decode tile data.\n";
5140 const OffsetData& offset_data,
5141 const unsigned char *head,
const size_t size,
5143 int num_channels = exr_header->num_channels;
5145 int num_scanline_blocks = 1;
5146 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
5147 num_scanline_blocks = 16;
5148 }
else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
5149 num_scanline_blocks = 32;
5150 }
else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
5151 num_scanline_blocks = 16;
5154 tinyexr::ZFPCompressionParam zfp_compression_param;
5155 if (!FindZFPCompressionParam(&zfp_compression_param,
5156 exr_header->custom_attributes,
5157 int(exr_header->num_custom_attributes), err)) {
5158 return TINYEXR_ERROR_INVALID_HEADER;
5163 if (exr_header->data_window.max_x < exr_header->data_window.min_x ||
5164 exr_header->data_window.max_y < exr_header->data_window.min_y) {
5166 (*err) +=
"Invalid data window.\n";
5168 return TINYEXR_ERROR_INVALID_DATA;
5171 tinyexr_int64 data_width =
5172 static_cast<tinyexr_int64
>(exr_header->data_window.max_x) -
static_cast<tinyexr_int64
>(exr_header->data_window.min_x) +
static_cast<tinyexr_int64
>(1);
5173 tinyexr_int64 data_height =
5174 static_cast<tinyexr_int64
>(exr_header->data_window.max_y) -
static_cast<tinyexr_int64
>(exr_header->data_window.min_y) +
static_cast<tinyexr_int64
>(1);
5176 if (data_width <= 0) {
5178 (*err) +=
"Invalid data window width.\n";
5180 return TINYEXR_ERROR_INVALID_DATA;
5183 if (data_height <= 0) {
5185 (*err) +=
"Invalid data window height.\n";
5187 return TINYEXR_ERROR_INVALID_DATA;
5192 if ((data_width > TINYEXR_DIMENSION_THRESHOLD) || (data_height > TINYEXR_DIMENSION_THRESHOLD)) {
5194 std::stringstream ss;
5195 ss <<
"data_with or data_height too large. data_width: " << data_width
5197 <<
"data_height = " << data_height << std::endl;
5200 return TINYEXR_ERROR_INVALID_DATA;
5202 if (exr_header->tiled) {
5203 if ((exr_header->tile_size_x > TINYEXR_DIMENSION_THRESHOLD) || (exr_header->tile_size_y > TINYEXR_DIMENSION_THRESHOLD)) {
5205 std::stringstream ss;
5206 ss <<
"tile with or tile height too large. tile width: " << exr_header->tile_size_x
5208 <<
"tile height = " << exr_header->tile_size_y << std::endl;
5211 return TINYEXR_ERROR_INVALID_DATA;
5216 const std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0];
5217 size_t num_blocks = offsets.size();
5219 std::vector<size_t> channel_offset_list;
5220 int pixel_data_size = 0;
5221 size_t channel_offset = 0;
5222 if (!tinyexr::ComputeChannelLayout(&channel_offset_list, &pixel_data_size,
5223 &channel_offset, num_channels,
5224 exr_header->channels)) {
5226 (*err) +=
"Failed to compute channel layout.\n";
5228 return TINYEXR_ERROR_INVALID_DATA;
5231#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5232 std::atomic<bool> invalid_data(
false);
5234 bool invalid_data(
false);
5237 if (exr_header->tiled) {
5239 if (exr_header->tile_size_x < 0) {
5241 std::stringstream ss;
5242 ss <<
"Invalid tile size x : " << exr_header->tile_size_x <<
"\n";
5245 return TINYEXR_ERROR_INVALID_HEADER;
5248 if (exr_header->tile_size_y < 0) {
5250 std::stringstream ss;
5251 ss <<
"Invalid tile size y : " << exr_header->tile_size_y <<
"\n";
5254 return TINYEXR_ERROR_INVALID_HEADER;
5256 if (exr_header->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) {
5258 for (
int level = 0; level < offset_data.num_x_levels; ++level) {
5260 level_image = exr_image;
5262 level_image->next_level =
new EXRImage;
5263 InitEXRImage(level_image->next_level);
5264 level_image = level_image->next_level;
5266 level_image->width =
5267 LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level, exr_header->tile_rounding_mode);
5268 if (level_image->width < 1) {
5269 return TINYEXR_ERROR_INVALID_DATA;
5272 level_image->height =
5273 LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level, exr_header->tile_rounding_mode);
5275 if (level_image->height < 1) {
5276 return TINYEXR_ERROR_INVALID_DATA;
5279 level_image->level_x = level;
5280 level_image->level_y = level;
5282 int ret = DecodeTiledLevel(level_image, exr_header,
5284 channel_offset_list,
5288 if (ret != TINYEXR_SUCCESS)
return ret;
5292 for (
int level_y = 0; level_y < offset_data.num_y_levels; ++level_y)
5293 for (
int level_x = 0; level_x < offset_data.num_x_levels; ++level_x) {
5295 level_image = exr_image;
5297 level_image->next_level =
new EXRImage;
5298 InitEXRImage(level_image->next_level);
5299 level_image = level_image->next_level;
5302 level_image->width =
5303 LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level_x, exr_header->tile_rounding_mode);
5304 if (level_image->width < 1) {
5305 return TINYEXR_ERROR_INVALID_DATA;
5308 level_image->height =
5309 LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level_y, exr_header->tile_rounding_mode);
5310 if (level_image->height < 1) {
5311 return TINYEXR_ERROR_INVALID_DATA;
5314 level_image->level_x = level_x;
5315 level_image->level_y = level_y;
5317 int ret = DecodeTiledLevel(level_image, exr_header,
5319 channel_offset_list,
5323 if (ret != TINYEXR_SUCCESS)
return ret;
5329 size_t total_data_len =
5330 size_t(data_width) * size_t(data_height) * size_t(num_channels);
5331 const bool total_data_len_overflown =
5332 sizeof(
void *) == 8 ? (total_data_len >= 0x4000000000) :
false;
5333 if ((total_data_len == 0) || total_data_len_overflown) {
5335 std::stringstream ss;
5336 ss <<
"Image data size is zero or too large: width = " << data_width
5337 <<
", height = " << data_height <<
", channels = " << num_channels
5341 return TINYEXR_ERROR_INVALID_DATA;
5344 bool alloc_success =
false;
5345 exr_image->images = tinyexr::AllocateImage(
5346 num_channels, exr_header->channels, exr_header->requested_pixel_types,
5347 int(data_width),
int(data_height), &alloc_success);
5349 if (!alloc_success) {
5351 std::stringstream ss;
5352 ss <<
"Failed to allocate memory for Images. Maybe EXR header is corrupted or Image data size is too large: width = " << data_width
5353 <<
", height = " << data_height <<
", channels = " << num_channels
5357 return TINYEXR_ERROR_INVALID_DATA;
5360#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5361 std::vector<std::thread> workers;
5362 std::atomic<int> y_count(0);
5364 int num_threads = std::max(1,
int(std::thread::hardware_concurrency()));
5365#if (TINYEXR_MAX_THREADS > 0)
5366 num_threads = std::min(num_threads,TINYEXR_MAX_THREADS);
5368 if (num_threads >
int(num_blocks)) {
5369 num_threads = int(num_blocks);
5371 for (
int t = 0; t < num_threads; t++) {
5372 workers.emplace_back(std::thread([&]() {
5374 while ((y = y_count++) <
int(num_blocks)) {
5378#if TINYEXR_USE_OPENMP
5379#pragma omp parallel for
5381 for (
int y = 0; y < static_cast<int>(num_blocks); y++) {
5384 size_t y_idx =
static_cast<size_t>(y);
5386 if (offsets[y_idx] +
sizeof(
int) * 2 > size) {
5387 invalid_data =
true;
5393 size_t(size - (offsets[y_idx] +
sizeof(
int) * 2));
5394 const unsigned char *data_ptr =
5395 reinterpret_cast<const unsigned char *
>(head + offsets[y_idx]);
5398 memcpy(&line_no, data_ptr,
sizeof(
int));
5400 memcpy(&data_len, data_ptr + 4,
sizeof(
int));
5401 tinyexr::swap4(&line_no);
5402 tinyexr::swap4(&data_len);
5404 if (
size_t(data_len) > data_size) {
5405 invalid_data =
true;
5407 }
else if ((line_no > (2 << 20)) || (line_no < -(2 << 20))) {
5410 invalid_data =
true;
5411 }
else if (data_len == 0) {
5414 invalid_data =
true;
5417 int end_line_no = (std::min)(line_no + num_scanline_blocks,
5418 (exr_header->data_window.max_y + 1));
5420 int num_lines = end_line_no - line_no;
5422 if (num_lines <= 0) {
5423 invalid_data =
true;
5432 static_cast<tinyexr_int64
>(line_no) -
5433 static_cast<tinyexr_int64
>(exr_header->data_window.min_y);
5434 if (lno > std::numeric_limits<int>::max()) {
5436 }
else if (lno < -std::numeric_limits<int>::max()) {
5439 line_no -= exr_header->data_window.min_y;
5443 invalid_data =
true;
5445 if (!tinyexr::DecodePixelData(
5446 exr_image->images, exr_header->requested_pixel_types,
5447 data_ptr,
static_cast<size_t>(data_len),
5448 exr_header->compression_type, exr_header->line_order,
5449 int(data_width), int(data_height), int(data_width), y, line_no,
5450 num_lines,
static_cast<size_t>(pixel_data_size),
5451 static_cast<size_t>(
5452 exr_header->num_custom_attributes),
5453 exr_header->custom_attributes,
5454 static_cast<size_t>(exr_header->num_channels),
5455 exr_header->channels, channel_offset_list)) {
5456 invalid_data =
true;
5463#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5468 for (
auto &t : workers) {
5478 (*err) +=
"Invalid/Corrupted data found when decoding pixels.\n";
5482 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
5483 if (exr_image->images[c]) {
5484 free(exr_image->images[c]);
5485 exr_image->images[c] = NULL;
5488 return TINYEXR_ERROR_INVALID_DATA;
5493 for (
int c = 0; c < exr_header->num_channels; c++) {
5494 exr_header->pixel_types[c] = exr_header->requested_pixel_types[c];
5499 exr_image->num_channels = num_channels;
5501 exr_image->width = int(data_width);
5502 exr_image->height = int(data_height);
5505 return TINYEXR_SUCCESS;
5508static bool ReconstructLineOffsets(
5509 std::vector<tinyexr::tinyexr_uint64> *offsets,
size_t n,
5510 const unsigned char *head,
const unsigned char *marker,
const size_t size) {
5511 if (head >= marker) {
5514 if (offsets->size() != n) {
5518 for (
size_t i = 0; i < n; i++) {
5519 size_t offset =
static_cast<size_t>(marker - head);
5521 if ((offset +
sizeof(tinyexr::tinyexr_uint64)) >= size) {
5526 unsigned int data_len;
5528 memcpy(&y, marker,
sizeof(
int));
5529 memcpy(&data_len, marker + 4,
sizeof(
unsigned int));
5531 if (data_len >= size) {
5536 tinyexr::swap4(&data_len);
5538 (*offsets)[i] = offset;
5540 marker += data_len + 8;
5547static int FloorLog2(
unsigned x) {
5560static int CeilLog2(
unsigned x) {
5576static int RoundLog2(
int x,
int tile_rounding_mode) {
5577 return (tile_rounding_mode == TINYEXR_TILE_ROUND_DOWN) ? FloorLog2(
static_cast<unsigned>(x)) : CeilLog2(static_cast<unsigned>(x));
5580static int CalculateNumXLevels(
const EXRHeader* exr_header) {
5581 int min_x = exr_header->data_window.min_x;
5582 int max_x = exr_header->data_window.max_x;
5583 int min_y = exr_header->data_window.min_y;
5584 int max_y = exr_header->data_window.max_y;
5587 switch (exr_header->tile_level_mode) {
5588 case TINYEXR_TILE_ONE_LEVEL:
5593 case TINYEXR_TILE_MIPMAP_LEVELS:
5596 int w = max_x - min_x + 1;
5597 int h = max_y - min_y + 1;
5598 num = RoundLog2(std::max(w, h), exr_header->tile_rounding_mode) + 1;
5602 case TINYEXR_TILE_RIPMAP_LEVELS:
5605 int w = max_x - min_x + 1;
5606 num = RoundLog2(w, exr_header->tile_rounding_mode) + 1;
5618static int CalculateNumYLevels(
const EXRHeader* exr_header) {
5619 int min_x = exr_header->data_window.min_x;
5620 int max_x = exr_header->data_window.max_x;
5621 int min_y = exr_header->data_window.min_y;
5622 int max_y = exr_header->data_window.max_y;
5625 switch (exr_header->tile_level_mode) {
5626 case TINYEXR_TILE_ONE_LEVEL:
5631 case TINYEXR_TILE_MIPMAP_LEVELS:
5634 int w = max_x - min_x + 1;
5635 int h = max_y - min_y + 1;
5636 num = RoundLog2(std::max(w, h), exr_header->tile_rounding_mode) + 1;
5640 case TINYEXR_TILE_RIPMAP_LEVELS:
5643 int h = max_y - min_y + 1;
5644 num = RoundLog2(h, exr_header->tile_rounding_mode) + 1;
5656static bool CalculateNumTiles(std::vector<int>& numTiles,
5659 int tile_rounding_mode) {
5660 for (
unsigned i = 0; i < numTiles.size(); i++) {
5661 int l = LevelSize(toplevel_size,
int(i), tile_rounding_mode);
5665 TINYEXR_CHECK_AND_RETURN_C(l <= std::numeric_limits<int>::max() - size + 1,
false);
5667 numTiles[i] = (l + size - 1) / size;
5672static bool PrecalculateTileInfo(std::vector<int>& num_x_tiles,
5673 std::vector<int>& num_y_tiles,
5675 int min_x = exr_header->data_window.min_x;
5676 int max_x = exr_header->data_window.max_x;
5677 int min_y = exr_header->data_window.min_y;
5678 int max_y = exr_header->data_window.max_y;
5680 int num_x_levels = CalculateNumXLevels(exr_header);
5682 if (num_x_levels < 0) {
5686 int num_y_levels = CalculateNumYLevels(exr_header);
5688 if (num_y_levels < 0) {
5692 num_x_tiles.resize(
size_t(num_x_levels));
5693 num_y_tiles.resize(
size_t(num_y_levels));
5695 if (!CalculateNumTiles(num_x_tiles,
5697 exr_header->tile_size_x,
5698 exr_header->tile_rounding_mode)) {
5702 if (!CalculateNumTiles(num_y_tiles,
5704 exr_header->tile_size_y,
5705 exr_header->tile_rounding_mode)) {
5712static void InitSingleResolutionOffsets(OffsetData& offset_data,
size_t num_blocks) {
5713 offset_data.offsets.resize(1);
5714 offset_data.offsets[0].resize(1);
5715 offset_data.offsets[0][0].resize(num_blocks);
5716 offset_data.num_x_levels = 1;
5717 offset_data.num_y_levels = 1;
5722static int InitTileOffsets(OffsetData& offset_data,
5724 const std::vector<int>& num_x_tiles,
5725 const std::vector<int>& num_y_tiles) {
5726 int num_tile_blocks = 0;
5727 offset_data.num_x_levels =
static_cast<int>(num_x_tiles.size());
5728 offset_data.num_y_levels =
static_cast<int>(num_y_tiles.size());
5729 switch (exr_header->tile_level_mode) {
5730 case TINYEXR_TILE_ONE_LEVEL:
5731 case TINYEXR_TILE_MIPMAP_LEVELS:
5732 TINYEXR_CHECK_AND_RETURN_C(offset_data.num_x_levels == offset_data.num_y_levels, 0);
5733 offset_data.offsets.resize(
size_t(offset_data.num_x_levels));
5735 for (
unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
5736 offset_data.offsets[l].resize(
size_t(num_y_tiles[l]));
5738 for (
unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
5739 offset_data.offsets[l][dy].resize(
size_t(num_x_tiles[l]));
5740 num_tile_blocks += num_x_tiles[l];
5745 case TINYEXR_TILE_RIPMAP_LEVELS:
5747 offset_data.offsets.resize(
static_cast<size_t>(offset_data.num_x_levels) *
static_cast<size_t>(offset_data.num_y_levels));
5749 for (
int ly = 0; ly < offset_data.num_y_levels; ++ly) {
5750 for (
int lx = 0; lx < offset_data.num_x_levels; ++lx) {
5751 int l = ly * offset_data.num_x_levels + lx;
5752 offset_data.offsets[size_t(l)].resize(
size_t(num_y_tiles[
size_t(ly)]));
5754 for (
size_t dy = 0; dy < offset_data.offsets[size_t(l)].size(); ++dy) {
5755 offset_data.offsets[size_t(l)][dy].resize(
size_t(num_x_tiles[
size_t(lx)]));
5756 num_tile_blocks += num_x_tiles[size_t(lx)];
5765 return num_tile_blocks;
5768static bool IsAnyOffsetsAreInvalid(
const OffsetData& offset_data) {
5769 for (
unsigned int l = 0; l < offset_data.offsets.size(); ++l)
5770 for (
unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy)
5771 for (
unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx)
5772 if (
reinterpret_cast<const tinyexr::tinyexr_int64&
>(offset_data.offsets[l][dy][dx]) <= 0)
5778static bool isValidTile(
const EXRHeader* exr_header,
5779 const OffsetData& offset_data,
5780 int dx,
int dy,
int lx,
int ly) {
5781 if (lx < 0 || ly < 0 || dx < 0 || dy < 0)
return false;
5782 int num_x_levels = offset_data.num_x_levels;
5783 int num_y_levels = offset_data.num_y_levels;
5784 switch (exr_header->tile_level_mode) {
5785 case TINYEXR_TILE_ONE_LEVEL:
5789 offset_data.offsets.size() > 0 &&
5790 offset_data.offsets[0].size() >
static_cast<size_t>(dy) &&
5791 offset_data.offsets[0][
size_t(dy)].size() >
static_cast<size_t>(dx)) {
5797 case TINYEXR_TILE_MIPMAP_LEVELS:
5799 if (lx < num_x_levels &&
5800 ly < num_y_levels &&
5801 offset_data.offsets.size() >
static_cast<size_t>(lx) &&
5802 offset_data.offsets[
size_t(lx)].size() >
static_cast<size_t>(dy) &&
5803 offset_data.offsets[
size_t(lx)][
size_t(dy)].size() >
static_cast<size_t>(dx)) {
5809 case TINYEXR_TILE_RIPMAP_LEVELS:
5811 size_t idx =
static_cast<size_t>(lx) +
static_cast<size_t>(ly)*
static_cast<size_t>(num_x_levels);
5812 if (lx < num_x_levels &&
5813 ly < num_y_levels &&
5814 (offset_data.offsets.size() > idx) &&
5815 offset_data.offsets[idx].size() >
static_cast<size_t>(dy) &&
5816 offset_data.offsets[idx][
size_t(dy)].size() >
static_cast<size_t>(dx)) {
5831static bool ReconstructTileOffsets(OffsetData& offset_data,
5833 const unsigned char* head,
const unsigned char* marker,
const size_t size,
5834 bool isMultiPartFile,
5836 int numXLevels = offset_data.num_x_levels;
5837 for (
unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
5838 for (
unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
5839 for (
unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
5840 tinyexr::tinyexr_uint64 tileOffset = tinyexr::tinyexr_uint64(marker - head);
5843 if (isMultiPartFile) {
5844 if ((marker +
sizeof(
int)) >= (head + size)) {
5849 marker +=
sizeof(int);
5852 if ((marker + 4 *
sizeof(
int)) >= (head + size)) {
5857 memcpy(&tileX, marker,
sizeof(
int));
5858 tinyexr::swap4(&tileX);
5859 marker +=
sizeof(int);
5862 memcpy(&tileY, marker,
sizeof(
int));
5863 tinyexr::swap4(&tileY);
5864 marker +=
sizeof(int);
5867 memcpy(&levelX, marker,
sizeof(
int));
5868 tinyexr::swap4(&levelX);
5869 marker +=
sizeof(int);
5872 memcpy(&levelY, marker,
sizeof(
int));
5873 tinyexr::swap4(&levelY);
5874 marker +=
sizeof(int);
5877 if ((marker + 2 *
sizeof(tinyexr::tinyexr_int64)) >= (head + size)) {
5880 tinyexr::tinyexr_int64 packed_offset_table_size;
5881 memcpy(&packed_offset_table_size, marker,
sizeof(tinyexr::tinyexr_int64));
5882 tinyexr::swap8(
reinterpret_cast<tinyexr::tinyexr_uint64*
>(&packed_offset_table_size));
5883 marker +=
sizeof(tinyexr::tinyexr_int64);
5885 tinyexr::tinyexr_int64 packed_sample_size;
5886 memcpy(&packed_sample_size, marker,
sizeof(tinyexr::tinyexr_int64));
5887 tinyexr::swap8(
reinterpret_cast<tinyexr::tinyexr_uint64*
>(&packed_sample_size));
5888 marker +=
sizeof(tinyexr::tinyexr_int64);
5891 marker += packed_offset_table_size + packed_sample_size + 8;
5893 if (marker >= (head + size)) {
5899 if ((marker +
sizeof(uint32_t)) >= (head + size)) {
5904 memcpy(&dataSize, marker,
sizeof(uint32_t));
5905 tinyexr::swap4(&dataSize);
5910 if (marker >= (head + size)) {
5915 if (!isValidTile(exr_header, offset_data,
5916 tileX, tileY, levelX, levelY)) {
5920 int level_idx = LevelIndex(levelX, levelY, exr_header->tile_level_mode, numXLevels);
5921 if (level_idx < 0) {
5925 if (
size_t(level_idx) >= offset_data.offsets.size()) {
5929 if (
size_t(tileY) >= offset_data.offsets[
size_t(level_idx)].size()) {
5933 if (
size_t(tileX) >= offset_data.offsets[
size_t(level_idx)][
size_t(tileY)].size()) {
5937 offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset;
5945static int ReadOffsets(OffsetData& offset_data,
5946 const unsigned char* head,
5947 const unsigned char*& marker,
5950 for (
unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
5951 for (
unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
5952 for (
unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
5953 tinyexr::tinyexr_uint64 offset;
5954 if ((marker +
sizeof(tinyexr_uint64)) >= (head + size)) {
5955 tinyexr::SetErrorMessage(
"Insufficient data size in offset table.", err);
5956 return TINYEXR_ERROR_INVALID_DATA;
5959 memcpy(&offset, marker,
sizeof(tinyexr::tinyexr_uint64));
5960 tinyexr::swap8(&offset);
5961 if (offset >= size) {
5962 tinyexr::SetErrorMessage(
"Invalid offset value in DecodeEXRImage.", err);
5963 return TINYEXR_ERROR_INVALID_DATA;
5965 marker +=
sizeof(tinyexr::tinyexr_uint64);
5966 offset_data.offsets[l][dy][dx] = offset;
5970 return TINYEXR_SUCCESS;
5974 const unsigned char *head,
5975 const unsigned char *marker,
const size_t size,
5977 if (exr_image == NULL || exr_header == NULL || head == NULL ||
5978 marker == NULL || (size <= tinyexr::kEXRVersionSize)) {
5979 tinyexr::SetErrorMessage(
"Invalid argument for DecodeEXRImage().", err);
5980 return TINYEXR_ERROR_INVALID_ARGUMENT;
5983 int num_scanline_blocks = 1;
5984 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
5985 num_scanline_blocks = 16;
5986 }
else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
5987 num_scanline_blocks = 32;
5988 }
else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
5989 num_scanline_blocks = 16;
5992 if (exr_header->data_window.max_x < exr_header->data_window.min_x ||
5993 exr_header->data_window.max_x - exr_header->data_window.min_x ==
5994 std::numeric_limits<int>::max()) {
5996 tinyexr::SetErrorMessage(
"Invalid data width value", err);
5997 return TINYEXR_ERROR_INVALID_DATA;
5999 tinyexr_int64 data_width =
6000 static_cast<tinyexr_int64
>(exr_header->data_window.max_x) -
static_cast<tinyexr_int64
>(exr_header->data_window.min_x) +
static_cast<tinyexr_int64
>(1);
6001 if (data_width <= 0) {
6002 tinyexr::SetErrorMessage(
"Invalid data window width value", err);
6003 return TINYEXR_ERROR_INVALID_DATA;
6006 if (exr_header->data_window.max_y < exr_header->data_window.min_y ||
6007 exr_header->data_window.max_y - exr_header->data_window.min_y ==
6008 std::numeric_limits<int>::max()) {
6009 tinyexr::SetErrorMessage(
"Invalid data height value", err);
6010 return TINYEXR_ERROR_INVALID_DATA;
6012 tinyexr_int64 data_height =
6013 static_cast<tinyexr_int64
>(exr_header->data_window.max_y) -
static_cast<tinyexr_int64
>(exr_header->data_window.min_y) +
static_cast<tinyexr_int64
>(1);
6015 if (data_height <= 0) {
6016 tinyexr::SetErrorMessage(
"Invalid data window height value", err);
6017 return TINYEXR_ERROR_INVALID_DATA;
6022 if (data_width > TINYEXR_DIMENSION_THRESHOLD) {
6023 tinyexr::SetErrorMessage(
"data width too large.", err);
6024 return TINYEXR_ERROR_INVALID_DATA;
6026 if (data_height > TINYEXR_DIMENSION_THRESHOLD) {
6027 tinyexr::SetErrorMessage(
"data height too large.", err);
6028 return TINYEXR_ERROR_INVALID_DATA;
6032 if (exr_header->tiled) {
6033 if (exr_header->tile_size_x > TINYEXR_DIMENSION_THRESHOLD) {
6034 tinyexr::SetErrorMessage(
"tile width too large.", err);
6035 return TINYEXR_ERROR_INVALID_DATA;
6037 if (exr_header->tile_size_y > TINYEXR_DIMENSION_THRESHOLD) {
6038 tinyexr::SetErrorMessage(
"tile height too large.", err);
6039 return TINYEXR_ERROR_INVALID_DATA;
6044 OffsetData offset_data;
6045 size_t num_blocks = 0;
6048 if (exr_header->tiled) {
6050 std::vector<int> num_x_tiles, num_y_tiles;
6051 if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_header)) {
6052 tinyexr::SetErrorMessage(
"Failed to precalculate tile info.", err);
6053 return TINYEXR_ERROR_INVALID_DATA;
6055 num_blocks = size_t(InitTileOffsets(offset_data, exr_header, num_x_tiles, num_y_tiles));
6056 if (exr_header->chunk_count > 0) {
6057 if (exr_header->chunk_count !=
static_cast<int>(num_blocks)) {
6058 tinyexr::SetErrorMessage(
"Invalid offset table size.", err);
6059 return TINYEXR_ERROR_INVALID_DATA;
6064 int ret = ReadOffsets(offset_data, head, marker, size, err);
6065 if (ret != TINYEXR_SUCCESS)
return ret;
6066 if (IsAnyOffsetsAreInvalid(offset_data)) {
6067 if (!ReconstructTileOffsets(offset_data, exr_header,
6069 exr_header->multipart, exr_header->non_image)) {
6071 tinyexr::SetErrorMessage(
"Invalid Tile Offsets data.", err);
6072 return TINYEXR_ERROR_INVALID_DATA;
6075 }
else if (exr_header->chunk_count > 0) {
6077 num_blocks =
static_cast<size_t>(exr_header->chunk_count);
6078 InitSingleResolutionOffsets(offset_data, num_blocks);
6080 num_blocks =
static_cast<size_t>(data_height) /
6081 static_cast<size_t>(num_scanline_blocks);
6082 if (num_blocks *
static_cast<size_t>(num_scanline_blocks) <
6083 static_cast<size_t>(data_height)) {
6087 InitSingleResolutionOffsets(offset_data, num_blocks);
6090 if (!exr_header->tiled) {
6091 std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0];
6092 for (
size_t y = 0; y < num_blocks; y++) {
6093 tinyexr::tinyexr_uint64 offset;
6095 if ((marker +
sizeof(tinyexr_uint64)) >= (head + size)) {
6096 tinyexr::SetErrorMessage(
"Insufficient data size in offset table.", err);
6097 return TINYEXR_ERROR_INVALID_DATA;
6100 memcpy(&offset, marker,
sizeof(tinyexr::tinyexr_uint64));
6101 tinyexr::swap8(&offset);
6102 if (offset >= size) {
6103 tinyexr::SetErrorMessage(
"Invalid offset value in DecodeEXRImage.", err);
6104 return TINYEXR_ERROR_INVALID_DATA;
6106 marker +=
sizeof(tinyexr::tinyexr_uint64);
6107 offsets[y] = offset;
6112 for (
size_t y = 0; y < num_blocks; y++) {
6113 if (offsets[y] <= 0) {
6121 ReconstructLineOffsets(&offsets, num_blocks, head, marker, size);
6126 tinyexr::SetErrorMessage(
6127 "Cannot reconstruct lineOffset table in DecodeEXRImage.", err);
6128 return TINYEXR_ERROR_INVALID_DATA;
6136 int ret = DecodeChunk(exr_image, exr_header, offset_data, head, size, &e);
6138 if (ret != TINYEXR_SUCCESS) {
6140 tinyexr::SetErrorMessage(e, err);
6144 FreeEXRImage(exr_image);
6147 if ((exr_header->num_channels > 0) && exr_image && exr_image->images) {
6148 for (
size_t c = 0; c < size_t(exr_header->num_channels); c++) {
6149 if (exr_image->images[c]) {
6150 free(exr_image->images[c]);
6151 exr_image->images[c] = NULL;
6154 free(exr_image->images);
6155 exr_image->images = NULL;
6164static void GetLayers(
const EXRHeader &exr_header,
6165 std::vector<std::string> &layer_names) {
6170 layer_names.clear();
6171 for (
int c = 0; c < exr_header.num_channels; c++) {
6172 std::string full_name(exr_header.channels[c].name);
6173 const size_t pos = full_name.find_last_of(
'.');
6174 if (pos != std::string::npos && pos != 0 && pos + 1 < full_name.size()) {
6175 full_name.erase(pos);
6176 if (std::find(layer_names.begin(), layer_names.end(), full_name) ==
6178 layer_names.push_back(full_name);
6183struct LayerChannel {
6184 explicit LayerChannel(
size_t i, std::string n) : index(i), name(n) {}
6189static void ChannelsInLayer(
const EXRHeader &exr_header,
6190 const std::string &layer_name,
6191 std::vector<LayerChannel> &channels) {
6194 for (
int c = 0; c < exr_header.num_channels; c++) {
6196 std::string ch_name(exr_header.channels[c].name);
6197 if (layer_name.empty()) {
6198 const size_t pos = ch_name.find_last_of(
'.');
6199 if (pos != std::string::npos && pos < ch_name.size()) {
6200 if (pos != 0)
continue;
6201 ch_name = ch_name.substr(pos + 1);
6204 const size_t pos = ch_name.find(layer_name +
'.');
6205 if (pos == std::string::npos)
continue;
6207 ch_name = ch_name.substr(layer_name.size() + 1);
6210 LayerChannel ch(
size_t(c), ch_name);
6211 channels.push_back(ch);
6217int EXRLayers(
const char *filename,
const char **layer_names[],
int *num_layers,
6221 InitEXRHeader(&exr_header);
6224 int ret = ParseEXRVersionFromFile(&exr_version, filename);
6225 if (ret != TINYEXR_SUCCESS) {
6226 tinyexr::SetErrorMessage(
"Invalid EXR header.", err);
6230 if (exr_version.multipart || exr_version.non_image) {
6231 tinyexr::SetErrorMessage(
6232 "Loading multipart or DeepImage is not supported in LoadEXR() API",
6234 return TINYEXR_ERROR_INVALID_DATA;
6238 int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err);
6239 if (ret != TINYEXR_SUCCESS) {
6240 FreeEXRHeader(&exr_header);
6244 std::vector<std::string> layer_vec;
6245 tinyexr::GetLayers(exr_header, layer_vec);
6247 (*num_layers) = int(layer_vec.size());
6248 (*layer_names) =
static_cast<const char **
>(
6249 malloc(
sizeof(
const char *) *
static_cast<size_t>(layer_vec.size())));
6250 for (
size_t c = 0; c < static_cast<size_t>(layer_vec.size()); c++) {
6252 (*layer_names)[c] = _strdup(layer_vec[c].c_str());
6254 (*layer_names)[c] = strdup(layer_vec[c].c_str());
6258 FreeEXRHeader(&exr_header);
6259 return TINYEXR_SUCCESS;
6262int LoadEXR(
float **out_rgba,
int *width,
int *height,
const char *filename,
6264 return LoadEXRWithLayer(out_rgba, width, height, filename,
6268int LoadEXRWithLayer(
float **out_rgba,
int *width,
int *height,
6269 const char *filename,
const char *layername,
6271 if (out_rgba == NULL) {
6272 tinyexr::SetErrorMessage(
"Invalid argument for LoadEXR()", err);
6273 return TINYEXR_ERROR_INVALID_ARGUMENT;
6279 InitEXRHeader(&exr_header);
6280 InitEXRImage(&exr_image);
6283 int ret = ParseEXRVersionFromFile(&exr_version, filename);
6284 if (ret != TINYEXR_SUCCESS) {
6285 std::stringstream ss;
6286 ss <<
"Failed to open EXR file or read version info from EXR file. code("
6288 tinyexr::SetErrorMessage(ss.str(), err);
6292 if (exr_version.multipart || exr_version.non_image) {
6293 tinyexr::SetErrorMessage(
6294 "Loading multipart or DeepImage is not supported in LoadEXR() API",
6296 return TINYEXR_ERROR_INVALID_DATA;
6301 int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err);
6302 if (ret != TINYEXR_SUCCESS) {
6303 FreeEXRHeader(&exr_header);
6309 for (
int i = 0; i < exr_header.num_channels; i++) {
6310 if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
6311 exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
6317 int ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename, err);
6318 if (ret != TINYEXR_SUCCESS) {
6319 FreeEXRHeader(&exr_header);
6330 std::vector<std::string> layer_names;
6331 tinyexr::GetLayers(exr_header, layer_names);
6333 std::vector<tinyexr::LayerChannel> channels;
6334 tinyexr::ChannelsInLayer(
6335 exr_header, layername == NULL ?
"" : std::string(layername), channels);
6338 if (channels.size() < 1) {
6339 if (layername == NULL) {
6340 tinyexr::SetErrorMessage(
"Layer Not Found. Seems EXR contains channels with layer(e.g. `diffuse.R`). if you are using LoadEXR(), please try LoadEXRWithLayer(). LoadEXR() cannot load EXR having channels with layer.", err);
6343 tinyexr::SetErrorMessage(
"Layer Not Found", err);
6345 FreeEXRHeader(&exr_header);
6346 FreeEXRImage(&exr_image);
6347 return TINYEXR_ERROR_LAYER_NOT_FOUND;
6350 size_t ch_count = channels.size() < 4 ? channels.size() : 4;
6351 for (
size_t c = 0; c < ch_count; c++) {
6352 const tinyexr::LayerChannel &ch = channels[c];
6354 if (ch.name ==
"R") {
6355 idxR = int(ch.index);
6356 }
else if (ch.name ==
"G") {
6357 idxG = int(ch.index);
6358 }
else if (ch.name ==
"B") {
6359 idxB = int(ch.index);
6360 }
else if (ch.name ==
"A") {
6361 idxA = int(ch.index);
6365 if (channels.size() == 1) {
6366 int chIdx = int(channels.front().index);
6369 (*out_rgba) =
reinterpret_cast<float *
>(
6370 malloc(4 *
sizeof(
float) *
static_cast<size_t>(exr_image.width) *
6371 static_cast<size_t>(exr_image.height)));
6373 if (exr_header.tiled) {
6374 const size_t tile_size_x =
static_cast<size_t>(exr_header.tile_size_x);
6375 const size_t tile_size_y =
static_cast<size_t>(exr_header.tile_size_y);
6376 for (
int it = 0; it < exr_image.num_tiles; it++) {
6377 for (
size_t j = 0; j < tile_size_y; j++) {
6378 for (
size_t i = 0; i < tile_size_x; i++) {
6380 static_cast<size_t>(exr_image.tiles[it].offset_x) * tile_size_x +
6383 static_cast<size_t>(exr_image.tiles[it].offset_y) * tile_size_y +
6385 const size_t idx = ii + jj *
static_cast<size_t>(exr_image.width);
6388 if (ii >=
static_cast<size_t>(exr_image.width)) {
6391 if (jj >=
static_cast<size_t>(exr_image.height)) {
6394 const size_t srcIdx = i + j * tile_size_x;
6395 unsigned char **src = exr_image.tiles[it].images;
6396 (*out_rgba)[4 * idx + 0] =
6397 reinterpret_cast<float **
>(src)[chIdx][srcIdx];
6398 (*out_rgba)[4 * idx + 1] =
6399 reinterpret_cast<float **
>(src)[chIdx][srcIdx];
6400 (*out_rgba)[4 * idx + 2] =
6401 reinterpret_cast<float **
>(src)[chIdx][srcIdx];
6402 (*out_rgba)[4 * idx + 3] =
6403 reinterpret_cast<float **
>(src)[chIdx][srcIdx];
6408 const size_t pixel_size =
static_cast<size_t>(exr_image.width) *
6409 static_cast<size_t>(exr_image.height);
6410 for (
size_t i = 0; i < pixel_size; i++) {
6412 reinterpret_cast<float **
>(exr_image.images)[chIdx][i];
6413 (*out_rgba)[4 * i + 0] = val;
6414 (*out_rgba)[4 * i + 1] = val;
6415 (*out_rgba)[4 * i + 2] = val;
6416 (*out_rgba)[4 * i + 3] = val;
6423 tinyexr::SetErrorMessage(
"R channel not found", err);
6425 FreeEXRHeader(&exr_header);
6426 FreeEXRImage(&exr_image);
6427 return TINYEXR_ERROR_INVALID_DATA;
6431 tinyexr::SetErrorMessage(
"G channel not found", err);
6432 FreeEXRHeader(&exr_header);
6433 FreeEXRImage(&exr_image);
6434 return TINYEXR_ERROR_INVALID_DATA;
6438 tinyexr::SetErrorMessage(
"B channel not found", err);
6439 FreeEXRHeader(&exr_header);
6440 FreeEXRImage(&exr_image);
6441 return TINYEXR_ERROR_INVALID_DATA;
6444 (*out_rgba) =
reinterpret_cast<float *
>(
6445 malloc(4 *
sizeof(
float) *
static_cast<size_t>(exr_image.width) *
6446 static_cast<size_t>(exr_image.height)));
6447 if (exr_header.tiled) {
6448 const size_t tile_size_x =
static_cast<size_t>(exr_header.tile_size_x);
6449 const size_t tile_size_y =
static_cast<size_t>(exr_header.tile_size_y);
6450 for (
int it = 0; it < exr_image.num_tiles; it++) {
6451 for (
size_t j = 0; j < tile_size_y; j++) {
6452 for (
size_t i = 0; i < tile_size_x; i++) {
6454 static_cast<size_t>(exr_image.tiles[it].offset_x) *
6458 static_cast<size_t>(exr_image.tiles[it].offset_y) *
6461 const size_t idx = ii + jj *
static_cast<size_t>(exr_image.width);
6464 if (ii >=
static_cast<size_t>(exr_image.width)) {
6467 if (jj >=
static_cast<size_t>(exr_image.height)) {
6470 const size_t srcIdx = i + j * tile_size_x;
6471 unsigned char **src = exr_image.tiles[it].images;
6472 (*out_rgba)[4 * idx + 0] =
6473 reinterpret_cast<float **
>(src)[idxR][srcIdx];
6474 (*out_rgba)[4 * idx + 1] =
6475 reinterpret_cast<float **
>(src)[idxG][srcIdx];
6476 (*out_rgba)[4 * idx + 2] =
6477 reinterpret_cast<float **
>(src)[idxB][srcIdx];
6479 (*out_rgba)[4 * idx + 3] =
6480 reinterpret_cast<float **
>(src)[idxA][srcIdx];
6482 (*out_rgba)[4 * idx + 3] = 1.0;
6488 const size_t pixel_size =
static_cast<size_t>(exr_image.width) *
6489 static_cast<size_t>(exr_image.height);
6490 for (
size_t i = 0; i < pixel_size; i++) {
6491 (*out_rgba)[4 * i + 0] =
6492 reinterpret_cast<float **
>(exr_image.images)[idxR][i];
6493 (*out_rgba)[4 * i + 1] =
6494 reinterpret_cast<float **
>(exr_image.images)[idxG][i];
6495 (*out_rgba)[4 * i + 2] =
6496 reinterpret_cast<float **
>(exr_image.images)[idxB][i];
6498 (*out_rgba)[4 * i + 3] =
6499 reinterpret_cast<float **
>(exr_image.images)[idxA][i];
6501 (*out_rgba)[4 * i + 3] = 1.0;
6507 (*width) = exr_image.width;
6508 (*height) = exr_image.height;
6510 FreeEXRHeader(&exr_header);
6511 FreeEXRImage(&exr_image);
6513 return TINYEXR_SUCCESS;
6516int IsEXR(
const char *filename) {
6519 int ret = ParseEXRVersionFromFile(&exr_version, filename);
6520 if (ret != TINYEXR_SUCCESS) {
6524 return TINYEXR_SUCCESS;
6527int IsEXRFromMemory(
const unsigned char *memory,
size_t size) {
6530 int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
6531 if (ret != TINYEXR_SUCCESS) {
6535 return TINYEXR_SUCCESS;
6539 const unsigned char *memory,
size_t size,
6541 if (memory == NULL || exr_header == NULL) {
6542 tinyexr::SetErrorMessage(
6543 "Invalid argument. `memory` or `exr_header` argument is null in "
6544 "ParseEXRHeaderFromMemory()",
6548 return TINYEXR_ERROR_INVALID_ARGUMENT;
6551 if (size < tinyexr::kEXRVersionSize) {
6552 tinyexr::SetErrorMessage(
"Insufficient header/data size.\n", err);
6553 return TINYEXR_ERROR_INVALID_DATA;
6556 const unsigned char *marker = memory + tinyexr::kEXRVersionSize;
6557 size_t marker_size = size - tinyexr::kEXRVersionSize;
6559 tinyexr::HeaderInfo info;
6564 std::string err_str;
6565 ret = ParseEXRHeader(&info, NULL, version, &err_str, marker, marker_size);
6567 if (ret != TINYEXR_SUCCESS) {
6568 if (err && !err_str.empty()) {
6569 tinyexr::SetErrorMessage(err_str, err);
6576 std::string err_str;
6578 if (!ConvertHeader(exr_header, info, &warn, &err_str)) {
6580 for (
size_t i = 0; i < info.attributes.size(); i++) {
6581 if (info.attributes[i].value) {
6582 free(info.attributes[i].value);
6585 if (err && !err_str.empty()) {
6586 tinyexr::SetErrorMessage(err_str, err);
6588 ret = TINYEXR_ERROR_INVALID_HEADER;
6592 exr_header->multipart = version->multipart ? 1 : 0;
6593 exr_header->non_image = version->non_image ? 1 : 0;
6598int LoadEXRFromMemory(
float **out_rgba,
int *width,
int *height,
6599 const unsigned char *memory,
size_t size,
6601 if (out_rgba == NULL || memory == NULL) {
6602 tinyexr::SetErrorMessage(
"Invalid argument for LoadEXRFromMemory", err);
6603 return TINYEXR_ERROR_INVALID_ARGUMENT;
6610 InitEXRHeader(&exr_header);
6612 int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
6613 if (ret != TINYEXR_SUCCESS) {
6614 std::stringstream ss;
6615 ss <<
"Failed to parse EXR version. code(" << ret <<
")";
6616 tinyexr::SetErrorMessage(ss.str(), err);
6620 ret = ParseEXRHeaderFromMemory(&exr_header, &exr_version, memory, size, err);
6621 if (ret != TINYEXR_SUCCESS) {
6626 for (
int i = 0; i < exr_header.num_channels; i++) {
6627 if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
6628 exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
6632 InitEXRImage(&exr_image);
6633 ret = LoadEXRImageFromMemory(&exr_image, &exr_header, memory, size, err);
6634 if (ret != TINYEXR_SUCCESS) {
6643 for (
int c = 0; c < exr_header.num_channels; c++) {
6644 if (strcmp(exr_header.channels[c].name,
"R") == 0) {
6646 }
else if (strcmp(exr_header.channels[c].name,
"G") == 0) {
6648 }
else if (strcmp(exr_header.channels[c].name,
"B") == 0) {
6650 }
else if (strcmp(exr_header.channels[c].name,
"A") == 0) {
6656 if (exr_header.num_channels == 1) {
6659 (*out_rgba) =
reinterpret_cast<float *
>(
6660 malloc(4 *
sizeof(
float) *
static_cast<size_t>(exr_image.width) *
6661 static_cast<size_t>(exr_image.height)));
6663 if (exr_header.tiled) {
6664 const size_t tile_size_x =
static_cast<size_t>(exr_header.tile_size_x);
6665 const size_t tile_size_y =
static_cast<size_t>(exr_header.tile_size_y);
6666 for (
int it = 0; it < exr_image.num_tiles; it++) {
6667 for (
size_t j = 0; j < tile_size_y; j++) {
6668 for (
size_t i = 0; i < tile_size_x; i++) {
6670 static_cast<size_t>(exr_image.tiles[it].offset_x) *
6674 static_cast<size_t>(exr_image.tiles[it].offset_y) *
6677 const size_t idx = ii + jj *
static_cast<size_t>(exr_image.width);
6680 if (ii >=
static_cast<size_t>(exr_image.width)) {
6683 if (jj >=
static_cast<size_t>(exr_image.height)) {
6686 const size_t srcIdx = i + j * tile_size_x;
6687 unsigned char **src = exr_image.tiles[it].images;
6688 (*out_rgba)[4 * idx + 0] =
6689 reinterpret_cast<float **
>(src)[0][srcIdx];
6690 (*out_rgba)[4 * idx + 1] =
6691 reinterpret_cast<float **
>(src)[0][srcIdx];
6692 (*out_rgba)[4 * idx + 2] =
6693 reinterpret_cast<float **
>(src)[0][srcIdx];
6694 (*out_rgba)[4 * idx + 3] =
6695 reinterpret_cast<float **
>(src)[0][srcIdx];
6700 const size_t pixel_size =
static_cast<size_t>(exr_image.width) *
6701 static_cast<size_t>(exr_image.height);
6702 for (
size_t i = 0; i < pixel_size; i++) {
6703 const float val =
reinterpret_cast<float **
>(exr_image.images)[0][i];
6704 (*out_rgba)[4 * i + 0] = val;
6705 (*out_rgba)[4 * i + 1] = val;
6706 (*out_rgba)[4 * i + 2] = val;
6707 (*out_rgba)[4 * i + 3] = val;
6715 tinyexr::SetErrorMessage(
"R channel not found", err);
6718 return TINYEXR_ERROR_INVALID_DATA;
6722 tinyexr::SetErrorMessage(
"G channel not found", err);
6724 return TINYEXR_ERROR_INVALID_DATA;
6728 tinyexr::SetErrorMessage(
"B channel not found", err);
6730 return TINYEXR_ERROR_INVALID_DATA;
6733 (*out_rgba) =
reinterpret_cast<float *
>(
6734 malloc(4 *
sizeof(
float) *
static_cast<size_t>(exr_image.width) *
6735 static_cast<size_t>(exr_image.height)));
6737 if (exr_header.tiled) {
6738 const size_t tile_size_x =
static_cast<size_t>(exr_header.tile_size_x);
6739 const size_t tile_size_y =
static_cast<size_t>(exr_header.tile_size_y);
6740 for (
int it = 0; it < exr_image.num_tiles; it++) {
6741 for (
size_t j = 0; j < tile_size_y; j++)
6742 for (
size_t i = 0; i < tile_size_x; i++) {
6744 static_cast<size_t>(exr_image.tiles[it].offset_x) *
6748 static_cast<size_t>(exr_image.tiles[it].offset_y) *
6751 const size_t idx = ii + jj *
static_cast<size_t>(exr_image.width);
6754 if (ii >=
static_cast<size_t>(exr_image.width)) {
6757 if (jj >=
static_cast<size_t>(exr_image.height)) {
6760 const size_t srcIdx = i + j * tile_size_x;
6761 unsigned char **src = exr_image.tiles[it].images;
6762 (*out_rgba)[4 * idx + 0] =
6763 reinterpret_cast<float **
>(src)[idxR][srcIdx];
6764 (*out_rgba)[4 * idx + 1] =
6765 reinterpret_cast<float **
>(src)[idxG][srcIdx];
6766 (*out_rgba)[4 * idx + 2] =
6767 reinterpret_cast<float **
>(src)[idxB][srcIdx];
6769 (*out_rgba)[4 * idx + 3] =
6770 reinterpret_cast<float **
>(src)[idxA][srcIdx];
6772 (*out_rgba)[4 * idx + 3] = 1.0;
6777 const size_t pixel_size =
static_cast<size_t>(exr_image.width) *
6778 static_cast<size_t>(exr_image.height);
6779 for (
size_t i = 0; i < pixel_size; i++) {
6780 (*out_rgba)[4 * i + 0] =
6781 reinterpret_cast<float **
>(exr_image.images)[idxR][i];
6782 (*out_rgba)[4 * i + 1] =
6783 reinterpret_cast<float **
>(exr_image.images)[idxG][i];
6784 (*out_rgba)[4 * i + 2] =
6785 reinterpret_cast<float **
>(exr_image.images)[idxB][i];
6787 (*out_rgba)[4 * i + 3] =
6788 reinterpret_cast<float **
>(exr_image.images)[idxA][i];
6790 (*out_rgba)[4 * i + 3] = 1.0;
6796 (*width) = exr_image.width;
6797 (*height) = exr_image.height;
6799 FreeEXRHeader(&exr_header);
6800 FreeEXRImage(&exr_image);
6802 return TINYEXR_SUCCESS;
6808struct MemoryMappedFile {
6809 unsigned char *data;
6811#ifdef TINYEXR_USE_WIN32_MMAP
6812 HANDLE windows_file;
6813 HANDLE windows_file_mapping;
6814#elif defined(TINYEXR_USE_POSIX_MMAP)
6815 int posix_descriptor;
6821 MemoryMappedFile(
const char *filename) {
6824#ifdef TINYEXR_USE_WIN32_MMAP
6825 windows_file_mapping = NULL;
6827 CreateFileW(tinyexr::UTF8ToWchar(filename).c_str(),
6832 FILE_ATTRIBUTE_READONLY,
6834 if (windows_file == INVALID_HANDLE_VALUE) {
6838 windows_file_mapping = CreateFileMapping(windows_file,
6844 if (windows_file_mapping == NULL) {
6848 data =
reinterpret_cast<unsigned char *
>(
6849 MapViewOfFile(windows_file_mapping,
6858 LARGE_INTEGER windows_file_size = {};
6859 if (!GetFileSizeEx(windows_file, &windows_file_size) ||
6860 static_cast<ULONGLONG
>(windows_file_size.QuadPart) >
6861 std::numeric_limits<size_t>::max()) {
6862 UnmapViewOfFile(data);
6866 size =
static_cast<size_t>(windows_file_size.QuadPart);
6867#elif defined(TINYEXR_USE_POSIX_MMAP)
6868 posix_descriptor = open(filename, O_RDONLY);
6869 if (posix_descriptor == -1) {
6874 if (fstat(posix_descriptor, &info) < 0) {
6884#pragma clang diagnostic push
6885#pragma clang diagnostic ignored "-Wtautological-type-limit-compare"
6887 if (info.st_size < 0 ||
6888 info.st_size > std::numeric_limits<ssize_t>::max()) {
6892#pragma clang diagnostic pop
6894 size =
static_cast<size_t>(info.st_size);
6896 data =
reinterpret_cast<unsigned char *
>(
6897 mmap(0, size, PROT_READ, MAP_SHARED, posix_descriptor, 0));
6898 if (data == MAP_FAILED) {
6903 FILE *fp = fopen(filename,
"rb");
6911 if (fseek(fp, 0, SEEK_END) != 0) {
6915 const long ftell_result = ftell(fp);
6916 if (ftell_result < 0) {
6921 size =
static_cast<size_t>(ftell_result);
6922 if (fseek(fp, 0, SEEK_SET) != 0) {
6928 data =
reinterpret_cast<unsigned char *
>(malloc(size));
6934 size_t read_bytes = fread(data, 1, size, fp);
6935 if (read_bytes != size) {
6947 ~MemoryMappedFile() {
6948#ifdef TINYEXR_USE_WIN32_MMAP
6950 (void)UnmapViewOfFile(data);
6954 if (windows_file_mapping != NULL) {
6955 (void)CloseHandle(windows_file_mapping);
6958 if (windows_file != INVALID_HANDLE_VALUE) {
6959 (void)CloseHandle(windows_file);
6961#elif defined(TINYEXR_USE_POSIX_MMAP)
6963 (void)munmap(data, size);
6967 if (posix_descriptor != -1) {
6968 (void)close(posix_descriptor);
6981#if TINYEXR_HAS_CXX11
6983#pragma clang diagnostic push
6984#pragma clang diagnostic ignored "-Wc++98-compat"
6986 MemoryMappedFile(
const MemoryMappedFile &) =
delete;
6987 MemoryMappedFile &operator=(
const MemoryMappedFile &) =
delete;
6988 MemoryMappedFile(MemoryMappedFile &&other)
noexcept =
delete;
6989 MemoryMappedFile &operator=(MemoryMappedFile &&other)
noexcept =
delete;
6991#pragma clang diagnostic pop
6996 bool valid()
const {
return data; }
7000 const char *filename,
const char **err) {
7001 if (exr_image == NULL) {
7002 tinyexr::SetErrorMessage(
"Invalid argument for LoadEXRImageFromFile", err);
7003 return TINYEXR_ERROR_INVALID_ARGUMENT;
7006 MemoryMappedFile file(filename);
7007 if (!file.valid()) {
7008 tinyexr::SetErrorMessage(
"Cannot read file " + std::string(filename), err);
7009 return TINYEXR_ERROR_CANT_OPEN_FILE;
7012 if (file.size < 16) {
7013 tinyexr::SetErrorMessage(
"File size too short : " + std::string(filename),
7015 return TINYEXR_ERROR_INVALID_FILE;
7018 return LoadEXRImageFromMemory(exr_image, exr_header, file.data, file.size,
7023 const unsigned char *memory,
const size_t size,
7025 if (exr_image == NULL || memory == NULL ||
7026 (size < tinyexr::kEXRVersionSize)) {
7027 tinyexr::SetErrorMessage(
"Invalid argument for LoadEXRImageFromMemory",
7029 return TINYEXR_ERROR_INVALID_ARGUMENT;
7032 if (exr_header->header_len == 0) {
7033 tinyexr::SetErrorMessage(
"EXRHeader variable is not initialized.", err);
7034 return TINYEXR_ERROR_INVALID_ARGUMENT;
7037 const unsigned char *head = memory;
7038 const unsigned char *marker =
reinterpret_cast<const unsigned char *
>(
7039 memory + exr_header->header_len +
7041 return tinyexr::DecodeEXRImage(exr_image, exr_header, head, marker, size,
7049#pragma clang diagnostic push
7050#pragma clang diagnostic ignored "-Wsign-conversion"
7055static bool EncodePixelData( std::vector<unsigned char>& out_data,
7056 const unsigned char*
const* images,
7057 int compression_type,
7064 size_t pixel_data_size,
7065 const std::vector<ChannelInfo>& channels,
7066 const std::vector<size_t>& channel_offset_list,
7068 const void* compression_param = 0)
7070 size_t buf_size =
static_cast<size_t>(width) *
7071 static_cast<size_t>(num_lines) *
7072 static_cast<size_t>(pixel_data_size);
7076 std::vector<unsigned char> buf(buf_size);
7078 size_t start_y =
static_cast<size_t>(line_no);
7079 for (
size_t c = 0; c < channels.size(); c++) {
7080 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
7081 if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
7082 for (
int y = 0; y < num_lines; y++) {
7084 float *line_ptr =
reinterpret_cast<float *
>(&buf.at(
7085 static_cast<size_t>(pixel_data_size *
size_t(y) *
size_t(width)) +
7086 channel_offset_list[c] *
7087 static_cast<size_t>(width)));
7088 for (
int x = 0; x < width; x++) {
7090 h16.u =
reinterpret_cast<const unsigned short *
const *
>(
7091 images)[c][(y + start_y) * size_t(x_stride) + size_t(x)];
7093 tinyexr::FP32
f32 = half_to_float(h16);
7095 tinyexr::swap4(&
f32.f);
7098 tinyexr::cpy4(line_ptr + x, &(
f32.f));
7101 }
else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
7102 for (
int y = 0; y < num_lines; y++) {
7104 unsigned short *line_ptr =
reinterpret_cast<unsigned short *
>(
7105 &buf.at(
static_cast<size_t>(pixel_data_size * y *
7107 channel_offset_list[c] *
7108 static_cast<size_t>(width)));
7109 for (
int x = 0; x < width; x++) {
7110 unsigned short val =
reinterpret_cast<const unsigned short *
const *
>(
7111 images)[c][(y + start_y) * x_stride + x];
7113 tinyexr::swap2(&val);
7116 tinyexr::cpy2(line_ptr + x, &val);
7121 (*err) +=
"Invalid requested_pixel_type.\n";
7126 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
7127 if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
7128 for (
int y = 0; y < num_lines; y++) {
7130 unsigned short *line_ptr =
reinterpret_cast<unsigned short *
>(
7131 &buf.at(
static_cast<size_t>(pixel_data_size * y *
7133 channel_offset_list[c] *
7134 static_cast<size_t>(width)));
7135 for (
int x = 0; x < width; x++) {
7137 f32.f =
reinterpret_cast<const float *
const *
>(
7138 images)[c][(y + start_y) * x_stride + x];
7141 h16 = float_to_half_full(f32);
7143 tinyexr::swap2(
reinterpret_cast<unsigned short *
>(&h16.u));
7146 tinyexr::cpy2(line_ptr + x, &(h16.u));
7149 }
else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
7150 for (
int y = 0; y < num_lines; y++) {
7152 float *line_ptr =
reinterpret_cast<float *
>(&buf.at(
7153 static_cast<size_t>(pixel_data_size * y * width) +
7154 channel_offset_list[c] *
7155 static_cast<size_t>(width)));
7156 for (
int x = 0; x < width; x++) {
7157 float val =
reinterpret_cast<const float *
const *
>(
7158 images)[c][(y + start_y) * x_stride + x];
7160 tinyexr::swap4(&val);
7163 tinyexr::cpy4(line_ptr + x, &val);
7168 (*err) +=
"Invalid requested_pixel_type.\n";
7172 }
else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
7173 for (
int y = 0; y < num_lines; y++) {
7175 unsigned int *line_ptr =
reinterpret_cast<unsigned int *
>(&buf.at(
7176 static_cast<size_t>(pixel_data_size * y * width) +
7177 channel_offset_list[c] *
static_cast<size_t>(width)));
7178 for (
int x = 0; x < width; x++) {
7179 unsigned int val =
reinterpret_cast<const unsigned int *
const *
>(
7180 images)[c][(y + start_y) * x_stride + x];
7182 tinyexr::swap4(&val);
7185 tinyexr::cpy4(line_ptr + x, &val);
7191 if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
7195 out_data.insert(out_data.end(), buf.begin(), buf.end());
7197 }
else if ((compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
7198 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) {
7199#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
7200 std::vector<unsigned char> block(mz_compressBound(
7201 static_cast<unsigned long>(buf.size())));
7202#elif TINYEXR_USE_STB_ZLIB
7205 std::vector<unsigned char> block(256 + 2 * buf.size());
7206#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB == 1)
7207 std::vector<unsigned char> block(nanoz_compressBound(
7208 static_cast<unsigned long>(buf.size())));
7210 std::vector<unsigned char> block(
7211 compressBound(
static_cast<uLong
>(buf.size())));
7213 tinyexr::tinyexr_uint64 outSize = block.size();
7215 if (!tinyexr::CompressZip(&block.at(0), outSize,
7216 reinterpret_cast<const unsigned char *
>(&buf.at(0)),
7217 static_cast<unsigned long>(buf.size()))) {
7219 (*err) +=
"Zip compresssion failed.\n";
7227 unsigned int data_len =
static_cast<unsigned int>(outSize);
7229 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7231 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) {
7233 std::vector<unsigned char> block((buf.size() * 3) / 2);
7235 tinyexr::tinyexr_uint64 outSize = block.size();
7237 if (!tinyexr::CompressRle(&block.at(0), outSize,
7238 reinterpret_cast<const unsigned char *
>(&buf.at(0)),
7239 static_cast<unsigned long>(buf.size()))) {
7241 (*err) +=
"RLE compresssion failed.\n";
7249 unsigned int data_len =
static_cast<unsigned int>(outSize);
7250 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7252 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
7254 unsigned int bufLen =
7255 8192 +
static_cast<unsigned int>(
7256 2 *
static_cast<unsigned int>(
7258 std::vector<unsigned char> block(bufLen);
7259 unsigned int outSize =
static_cast<unsigned int>(block.size());
7261 if (!CompressPiz(&block.at(0), &outSize,
7262 reinterpret_cast<const unsigned char *
>(&buf.at(0)),
7263 buf.size(), channels, width, num_lines)) {
7265 (*err) +=
"PIZ compresssion failed.\n";
7273 unsigned int data_len = outSize;
7274 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7278 (*err) +=
"PIZ compression is disabled in this build.\n";
7282 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
7284 const ZFPCompressionParam* zfp_compression_param =
reinterpret_cast<const ZFPCompressionParam*
>(compression_param);
7285 std::vector<unsigned char> block;
7286 unsigned int outSize;
7288 tinyexr::CompressZfp(
7289 &block, &outSize,
reinterpret_cast<const float *
>(&buf.at(0)),
7290 width, num_lines,
static_cast<int>(channels.size()), *zfp_compression_param);
7295 unsigned int data_len = outSize;
7296 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7300 (*err) +=
"ZFP compression is disabled in this build.\n";
7302 (void)compression_param;
7312static int EncodeTiledLevel(
const EXRImage* level_image,
const EXRHeader* exr_header,
7313 const std::vector<tinyexr::ChannelInfo>& channels,
7314 std::vector<std::vector<unsigned char> >& data_list,
7316 int num_x_tiles,
int num_y_tiles,
7317 const std::vector<size_t>& channel_offset_list,
7318 int pixel_data_size,
7319 const void* compression_param,
7321 int num_tiles = num_x_tiles * num_y_tiles;
7322 if (num_tiles != level_image->num_tiles) {
7324 (*err) +=
"Invalid number of tiles in argument.\n";
7326 return TINYEXR_ERROR_INVALID_ARGUMENT;
7329 if ((exr_header->tile_size_x > level_image->width || exr_header->tile_size_y > level_image->height) &&
7330 level_image->level_x == 0 && level_image->level_y == 0) {
7332 (*err) +=
"Failed to encode tile data.\n";
7334 return TINYEXR_ERROR_INVALID_DATA;
7338#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7339 std::atomic<bool> invalid_data(
false);
7341 bool invalid_data(
false);
7344#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7345 std::vector<std::thread> workers;
7346 std::atomic<int> tile_count(0);
7348 int num_threads = std::max(1,
int(std::thread::hardware_concurrency()));
7349#if (TINYEXR_MAX_THREADS > 0)
7350 num_threads = std::min(num_threads,TINYEXR_MAX_THREADS);
7352 if (num_threads >
int(num_tiles)) {
7353 num_threads = int(num_tiles);
7356 for (
int t = 0; t < num_threads; t++) {
7357 workers.emplace_back(std::thread([&]() {
7359 while ((i = tile_count++) < num_tiles) {
7364#if TINYEXR_USE_OPENMP
7365#pragma omp parallel for
7367 for (
int i = 0; i < num_tiles; i++) {
7370 size_t tile_idx =
static_cast<size_t>(i);
7371 size_t data_idx = tile_idx + start_index;
7373 int x_tile = i % num_x_tiles;
7374 int y_tile = i / num_x_tiles;
7376 EXRTile& tile = level_image->tiles[tile_idx];
7378 const unsigned char*
const* images =
7379 static_cast<const unsigned char* const*
>(tile.images);
7381 data_list[data_idx].resize(5*
sizeof(
int));
7382 size_t data_header_size = data_list[data_idx].size();
7383 bool ret = EncodePixelData(data_list[data_idx],
7385 exr_header->compression_type,
7388 exr_header->tile_size_y,
7389 exr_header->tile_size_x,
7394 channel_offset_list,
7395 err, compression_param);
7397 invalid_data =
true;
7400 if (data_list[data_idx].size() <= data_header_size) {
7401 invalid_data =
true;
7405 int data_len =
static_cast<int>(data_list[data_idx].size() - data_header_size);
7407 memcpy(&data_list[data_idx][0], &x_tile,
sizeof(
int));
7408 memcpy(&data_list[data_idx][4], &y_tile,
sizeof(
int));
7409 memcpy(&data_list[data_idx][8], &level_image->level_x,
sizeof(
int));
7410 memcpy(&data_list[data_idx][12], &level_image->level_y,
sizeof(
int));
7411 memcpy(&data_list[data_idx][16], &data_len,
sizeof(
int));
7413 swap4(
reinterpret_cast<int*
>(&data_list[data_idx][0]));
7414 swap4(
reinterpret_cast<int*
>(&data_list[data_idx][4]));
7415 swap4(
reinterpret_cast<int*
>(&data_list[data_idx][8]));
7416 swap4(
reinterpret_cast<int*
>(&data_list[data_idx][12]));
7417 swap4(
reinterpret_cast<int*
>(&data_list[data_idx][16]));
7419#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7424 for (
auto &t : workers) {
7433 (*err) +=
"Failed to encode tile data.\n";
7435 return TINYEXR_ERROR_INVALID_DATA;
7437 return TINYEXR_SUCCESS;
7440static int NumScanlines(
int compression_type) {
7441 int num_scanlines = 1;
7442 if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
7444 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
7446 }
else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
7449 return num_scanlines;
7453 const std::vector<ChannelInfo>& channels,
7455 tinyexr_uint64 chunk_offset,
7457 OffsetData& offset_data,
7458 std::vector<std::vector<unsigned char> >& data_list,
7459 tinyexr_uint64& total_size,
7461 int num_scanlines = NumScanlines(exr_header->compression_type);
7463 data_list.resize(num_blocks);
7465 std::vector<size_t> channel_offset_list(
7466 static_cast<size_t>(exr_header->num_channels));
7468 int pixel_data_size = 0;
7470 size_t channel_offset = 0;
7471 for (
size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
7472 channel_offset_list[c] = channel_offset;
7473 if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
7474 pixel_data_size +=
sizeof(
unsigned short);
7475 channel_offset +=
sizeof(
unsigned short);
7476 }
else if (channels[c].requested_pixel_type ==
7477 TINYEXR_PIXELTYPE_FLOAT) {
7478 pixel_data_size +=
sizeof(float);
7479 channel_offset +=
sizeof(float);
7480 }
else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_UINT) {
7481 pixel_data_size +=
sizeof(
unsigned int);
7482 channel_offset +=
sizeof(
unsigned int);
7485 (*err) +=
"Invalid requested_pixel_type.\n";
7487 return TINYEXR_ERROR_INVALID_DATA;
7492 const void* compression_param = 0;
7494 tinyexr::ZFPCompressionParam zfp_compression_param;
7500 bool ret = tinyexr::FindZFPCompressionParam(
7501 &zfp_compression_param, exr_header->custom_attributes,
7502 exr_header->num_custom_attributes, &e);
7506 zfp_compression_param.type = 0;
7507 zfp_compression_param.rate = 2;
7509 compression_param = &zfp_compression_param;
7513 tinyexr_uint64 offset = chunk_offset;
7514 tinyexr_uint64 doffset = is_multipart ? 4u : 0u;
7516 if (exr_image->tiles) {
7517 const EXRImage* level_image = exr_image;
7518 size_t block_idx = 0;
7520 int num_levels = (exr_header->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) ?
7521 offset_data.num_x_levels : (offset_data.num_x_levels * offset_data.num_y_levels);
7522 for (
int level_index = 0; level_index < num_levels; ++level_index) {
7525 (*err) +=
"Invalid number of tiled levels for EncodeChunk\n";
7527 return TINYEXR_ERROR_INVALID_DATA;
7530 int level_index_from_image = LevelIndex(level_image->level_x, level_image->level_y,
7531 exr_header->tile_level_mode, offset_data.num_x_levels);
7532 if (level_index_from_image < 0) {
7534 (*err) +=
"Invalid tile level mode\n";
7536 return TINYEXR_ERROR_INVALID_DATA;
7539 if (level_index_from_image != level_index) {
7541 (*err) +=
"Incorrect level ordering in tiled image\n";
7543 return TINYEXR_ERROR_INVALID_DATA;
7545 int num_y_tiles = int(offset_data.offsets[level_index].size());
7546 if (num_y_tiles <= 0) {
7548 (*err) +=
"Invalid Y tile size\n";
7550 return TINYEXR_ERROR_INVALID_DATA;
7553 int num_x_tiles = int(offset_data.offsets[level_index][0].size());
7554 if (num_x_tiles <= 0) {
7556 (*err) +=
"Invalid X tile size\n";
7558 return TINYEXR_ERROR_INVALID_DATA;
7562 int ret = EncodeTiledLevel(level_image,
7569 channel_offset_list,
7573 if (ret != TINYEXR_SUCCESS) {
7574 if (!e.empty() && err) {
7580 for (
size_t j = 0; j < static_cast<size_t>(num_y_tiles); ++j)
7581 for (
size_t i = 0; i < static_cast<size_t>(num_x_tiles); ++i) {
7582 offset_data.offsets[level_index][j][i] = offset;
7583 swap8(
reinterpret_cast<tinyexr_uint64*
>(&offset_data.offsets[level_index][j][i]));
7584 offset += data_list[block_idx].size() + doffset;
7588 level_image = level_image->next_level;
7590 TINYEXR_CHECK_AND_RETURN_C(
static_cast<int>(block_idx) == num_blocks, TINYEXR_ERROR_INVALID_DATA);
7591 total_size = offset;
7593 std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0];
7595#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7596 std::atomic<bool> invalid_data(
false);
7597 std::vector<std::thread> workers;
7598 std::atomic<int> block_count(0);
7600 int num_threads = std::min(std::max(1,
int(std::thread::hardware_concurrency())), num_blocks);
7601#if (TINYEXR_MAX_THREADS > 0)
7602 num_threads = std::min(num_threads,TINYEXR_MAX_THREADS);
7604 for (
int t = 0; t < num_threads; t++) {
7605 workers.emplace_back(std::thread([&]() {
7607 while ((i = block_count++) < num_blocks) {
7610 bool invalid_data(
false);
7611#if TINYEXR_USE_OPENMP
7612#pragma omp parallel for
7614 for (
int i = 0; i < num_blocks; i++) {
7617 int start_y = num_scanlines * i;
7618 int end_Y = (std::min)(num_scanlines * (i + 1), exr_image->height);
7619 int num_lines = end_Y - start_y;
7621 const unsigned char*
const* images =
7622 static_cast<const unsigned char* const*
>(exr_image->images);
7624 data_list[i].resize(2*
sizeof(
int));
7625 size_t data_header_size = data_list[i].size();
7627 bool ret = EncodePixelData(data_list[i],
7629 exr_header->compression_type,
7638 channel_offset_list,
7642 invalid_data =
true;
7645 if (data_list[i].size() <= data_header_size) {
7646 invalid_data =
true;
7649 int data_len =
static_cast<int>(data_list[i].size() - data_header_size);
7650 memcpy(&data_list[i][0], &start_y,
sizeof(
int));
7651 memcpy(&data_list[i][4], &data_len,
sizeof(
int));
7653 swap4(
reinterpret_cast<int*
>(&data_list[i][0]));
7654 swap4(
reinterpret_cast<int*
>(&data_list[i][4]));
7655#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7660 for (
auto &t : workers) {
7669 (*err) +=
"Failed to encode scanline data.\n";
7671 return TINYEXR_ERROR_INVALID_DATA;
7674 for (
size_t i = 0; i < static_cast<size_t>(num_blocks); i++) {
7675 offsets[i] = offset;
7676 tinyexr::swap8(
reinterpret_cast<tinyexr::tinyexr_uint64 *
>(&offsets[i]));
7677 offset += data_list[i].size() + doffset;
7680 total_size =
static_cast<size_t>(offset);
7682 return TINYEXR_SUCCESS;
7686static size_t SaveEXRNPartImageToMemory(
const EXRImage* exr_images,
7688 unsigned int num_parts,
7689 unsigned char** memory_out,
const char** err) {
7690 if (exr_images == NULL || exr_headers == NULL || num_parts == 0 ||
7691 memory_out == NULL) {
7692 SetErrorMessage(
"Invalid argument for SaveEXRNPartImageToMemory",
7697 for (
unsigned int i = 0; i < num_parts; ++i) {
7698 if (exr_headers[i]->compression_type < 0) {
7699 SetErrorMessage(
"Invalid argument for SaveEXRNPartImageToMemory",
7704 if (exr_headers[i]->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
7705 SetErrorMessage(
"PIZ compression is not supported in this build",
7710 if (exr_headers[i]->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
7712 SetErrorMessage(
"ZFP compression is not supported in this build",
7719 for (
int c = 0; c < exr_headers[i]->num_channels; ++c) {
7720 if (exr_headers[i]->requested_pixel_types[c] != TINYEXR_PIXELTYPE_FLOAT) {
7721 SetErrorMessage(
"Pixel type must be FLOAT for ZFP compression",
7731 std::vector<unsigned char> memory;
7735 const char header[] = { 0x76, 0x2f, 0x31, 0x01 };
7736 memory.insert(memory.end(), header, header + 4);
7741 int long_name = exr_headers[0]->long_name;
7743 char marker[] = { 2, 0, 0, 0 };
7750 if (num_parts == 1 && exr_images[0].tiles) {
7758 if (num_parts > 1) {
7761 memory.insert(memory.end(), marker, marker + 4);
7764 int total_chunk_count = 0;
7765 std::vector<int> chunk_count(num_parts);
7766 std::vector<OffsetData> offset_data(num_parts);
7767 for (
unsigned int i = 0; i < num_parts; ++i) {
7768 if (!exr_images[i].tiles) {
7769 int num_scanlines = NumScanlines(exr_headers[i]->compression_type);
7771 (exr_images[i].height + num_scanlines - 1) / num_scanlines;
7772 InitSingleResolutionOffsets(offset_data[i], chunk_count[i]);
7773 total_chunk_count += chunk_count[i];
7776 std::vector<int> num_x_tiles, num_y_tiles;
7777 if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) {
7778 SetErrorMessage(
"Failed to precalculate Tile info",
7780 return TINYEXR_ERROR_INVALID_DATA;
7782 int ntiles = InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles);
7784 chunk_count[i] = ntiles;
7786 SetErrorMessage(
"Failed to compute Tile offsets",
7788 return TINYEXR_ERROR_INVALID_DATA;
7791 total_chunk_count += chunk_count[i];
7796 std::vector< std::vector<tinyexr::ChannelInfo> > channels(num_parts);
7798 std::set<std::string> partnames;
7799 for (
unsigned int i = 0; i < num_parts; ++i) {
7802 std::vector<unsigned char> data;
7804 for (
int c = 0; c < exr_headers[i]->num_channels; c++) {
7805 tinyexr::ChannelInfo info;
7807 info.pixel_type = exr_headers[i]->pixel_types[c];
7808 info.requested_pixel_type = exr_headers[i]->requested_pixel_types[c];
7809 info.x_sampling = 1;
7810 info.y_sampling = 1;
7811 info.name = std::string(exr_headers[i]->channels[c].name);
7812 channels[i].push_back(info);
7815 tinyexr::WriteChannelInfo(data, channels[i]);
7817 tinyexr::WriteAttributeToMemory(&memory,
"channels",
"chlist", &data.at(0),
7818 static_cast<int>(data.size()));
7822 int comp = exr_headers[i]->compression_type;
7824 WriteAttributeToMemory(
7825 &memory,
"compression",
"compression",
7826 reinterpret_cast<const unsigned char*
>(&comp), 1);
7830 int data[4] = { 0, 0, exr_images[i].width - 1, exr_images[i].height - 1 };
7835 WriteAttributeToMemory(
7836 &memory,
"dataWindow",
"box2i",
7837 reinterpret_cast<const unsigned char*
>(data),
sizeof(
int) * 4);
7839 int data0[4] = { 0, 0, exr_images[0].width - 1, exr_images[0].height - 1 };
7845 WriteAttributeToMemory(
7846 &memory,
"displayWindow",
"box2i",
7847 reinterpret_cast<const unsigned char*
>(data0),
sizeof(
int) * 4);
7851 unsigned char line_order = 0;
7852 WriteAttributeToMemory(&memory,
"lineOrder",
"lineOrder",
7858 float aspectRatio = 1.0f;
7859 swap4(&aspectRatio);
7860 WriteAttributeToMemory(
7861 &memory,
"pixelAspectRatio",
"float",
7862 reinterpret_cast<const unsigned char*
>(&aspectRatio),
sizeof(
float));
7866 float center[2] = { 0.0f, 0.0f };
7869 WriteAttributeToMemory(
7870 &memory,
"screenWindowCenter",
"v2f",
7871 reinterpret_cast<const unsigned char*
>(center), 2 *
sizeof(
float));
7877 WriteAttributeToMemory(&memory,
"screenWindowWidth",
"float",
7878 reinterpret_cast<const unsigned char*
>(&w),
7882 if (exr_images[i].tiles) {
7883 unsigned char tile_mode =
static_cast<unsigned char>(exr_headers[i]->tile_level_mode & 0x3);
7884 if (exr_headers[i]->tile_rounding_mode) tile_mode |= (1u << 4u);
7886 unsigned int datai[3] = { 0, 0, 0 };
7887 unsigned char* data =
reinterpret_cast<unsigned char*
>(&datai[0]);
7888 datai[0] =
static_cast<unsigned int>(exr_headers[i]->tile_size_x);
7889 datai[1] =
static_cast<unsigned int>(exr_headers[i]->tile_size_y);
7890 data[8] = tile_mode;
7891 swap4(
reinterpret_cast<unsigned int*
>(&data[0]));
7892 swap4(
reinterpret_cast<unsigned int*
>(&data[4]));
7893 WriteAttributeToMemory(
7894 &memory,
"tiles",
"tiledesc",
7895 reinterpret_cast<const unsigned char*
>(data), 9);
7899 if (num_parts > 1) {
7903 if ((len = strlen(exr_headers[i]->name)) > 0) {
7904#if TINYEXR_HAS_CXX11
7905 partnames.emplace(exr_headers[i]->name);
7907 partnames.insert(std::string(exr_headers[i]->name));
7909 if (partnames.size() != i + 1) {
7910 SetErrorMessage(
"'name' attributes must be unique for a multi-part file", err);
7913 WriteAttributeToMemory(
7914 &memory,
"name",
"string",
7915 reinterpret_cast<const unsigned char*
>(exr_headers[i]->name),
7916 static_cast<int>(len));
7918 SetErrorMessage(
"Invalid 'name' attribute for a multi-part file", err);
7924 const char* type =
"scanlineimage";
7925 if (exr_images[i].tiles) type =
"tiledimage";
7926 WriteAttributeToMemory(
7927 &memory,
"type",
"string",
7928 reinterpret_cast<const unsigned char*
>(type),
7929 static_cast<int>(strlen(type)));
7933 WriteAttributeToMemory(
7934 &memory,
"chunkCount",
"int",
7935 reinterpret_cast<const unsigned char*
>(&chunk_count[i]),
7941 if (exr_headers[i]->num_custom_attributes > 0) {
7942 for (
int j = 0; j < exr_headers[i]->num_custom_attributes; j++) {
7943 tinyexr::WriteAttributeToMemory(
7944 &memory, exr_headers[i]->custom_attributes[j].name,
7945 exr_headers[i]->custom_attributes[j].type,
7946 reinterpret_cast<const unsigned char*
>(
7947 exr_headers[i]->custom_attributes[j].value),
7948 exr_headers[i]->custom_attributes[j].size);
7953 memory.push_back(0);
7957 if (num_parts > 1) {
7959 memory.push_back(0);
7962 tinyexr_uint64 chunk_offset = memory.size() + size_t(total_chunk_count) *
sizeof(tinyexr_uint64);
7964 tinyexr_uint64 total_size = 0;
7965 std::vector< std::vector< std::vector<unsigned char> > > data_lists(num_parts);
7966 for (
unsigned int i = 0; i < num_parts; ++i) {
7968 int ret = EncodeChunk(&exr_images[i], exr_headers[i],
7978 if (ret != TINYEXR_SUCCESS) {
7980 tinyexr::SetErrorMessage(e, err);
7984 chunk_offset = total_size;
7988 if (total_size == 0) {
7989 tinyexr::SetErrorMessage(
"Output memory size is zero", err);
7990 return TINYEXR_ERROR_INVALID_DATA;
7992 (*memory_out) =
static_cast<unsigned char*
>(malloc(
size_t(total_size)));
7995 memcpy((*memory_out), &memory[0], memory.size());
7996 unsigned char* memory_ptr = *memory_out + memory.size();
7997 size_t sum = memory.size();
8000 for (
unsigned int i = 0; i < num_parts; ++i) {
8001 if (exr_images[i].tiles) {
8002 const EXRImage* level_image = &exr_images[i];
8003 int num_levels = (exr_headers[i]->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) ?
8004 offset_data[i].num_x_levels : (offset_data[i].num_x_levels * offset_data[i].num_y_levels);
8005 for (
int level_index = 0; level_index < num_levels; ++level_index) {
8006 for (
size_t j = 0; j < offset_data[i].offsets[level_index].size(); ++j) {
8007 size_t num_bytes =
sizeof(tinyexr_uint64) * offset_data[i].offsets[level_index][j].size();
8009 if (sum > total_size) {
8010 tinyexr::SetErrorMessage(
"Invalid offset bytes in Tiled Part image.", err);
8011 return TINYEXR_ERROR_INVALID_DATA;
8015 reinterpret_cast<unsigned char*
>(&offset_data[i].offsets[level_index][j][0]),
8017 memory_ptr += num_bytes;
8019 level_image = level_image->next_level;
8022 size_t num_bytes =
sizeof(tinyexr::tinyexr_uint64) *
static_cast<size_t>(chunk_count[i]);
8024 if (sum > total_size) {
8025 tinyexr::SetErrorMessage(
"Invalid offset bytes in Part image.", err);
8026 return TINYEXR_ERROR_INVALID_DATA;
8028 std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data[i].offsets[0][0];
8029 memcpy(memory_ptr,
reinterpret_cast<unsigned char*
>(&offsets[0]), num_bytes);
8030 memory_ptr += num_bytes;
8035 for (
unsigned int i = 0; i < num_parts; ++i) {
8036 for (
size_t j = 0; j < static_cast<size_t>(chunk_count[i]); ++j) {
8037 if (num_parts > 1) {
8039 if (sum > total_size) {
8040 tinyexr::SetErrorMessage(
"Buffer overrun in reading Part image chunk data.", err);
8041 return TINYEXR_ERROR_INVALID_DATA;
8043 unsigned int part_number = i;
8044 swap4(&part_number);
8045 memcpy(memory_ptr, &part_number, 4);
8048 sum += data_lists[i][j].size();
8049 if (sum > total_size) {
8050 tinyexr::SetErrorMessage(
"Buffer overrun in reading Part image chunk data.", err);
8051 return TINYEXR_ERROR_INVALID_DATA;
8053 memcpy(memory_ptr, &data_lists[i][j][0], data_lists[i][j].size());
8054 memory_ptr += data_lists[i][j].size();
8058 if (sum != total_size) {
8059 tinyexr::SetErrorMessage(
"Corrupted Part image chunk data.", err);
8060 return TINYEXR_ERROR_INVALID_DATA;
8063 return size_t(total_size);
8067#pragma clang diagnostic pop
8072size_t SaveEXRImageToMemory(
const EXRImage* exr_image,
8074 unsigned char** memory_out,
const char** err) {
8075 return tinyexr::SaveEXRNPartImageToMemory(exr_image, &exr_header, 1, memory_out, err);
8079 const char *filename,
const char **err) {
8080 if (exr_image == NULL || filename == NULL ||
8081 exr_header->compression_type < 0) {
8082 tinyexr::SetErrorMessage(
"Invalid argument for SaveEXRImageToFile", err);
8083 return TINYEXR_ERROR_INVALID_ARGUMENT;
8087 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
8088 tinyexr::SetErrorMessage(
"PIZ compression is not supported in this build",
8090 return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
8095 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
8096 tinyexr::SetErrorMessage(
"ZFP compression is not supported in this build",
8098 return TINYEXR_ERROR_UNSUPPORTED_FEATURE;
8104#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API)
8106 _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L
"wb");
8108 tinyexr::SetErrorMessage(
"Cannot write a file: " + std::string(filename),
8110 return TINYEXR_ERROR_CANT_WRITE_FILE;
8114 fp = fopen(filename,
"wb");
8117 fp = fopen(filename,
"wb");
8120 tinyexr::SetErrorMessage(
"Cannot write a file: " + std::string(filename),
8122 return TINYEXR_ERROR_CANT_WRITE_FILE;
8125 unsigned char *mem = NULL;
8126 size_t mem_size = SaveEXRImageToMemory(exr_image, exr_header, &mem, err);
8127 if (mem_size == 0) {
8129 return TINYEXR_ERROR_SERIALIZATION_FAILED;
8132 size_t written_size = 0;
8133 if ((mem_size > 0) && mem) {
8134 written_size = fwrite(mem, 1, mem_size, fp);
8140 if (written_size != mem_size) {
8141 tinyexr::SetErrorMessage(
"Cannot write a file", err);
8142 return TINYEXR_ERROR_CANT_WRITE_FILE;
8145 return TINYEXR_SUCCESS;
8148size_t SaveEXRMultipartImageToMemory(
const EXRImage* exr_images,
8150 unsigned int num_parts,
8151 unsigned char** memory_out,
const char** err) {
8152 if (exr_images == NULL || exr_headers == NULL || num_parts < 2 ||
8153 memory_out == NULL) {
8154 tinyexr::SetErrorMessage(
"Invalid argument for SaveEXRNPartImageToMemory",
8158 return tinyexr::SaveEXRNPartImageToMemory(exr_images, exr_headers, num_parts, memory_out, err);
8161int SaveEXRMultipartImageToFile(
const EXRImage* exr_images,
8163 unsigned int num_parts,
8164 const char* filename,
8166 if (exr_images == NULL || exr_headers == NULL || num_parts < 2) {
8167 tinyexr::SetErrorMessage(
"Invalid argument for SaveEXRMultipartImageToFile",
8169 return TINYEXR_ERROR_INVALID_ARGUMENT;
8174#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API)
8176 _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L
"wb");
8178 tinyexr::SetErrorMessage(
"Cannot write a file: " + std::string(filename),
8180 return TINYEXR_ERROR_CANT_WRITE_FILE;
8184 fp = fopen(filename,
"wb");
8187 fp = fopen(filename,
"wb");
8190 tinyexr::SetErrorMessage(
"Cannot write a file: " + std::string(filename),
8192 return TINYEXR_ERROR_CANT_WRITE_FILE;
8195 unsigned char *mem = NULL;
8196 size_t mem_size = SaveEXRMultipartImageToMemory(exr_images, exr_headers, num_parts, &mem, err);
8197 if (mem_size == 0) {
8199 return TINYEXR_ERROR_SERIALIZATION_FAILED;
8202 size_t written_size = 0;
8203 if ((mem_size > 0) && mem) {
8204 written_size = fwrite(mem, 1, mem_size, fp);
8210 if (written_size != mem_size) {
8211 tinyexr::SetErrorMessage(
"Cannot write a file", err);
8212 return TINYEXR_ERROR_CANT_WRITE_FILE;
8215 return TINYEXR_SUCCESS;
8218int LoadDeepEXR(
DeepImage *deep_image,
const char *filename,
const char **err) {
8219 if (deep_image == NULL) {
8220 tinyexr::SetErrorMessage(
"Invalid argument for LoadDeepEXR", err);
8221 return TINYEXR_ERROR_INVALID_ARGUMENT;
8224 MemoryMappedFile file(filename);
8225 if (!file.valid()) {
8226 tinyexr::SetErrorMessage(
"Cannot read file " + std::string(filename), err);
8227 return TINYEXR_ERROR_CANT_OPEN_FILE;
8230 if (file.size == 0) {
8231 tinyexr::SetErrorMessage(
"File size is zero : " + std::string(filename),
8233 return TINYEXR_ERROR_INVALID_FILE;
8236 const char *head =
reinterpret_cast<const char *
>(file.data);
8237 const char *marker =
reinterpret_cast<const char *
>(file.data);
8241 const char header[] = {0x76, 0x2f, 0x31, 0x01};
8243 if (memcmp(marker, header, 4) != 0) {
8244 tinyexr::SetErrorMessage(
"Invalid magic number", err);
8245 return TINYEXR_ERROR_INVALID_MAGIC_NUMBER;
8254 if (marker[0] != 2 || marker[1] != 8 || marker[2] != 0 || marker[3] != 0) {
8255 tinyexr::SetErrorMessage(
"Unsupported version or scanline", err);
8256 return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
8266 int num_scanline_blocks = 1;
8267 int compression_type = -1;
8268 int num_channels = -1;
8269 std::vector<tinyexr::ChannelInfo> channels;
8272 size_t size = file.size - tinyexr::kEXRVersionSize;
8275 return TINYEXR_ERROR_INVALID_DATA;
8276 }
else if (marker[0] ==
'\0') {
8282 std::string attr_name;
8283 std::string attr_type;
8284 std::vector<unsigned char> data;
8286 if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size,
8288 std::stringstream ss;
8289 ss <<
"Failed to parse attribute\n";
8290 tinyexr::SetErrorMessage(ss.str(), err);
8291 return TINYEXR_ERROR_INVALID_DATA;
8293 marker += marker_size;
8294 size -= marker_size;
8296 if (attr_name.compare(
"compression") == 0) {
8297 compression_type = data[0];
8298 if (compression_type > TINYEXR_COMPRESSIONTYPE_PIZ) {
8299 std::stringstream ss;
8300 ss <<
"Unsupported compression type : " << compression_type;
8301 tinyexr::SetErrorMessage(ss.str(), err);
8302 return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
8305 if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
8306 num_scanline_blocks = 16;
8309 }
else if (attr_name.compare(
"channels") == 0) {
8317 if (!tinyexr::ReadChannelInfo(channels, data)) {
8318 tinyexr::SetErrorMessage(
"Failed to parse channel info", err);
8319 return TINYEXR_ERROR_INVALID_DATA;
8322 num_channels =
static_cast<int>(channels.size());
8324 if (num_channels < 1) {
8325 tinyexr::SetErrorMessage(
"Invalid channels format", err);
8326 return TINYEXR_ERROR_INVALID_DATA;
8329 }
else if (attr_name.compare(
"dataWindow") == 0) {
8330 memcpy(&dx, &data.at(0),
sizeof(
int));
8331 memcpy(&dy, &data.at(4),
sizeof(
int));
8332 memcpy(&dw, &data.at(8),
sizeof(
int));
8333 memcpy(&dh, &data.at(12),
sizeof(
int));
8334 tinyexr::swap4(&dx);
8335 tinyexr::swap4(&dy);
8336 tinyexr::swap4(&dw);
8337 tinyexr::swap4(&dh);
8339 }
else if (attr_name.compare(
"displayWindow") == 0) {
8344 memcpy(&x, &data.at(0),
sizeof(
int));
8345 memcpy(&y, &data.at(4),
sizeof(
int));
8346 memcpy(&w, &data.at(8),
sizeof(
int));
8347 memcpy(&h, &data.at(12),
sizeof(
int));
8355 TINYEXR_CHECK_AND_RETURN_C(dx >= 0, TINYEXR_ERROR_INVALID_DATA);
8356 TINYEXR_CHECK_AND_RETURN_C(dy >= 0, TINYEXR_ERROR_INVALID_DATA);
8357 TINYEXR_CHECK_AND_RETURN_C(dw >= 0, TINYEXR_ERROR_INVALID_DATA);
8358 TINYEXR_CHECK_AND_RETURN_C(dh >= 0, TINYEXR_ERROR_INVALID_DATA);
8359 TINYEXR_CHECK_AND_RETURN_C(num_channels >= 1, TINYEXR_ERROR_INVALID_DATA);
8361 int data_width = dw - dx + 1;
8362 int data_height = dh - dy + 1;
8365 int num_blocks = data_height / num_scanline_blocks;
8366 if (num_blocks * num_scanline_blocks < data_height) {
8370 std::vector<tinyexr::tinyexr_int64> offsets(
static_cast<size_t>(num_blocks));
8372 for (
size_t y = 0; y < static_cast<size_t>(num_blocks); y++) {
8373 tinyexr::tinyexr_int64 offset;
8374 memcpy(&offset, marker,
sizeof(tinyexr::tinyexr_int64));
8375 tinyexr::swap8(
reinterpret_cast<tinyexr::tinyexr_uint64 *
>(&offset));
8376 marker +=
sizeof(tinyexr::tinyexr_int64);
8377 offsets[y] = offset;
8381 if ((compression_type == TINYEXR_COMPRESSIONTYPE_NONE) ||
8382 (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) ||
8383 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
8384 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) ||
8385 (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ)) {
8387 if ((compression_type == TINYEXR_COMPRESSIONTYPE_NONE) ||
8388 (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) ||
8389 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
8390 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) {
8394 tinyexr::SetErrorMessage(
"Unsupported compression format", err);
8395 return TINYEXR_ERROR_UNSUPPORTED_FORMAT;
8398 deep_image->image =
static_cast<float ***
>(
8399 malloc(
sizeof(
float **) *
static_cast<size_t>(num_channels)));
8400 for (
int c = 0; c < num_channels; c++) {
8401 deep_image->image[c] =
static_cast<float **
>(
8402 malloc(
sizeof(
float *) *
static_cast<size_t>(data_height)));
8403 for (
int y = 0; y < data_height; y++) {
8407 deep_image->offset_table =
static_cast<int **
>(
8408 malloc(
sizeof(
int *) *
static_cast<size_t>(data_height)));
8409 for (
int y = 0; y < data_height; y++) {
8410 deep_image->offset_table[y] =
static_cast<int *
>(
8411 malloc(
sizeof(
int) *
static_cast<size_t>(data_width)));
8414 for (
size_t y = 0; y < static_cast<size_t>(num_blocks); y++) {
8415 const unsigned char *data_ptr =
8416 reinterpret_cast<const unsigned char *
>(head + offsets[y]);
8425 tinyexr::tinyexr_int64 packedOffsetTableSize;
8426 tinyexr::tinyexr_int64 packedSampleDataSize;
8427 tinyexr::tinyexr_int64 unpackedSampleDataSize;
8428 memcpy(&line_no, data_ptr,
sizeof(
int));
8429 memcpy(&packedOffsetTableSize, data_ptr + 4,
8430 sizeof(tinyexr::tinyexr_int64));
8431 memcpy(&packedSampleDataSize, data_ptr + 12,
8432 sizeof(tinyexr::tinyexr_int64));
8433 memcpy(&unpackedSampleDataSize, data_ptr + 20,
8434 sizeof(tinyexr::tinyexr_int64));
8436 tinyexr::swap4(&line_no);
8438 reinterpret_cast<tinyexr::tinyexr_uint64 *
>(&packedOffsetTableSize));
8440 reinterpret_cast<tinyexr::tinyexr_uint64 *
>(&packedSampleDataSize));
8442 reinterpret_cast<tinyexr::tinyexr_uint64 *
>(&unpackedSampleDataSize));
8444 std::vector<int> pixelOffsetTable(
static_cast<size_t>(data_width));
8448 unsigned long dstLen =
8449 static_cast<unsigned long>(pixelOffsetTable.size() *
sizeof(int));
8450 if (!tinyexr::DecompressZip(
8451 reinterpret_cast<unsigned char *
>(&pixelOffsetTable.at(0)),
8452 &dstLen, data_ptr + 28,
8453 static_cast<unsigned long>(packedOffsetTableSize))) {
8457 TINYEXR_CHECK_AND_RETURN_C(dstLen == pixelOffsetTable.size() *
sizeof(
int), TINYEXR_ERROR_INVALID_DATA);
8458 for (
size_t i = 0; i < static_cast<size_t>(data_width); i++) {
8459 deep_image->offset_table[y][i] = pixelOffsetTable[i];
8463 std::vector<unsigned char> sample_data(
8464 static_cast<size_t>(unpackedSampleDataSize));
8468 unsigned long dstLen =
static_cast<unsigned long>(unpackedSampleDataSize);
8470 if (!tinyexr::DecompressZip(
8471 reinterpret_cast<unsigned char *
>(&sample_data.at(0)), &dstLen,
8472 data_ptr + 28 + packedOffsetTableSize,
8473 static_cast<unsigned long>(packedSampleDataSize))) {
8476 TINYEXR_CHECK_AND_RETURN_C(dstLen ==
static_cast<unsigned long>(unpackedSampleDataSize), TINYEXR_ERROR_INVALID_DATA);
8481 int sampleSize = -1;
8482 std::vector<int> channel_offset_list(
static_cast<size_t>(num_channels));
8484 int channel_offset = 0;
8485 for (
size_t i = 0; i < static_cast<size_t>(num_channels); i++) {
8486 channel_offset_list[i] = channel_offset;
8487 if (channels[i].pixel_type == TINYEXR_PIXELTYPE_UINT) {
8488 channel_offset += 4;
8489 }
else if (channels[i].pixel_type == TINYEXR_PIXELTYPE_HALF) {
8490 channel_offset += 2;
8491 }
else if (channels[i].pixel_type ==
8492 TINYEXR_PIXELTYPE_FLOAT) {
8493 channel_offset += 4;
8495 tinyexr::SetErrorMessage(
"Invalid pixel_type in chnnels.", err);
8496 return TINYEXR_ERROR_INVALID_DATA;
8499 sampleSize = channel_offset;
8501 TINYEXR_CHECK_AND_RETURN_C(sampleSize >= 2, TINYEXR_ERROR_INVALID_DATA);
8503 TINYEXR_CHECK_AND_RETURN_C(
static_cast<size_t>(
8504 pixelOffsetTable[
static_cast<size_t>(data_width - 1)] *
8505 sampleSize) == sample_data.size(), TINYEXR_ERROR_INVALID_DATA);
8506 int samples_per_line =
static_cast<int>(sample_data.size()) / sampleSize;
8516 tinyexr::tinyexr_uint64 data_offset = 0;
8517 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
8518 deep_image->image[c][y] =
static_cast<float *
>(
8519 malloc(
sizeof(
float) *
static_cast<size_t>(samples_per_line)));
8521 if (channels[c].pixel_type == 0) {
8522 for (
size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
8524 unsigned int *src_ptr =
reinterpret_cast<unsigned int *
>(
8525 &sample_data.at(
size_t(data_offset) + x *
sizeof(int)));
8526 tinyexr::cpy4(&ui, src_ptr);
8527 deep_image->image[c][y][x] =
static_cast<float>(ui);
8530 sizeof(
unsigned int) *
static_cast<size_t>(samples_per_line);
8531 }
else if (channels[c].pixel_type == 1) {
8532 for (
size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
8534 const unsigned short *src_ptr =
reinterpret_cast<unsigned short *
>(
8535 &sample_data.at(
size_t(data_offset) + x *
sizeof(short)));
8536 tinyexr::cpy2(&(f16.u), src_ptr);
8537 tinyexr::FP32
f32 = half_to_float(f16);
8538 deep_image->image[c][y][x] =
f32.f;
8540 data_offset +=
sizeof(short) *
static_cast<size_t>(samples_per_line);
8542 for (
size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
8544 const float *src_ptr =
reinterpret_cast<float *
>(
8545 &sample_data.at(
size_t(data_offset) + x *
sizeof(float)));
8546 tinyexr::cpy4(&f, src_ptr);
8547 deep_image->image[c][y][x] = f;
8549 data_offset +=
sizeof(float) *
static_cast<size_t>(samples_per_line);
8555 deep_image->width = data_width;
8556 deep_image->height = data_height;
8558 deep_image->channel_names =
static_cast<const char **
>(
8559 malloc(
sizeof(
const char *) *
static_cast<size_t>(num_channels)));
8560 for (
size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
8562 deep_image->channel_names[c] = _strdup(channels[c].name.c_str());
8564 deep_image->channel_names[c] = strdup(channels[c].name.c_str());
8567 deep_image->num_channels = num_channels;
8569 return TINYEXR_SUCCESS;
8572void InitEXRImage(
EXRImage *exr_image) {
8573 if (exr_image == NULL) {
8577 exr_image->width = 0;
8578 exr_image->height = 0;
8579 exr_image->num_channels = 0;
8581 exr_image->images = NULL;
8582 exr_image->tiles = NULL;
8583 exr_image->next_level = NULL;
8584 exr_image->level_x = 0;
8585 exr_image->level_y = 0;
8587 exr_image->num_tiles = 0;
8590void FreeEXRErrorMessage(
const char *msg) {
8592 free(
reinterpret_cast<void *
>(
const_cast<char *
>(msg)));
8597void InitEXRHeader(
EXRHeader *exr_header) {
8598 if (exr_header == NULL) {
8602 memset(exr_header, 0,
sizeof(
EXRHeader));
8605int FreeEXRHeader(
EXRHeader *exr_header) {
8606 if (exr_header == NULL) {
8607 return TINYEXR_ERROR_INVALID_ARGUMENT;
8610 if (exr_header->channels) {
8611 free(exr_header->channels);
8614 if (exr_header->pixel_types) {
8615 free(exr_header->pixel_types);
8618 if (exr_header->requested_pixel_types) {
8619 free(exr_header->requested_pixel_types);
8622 for (
int i = 0; i < exr_header->num_custom_attributes; i++) {
8623 if (exr_header->custom_attributes[i].value) {
8624 free(exr_header->custom_attributes[i].value);
8628 if (exr_header->custom_attributes) {
8629 free(exr_header->custom_attributes);
8632 EXRSetNameAttr(exr_header, NULL);
8634 return TINYEXR_SUCCESS;
8637void EXRSetNameAttr(
EXRHeader* exr_header,
const char* name) {
8638 if (exr_header == NULL) {
8641 memset(exr_header->name, 0, 256);
8643 size_t len = std::min(strlen(name),
size_t(255));
8645 memcpy(exr_header->name, name, len);
8650int EXRNumLevels(
const EXRImage* exr_image) {
8651 if (exr_image == NULL)
return 0;
8652 if(exr_image->images)
return 1;
8654 const EXRImage* level_image = exr_image;
8655 while((level_image = level_image->next_level)) ++
levels;
8659int FreeEXRImage(
EXRImage *exr_image) {
8660 if (exr_image == NULL) {
8661 return TINYEXR_ERROR_INVALID_ARGUMENT;
8664 if (exr_image->next_level) {
8665 FreeEXRImage(exr_image->next_level);
8666 delete exr_image->next_level;
8669 for (
int i = 0; i < exr_image->num_channels; i++) {
8670 if (exr_image->images && exr_image->images[i]) {
8671 free(exr_image->images[i]);
8675 if (exr_image->images) {
8676 free(exr_image->images);
8679 if (exr_image->tiles) {
8680 for (
int tid = 0; tid < exr_image->num_tiles; tid++) {
8681 for (
int i = 0; i < exr_image->num_channels; i++) {
8682 if (exr_image->tiles[tid].images && exr_image->tiles[tid].images[i]) {
8683 free(exr_image->tiles[tid].images[i]);
8686 if (exr_image->tiles[tid].images) {
8687 free(exr_image->tiles[tid].images);
8690 free(exr_image->tiles);
8693 return TINYEXR_SUCCESS;
8697 const char *filename,
const char **err) {
8698 if (exr_header == NULL || exr_version == NULL || filename == NULL) {
8699 tinyexr::SetErrorMessage(
"Invalid argument for ParseEXRHeaderFromFile",
8701 return TINYEXR_ERROR_INVALID_ARGUMENT;
8704 MemoryMappedFile file(filename);
8705 if (!file.valid()) {
8706 tinyexr::SetErrorMessage(
"Cannot read file " + std::string(filename), err);
8707 return TINYEXR_ERROR_CANT_OPEN_FILE;
8710 return ParseEXRHeaderFromMemory(exr_header, exr_version, file.data, file.size,
8714int ParseEXRMultipartHeaderFromMemory(
EXRHeader ***exr_headers,
8717 const unsigned char *memory,
size_t size,
8719 if (memory == NULL || exr_headers == NULL || num_headers == NULL ||
8720 exr_version == NULL) {
8722 tinyexr::SetErrorMessage(
8723 "Invalid argument for ParseEXRMultipartHeaderFromMemory", err);
8724 return TINYEXR_ERROR_INVALID_ARGUMENT;
8727 if (size < tinyexr::kEXRVersionSize) {
8728 tinyexr::SetErrorMessage(
"Data size too short", err);
8729 return TINYEXR_ERROR_INVALID_DATA;
8732 const unsigned char *marker = memory + tinyexr::kEXRVersionSize;
8733 size_t marker_size = size - tinyexr::kEXRVersionSize;
8735 std::vector<tinyexr::HeaderInfo> infos;
8738 tinyexr::HeaderInfo info;
8741 std::string err_str;
8742 bool empty_header =
false;
8743 int ret = ParseEXRHeader(&info, &empty_header, exr_version, &err_str,
8744 marker, marker_size);
8746 if (ret != TINYEXR_SUCCESS) {
8749 for (
size_t i = 0; i < info.attributes.size(); i++) {
8750 if (info.attributes[i].value) {
8751 free(info.attributes[i].value);
8755 tinyexr::SetErrorMessage(err_str, err);
8765 if (info.chunk_count == 0) {
8768 for (
size_t i = 0; i < info.attributes.size(); i++) {
8769 if (info.attributes[i].value) {
8770 free(info.attributes[i].value);
8774 tinyexr::SetErrorMessage(
8775 "`chunkCount' attribute is not found in the header.", err);
8776 return TINYEXR_ERROR_INVALID_DATA;
8779 infos.push_back(info);
8782 marker += info.header_len;
8783 size -= info.header_len;
8791 int retcode = TINYEXR_SUCCESS;
8793 for (
size_t i = 0; i < infos.size(); i++) {
8795 memset(exr_header, 0,
sizeof(
EXRHeader));
8799 if (!ConvertHeader(exr_header, infos[i], &warn, &_err)) {
8802 for (
size_t k = 0; k < infos[i].attributes.size(); k++) {
8803 if (infos[i].attributes[k].value) {
8804 free(infos[i].attributes[k].value);
8808 if (!_err.empty()) {
8809 tinyexr::SetErrorMessage(
8813 retcode = TINYEXR_ERROR_INVALID_HEADER;
8816 exr_header->multipart = exr_version->multipart ? 1 : 0;
8818 (*exr_headers)[i] = exr_header;
8821 (*num_headers) =
static_cast<int>(infos.size());
8826int ParseEXRMultipartHeaderFromFile(
EXRHeader ***exr_headers,
int *num_headers,
8828 const char *filename,
const char **err) {
8829 if (exr_headers == NULL || num_headers == NULL || exr_version == NULL ||
8831 tinyexr::SetErrorMessage(
8832 "Invalid argument for ParseEXRMultipartHeaderFromFile()", err);
8833 return TINYEXR_ERROR_INVALID_ARGUMENT;
8836 MemoryMappedFile file(filename);
8837 if (!file.valid()) {
8838 tinyexr::SetErrorMessage(
"Cannot read file " + std::string(filename), err);
8839 return TINYEXR_ERROR_CANT_OPEN_FILE;
8842 return ParseEXRMultipartHeaderFromMemory(
8843 exr_headers, num_headers, exr_version, file.data, file.size, err);
8846int ParseEXRVersionFromMemory(
EXRVersion *version,
const unsigned char *memory,
8848 if (version == NULL || memory == NULL) {
8849 return TINYEXR_ERROR_INVALID_ARGUMENT;
8852 if (size < tinyexr::kEXRVersionSize) {
8853 return TINYEXR_ERROR_INVALID_DATA;
8856 const unsigned char *marker = memory;
8860 const char header[] = {0x76, 0x2f, 0x31, 0x01};
8862 if (memcmp(marker, header, 4) != 0) {
8863 return TINYEXR_ERROR_INVALID_MAGIC_NUMBER;
8868 version->tiled =
false;
8869 version->long_name =
false;
8870 version->non_image =
false;
8871 version->multipart =
false;
8876 if (marker[0] != 2) {
8877 return TINYEXR_ERROR_INVALID_EXR_VERSION;
8880 if (version == NULL) {
8881 return TINYEXR_SUCCESS;
8884 version->version = 2;
8886 if (marker[1] & 0x2) {
8887 version->tiled =
true;
8889 if (marker[1] & 0x4) {
8890 version->long_name =
true;
8892 if (marker[1] & 0x8) {
8893 version->non_image =
true;
8895 if (marker[1] & 0x10) {
8896 version->multipart =
true;
8900 return TINYEXR_SUCCESS;
8903int ParseEXRVersionFromFile(
EXRVersion *version,
const char *filename) {
8904 if (filename == NULL) {
8905 return TINYEXR_ERROR_INVALID_ARGUMENT;
8910#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API)
8911 errno_t err = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L
"rb");
8914 return TINYEXR_ERROR_CANT_OPEN_FILE;
8918 fp = fopen(filename,
"rb");
8921 fp = fopen(filename,
"rb");
8924 return TINYEXR_ERROR_CANT_OPEN_FILE;
8931 unsigned char buf[tinyexr::kEXRVersionSize];
8932 size_t ret = fread(&buf[0], 1, tinyexr::kEXRVersionSize, fp);
8935 if (ret != tinyexr::kEXRVersionSize) {
8936 return TINYEXR_ERROR_INVALID_FILE;
8939 return ParseEXRVersionFromMemory(version, buf, tinyexr::kEXRVersionSize);
8942int LoadEXRMultipartImageFromMemory(
EXRImage *exr_images,
8944 unsigned int num_parts,
8945 const unsigned char *memory,
8946 const size_t size,
const char **err) {
8947 if (exr_images == NULL || exr_headers == NULL || num_parts == 0 ||
8948 memory == NULL || (size <= tinyexr::kEXRVersionSize)) {
8949 tinyexr::SetErrorMessage(
8950 "Invalid argument for LoadEXRMultipartImageFromMemory()", err);
8951 return TINYEXR_ERROR_INVALID_ARGUMENT;
8955 size_t total_header_size = 0;
8956 for (
unsigned int i = 0; i < num_parts; i++) {
8957 if (exr_headers[i]->header_len == 0) {
8958 tinyexr::SetErrorMessage(
"EXRHeader variable is not initialized.", err);
8959 return TINYEXR_ERROR_INVALID_ARGUMENT;
8962 total_header_size += exr_headers[i]->header_len;
8965 const char *marker =
reinterpret_cast<const char *
>(
8966 memory + total_header_size + 4 +
8982 std::vector<tinyexr::OffsetData> chunk_offset_table_list;
8983 chunk_offset_table_list.reserve(num_parts);
8984 for (
size_t i = 0; i < static_cast<size_t>(num_parts); i++) {
8985 chunk_offset_table_list.resize(chunk_offset_table_list.size() + 1);
8986 tinyexr::OffsetData& offset_data = chunk_offset_table_list.back();
8987 if (!exr_headers[i]->tiled || exr_headers[i]->tile_level_mode == TINYEXR_TILE_ONE_LEVEL) {
8988 tinyexr::InitSingleResolutionOffsets(offset_data,
size_t(exr_headers[i]->chunk_count));
8989 std::vector<tinyexr::tinyexr_uint64>& offset_table = offset_data.offsets[0][0];
8991 for (
size_t c = 0; c < offset_table.size(); c++) {
8992 tinyexr::tinyexr_uint64 offset;
8993 memcpy(&offset, marker, 8);
8994 tinyexr::swap8(&offset);
8996 if (offset >= size) {
8997 tinyexr::SetErrorMessage(
"Invalid offset size in EXR header chunks.",
8999 return TINYEXR_ERROR_INVALID_DATA;
9002 offset_table[c] = offset + 4;
9007 std::vector<int> num_x_tiles, num_y_tiles;
9008 if (!tinyexr::PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) {
9009 tinyexr::SetErrorMessage(
"Invalid tile info.", err);
9010 return TINYEXR_ERROR_INVALID_DATA;
9012 int num_blocks = InitTileOffsets(offset_data, exr_headers[i], num_x_tiles, num_y_tiles);
9013 if (num_blocks != exr_headers[i]->chunk_count) {
9014 tinyexr::SetErrorMessage(
"Invalid offset table size.", err);
9015 return TINYEXR_ERROR_INVALID_DATA;
9018 for (
unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
9019 for (
unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
9020 for (
unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
9021 tinyexr::tinyexr_uint64 offset;
9022 memcpy(&offset, marker,
sizeof(tinyexr::tinyexr_uint64));
9023 tinyexr::swap8(&offset);
9024 if (offset >= size) {
9025 tinyexr::SetErrorMessage(
"Invalid offset size in EXR header chunks.",
9027 return TINYEXR_ERROR_INVALID_DATA;
9029 offset_data.offsets[l][dy][dx] = offset + 4;
9030 marker +=
sizeof(tinyexr::tinyexr_uint64);
9038 for (
size_t i = 0; i < static_cast<size_t>(num_parts); i++) {
9039 tinyexr::OffsetData &offset_data = chunk_offset_table_list[i];
9042 for (
unsigned int l = 0; l < offset_data.offsets.size(); ++l)
9043 for (
unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy)
9044 for (
unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
9046 const unsigned char *part_number_addr =
9047 memory + offset_data.offsets[l][dy][dx] - 4;
9048 unsigned int part_no;
9049 memcpy(&part_no, part_number_addr,
sizeof(
unsigned int));
9050 tinyexr::swap4(&part_no);
9053 tinyexr::SetErrorMessage(
"Invalid `part number' in EXR header chunks.",
9055 return TINYEXR_ERROR_INVALID_DATA;
9060 int ret = tinyexr::DecodeChunk(&exr_images[i], exr_headers[i], offset_data,
9062 if (ret != TINYEXR_SUCCESS) {
9064 tinyexr::SetErrorMessage(e, err);
9070 return TINYEXR_SUCCESS;
9073int LoadEXRMultipartImageFromFile(
EXRImage *exr_images,
9075 unsigned int num_parts,
const char *filename,
9077 if (exr_images == NULL || exr_headers == NULL || num_parts == 0) {
9078 tinyexr::SetErrorMessage(
9079 "Invalid argument for LoadEXRMultipartImageFromFile", err);
9080 return TINYEXR_ERROR_INVALID_ARGUMENT;
9083 MemoryMappedFile file(filename);
9084 if (!file.valid()) {
9085 tinyexr::SetErrorMessage(
"Cannot read file " + std::string(filename), err);
9086 return TINYEXR_ERROR_CANT_OPEN_FILE;
9089 return LoadEXRMultipartImageFromMemory(exr_images, exr_headers, num_parts,
9090 file.data, file.size, err);
9093int SaveEXRToMemory(
const float *data,
int width,
int height,
int components,
9094 const int save_as_fp16,
unsigned char **outbuf,
const char **err) {
9096 if ((components == 1) || components == 3 || components == 4) {
9099 std::stringstream ss;
9100 ss <<
"Unsupported component value : " << components << std::endl;
9102 tinyexr::SetErrorMessage(ss.str(), err);
9103 return TINYEXR_ERROR_INVALID_ARGUMENT;
9107 InitEXRHeader(&header);
9109 if ((width < 16) && (height < 16)) {
9111 header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE;
9113 header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
9117 InitEXRImage(&image);
9119 image.num_channels = components;
9121 std::vector<float> images[4];
9123 if (components == 1) {
9124 images[0].resize(
static_cast<size_t>(width * height));
9125 memcpy(images[0].data(), data,
sizeof(
float) *
size_t(width * height));
9127 images[0].resize(
static_cast<size_t>(width * height));
9128 images[1].resize(
static_cast<size_t>(width * height));
9129 images[2].resize(
static_cast<size_t>(width * height));
9130 images[3].resize(
static_cast<size_t>(width * height));
9133 if (components == 4) {
9134 for (
size_t i = 0; i < static_cast<size_t>(width * height); i++) {
9135 images[0][i] = data[
static_cast<size_t>(components) * i + 0];
9136 images[1][i] = data[
static_cast<size_t>(components) * i + 1];
9137 images[2][i] = data[
static_cast<size_t>(components) * i + 2];
9138 images[3][i] = data[
static_cast<size_t>(components) * i + 3];
9141 for (
size_t i = 0; i < static_cast<size_t>(width * height); i++) {
9142 images[0][i] = data[
static_cast<size_t>(components) * i + 0];
9143 images[1][i] = data[
static_cast<size_t>(components) * i + 1];
9144 images[2][i] = data[
static_cast<size_t>(components) * i + 2];
9149 float *image_ptr[4] = {0, 0, 0, 0};
9150 if (components == 4) {
9151 image_ptr[0] = &(images[3].at(0));
9152 image_ptr[1] = &(images[2].at(0));
9153 image_ptr[2] = &(images[1].at(0));
9154 image_ptr[3] = &(images[0].at(0));
9155 }
else if (components == 3) {
9156 image_ptr[0] = &(images[2].at(0));
9157 image_ptr[1] = &(images[1].at(0));
9158 image_ptr[2] = &(images[0].at(0));
9159 }
else if (components == 1) {
9160 image_ptr[0] = &(images[0].at(0));
9163 image.images =
reinterpret_cast<unsigned char **
>(image_ptr);
9164 image.width = width;
9165 image.height = height;
9167 header.num_channels = components;
9169 sizeof(
EXRChannelInfo) *
static_cast<size_t>(header.num_channels)));
9171 if (components == 4) {
9173 strncpy_s(header.channels[0].name,
"A", 255);
9174 strncpy_s(header.channels[1].name,
"B", 255);
9175 strncpy_s(header.channels[2].name,
"G", 255);
9176 strncpy_s(header.channels[3].name,
"R", 255);
9178 strncpy(header.channels[0].name,
"A", 255);
9179 strncpy(header.channels[1].name,
"B", 255);
9180 strncpy(header.channels[2].name,
"G", 255);
9181 strncpy(header.channels[3].name,
"R", 255);
9183 header.channels[0].name[strlen(
"A")] =
'\0';
9184 header.channels[1].name[strlen(
"B")] =
'\0';
9185 header.channels[2].name[strlen(
"G")] =
'\0';
9186 header.channels[3].name[strlen(
"R")] =
'\0';
9187 }
else if (components == 3) {
9189 strncpy_s(header.channels[0].name,
"B", 255);
9190 strncpy_s(header.channels[1].name,
"G", 255);
9191 strncpy_s(header.channels[2].name,
"R", 255);
9193 strncpy(header.channels[0].name,
"B", 255);
9194 strncpy(header.channels[1].name,
"G", 255);
9195 strncpy(header.channels[2].name,
"R", 255);
9197 header.channels[0].name[strlen(
"B")] =
'\0';
9198 header.channels[1].name[strlen(
"G")] =
'\0';
9199 header.channels[2].name[strlen(
"R")] =
'\0';
9202 strncpy_s(header.channels[0].name,
"A", 255);
9204 strncpy(header.channels[0].name,
"A", 255);
9206 header.channels[0].name[strlen(
"A")] =
'\0';
9209 header.pixel_types =
static_cast<int *
>(
9210 malloc(
sizeof(
int) *
static_cast<size_t>(header.num_channels)));
9211 header.requested_pixel_types =
static_cast<int *
>(
9212 malloc(
sizeof(
int) *
static_cast<size_t>(header.num_channels)));
9213 for (
int i = 0; i < header.num_channels; i++) {
9214 header.pixel_types[i] =
9215 TINYEXR_PIXELTYPE_FLOAT;
9217 if (save_as_fp16 > 0) {
9218 header.requested_pixel_types[i] =
9219 TINYEXR_PIXELTYPE_HALF;
9221 header.requested_pixel_types[i] =
9222 TINYEXR_PIXELTYPE_FLOAT;
9228 unsigned char *mem_buf;
9229 size_t mem_size = SaveEXRImageToMemory(&image, &header, &mem_buf, err);
9231 if (mem_size == 0) {
9232 return TINYEXR_ERROR_SERIALIZATION_FAILED;
9235 free(header.channels);
9236 free(header.pixel_types);
9237 free(header.requested_pixel_types);
9239 if (mem_size >
size_t(std::numeric_limits<int>::max())) {
9241 return TINYEXR_ERROR_DATA_TOO_LARGE;
9244 (*outbuf) = mem_buf;
9246 return int(mem_size);
9249int SaveEXR(
const float *data,
int width,
int height,
int components,
9250 const int save_as_fp16,
const char *outfilename,
const char **err) {
9251 if ((components == 1) || components == 3 || components == 4) {
9254 std::stringstream ss;
9255 ss <<
"Unsupported component value : " << components << std::endl;
9257 tinyexr::SetErrorMessage(ss.str(), err);
9258 return TINYEXR_ERROR_INVALID_ARGUMENT;
9262 InitEXRHeader(&header);
9264 if ((width < 16) && (height < 16)) {
9266 header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE;
9268 header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
9272 InitEXRImage(&image);
9274 image.num_channels = components;
9276 std::vector<float> images[4];
9277 const size_t pixel_count =
9278 static_cast<size_t>(width) *
static_cast<size_t>(height);
9280 if (components == 1) {
9281 images[0].resize(pixel_count);
9282 memcpy(images[0].data(), data,
sizeof(
float) * pixel_count);
9284 images[0].resize(pixel_count);
9285 images[1].resize(pixel_count);
9286 images[2].resize(pixel_count);
9287 images[3].resize(pixel_count);
9290 if (components == 4) {
9291 for (
size_t i = 0; i < pixel_count; i++) {
9292 images[0][i] = data[
static_cast<size_t>(components) * i + 0];
9293 images[1][i] = data[
static_cast<size_t>(components) * i + 1];
9294 images[2][i] = data[
static_cast<size_t>(components) * i + 2];
9295 images[3][i] = data[
static_cast<size_t>(components) * i + 3];
9298 for (
size_t i = 0; i < pixel_count; i++) {
9299 images[0][i] = data[
static_cast<size_t>(components) * i + 0];
9300 images[1][i] = data[
static_cast<size_t>(components) * i + 1];
9301 images[2][i] = data[
static_cast<size_t>(components) * i + 2];
9306 float *image_ptr[4] = {0, 0, 0, 0};
9307 if (components == 4) {
9308 image_ptr[0] = &(images[3].at(0));
9309 image_ptr[1] = &(images[2].at(0));
9310 image_ptr[2] = &(images[1].at(0));
9311 image_ptr[3] = &(images[0].at(0));
9312 }
else if (components == 3) {
9313 image_ptr[0] = &(images[2].at(0));
9314 image_ptr[1] = &(images[1].at(0));
9315 image_ptr[2] = &(images[0].at(0));
9316 }
else if (components == 1) {
9317 image_ptr[0] = &(images[0].at(0));
9320 image.images =
reinterpret_cast<unsigned char **
>(image_ptr);
9321 image.width = width;
9322 image.height = height;
9324 header.num_channels = components;
9326 sizeof(
EXRChannelInfo) *
static_cast<size_t>(header.num_channels)));
9328 if (components == 4) {
9330 strncpy_s(header.channels[0].name,
"A", 255);
9331 strncpy_s(header.channels[1].name,
"B", 255);
9332 strncpy_s(header.channels[2].name,
"G", 255);
9333 strncpy_s(header.channels[3].name,
"R", 255);
9335 strncpy(header.channels[0].name,
"A", 255);
9336 strncpy(header.channels[1].name,
"B", 255);
9337 strncpy(header.channels[2].name,
"G", 255);
9338 strncpy(header.channels[3].name,
"R", 255);
9340 header.channels[0].name[strlen(
"A")] =
'\0';
9341 header.channels[1].name[strlen(
"B")] =
'\0';
9342 header.channels[2].name[strlen(
"G")] =
'\0';
9343 header.channels[3].name[strlen(
"R")] =
'\0';
9344 }
else if (components == 3) {
9346 strncpy_s(header.channels[0].name,
"B", 255);
9347 strncpy_s(header.channels[1].name,
"G", 255);
9348 strncpy_s(header.channels[2].name,
"R", 255);
9350 strncpy(header.channels[0].name,
"B", 255);
9351 strncpy(header.channels[1].name,
"G", 255);
9352 strncpy(header.channels[2].name,
"R", 255);
9354 header.channels[0].name[strlen(
"B")] =
'\0';
9355 header.channels[1].name[strlen(
"G")] =
'\0';
9356 header.channels[2].name[strlen(
"R")] =
'\0';
9359 strncpy_s(header.channels[0].name,
"A", 255);
9361 strncpy(header.channels[0].name,
"A", 255);
9363 header.channels[0].name[strlen(
"A")] =
'\0';
9366 header.pixel_types =
static_cast<int *
>(
9367 malloc(
sizeof(
int) *
static_cast<size_t>(header.num_channels)));
9368 header.requested_pixel_types =
static_cast<int *
>(
9369 malloc(
sizeof(
int) *
static_cast<size_t>(header.num_channels)));
9370 for (
int i = 0; i < header.num_channels; i++) {
9371 header.pixel_types[i] =
9372 TINYEXR_PIXELTYPE_FLOAT;
9374 if (save_as_fp16 > 0) {
9375 header.requested_pixel_types[i] =
9376 TINYEXR_PIXELTYPE_HALF;
9378 header.requested_pixel_types[i] =
9379 TINYEXR_PIXELTYPE_FLOAT;
9384 int ret = SaveEXRImageToFile(&image, &header, outfilename, err);
9386 free(header.channels);
9387 free(header.pixel_types);
9388 free(header.requested_pixel_types);
9395#pragma clang diagnostic pop
GLM_FUNC_DECL GLM_CONSTEXPR genType zero()
Definition constants.inl:6
uint64 uint64_t
Definition fwd.hpp:145
float f32
Definition fwd.hpp:152
int64 int64_t
Definition fwd.hpp:85
uint32 uint32_t
Definition fwd.hpp:131
T levels(vec< L, T, Q > const &Extent)
Definition texture.inl:6