Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
tinyexr.h
Go to the documentation of this file.
1#ifndef TINYEXR_H_
2#define TINYEXR_H_
3/*
4Copyright (c) 2014 - 2021, Syoyo Fujita and many contributors.
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of the Syoyo Fujita nor the
15 names of its contributors may be used to endorse or promote products
16 derived from this software without specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
22DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30// TinyEXR contains some OpenEXR code, which is licensed under ------------
31
33//
34// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
35// Digital Ltd. LLC
36//
37// All rights reserved.
38//
39// Redistribution and use in source and binary forms, with or without
40// modification, are permitted provided that the following conditions are
41// met:
42// * Redistributions of source code must retain the above copyright
43// notice, this list of conditions and the following disclaimer.
44// * Redistributions in binary form must reproduce the above
45// copyright notice, this list of conditions and the following disclaimer
46// in the documentation and/or other materials provided with the
47// distribution.
48// * Neither the name of Industrial Light & Magic nor the names of
49// its contributors may be used to endorse or promote products derived
50// from this software without specific prior written permission.
51//
52// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
53// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
54// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
55// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
56// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
57// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
58// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
59// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
60// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
61// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
62// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63//
65
66// End of OpenEXR license -------------------------------------------------
67
68
69//
70//
71// Do this:
72// #define TINYEXR_IMPLEMENTATION
73// before you include this file in *one* C or C++ file to create the
74// implementation.
75//
76// // i.e. it should look like this:
77// #include ...
78// #include ...
79// #include ...
80// #define TINYEXR_IMPLEMENTATION
81// #include "tinyexr.h"
82//
83//
84
85#include <stddef.h> // for size_t
86#include <stdint.h> // guess stdint.h is available(C99)
87
88#ifdef __cplusplus
89extern "C" {
90#endif
91
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
96#else
97#define TINYEXR_X86_OR_X64_CPU 0
98#endif
99
100#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || TINYEXR_X86_OR_X64_CPU
101#define TINYEXR_LITTLE_ENDIAN 1
102#else
103#define TINYEXR_LITTLE_ENDIAN 0
104#endif
105
106// Use miniz or not to decode ZIP format pixel. Linking with zlib
107// required if this flag is 0 and TINYEXR_USE_STB_ZLIB is 0.
108#ifndef TINYEXR_USE_MINIZ
109#define TINYEXR_USE_MINIZ (1)
110#endif
111
112// Use the ZIP implementation of stb_image.h and stb_image_write.h.
113#ifndef TINYEXR_USE_STB_ZLIB
114#define TINYEXR_USE_STB_ZLIB (0)
115#endif
116
117// Use nanozlib.
118#ifndef TINYEXR_USE_NANOZLIB
119#define TINYEXR_USE_NANOZLIB (0)
120#endif
121
122// Disable PIZ compression when applying cpplint.
123#ifndef TINYEXR_USE_PIZ
124#define TINYEXR_USE_PIZ (1)
125#endif
126
127#ifndef TINYEXR_USE_ZFP
128#define TINYEXR_USE_ZFP (0) // TinyEXR extension.
129// http://computation.llnl.gov/projects/floating-point-compression
130#endif
131
132#ifndef TINYEXR_USE_THREAD
133#define TINYEXR_USE_THREAD (0) // No threaded loading.
134// http://computation.llnl.gov/projects/floating-point-compression
135#endif
136
137#ifndef TINYEXR_USE_OPENMP
138#ifdef _OPENMP
139#define TINYEXR_USE_OPENMP (1)
140#else
141#define TINYEXR_USE_OPENMP (0)
142#endif
143#endif
144
145#define TINYEXR_SUCCESS (0)
146#define TINYEXR_ERROR_INVALID_MAGIC_NUMBER (-1)
147#define TINYEXR_ERROR_INVALID_EXR_VERSION (-2)
148#define TINYEXR_ERROR_INVALID_ARGUMENT (-3)
149#define TINYEXR_ERROR_INVALID_DATA (-4)
150#define TINYEXR_ERROR_INVALID_FILE (-5)
151#define TINYEXR_ERROR_INVALID_PARAMETER (-6)
152#define TINYEXR_ERROR_CANT_OPEN_FILE (-7)
153#define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-8)
154#define TINYEXR_ERROR_INVALID_HEADER (-9)
155#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-10)
156#define TINYEXR_ERROR_CANT_WRITE_FILE (-11)
157#define TINYEXR_ERROR_SERIALIZATION_FAILED (-12)
158#define TINYEXR_ERROR_LAYER_NOT_FOUND (-13)
159#define TINYEXR_ERROR_DATA_TOO_LARGE (-14)
160
161// @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf }
162
163// pixel type: possible values are: UINT = 0 HALF = 1 FLOAT = 2
164#define TINYEXR_PIXELTYPE_UINT (0)
165#define TINYEXR_PIXELTYPE_HALF (1)
166#define TINYEXR_PIXELTYPE_FLOAT (2)
167
168#define TINYEXR_MAX_HEADER_ATTRIBUTES (1024)
169#define TINYEXR_MAX_CUSTOM_ATTRIBUTES (128)
170
171#define TINYEXR_COMPRESSIONTYPE_NONE (0)
172#define TINYEXR_COMPRESSIONTYPE_RLE (1)
173#define TINYEXR_COMPRESSIONTYPE_ZIPS (2)
174#define TINYEXR_COMPRESSIONTYPE_ZIP (3)
175#define TINYEXR_COMPRESSIONTYPE_PIZ (4)
176#define TINYEXR_COMPRESSIONTYPE_ZFP (128) // TinyEXR extension
177
178#define TINYEXR_ZFP_COMPRESSIONTYPE_RATE (0)
179#define TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION (1)
180#define TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY (2)
181
182#define TINYEXR_TILE_ONE_LEVEL (0)
183#define TINYEXR_TILE_MIPMAP_LEVELS (1)
184#define TINYEXR_TILE_RIPMAP_LEVELS (2)
185
186#define TINYEXR_TILE_ROUND_DOWN (0)
187#define TINYEXR_TILE_ROUND_UP (1)
188
189typedef struct TEXRVersion {
190 int version; // this must be 2
191 // tile format image;
192 // not zero for only a single-part "normal" tiled file (according to spec.)
193 int tiled;
194 int long_name; // long name attribute
195 // deep image(EXR 2.0);
196 // for a multi-part file, indicates that at least one part is of type deep* (according to spec.)
198 int multipart; // multi-part(EXR 2.0)
200
201typedef struct TEXRAttribute {
202 char name[256]; // name and type are up to 255 chars long.
203 char type[256];
204 unsigned char *value; // uint8_t*
205 int size;
206 int pad0;
208
209typedef struct TEXRChannelInfo {
210 char name[256]; // less than 255 bytes long
214 unsigned char p_linear;
215 unsigned char pad[3];
217
218typedef struct TEXRTile {
223
224 int width; // actual width in a tile.
225 int height; // actual height int a tile.
226
227 unsigned char **images; // image[channels][pixels]
229
230typedef struct TEXRBox2i {
231 int min_x;
232 int min_y;
233 int max_x;
234 int max_y;
236
237typedef struct TEXRHeader {
244
246
247 // Properties for tiled format(`tiledesc`).
248 int tiled;
253
255 // for a single-part file, agree with the version field bit 11
256 // for a multi-part file, it is consistent with the type of part
259 unsigned int header_len;
260
261 // Custom attributes(exludes required attributes(e.g. `channels`,
262 // `compression`, etc)
264 EXRAttribute *custom_attributes; // array of EXRAttribute. size =
265 // `num_custom_attributes`.
266
267 EXRChannelInfo *channels; // [num_channels]
268
269 int *pixel_types; // Loaded pixel type(TINYEXR_PIXELTYPE_*) of `images` for
270 // each channel. This is overwritten with `requested_pixel_types` when
271 // loading.
273
274 int compression_type; // compression type(TINYEXR_COMPRESSIONTYPE_*)
275 int *requested_pixel_types; // Filled initially by
276 // ParseEXRHeaderFrom(Meomory|File), then users
277 // can edit it(only valid for HALF pixel type
278 // channel)
279 // name attribute required for multipart files;
280 // must be unique and non empty (according to spec.);
281 // use EXRSetNameAttr for setting value;
282 // max 255 character allowed - excluding terminating zero
283 char name[256];
285
291
292typedef struct TEXRImage {
293 EXRTile *tiles; // Tiled pixel data. The application must reconstruct image
294 // from tiles manually. NULL if scanline format.
295 struct TEXRImage* next_level; // NULL if scanline format or image is the last level.
296 int level_x; // x level index
297 int level_y; // y level index
298
299 unsigned char **images; // image[channels][pixels]. NULL if tiled format.
300
301 int width;
304
305 // Properties for tile format.
307
309
315
316typedef struct TDeepImage {
317 const char **channel_names;
318 float ***image; // image[channels][scanlines][samples]
319 int **offset_table; // offset_table[scanline][offsets]
321 int width;
323 int pad0;
325
326// @deprecated { For backward compatibility. Not recommended to use. }
327// Loads single-frame OpenEXR image. Assume EXR image contains A(single channel
328// alpha) or RGB(A) channels.
329// Application must free image data as returned by `out_rgba`
330// Result image format is: float x RGBA x width x hight
331// Returns negative value and may set error string in `err` when there's an
332// error
333extern int LoadEXR(float **out_rgba, int *width, int *height,
334 const char *filename, const char **err);
335
336// Loads single-frame OpenEXR image by specifying layer name. Assume EXR image
337// contains A(single channel alpha) or RGB(A) channels. Application must free
338// image data as returned by `out_rgba` Result image format is: float x RGBA x
339// width x hight Returns negative value and may set error string in `err` when
340// there's an error When the specified layer name is not found in the EXR file,
341// the function will return `TINYEXR_ERROR_LAYER_NOT_FOUND`.
342extern int LoadEXRWithLayer(float **out_rgba, int *width, int *height,
343 const char *filename, const char *layer_name,
344 const char **err);
345
346//
347// Get layer infos from EXR file.
348//
349// @param[out] layer_names List of layer names. Application must free memory
350// after using this.
351// @param[out] num_layers The number of layers
352// @param[out] err Error string(will be filled when the function returns error
353// code). Free it using FreeEXRErrorMessage after using this value.
354//
355// @return TINYEXR_SUCCEES upon success.
356//
357extern int EXRLayers(const char *filename, const char **layer_names[],
358 int *num_layers, const char **err);
359
360// @deprecated
361// Simple wrapper API for ParseEXRHeaderFromFile.
362// checking given file is a EXR file(by just look up header)
363// @return TINYEXR_SUCCEES for EXR image, TINYEXR_ERROR_INVALID_HEADER for
364// others
365extern int IsEXR(const char *filename);
366
367// Simple wrapper API for ParseEXRHeaderFromMemory.
368// Check if given data is a EXR image(by just looking up a header section)
369// @return TINYEXR_SUCCEES for EXR image, TINYEXR_ERROR_INVALID_HEADER for
370// others
371extern int IsEXRFromMemory(const unsigned char *memory, size_t size);
372
373// @deprecated
374// Saves single-frame OpenEXR image to a buffer. Assume EXR image contains RGB(A) channels.
375// components must be 1(Grayscale), 3(RGB) or 4(RGBA).
376// Input image format is: `float x width x height`, or `float x RGB(A) x width x
377// hight`
378// Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero
379// value.
380// Save image as fp32(FLOAT) format when `save_as_fp16` is 0.
381// Use ZIP compression by default.
382// `buffer` is the pointer to write EXR data.
383// Memory for `buffer` is allocated internally in SaveEXRToMemory.
384// Returns the data size of EXR file when the value is positive(up to 2GB EXR data).
385// Returns negative value and may set error string in `err` when there's an
386// error
387extern int SaveEXRToMemory(const float *data, const int width, const int height,
388 const int components, const int save_as_fp16,
389 unsigned char **buffer, const char **err);
390
391// @deprecated { Not recommended, but handy to use. }
392// Saves single-frame OpenEXR image to a buffer. Assume EXR image contains RGB(A) channels.
393// components must be 1(Grayscale), 3(RGB) or 4(RGBA).
394// Input image format is: `float x width x height`, or `float x RGB(A) x width x
395// hight`
396// Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero
397// value.
398// Save image as fp32(FLOAT) format when `save_as_fp16` is 0.
399// Use ZIP compression by default.
400// Returns TINYEXR_SUCCEES(0) when success.
401// Returns negative value and may set error string in `err` when there's an
402// error
403extern int SaveEXR(const float *data, const int width, const int height,
404 const int components, const int save_as_fp16,
405 const char *filename, const char **err);
406
407// Returns the number of resolution levels of the image (including the base)
408extern int EXRNumLevels(const EXRImage* exr_image);
409
410// Initialize EXRHeader struct
411extern void InitEXRHeader(EXRHeader *exr_header);
412
413// Set name attribute of EXRHeader struct (it makes a copy)
414extern void EXRSetNameAttr(EXRHeader *exr_header, const char* name);
415
416// Initialize EXRImage struct
417extern void InitEXRImage(EXRImage *exr_image);
418
419// Frees internal data of EXRHeader struct
420extern int FreeEXRHeader(EXRHeader *exr_header);
421
422// Frees internal data of EXRImage struct
423extern int FreeEXRImage(EXRImage *exr_image);
424
425// Frees error message
426extern void FreeEXRErrorMessage(const char *msg);
427
428// Parse EXR version header of a file.
429extern int ParseEXRVersionFromFile(EXRVersion *version, const char *filename);
430
431// Parse EXR version header from memory-mapped EXR data.
433 const unsigned char *memory, size_t size);
434
435// Parse single-part OpenEXR header from a file and initialize `EXRHeader`.
436// When there was an error message, Application must free `err` with
437// FreeEXRErrorMessage()
438extern int ParseEXRHeaderFromFile(EXRHeader *header, const EXRVersion *version,
439 const char *filename, const char **err);
440
441// Parse single-part OpenEXR header from a memory and initialize `EXRHeader`.
442// When there was an error message, Application must free `err` with
443// FreeEXRErrorMessage()
445 const EXRVersion *version,
446 const unsigned char *memory, size_t size,
447 const char **err);
448
449// Parse multi-part OpenEXR headers from a file and initialize `EXRHeader*`
450// array.
451// When there was an error message, Application must free `err` with
452// FreeEXRErrorMessage()
454 int *num_headers,
455 const EXRVersion *version,
456 const char *filename,
457 const char **err);
458
459// Parse multi-part OpenEXR headers from a memory and initialize `EXRHeader*`
460// array
461// When there was an error message, Application must free `err` with
462// FreeEXRErrorMessage()
464 int *num_headers,
465 const EXRVersion *version,
466 const unsigned char *memory,
467 size_t size, const char **err);
468
469// Loads single-part OpenEXR image from a file.
470// Application must setup `ParseEXRHeaderFromFile` before calling this function.
471// Application can free EXRImage using `FreeEXRImage`
472// Returns negative value and may set error string in `err` when there's an
473// error
474// When there was an error message, Application must free `err` with
475// FreeEXRErrorMessage()
476extern int LoadEXRImageFromFile(EXRImage *image, const EXRHeader *header,
477 const char *filename, const char **err);
478
479// Loads single-part OpenEXR image from a memory.
480// Application must setup `EXRHeader` with
481// `ParseEXRHeaderFromMemory` before calling this function.
482// Application can free EXRImage using `FreeEXRImage`
483// Returns negative value and may set error string in `err` when there's an
484// error
485// When there was an error message, Application must free `err` with
486// FreeEXRErrorMessage()
487extern int LoadEXRImageFromMemory(EXRImage *image, const EXRHeader *header,
488 const unsigned char *memory,
489 const size_t size, const char **err);
490
491// Loads multi-part OpenEXR image from a file.
492// Application must setup `ParseEXRMultipartHeaderFromFile` before calling this
493// function.
494// Application can free EXRImage using `FreeEXRImage`
495// Returns negative value and may set error string in `err` when there's an
496// error
497// When there was an error message, Application must free `err` with
498// FreeEXRErrorMessage()
500 const EXRHeader **headers,
501 unsigned int num_parts,
502 const char *filename,
503 const char **err);
504
505// Loads multi-part OpenEXR image from a memory.
506// Application must setup `EXRHeader*` array with
507// `ParseEXRMultipartHeaderFromMemory` before calling this function.
508// Application can free EXRImage using `FreeEXRImage`
509// Returns negative value and may set error string in `err` when there's an
510// error
511// When there was an error message, Application must free `err` with
512// FreeEXRErrorMessage()
514 const EXRHeader **headers,
515 unsigned int num_parts,
516 const unsigned char *memory,
517 const size_t size, const char **err);
518
519// Saves multi-channel, single-frame OpenEXR image to a file.
520// Returns negative value and may set error string in `err` when there's an
521// error
522// When there was an error message, Application must free `err` with
523// FreeEXRErrorMessage()
524extern int SaveEXRImageToFile(const EXRImage *image,
525 const EXRHeader *exr_header, const char *filename,
526 const char **err);
527
528// Saves multi-channel, single-frame OpenEXR image to a memory.
529// Image is compressed using EXRImage.compression value.
530// Return the number of bytes if success.
531// Return zero and will set error string in `err` when there's an
532// error.
533// When there was an error message, Application must free `err` with
534// FreeEXRErrorMessage()
535extern size_t SaveEXRImageToMemory(const EXRImage *image,
536 const EXRHeader *exr_header,
537 unsigned char **memory, const char **err);
538
539// Saves multi-channel, multi-frame OpenEXR image to a memory.
540// Image is compressed using EXRImage.compression value.
541// File global attributes (eg. display_window) must be set in the first header.
542// Returns negative value and may set error string in `err` when there's an
543// error
544// When there was an error message, Application must free `err` with
545// FreeEXRErrorMessage()
546extern int SaveEXRMultipartImageToFile(const EXRImage *images,
547 const EXRHeader **exr_headers,
548 unsigned int num_parts,
549 const char *filename, const char **err);
550
551// Saves multi-channel, multi-frame OpenEXR image to a memory.
552// Image is compressed using EXRImage.compression value.
553// File global attributes (eg. display_window) must be set in the first header.
554// Return the number of bytes if success.
555// Return zero and will set error string in `err` when there's an
556// error.
557// When there was an error message, Application must free `err` with
558// FreeEXRErrorMessage()
559extern size_t SaveEXRMultipartImageToMemory(const EXRImage *images,
560 const EXRHeader **exr_headers,
561 unsigned int num_parts,
562 unsigned char **memory, const char **err);
563// Loads single-frame OpenEXR deep image.
564// Application must free memory of variables in DeepImage(image, offset_table)
565// Returns negative value and may set error string in `err` when there's an
566// error
567// When there was an error message, Application must free `err` with
568// FreeEXRErrorMessage()
569extern int LoadDeepEXR(DeepImage *out_image, const char *filename,
570 const char **err);
571
572// NOT YET IMPLEMENTED:
573// Saves single-frame OpenEXR deep image.
574// Returns negative value and may set error string in `err` when there's an
575// error
576// extern int SaveDeepEXR(const DeepImage *in_image, const char *filename,
577// const char **err);
578
579// NOT YET IMPLEMENTED:
580// Loads multi-part OpenEXR deep image.
581// Application must free memory of variables in DeepImage(image, offset_table)
582// extern int LoadMultiPartDeepEXR(DeepImage **out_image, int num_parts, const
583// char *filename,
584// const char **err);
585
586// For emscripten.
587// Loads single-frame OpenEXR image from memory. Assume EXR image contains
588// RGB(A) channels.
589// Returns negative value and may set error string in `err` when there's an
590// error
591// When there was an error message, Application must free `err` with
592// FreeEXRErrorMessage()
593extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
594 const unsigned char *memory, size_t size,
595 const char **err);
596
597#ifdef __cplusplus
598}
599#endif
600
601#endif // TINYEXR_H_
602
603#ifdef TINYEXR_IMPLEMENTATION
604#ifndef TINYEXR_IMPLEMENTATION_DEFINED
605#define TINYEXR_IMPLEMENTATION_DEFINED
606
607#ifdef _WIN32
608
609#ifndef WIN32_LEAN_AND_MEAN
610#define WIN32_LEAN_AND_MEAN
611#endif
612#ifndef NOMINMAX
613#define NOMINMAX
614#endif
615#include <windows.h> // for UTF-8 and memory-mapping
616
617#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
618#define TINYEXR_USE_WIN32_MMAP (1)
619#endif
620
621#elif defined(__linux__) || defined(__unix__)
622#include <fcntl.h> // for open()
623#include <sys/mman.h> // for memory-mapping
624#include <sys/stat.h> // for stat
625#include <unistd.h> // for close()
626#define TINYEXR_USE_POSIX_MMAP (1)
627#endif
628
629#include <algorithm>
630#include <cstdio>
631#include <cstdlib>
632#include <cstring>
633#include <sstream>
634
635//#include <iostream> // debug
636
637#include <limits>
638#include <string>
639#include <vector>
640#include <set>
641
642// https://stackoverflow.com/questions/5047971/how-do-i-check-for-c11-support
643#if __cplusplus > 199711L || (defined(_MSC_VER) && _MSC_VER >= 1900)
644#define TINYEXR_HAS_CXX11 (1)
645// C++11
646#include <cstdint>
647
648#if TINYEXR_USE_THREAD
649#include <atomic>
650#include <thread>
651#endif
652
653#else // __cplusplus > 199711L
654#define TINYEXR_HAS_CXX11 (0)
655#endif // __cplusplus > 199711L
656
657#if TINYEXR_USE_OPENMP
658#include <omp.h>
659#endif
660
661#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
662#include <miniz.h>
663#else
664// Issue #46. Please include your own zlib-compatible API header before
665// including `tinyexr.h`
666//#include "zlib.h"
667#endif
668
669#if defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
670#define NANOZLIB_IMPLEMENTATION
671#include "nanozlib.h"
672#endif
673
674#if TINYEXR_USE_STB_ZLIB
675// Since we don't know where a project has stb_image.h and stb_image_write.h
676// and whether they are in the include path, we don't include them here, and
677// instead declare the two relevant functions manually.
678// from stb_image.h:
679extern "C" int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
680// from stb_image_write.h:
681extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality);
682#endif
683
684
685#if TINYEXR_USE_ZFP
686
687#ifdef __clang__
688#pragma clang diagnostic push
689#pragma clang diagnostic ignored "-Weverything"
690#endif
691
692#include "zfp.h"
693
694#ifdef __clang__
695#pragma clang diagnostic pop
696#endif
697
698#endif
699
700// cond: conditional expression
701// msg: std::string
702// err: std::string*
703#define TINYEXR_CHECK_AND_RETURN_MSG(cond, msg, err) do { \
704 if (!(cond)) { \
705 if (!err) { \
706 std::ostringstream ss_e; \
707 ss_e << __func__ << "():" << __LINE__ << msg << "\n"; \
708 (*err) += ss_e.str(); \
709 } \
710 return false;\
711 } \
712 } while(0)
713
714// no error message.
715#define TINYEXR_CHECK_AND_RETURN_C(cond, retcode) do { \
716 if (!(cond)) { \
717 return retcode; \
718 } \
719 } while(0)
720
721namespace tinyexr {
722
723#if __cplusplus > 199711L
724// C++11
725typedef uint64_t tinyexr_uint64;
726typedef int64_t tinyexr_int64;
727#else
728// Although `long long` is not a standard type pre C++11, assume it is defined
729// as a compiler's extension.
730#ifdef __clang__
731#pragma clang diagnostic push
732#pragma clang diagnostic ignored "-Wc++11-long-long"
733#endif
734typedef unsigned long long tinyexr_uint64;
735typedef long long tinyexr_int64;
736#ifdef __clang__
737#pragma clang diagnostic pop
738#endif
739#endif
740
741// static bool IsBigEndian(void) {
742// union {
743// unsigned int i;
744// char c[4];
745// } bint = {0x01020304};
746//
747// return bint.c[0] == 1;
748//}
749
750static void SetErrorMessage(const std::string &msg, const char **err) {
751 if (err) {
752#ifdef _WIN32
753 (*err) = _strdup(msg.c_str());
754#else
755 (*err) = strdup(msg.c_str());
756#endif
757 }
758}
759
760#if 0
761static void SetWarningMessage(const std::string &msg, const char **warn) {
762 if (warn) {
763#ifdef _WIN32
764 (*warn) = _strdup(msg.c_str());
765#else
766 (*warn) = strdup(msg.c_str());
767#endif
768 }
769}
770#endif
771
772static const unsigned int kEXRVersionSize = 8;
773
774static void cpy2(unsigned short *dst_val, const unsigned short *src_val) {
775 unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
776 const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
777
778 dst[0] = src[0];
779 dst[1] = src[1];
780}
781
782static void swap2(unsigned short *val) {
783#if TINYEXR_LITTLE_ENDIAN
784 (void)val;
785#else
786 unsigned short tmp = *val;
787 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
788 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
789
790 dst[0] = src[1];
791 dst[1] = src[0];
792#endif
793}
794
795#ifdef __clang__
796#pragma clang diagnostic push
797#pragma clang diagnostic ignored "-Wunused-function"
798#endif
799
800#ifdef __GNUC__
801#pragma GCC diagnostic push
802#pragma GCC diagnostic ignored "-Wunused-function"
803#endif
804static void cpy4(int *dst_val, const int *src_val) {
805 unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
806 const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
807
808 dst[0] = src[0];
809 dst[1] = src[1];
810 dst[2] = src[2];
811 dst[3] = src[3];
812}
813
814static void cpy4(unsigned int *dst_val, const unsigned int *src_val) {
815 unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
816 const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
817
818 dst[0] = src[0];
819 dst[1] = src[1];
820 dst[2] = src[2];
821 dst[3] = src[3];
822}
823
824static void cpy4(float *dst_val, const float *src_val) {
825 unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
826 const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
827
828 dst[0] = src[0];
829 dst[1] = src[1];
830 dst[2] = src[2];
831 dst[3] = src[3];
832}
833#ifdef __clang__
834#pragma clang diagnostic pop
835#endif
836
837#ifdef __GNUC__
838#pragma GCC diagnostic pop
839#endif
840
841static void swap4(unsigned int *val) {
842#if TINYEXR_LITTLE_ENDIAN
843 (void)val;
844#else
845 unsigned int tmp = *val;
846 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
847 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
848
849 dst[0] = src[3];
850 dst[1] = src[2];
851 dst[2] = src[1];
852 dst[3] = src[0];
853#endif
854}
855
856static void swap4(int *val) {
857#if TINYEXR_LITTLE_ENDIAN
858 (void)val;
859#else
860 int tmp = *val;
861 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
862 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
863
864 dst[0] = src[3];
865 dst[1] = src[2];
866 dst[2] = src[1];
867 dst[3] = src[0];
868#endif
869}
870
871static void swap4(float *val) {
872#if TINYEXR_LITTLE_ENDIAN
873 (void)val;
874#else
875 float tmp = *val;
876 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
877 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
878
879 dst[0] = src[3];
880 dst[1] = src[2];
881 dst[2] = src[1];
882 dst[3] = src[0];
883#endif
884}
885
886#if 0
887static void cpy8(tinyexr::tinyexr_uint64 *dst_val, const tinyexr::tinyexr_uint64 *src_val) {
888 unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
889 const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
890
891 dst[0] = src[0];
892 dst[1] = src[1];
893 dst[2] = src[2];
894 dst[3] = src[3];
895 dst[4] = src[4];
896 dst[5] = src[5];
897 dst[6] = src[6];
898 dst[7] = src[7];
899}
900#endif
901
902static void swap8(tinyexr::tinyexr_uint64 *val) {
903#if TINYEXR_LITTLE_ENDIAN
904 (void)val;
905#else
906 tinyexr::tinyexr_uint64 tmp = (*val);
907 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
908 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
909
910 dst[0] = src[7];
911 dst[1] = src[6];
912 dst[2] = src[5];
913 dst[3] = src[4];
914 dst[4] = src[3];
915 dst[5] = src[2];
916 dst[6] = src[1];
917 dst[7] = src[0];
918#endif
919}
920
921// https://gist.github.com/rygorous/2156668
922union FP32 {
923 unsigned int u;
924 float f;
925 struct {
926#if TINYEXR_LITTLE_ENDIAN
927 unsigned int Mantissa : 23;
928 unsigned int Exponent : 8;
929 unsigned int Sign : 1;
930#else
931 unsigned int Sign : 1;
932 unsigned int Exponent : 8;
933 unsigned int Mantissa : 23;
934#endif
935 } s;
936};
937
938#ifdef __clang__
939#pragma clang diagnostic push
940#pragma clang diagnostic ignored "-Wpadded"
941#endif
942
943union FP16 {
944 unsigned short u;
945 struct {
946#if TINYEXR_LITTLE_ENDIAN
947 unsigned int Mantissa : 10;
948 unsigned int Exponent : 5;
949 unsigned int Sign : 1;
950#else
951 unsigned int Sign : 1;
952 unsigned int Exponent : 5;
953 unsigned int Mantissa : 10;
954#endif
955 } s;
956};
957
958#ifdef __clang__
959#pragma clang diagnostic pop
960#endif
961
962static FP32 half_to_float(FP16 h) {
963 static const FP32 magic = {113 << 23};
964 static const unsigned int shifted_exp = 0x7c00
965 << 13; // exponent mask after shift
966 FP32 o;
967
968 o.u = (h.u & 0x7fffU) << 13U; // exponent/mantissa bits
969 unsigned int exp_ = shifted_exp & o.u; // just the exponent
970 o.u += (127 - 15) << 23; // exponent adjust
971
972 // handle exponent special cases
973 if (exp_ == shifted_exp) // Inf/NaN?
974 o.u += (128 - 16) << 23; // extra exp adjust
975 else if (exp_ == 0) // Zero/Denormal?
976 {
977 o.u += 1 << 23; // extra exp adjust
978 o.f -= magic.f; // renormalize
979 }
980
981 o.u |= (h.u & 0x8000U) << 16U; // sign bit
982 return o;
983}
984
985static FP16 float_to_half_full(FP32 f) {
986 FP16 o = {0};
987
988 // Based on ISPC reference code (with minor modifications)
989 if (f.s.Exponent == 0) // Signed zero/denormal (which will underflow)
990 o.s.Exponent = 0;
991 else if (f.s.Exponent == 255) // Inf or NaN (all exponent bits set)
992 {
993 o.s.Exponent = 31;
994 o.s.Mantissa = f.s.Mantissa ? 0x200 : 0; // NaN->qNaN and Inf->Inf
995 } else // Normalized number
996 {
997 // Exponent unbias the single, then bias the halfp
998 int newexp = f.s.Exponent - 127 + 15;
999 if (newexp >= 31) // Overflow, return signed infinity
1000 o.s.Exponent = 31;
1001 else if (newexp <= 0) // Underflow
1002 {
1003 if ((14 - newexp) <= 24) // Mantissa might be non-zero
1004 {
1005 unsigned int mant = f.s.Mantissa | 0x800000; // Hidden 1 bit
1006 o.s.Mantissa = mant >> (14 - newexp);
1007 if ((mant >> (13 - newexp)) & 1) // Check for rounding
1008 o.u++; // Round, might overflow into exp bit, but this is OK
1009 }
1010 } else {
1011 o.s.Exponent = static_cast<unsigned int>(newexp);
1012 o.s.Mantissa = f.s.Mantissa >> 13;
1013 if (f.s.Mantissa & 0x1000) // Check for rounding
1014 o.u++; // Round, might overflow to inf, this is OK
1015 }
1016 }
1017
1018 o.s.Sign = f.s.Sign;
1019 return o;
1020}
1021
1022// NOTE: From OpenEXR code
1023// #define IMF_INCREASING_Y 0
1024// #define IMF_DECREASING_Y 1
1025// #define IMF_RAMDOM_Y 2
1026//
1027// #define IMF_NO_COMPRESSION 0
1028// #define IMF_RLE_COMPRESSION 1
1029// #define IMF_ZIPS_COMPRESSION 2
1030// #define IMF_ZIP_COMPRESSION 3
1031// #define IMF_PIZ_COMPRESSION 4
1032// #define IMF_PXR24_COMPRESSION 5
1033// #define IMF_B44_COMPRESSION 6
1034// #define IMF_B44A_COMPRESSION 7
1035
1036#ifdef __clang__
1037#pragma clang diagnostic push
1038
1039#if __has_warning("-Wzero-as-null-pointer-constant")
1040#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1041#endif
1042
1043#endif
1044
1045static const char *ReadString(std::string *s, const char *ptr, size_t len) {
1046 // Read untile NULL(\0).
1047 const char *p = ptr;
1048 const char *q = ptr;
1049 while ((size_t(q - ptr) < len) && (*q) != 0) {
1050 q++;
1051 }
1052
1053 if (size_t(q - ptr) >= len) {
1054 (*s).clear();
1055 return NULL;
1056 }
1057
1058 (*s) = std::string(p, q);
1059
1060 return q + 1; // skip '\0'
1061}
1062
1063static bool ReadAttribute(std::string *name, std::string *type,
1064 std::vector<unsigned char> *data, size_t *marker_size,
1065 const char *marker, size_t size) {
1066 size_t name_len = strnlen(marker, size);
1067 if (name_len == size) {
1068 // String does not have a terminating character.
1069 return false;
1070 }
1071 *name = std::string(marker, name_len);
1072
1073 marker += name_len + 1;
1074 size -= name_len + 1;
1075
1076 size_t type_len = strnlen(marker, size);
1077 if (type_len == size) {
1078 return false;
1079 }
1080 *type = std::string(marker, type_len);
1081
1082 marker += type_len + 1;
1083 size -= type_len + 1;
1084
1085 if (size < sizeof(uint32_t)) {
1086 return false;
1087 }
1088
1089 uint32_t data_len;
1090 memcpy(&data_len, marker, sizeof(uint32_t));
1091 tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len));
1092
1093 if (data_len == 0) {
1094 if ((*type).compare("string") == 0) {
1095 // Accept empty string attribute.
1096
1097 marker += sizeof(uint32_t);
1098 size -= sizeof(uint32_t);
1099
1100 *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t);
1101
1102 data->resize(1);
1103 (*data)[0] = '\0';
1104
1105 return true;
1106 } else {
1107 return false;
1108 }
1109 }
1110
1111 marker += sizeof(uint32_t);
1112 size -= sizeof(uint32_t);
1113
1114 if (size < data_len) {
1115 return false;
1116 }
1117
1118 data->resize(static_cast<size_t>(data_len));
1119 memcpy(&data->at(0), marker, static_cast<size_t>(data_len));
1120
1121 *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t) + data_len;
1122 return true;
1123}
1124
1125static void WriteAttributeToMemory(std::vector<unsigned char> *out,
1126 const char *name, const char *type,
1127 const unsigned char *data, int len) {
1128 out->insert(out->end(), name, name + strlen(name) + 1);
1129 out->insert(out->end(), type, type + strlen(type) + 1);
1130
1131 int outLen = len;
1132 tinyexr::swap4(&outLen);
1133 out->insert(out->end(), reinterpret_cast<unsigned char *>(&outLen),
1134 reinterpret_cast<unsigned char *>(&outLen) + sizeof(int));
1135 out->insert(out->end(), data, data + len);
1136}
1137
1138typedef struct TChannelInfo {
1139 std::string name; // less than 255 bytes long
1140 int pixel_type;
1141 int requested_pixel_type;
1142 int x_sampling;
1143 int y_sampling;
1144 unsigned char p_linear;
1145 unsigned char pad[3];
1146} ChannelInfo;
1147
1148typedef struct {
1149 int min_x;
1150 int min_y;
1151 int max_x;
1152 int max_y;
1153} Box2iInfo;
1154
1155struct HeaderInfo {
1156 std::vector<tinyexr::ChannelInfo> channels;
1157 std::vector<EXRAttribute> attributes;
1158
1159 Box2iInfo data_window;
1160 int line_order;
1161 Box2iInfo display_window;
1162 float screen_window_center[2];
1163 float screen_window_width;
1164 float pixel_aspect_ratio;
1165
1166 int chunk_count;
1167
1168 // Tiled format
1169 int tiled; // Non-zero if the part is tiled.
1170 int tile_size_x;
1171 int tile_size_y;
1172 int tile_level_mode;
1173 int tile_rounding_mode;
1174
1175 unsigned int header_len;
1176
1177 int compression_type;
1178
1179 // required for multi-part or non-image files
1180 std::string name;
1181 // required for multi-part or non-image files
1182 std::string type;
1183
1184 void clear() {
1185 channels.clear();
1186 attributes.clear();
1187
1188 data_window.min_x = 0;
1189 data_window.min_y = 0;
1190 data_window.max_x = 0;
1191 data_window.max_y = 0;
1192 line_order = 0;
1193 display_window.min_x = 0;
1194 display_window.min_y = 0;
1195 display_window.max_x = 0;
1196 display_window.max_y = 0;
1197 screen_window_center[0] = 0.0f;
1198 screen_window_center[1] = 0.0f;
1199 screen_window_width = 0.0f;
1200 pixel_aspect_ratio = 0.0f;
1201
1202 chunk_count = 0;
1203
1204 // Tiled format
1205 tiled = 0;
1206 tile_size_x = 0;
1207 tile_size_y = 0;
1208 tile_level_mode = 0;
1209 tile_rounding_mode = 0;
1210
1211 header_len = 0;
1212 compression_type = 0;
1213
1214 name.clear();
1215 type.clear();
1216 }
1217};
1218
1219static bool ReadChannelInfo(std::vector<ChannelInfo> &channels,
1220 const std::vector<unsigned char> &data) {
1221 const char *p = reinterpret_cast<const char *>(&data.at(0));
1222
1223 for (;;) {
1224 if ((*p) == 0) {
1225 break;
1226 }
1227 ChannelInfo info;
1228 info.requested_pixel_type = 0;
1229
1230 tinyexr_int64 data_len = static_cast<tinyexr_int64>(data.size()) -
1231 (p - reinterpret_cast<const char *>(data.data()));
1232 if (data_len < 0) {
1233 return false;
1234 }
1235
1236 p = ReadString(&info.name, p, size_t(data_len));
1237 if ((p == NULL) && (info.name.empty())) {
1238 // Buffer overrun. Issue #51.
1239 return false;
1240 }
1241
1242 const unsigned char *data_end =
1243 reinterpret_cast<const unsigned char *>(p) + 16;
1244 if (data_end >= (data.data() + data.size())) {
1245 return false;
1246 }
1247
1248 memcpy(&info.pixel_type, p, sizeof(int));
1249 p += 4;
1250 info.p_linear = static_cast<unsigned char>(p[0]); // uchar
1251 p += 1 + 3; // reserved: uchar[3]
1252 memcpy(&info.x_sampling, p, sizeof(int)); // int
1253 p += 4;
1254 memcpy(&info.y_sampling, p, sizeof(int)); // int
1255 p += 4;
1256
1257 tinyexr::swap4(&info.pixel_type);
1258 tinyexr::swap4(&info.x_sampling);
1259 tinyexr::swap4(&info.y_sampling);
1260
1261 channels.push_back(info);
1262 }
1263
1264 return true;
1265}
1266
1267static void WriteChannelInfo(std::vector<unsigned char> &data,
1268 const std::vector<ChannelInfo> &channels) {
1269 size_t sz = 0;
1270
1271 // Calculate total size.
1272 for (size_t c = 0; c < channels.size(); c++) {
1273 sz += channels[c].name.length() + 1; // +1 for \0
1274 sz += 16; // 4 * int
1275 }
1276 data.resize(sz + 1);
1277
1278 unsigned char *p = &data.at(0);
1279
1280 for (size_t c = 0; c < channels.size(); c++) {
1281 memcpy(p, channels[c].name.c_str(), channels[c].name.length());
1282 p += channels[c].name.length();
1283 (*p) = '\0';
1284 p++;
1285
1286 int pixel_type = channels[c].requested_pixel_type;
1287 int x_sampling = channels[c].x_sampling;
1288 int y_sampling = channels[c].y_sampling;
1289 tinyexr::swap4(&pixel_type);
1290 tinyexr::swap4(&x_sampling);
1291 tinyexr::swap4(&y_sampling);
1292
1293 memcpy(p, &pixel_type, sizeof(int));
1294 p += sizeof(int);
1295
1296 (*p) = channels[c].p_linear;
1297 p += 4;
1298
1299 memcpy(p, &x_sampling, sizeof(int));
1300 p += sizeof(int);
1301
1302 memcpy(p, &y_sampling, sizeof(int));
1303 p += sizeof(int);
1304 }
1305
1306 (*p) = '\0';
1307}
1308
1309static bool CompressZip(unsigned char *dst,
1310 tinyexr::tinyexr_uint64 &compressedSize,
1311 const unsigned char *src, unsigned long src_size) {
1312 std::vector<unsigned char> tmpBuf(src_size);
1313
1314 //
1315 // Apply EXR-specific? postprocess. Grabbed from OpenEXR's
1316 // ImfZipCompressor.cpp
1317 //
1318
1319 //
1320 // Reorder the pixel data.
1321 //
1322
1323 const char *srcPtr = reinterpret_cast<const char *>(src);
1324
1325 {
1326 char *t1 = reinterpret_cast<char *>(&tmpBuf.at(0));
1327 char *t2 = reinterpret_cast<char *>(&tmpBuf.at(0)) + (src_size + 1) / 2;
1328 const char *stop = srcPtr + src_size;
1329
1330 for (;;) {
1331 if (srcPtr < stop)
1332 *(t1++) = *(srcPtr++);
1333 else
1334 break;
1335
1336 if (srcPtr < stop)
1337 *(t2++) = *(srcPtr++);
1338 else
1339 break;
1340 }
1341 }
1342
1343 //
1344 // Predictor.
1345 //
1346
1347 {
1348 unsigned char *t = &tmpBuf.at(0) + 1;
1349 unsigned char *stop = &tmpBuf.at(0) + src_size;
1350 int p = t[-1];
1351
1352 while (t < stop) {
1353 int d = int(t[0]) - p + (128 + 256);
1354 p = t[0];
1355 t[0] = static_cast<unsigned char>(d);
1356 ++t;
1357 }
1358 }
1359
1360#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
1361 //
1362 // Compress the data using miniz
1363 //
1364
1365 mz_ulong outSize = mz_compressBound(src_size);
1366 int ret = mz_compress(
1367 dst, &outSize, static_cast<const unsigned char *>(&tmpBuf.at(0)),
1368 src_size);
1369 if (ret != MZ_OK) {
1370 return false;
1371 }
1372
1373 compressedSize = outSize;
1374#elif defined(TINYEXR_USE_STB_ZLIB) && (TINYEXR_USE_STB_ZLIB==1)
1375 int outSize;
1376 unsigned char* ret = stbi_zlib_compress(const_cast<unsigned char*>(&tmpBuf.at(0)), src_size, &outSize, 8);
1377 if (!ret) {
1378 return false;
1379 }
1380 memcpy(dst, ret, outSize);
1381 free(ret);
1382
1383 compressedSize = outSize;
1384#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
1385 uint64_t dstSize = nanoz_compressBound(static_cast<uint64_t>(src_size));
1386 int outSize{0};
1387 unsigned char *ret = nanoz_compress(&tmpBuf.at(0), src_size, &outSize, /* quality */8);
1388 if (!ret) {
1389 return false;
1390 }
1391
1392 memcpy(dst, ret, outSize);
1393 free(ret);
1394
1395 compressedSize = outSize;
1396#else
1397 uLong outSize = compressBound(static_cast<uLong>(src_size));
1398 int ret = compress(dst, &outSize, static_cast<const Bytef *>(&tmpBuf.at(0)),
1399 src_size);
1400 if (ret != Z_OK) {
1401 return false;
1402 }
1403
1404 compressedSize = outSize;
1405#endif
1406
1407 // Use uncompressed data when compressed data is larger than uncompressed.
1408 // (Issue 40)
1409 if (compressedSize >= src_size) {
1410 compressedSize = src_size;
1411 memcpy(dst, src, src_size);
1412 }
1413
1414 return true;
1415}
1416
1417static bool DecompressZip(unsigned char *dst,
1418 unsigned long *uncompressed_size /* inout */,
1419 const unsigned char *src, unsigned long src_size) {
1420 if ((*uncompressed_size) == src_size) {
1421 // Data is not compressed(Issue 40).
1422 memcpy(dst, src, src_size);
1423 return true;
1424 }
1425 std::vector<unsigned char> tmpBuf(*uncompressed_size);
1426
1427#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
1428 int ret =
1429 mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
1430 if (MZ_OK != ret) {
1431 return false;
1432 }
1433#elif TINYEXR_USE_STB_ZLIB
1434 int ret = stbi_zlib_decode_buffer(reinterpret_cast<char*>(&tmpBuf.at(0)),
1435 *uncompressed_size, reinterpret_cast<const char*>(src), src_size);
1436 if (ret < 0) {
1437 return false;
1438 }
1439#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
1440 uint64_t dest_size = (*uncompressed_size);
1441 uint64_t uncomp_size{0};
1442 nanoz_status_t ret =
1443 nanoz_uncompress(src, src_size, dest_size, &tmpBuf.at(0), &uncomp_size);
1444 if (NANOZ_SUCCESS != ret) {
1445 return false;
1446 }
1447 if ((*uncompressed_size) != uncomp_size) {
1448 return false;
1449 }
1450#else
1451 int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
1452 if (Z_OK != ret) {
1453 return false;
1454 }
1455#endif
1456
1457 //
1458 // Apply EXR-specific? postprocess. Grabbed from OpenEXR's
1459 // ImfZipCompressor.cpp
1460 //
1461
1462 // Predictor.
1463 {
1464 unsigned char *t = &tmpBuf.at(0) + 1;
1465 unsigned char *stop = &tmpBuf.at(0) + (*uncompressed_size);
1466
1467 while (t < stop) {
1468 int d = int(t[-1]) + int(t[0]) - 128;
1469 t[0] = static_cast<unsigned char>(d);
1470 ++t;
1471 }
1472 }
1473
1474 // Reorder the pixel data.
1475 {
1476 const char *t1 = reinterpret_cast<const char *>(&tmpBuf.at(0));
1477 const char *t2 = reinterpret_cast<const char *>(&tmpBuf.at(0)) +
1478 (*uncompressed_size + 1) / 2;
1479 char *s = reinterpret_cast<char *>(dst);
1480 char *stop = s + (*uncompressed_size);
1481
1482 for (;;) {
1483 if (s < stop)
1484 *(s++) = *(t1++);
1485 else
1486 break;
1487
1488 if (s < stop)
1489 *(s++) = *(t2++);
1490 else
1491 break;
1492 }
1493 }
1494
1495 return true;
1496}
1497
1498// RLE code from OpenEXR --------------------------------------
1499
1500#ifdef __clang__
1501#pragma clang diagnostic push
1502#pragma clang diagnostic ignored "-Wsign-conversion"
1503#if __has_warning("-Wextra-semi-stmt")
1504#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1505#endif
1506#endif
1507
1508#ifdef _MSC_VER
1509#pragma warning(push)
1510#pragma warning(disable : 4204) // nonstandard extension used : non-constant
1511 // aggregate initializer (also supported by GNU
1512 // C and C99, so no big deal)
1513#pragma warning(disable : 4244) // 'initializing': conversion from '__int64' to
1514 // 'int', possible loss of data
1515#pragma warning(disable : 4267) // 'argument': conversion from '__int64' to
1516 // 'int', possible loss of data
1517#pragma warning(disable : 4996) // 'strdup': The POSIX name for this item is
1518 // deprecated. Instead, use the ISO C and C++
1519 // conformant name: _strdup.
1520#endif
1521
1522const int MIN_RUN_LENGTH = 3;
1523const int MAX_RUN_LENGTH = 127;
1524
1525//
1526// Compress an array of bytes, using run-length encoding,
1527// and return the length of the compressed data.
1528//
1529
1530static int rleCompress(int inLength, const char in[], signed char out[]) {
1531 const char *inEnd = in + inLength;
1532 const char *runStart = in;
1533 const char *runEnd = in + 1;
1534 signed char *outWrite = out;
1535
1536 while (runStart < inEnd) {
1537 while (runEnd < inEnd && *runStart == *runEnd &&
1538 runEnd - runStart - 1 < MAX_RUN_LENGTH) {
1539 ++runEnd;
1540 }
1541
1542 if (runEnd - runStart >= MIN_RUN_LENGTH) {
1543 //
1544 // Compressible run
1545 //
1546
1547 *outWrite++ = static_cast<char>(runEnd - runStart) - 1;
1548 *outWrite++ = *(reinterpret_cast<const signed char *>(runStart));
1549 runStart = runEnd;
1550 } else {
1551 //
1552 // Uncompressable run
1553 //
1554
1555 while (runEnd < inEnd &&
1556 ((runEnd + 1 >= inEnd || *runEnd != *(runEnd + 1)) ||
1557 (runEnd + 2 >= inEnd || *(runEnd + 1) != *(runEnd + 2))) &&
1558 runEnd - runStart < MAX_RUN_LENGTH) {
1559 ++runEnd;
1560 }
1561
1562 *outWrite++ = static_cast<char>(runStart - runEnd);
1563
1564 while (runStart < runEnd) {
1565 *outWrite++ = *(reinterpret_cast<const signed char *>(runStart++));
1566 }
1567 }
1568
1569 ++runEnd;
1570 }
1571
1572 return static_cast<int>(outWrite - out);
1573}
1574
1575//
1576// Uncompress an array of bytes compressed with rleCompress().
1577// Returns the length of the uncompressed data, or 0 if the
1578// length of the uncompressed data would be more than maxLength.
1579//
1580
1581static int rleUncompress(int inLength, int maxLength, const signed char in[],
1582 char out[]) {
1583 char *outStart = out;
1584
1585 while (inLength > 0) {
1586 if (*in < 0) {
1587 int count = -(static_cast<int>(*in++));
1588 inLength -= count + 1;
1589
1590 // Fixes #116: Add bounds check to in buffer.
1591 if ((0 > (maxLength -= count)) || (inLength < 0)) return 0;
1592
1593 memcpy(out, in, count);
1594 out += count;
1595 in += count;
1596 } else {
1597 int count = *in++;
1598 inLength -= 2;
1599
1600 if ((0 > (maxLength -= count + 1)) || (inLength < 0)) return 0;
1601
1602 memset(out, *reinterpret_cast<const char *>(in), count + 1);
1603 out += count + 1;
1604
1605 in++;
1606 }
1607 }
1608
1609 return static_cast<int>(out - outStart);
1610}
1611
1612#ifdef __clang__
1613#pragma clang diagnostic pop
1614#endif
1615
1616// End of RLE code from OpenEXR -----------------------------------
1617
1618static bool CompressRle(unsigned char *dst,
1619 tinyexr::tinyexr_uint64 &compressedSize,
1620 const unsigned char *src, unsigned long src_size) {
1621 std::vector<unsigned char> tmpBuf(src_size);
1622
1623 //
1624 // Apply EXR-specific? postprocess. Grabbed from OpenEXR's
1625 // ImfRleCompressor.cpp
1626 //
1627
1628 //
1629 // Reorder the pixel data.
1630 //
1631
1632 const char *srcPtr = reinterpret_cast<const char *>(src);
1633
1634 {
1635 char *t1 = reinterpret_cast<char *>(&tmpBuf.at(0));
1636 char *t2 = reinterpret_cast<char *>(&tmpBuf.at(0)) + (src_size + 1) / 2;
1637 const char *stop = srcPtr + src_size;
1638
1639 for (;;) {
1640 if (srcPtr < stop)
1641 *(t1++) = *(srcPtr++);
1642 else
1643 break;
1644
1645 if (srcPtr < stop)
1646 *(t2++) = *(srcPtr++);
1647 else
1648 break;
1649 }
1650 }
1651
1652 //
1653 // Predictor.
1654 //
1655
1656 {
1657 unsigned char *t = &tmpBuf.at(0) + 1;
1658 unsigned char *stop = &tmpBuf.at(0) + src_size;
1659 int p = t[-1];
1660
1661 while (t < stop) {
1662 int d = int(t[0]) - p + (128 + 256);
1663 p = t[0];
1664 t[0] = static_cast<unsigned char>(d);
1665 ++t;
1666 }
1667 }
1668
1669 // outSize will be (srcSiz * 3) / 2 at max.
1670 int outSize = rleCompress(static_cast<int>(src_size),
1671 reinterpret_cast<const char *>(&tmpBuf.at(0)),
1672 reinterpret_cast<signed char *>(dst));
1673 TINYEXR_CHECK_AND_RETURN_C(outSize > 0, false);
1674
1675 compressedSize = static_cast<tinyexr::tinyexr_uint64>(outSize);
1676
1677 // Use uncompressed data when compressed data is larger than uncompressed.
1678 // (Issue 40)
1679 if (compressedSize >= src_size) {
1680 compressedSize = src_size;
1681 memcpy(dst, src, src_size);
1682 }
1683
1684 return true;
1685}
1686
1687static bool DecompressRle(unsigned char *dst,
1688 const unsigned long uncompressed_size,
1689 const unsigned char *src, unsigned long src_size) {
1690 if (uncompressed_size == src_size) {
1691 // Data is not compressed(Issue 40).
1692 memcpy(dst, src, src_size);
1693 return true;
1694 }
1695
1696 // Workaround for issue #112.
1697 // TODO(syoyo): Add more robust out-of-bounds check in `rleUncompress`.
1698 if (src_size <= 2) {
1699 return false;
1700 }
1701
1702 std::vector<unsigned char> tmpBuf(uncompressed_size);
1703
1704 int ret = rleUncompress(static_cast<int>(src_size),
1705 static_cast<int>(uncompressed_size),
1706 reinterpret_cast<const signed char *>(src),
1707 reinterpret_cast<char *>(&tmpBuf.at(0)));
1708 if (ret != static_cast<int>(uncompressed_size)) {
1709 return false;
1710 }
1711
1712 //
1713 // Apply EXR-specific? postprocess. Grabbed from OpenEXR's
1714 // ImfRleCompressor.cpp
1715 //
1716
1717 // Predictor.
1718 {
1719 unsigned char *t = &tmpBuf.at(0) + 1;
1720 unsigned char *stop = &tmpBuf.at(0) + uncompressed_size;
1721
1722 while (t < stop) {
1723 int d = int(t[-1]) + int(t[0]) - 128;
1724 t[0] = static_cast<unsigned char>(d);
1725 ++t;
1726 }
1727 }
1728
1729 // Reorder the pixel data.
1730 {
1731 const char *t1 = reinterpret_cast<const char *>(&tmpBuf.at(0));
1732 const char *t2 = reinterpret_cast<const char *>(&tmpBuf.at(0)) +
1733 (uncompressed_size + 1) / 2;
1734 char *s = reinterpret_cast<char *>(dst);
1735 char *stop = s + uncompressed_size;
1736
1737 for (;;) {
1738 if (s < stop)
1739 *(s++) = *(t1++);
1740 else
1741 break;
1742
1743 if (s < stop)
1744 *(s++) = *(t2++);
1745 else
1746 break;
1747 }
1748 }
1749
1750 return true;
1751}
1752
1753#if TINYEXR_USE_PIZ
1754
1755#ifdef __clang__
1756#pragma clang diagnostic push
1757#pragma clang diagnostic ignored "-Wc++11-long-long"
1758#pragma clang diagnostic ignored "-Wold-style-cast"
1759#pragma clang diagnostic ignored "-Wpadded"
1760#pragma clang diagnostic ignored "-Wsign-conversion"
1761#pragma clang diagnostic ignored "-Wc++11-extensions"
1762#pragma clang diagnostic ignored "-Wconversion"
1763#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1764
1765#if __has_warning("-Wcast-qual")
1766#pragma clang diagnostic ignored "-Wcast-qual"
1767#endif
1768
1769#if __has_warning("-Wextra-semi-stmt")
1770#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1771#endif
1772
1773#endif
1774
1775//
1776// PIZ compress/uncompress, based on OpenEXR's ImfPizCompressor.cpp
1777//
1778// -----------------------------------------------------------------
1779// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
1780// Digital Ltd. LLC)
1781// (3 clause BSD license)
1782//
1783
1784struct PIZChannelData {
1785 unsigned short *start;
1786 unsigned short *end;
1787 int nx;
1788 int ny;
1789 int ys;
1790 int size;
1791};
1792
1793//-----------------------------------------------------------------------------
1794//
1795// 16-bit Haar Wavelet encoding and decoding
1796//
1797// The source code in this file is derived from the encoding
1798// and decoding routines written by Christian Rouet for his
1799// PIZ image file format.
1800//
1801//-----------------------------------------------------------------------------
1802
1803//
1804// Wavelet basis functions without modulo arithmetic; they produce
1805// the best compression ratios when the wavelet-transformed data are
1806// Huffman-encoded, but the wavelet transform works only for 14-bit
1807// data (untransformed data values must be less than (1 << 14)).
1808//
1809
1810inline void wenc14(unsigned short a, unsigned short b, unsigned short &l,
1811 unsigned short &h) {
1812 short as = static_cast<short>(a);
1813 short bs = static_cast<short>(b);
1814
1815 short ms = (as + bs) >> 1;
1816 short ds = as - bs;
1817
1818 l = static_cast<unsigned short>(ms);
1819 h = static_cast<unsigned short>(ds);
1820}
1821
1822inline void wdec14(unsigned short l, unsigned short h, unsigned short &a,
1823 unsigned short &b) {
1824 short ls = static_cast<short>(l);
1825 short hs = static_cast<short>(h);
1826
1827 int hi = hs;
1828 int ai = ls + (hi & 1) + (hi >> 1);
1829
1830 short as = static_cast<short>(ai);
1831 short bs = static_cast<short>(ai - hi);
1832
1833 a = static_cast<unsigned short>(as);
1834 b = static_cast<unsigned short>(bs);
1835}
1836
1837//
1838// Wavelet basis functions with modulo arithmetic; they work with full
1839// 16-bit data, but Huffman-encoding the wavelet-transformed data doesn't
1840// compress the data quite as well.
1841//
1842
1843const int NBITS = 16;
1844const int A_OFFSET = 1 << (NBITS - 1);
1845const int M_OFFSET = 1 << (NBITS - 1);
1846const int MOD_MASK = (1 << NBITS) - 1;
1847
1848inline void wenc16(unsigned short a, unsigned short b, unsigned short &l,
1849 unsigned short &h) {
1850 int ao = (a + A_OFFSET) & MOD_MASK;
1851 int m = ((ao + b) >> 1);
1852 int d = ao - b;
1853
1854 if (d < 0) m = (m + M_OFFSET) & MOD_MASK;
1855
1856 d &= MOD_MASK;
1857
1858 l = static_cast<unsigned short>(m);
1859 h = static_cast<unsigned short>(d);
1860}
1861
1862inline void wdec16(unsigned short l, unsigned short h, unsigned short &a,
1863 unsigned short &b) {
1864 int m = l;
1865 int d = h;
1866 int bb = (m - (d >> 1)) & MOD_MASK;
1867 int aa = (d + bb - A_OFFSET) & MOD_MASK;
1868 b = static_cast<unsigned short>(bb);
1869 a = static_cast<unsigned short>(aa);
1870}
1871
1872//
1873// 2D Wavelet encoding:
1874//
1875
1876static void wav2Encode(
1877 unsigned short *in, // io: values are transformed in place
1878 int nx, // i : x size
1879 int ox, // i : x offset
1880 int ny, // i : y size
1881 int oy, // i : y offset
1882 unsigned short mx) // i : maximum in[x][y] value
1883{
1884 bool w14 = (mx < (1 << 14));
1885 int n = (nx > ny) ? ny : nx;
1886 int p = 1; // == 1 << level
1887 int p2 = 2; // == 1 << (level+1)
1888
1889 //
1890 // Hierarchical loop on smaller dimension n
1891 //
1892
1893 while (p2 <= n) {
1894 unsigned short *py = in;
1895 unsigned short *ey = in + oy * (ny - p2);
1896 int oy1 = oy * p;
1897 int oy2 = oy * p2;
1898 int ox1 = ox * p;
1899 int ox2 = ox * p2;
1900 unsigned short i00, i01, i10, i11;
1901
1902 //
1903 // Y loop
1904 //
1905
1906 for (; py <= ey; py += oy2) {
1907 unsigned short *px = py;
1908 unsigned short *ex = py + ox * (nx - p2);
1909
1910 //
1911 // X loop
1912 //
1913
1914 for (; px <= ex; px += ox2) {
1915 unsigned short *p01 = px + ox1;
1916 unsigned short *p10 = px + oy1;
1917 unsigned short *p11 = p10 + ox1;
1918
1919 //
1920 // 2D wavelet encoding
1921 //
1922
1923 if (w14) {
1924 wenc14(*px, *p01, i00, i01);
1925 wenc14(*p10, *p11, i10, i11);
1926 wenc14(i00, i10, *px, *p10);
1927 wenc14(i01, i11, *p01, *p11);
1928 } else {
1929 wenc16(*px, *p01, i00, i01);
1930 wenc16(*p10, *p11, i10, i11);
1931 wenc16(i00, i10, *px, *p10);
1932 wenc16(i01, i11, *p01, *p11);
1933 }
1934 }
1935
1936 //
1937 // Encode (1D) odd column (still in Y loop)
1938 //
1939
1940 if (nx & p) {
1941 unsigned short *p10 = px + oy1;
1942
1943 if (w14)
1944 wenc14(*px, *p10, i00, *p10);
1945 else
1946 wenc16(*px, *p10, i00, *p10);
1947
1948 *px = i00;
1949 }
1950 }
1951
1952 //
1953 // Encode (1D) odd line (must loop in X)
1954 //
1955
1956 if (ny & p) {
1957 unsigned short *px = py;
1958 unsigned short *ex = py + ox * (nx - p2);
1959
1960 for (; px <= ex; px += ox2) {
1961 unsigned short *p01 = px + ox1;
1962
1963 if (w14)
1964 wenc14(*px, *p01, i00, *p01);
1965 else
1966 wenc16(*px, *p01, i00, *p01);
1967
1968 *px = i00;
1969 }
1970 }
1971
1972 //
1973 // Next level
1974 //
1975
1976 p = p2;
1977 p2 <<= 1;
1978 }
1979}
1980
1981//
1982// 2D Wavelet decoding:
1983//
1984
1985static void wav2Decode(
1986 unsigned short *in, // io: values are transformed in place
1987 int nx, // i : x size
1988 int ox, // i : x offset
1989 int ny, // i : y size
1990 int oy, // i : y offset
1991 unsigned short mx) // i : maximum in[x][y] value
1992{
1993 bool w14 = (mx < (1 << 14));
1994 int n = (nx > ny) ? ny : nx;
1995 int p = 1;
1996 int p2;
1997
1998 //
1999 // Search max level
2000 //
2001
2002 while (p <= n) p <<= 1;
2003
2004 p >>= 1;
2005 p2 = p;
2006 p >>= 1;
2007
2008 //
2009 // Hierarchical loop on smaller dimension n
2010 //
2011
2012 while (p >= 1) {
2013 unsigned short *py = in;
2014 unsigned short *ey = in + oy * (ny - p2);
2015 int oy1 = oy * p;
2016 int oy2 = oy * p2;
2017 int ox1 = ox * p;
2018 int ox2 = ox * p2;
2019 unsigned short i00, i01, i10, i11;
2020
2021 //
2022 // Y loop
2023 //
2024
2025 for (; py <= ey; py += oy2) {
2026 unsigned short *px = py;
2027 unsigned short *ex = py + ox * (nx - p2);
2028
2029 //
2030 // X loop
2031 //
2032
2033 for (; px <= ex; px += ox2) {
2034 unsigned short *p01 = px + ox1;
2035 unsigned short *p10 = px + oy1;
2036 unsigned short *p11 = p10 + ox1;
2037
2038 //
2039 // 2D wavelet decoding
2040 //
2041
2042 if (w14) {
2043 wdec14(*px, *p10, i00, i10);
2044 wdec14(*p01, *p11, i01, i11);
2045 wdec14(i00, i01, *px, *p01);
2046 wdec14(i10, i11, *p10, *p11);
2047 } else {
2048 wdec16(*px, *p10, i00, i10);
2049 wdec16(*p01, *p11, i01, i11);
2050 wdec16(i00, i01, *px, *p01);
2051 wdec16(i10, i11, *p10, *p11);
2052 }
2053 }
2054
2055 //
2056 // Decode (1D) odd column (still in Y loop)
2057 //
2058
2059 if (nx & p) {
2060 unsigned short *p10 = px + oy1;
2061
2062 if (w14)
2063 wdec14(*px, *p10, i00, *p10);
2064 else
2065 wdec16(*px, *p10, i00, *p10);
2066
2067 *px = i00;
2068 }
2069 }
2070
2071 //
2072 // Decode (1D) odd line (must loop in X)
2073 //
2074
2075 if (ny & p) {
2076 unsigned short *px = py;
2077 unsigned short *ex = py + ox * (nx - p2);
2078
2079 for (; px <= ex; px += ox2) {
2080 unsigned short *p01 = px + ox1;
2081
2082 if (w14)
2083 wdec14(*px, *p01, i00, *p01);
2084 else
2085 wdec16(*px, *p01, i00, *p01);
2086
2087 *px = i00;
2088 }
2089 }
2090
2091 //
2092 // Next level
2093 //
2094
2095 p2 = p;
2096 p >>= 1;
2097 }
2098}
2099
2100//-----------------------------------------------------------------------------
2101//
2102// 16-bit Huffman compression and decompression.
2103//
2104// The source code in this file is derived from the 8-bit
2105// Huffman compression and decompression routines written
2106// by Christian Rouet for his PIZ image file format.
2107//
2108//-----------------------------------------------------------------------------
2109
2110// Adds some modification for tinyexr.
2111
2112const int HUF_ENCBITS = 16; // literal (value) bit length
2113const int HUF_DECBITS = 14; // decoding bit size (>= 8)
2114
2115const int HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; // encoding table size
2116const int HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size
2117const int HUF_DECMASK = HUF_DECSIZE - 1;
2118
2119struct HufDec { // short code long code
2120 //-------------------------------
2121 unsigned int len : 8; // code length 0
2122 unsigned int lit : 24; // lit p size
2123 unsigned int *p; // 0 lits
2124};
2125
2126inline long long hufLength(long long code) { return code & 63; }
2127
2128inline long long hufCode(long long code) { return code >> 6; }
2129
2130inline void outputBits(int nBits, long long bits, long long &c, int &lc,
2131 char *&out) {
2132 c <<= nBits;
2133 lc += nBits;
2134
2135 c |= bits;
2136
2137 while (lc >= 8) *out++ = static_cast<char>((c >> (lc -= 8)));
2138}
2139
2140inline long long getBits(int nBits, long long &c, int &lc, const char *&in) {
2141 while (lc < nBits) {
2142 c = (c << 8) | *(reinterpret_cast<const unsigned char *>(in++));
2143 lc += 8;
2144 }
2145
2146 lc -= nBits;
2147 return (c >> lc) & ((1 << nBits) - 1);
2148}
2149
2150//
2151// ENCODING TABLE BUILDING & (UN)PACKING
2152//
2153
2154//
2155// Build a "canonical" Huffman code table:
2156// - for each (uncompressed) symbol, hcode contains the length
2157// of the corresponding code (in the compressed data)
2158// - canonical codes are computed and stored in hcode
2159// - the rules for constructing canonical codes are as follows:
2160// * shorter codes (if filled with zeroes to the right)
2161// have a numerically higher value than longer codes
2162// * for codes with the same length, numerical values
2163// increase with numerical symbol values
2164// - because the canonical code table can be constructed from
2165// symbol lengths alone, the code table can be transmitted
2166// without sending the actual code values
2167// - see http://www.compressconsult.com/huffman/
2168//
2169
2170static void hufCanonicalCodeTable(long long hcode[HUF_ENCSIZE]) {
2171 long long n[59];
2172
2173 //
2174 // For each i from 0 through 58, count the
2175 // number of different codes of length i, and
2176 // store the count in n[i].
2177 //
2178
2179 for (int i = 0; i <= 58; ++i) n[i] = 0;
2180
2181 for (int i = 0; i < HUF_ENCSIZE; ++i) n[hcode[i]] += 1;
2182
2183 //
2184 // For each i from 58 through 1, compute the
2185 // numerically lowest code with length i, and
2186 // store that code in n[i].
2187 //
2188
2189 long long c = 0;
2190
2191 for (int i = 58; i > 0; --i) {
2192 long long nc = ((c + n[i]) >> 1);
2193 n[i] = c;
2194 c = nc;
2195 }
2196
2197 //
2198 // hcode[i] contains the length, l, of the
2199 // code for symbol i. Assign the next available
2200 // code of length l to the symbol and store both
2201 // l and the code in hcode[i].
2202 //
2203
2204 for (int i = 0; i < HUF_ENCSIZE; ++i) {
2205 int l = static_cast<int>(hcode[i]);
2206
2207 if (l > 0) hcode[i] = l | (n[l]++ << 6);
2208 }
2209}
2210
2211//
2212// Compute Huffman codes (based on frq input) and store them in frq:
2213// - code structure is : [63:lsb - 6:msb] | [5-0: bit length];
2214// - max code length is 58 bits;
2215// - codes outside the range [im-iM] have a null length (unused values);
2216// - original frequencies are destroyed;
2217// - encoding tables are used by hufEncode() and hufBuildDecTable();
2218//
2219
2220struct FHeapCompare {
2221 bool operator()(long long *a, long long *b) { return *a > *b; }
2222};
2223
2224static bool hufBuildEncTable(
2225 long long *frq, // io: input frequencies [HUF_ENCSIZE], output table
2226 int *im, // o: min frq index
2227 int *iM) // o: max frq index
2228{
2229 //
2230 // This function assumes that when it is called, array frq
2231 // indicates the frequency of all possible symbols in the data
2232 // that are to be Huffman-encoded. (frq[i] contains the number
2233 // of occurrences of symbol i in the data.)
2234 //
2235 // The loop below does three things:
2236 //
2237 // 1) Finds the minimum and maximum indices that point
2238 // to non-zero entries in frq:
2239 //
2240 // frq[im] != 0, and frq[i] == 0 for all i < im
2241 // frq[iM] != 0, and frq[i] == 0 for all i > iM
2242 //
2243 // 2) Fills array fHeap with pointers to all non-zero
2244 // entries in frq.
2245 //
2246 // 3) Initializes array hlink such that hlink[i] == i
2247 // for all array entries.
2248 //
2249
2250 std::vector<int> hlink(HUF_ENCSIZE);
2251 std::vector<long long *> fHeap(HUF_ENCSIZE);
2252
2253 *im = 0;
2254
2255 while (!frq[*im]) (*im)++;
2256
2257 int nf = 0;
2258
2259 for (int i = *im; i < HUF_ENCSIZE; i++) {
2260 hlink[i] = i;
2261
2262 if (frq[i]) {
2263 fHeap[nf] = &frq[i];
2264 nf++;
2265 *iM = i;
2266 }
2267 }
2268
2269 //
2270 // Add a pseudo-symbol, with a frequency count of 1, to frq;
2271 // adjust the fHeap and hlink array accordingly. Function
2272 // hufEncode() uses the pseudo-symbol for run-length encoding.
2273 //
2274
2275 (*iM)++;
2276 frq[*iM] = 1;
2277 fHeap[nf] = &frq[*iM];
2278 nf++;
2279
2280 //
2281 // Build an array, scode, such that scode[i] contains the number
2282 // of bits assigned to symbol i. Conceptually this is done by
2283 // constructing a tree whose leaves are the symbols with non-zero
2284 // frequency:
2285 //
2286 // Make a heap that contains all symbols with a non-zero frequency,
2287 // with the least frequent symbol on top.
2288 //
2289 // Repeat until only one symbol is left on the heap:
2290 //
2291 // Take the two least frequent symbols off the top of the heap.
2292 // Create a new node that has first two nodes as children, and
2293 // whose frequency is the sum of the frequencies of the first
2294 // two nodes. Put the new node back into the heap.
2295 //
2296 // The last node left on the heap is the root of the tree. For each
2297 // leaf node, the distance between the root and the leaf is the length
2298 // of the code for the corresponding symbol.
2299 //
2300 // The loop below doesn't actually build the tree; instead we compute
2301 // the distances of the leaves from the root on the fly. When a new
2302 // node is added to the heap, then that node's descendants are linked
2303 // into a single linear list that starts at the new node, and the code
2304 // lengths of the descendants (that is, their distance from the root
2305 // of the tree) are incremented by one.
2306 //
2307
2308 std::make_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2309
2310 std::vector<long long> scode(HUF_ENCSIZE);
2311 memset(scode.data(), 0, sizeof(long long) * HUF_ENCSIZE);
2312
2313 while (nf > 1) {
2314 //
2315 // Find the indices, mm and m, of the two smallest non-zero frq
2316 // values in fHeap, add the smallest frq to the second-smallest
2317 // frq, and remove the smallest frq value from fHeap.
2318 //
2319
2320 int mm = fHeap[0] - frq;
2321 std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2322 --nf;
2323
2324 int m = fHeap[0] - frq;
2325 std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2326
2327 frq[m] += frq[mm];
2328 std::push_heap(&fHeap[0], &fHeap[nf], FHeapCompare());
2329
2330 //
2331 // The entries in scode are linked into lists with the
2332 // entries in hlink serving as "next" pointers and with
2333 // the end of a list marked by hlink[j] == j.
2334 //
2335 // Traverse the lists that start at scode[m] and scode[mm].
2336 // For each element visited, increment the length of the
2337 // corresponding code by one bit. (If we visit scode[j]
2338 // during the traversal, then the code for symbol j becomes
2339 // one bit longer.)
2340 //
2341 // Merge the lists that start at scode[m] and scode[mm]
2342 // into a single list that starts at scode[m].
2343 //
2344
2345 //
2346 // Add a bit to all codes in the first list.
2347 //
2348
2349 for (int j = m;; j = hlink[j]) {
2350 scode[j]++;
2351
2352 TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false);
2353
2354 if (hlink[j] == j) {
2355 //
2356 // Merge the two lists.
2357 //
2358
2359 hlink[j] = mm;
2360 break;
2361 }
2362 }
2363
2364 //
2365 // Add a bit to all codes in the second list
2366 //
2367
2368 for (int j = mm;; j = hlink[j]) {
2369 scode[j]++;
2370
2371 TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false);
2372
2373 if (hlink[j] == j) break;
2374 }
2375 }
2376
2377 //
2378 // Build a canonical Huffman code table, replacing the code
2379 // lengths in scode with (code, code length) pairs. Copy the
2380 // code table from scode into frq.
2381 //
2382
2383 hufCanonicalCodeTable(scode.data());
2384 memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE);
2385
2386 return true;
2387}
2388
2389//
2390// Pack an encoding table:
2391// - only code lengths, not actual codes, are stored
2392// - runs of zeroes are compressed as follows:
2393//
2394// unpacked packed
2395// --------------------------------
2396// 1 zero 0 (6 bits)
2397// 2 zeroes 59
2398// 3 zeroes 60
2399// 4 zeroes 61
2400// 5 zeroes 62
2401// n zeroes (6 or more) 63 n-6 (6 + 8 bits)
2402//
2403
2404const int SHORT_ZEROCODE_RUN = 59;
2405const int LONG_ZEROCODE_RUN = 63;
2406const int SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
2407const int LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN;
2408
2409static void hufPackEncTable(
2410 const long long *hcode, // i : encoding table [HUF_ENCSIZE]
2411 int im, // i : min hcode index
2412 int iM, // i : max hcode index
2413 char **pcode) // o: ptr to packed table (updated)
2414{
2415 char *p = *pcode;
2416 long long c = 0;
2417 int lc = 0;
2418
2419 for (; im <= iM; im++) {
2420 int l = hufLength(hcode[im]);
2421
2422 if (l == 0) {
2423 int zerun = 1;
2424
2425 while ((im < iM) && (zerun < LONGEST_LONG_RUN)) {
2426 if (hufLength(hcode[im + 1]) > 0) break;
2427 im++;
2428 zerun++;
2429 }
2430
2431 if (zerun >= 2) {
2432 if (zerun >= SHORTEST_LONG_RUN) {
2433 outputBits(6, LONG_ZEROCODE_RUN, c, lc, p);
2434 outputBits(8, zerun - SHORTEST_LONG_RUN, c, lc, p);
2435 } else {
2436 outputBits(6, SHORT_ZEROCODE_RUN + zerun - 2, c, lc, p);
2437 }
2438 continue;
2439 }
2440 }
2441
2442 outputBits(6, l, c, lc, p);
2443 }
2444
2445 if (lc > 0) *p++ = (unsigned char)(c << (8 - lc));
2446
2447 *pcode = p;
2448}
2449
2450//
2451// Unpack an encoding table packed by hufPackEncTable():
2452//
2453
2454static bool hufUnpackEncTable(
2455 const char **pcode, // io: ptr to packed table (updated)
2456 int ni, // i : input size (in bytes)
2457 int im, // i : min hcode index
2458 int iM, // i : max hcode index
2459 long long *hcode) // o: encoding table [HUF_ENCSIZE]
2460{
2461 memset(hcode, 0, sizeof(long long) * HUF_ENCSIZE);
2462
2463 const char *p = *pcode;
2464 long long c = 0;
2465 int lc = 0;
2466
2467 for (; im <= iM; im++) {
2468 if (p - *pcode >= ni) {
2469 return false;
2470 }
2471
2472 long long l = hcode[im] = getBits(6, c, lc, p); // code length
2473
2474 if (l == (long long)LONG_ZEROCODE_RUN) {
2475 if (p - *pcode > ni) {
2476 return false;
2477 }
2478
2479 int zerun = getBits(8, c, lc, p) + SHORTEST_LONG_RUN;
2480
2481 if (im + zerun > iM + 1) {
2482 return false;
2483 }
2484
2485 while (zerun--) hcode[im++] = 0;
2486
2487 im--;
2488 } else if (l >= (long long)SHORT_ZEROCODE_RUN) {
2489 int zerun = l - SHORT_ZEROCODE_RUN + 2;
2490
2491 if (im + zerun > iM + 1) {
2492 return false;
2493 }
2494
2495 while (zerun--) hcode[im++] = 0;
2496
2497 im--;
2498 }
2499 }
2500
2501 *pcode = const_cast<char *>(p);
2502
2503 hufCanonicalCodeTable(hcode);
2504
2505 return true;
2506}
2507
2508//
2509// DECODING TABLE BUILDING
2510//
2511
2512//
2513// Clear a newly allocated decoding table so that it contains only zeroes.
2514//
2515
2516static void hufClearDecTable(HufDec *hdecod) // io: (allocated by caller)
2517// decoding table [HUF_DECSIZE]
2518{
2519 for (int i = 0; i < HUF_DECSIZE; i++) {
2520 hdecod[i].len = 0;
2521 hdecod[i].lit = 0;
2522 hdecod[i].p = NULL;
2523 }
2524 // memset(hdecod, 0, sizeof(HufDec) * HUF_DECSIZE);
2525}
2526
2527//
2528// Build a decoding hash table based on the encoding table hcode:
2529// - short codes (<= HUF_DECBITS) are resolved with a single table access;
2530// - long code entry allocations are not optimized, because long codes are
2531// unfrequent;
2532// - decoding tables are used by hufDecode();
2533//
2534
2535static bool hufBuildDecTable(const long long *hcode, // i : encoding table
2536 int im, // i : min index in hcode
2537 int iM, // i : max index in hcode
2538 HufDec *hdecod) // o: (allocated by caller)
2539// decoding table [HUF_DECSIZE]
2540{
2541 //
2542 // Init hashtable & loop on all codes.
2543 // Assumes that hufClearDecTable(hdecod) has already been called.
2544 //
2545
2546 for (; im <= iM; im++) {
2547 long long c = hufCode(hcode[im]);
2548 int l = hufLength(hcode[im]);
2549
2550 if (c >> l) {
2551 //
2552 // Error: c is supposed to be an l-bit code,
2553 // but c contains a value that is greater
2554 // than the largest l-bit number.
2555 //
2556
2557 // invalidTableEntry();
2558 return false;
2559 }
2560
2561 if (l > HUF_DECBITS) {
2562 //
2563 // Long code: add a secondary entry
2564 //
2565
2566 HufDec *pl = hdecod + (c >> (l - HUF_DECBITS));
2567
2568 if (pl->len) {
2569 //
2570 // Error: a short code has already
2571 // been stored in table entry *pl.
2572 //
2573
2574 // invalidTableEntry();
2575 return false;
2576 }
2577
2578 pl->lit++;
2579
2580 if (pl->p) {
2581 unsigned int *p = pl->p;
2582 pl->p = new unsigned int[pl->lit];
2583
2584 for (unsigned int i = 0; i < pl->lit - 1u; ++i) pl->p[i] = p[i];
2585
2586 delete[] p;
2587 } else {
2588 pl->p = new unsigned int[1];
2589 }
2590
2591 pl->p[pl->lit - 1] = im;
2592 } else if (l) {
2593 //
2594 // Short code: init all primary entries
2595 //
2596
2597 HufDec *pl = hdecod + (c << (HUF_DECBITS - l));
2598
2599 for (long long i = 1ULL << (HUF_DECBITS - l); i > 0; i--, pl++) {
2600 if (pl->len || pl->p) {
2601 //
2602 // Error: a short code or a long code has
2603 // already been stored in table entry *pl.
2604 //
2605
2606 // invalidTableEntry();
2607 return false;
2608 }
2609
2610 pl->len = l;
2611 pl->lit = im;
2612 }
2613 }
2614 }
2615
2616 return true;
2617}
2618
2619//
2620// Free the long code entries of a decoding table built by hufBuildDecTable()
2621//
2622
2623static void hufFreeDecTable(HufDec *hdecod) // io: Decoding table
2624{
2625 for (int i = 0; i < HUF_DECSIZE; i++) {
2626 if (hdecod[i].p) {
2627 delete[] hdecod[i].p;
2628 hdecod[i].p = 0;
2629 }
2630 }
2631}
2632
2633//
2634// ENCODING
2635//
2636
2637inline void outputCode(long long code, long long &c, int &lc, char *&out) {
2638 outputBits(hufLength(code), hufCode(code), c, lc, out);
2639}
2640
2641inline void sendCode(long long sCode, int runCount, long long runCode,
2642 long long &c, int &lc, char *&out) {
2643 //
2644 // Output a run of runCount instances of the symbol sCount.
2645 // Output the symbols explicitly, or if that is shorter, output
2646 // the sCode symbol once followed by a runCode symbol and runCount
2647 // expressed as an 8-bit number.
2648 //
2649
2650 if (hufLength(sCode) + hufLength(runCode) + 8 < hufLength(sCode) * runCount) {
2651 outputCode(sCode, c, lc, out);
2652 outputCode(runCode, c, lc, out);
2653 outputBits(8, runCount, c, lc, out);
2654 } else {
2655 while (runCount-- >= 0) outputCode(sCode, c, lc, out);
2656 }
2657}
2658
2659//
2660// Encode (compress) ni values based on the Huffman encoding table hcode:
2661//
2662
2663static int hufEncode // return: output size (in bits)
2664 (const long long *hcode, // i : encoding table
2665 const unsigned short *in, // i : uncompressed input buffer
2666 const int ni, // i : input buffer size (in bytes)
2667 int rlc, // i : rl code
2668 char *out) // o: compressed output buffer
2669{
2670 char *outStart = out;
2671 long long c = 0; // bits not yet written to out
2672 int lc = 0; // number of valid bits in c (LSB)
2673 int s = in[0];
2674 int cs = 0;
2675
2676 //
2677 // Loop on input values
2678 //
2679
2680 for (int i = 1; i < ni; i++) {
2681 //
2682 // Count same values or send code
2683 //
2684
2685 if (s == in[i] && cs < 255) {
2686 cs++;
2687 } else {
2688 sendCode(hcode[s], cs, hcode[rlc], c, lc, out);
2689 cs = 0;
2690 }
2691
2692 s = in[i];
2693 }
2694
2695 //
2696 // Send remaining code
2697 //
2698
2699 sendCode(hcode[s], cs, hcode[rlc], c, lc, out);
2700
2701 if (lc) *out = (c << (8 - lc)) & 0xff;
2702
2703 return (out - outStart) * 8 + lc;
2704}
2705
2706//
2707// DECODING
2708//
2709
2710//
2711// In order to force the compiler to inline them,
2712// getChar() and getCode() are implemented as macros
2713// instead of "inline" functions.
2714//
2715
2716#define getChar(c, lc, in) \
2717 { \
2718 c = (c << 8) | *(unsigned char *)(in++); \
2719 lc += 8; \
2720 }
2721
2722#if 0
2723#define getCode(po, rlc, c, lc, in, out, ob, oe) \
2724 { \
2725 if (po == rlc) { \
2726 if (lc < 8) getChar(c, lc, in); \
2727 \
2728 lc -= 8; \
2729 \
2730 unsigned char cs = (c >> lc); \
2731 \
2732 if (out + cs > oe) return false; \
2733 \
2734 /* TinyEXR issue 78 */ \
2735 unsigned short s = out[-1]; \
2736 \
2737 while (cs-- > 0) *out++ = s; \
2738 } else if (out < oe) { \
2739 *out++ = po; \
2740 } else { \
2741 return false; \
2742 } \
2743 }
2744#else
2745static bool getCode(int po, int rlc, long long &c, int &lc, const char *&in,
2746 const char *in_end, unsigned short *&out,
2747 const unsigned short *ob, const unsigned short *oe) {
2748 (void)ob;
2749 if (po == rlc) {
2750 if (lc < 8) {
2751 /* TinyEXR issue 78 */
2752 /* TinyEXR issue 160. in + 1 -> in */
2753 if (in >= in_end) {
2754 return false;
2755 }
2756
2757 getChar(c, lc, in);
2758 }
2759
2760 lc -= 8;
2761
2762 unsigned char cs = (c >> lc);
2763
2764 if (out + cs > oe) return false;
2765
2766 // Bounds check for safety
2767 // Issue 100.
2768 if ((out - 1) < ob) return false;
2769 unsigned short s = out[-1];
2770
2771 while (cs-- > 0) *out++ = s;
2772 } else if (out < oe) {
2773 *out++ = po;
2774 } else {
2775 return false;
2776 }
2777 return true;
2778}
2779#endif
2780
2781//
2782// Decode (uncompress) ni bits based on encoding & decoding tables:
2783//
2784
2785static bool hufDecode(const long long *hcode, // i : encoding table
2786 const HufDec *hdecod, // i : decoding table
2787 const char *in, // i : compressed input buffer
2788 int ni, // i : input size (in bits)
2789 int rlc, // i : run-length code
2790 int no, // i : expected output size (in bytes)
2791 unsigned short *out) // o: uncompressed output buffer
2792{
2793 long long c = 0;
2794 int lc = 0;
2795 unsigned short *outb = out; // begin
2796 unsigned short *oe = out + no; // end
2797 const char *ie = in + (ni + 7) / 8; // input byte size
2798
2799 //
2800 // Loop on input bytes
2801 //
2802
2803 while (in < ie) {
2804 getChar(c, lc, in);
2805
2806 //
2807 // Access decoding table
2808 //
2809
2810 while (lc >= HUF_DECBITS) {
2811 const HufDec pl = hdecod[(c >> (lc - HUF_DECBITS)) & HUF_DECMASK];
2812
2813 if (pl.len) {
2814 //
2815 // Get short code
2816 //
2817
2818 lc -= pl.len;
2819 // std::cout << "lit = " << pl.lit << std::endl;
2820 // std::cout << "rlc = " << rlc << std::endl;
2821 // std::cout << "c = " << c << std::endl;
2822 // std::cout << "lc = " << lc << std::endl;
2823 // std::cout << "in = " << in << std::endl;
2824 // std::cout << "out = " << out << std::endl;
2825 // std::cout << "oe = " << oe << std::endl;
2826 if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) {
2827 return false;
2828 }
2829 } else {
2830 if (!pl.p) {
2831 return false;
2832 }
2833 // invalidCode(); // wrong code
2834
2835 //
2836 // Search long code
2837 //
2838
2839 unsigned int j;
2840
2841 for (j = 0; j < pl.lit; j++) {
2842 int l = hufLength(hcode[pl.p[j]]);
2843
2844 while (lc < l && in < ie) // get more bits
2845 getChar(c, lc, in);
2846
2847 if (lc >= l) {
2848 if (hufCode(hcode[pl.p[j]]) ==
2849 ((c >> (lc - l)) & (((long long)(1) << l) - 1))) {
2850 //
2851 // Found : get long code
2852 //
2853
2854 lc -= l;
2855 if (!getCode(pl.p[j], rlc, c, lc, in, ie, out, outb, oe)) {
2856 return false;
2857 }
2858 break;
2859 }
2860 }
2861 }
2862
2863 if (j == pl.lit) {
2864 return false;
2865 // invalidCode(); // Not found
2866 }
2867 }
2868 }
2869 }
2870
2871 //
2872 // Get remaining (short) codes
2873 //
2874
2875 int i = (8 - ni) & 7;
2876 c >>= i;
2877 lc -= i;
2878
2879 while (lc > 0) {
2880 const HufDec pl = hdecod[(c << (HUF_DECBITS - lc)) & HUF_DECMASK];
2881
2882 if (pl.len) {
2883 lc -= pl.len;
2884 if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) {
2885 return false;
2886 }
2887 } else {
2888 return false;
2889 // invalidCode(); // wrong (long) code
2890 }
2891 }
2892
2893 if (out - outb != no) {
2894 return false;
2895 }
2896 // notEnoughData ();
2897
2898 return true;
2899}
2900
2901static void countFrequencies(std::vector<long long> &freq,
2902 const unsigned short data[/*n*/], int n) {
2903 for (int i = 0; i < HUF_ENCSIZE; ++i) freq[i] = 0;
2904
2905 for (int i = 0; i < n; ++i) ++freq[data[i]];
2906}
2907
2908static void writeUInt(char buf[4], unsigned int i) {
2909 unsigned char *b = (unsigned char *)buf;
2910
2911 b[0] = i;
2912 b[1] = i >> 8;
2913 b[2] = i >> 16;
2914 b[3] = i >> 24;
2915}
2916
2917static unsigned int readUInt(const char buf[4]) {
2918 const unsigned char *b = (const unsigned char *)buf;
2919
2920 return (b[0] & 0x000000ff) | ((b[1] << 8) & 0x0000ff00) |
2921 ((b[2] << 16) & 0x00ff0000) | ((b[3] << 24) & 0xff000000);
2922}
2923
2924//
2925// EXTERNAL INTERFACE
2926//
2927
2928static int hufCompress(const unsigned short raw[], int nRaw,
2929 char compressed[]) {
2930 if (nRaw == 0) return 0;
2931
2932 std::vector<long long> freq(HUF_ENCSIZE);
2933
2934 countFrequencies(freq, raw, nRaw);
2935
2936 int im = 0;
2937 int iM = 0;
2938 hufBuildEncTable(freq.data(), &im, &iM);
2939
2940 char *tableStart = compressed + 20;
2941 char *tableEnd = tableStart;
2942 hufPackEncTable(freq.data(), im, iM, &tableEnd);
2943 int tableLength = tableEnd - tableStart;
2944
2945 char *dataStart = tableEnd;
2946 int nBits = hufEncode(freq.data(), raw, nRaw, iM, dataStart);
2947 int data_length = (nBits + 7) / 8;
2948
2949 writeUInt(compressed, im);
2950 writeUInt(compressed + 4, iM);
2951 writeUInt(compressed + 8, tableLength);
2952 writeUInt(compressed + 12, nBits);
2953 writeUInt(compressed + 16, 0); // room for future extensions
2954
2955 return dataStart + data_length - compressed;
2956}
2957
2958static bool hufUncompress(const char compressed[], int nCompressed,
2959 std::vector<unsigned short> *raw) {
2960 if (nCompressed == 0) {
2961 if (raw->size() != 0) return false;
2962
2963 return false;
2964 }
2965
2966 int im = readUInt(compressed);
2967 int iM = readUInt(compressed + 4);
2968 // int tableLength = readUInt (compressed + 8);
2969 int nBits = readUInt(compressed + 12);
2970
2971 if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) return false;
2972
2973 const char *ptr = compressed + 20;
2974
2975 //
2976 // Fast decoder needs at least 2x64-bits of compressed data, and
2977 // needs to be run-able on this platform. Otherwise, fall back
2978 // to the original decoder
2979 //
2980
2981 // if (FastHufDecoder::enabled() && nBits > 128)
2982 //{
2983 // FastHufDecoder fhd (ptr, nCompressed - (ptr - compressed), im, iM, iM);
2984 // fhd.decode ((unsigned char*)ptr, nBits, raw, nRaw);
2985 //}
2986 // else
2987 {
2988 std::vector<long long> freq(HUF_ENCSIZE);
2989 std::vector<HufDec> hdec(HUF_DECSIZE);
2990
2991 hufClearDecTable(&hdec.at(0));
2992
2993 hufUnpackEncTable(&ptr, nCompressed - (ptr - compressed), im, iM,
2994 &freq.at(0));
2995
2996 {
2997 if (nBits > 8 * (nCompressed - (ptr - compressed))) {
2998 return false;
2999 }
3000
3001 hufBuildDecTable(&freq.at(0), im, iM, &hdec.at(0));
3002 hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, raw->size(),
3003 raw->data());
3004 }
3005 // catch (...)
3006 //{
3007 // hufFreeDecTable (hdec);
3008 // throw;
3009 //}
3010
3011 hufFreeDecTable(&hdec.at(0));
3012 }
3013
3014 return true;
3015}
3016
3017//
3018// Functions to compress the range of values in the pixel data
3019//
3020
3021const int USHORT_RANGE = (1 << 16);
3022const int BITMAP_SIZE = (USHORT_RANGE >> 3);
3023
3024static void bitmapFromData(const unsigned short data[/*nData*/], int nData,
3025 unsigned char bitmap[BITMAP_SIZE],
3026 unsigned short &minNonZero,
3027 unsigned short &maxNonZero) {
3028 for (int i = 0; i < BITMAP_SIZE; ++i) bitmap[i] = 0;
3029
3030 for (int i = 0; i < nData; ++i) bitmap[data[i] >> 3] |= (1 << (data[i] & 7));
3031
3032 bitmap[0] &= ~1; // zero is not explicitly stored in
3033 // the bitmap; we assume that the
3034 // data always contain zeroes
3035 minNonZero = BITMAP_SIZE - 1;
3036 maxNonZero = 0;
3037
3038 for (int i = 0; i < BITMAP_SIZE; ++i) {
3039 if (bitmap[i]) {
3040 if (minNonZero > i) minNonZero = i;
3041 if (maxNonZero < i) maxNonZero = i;
3042 }
3043 }
3044}
3045
3046static unsigned short forwardLutFromBitmap(
3047 const unsigned char bitmap[BITMAP_SIZE], unsigned short lut[USHORT_RANGE]) {
3048 int k = 0;
3049
3050 for (int i = 0; i < USHORT_RANGE; ++i) {
3051 if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))))
3052 lut[i] = k++;
3053 else
3054 lut[i] = 0;
3055 }
3056
3057 return k - 1; // maximum value stored in lut[],
3058} // i.e. number of ones in bitmap minus 1
3059
3060static unsigned short reverseLutFromBitmap(
3061 const unsigned char bitmap[BITMAP_SIZE], unsigned short lut[USHORT_RANGE]) {
3062 int k = 0;
3063
3064 for (int i = 0; i < USHORT_RANGE; ++i) {
3065 if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) lut[k++] = i;
3066 }
3067
3068 int n = k - 1;
3069
3070 while (k < USHORT_RANGE) lut[k++] = 0;
3071
3072 return n; // maximum k where lut[k] is non-zero,
3073} // i.e. number of ones in bitmap minus 1
3074
3075static void applyLut(const unsigned short lut[USHORT_RANGE],
3076 unsigned short data[/*nData*/], int nData) {
3077 for (int i = 0; i < nData; ++i) data[i] = lut[data[i]];
3078}
3079
3080#ifdef __clang__
3081#pragma clang diagnostic pop
3082#endif // __clang__
3083
3084#ifdef _MSC_VER
3085#pragma warning(pop)
3086#endif
3087
3088static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize,
3089 const unsigned char *inPtr, size_t inSize,
3090 const std::vector<ChannelInfo> &channelInfo,
3091 int data_width, int num_lines) {
3092 std::vector<unsigned char> bitmap(BITMAP_SIZE);
3093 unsigned short minNonZero;
3094 unsigned short maxNonZero;
3095
3096#if !TINYEXR_LITTLE_ENDIAN
3097 // @todo { PIZ compression on BigEndian architecture. }
3098 return false;
3099#endif
3100
3101 // Assume `inSize` is multiple of 2 or 4.
3102 std::vector<unsigned short> tmpBuffer(inSize / sizeof(unsigned short));
3103
3104 std::vector<PIZChannelData> channelData(channelInfo.size());
3105 unsigned short *tmpBufferEnd = &tmpBuffer.at(0);
3106
3107 for (size_t c = 0; c < channelData.size(); c++) {
3108 PIZChannelData &cd = channelData[c];
3109
3110 cd.start = tmpBufferEnd;
3111 cd.end = cd.start;
3112
3113 cd.nx = data_width;
3114 cd.ny = num_lines;
3115 // cd.ys = c.channel().ySampling;
3116
3117 size_t pixelSize = sizeof(int); // UINT and FLOAT
3118 if (channelInfo[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
3119 pixelSize = sizeof(short);
3120 }
3121
3122 cd.size = static_cast<int>(pixelSize / sizeof(short));
3123
3124 tmpBufferEnd += cd.nx * cd.ny * cd.size;
3125 }
3126
3127 const unsigned char *ptr = inPtr;
3128 for (int y = 0; y < num_lines; ++y) {
3129 for (size_t i = 0; i < channelData.size(); ++i) {
3130 PIZChannelData &cd = channelData[i];
3131
3132 // if (modp (y, cd.ys) != 0)
3133 // continue;
3134
3135 size_t n = static_cast<size_t>(cd.nx * cd.size);
3136 memcpy(cd.end, ptr, n * sizeof(unsigned short));
3137 ptr += n * sizeof(unsigned short);
3138 cd.end += n;
3139 }
3140 }
3141
3142 bitmapFromData(&tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()),
3143 bitmap.data(), minNonZero, maxNonZero);
3144
3145 std::vector<unsigned short> lut(USHORT_RANGE);
3146 unsigned short maxValue = forwardLutFromBitmap(bitmap.data(), lut.data());
3147 applyLut(lut.data(), &tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()));
3148
3149 //
3150 // Store range compression info in _outBuffer
3151 //
3152
3153 char *buf = reinterpret_cast<char *>(outPtr);
3154
3155 memcpy(buf, &minNonZero, sizeof(unsigned short));
3156 buf += sizeof(unsigned short);
3157 memcpy(buf, &maxNonZero, sizeof(unsigned short));
3158 buf += sizeof(unsigned short);
3159
3160 if (minNonZero <= maxNonZero) {
3161 memcpy(buf, reinterpret_cast<char *>(&bitmap[0] + minNonZero),
3162 maxNonZero - minNonZero + 1);
3163 buf += maxNonZero - minNonZero + 1;
3164 }
3165
3166 //
3167 // Apply wavelet encoding
3168 //
3169
3170 for (size_t i = 0; i < channelData.size(); ++i) {
3171 PIZChannelData &cd = channelData[i];
3172
3173 for (int j = 0; j < cd.size; ++j) {
3174 wav2Encode(cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size,
3175 maxValue);
3176 }
3177 }
3178
3179 //
3180 // Apply Huffman encoding; append the result to _outBuffer
3181 //
3182
3183 // length header(4byte), then huff data. Initialize length header with zero,
3184 // then later fill it by `length`.
3185 char *lengthPtr = buf;
3186 int zero = 0;
3187 memcpy(buf, &zero, sizeof(int));
3188 buf += sizeof(int);
3189
3190 int length =
3191 hufCompress(&tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()), buf);
3192 memcpy(lengthPtr, &length, sizeof(int));
3193
3194 (*outSize) = static_cast<unsigned int>(
3195 (reinterpret_cast<unsigned char *>(buf) - outPtr) +
3196 static_cast<unsigned int>(length));
3197
3198 // Use uncompressed data when compressed data is larger than uncompressed.
3199 // (Issue 40)
3200 if ((*outSize) >= inSize) {
3201 (*outSize) = static_cast<unsigned int>(inSize);
3202 memcpy(outPtr, inPtr, inSize);
3203 }
3204 return true;
3205}
3206
3207static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr,
3208 size_t tmpBufSizeInBytes, size_t inLen, int num_channels,
3209 const EXRChannelInfo *channels, int data_width,
3210 int num_lines) {
3211 if (inLen == tmpBufSizeInBytes) {
3212 // Data is not compressed(Issue 40).
3213 memcpy(outPtr, inPtr, inLen);
3214 return true;
3215 }
3216
3217 std::vector<unsigned char> bitmap(BITMAP_SIZE);
3218 unsigned short minNonZero;
3219 unsigned short maxNonZero;
3220
3221#if !TINYEXR_LITTLE_ENDIAN
3222 // @todo { PIZ compression on BigEndian architecture. }
3223 return false;
3224#endif
3225
3226 memset(bitmap.data(), 0, BITMAP_SIZE);
3227
3228 if (inLen < 4) {
3229 return false;
3230 }
3231
3232 size_t readLen = 0;
3233
3234 const unsigned char *ptr = inPtr;
3235 // minNonZero = *(reinterpret_cast<const unsigned short *>(ptr));
3236 tinyexr::cpy2(&minNonZero, reinterpret_cast<const unsigned short *>(ptr));
3237 // maxNonZero = *(reinterpret_cast<const unsigned short *>(ptr + 2));
3238 tinyexr::cpy2(&maxNonZero, reinterpret_cast<const unsigned short *>(ptr + 2));
3239 ptr += 4;
3240 readLen += 4;
3241
3242 if (maxNonZero >= BITMAP_SIZE) {
3243 return false;
3244 }
3245
3246 //printf("maxNonZero = %d\n", maxNonZero);
3247 //printf("minNonZero = %d\n", minNonZero);
3248 //printf("len = %d\n", (maxNonZero - minNonZero + 1));
3249 //printf("BITMAPSIZE - min = %d\n", (BITMAP_SIZE - minNonZero));
3250
3251 if (minNonZero <= maxNonZero) {
3252 if (((maxNonZero - minNonZero + 1) + readLen) > inLen) {
3253 // Input too short
3254 return false;
3255 }
3256
3257 memcpy(reinterpret_cast<char *>(&bitmap[0] + minNonZero), ptr,
3258 maxNonZero - minNonZero + 1);
3259 ptr += maxNonZero - minNonZero + 1;
3260 readLen += maxNonZero - minNonZero + 1;
3261 } else {
3262 // Issue 194
3263 if ((minNonZero == (BITMAP_SIZE - 1)) && (maxNonZero == 0)) {
3264 // OK. all pixels are zero. And no need to read `bitmap` data.
3265 } else {
3266 // invalid minNonZero/maxNonZero combination.
3267 return false;
3268 }
3269 }
3270
3271 std::vector<unsigned short> lut(USHORT_RANGE);
3272 memset(lut.data(), 0, sizeof(unsigned short) * USHORT_RANGE);
3273 unsigned short maxValue = reverseLutFromBitmap(bitmap.data(), lut.data());
3274
3275 //
3276 // Huffman decoding
3277 //
3278
3279 if ((readLen + 4) > inLen) {
3280 return false;
3281 }
3282
3283 int length=0;
3284
3285 // length = *(reinterpret_cast<const int *>(ptr));
3286 tinyexr::cpy4(&length, reinterpret_cast<const int *>(ptr));
3287 ptr += sizeof(int);
3288
3289 if (size_t((ptr - inPtr) + length) > inLen) {
3290 return false;
3291 }
3292
3293 std::vector<unsigned short> tmpBuffer(tmpBufSizeInBytes / sizeof(unsigned short));
3294 hufUncompress(reinterpret_cast<const char *>(ptr), length, &tmpBuffer);
3295
3296 //
3297 // Wavelet decoding
3298 //
3299
3300 std::vector<PIZChannelData> channelData(static_cast<size_t>(num_channels));
3301
3302 unsigned short *tmpBufferEnd = &tmpBuffer.at(0);
3303
3304 for (size_t i = 0; i < static_cast<size_t>(num_channels); ++i) {
3305 const EXRChannelInfo &chan = channels[i];
3306
3307 size_t pixelSize = sizeof(int); // UINT and FLOAT
3308 if (chan.pixel_type == TINYEXR_PIXELTYPE_HALF) {
3309 pixelSize = sizeof(short);
3310 }
3311
3312 channelData[i].start = tmpBufferEnd;
3313 channelData[i].end = channelData[i].start;
3314 channelData[i].nx = data_width;
3315 channelData[i].ny = num_lines;
3316 // channelData[i].ys = 1;
3317 channelData[i].size = static_cast<int>(pixelSize / sizeof(short));
3318
3319 tmpBufferEnd += channelData[i].nx * channelData[i].ny * channelData[i].size;
3320 }
3321
3322 for (size_t i = 0; i < channelData.size(); ++i) {
3323 PIZChannelData &cd = channelData[i];
3324
3325 for (int j = 0; j < cd.size; ++j) {
3326 wav2Decode(cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size,
3327 maxValue);
3328 }
3329 }
3330
3331 //
3332 // Expand the pixel data to their original range
3333 //
3334
3335 applyLut(lut.data(), &tmpBuffer.at(0), static_cast<int>(tmpBufSizeInBytes / sizeof(unsigned short)));
3336
3337 for (int y = 0; y < num_lines; y++) {
3338 for (size_t i = 0; i < channelData.size(); ++i) {
3339 PIZChannelData &cd = channelData[i];
3340
3341 // if (modp (y, cd.ys) != 0)
3342 // continue;
3343
3344 size_t n = static_cast<size_t>(cd.nx * cd.size);
3345 memcpy(outPtr, cd.end, static_cast<size_t>(n * sizeof(unsigned short)));
3346 outPtr += n * sizeof(unsigned short);
3347 cd.end += n;
3348 }
3349 }
3350
3351 return true;
3352}
3353#endif // TINYEXR_USE_PIZ
3354
3355#if TINYEXR_USE_ZFP
3356
3357struct ZFPCompressionParam {
3358 double rate;
3359 unsigned int precision;
3360 unsigned int __pad0;
3361 double tolerance;
3362 int type; // TINYEXR_ZFP_COMPRESSIONTYPE_*
3363 unsigned int __pad1;
3364
3365 ZFPCompressionParam() {
3367 rate = 2.0;
3368 precision = 0;
3369 tolerance = 0.0;
3370 }
3371};
3372
3373static bool FindZFPCompressionParam(ZFPCompressionParam *param,
3374 const EXRAttribute *attributes,
3375 int num_attributes, std::string *err) {
3376 bool foundType = false;
3377
3378 for (int i = 0; i < num_attributes; i++) {
3379 if ((strcmp(attributes[i].name, "zfpCompressionType") == 0)) {
3380 if (attributes[i].size == 1) {
3381 param->type = static_cast<int>(attributes[i].value[0]);
3382 foundType = true;
3383 break;
3384 } else {
3385 if (err) {
3386 (*err) +=
3387 "zfpCompressionType attribute must be uchar(1 byte) type.\n";
3388 }
3389 return false;
3390 }
3391 }
3392 }
3393
3394 if (!foundType) {
3395 if (err) {
3396 (*err) += "`zfpCompressionType` attribute not found.\n";
3397 }
3398 return false;
3399 }
3400
3401 if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
3402 for (int i = 0; i < num_attributes; i++) {
3403 if ((strcmp(attributes[i].name, "zfpCompressionRate") == 0) &&
3404 (attributes[i].size == 8)) {
3405 param->rate = *(reinterpret_cast<double *>(attributes[i].value));
3406 return true;
3407 }
3408 }
3409
3410 if (err) {
3411 (*err) += "`zfpCompressionRate` attribute not found.\n";
3412 }
3413
3414 } else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
3415 for (int i = 0; i < num_attributes; i++) {
3416 if ((strcmp(attributes[i].name, "zfpCompressionPrecision") == 0) &&
3417 (attributes[i].size == 4)) {
3418 param->rate = *(reinterpret_cast<int *>(attributes[i].value));
3419 return true;
3420 }
3421 }
3422
3423 if (err) {
3424 (*err) += "`zfpCompressionPrecision` attribute not found.\n";
3425 }
3426
3427 } else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
3428 for (int i = 0; i < num_attributes; i++) {
3429 if ((strcmp(attributes[i].name, "zfpCompressionTolerance") == 0) &&
3430 (attributes[i].size == 8)) {
3431 param->tolerance = *(reinterpret_cast<double *>(attributes[i].value));
3432 return true;
3433 }
3434 }
3435
3436 if (err) {
3437 (*err) += "`zfpCompressionTolerance` attribute not found.\n";
3438 }
3439 } else {
3440 if (err) {
3441 (*err) += "Unknown value specified for `zfpCompressionType`.\n";
3442 }
3443 }
3444
3445 return false;
3446}
3447
3448// Assume pixel format is FLOAT for all channels.
3449static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines,
3450 size_t num_channels, const unsigned char *src,
3451 unsigned long src_size,
3452 const ZFPCompressionParam &param) {
3453 size_t uncompressed_size =
3454 size_t(dst_width) * size_t(dst_num_lines) * num_channels;
3455
3456 if (uncompressed_size == src_size) {
3457 // Data is not compressed(Issue 40).
3458 memcpy(dst, src, src_size);
3459 }
3460
3461 zfp_stream *zfp = NULL;
3462 zfp_field *field = NULL;
3463
3464 TINYEXR_CHECK_AND_RETURN_C((dst_width % 4) == 0, false);
3465 TINYEXR_CHECK_AND_RETURN_C((dst_num_lines % 4) == 0, false);
3466
3467 if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) {
3468 return false;
3469 }
3470
3471 field =
3472 zfp_field_2d(reinterpret_cast<void *>(const_cast<unsigned char *>(src)),
3473 zfp_type_float, static_cast<unsigned int>(dst_width),
3474 static_cast<unsigned int>(dst_num_lines) *
3475 static_cast<unsigned int>(num_channels));
3476 zfp = zfp_stream_open(NULL);
3477
3478 if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
3479 zfp_stream_set_rate(zfp, param.rate, zfp_type_float, /* dimension */ 2,
3480 /* write random access */ 0);
3481 } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
3482 zfp_stream_set_precision(zfp, param.precision);
3483 } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
3484 zfp_stream_set_accuracy(zfp, param.tolerance);
3485 } else {
3486 return false;
3487 }
3488
3489 size_t buf_size = zfp_stream_maximum_size(zfp, field);
3490 std::vector<unsigned char> buf(buf_size);
3491 memcpy(&buf.at(0), src, src_size);
3492
3493 bitstream *stream = stream_open(&buf.at(0), buf_size);
3494 zfp_stream_set_bit_stream(zfp, stream);
3495 zfp_stream_rewind(zfp);
3496
3497 size_t image_size = size_t(dst_width) * size_t(dst_num_lines);
3498
3499 for (size_t c = 0; c < size_t(num_channels); c++) {
3500 // decompress 4x4 pixel block.
3501 for (size_t y = 0; y < size_t(dst_num_lines); y += 4) {
3502 for (size_t x = 0; x < size_t(dst_width); x += 4) {
3503 float fblock[16];
3504 zfp_decode_block_float_2(zfp, fblock);
3505 for (size_t j = 0; j < 4; j++) {
3506 for (size_t i = 0; i < 4; i++) {
3507 dst[c * image_size + ((y + j) * size_t(dst_width) + (x + i))] =
3508 fblock[j * 4 + i];
3509 }
3510 }
3511 }
3512 }
3513 }
3514
3515 zfp_field_free(field);
3516 zfp_stream_close(zfp);
3517 stream_close(stream);
3518
3519 return true;
3520}
3521
3522// Assume pixel format is FLOAT for all channels.
3523static bool CompressZfp(std::vector<unsigned char> *outBuf,
3524 unsigned int *outSize, const float *inPtr, int width,
3525 int num_lines, int num_channels,
3526 const ZFPCompressionParam &param) {
3527 zfp_stream *zfp = NULL;
3528 zfp_field *field = NULL;
3529
3530 TINYEXR_CHECK_AND_RETURN_C((width % 4) == 0, false);
3531 TINYEXR_CHECK_AND_RETURN_C((num_lines % 4) == 0, false);
3532
3533 if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) {
3534 return false;
3535 }
3536
3537 // create input array.
3538 field = zfp_field_2d(reinterpret_cast<void *>(const_cast<float *>(inPtr)),
3539 zfp_type_float, static_cast<unsigned int>(width),
3540 static_cast<unsigned int>(num_lines * num_channels));
3541
3542 zfp = zfp_stream_open(NULL);
3543
3544 if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
3545 zfp_stream_set_rate(zfp, param.rate, zfp_type_float, 2, 0);
3546 } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
3547 zfp_stream_set_precision(zfp, param.precision);
3548 } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
3549 zfp_stream_set_accuracy(zfp, param.tolerance);
3550 } else {
3551 return false;
3552 }
3553
3554 size_t buf_size = zfp_stream_maximum_size(zfp, field);
3555
3556 outBuf->resize(buf_size);
3557
3558 bitstream *stream = stream_open(&outBuf->at(0), buf_size);
3559 zfp_stream_set_bit_stream(zfp, stream);
3560 zfp_field_free(field);
3561
3562 size_t image_size = size_t(width) * size_t(num_lines);
3563
3564 for (size_t c = 0; c < size_t(num_channels); c++) {
3565 // compress 4x4 pixel block.
3566 for (size_t y = 0; y < size_t(num_lines); y += 4) {
3567 for (size_t x = 0; x < size_t(width); x += 4) {
3568 float fblock[16];
3569 for (size_t j = 0; j < 4; j++) {
3570 for (size_t i = 0; i < 4; i++) {
3571 fblock[j * 4 + i] =
3572 inPtr[c * image_size + ((y + j) * size_t(width) + (x + i))];
3573 }
3574 }
3575 zfp_encode_block_float_2(zfp, fblock);
3576 }
3577 }
3578 }
3579
3580 zfp_stream_flush(zfp);
3581 (*outSize) = static_cast<unsigned int>(zfp_stream_compressed_size(zfp));
3582
3583 zfp_stream_close(zfp);
3584
3585 return true;
3586}
3587
3588#endif
3589
3590//
3591// -----------------------------------------------------------------
3592//
3593
3594// heuristics
3595#define TINYEXR_DIMENSION_THRESHOLD (1024 * 8192)
3596
3597// TODO(syoyo): Refactor function arguments.
3598static bool DecodePixelData(/* out */ unsigned char **out_images,
3599 const int *requested_pixel_types,
3600 const unsigned char *data_ptr, size_t data_len,
3601 int compression_type, int line_order, int width,
3602 int height, int x_stride, int y, int line_no,
3603 int num_lines, size_t pixel_data_size,
3604 size_t num_attributes,
3605 const EXRAttribute *attributes, size_t num_channels,
3606 const EXRChannelInfo *channels,
3607 const std::vector<size_t> &channel_offset_list) {
3608 if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { // PIZ
3609#if TINYEXR_USE_PIZ
3610 if ((width == 0) || (num_lines == 0) || (pixel_data_size == 0)) {
3611 // Invalid input #90
3612 return false;
3613 }
3614
3615 // Allocate original data size.
3616 std::vector<unsigned char> outBuf(static_cast<size_t>(
3617 static_cast<size_t>(width * num_lines) * pixel_data_size));
3618 size_t tmpBufLen = outBuf.size();
3619
3620 bool ret = tinyexr::DecompressPiz(
3621 reinterpret_cast<unsigned char *>(&outBuf.at(0)), data_ptr, tmpBufLen,
3622 data_len, static_cast<int>(num_channels), channels, width, num_lines);
3623
3624 if (!ret) {
3625 return false;
3626 }
3627
3628 // For PIZ_COMPRESSION:
3629 // pixel sample data for channel 0 for scanline 0
3630 // pixel sample data for channel 1 for scanline 0
3631 // pixel sample data for channel ... for scanline 0
3632 // pixel sample data for channel n for scanline 0
3633 // pixel sample data for channel 0 for scanline 1
3634 // pixel sample data for channel 1 for scanline 1
3635 // pixel sample data for channel ... for scanline 1
3636 // pixel sample data for channel n for scanline 1
3637 // ...
3638 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
3639 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
3640 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3641 const unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
3642 &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
3643 channel_offset_list[c] * static_cast<size_t>(width)));
3644 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3645 FP16 hf;
3646
3647 // hf.u = line_ptr[u];
3648 // use `cpy` to avoid unaligned memory access when compiler's
3649 // optimization is on.
3650 tinyexr::cpy2(&(hf.u), line_ptr + u);
3651
3652 tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
3653
3654 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
3655 unsigned short *image =
3656 reinterpret_cast<unsigned short **>(out_images)[c];
3657 if (line_order == 0) {
3658 image += (static_cast<size_t>(line_no) + v) *
3659 static_cast<size_t>(x_stride) +
3660 u;
3661 } else {
3662 image += static_cast<size_t>(
3663 (height - 1 - (line_no + static_cast<int>(v)))) *
3664 static_cast<size_t>(x_stride) +
3665 u;
3666 }
3667 *image = hf.u;
3668 } else { // HALF -> FLOAT
3669 FP32 f32 = half_to_float(hf);
3670 float *image = reinterpret_cast<float **>(out_images)[c];
3671 size_t offset = 0;
3672 if (line_order == 0) {
3673 offset = (static_cast<size_t>(line_no) + v) *
3674 static_cast<size_t>(x_stride) +
3675 u;
3676 } else {
3677 offset = static_cast<size_t>(
3678 (height - 1 - (line_no + static_cast<int>(v)))) *
3679 static_cast<size_t>(x_stride) +
3680 u;
3681 }
3682 image += offset;
3683 *image = f32.f;
3684 }
3685 }
3686 }
3687 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
3688 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false);
3689
3690 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3691 const unsigned int *line_ptr = reinterpret_cast<unsigned int *>(
3692 &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
3693 channel_offset_list[c] * static_cast<size_t>(width)));
3694 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3695 unsigned int val;
3696 // val = line_ptr[u];
3697 tinyexr::cpy4(&val, line_ptr + u);
3698
3699 tinyexr::swap4(&val);
3700
3701 unsigned int *image =
3702 reinterpret_cast<unsigned int **>(out_images)[c];
3703 if (line_order == 0) {
3704 image += (static_cast<size_t>(line_no) + v) *
3705 static_cast<size_t>(x_stride) +
3706 u;
3707 } else {
3708 image += static_cast<size_t>(
3709 (height - 1 - (line_no + static_cast<int>(v)))) *
3710 static_cast<size_t>(x_stride) +
3711 u;
3712 }
3713 *image = val;
3714 }
3715 }
3716 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
3717 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
3718 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3719 const float *line_ptr = reinterpret_cast<float *>(&outBuf.at(
3720 v * pixel_data_size * static_cast<size_t>(width) +
3721 channel_offset_list[c] * static_cast<size_t>(width)));
3722 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3723 float val;
3724 // val = line_ptr[u];
3725 tinyexr::cpy4(&val, line_ptr + u);
3726
3727 tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
3728
3729 float *image = reinterpret_cast<float **>(out_images)[c];
3730 if (line_order == 0) {
3731 image += (static_cast<size_t>(line_no) + v) *
3732 static_cast<size_t>(x_stride) +
3733 u;
3734 } else {
3735 image += static_cast<size_t>(
3736 (height - 1 - (line_no + static_cast<int>(v)))) *
3737 static_cast<size_t>(x_stride) +
3738 u;
3739 }
3740 *image = val;
3741 }
3742 }
3743 } else {
3744 return false;
3745 }
3746 }
3747#else
3748 return false;
3749#endif
3750
3751 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS ||
3752 compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
3753 // Allocate original data size.
3754 std::vector<unsigned char> outBuf(static_cast<size_t>(width) *
3755 static_cast<size_t>(num_lines) *
3756 pixel_data_size);
3757
3758 unsigned long dstLen = static_cast<unsigned long>(outBuf.size());
3759 TINYEXR_CHECK_AND_RETURN_C(dstLen > 0, false);
3760 if (!tinyexr::DecompressZip(
3761 reinterpret_cast<unsigned char *>(&outBuf.at(0)), &dstLen, data_ptr,
3762 static_cast<unsigned long>(data_len))) {
3763 return false;
3764 }
3765
3766 // For ZIP_COMPRESSION:
3767 // pixel sample data for channel 0 for scanline 0
3768 // pixel sample data for channel 1 for scanline 0
3769 // pixel sample data for channel ... for scanline 0
3770 // pixel sample data for channel n for scanline 0
3771 // pixel sample data for channel 0 for scanline 1
3772 // pixel sample data for channel 1 for scanline 1
3773 // pixel sample data for channel ... for scanline 1
3774 // pixel sample data for channel n for scanline 1
3775 // ...
3776 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
3777 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
3778 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3779 const unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
3780 &outBuf.at(v * static_cast<size_t>(pixel_data_size) *
3781 static_cast<size_t>(width) +
3782 channel_offset_list[c] * static_cast<size_t>(width)));
3783 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3784 tinyexr::FP16 hf;
3785
3786 // hf.u = line_ptr[u];
3787 tinyexr::cpy2(&(hf.u), line_ptr + u);
3788
3789 tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
3790
3791 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
3792 unsigned short *image =
3793 reinterpret_cast<unsigned short **>(out_images)[c];
3794 if (line_order == 0) {
3795 image += (static_cast<size_t>(line_no) + v) *
3796 static_cast<size_t>(x_stride) +
3797 u;
3798 } else {
3799 image += (static_cast<size_t>(height) - 1U -
3800 (static_cast<size_t>(line_no) + v)) *
3801 static_cast<size_t>(x_stride) +
3802 u;
3803 }
3804 *image = hf.u;
3805 } else { // HALF -> FLOAT
3806 tinyexr::FP32 f32 = half_to_float(hf);
3807 float *image = reinterpret_cast<float **>(out_images)[c];
3808 size_t offset = 0;
3809 if (line_order == 0) {
3810 offset = (static_cast<size_t>(line_no) + v) *
3811 static_cast<size_t>(x_stride) +
3812 u;
3813 } else {
3814 offset = (static_cast<size_t>(height) - 1U -
3815 (static_cast<size_t>(line_no) + v)) *
3816 static_cast<size_t>(x_stride) +
3817 u;
3818 }
3819 image += offset;
3820
3821 *image = f32.f;
3822 }
3823 }
3824 }
3825 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
3826 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false);
3827
3828 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3829 const unsigned int *line_ptr = reinterpret_cast<unsigned int *>(
3830 &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
3831 channel_offset_list[c] * static_cast<size_t>(width)));
3832 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3833 unsigned int val;
3834 // val = line_ptr[u];
3835 tinyexr::cpy4(&val, line_ptr + u);
3836
3837 tinyexr::swap4(&val);
3838
3839 unsigned int *image =
3840 reinterpret_cast<unsigned int **>(out_images)[c];
3841 if (line_order == 0) {
3842 image += (static_cast<size_t>(line_no) + v) *
3843 static_cast<size_t>(x_stride) +
3844 u;
3845 } else {
3846 image += (static_cast<size_t>(height) - 1U -
3847 (static_cast<size_t>(line_no) + v)) *
3848 static_cast<size_t>(x_stride) +
3849 u;
3850 }
3851 *image = val;
3852 }
3853 }
3854 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
3855 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
3856 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3857 const float *line_ptr = reinterpret_cast<float *>(
3858 &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
3859 channel_offset_list[c] * static_cast<size_t>(width)));
3860 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3861 float val;
3862 // val = line_ptr[u];
3863 tinyexr::cpy4(&val, line_ptr + u);
3864
3865 tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
3866
3867 float *image = reinterpret_cast<float **>(out_images)[c];
3868 if (line_order == 0) {
3869 image += (static_cast<size_t>(line_no) + v) *
3870 static_cast<size_t>(x_stride) +
3871 u;
3872 } else {
3873 image += (static_cast<size_t>(height) - 1U -
3874 (static_cast<size_t>(line_no) + v)) *
3875 static_cast<size_t>(x_stride) +
3876 u;
3877 }
3878 *image = val;
3879 }
3880 }
3881 } else {
3882 return false;
3883 }
3884 }
3885 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) {
3886 // Allocate original data size.
3887 std::vector<unsigned char> outBuf(static_cast<size_t>(width) *
3888 static_cast<size_t>(num_lines) *
3889 pixel_data_size);
3890
3891 unsigned long dstLen = static_cast<unsigned long>(outBuf.size());
3892 if (dstLen == 0) {
3893 return false;
3894 }
3895
3896 if (!tinyexr::DecompressRle(
3897 reinterpret_cast<unsigned char *>(&outBuf.at(0)), dstLen, data_ptr,
3898 static_cast<unsigned long>(data_len))) {
3899 return false;
3900 }
3901
3902 // For RLE_COMPRESSION:
3903 // pixel sample data for channel 0 for scanline 0
3904 // pixel sample data for channel 1 for scanline 0
3905 // pixel sample data for channel ... for scanline 0
3906 // pixel sample data for channel n for scanline 0
3907 // pixel sample data for channel 0 for scanline 1
3908 // pixel sample data for channel 1 for scanline 1
3909 // pixel sample data for channel ... for scanline 1
3910 // pixel sample data for channel n for scanline 1
3911 // ...
3912 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
3913 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
3914 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3915 const unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
3916 &outBuf.at(v * static_cast<size_t>(pixel_data_size) *
3917 static_cast<size_t>(width) +
3918 channel_offset_list[c] * static_cast<size_t>(width)));
3919 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3920 tinyexr::FP16 hf;
3921
3922 // hf.u = line_ptr[u];
3923 tinyexr::cpy2(&(hf.u), line_ptr + u);
3924
3925 tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
3926
3927 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
3928 unsigned short *image =
3929 reinterpret_cast<unsigned short **>(out_images)[c];
3930 if (line_order == 0) {
3931 image += (static_cast<size_t>(line_no) + v) *
3932 static_cast<size_t>(x_stride) +
3933 u;
3934 } else {
3935 image += (static_cast<size_t>(height) - 1U -
3936 (static_cast<size_t>(line_no) + v)) *
3937 static_cast<size_t>(x_stride) +
3938 u;
3939 }
3940 *image = hf.u;
3941 } else { // HALF -> FLOAT
3942 tinyexr::FP32 f32 = half_to_float(hf);
3943 float *image = reinterpret_cast<float **>(out_images)[c];
3944 if (line_order == 0) {
3945 image += (static_cast<size_t>(line_no) + v) *
3946 static_cast<size_t>(x_stride) +
3947 u;
3948 } else {
3949 image += (static_cast<size_t>(height) - 1U -
3950 (static_cast<size_t>(line_no) + v)) *
3951 static_cast<size_t>(x_stride) +
3952 u;
3953 }
3954 *image = f32.f;
3955 }
3956 }
3957 }
3958 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
3959 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false);
3960
3961 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3962 const unsigned int *line_ptr = reinterpret_cast<unsigned int *>(
3963 &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
3964 channel_offset_list[c] * static_cast<size_t>(width)));
3965 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
3966 unsigned int val;
3967 // val = line_ptr[u];
3968 tinyexr::cpy4(&val, line_ptr + u);
3969
3970 tinyexr::swap4(&val);
3971
3972 unsigned int *image =
3973 reinterpret_cast<unsigned int **>(out_images)[c];
3974 if (line_order == 0) {
3975 image += (static_cast<size_t>(line_no) + v) *
3976 static_cast<size_t>(x_stride) +
3977 u;
3978 } else {
3979 image += (static_cast<size_t>(height) - 1U -
3980 (static_cast<size_t>(line_no) + v)) *
3981 static_cast<size_t>(x_stride) +
3982 u;
3983 }
3984 *image = val;
3985 }
3986 }
3987 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
3988 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
3989 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
3990 const float *line_ptr = reinterpret_cast<float *>(
3991 &outBuf.at(v * pixel_data_size * 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++) {
3994 float val;
3995 // val = line_ptr[u];
3996 tinyexr::cpy4(&val, line_ptr + u);
3997
3998 tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
3999
4000 float *image = reinterpret_cast<float **>(out_images)[c];
4001 if (line_order == 0) {
4002 image += (static_cast<size_t>(line_no) + v) *
4003 static_cast<size_t>(x_stride) +
4004 u;
4005 } else {
4006 image += (static_cast<size_t>(height) - 1U -
4007 (static_cast<size_t>(line_no) + v)) *
4008 static_cast<size_t>(x_stride) +
4009 u;
4010 }
4011 *image = val;
4012 }
4013 }
4014 } else {
4015 return false;
4016 }
4017 }
4018 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
4019#if TINYEXR_USE_ZFP
4020 tinyexr::ZFPCompressionParam zfp_compression_param;
4021 std::string e;
4022 if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes,
4023 int(num_attributes), &e)) {
4024 // This code path should not be reachable.
4025 return false;
4026 }
4027
4028 // Allocate original data size.
4029 std::vector<unsigned char> outBuf(static_cast<size_t>(width) *
4030 static_cast<size_t>(num_lines) *
4031 pixel_data_size);
4032
4033 unsigned long dstLen = outBuf.size();
4034 TINYEXR_CHECK_AND_RETURN_C(dstLen > 0, false);
4035 tinyexr::DecompressZfp(reinterpret_cast<float *>(&outBuf.at(0)), width,
4036 num_lines, num_channels, data_ptr,
4037 static_cast<unsigned long>(data_len),
4038 zfp_compression_param);
4039
4040 // For ZFP_COMPRESSION:
4041 // pixel sample data for channel 0 for scanline 0
4042 // pixel sample data for channel 1 for scanline 0
4043 // pixel sample data for channel ... for scanline 0
4044 // pixel sample data for channel n for scanline 0
4045 // pixel sample data for channel 0 for scanline 1
4046 // pixel sample data for channel 1 for scanline 1
4047 // pixel sample data for channel ... for scanline 1
4048 // pixel sample data for channel n for scanline 1
4049 // ...
4050 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4051 TINYEXR_CHECK_AND_RETURN_C(channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT, false);
4052 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4053 TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false);
4054 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
4055 const float *line_ptr = reinterpret_cast<float *>(
4056 &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) +
4057 channel_offset_list[c] * static_cast<size_t>(width)));
4058 for (size_t u = 0; u < static_cast<size_t>(width); u++) {
4059 float val;
4060 tinyexr::cpy4(&val, line_ptr + u);
4061
4062 tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
4063
4064 float *image = reinterpret_cast<float **>(out_images)[c];
4065 if (line_order == 0) {
4066 image += (static_cast<size_t>(line_no) + v) *
4067 static_cast<size_t>(x_stride) +
4068 u;
4069 } else {
4070 image += (static_cast<size_t>(height) - 1U -
4071 (static_cast<size_t>(line_no) + v)) *
4072 static_cast<size_t>(x_stride) +
4073 u;
4074 }
4075 *image = val;
4076 }
4077 }
4078 } else {
4079 return false;
4080 }
4081 }
4082#else
4083 (void)attributes;
4084 (void)num_attributes;
4085 (void)num_channels;
4086 return false;
4087#endif
4088 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
4089 for (size_t c = 0; c < num_channels; c++) {
4090 for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) {
4091 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
4092 const unsigned short *line_ptr =
4093 reinterpret_cast<const unsigned short *>(
4094 data_ptr + v * pixel_data_size * size_t(width) +
4095 channel_offset_list[c] * static_cast<size_t>(width));
4096
4097 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
4098 unsigned short *outLine =
4099 reinterpret_cast<unsigned short *>(out_images[c]);
4100 if (line_order == 0) {
4101 outLine += (size_t(y) + v) * size_t(x_stride);
4102 } else {
4103 outLine +=
4104 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4105 }
4106
4107 for (int u = 0; u < width; u++) {
4108 tinyexr::FP16 hf;
4109
4110 // hf.u = line_ptr[u];
4111 tinyexr::cpy2(&(hf.u), line_ptr + u);
4112
4113 tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
4114
4115 outLine[u] = hf.u;
4116 }
4117 } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
4118 float *outLine = reinterpret_cast<float *>(out_images[c]);
4119 if (line_order == 0) {
4120 outLine += (size_t(y) + v) * size_t(x_stride);
4121 } else {
4122 outLine +=
4123 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4124 }
4125
4126 if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
4127 (data_ptr + data_len)) {
4128 // Insufficient data size
4129 return false;
4130 }
4131
4132 for (int u = 0; u < width; u++) {
4133 tinyexr::FP16 hf;
4134
4135 // address may not be aligned. use byte-wise copy for safety.#76
4136 // hf.u = line_ptr[u];
4137 tinyexr::cpy2(&(hf.u), line_ptr + u);
4138
4139 tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u));
4140
4141 tinyexr::FP32 f32 = half_to_float(hf);
4142
4143 outLine[u] = f32.f;
4144 }
4145 } else {
4146 return false;
4147 }
4148 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4149 const float *line_ptr = reinterpret_cast<const float *>(
4150 data_ptr + v * pixel_data_size * size_t(width) +
4151 channel_offset_list[c] * static_cast<size_t>(width));
4152
4153 float *outLine = reinterpret_cast<float *>(out_images[c]);
4154 if (line_order == 0) {
4155 outLine += (size_t(y) + v) * size_t(x_stride);
4156 } else {
4157 outLine +=
4158 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4159 }
4160
4161 if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
4162 (data_ptr + data_len)) {
4163 // Insufficient data size
4164 return false;
4165 }
4166
4167 for (int u = 0; u < width; u++) {
4168 float val;
4169 tinyexr::cpy4(&val, line_ptr + u);
4170
4171 tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
4172
4173 outLine[u] = val;
4174 }
4175 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
4176 const unsigned int *line_ptr = reinterpret_cast<const unsigned int *>(
4177 data_ptr + v * pixel_data_size * size_t(width) +
4178 channel_offset_list[c] * static_cast<size_t>(width));
4179
4180 unsigned int *outLine =
4181 reinterpret_cast<unsigned int *>(out_images[c]);
4182 if (line_order == 0) {
4183 outLine += (size_t(y) + v) * size_t(x_stride);
4184 } else {
4185 outLine +=
4186 (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride);
4187 }
4188
4189 if (reinterpret_cast<const unsigned char *>(line_ptr + width) >
4190 (data_ptr + data_len)) {
4191 // Corrupted data
4192 return false;
4193 }
4194
4195 for (int u = 0; u < width; u++) {
4196
4197 unsigned int val;
4198 tinyexr::cpy4(&val, line_ptr + u);
4199
4200 tinyexr::swap4(reinterpret_cast<unsigned int *>(&val));
4201
4202 outLine[u] = val;
4203 }
4204 }
4205 }
4206 }
4207 }
4208
4209 return true;
4210}
4211
4212static bool DecodeTiledPixelData(
4213 unsigned char **out_images, int *width, int *height,
4214 const int *requested_pixel_types, const unsigned char *data_ptr,
4215 size_t data_len, int compression_type, int line_order, int data_width,
4216 int data_height, int tile_offset_x, int tile_offset_y, int tile_size_x,
4217 int tile_size_y, size_t pixel_data_size, size_t num_attributes,
4218 const EXRAttribute *attributes, size_t num_channels,
4219 const EXRChannelInfo *channels,
4220 const std::vector<size_t> &channel_offset_list) {
4221 // Here, data_width and data_height are the dimensions of the current (sub)level.
4222 if (tile_size_x * tile_offset_x > data_width ||
4223 tile_size_y * tile_offset_y > data_height) {
4224 return false;
4225 }
4226
4227 // Compute actual image size in a tile.
4228 if ((tile_offset_x + 1) * tile_size_x >= data_width) {
4229 (*width) = data_width - (tile_offset_x * tile_size_x);
4230 } else {
4231 (*width) = tile_size_x;
4232 }
4233
4234 if ((tile_offset_y + 1) * tile_size_y >= data_height) {
4235 (*height) = data_height - (tile_offset_y * tile_size_y);
4236 } else {
4237 (*height) = tile_size_y;
4238 }
4239
4240 // Image size = tile size.
4241 return DecodePixelData(out_images, requested_pixel_types, data_ptr, data_len,
4242 compression_type, line_order, (*width), tile_size_y,
4243 /* stride */ tile_size_x, /* y */ 0, /* line_no */ 0,
4244 (*height), pixel_data_size, num_attributes, attributes,
4245 num_channels, channels, channel_offset_list);
4246}
4247
4248static bool ComputeChannelLayout(std::vector<size_t> *channel_offset_list,
4249 int *pixel_data_size, size_t *channel_offset,
4250 int num_channels,
4251 const EXRChannelInfo *channels) {
4252 channel_offset_list->resize(static_cast<size_t>(num_channels));
4253
4254 (*pixel_data_size) = 0;
4255 (*channel_offset) = 0;
4256
4257 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4258 (*channel_offset_list)[c] = (*channel_offset);
4259 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
4260 (*pixel_data_size) += sizeof(unsigned short);
4261 (*channel_offset) += sizeof(unsigned short);
4262 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4263 (*pixel_data_size) += sizeof(float);
4264 (*channel_offset) += sizeof(float);
4265 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
4266 (*pixel_data_size) += sizeof(unsigned int);
4267 (*channel_offset) += sizeof(unsigned int);
4268 } else {
4269 // ???
4270 return false;
4271 }
4272 }
4273 return true;
4274}
4275
4276// TODO: Simply return nullptr when failed to allocate?
4277static unsigned char **AllocateImage(int num_channels,
4278 const EXRChannelInfo *channels,
4279 const int *requested_pixel_types,
4280 int data_width, int data_height, bool *success) {
4281 unsigned char **images =
4282 reinterpret_cast<unsigned char **>(static_cast<float **>(
4283 malloc(sizeof(float *) * static_cast<size_t>(num_channels))));
4284
4285 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4286 images[c] = NULL;
4287 }
4288
4289 bool valid = true;
4290
4291 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4292 size_t data_len =
4293 static_cast<size_t>(data_width) * static_cast<size_t>(data_height);
4294 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
4295 // pixel_data_size += sizeof(unsigned short);
4296 // channel_offset += sizeof(unsigned short);
4297 // Alloc internal image for half type.
4298 if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) {
4299 images[c] =
4300 reinterpret_cast<unsigned char *>(static_cast<unsigned short *>(
4301 malloc(sizeof(unsigned short) * data_len)));
4302 } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) {
4303 images[c] = reinterpret_cast<unsigned char *>(
4304 static_cast<float *>(malloc(sizeof(float) * data_len)));
4305 } else {
4306 images[c] = NULL; // just in case.
4307 valid = false;
4308 break;
4309 }
4310 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
4311 // pixel_data_size += sizeof(float);
4312 // channel_offset += sizeof(float);
4313 images[c] = reinterpret_cast<unsigned char *>(
4314 static_cast<float *>(malloc(sizeof(float) * data_len)));
4315 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
4316 // pixel_data_size += sizeof(unsigned int);
4317 // channel_offset += sizeof(unsigned int);
4318 images[c] = reinterpret_cast<unsigned char *>(
4319 static_cast<unsigned int *>(malloc(sizeof(unsigned int) * data_len)));
4320 } else {
4321 images[c] = NULL; // just in case.
4322 valid = false;
4323 break;
4324 }
4325 }
4326
4327 if (!valid) {
4328 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
4329 if (images[c]) {
4330 free(images[c]);
4331 images[c] = NULL;
4332 }
4333 }
4334
4335 if (success) {
4336 (*success) = false;
4337 }
4338 } else {
4339 if (success) {
4340 (*success) = true;
4341 }
4342 }
4343
4344 return images;
4345}
4346
4347#ifdef _WIN32
4348static inline std::wstring UTF8ToWchar(const std::string &str) {
4349 int wstr_size =
4350 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), NULL, 0);
4351 std::wstring wstr(wstr_size, 0);
4352 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
4353 (int)wstr.size());
4354 return wstr;
4355}
4356#endif
4357
4358
4359static int ParseEXRHeader(HeaderInfo *info, bool *empty_header,
4360 const EXRVersion *version, std::string *err,
4361 const unsigned char *buf, size_t size) {
4362 const char *marker = reinterpret_cast<const char *>(&buf[0]);
4363
4364 if (empty_header) {
4365 (*empty_header) = false;
4366 }
4367
4368 if (version->multipart) {
4369 if (size > 0 && marker[0] == '\0') {
4370 // End of header list.
4371 if (empty_header) {
4372 (*empty_header) = true;
4373 }
4374 return TINYEXR_SUCCESS;
4375 }
4376 }
4377
4378 // According to the spec, the header of every OpenEXR file must contain at
4379 // least the following attributes:
4380 //
4381 // channels chlist
4382 // compression compression
4383 // dataWindow box2i
4384 // displayWindow box2i
4385 // lineOrder lineOrder
4386 // pixelAspectRatio float
4387 // screenWindowCenter v2f
4388 // screenWindowWidth float
4389 bool has_channels = false;
4390 bool has_compression = false;
4391 bool has_data_window = false;
4392 bool has_display_window = false;
4393 bool has_line_order = false;
4394 bool has_pixel_aspect_ratio = false;
4395 bool has_screen_window_center = false;
4396 bool has_screen_window_width = false;
4397 bool has_name = false;
4398 bool has_type = false;
4399
4400 info->name.clear();
4401 info->type.clear();
4402
4403 info->data_window.min_x = 0;
4404 info->data_window.min_y = 0;
4405 info->data_window.max_x = 0;
4406 info->data_window.max_y = 0;
4407 info->line_order = 0; // @fixme
4408 info->display_window.min_x = 0;
4409 info->display_window.min_y = 0;
4410 info->display_window.max_x = 0;
4411 info->display_window.max_y = 0;
4412 info->screen_window_center[0] = 0.0f;
4413 info->screen_window_center[1] = 0.0f;
4414 info->screen_window_width = -1.0f;
4415 info->pixel_aspect_ratio = -1.0f;
4416
4417 info->tiled = 0;
4418 info->tile_size_x = -1;
4419 info->tile_size_y = -1;
4420 info->tile_level_mode = -1;
4421 info->tile_rounding_mode = -1;
4422
4423 info->attributes.clear();
4424
4425 // Read attributes
4426 size_t orig_size = size;
4427 for (size_t nattr = 0; nattr < TINYEXR_MAX_HEADER_ATTRIBUTES; nattr++) {
4428 if (0 == size) {
4429 if (err) {
4430 (*err) += "Insufficient data size for attributes.\n";
4431 }
4433 } else if (marker[0] == '\0') {
4434 size--;
4435 break;
4436 }
4437
4438 std::string attr_name;
4439 std::string attr_type;
4440 std::vector<unsigned char> data;
4441 size_t marker_size;
4442 if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size,
4443 marker, size)) {
4444 if (err) {
4445 (*err) += "Failed to read attribute.\n";
4446 }
4448 }
4449 marker += marker_size;
4450 size -= marker_size;
4451
4452 // For a multipart file, the version field 9th bit is 0.
4453 if ((version->tiled || version->multipart || version->non_image) && attr_name.compare("tiles") == 0) {
4454 unsigned int x_size, y_size;
4455 unsigned char tile_mode;
4456 if (data.size() != 9) {
4457 if (err) {
4458 (*err) += "(ParseEXRHeader) Invalid attribute data size. Attribute data size must be 9.\n";
4459 }
4461 }
4462
4463 memcpy(&x_size, &data.at(0), sizeof(int));
4464 memcpy(&y_size, &data.at(4), sizeof(int));
4465 tile_mode = data[8];
4466 tinyexr::swap4(&x_size);
4467 tinyexr::swap4(&y_size);
4468
4469 if (x_size > static_cast<unsigned int>(std::numeric_limits<int>::max()) ||
4470 y_size > static_cast<unsigned int>(std::numeric_limits<int>::max())) {
4471 if (err) {
4472 (*err) = "Tile sizes were invalid.";
4473 }
4475 }
4476
4477 info->tile_size_x = static_cast<int>(x_size);
4478 info->tile_size_y = static_cast<int>(y_size);
4479
4480 // mode = levelMode + roundingMode * 16
4481 info->tile_level_mode = tile_mode & 0x3;
4482 info->tile_rounding_mode = (tile_mode >> 4) & 0x1;
4483 info->tiled = 1;
4484 } else if (attr_name.compare("compression") == 0) {
4485 bool ok = false;
4486 if (data[0] < TINYEXR_COMPRESSIONTYPE_PIZ) {
4487 ok = true;
4488 }
4489
4490 if (data[0] == TINYEXR_COMPRESSIONTYPE_PIZ) {
4491#if TINYEXR_USE_PIZ
4492 ok = true;
4493#else
4494 if (err) {
4495 (*err) = "PIZ compression is not supported.";
4496 }
4498#endif
4499 }
4500
4501 if (data[0] == TINYEXR_COMPRESSIONTYPE_ZFP) {
4502#if TINYEXR_USE_ZFP
4503 ok = true;
4504#else
4505 if (err) {
4506 (*err) = "ZFP compression is not supported.";
4507 }
4509#endif
4510 }
4511
4512 if (!ok) {
4513 if (err) {
4514 (*err) = "Unknown compression type.";
4515 }
4517 }
4518
4519 info->compression_type = static_cast<int>(data[0]);
4520 has_compression = true;
4521
4522 } else if (attr_name.compare("channels") == 0) {
4523 // name: zero-terminated string, from 1 to 255 bytes long
4524 // pixel type: int, possible values are: UINT = 0 HALF = 1 FLOAT = 2
4525 // pLinear: unsigned char, possible values are 0 and 1
4526 // reserved: three chars, should be zero
4527 // xSampling: int
4528 // ySampling: int
4529
4530 if (!ReadChannelInfo(info->channels, data)) {
4531 if (err) {
4532 (*err) += "Failed to parse channel info.\n";
4533 }
4535 }
4536
4537 if (info->channels.size() < 1) {
4538 if (err) {
4539 (*err) += "# of channels is zero.\n";
4540 }
4542 }
4543
4544 has_channels = true;
4545
4546 } else if (attr_name.compare("dataWindow") == 0) {
4547 if (data.size() >= 16) {
4548 memcpy(&info->data_window.min_x, &data.at(0), sizeof(int));
4549 memcpy(&info->data_window.min_y, &data.at(4), sizeof(int));
4550 memcpy(&info->data_window.max_x, &data.at(8), sizeof(int));
4551 memcpy(&info->data_window.max_y, &data.at(12), sizeof(int));
4552 tinyexr::swap4(&info->data_window.min_x);
4553 tinyexr::swap4(&info->data_window.min_y);
4554 tinyexr::swap4(&info->data_window.max_x);
4555 tinyexr::swap4(&info->data_window.max_y);
4556 has_data_window = true;
4557 }
4558 } else if (attr_name.compare("displayWindow") == 0) {
4559 if (data.size() >= 16) {
4560 memcpy(&info->display_window.min_x, &data.at(0), sizeof(int));
4561 memcpy(&info->display_window.min_y, &data.at(4), sizeof(int));
4562 memcpy(&info->display_window.max_x, &data.at(8), sizeof(int));
4563 memcpy(&info->display_window.max_y, &data.at(12), sizeof(int));
4564 tinyexr::swap4(&info->display_window.min_x);
4565 tinyexr::swap4(&info->display_window.min_y);
4566 tinyexr::swap4(&info->display_window.max_x);
4567 tinyexr::swap4(&info->display_window.max_y);
4568
4569 has_display_window = true;
4570 }
4571 } else if (attr_name.compare("lineOrder") == 0) {
4572 if (data.size() >= 1) {
4573 info->line_order = static_cast<int>(data[0]);
4574 has_line_order = true;
4575 }
4576 } else if (attr_name.compare("pixelAspectRatio") == 0) {
4577 if (data.size() >= sizeof(float)) {
4578 memcpy(&info->pixel_aspect_ratio, &data.at(0), sizeof(float));
4579 tinyexr::swap4(&info->pixel_aspect_ratio);
4580 has_pixel_aspect_ratio = true;
4581 }
4582 } else if (attr_name.compare("screenWindowCenter") == 0) {
4583 if (data.size() >= 8) {
4584 memcpy(&info->screen_window_center[0], &data.at(0), sizeof(float));
4585 memcpy(&info->screen_window_center[1], &data.at(4), sizeof(float));
4586 tinyexr::swap4(&info->screen_window_center[0]);
4587 tinyexr::swap4(&info->screen_window_center[1]);
4588 has_screen_window_center = true;
4589 }
4590 } else if (attr_name.compare("screenWindowWidth") == 0) {
4591 if (data.size() >= sizeof(float)) {
4592 memcpy(&info->screen_window_width, &data.at(0), sizeof(float));
4593 tinyexr::swap4(&info->screen_window_width);
4594
4595 has_screen_window_width = true;
4596 }
4597 } else if (attr_name.compare("chunkCount") == 0) {
4598 if (data.size() >= sizeof(int)) {
4599 memcpy(&info->chunk_count, &data.at(0), sizeof(int));
4600 tinyexr::swap4(&info->chunk_count);
4601 }
4602 } else if (attr_name.compare("name") == 0) {
4603 if (!data.empty() && data[0]) {
4604 data.push_back(0);
4605 size_t len = strlen(reinterpret_cast<const char*>(&data[0]));
4606 info->name.resize(len);
4607 info->name.assign(reinterpret_cast<const char*>(&data[0]), len);
4608 has_name = true;
4609 }
4610 } else if (attr_name.compare("type") == 0) {
4611 if (!data.empty() && data[0]) {
4612 data.push_back(0);
4613 size_t len = strlen(reinterpret_cast<const char*>(&data[0]));
4614 info->type.resize(len);
4615 info->type.assign(reinterpret_cast<const char*>(&data[0]), len);
4616 has_type = true;
4617 }
4618 } else {
4619 // Custom attribute(up to TINYEXR_MAX_CUSTOM_ATTRIBUTES)
4620 if (info->attributes.size() < TINYEXR_MAX_CUSTOM_ATTRIBUTES) {
4621 EXRAttribute attrib;
4622#ifdef _MSC_VER
4623 strncpy_s(attrib.name, attr_name.c_str(), 255);
4624 strncpy_s(attrib.type, attr_type.c_str(), 255);
4625#else
4626 strncpy(attrib.name, attr_name.c_str(), 255);
4627 strncpy(attrib.type, attr_type.c_str(), 255);
4628#endif
4629 attrib.name[255] = '\0';
4630 attrib.type[255] = '\0';
4631 //std::cout << "i = " << info->attributes.size() << ", dsize = " << data.size() << "\n";
4632 attrib.size = static_cast<int>(data.size());
4633 attrib.value = static_cast<unsigned char *>(malloc(data.size()));
4634 memcpy(reinterpret_cast<char *>(attrib.value), &data.at(0),
4635 data.size());
4636 info->attributes.push_back(attrib);
4637 }
4638 }
4639 }
4640
4641 // Check if required attributes exist
4642 {
4643 std::stringstream ss_err;
4644
4645 if (!has_compression) {
4646 ss_err << "\"compression\" attribute not found in the header."
4647 << std::endl;
4648 }
4649
4650 if (!has_channels) {
4651 ss_err << "\"channels\" attribute not found in the header." << std::endl;
4652 }
4653
4654 if (!has_line_order) {
4655 ss_err << "\"lineOrder\" attribute not found in the header." << std::endl;
4656 }
4657
4658 if (!has_display_window) {
4659 ss_err << "\"displayWindow\" attribute not found in the header."
4660 << std::endl;
4661 }
4662
4663 if (!has_data_window) {
4664 ss_err << "\"dataWindow\" attribute not found in the header or invalid."
4665 << std::endl;
4666 }
4667
4668 if (!has_pixel_aspect_ratio) {
4669 ss_err << "\"pixelAspectRatio\" attribute not found in the header."
4670 << std::endl;
4671 }
4672
4673 if (!has_screen_window_width) {
4674 ss_err << "\"screenWindowWidth\" attribute not found in the header."
4675 << std::endl;
4676 }
4677
4678 if (!has_screen_window_center) {
4679 ss_err << "\"screenWindowCenter\" attribute not found in the header."
4680 << std::endl;
4681 }
4682
4683 if (version->multipart || version->non_image) {
4684 if (!has_name) {
4685 ss_err << "\"name\" attribute not found in the header."
4686 << std::endl;
4687 }
4688 if (!has_type) {
4689 ss_err << "\"type\" attribute not found in the header."
4690 << std::endl;
4691 }
4692 }
4693
4694 if (!(ss_err.str().empty())) {
4695 if (err) {
4696 (*err) += ss_err.str();
4697 }
4698
4700 }
4701 }
4702
4703 info->header_len = static_cast<unsigned int>(orig_size - size);
4704
4705 return TINYEXR_SUCCESS;
4706}
4707
4708// C++ HeaderInfo to C EXRHeader conversion.
4709static bool ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info, std::string *warn, std::string *err) {
4710 exr_header->pixel_aspect_ratio = info.pixel_aspect_ratio;
4711 exr_header->screen_window_center[0] = info.screen_window_center[0];
4712 exr_header->screen_window_center[1] = info.screen_window_center[1];
4713 exr_header->screen_window_width = info.screen_window_width;
4714 exr_header->chunk_count = info.chunk_count;
4715 exr_header->display_window.min_x = info.display_window.min_x;
4716 exr_header->display_window.min_y = info.display_window.min_y;
4717 exr_header->display_window.max_x = info.display_window.max_x;
4718 exr_header->display_window.max_y = info.display_window.max_y;
4719 exr_header->data_window.min_x = info.data_window.min_x;
4720 exr_header->data_window.min_y = info.data_window.min_y;
4721 exr_header->data_window.max_x = info.data_window.max_x;
4722 exr_header->data_window.max_y = info.data_window.max_y;
4723 exr_header->line_order = info.line_order;
4724 exr_header->compression_type = info.compression_type;
4725 exr_header->tiled = info.tiled;
4726 exr_header->tile_size_x = info.tile_size_x;
4727 exr_header->tile_size_y = info.tile_size_y;
4728 exr_header->tile_level_mode = info.tile_level_mode;
4729 exr_header->tile_rounding_mode = info.tile_rounding_mode;
4730
4731 EXRSetNameAttr(exr_header, info.name.c_str());
4732
4733
4734 if (!info.type.empty()) {
4735 bool valid = true;
4736 if (info.type == "scanlineimage") {
4737 if (exr_header->tiled) {
4738 if (err) {
4739 (*err) += "(ConvertHeader) tiled bit must be off for `scanlineimage` type.\n";
4740 }
4741 valid = false;
4742 }
4743 } else if (info.type == "tiledimage") {
4744 if (!exr_header->tiled) {
4745 if (err) {
4746 (*err) += "(ConvertHeader) tiled bit must be on for `tiledimage` type.\n";
4747 }
4748 valid = false;
4749 }
4750 } else if (info.type == "deeptile") {
4751 exr_header->non_image = 1;
4752 if (!exr_header->tiled) {
4753 if (err) {
4754 (*err) += "(ConvertHeader) tiled bit must be on for `deeptile` type.\n";
4755 }
4756 valid = false;
4757 }
4758 } else if (info.type == "deepscanline") {
4759 exr_header->non_image = 1;
4760 if (exr_header->tiled) {
4761 if (err) {
4762 (*err) += "(ConvertHeader) tiled bit must be off for `deepscanline` type.\n";
4763 }
4764 //valid = false;
4765 }
4766 } else {
4767 if (warn) {
4768 std::stringstream ss;
4769 ss << "(ConvertHeader) Unsupported or unknown info.type: " << info.type << "\n";
4770 (*warn) += ss.str();
4771 }
4772 }
4773
4774 if (!valid) {
4775 return false;
4776 }
4777 }
4778
4779 exr_header->num_channels = static_cast<int>(info.channels.size());
4780
4781 exr_header->channels = static_cast<EXRChannelInfo *>(malloc(
4782 sizeof(EXRChannelInfo) * static_cast<size_t>(exr_header->num_channels)));
4783 for (size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
4784#ifdef _MSC_VER
4785 strncpy_s(exr_header->channels[c].name, info.channels[c].name.c_str(), 255);
4786#else
4787 strncpy(exr_header->channels[c].name, info.channels[c].name.c_str(), 255);
4788#endif
4789 // manually add '\0' for safety.
4790 exr_header->channels[c].name[255] = '\0';
4791
4792 exr_header->channels[c].pixel_type = info.channels[c].pixel_type;
4793 exr_header->channels[c].p_linear = info.channels[c].p_linear;
4794 exr_header->channels[c].x_sampling = info.channels[c].x_sampling;
4795 exr_header->channels[c].y_sampling = info.channels[c].y_sampling;
4796 }
4797
4798 exr_header->pixel_types = static_cast<int *>(
4799 malloc(sizeof(int) * static_cast<size_t>(exr_header->num_channels)));
4800 for (size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
4801 exr_header->pixel_types[c] = info.channels[c].pixel_type;
4802 }
4803
4804 // Initially fill with values of `pixel_types`
4805 exr_header->requested_pixel_types = static_cast<int *>(
4806 malloc(sizeof(int) * static_cast<size_t>(exr_header->num_channels)));
4807 for (size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
4808 exr_header->requested_pixel_types[c] = info.channels[c].pixel_type;
4809 }
4810
4811 exr_header->num_custom_attributes = static_cast<int>(info.attributes.size());
4812
4813 if (exr_header->num_custom_attributes > 0) {
4814 // TODO(syoyo): Report warning when # of attributes exceeds
4815 // `TINYEXR_MAX_CUSTOM_ATTRIBUTES`
4818 }
4819
4820 exr_header->custom_attributes = static_cast<EXRAttribute *>(malloc(
4821 sizeof(EXRAttribute) * size_t(exr_header->num_custom_attributes)));
4822
4823 for (size_t i = 0; i < size_t(exr_header->num_custom_attributes); i++) {
4824 memcpy(exr_header->custom_attributes[i].name, info.attributes[i].name,
4825 256);
4826 memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type,
4827 256);
4828 exr_header->custom_attributes[i].size = info.attributes[i].size;
4829 // Just copy pointer
4830 exr_header->custom_attributes[i].value = info.attributes[i].value;
4831 }
4832
4833 } else {
4834 exr_header->custom_attributes = NULL;
4835 }
4836
4837 exr_header->header_len = info.header_len;
4838
4839 return true;
4840}
4841
4842struct OffsetData {
4843 OffsetData() : num_x_levels(0), num_y_levels(0) {}
4844 std::vector<std::vector<std::vector <tinyexr::tinyexr_uint64> > > offsets;
4845 int num_x_levels;
4846 int num_y_levels;
4847};
4848
4849// -1 = error
4850static int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) {
4851 switch (tile_level_mode) {
4853 return 0;
4854
4856 return lx;
4857
4859 return lx + ly * num_x_levels;
4860
4861 default:
4862 return -1;
4863 }
4864 return 0;
4865}
4866
4867static int LevelSize(int toplevel_size, int level, int tile_rounding_mode) {
4868 if (level < 0) {
4869 return -1;
4870 }
4871
4872 int b = static_cast<int>(1u << static_cast<unsigned int>(level));
4873 int level_size = toplevel_size / b;
4874
4875 if (tile_rounding_mode == TINYEXR_TILE_ROUND_UP && level_size * b < toplevel_size)
4876 level_size += 1;
4877
4878 return std::max(level_size, 1);
4879}
4880
4881static int DecodeTiledLevel(EXRImage* exr_image, const EXRHeader* exr_header,
4882 const OffsetData& offset_data,
4883 const std::vector<size_t>& channel_offset_list,
4884 int pixel_data_size,
4885 const unsigned char* head, const size_t size,
4886 std::string* err) {
4887 int num_channels = exr_header->num_channels;
4888
4889 int level_index = LevelIndex(exr_image->level_x, exr_image->level_y, exr_header->tile_level_mode, offset_data.num_x_levels);
4890 int num_y_tiles = int(offset_data.offsets[size_t(level_index)].size());
4891 if (num_y_tiles < 1) {
4893 }
4894 int num_x_tiles = int(offset_data.offsets[size_t(level_index)][0].size());
4895 if (num_x_tiles < 1) {
4897 }
4898 int num_tiles = num_x_tiles * num_y_tiles;
4899
4900 int err_code = TINYEXR_SUCCESS;
4901
4902 enum {
4903 EF_SUCCESS = 0,
4904 EF_INVALID_DATA = 1,
4905 EF_INSUFFICIENT_DATA = 2,
4906 EF_FAILED_TO_DECODE = 4
4907 };
4908#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
4909 std::atomic<unsigned> error_flag(EF_SUCCESS);
4910#else
4911 unsigned error_flag(EF_SUCCESS);
4912#endif
4913
4914 // Although the spec says : "...the data window is subdivided into an array of smaller rectangles...",
4915 // the IlmImf library allows the dimensions of the tile to be larger (or equal) than the dimensions of the data window.
4916#if 0
4917 if ((exr_header->tile_size_x > exr_image->width || exr_header->tile_size_y > exr_image->height) &&
4918 exr_image->level_x == 0 && exr_image->level_y == 0) {
4919 if (err) {
4920 (*err) += "Failed to decode tile data.\n";
4921 }
4922 err_code = TINYEXR_ERROR_INVALID_DATA;
4923 }
4924#endif
4925 exr_image->tiles = static_cast<EXRTile*>(
4926 calloc(static_cast<size_t>(num_tiles), sizeof(EXRTile)));
4927
4928#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
4929 std::vector<std::thread> workers;
4930 std::atomic<int> tile_count(0);
4931
4932 int num_threads = std::max(1, int(std::thread::hardware_concurrency()));
4933 if (num_threads > int(num_tiles)) {
4934 num_threads = int(num_tiles);
4935 }
4936
4937 for (int t = 0; t < num_threads; t++) {
4938 workers.emplace_back(std::thread([&]()
4939 {
4940 int tile_idx = 0;
4941 while ((tile_idx = tile_count++) < num_tiles) {
4942
4943#else
4944#if TINYEXR_USE_OPENMP
4945#pragma omp parallel for
4946#endif
4947 for (int tile_idx = 0; tile_idx < num_tiles; tile_idx++) {
4948#endif
4949 // Allocate memory for each tile.
4950 bool alloc_success = false;
4951 exr_image->tiles[tile_idx].images = tinyexr::AllocateImage(
4952 num_channels, exr_header->channels,
4953 exr_header->requested_pixel_types, exr_header->tile_size_x,
4954 exr_header->tile_size_y, &alloc_success);
4955
4956 if (!alloc_success) {
4957 error_flag |= EF_INVALID_DATA;
4958 continue;
4959 }
4960
4961 int x_tile = tile_idx % num_x_tiles;
4962 int y_tile = tile_idx / num_x_tiles;
4963 // 16 byte: tile coordinates
4964 // 4 byte : data size
4965 // ~ : data(uncompressed or compressed)
4966 tinyexr::tinyexr_uint64 offset = offset_data.offsets[size_t(level_index)][size_t(y_tile)][size_t(x_tile)];
4967 if (offset + sizeof(int) * 5 > size) {
4968 // Insufficient data size.
4969 error_flag |= EF_INSUFFICIENT_DATA;
4970 continue;
4971 }
4972
4973 size_t data_size =
4974 size_t(size - (offset + sizeof(int) * 5));
4975 const unsigned char* data_ptr =
4976 reinterpret_cast<const unsigned char*>(head + offset);
4977
4978 int tile_coordinates[4];
4979 memcpy(tile_coordinates, data_ptr, sizeof(int) * 4);
4980 tinyexr::swap4(&tile_coordinates[0]);
4981 tinyexr::swap4(&tile_coordinates[1]);
4982 tinyexr::swap4(&tile_coordinates[2]);
4983 tinyexr::swap4(&tile_coordinates[3]);
4984
4985 if (tile_coordinates[2] != exr_image->level_x) {
4986 // Invalid data.
4987 error_flag |= EF_INVALID_DATA;
4988 continue;
4989 }
4990 if (tile_coordinates[3] != exr_image->level_y) {
4991 // Invalid data.
4992 error_flag |= EF_INVALID_DATA;
4993 continue;
4994 }
4995
4996 int data_len;
4997 memcpy(&data_len, data_ptr + 16,
4998 sizeof(int)); // 16 = sizeof(tile_coordinates)
4999 tinyexr::swap4(&data_len);
5000
5001 if (data_len < 2 || size_t(data_len) > data_size) {
5002 // Insufficient data size.
5003 error_flag |= EF_INSUFFICIENT_DATA;
5004 continue;
5005 }
5006
5007 // Move to data addr: 20 = 16 + 4;
5008 data_ptr += 20;
5009 bool ret = tinyexr::DecodeTiledPixelData(
5010 exr_image->tiles[tile_idx].images,
5011 &(exr_image->tiles[tile_idx].width),
5012 &(exr_image->tiles[tile_idx].height),
5013 exr_header->requested_pixel_types, data_ptr,
5014 static_cast<size_t>(data_len), exr_header->compression_type,
5015 exr_header->line_order,
5016 exr_image->width, exr_image->height,
5017 tile_coordinates[0], tile_coordinates[1], exr_header->tile_size_x,
5018 exr_header->tile_size_y, static_cast<size_t>(pixel_data_size),
5019 static_cast<size_t>(exr_header->num_custom_attributes),
5020 exr_header->custom_attributes,
5021 static_cast<size_t>(exr_header->num_channels),
5022 exr_header->channels, channel_offset_list);
5023
5024 if (!ret) {
5025 // Failed to decode tile data.
5026 error_flag |= EF_FAILED_TO_DECODE;
5027 }
5028
5029 exr_image->tiles[tile_idx].offset_x = tile_coordinates[0];
5030 exr_image->tiles[tile_idx].offset_y = tile_coordinates[1];
5031 exr_image->tiles[tile_idx].level_x = tile_coordinates[2];
5032 exr_image->tiles[tile_idx].level_y = tile_coordinates[3];
5033
5034#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5035 }
5036 }));
5037 } // num_thread loop
5038
5039 for (auto& t : workers) {
5040 t.join();
5041 }
5042
5043#else
5044 } // parallel for
5045#endif
5046
5047 // Even in the event of an error, the reserved memory may be freed.
5048 exr_image->num_channels = num_channels;
5049 exr_image->num_tiles = static_cast<int>(num_tiles);
5050
5051 if (error_flag) err_code = TINYEXR_ERROR_INVALID_DATA;
5052 if (err) {
5053 if (error_flag & EF_INSUFFICIENT_DATA) {
5054 (*err) += "Insufficient data length.\n";
5055 }
5056 if (error_flag & EF_FAILED_TO_DECODE) {
5057 (*err) += "Failed to decode tile data.\n";
5058 }
5059 }
5060 return err_code;
5061}
5062
5063static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
5064 const OffsetData& offset_data,
5065 const unsigned char *head, const size_t size,
5066 std::string *err) {
5067 int num_channels = exr_header->num_channels;
5068
5069 int num_scanline_blocks = 1;
5070 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
5071 num_scanline_blocks = 16;
5072 } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
5073 num_scanline_blocks = 32;
5074 } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
5075 num_scanline_blocks = 16;
5076
5077#if TINYEXR_USE_ZFP
5078 tinyexr::ZFPCompressionParam zfp_compression_param;
5079 if (!FindZFPCompressionParam(&zfp_compression_param,
5080 exr_header->custom_attributes,
5081 int(exr_header->num_custom_attributes), err)) {
5083 }
5084#endif
5085 }
5086
5087 if (exr_header->data_window.max_x < exr_header->data_window.min_x ||
5088 exr_header->data_window.max_y < exr_header->data_window.min_y) {
5089 if (err) {
5090 (*err) += "Invalid data window.\n";
5091 }
5093 }
5094
5095 tinyexr_int64 data_width =
5096 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);
5097 tinyexr_int64 data_height =
5098 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);
5099
5100 if (data_width <= 0) {
5101 if (err) {
5102 (*err) += "Invalid data window width.\n";
5103 }
5105 }
5106
5107 if (data_height <= 0) {
5108 if (err) {
5109 (*err) += "Invalid data window height.\n";
5110 }
5112 }
5113
5114 // Do not allow too large data_width and data_height. header invalid?
5115 {
5116 if ((data_width > TINYEXR_DIMENSION_THRESHOLD) || (data_height > TINYEXR_DIMENSION_THRESHOLD)) {
5117 if (err) {
5118 std::stringstream ss;
5119 ss << "data_with or data_height too large. data_width: " << data_width
5120 << ", "
5121 << "data_height = " << data_height << std::endl;
5122 (*err) += ss.str();
5123 }
5125 }
5126 if (exr_header->tiled) {
5127 if ((exr_header->tile_size_x > TINYEXR_DIMENSION_THRESHOLD) || (exr_header->tile_size_y > TINYEXR_DIMENSION_THRESHOLD)) {
5128 if (err) {
5129 std::stringstream ss;
5130 ss << "tile with or tile height too large. tile width: " << exr_header->tile_size_x
5131 << ", "
5132 << "tile height = " << exr_header->tile_size_y << std::endl;
5133 (*err) += ss.str();
5134 }
5136 }
5137 }
5138 }
5139
5140 const std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0];
5141 size_t num_blocks = offsets.size();
5142
5143 std::vector<size_t> channel_offset_list;
5144 int pixel_data_size = 0;
5145 size_t channel_offset = 0;
5146 if (!tinyexr::ComputeChannelLayout(&channel_offset_list, &pixel_data_size,
5147 &channel_offset, num_channels,
5148 exr_header->channels)) {
5149 if (err) {
5150 (*err) += "Failed to compute channel layout.\n";
5151 }
5153 }
5154
5155#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5156 std::atomic<bool> invalid_data(false);
5157#else
5158 bool invalid_data(false);
5159#endif
5160
5161 if (exr_header->tiled) {
5162 // value check
5163 if (exr_header->tile_size_x < 0) {
5164 if (err) {
5165 std::stringstream ss;
5166 ss << "Invalid tile size x : " << exr_header->tile_size_x << "\n";
5167 (*err) += ss.str();
5168 }
5170 }
5171
5172 if (exr_header->tile_size_y < 0) {
5173 if (err) {
5174 std::stringstream ss;
5175 ss << "Invalid tile size y : " << exr_header->tile_size_y << "\n";
5176 (*err) += ss.str();
5177 }
5179 }
5180 if (exr_header->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) {
5181 EXRImage* level_image = NULL;
5182 for (int level = 0; level < offset_data.num_x_levels; ++level) {
5183 if (!level_image) {
5184 level_image = exr_image;
5185 } else {
5186 level_image->next_level = new EXRImage;
5187 InitEXRImage(level_image->next_level);
5188 level_image = level_image->next_level;
5189 }
5190 level_image->width =
5191 LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level, exr_header->tile_rounding_mode);
5192 if (level_image->width < 1) {
5194 }
5195
5196 level_image->height =
5197 LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level, exr_header->tile_rounding_mode);
5198
5199 if (level_image->height < 1) {
5201 }
5202
5203 level_image->level_x = level;
5204 level_image->level_y = level;
5205
5206 int ret = DecodeTiledLevel(level_image, exr_header,
5207 offset_data,
5208 channel_offset_list,
5209 pixel_data_size,
5210 head, size,
5211 err);
5212 if (ret != TINYEXR_SUCCESS) return ret;
5213 }
5214 } else {
5215 EXRImage* level_image = NULL;
5216 for (int level_y = 0; level_y < offset_data.num_y_levels; ++level_y)
5217 for (int level_x = 0; level_x < offset_data.num_x_levels; ++level_x) {
5218 if (!level_image) {
5219 level_image = exr_image;
5220 } else {
5221 level_image->next_level = new EXRImage;
5222 InitEXRImage(level_image->next_level);
5223 level_image = level_image->next_level;
5224 }
5225
5226 level_image->width =
5227 LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level_x, exr_header->tile_rounding_mode);
5228 if (level_image->width < 1) {
5230 }
5231
5232 level_image->height =
5233 LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level_y, exr_header->tile_rounding_mode);
5234 if (level_image->height < 1) {
5236 }
5237
5238 level_image->level_x = level_x;
5239 level_image->level_y = level_y;
5240
5241 int ret = DecodeTiledLevel(level_image, exr_header,
5242 offset_data,
5243 channel_offset_list,
5244 pixel_data_size,
5245 head, size,
5246 err);
5247 if (ret != TINYEXR_SUCCESS) return ret;
5248 }
5249 }
5250 } else { // scanline format
5251 // Don't allow too large image(256GB * pixel_data_size or more). Workaround
5252 // for #104.
5253 size_t total_data_len =
5254 size_t(data_width) * size_t(data_height) * size_t(num_channels);
5255 const bool total_data_len_overflown =
5256 sizeof(void *) == 8 ? (total_data_len >= 0x4000000000) : false;
5257 if ((total_data_len == 0) || total_data_len_overflown) {
5258 if (err) {
5259 std::stringstream ss;
5260 ss << "Image data size is zero or too large: width = " << data_width
5261 << ", height = " << data_height << ", channels = " << num_channels
5262 << std::endl;
5263 (*err) += ss.str();
5264 }
5266 }
5267
5268 bool alloc_success = false;
5269 exr_image->images = tinyexr::AllocateImage(
5270 num_channels, exr_header->channels, exr_header->requested_pixel_types,
5271 int(data_width), int(data_height), &alloc_success);
5272
5273 if (!alloc_success) {
5274 if (err) {
5275 std::stringstream ss;
5276 ss << "Failed to allocate memory for Images. Maybe EXR header is corrupted or Image data size is too large: width = " << data_width
5277 << ", height = " << data_height << ", channels = " << num_channels
5278 << std::endl;
5279 (*err) += ss.str();
5280 }
5282 }
5283
5284#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5285 std::vector<std::thread> workers;
5286 std::atomic<int> y_count(0);
5287
5288 int num_threads = std::max(1, int(std::thread::hardware_concurrency()));
5289 if (num_threads > int(num_blocks)) {
5290 num_threads = int(num_blocks);
5291 }
5292
5293 for (int t = 0; t < num_threads; t++) {
5294 workers.emplace_back(std::thread([&]() {
5295 int y = 0;
5296 while ((y = y_count++) < int(num_blocks)) {
5297
5298#else
5299
5300#if TINYEXR_USE_OPENMP
5301#pragma omp parallel for
5302#endif
5303 for (int y = 0; y < static_cast<int>(num_blocks); y++) {
5304
5305#endif
5306 size_t y_idx = static_cast<size_t>(y);
5307
5308 if (offsets[y_idx] + sizeof(int) * 2 > size) {
5309 invalid_data = true;
5310 } else {
5311 // 4 byte: scan line
5312 // 4 byte: data size
5313 // ~ : pixel data(uncompressed or compressed)
5314 size_t data_size =
5315 size_t(size - (offsets[y_idx] + sizeof(int) * 2));
5316 const unsigned char *data_ptr =
5317 reinterpret_cast<const unsigned char *>(head + offsets[y_idx]);
5318
5319 int line_no;
5320 memcpy(&line_no, data_ptr, sizeof(int));
5321 int data_len;
5322 memcpy(&data_len, data_ptr + 4, sizeof(int));
5323 tinyexr::swap4(&line_no);
5324 tinyexr::swap4(&data_len);
5325
5326 if (size_t(data_len) > data_size) {
5327 invalid_data = true;
5328
5329 } else if ((line_no > (2 << 20)) || (line_no < -(2 << 20))) {
5330 // Too large value. Assume this is invalid
5331 // 2**20 = 1048576 = heuristic value.
5332 invalid_data = true;
5333 } else if (data_len == 0) {
5334 // TODO(syoyo): May be ok to raise the threshold for example
5335 // `data_len < 4`
5336 invalid_data = true;
5337 } else {
5338 // line_no may be negative.
5339 int end_line_no = (std::min)(line_no + num_scanline_blocks,
5340 (exr_header->data_window.max_y + 1));
5341
5342 int num_lines = end_line_no - line_no;
5343
5344 if (num_lines <= 0) {
5345 invalid_data = true;
5346 } else {
5347 // Move to data addr: 8 = 4 + 4;
5348 data_ptr += 8;
5349
5350 // Adjust line_no with data_window.bmin.y
5351
5352 // overflow check
5353 tinyexr_int64 lno =
5354 static_cast<tinyexr_int64>(line_no) -
5355 static_cast<tinyexr_int64>(exr_header->data_window.min_y);
5356 if (lno > std::numeric_limits<int>::max()) {
5357 line_no = -1; // invalid
5358 } else if (lno < -std::numeric_limits<int>::max()) {
5359 line_no = -1; // invalid
5360 } else {
5361 line_no -= exr_header->data_window.min_y;
5362 }
5363
5364 if (line_no < 0) {
5365 invalid_data = true;
5366 } else {
5367 if (!tinyexr::DecodePixelData(
5368 exr_image->images, exr_header->requested_pixel_types,
5369 data_ptr, static_cast<size_t>(data_len),
5370 exr_header->compression_type, exr_header->line_order,
5371 int(data_width), int(data_height), int(data_width), y, line_no,
5372 num_lines, static_cast<size_t>(pixel_data_size),
5373 static_cast<size_t>(
5374 exr_header->num_custom_attributes),
5375 exr_header->custom_attributes,
5376 static_cast<size_t>(exr_header->num_channels),
5377 exr_header->channels, channel_offset_list)) {
5378 invalid_data = true;
5379 }
5380 }
5381 }
5382 }
5383 }
5384
5385#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
5386 }
5387 }));
5388 }
5389
5390 for (auto &t : workers) {
5391 t.join();
5392 }
5393#else
5394 } // omp parallel
5395#endif
5396 }
5397
5398 if (invalid_data) {
5399 if (err) {
5400 (*err) += "Invalid/Corrupted data found when decoding pixels.\n";
5401 }
5402
5403 // free alloced image.
5404 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
5405 if (exr_image->images[c]) {
5406 free(exr_image->images[c]);
5407 exr_image->images[c] = NULL;
5408 }
5409 }
5411 }
5412
5413 // Overwrite `pixel_type` with `requested_pixel_type`.
5414 {
5415 for (int c = 0; c < exr_header->num_channels; c++) {
5416 exr_header->pixel_types[c] = exr_header->requested_pixel_types[c];
5417 }
5418 }
5419
5420 {
5421 exr_image->num_channels = num_channels;
5422
5423 exr_image->width = int(data_width);
5424 exr_image->height = int(data_height);
5425 }
5426
5427 return TINYEXR_SUCCESS;
5428}
5429
5430static bool ReconstructLineOffsets(
5431 std::vector<tinyexr::tinyexr_uint64> *offsets, size_t n,
5432 const unsigned char *head, const unsigned char *marker, const size_t size) {
5433 if (head >= marker) {
5434 return false;
5435 }
5436 if (offsets->size() != n) {
5437 return false;
5438 }
5439
5440 for (size_t i = 0; i < n; i++) {
5441 size_t offset = static_cast<size_t>(marker - head);
5442 // Offset should not exceed whole EXR file/data size.
5443 if ((offset + sizeof(tinyexr::tinyexr_uint64)) >= size) {
5444 return false;
5445 }
5446
5447 int y;
5448 unsigned int data_len;
5449
5450 memcpy(&y, marker, sizeof(int));
5451 memcpy(&data_len, marker + 4, sizeof(unsigned int));
5452
5453 if (data_len >= size) {
5454 return false;
5455 }
5456
5457 tinyexr::swap4(&y);
5458 tinyexr::swap4(&data_len);
5459
5460 (*offsets)[i] = offset;
5461
5462 marker += data_len + 8; // 8 = 4 bytes(y) + 4 bytes(data_len)
5463 }
5464
5465 return true;
5466}
5467
5468
5469static int FloorLog2(unsigned x) {
5470 //
5471 // For x > 0, floorLog2(y) returns floor(log(x)/log(2)).
5472 //
5473 int y = 0;
5474 while (x > 1) {
5475 y += 1;
5476 x >>= 1u;
5477 }
5478 return y;
5479}
5480
5481
5482static int CeilLog2(unsigned x) {
5483 //
5484 // For x > 0, ceilLog2(y) returns ceil(log(x)/log(2)).
5485 //
5486 int y = 0;
5487 int r = 0;
5488 while (x > 1) {
5489 if (x & 1)
5490 r = 1;
5491
5492 y += 1;
5493 x >>= 1u;
5494 }
5495 return y + r;
5496}
5497
5498static int RoundLog2(int x, int tile_rounding_mode) {
5499 return (tile_rounding_mode == TINYEXR_TILE_ROUND_DOWN) ? FloorLog2(static_cast<unsigned>(x)) : CeilLog2(static_cast<unsigned>(x));
5500}
5501
5502static int CalculateNumXLevels(const EXRHeader* exr_header) {
5503 int min_x = exr_header->data_window.min_x;
5504 int max_x = exr_header->data_window.max_x;
5505 int min_y = exr_header->data_window.min_y;
5506 int max_y = exr_header->data_window.max_y;
5507
5508 int num = 0;
5509 switch (exr_header->tile_level_mode) {
5511
5512 num = 1;
5513 break;
5514
5516
5517 {
5518 int w = max_x - min_x + 1;
5519 int h = max_y - min_y + 1;
5520 num = RoundLog2(std::max(w, h), exr_header->tile_rounding_mode) + 1;
5521 }
5522 break;
5523
5525
5526 {
5527 int w = max_x - min_x + 1;
5528 num = RoundLog2(w, exr_header->tile_rounding_mode) + 1;
5529 }
5530 break;
5531
5532 default:
5533
5534 return -1;
5535 }
5536
5537 return num;
5538}
5539
5540static int CalculateNumYLevels(const EXRHeader* exr_header) {
5541 int min_x = exr_header->data_window.min_x;
5542 int max_x = exr_header->data_window.max_x;
5543 int min_y = exr_header->data_window.min_y;
5544 int max_y = exr_header->data_window.max_y;
5545 int num = 0;
5546
5547 switch (exr_header->tile_level_mode) {
5549
5550 num = 1;
5551 break;
5552
5554
5555 {
5556 int w = max_x - min_x + 1;
5557 int h = max_y - min_y + 1;
5558 num = RoundLog2(std::max(w, h), exr_header->tile_rounding_mode) + 1;
5559 }
5560 break;
5561
5563
5564 {
5565 int h = max_y - min_y + 1;
5566 num = RoundLog2(h, exr_header->tile_rounding_mode) + 1;
5567 }
5568 break;
5569
5570 default:
5571
5572 return -1;
5573 }
5574
5575 return num;
5576}
5577
5578static bool CalculateNumTiles(std::vector<int>& numTiles,
5579 int toplevel_size,
5580 int size,
5581 int tile_rounding_mode) {
5582 for (unsigned i = 0; i < numTiles.size(); i++) {
5583 int l = LevelSize(toplevel_size, int(i), tile_rounding_mode);
5584 if (l < 0) {
5585 return false;
5586 }
5587 TINYEXR_CHECK_AND_RETURN_C(l <= std::numeric_limits<int>::max() - size + 1, false);
5588
5589 numTiles[i] = (l + size - 1) / size;
5590 }
5591 return true;
5592}
5593
5594static bool PrecalculateTileInfo(std::vector<int>& num_x_tiles,
5595 std::vector<int>& num_y_tiles,
5596 const EXRHeader* exr_header) {
5597 int min_x = exr_header->data_window.min_x;
5598 int max_x = exr_header->data_window.max_x;
5599 int min_y = exr_header->data_window.min_y;
5600 int max_y = exr_header->data_window.max_y;
5601
5602 int num_x_levels = CalculateNumXLevels(exr_header);
5603
5604 if (num_x_levels < 0) {
5605 return false;
5606 }
5607
5608 int num_y_levels = CalculateNumYLevels(exr_header);
5609
5610 if (num_y_levels < 0) {
5611 return false;
5612 }
5613
5614 num_x_tiles.resize(size_t(num_x_levels));
5615 num_y_tiles.resize(size_t(num_y_levels));
5616
5617 if (!CalculateNumTiles(num_x_tiles,
5618 max_x - min_x + 1,
5619 exr_header->tile_size_x,
5620 exr_header->tile_rounding_mode)) {
5621 return false;
5622 }
5623
5624 if (!CalculateNumTiles(num_y_tiles,
5625 max_y - min_y + 1,
5626 exr_header->tile_size_y,
5627 exr_header->tile_rounding_mode)) {
5628 return false;
5629 }
5630
5631 return true;
5632}
5633
5634static void InitSingleResolutionOffsets(OffsetData& offset_data, size_t num_blocks) {
5635 offset_data.offsets.resize(1);
5636 offset_data.offsets[0].resize(1);
5637 offset_data.offsets[0][0].resize(num_blocks);
5638 offset_data.num_x_levels = 1;
5639 offset_data.num_y_levels = 1;
5640}
5641
5642// Return sum of tile blocks.
5643// 0 = error
5644static int InitTileOffsets(OffsetData& offset_data,
5645 const EXRHeader* exr_header,
5646 const std::vector<int>& num_x_tiles,
5647 const std::vector<int>& num_y_tiles) {
5648 int num_tile_blocks = 0;
5649 offset_data.num_x_levels = static_cast<int>(num_x_tiles.size());
5650 offset_data.num_y_levels = static_cast<int>(num_y_tiles.size());
5651 switch (exr_header->tile_level_mode) {
5654 TINYEXR_CHECK_AND_RETURN_C(offset_data.num_x_levels == offset_data.num_y_levels, 0);
5655 offset_data.offsets.resize(size_t(offset_data.num_x_levels));
5656
5657 for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
5658 offset_data.offsets[l].resize(size_t(num_y_tiles[l]));
5659
5660 for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
5661 offset_data.offsets[l][dy].resize(size_t(num_x_tiles[l]));
5662 num_tile_blocks += num_x_tiles[l];
5663 }
5664 }
5665 break;
5666
5668
5669 offset_data.offsets.resize(static_cast<size_t>(offset_data.num_x_levels) * static_cast<size_t>(offset_data.num_y_levels));
5670
5671 for (int ly = 0; ly < offset_data.num_y_levels; ++ly) {
5672 for (int lx = 0; lx < offset_data.num_x_levels; ++lx) {
5673 int l = ly * offset_data.num_x_levels + lx;
5674 offset_data.offsets[size_t(l)].resize(size_t(num_y_tiles[size_t(ly)]));
5675
5676 for (size_t dy = 0; dy < offset_data.offsets[size_t(l)].size(); ++dy) {
5677 offset_data.offsets[size_t(l)][dy].resize(size_t(num_x_tiles[size_t(lx)]));
5678 num_tile_blocks += num_x_tiles[size_t(lx)];
5679 }
5680 }
5681 }
5682 break;
5683
5684 default:
5685 return 0;
5686 }
5687 return num_tile_blocks;
5688}
5689
5690static bool IsAnyOffsetsAreInvalid(const OffsetData& offset_data) {
5691 for (unsigned int l = 0; l < offset_data.offsets.size(); ++l)
5692 for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy)
5693 for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx)
5694 if (reinterpret_cast<const tinyexr::tinyexr_int64&>(offset_data.offsets[l][dy][dx]) <= 0)
5695 return true;
5696
5697 return false;
5698}
5699
5700static bool isValidTile(const EXRHeader* exr_header,
5701 const OffsetData& offset_data,
5702 int dx, int dy, int lx, int ly) {
5703 if (lx < 0 || ly < 0 || dx < 0 || dy < 0) return false;
5704 int num_x_levels = offset_data.num_x_levels;
5705 int num_y_levels = offset_data.num_y_levels;
5706 switch (exr_header->tile_level_mode) {
5708
5709 if (lx == 0 &&
5710 ly == 0 &&
5711 offset_data.offsets.size() > 0 &&
5712 offset_data.offsets[0].size() > static_cast<size_t>(dy) &&
5713 offset_data.offsets[0][size_t(dy)].size() > static_cast<size_t>(dx)) {
5714 return true;
5715 }
5716
5717 break;
5718
5720
5721 if (lx < num_x_levels &&
5722 ly < num_y_levels &&
5723 offset_data.offsets.size() > static_cast<size_t>(lx) &&
5724 offset_data.offsets[size_t(lx)].size() > static_cast<size_t>(dy) &&
5725 offset_data.offsets[size_t(lx)][size_t(dy)].size() > static_cast<size_t>(dx)) {
5726 return true;
5727 }
5728
5729 break;
5730
5732 {
5733 size_t idx = static_cast<size_t>(lx) + static_cast<size_t>(ly)* static_cast<size_t>(num_x_levels);
5734 if (lx < num_x_levels &&
5735 ly < num_y_levels &&
5736 (offset_data.offsets.size() > idx) &&
5737 offset_data.offsets[idx].size() > static_cast<size_t>(dy) &&
5738 offset_data.offsets[idx][size_t(dy)].size() > static_cast<size_t>(dx)) {
5739 return true;
5740 }
5741 }
5742
5743 break;
5744
5745 default:
5746
5747 return false;
5748 }
5749
5750 return false;
5751}
5752
5753static bool ReconstructTileOffsets(OffsetData& offset_data,
5754 const EXRHeader* exr_header,
5755 const unsigned char* head, const unsigned char* marker, const size_t size,
5756 bool isMultiPartFile,
5757 bool isDeep) {
5758 int numXLevels = offset_data.num_x_levels;
5759 for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
5760 for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
5761 for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
5762 tinyexr::tinyexr_uint64 tileOffset = tinyexr::tinyexr_uint64(marker - head);
5763
5764
5765 if (isMultiPartFile) {
5766 if ((marker + sizeof(int)) >= (head + size)) {
5767 return false;
5768 }
5769
5770 //int partNumber;
5771 marker += sizeof(int);
5772 }
5773
5774 if ((marker + 4 * sizeof(int)) >= (head + size)) {
5775 return false;
5776 }
5777
5778 int tileX;
5779 memcpy(&tileX, marker, sizeof(int));
5780 tinyexr::swap4(&tileX);
5781 marker += sizeof(int);
5782
5783 int tileY;
5784 memcpy(&tileY, marker, sizeof(int));
5785 tinyexr::swap4(&tileY);
5786 marker += sizeof(int);
5787
5788 int levelX;
5789 memcpy(&levelX, marker, sizeof(int));
5790 tinyexr::swap4(&levelX);
5791 marker += sizeof(int);
5792
5793 int levelY;
5794 memcpy(&levelY, marker, sizeof(int));
5795 tinyexr::swap4(&levelY);
5796 marker += sizeof(int);
5797
5798 if (isDeep) {
5799 if ((marker + 2 * sizeof(tinyexr::tinyexr_int64)) >= (head + size)) {
5800 return false;
5801 }
5802 tinyexr::tinyexr_int64 packed_offset_table_size;
5803 memcpy(&packed_offset_table_size, marker, sizeof(tinyexr::tinyexr_int64));
5804 tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64*>(&packed_offset_table_size));
5805 marker += sizeof(tinyexr::tinyexr_int64);
5806
5807 tinyexr::tinyexr_int64 packed_sample_size;
5808 memcpy(&packed_sample_size, marker, sizeof(tinyexr::tinyexr_int64));
5809 tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64*>(&packed_sample_size));
5810 marker += sizeof(tinyexr::tinyexr_int64);
5811
5812 // next Int64 is unpacked sample size - skip that too
5813 marker += packed_offset_table_size + packed_sample_size + 8;
5814
5815 if (marker >= (head + size)) {
5816 return false;
5817 }
5818
5819 } else {
5820
5821 if ((marker + sizeof(uint32_t)) >= (head + size)) {
5822 return false;
5823 }
5824
5825 uint32_t dataSize;
5826 memcpy(&dataSize, marker, sizeof(uint32_t));
5827 tinyexr::swap4(&dataSize);
5828 marker += sizeof(uint32_t);
5829
5830 marker += dataSize;
5831
5832 if (marker >= (head + size)) {
5833 return false;
5834 }
5835 }
5836
5837 if (!isValidTile(exr_header, offset_data,
5838 tileX, tileY, levelX, levelY)) {
5839 return false;
5840 }
5841
5842 int level_idx = LevelIndex(levelX, levelY, exr_header->tile_level_mode, numXLevels);
5843 if (level_idx < 0) {
5844 return false;
5845 }
5846
5847 if (size_t(level_idx) >= offset_data.offsets.size()) {
5848 return false;
5849 }
5850
5851 if (size_t(tileY) >= offset_data.offsets[size_t(level_idx)].size()) {
5852 return false;
5853 }
5854
5855 if (size_t(tileX) >= offset_data.offsets[size_t(level_idx)][size_t(tileY)].size()) {
5856 return false;
5857 }
5858
5859 offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset;
5860 }
5861 }
5862 }
5863 return true;
5864}
5865
5866// marker output is also
5867static int ReadOffsets(OffsetData& offset_data,
5868 const unsigned char* head,
5869 const unsigned char*& marker,
5870 const size_t size,
5871 const char** err) {
5872 for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
5873 for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
5874 for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
5875 tinyexr::tinyexr_uint64 offset;
5876 if ((marker + sizeof(tinyexr_uint64)) >= (head + size)) {
5877 tinyexr::SetErrorMessage("Insufficient data size in offset table.", err);
5879 }
5880
5881 memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64));
5882 tinyexr::swap8(&offset);
5883 if (offset >= size) {
5884 tinyexr::SetErrorMessage("Invalid offset value in DecodeEXRImage.", err);
5886 }
5887 marker += sizeof(tinyexr::tinyexr_uint64); // = 8
5888 offset_data.offsets[l][dy][dx] = offset;
5889 }
5890 }
5891 }
5892 return TINYEXR_SUCCESS;
5893}
5894
5895static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header,
5896 const unsigned char *head,
5897 const unsigned char *marker, const size_t size,
5898 const char **err) {
5899 if (exr_image == NULL || exr_header == NULL || head == NULL ||
5900 marker == NULL || (size <= tinyexr::kEXRVersionSize)) {
5901 tinyexr::SetErrorMessage("Invalid argument for DecodeEXRImage().", err);
5903 }
5904
5905 int num_scanline_blocks = 1;
5906 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
5907 num_scanline_blocks = 16;
5908 } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
5909 num_scanline_blocks = 32;
5910 } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
5911 num_scanline_blocks = 16;
5912 }
5913
5914 if (exr_header->data_window.max_x < exr_header->data_window.min_x ||
5915 exr_header->data_window.max_x - exr_header->data_window.min_x ==
5916 std::numeric_limits<int>::max()) {
5917 // Issue 63
5918 tinyexr::SetErrorMessage("Invalid data width value", err);
5920 }
5921 tinyexr_int64 data_width =
5922 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);
5923 if (data_width <= 0) {
5924 tinyexr::SetErrorMessage("Invalid data window width value", err);
5926 }
5927
5928 if (exr_header->data_window.max_y < exr_header->data_window.min_y ||
5929 exr_header->data_window.max_y - exr_header->data_window.min_y ==
5930 std::numeric_limits<int>::max()) {
5931 tinyexr::SetErrorMessage("Invalid data height value", err);
5933 }
5934 tinyexr_int64 data_height =
5935 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);
5936
5937 if (data_height <= 0) {
5938 tinyexr::SetErrorMessage("Invalid data window height value", err);
5940 }
5941
5942 // Do not allow too large data_width and data_height. header invalid?
5943 {
5944 if (data_width > TINYEXR_DIMENSION_THRESHOLD) {
5945 tinyexr::SetErrorMessage("data width too large.", err);
5947 }
5948 if (data_height > TINYEXR_DIMENSION_THRESHOLD) {
5949 tinyexr::SetErrorMessage("data height too large.", err);
5951 }
5952 }
5953
5954 if (exr_header->tiled) {
5955 if (exr_header->tile_size_x > TINYEXR_DIMENSION_THRESHOLD) {
5956 tinyexr::SetErrorMessage("tile width too large.", err);
5958 }
5959 if (exr_header->tile_size_y > TINYEXR_DIMENSION_THRESHOLD) {
5960 tinyexr::SetErrorMessage("tile height too large.", err);
5962 }
5963 }
5964
5965 // Read offset tables.
5966 OffsetData offset_data;
5967 size_t num_blocks = 0;
5968 // For a multi-resolution image, the size of the offset table will be calculated from the other attributes of the header.
5969 // If chunk_count > 0 then chunk_count must be equal to the calculated tile count.
5970 if (exr_header->tiled) {
5971 {
5972 std::vector<int> num_x_tiles, num_y_tiles;
5973 if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_header)) {
5974 tinyexr::SetErrorMessage("Failed to precalculate tile info.", err);
5976 }
5977 num_blocks = size_t(InitTileOffsets(offset_data, exr_header, num_x_tiles, num_y_tiles));
5978 if (exr_header->chunk_count > 0) {
5979 if (exr_header->chunk_count != static_cast<int>(num_blocks)) {
5980 tinyexr::SetErrorMessage("Invalid offset table size.", err);
5982 }
5983 }
5984 }
5985
5986 int ret = ReadOffsets(offset_data, head, marker, size, err);
5987 if (ret != TINYEXR_SUCCESS) return ret;
5988 if (IsAnyOffsetsAreInvalid(offset_data)) {
5989 if (!ReconstructTileOffsets(offset_data, exr_header,
5990 head, marker, size,
5991 exr_header->multipart, exr_header->non_image)) {
5992
5993 tinyexr::SetErrorMessage("Invalid Tile Offsets data.", err);
5995 }
5996 }
5997 } else if (exr_header->chunk_count > 0) {
5998 // Use `chunkCount` attribute.
5999 num_blocks = static_cast<size_t>(exr_header->chunk_count);
6000 InitSingleResolutionOffsets(offset_data, num_blocks);
6001 } else {
6002 num_blocks = static_cast<size_t>(data_height) /
6003 static_cast<size_t>(num_scanline_blocks);
6004 if (num_blocks * static_cast<size_t>(num_scanline_blocks) <
6005 static_cast<size_t>(data_height)) {
6006 num_blocks++;
6007 }
6008
6009 InitSingleResolutionOffsets(offset_data, num_blocks);
6010 }
6011
6012 if (!exr_header->tiled) {
6013 std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0];
6014 for (size_t y = 0; y < num_blocks; y++) {
6015 tinyexr::tinyexr_uint64 offset;
6016 // Issue #81
6017 if ((marker + sizeof(tinyexr_uint64)) >= (head + size)) {
6018 tinyexr::SetErrorMessage("Insufficient data size in offset table.", err);
6020 }
6021
6022 memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64));
6023 tinyexr::swap8(&offset);
6024 if (offset >= size) {
6025 tinyexr::SetErrorMessage("Invalid offset value in DecodeEXRImage.", err);
6027 }
6028 marker += sizeof(tinyexr::tinyexr_uint64); // = 8
6029 offsets[y] = offset;
6030 }
6031
6032 // If line offsets are invalid, we try to reconstruct it.
6033 // See OpenEXR/IlmImf/ImfScanLineInputFile.cpp::readLineOffsets() for details.
6034 for (size_t y = 0; y < num_blocks; y++) {
6035 if (offsets[y] <= 0) {
6036 // TODO(syoyo) Report as warning?
6037 // if (err) {
6038 // stringstream ss;
6039 // ss << "Incomplete lineOffsets." << std::endl;
6040 // (*err) += ss.str();
6041 //}
6042 bool ret =
6043 ReconstructLineOffsets(&offsets, num_blocks, head, marker, size);
6044 if (ret) {
6045 // OK
6046 break;
6047 } else {
6048 tinyexr::SetErrorMessage(
6049 "Cannot reconstruct lineOffset table in DecodeEXRImage.", err);
6051 }
6052 }
6053 }
6054 }
6055
6056 {
6057 std::string e;
6058 int ret = DecodeChunk(exr_image, exr_header, offset_data, head, size, &e);
6059
6060 if (ret != TINYEXR_SUCCESS) {
6061 if (!e.empty()) {
6062 tinyexr::SetErrorMessage(e, err);
6063 }
6064
6065#if 1
6066 FreeEXRImage(exr_image);
6067#else
6068 // release memory(if exists)
6069 if ((exr_header->num_channels > 0) && exr_image && exr_image->images) {
6070 for (size_t c = 0; c < size_t(exr_header->num_channels); c++) {
6071 if (exr_image->images[c]) {
6072 free(exr_image->images[c]);
6073 exr_image->images[c] = NULL;
6074 }
6075 }
6076 free(exr_image->images);
6077 exr_image->images = NULL;
6078 }
6079#endif
6080 }
6081
6082 return ret;
6083 }
6084}
6085
6086static void GetLayers(const EXRHeader &exr_header,
6087 std::vector<std::string> &layer_names) {
6088 // Naive implementation
6089 // Group channels by layers
6090 // go over all channel names, split by periods
6091 // collect unique names
6092 layer_names.clear();
6093 for (int c = 0; c < exr_header.num_channels; c++) {
6094 std::string full_name(exr_header.channels[c].name);
6095 const size_t pos = full_name.find_last_of('.');
6096 if (pos != std::string::npos && pos != 0 && pos + 1 < full_name.size()) {
6097 full_name.erase(pos);
6098 if (std::find(layer_names.begin(), layer_names.end(), full_name) ==
6099 layer_names.end())
6100 layer_names.push_back(full_name);
6101 }
6102 }
6103}
6104
6105struct LayerChannel {
6106 explicit LayerChannel(size_t i, std::string n) : index(i), name(n) {}
6107 size_t index;
6108 std::string name;
6109};
6110
6111static void ChannelsInLayer(const EXRHeader &exr_header,
6112 const std::string &layer_name,
6113 std::vector<LayerChannel> &channels) {
6114 channels.clear();
6115 //std::cout << "layer_name = " << layer_name << "\n";
6116 for (int c = 0; c < exr_header.num_channels; c++) {
6117 //std::cout << "chan[" << c << "] = " << exr_header.channels[c].name << "\n";
6118 std::string ch_name(exr_header.channels[c].name);
6119 if (layer_name.empty()) {
6120 const size_t pos = ch_name.find_last_of('.');
6121 if (pos != std::string::npos && pos < ch_name.size()) {
6122 if (pos != 0) continue;
6123 ch_name = ch_name.substr(pos + 1);
6124 }
6125 } else {
6126 const size_t pos = ch_name.find(layer_name + '.');
6127 if (pos == std::string::npos) continue;
6128 if (pos == 0) {
6129 ch_name = ch_name.substr(layer_name.size() + 1);
6130 }
6131 }
6132 LayerChannel ch(size_t(c), ch_name);
6133 channels.push_back(ch);
6134 }
6135}
6136
6137} // namespace tinyexr
6138
6139int EXRLayers(const char *filename, const char **layer_names[], int *num_layers,
6140 const char **err) {
6141 EXRVersion exr_version;
6142 EXRHeader exr_header;
6143 InitEXRHeader(&exr_header);
6144
6145 {
6146 int ret = ParseEXRVersionFromFile(&exr_version, filename);
6147 if (ret != TINYEXR_SUCCESS) {
6148 tinyexr::SetErrorMessage("Invalid EXR header.", err);
6149 return ret;
6150 }
6151
6152 if (exr_version.multipart || exr_version.non_image) {
6153 tinyexr::SetErrorMessage(
6154 "Loading multipart or DeepImage is not supported in LoadEXR() API",
6155 err);
6156 return TINYEXR_ERROR_INVALID_DATA; // @fixme.
6157 }
6158 }
6159
6160 int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err);
6161 if (ret != TINYEXR_SUCCESS) {
6162 FreeEXRHeader(&exr_header);
6163 return ret;
6164 }
6165
6166 std::vector<std::string> layer_vec;
6167 tinyexr::GetLayers(exr_header, layer_vec);
6168
6169 (*num_layers) = int(layer_vec.size());
6170 (*layer_names) = static_cast<const char **>(
6171 malloc(sizeof(const char *) * static_cast<size_t>(layer_vec.size())));
6172 for (size_t c = 0; c < static_cast<size_t>(layer_vec.size()); c++) {
6173#ifdef _MSC_VER
6174 (*layer_names)[c] = _strdup(layer_vec[c].c_str());
6175#else
6176 (*layer_names)[c] = strdup(layer_vec[c].c_str());
6177#endif
6178 }
6179
6180 FreeEXRHeader(&exr_header);
6181 return TINYEXR_SUCCESS;
6182}
6183
6184int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
6185 const char **err) {
6186 return LoadEXRWithLayer(out_rgba, width, height, filename,
6187 /* layername */ NULL, err);
6188}
6189
6190int LoadEXRWithLayer(float **out_rgba, int *width, int *height,
6191 const char *filename, const char *layername,
6192 const char **err) {
6193 if (out_rgba == NULL) {
6194 tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err);
6196 }
6197
6198 EXRVersion exr_version;
6199 EXRImage exr_image;
6200 EXRHeader exr_header;
6201 InitEXRHeader(&exr_header);
6202 InitEXRImage(&exr_image);
6203
6204 {
6205 int ret = ParseEXRVersionFromFile(&exr_version, filename);
6206 if (ret != TINYEXR_SUCCESS) {
6207 std::stringstream ss;
6208 ss << "Failed to open EXR file or read version info from EXR file. code("
6209 << ret << ")";
6210 tinyexr::SetErrorMessage(ss.str(), err);
6211 return ret;
6212 }
6213
6214 if (exr_version.multipart || exr_version.non_image) {
6215 tinyexr::SetErrorMessage(
6216 "Loading multipart or DeepImage is not supported in LoadEXR() API",
6217 err);
6218 return TINYEXR_ERROR_INVALID_DATA; // @fixme.
6219 }
6220 }
6221
6222 {
6223 int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err);
6224 if (ret != TINYEXR_SUCCESS) {
6225 FreeEXRHeader(&exr_header);
6226 return ret;
6227 }
6228 }
6229
6230 // Read HALF channel as FLOAT.
6231 for (int i = 0; i < exr_header.num_channels; i++) {
6232 if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
6234 }
6235 }
6236
6237 // TODO: Probably limit loading to layers (channels) selected by layer index
6238 {
6239 int ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename, err);
6240 if (ret != TINYEXR_SUCCESS) {
6241 FreeEXRHeader(&exr_header);
6242 return ret;
6243 }
6244 }
6245
6246 // RGBA
6247 int idxR = -1;
6248 int idxG = -1;
6249 int idxB = -1;
6250 int idxA = -1;
6251
6252 std::vector<std::string> layer_names;
6253 tinyexr::GetLayers(exr_header, layer_names);
6254
6255 std::vector<tinyexr::LayerChannel> channels;
6256 tinyexr::ChannelsInLayer(
6257 exr_header, layername == NULL ? "" : std::string(layername), channels);
6258
6259
6260 if (channels.size() < 1) {
6261 if (layername == NULL) {
6262 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);
6263
6264 } else {
6265 tinyexr::SetErrorMessage("Layer Not Found", err);
6266 }
6267 FreeEXRHeader(&exr_header);
6268 FreeEXRImage(&exr_image);
6270 }
6271
6272 size_t ch_count = channels.size() < 4 ? channels.size() : 4;
6273 for (size_t c = 0; c < ch_count; c++) {
6274 const tinyexr::LayerChannel &ch = channels[c];
6275
6276 if (ch.name == "R") {
6277 idxR = int(ch.index);
6278 } else if (ch.name == "G") {
6279 idxG = int(ch.index);
6280 } else if (ch.name == "B") {
6281 idxB = int(ch.index);
6282 } else if (ch.name == "A") {
6283 idxA = int(ch.index);
6284 }
6285 }
6286
6287 if (channels.size() == 1) {
6288 int chIdx = int(channels.front().index);
6289 // Grayscale channel only.
6290
6291 (*out_rgba) = reinterpret_cast<float *>(
6292 malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
6293 static_cast<size_t>(exr_image.height)));
6294
6295 if (exr_header.tiled) {
6296 const size_t tile_size_x = static_cast<size_t>(exr_header.tile_size_x);
6297 const size_t tile_size_y = static_cast<size_t>(exr_header.tile_size_y);
6298 for (int it = 0; it < exr_image.num_tiles; it++) {
6299 for (size_t j = 0; j < tile_size_y; j++) {
6300 for (size_t i = 0; i < tile_size_x; i++) {
6301 const size_t ii =
6302 static_cast<size_t>(exr_image.tiles[it].offset_x) * tile_size_x +
6303 i;
6304 const size_t jj =
6305 static_cast<size_t>(exr_image.tiles[it].offset_y) * tile_size_y +
6306 j;
6307 const size_t idx = ii + jj * static_cast<size_t>(exr_image.width);
6308
6309 // out of region check.
6310 if (ii >= static_cast<size_t>(exr_image.width)) {
6311 continue;
6312 }
6313 if (jj >= static_cast<size_t>(exr_image.height)) {
6314 continue;
6315 }
6316 const size_t srcIdx = i + j * tile_size_x;
6317 unsigned char **src = exr_image.tiles[it].images;
6318 (*out_rgba)[4 * idx + 0] =
6319 reinterpret_cast<float **>(src)[chIdx][srcIdx];
6320 (*out_rgba)[4 * idx + 1] =
6321 reinterpret_cast<float **>(src)[chIdx][srcIdx];
6322 (*out_rgba)[4 * idx + 2] =
6323 reinterpret_cast<float **>(src)[chIdx][srcIdx];
6324 (*out_rgba)[4 * idx + 3] =
6325 reinterpret_cast<float **>(src)[chIdx][srcIdx];
6326 }
6327 }
6328 }
6329 } else {
6330 const size_t pixel_size = static_cast<size_t>(exr_image.width) *
6331 static_cast<size_t>(exr_image.height);
6332 for (size_t i = 0; i < pixel_size; i++) {
6333 const float val =
6334 reinterpret_cast<float **>(exr_image.images)[chIdx][i];
6335 (*out_rgba)[4 * i + 0] = val;
6336 (*out_rgba)[4 * i + 1] = val;
6337 (*out_rgba)[4 * i + 2] = val;
6338 (*out_rgba)[4 * i + 3] = val;
6339 }
6340 }
6341 } else {
6342 // Assume RGB(A)
6343
6344 if (idxR == -1) {
6345 tinyexr::SetErrorMessage("R channel not found", err);
6346
6347 FreeEXRHeader(&exr_header);
6348 FreeEXRImage(&exr_image);
6350 }
6351
6352 if (idxG == -1) {
6353 tinyexr::SetErrorMessage("G channel not found", err);
6354 FreeEXRHeader(&exr_header);
6355 FreeEXRImage(&exr_image);
6357 }
6358
6359 if (idxB == -1) {
6360 tinyexr::SetErrorMessage("B channel not found", err);
6361 FreeEXRHeader(&exr_header);
6362 FreeEXRImage(&exr_image);
6364 }
6365
6366 (*out_rgba) = reinterpret_cast<float *>(
6367 malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
6368 static_cast<size_t>(exr_image.height)));
6369 if (exr_header.tiled) {
6370 const size_t tile_size_x = static_cast<size_t>(exr_header.tile_size_x);
6371 const size_t tile_size_y = static_cast<size_t>(exr_header.tile_size_y);
6372 for (int it = 0; it < exr_image.num_tiles; it++) {
6373 for (size_t j = 0; j < tile_size_y; j++) {
6374 for (size_t i = 0; i < tile_size_x; i++) {
6375 const size_t ii =
6376 static_cast<size_t>(exr_image.tiles[it].offset_x) *
6377 tile_size_x +
6378 i;
6379 const size_t jj =
6380 static_cast<size_t>(exr_image.tiles[it].offset_y) *
6381 tile_size_y +
6382 j;
6383 const size_t idx = ii + jj * static_cast<size_t>(exr_image.width);
6384
6385 // out of region check.
6386 if (ii >= static_cast<size_t>(exr_image.width)) {
6387 continue;
6388 }
6389 if (jj >= static_cast<size_t>(exr_image.height)) {
6390 continue;
6391 }
6392 const size_t srcIdx = i + j * tile_size_x;
6393 unsigned char **src = exr_image.tiles[it].images;
6394 (*out_rgba)[4 * idx + 0] =
6395 reinterpret_cast<float **>(src)[idxR][srcIdx];
6396 (*out_rgba)[4 * idx + 1] =
6397 reinterpret_cast<float **>(src)[idxG][srcIdx];
6398 (*out_rgba)[4 * idx + 2] =
6399 reinterpret_cast<float **>(src)[idxB][srcIdx];
6400 if (idxA != -1) {
6401 (*out_rgba)[4 * idx + 3] =
6402 reinterpret_cast<float **>(src)[idxA][srcIdx];
6403 } else {
6404 (*out_rgba)[4 * idx + 3] = 1.0;
6405 }
6406 }
6407 }
6408 }
6409 } else {
6410 const size_t pixel_size = static_cast<size_t>(exr_image.width) *
6411 static_cast<size_t>(exr_image.height);
6412 for (size_t i = 0; i < pixel_size; i++) {
6413 (*out_rgba)[4 * i + 0] =
6414 reinterpret_cast<float **>(exr_image.images)[idxR][i];
6415 (*out_rgba)[4 * i + 1] =
6416 reinterpret_cast<float **>(exr_image.images)[idxG][i];
6417 (*out_rgba)[4 * i + 2] =
6418 reinterpret_cast<float **>(exr_image.images)[idxB][i];
6419 if (idxA != -1) {
6420 (*out_rgba)[4 * i + 3] =
6421 reinterpret_cast<float **>(exr_image.images)[idxA][i];
6422 } else {
6423 (*out_rgba)[4 * i + 3] = 1.0;
6424 }
6425 }
6426 }
6427 }
6428
6429 (*width) = exr_image.width;
6430 (*height) = exr_image.height;
6431
6432 FreeEXRHeader(&exr_header);
6433 FreeEXRImage(&exr_image);
6434
6435 return TINYEXR_SUCCESS;
6436}
6437
6438int IsEXR(const char *filename) {
6439 EXRVersion exr_version;
6440
6441 int ret = ParseEXRVersionFromFile(&exr_version, filename);
6442 if (ret != TINYEXR_SUCCESS) {
6443 return ret;
6444 }
6445
6446 return TINYEXR_SUCCESS;
6447}
6448
6449int IsEXRFromMemory(const unsigned char *memory, size_t size) {
6450 EXRVersion exr_version;
6451
6452 int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
6453 if (ret != TINYEXR_SUCCESS) {
6454 return ret;
6455 }
6456
6457 return TINYEXR_SUCCESS;
6458}
6459
6460int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version,
6461 const unsigned char *memory, size_t size,
6462 const char **err) {
6463 if (memory == NULL || exr_header == NULL) {
6464 tinyexr::SetErrorMessage(
6465 "Invalid argument. `memory` or `exr_header` argument is null in "
6466 "ParseEXRHeaderFromMemory()",
6467 err);
6468
6469 // Invalid argument
6471 }
6472
6473 if (size < tinyexr::kEXRVersionSize) {
6474 tinyexr::SetErrorMessage("Insufficient header/data size.\n", err);
6476 }
6477
6478 const unsigned char *marker = memory + tinyexr::kEXRVersionSize;
6479 size_t marker_size = size - tinyexr::kEXRVersionSize;
6480
6481 tinyexr::HeaderInfo info;
6482 info.clear();
6483
6484 int ret;
6485 {
6486 std::string err_str;
6487 ret = ParseEXRHeader(&info, NULL, version, &err_str, marker, marker_size);
6488
6489 if (ret != TINYEXR_SUCCESS) {
6490 if (err && !err_str.empty()) {
6491 tinyexr::SetErrorMessage(err_str, err);
6492 }
6493 }
6494 }
6495
6496 {
6497 std::string warn;
6498 std::string err_str;
6499
6500 if (!ConvertHeader(exr_header, info, &warn, &err_str)) {
6501 // release mem
6502 for (size_t i = 0; i < info.attributes.size(); i++) {
6503 if (info.attributes[i].value) {
6504 free(info.attributes[i].value);
6505 }
6506 }
6507 if (err && !err_str.empty()) {
6508 tinyexr::SetErrorMessage(err_str, err);
6509 }
6511 }
6512 }
6513
6514 exr_header->multipart = version->multipart ? 1 : 0;
6515 exr_header->non_image = version->non_image ? 1 : 0;
6516
6517 return ret;
6518}
6519
6520int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
6521 const unsigned char *memory, size_t size,
6522 const char **err) {
6523 if (out_rgba == NULL || memory == NULL) {
6524 tinyexr::SetErrorMessage("Invalid argument for LoadEXRFromMemory", err);
6526 }
6527
6528 EXRVersion exr_version;
6529 EXRImage exr_image;
6530 EXRHeader exr_header;
6531
6532 InitEXRHeader(&exr_header);
6533
6534 int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
6535 if (ret != TINYEXR_SUCCESS) {
6536 std::stringstream ss;
6537 ss << "Failed to parse EXR version. code(" << ret << ")";
6538 tinyexr::SetErrorMessage(ss.str(), err);
6539 return ret;
6540 }
6541
6542 ret = ParseEXRHeaderFromMemory(&exr_header, &exr_version, memory, size, err);
6543 if (ret != TINYEXR_SUCCESS) {
6544 return ret;
6545 }
6546
6547 // Read HALF channel as FLOAT.
6548 for (int i = 0; i < exr_header.num_channels; i++) {
6549 if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
6551 }
6552 }
6553
6554 InitEXRImage(&exr_image);
6555 ret = LoadEXRImageFromMemory(&exr_image, &exr_header, memory, size, err);
6556 if (ret != TINYEXR_SUCCESS) {
6557 return ret;
6558 }
6559
6560 // RGBA
6561 int idxR = -1;
6562 int idxG = -1;
6563 int idxB = -1;
6564 int idxA = -1;
6565 for (int c = 0; c < exr_header.num_channels; c++) {
6566 if (strcmp(exr_header.channels[c].name, "R") == 0) {
6567 idxR = c;
6568 } else if (strcmp(exr_header.channels[c].name, "G") == 0) {
6569 idxG = c;
6570 } else if (strcmp(exr_header.channels[c].name, "B") == 0) {
6571 idxB = c;
6572 } else if (strcmp(exr_header.channels[c].name, "A") == 0) {
6573 idxA = c;
6574 }
6575 }
6576
6577 // TODO(syoyo): Refactor removing same code as used in LoadEXR().
6578 if (exr_header.num_channels == 1) {
6579 // Grayscale channel only.
6580
6581 (*out_rgba) = reinterpret_cast<float *>(
6582 malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
6583 static_cast<size_t>(exr_image.height)));
6584
6585 if (exr_header.tiled) {
6586 const size_t tile_size_x = static_cast<size_t>(exr_header.tile_size_x);
6587 const size_t tile_size_y = static_cast<size_t>(exr_header.tile_size_y);
6588 for (int it = 0; it < exr_image.num_tiles; it++) {
6589 for (size_t j = 0; j < tile_size_y; j++) {
6590 for (size_t i = 0; i < tile_size_x; i++) {
6591 const size_t ii =
6592 static_cast<size_t>(exr_image.tiles[it].offset_x) *
6593 tile_size_x +
6594 i;
6595 const size_t jj =
6596 static_cast<size_t>(exr_image.tiles[it].offset_y) *
6597 tile_size_y +
6598 j;
6599 const size_t idx = ii + jj * static_cast<size_t>(exr_image.width);
6600
6601 // out of region check.
6602 if (ii >= static_cast<size_t>(exr_image.width)) {
6603 continue;
6604 }
6605 if (jj >= static_cast<size_t>(exr_image.height)) {
6606 continue;
6607 }
6608 const size_t srcIdx = i + j * tile_size_x;
6609 unsigned char **src = exr_image.tiles[it].images;
6610 (*out_rgba)[4 * idx + 0] =
6611 reinterpret_cast<float **>(src)[0][srcIdx];
6612 (*out_rgba)[4 * idx + 1] =
6613 reinterpret_cast<float **>(src)[0][srcIdx];
6614 (*out_rgba)[4 * idx + 2] =
6615 reinterpret_cast<float **>(src)[0][srcIdx];
6616 (*out_rgba)[4 * idx + 3] =
6617 reinterpret_cast<float **>(src)[0][srcIdx];
6618 }
6619 }
6620 }
6621 } else {
6622 const size_t pixel_size = static_cast<size_t>(exr_image.width) *
6623 static_cast<size_t>(exr_image.height);
6624 for (size_t i = 0; i < pixel_size; i++) {
6625 const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
6626 (*out_rgba)[4 * i + 0] = val;
6627 (*out_rgba)[4 * i + 1] = val;
6628 (*out_rgba)[4 * i + 2] = val;
6629 (*out_rgba)[4 * i + 3] = val;
6630 }
6631 }
6632
6633 } else {
6634 // TODO(syoyo): Support non RGBA image.
6635
6636 if (idxR == -1) {
6637 tinyexr::SetErrorMessage("R channel not found", err);
6638
6639 // @todo { free exr_image }
6641 }
6642
6643 if (idxG == -1) {
6644 tinyexr::SetErrorMessage("G channel not found", err);
6645 // @todo { free exr_image }
6647 }
6648
6649 if (idxB == -1) {
6650 tinyexr::SetErrorMessage("B channel not found", err);
6651 // @todo { free exr_image }
6653 }
6654
6655 (*out_rgba) = reinterpret_cast<float *>(
6656 malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
6657 static_cast<size_t>(exr_image.height)));
6658
6659 if (exr_header.tiled) {
6660 const size_t tile_size_x = static_cast<size_t>(exr_header.tile_size_x);
6661 const size_t tile_size_y = static_cast<size_t>(exr_header.tile_size_y);
6662 for (int it = 0; it < exr_image.num_tiles; it++) {
6663 for (size_t j = 0; j < tile_size_y; j++)
6664 for (size_t i = 0; i < tile_size_x; i++) {
6665 const size_t ii =
6666 static_cast<size_t>(exr_image.tiles[it].offset_x) *
6667 tile_size_x +
6668 i;
6669 const size_t jj =
6670 static_cast<size_t>(exr_image.tiles[it].offset_y) *
6671 tile_size_y +
6672 j;
6673 const size_t idx = ii + jj * static_cast<size_t>(exr_image.width);
6674
6675 // out of region check.
6676 if (ii >= static_cast<size_t>(exr_image.width)) {
6677 continue;
6678 }
6679 if (jj >= static_cast<size_t>(exr_image.height)) {
6680 continue;
6681 }
6682 const size_t srcIdx = i + j * tile_size_x;
6683 unsigned char **src = exr_image.tiles[it].images;
6684 (*out_rgba)[4 * idx + 0] =
6685 reinterpret_cast<float **>(src)[idxR][srcIdx];
6686 (*out_rgba)[4 * idx + 1] =
6687 reinterpret_cast<float **>(src)[idxG][srcIdx];
6688 (*out_rgba)[4 * idx + 2] =
6689 reinterpret_cast<float **>(src)[idxB][srcIdx];
6690 if (idxA != -1) {
6691 (*out_rgba)[4 * idx + 3] =
6692 reinterpret_cast<float **>(src)[idxA][srcIdx];
6693 } else {
6694 (*out_rgba)[4 * idx + 3] = 1.0;
6695 }
6696 }
6697 }
6698 } else {
6699 const size_t pixel_size = static_cast<size_t>(exr_image.width) *
6700 static_cast<size_t>(exr_image.height);
6701 for (size_t i = 0; i < pixel_size; i++) {
6702 (*out_rgba)[4 * i + 0] =
6703 reinterpret_cast<float **>(exr_image.images)[idxR][i];
6704 (*out_rgba)[4 * i + 1] =
6705 reinterpret_cast<float **>(exr_image.images)[idxG][i];
6706 (*out_rgba)[4 * i + 2] =
6707 reinterpret_cast<float **>(exr_image.images)[idxB][i];
6708 if (idxA != -1) {
6709 (*out_rgba)[4 * i + 3] =
6710 reinterpret_cast<float **>(exr_image.images)[idxA][i];
6711 } else {
6712 (*out_rgba)[4 * i + 3] = 1.0;
6713 }
6714 }
6715 }
6716 }
6717
6718 (*width) = exr_image.width;
6719 (*height) = exr_image.height;
6720
6721 FreeEXRHeader(&exr_header);
6722 FreeEXRImage(&exr_image);
6723
6724 return TINYEXR_SUCCESS;
6725}
6726
6727// Represents a read-only file mapped to an address space in memory.
6728// If no memory-mapping API is available, falls back to allocating a buffer
6729// with a copy of the file's data.
6730struct MemoryMappedFile {
6731 unsigned char *data; // To the start of the file's data.
6732 size_t size; // The size of the file in bytes.
6733#ifdef TINYEXR_USE_WIN32_MMAP
6734 HANDLE windows_file;
6735 HANDLE windows_file_mapping;
6736#elif defined(TINYEXR_USE_POSIX_MMAP)
6737 int posix_descriptor;
6738#endif
6739
6740 // MemoryMappedFile's constructor tries to map memory to a file.
6741 // If this succeeds, valid() will return true and all fields
6742 // are usable; otherwise, valid() will return false.
6743 MemoryMappedFile(const char *filename) {
6744 data = NULL;
6745 size = 0;
6746#ifdef TINYEXR_USE_WIN32_MMAP
6747 windows_file_mapping = NULL;
6748 windows_file =
6749 CreateFileW(tinyexr::UTF8ToWchar(filename).c_str(), // lpFileName
6750 GENERIC_READ, // dwDesiredAccess
6751 FILE_SHARE_READ, // dwShareMode
6752 NULL, // lpSecurityAttributes
6753 OPEN_EXISTING, // dwCreationDisposition
6754 FILE_ATTRIBUTE_READONLY, // dwFlagsAndAttributes
6755 NULL); // hTemplateFile
6756 if (windows_file == INVALID_HANDLE_VALUE) {
6757 return;
6758 }
6759
6760 windows_file_mapping = CreateFileMapping(windows_file, // hFile
6761 NULL, // lpFileMappingAttributes
6762 PAGE_READONLY, // flProtect
6763 0, // dwMaximumSizeHigh
6764 0, // dwMaximumSizeLow
6765 NULL); // lpName
6766 if (windows_file_mapping == NULL) {
6767 return;
6768 }
6769
6770 data = reinterpret_cast<unsigned char *>(
6771 MapViewOfFile(windows_file_mapping, // hFileMappingObject
6772 FILE_MAP_READ, // dwDesiredAccess
6773 0, // dwFileOffsetHigh
6774 0, // dwFileOffsetLow
6775 0)); // dwNumberOfBytesToMap
6776 if (!data) {
6777 return;
6778 }
6779
6780 LARGE_INTEGER windows_file_size = {};
6781 if (!GetFileSizeEx(windows_file, &windows_file_size) ||
6782 static_cast<ULONGLONG>(windows_file_size.QuadPart) >
6783 std::numeric_limits<size_t>::max()) {
6784 UnmapViewOfFile(data);
6785 data = NULL;
6786 return;
6787 }
6788 size = static_cast<size_t>(windows_file_size.QuadPart);
6789#elif defined(TINYEXR_USE_POSIX_MMAP)
6790 posix_descriptor = open(filename, O_RDONLY);
6791 if (posix_descriptor == -1) {
6792 return;
6793 }
6794
6795 struct stat info;
6796 if (fstat(posix_descriptor, &info) < 0) {
6797 return;
6798 }
6799 // Make sure st_size is in the valid range for a size_t. The second case
6800 // can only fail if a POSIX implementation defines off_t to be a larger
6801 // type than size_t - for instance, compiling with _FILE_OFFSET_BITS=64
6802 // on a 32-bit system. On current 64-bit systems, this check can never
6803 // fail, so we turn off clang's Wtautological-type-limit-compare warning
6804 // around this code.
6805#ifdef __clang__
6806#pragma clang diagnostic push
6807#pragma clang diagnostic ignored "-Wtautological-type-limit-compare"
6808#endif
6809 if (info.st_size < 0 ||
6810 info.st_size > std::numeric_limits<ssize_t>::max()) {
6811 return;
6812 }
6813#ifdef __clang__
6814#pragma clang diagnostic pop
6815#endif
6816 size = static_cast<size_t>(info.st_size);
6817
6818 data = reinterpret_cast<unsigned char *>(
6819 mmap(0, size, PROT_READ, MAP_SHARED, posix_descriptor, 0));
6820 if (data == MAP_FAILED) {
6821 data = nullptr;
6822 return;
6823 }
6824#else
6825 FILE *fp = fopen(filename, "rb");
6826 if (!fp) {
6827 return;
6828 }
6829
6830 // Calling fseek(fp, 0, SEEK_END) isn't strictly-conforming C code, but
6831 // since neither the WIN32 nor POSIX APIs are available in this branch, this
6832 // is a reasonable fallback option.
6833 if (fseek(fp, 0, SEEK_END) != 0) {
6834 fclose(fp);
6835 return;
6836 }
6837 const long ftell_result = ftell(fp);
6838 if (ftell_result < 0) {
6839 // Error from ftell
6840 fclose(fp);
6841 return;
6842 }
6843 size = static_cast<size_t>(ftell_result);
6844 if (fseek(fp, 0, SEEK_SET) != 0) {
6845 fclose(fp);
6846 size = 0;
6847 return;
6848 }
6849
6850 data = reinterpret_cast<unsigned char *>(malloc(size));
6851 if (!data) {
6852 size = 0;
6853 fclose(fp);
6854 return;
6855 }
6856 size_t read_bytes = fread(data, 1, size, fp);
6857 if (read_bytes != size) {
6858 // TODO: Try to read data until reading `size` bytes.
6859 fclose(fp);
6860 size = 0;
6861 data = nullptr;
6862 return;
6863 }
6864 fclose(fp);
6865#endif
6866 }
6867
6868 // MemoryMappedFile's destructor closes all its handles.
6869 ~MemoryMappedFile() {
6870#ifdef TINYEXR_USE_WIN32_MMAP
6871 if (data) {
6872 (void)UnmapViewOfFile(data);
6873 data = NULL;
6874 }
6875
6876 if (windows_file_mapping != NULL) {
6877 (void)CloseHandle(windows_file_mapping);
6878 }
6879
6880 if (windows_file != INVALID_HANDLE_VALUE) {
6881 (void)CloseHandle(windows_file);
6882 }
6883#elif defined(TINYEXR_USE_POSIX_MMAP)
6884 if (data) {
6885 (void)munmap(data, size);
6886 data = NULL;
6887 }
6888
6889 if (posix_descriptor != -1) {
6890 (void)close(posix_descriptor);
6891 }
6892#else
6893 if (data) {
6894 (void)free(data);
6895 }
6896 data = NULL;
6897#endif
6898 }
6899
6900 // A MemoryMappedFile cannot be copied or moved.
6901 // Only check for this when compiling with C++11 or higher, since deleted
6902 // function definitions were added then.
6903#if TINYEXR_HAS_CXX11
6904#ifdef __clang__
6905#pragma clang diagnostic push
6906#pragma clang diagnostic ignored "-Wc++98-compat"
6907#endif
6908 MemoryMappedFile(const MemoryMappedFile &) = delete;
6909 MemoryMappedFile &operator=(const MemoryMappedFile &) = delete;
6910 MemoryMappedFile(MemoryMappedFile &&other) noexcept = delete;
6911 MemoryMappedFile &operator=(MemoryMappedFile &&other) noexcept = delete;
6912#ifdef __clang__
6913#pragma clang diagnostic pop
6914#endif
6915#endif
6916
6917 // Returns whether this was successfully opened.
6918 bool valid() const { return data; }
6919};
6920
6921int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header,
6922 const char *filename, const char **err) {
6923 if (exr_image == NULL) {
6924 tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromFile", err);
6926 }
6927
6928 MemoryMappedFile file(filename);
6929 if (!file.valid()) {
6930 tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
6932 }
6933
6934 if (file.size < 16) {
6935 tinyexr::SetErrorMessage("File size too short : " + std::string(filename),
6936 err);
6938 }
6939
6940 return LoadEXRImageFromMemory(exr_image, exr_header, file.data, file.size,
6941 err);
6942}
6943
6944int LoadEXRImageFromMemory(EXRImage *exr_image, const EXRHeader *exr_header,
6945 const unsigned char *memory, const size_t size,
6946 const char **err) {
6947 if (exr_image == NULL || memory == NULL ||
6948 (size < tinyexr::kEXRVersionSize)) {
6949 tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromMemory",
6950 err);
6952 }
6953
6954 if (exr_header->header_len == 0) {
6955 tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err);
6957 }
6958
6959 const unsigned char *head = memory;
6960 const unsigned char *marker = reinterpret_cast<const unsigned char *>(
6961 memory + exr_header->header_len +
6962 8); // +8 for magic number + version header.
6963 return tinyexr::DecodeEXRImage(exr_image, exr_header, head, marker, size,
6964 err);
6965}
6966
6967namespace tinyexr
6968{
6969
6970#ifdef __clang__
6971#pragma clang diagnostic push
6972#pragma clang diagnostic ignored "-Wsign-conversion"
6973#endif
6974
6975// out_data must be allocated initially with the block-header size
6976// of the current image(-part) type
6977static bool EncodePixelData(/* out */ std::vector<unsigned char>& out_data,
6978 const unsigned char* const* images,
6979 int compression_type,
6980 int /*line_order*/,
6981 int width, // for tiled : tile.width
6982 int /*height*/, // for tiled : header.tile_size_y
6983 int x_stride, // for tiled : header.tile_size_x
6984 int line_no, // for tiled : 0
6985 int num_lines, // for tiled : tile.height
6986 size_t pixel_data_size,
6987 const std::vector<ChannelInfo>& channels,
6988 const std::vector<size_t>& channel_offset_list,
6989 std::string *err,
6990 const void* compression_param = 0) // zfp compression param
6991{
6992 size_t buf_size = static_cast<size_t>(width) *
6993 static_cast<size_t>(num_lines) *
6994 static_cast<size_t>(pixel_data_size);
6995 //int last2bit = (buf_size & 3);
6996 // buf_size must be multiple of four
6997 //if(last2bit) buf_size += 4 - last2bit;
6998 std::vector<unsigned char> buf(buf_size);
6999
7000 size_t start_y = static_cast<size_t>(line_no);
7001 for (size_t c = 0; c < channels.size(); c++) {
7002 if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) {
7003 if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
7004 for (int y = 0; y < num_lines; y++) {
7005 // Assume increasing Y
7006 float *line_ptr = reinterpret_cast<float *>(&buf.at(
7007 static_cast<size_t>(pixel_data_size * size_t(y) * size_t(width)) +
7008 channel_offset_list[c] *
7009 static_cast<size_t>(width)));
7010 for (int x = 0; x < width; x++) {
7011 tinyexr::FP16 h16;
7012 h16.u = reinterpret_cast<const unsigned short * const *>(
7013 images)[c][(y + start_y) * size_t(x_stride) + size_t(x)];
7014
7015 tinyexr::FP32 f32 = half_to_float(h16);
7016
7017 tinyexr::swap4(&f32.f);
7018
7019 // line_ptr[x] = f32.f;
7020 tinyexr::cpy4(line_ptr + x, &(f32.f));
7021 }
7022 }
7023 } else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
7024 for (int y = 0; y < num_lines; y++) {
7025 // Assume increasing Y
7026 unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
7027 &buf.at(static_cast<size_t>(pixel_data_size * y *
7028 width) +
7029 channel_offset_list[c] *
7030 static_cast<size_t>(width)));
7031 for (int x = 0; x < width; x++) {
7032 unsigned short val = reinterpret_cast<const unsigned short * const *>(
7033 images)[c][(y + start_y) * x_stride + x];
7034
7035 tinyexr::swap2(&val);
7036
7037 // line_ptr[x] = val;
7038 tinyexr::cpy2(line_ptr + x, &val);
7039 }
7040 }
7041 } else {
7042 if (err) {
7043 (*err) += "Invalid requested_pixel_type.\n";
7044 }
7045 return false;
7046 }
7047
7048 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
7049 if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
7050 for (int y = 0; y < num_lines; y++) {
7051 // Assume increasing Y
7052 unsigned short *line_ptr = reinterpret_cast<unsigned short *>(
7053 &buf.at(static_cast<size_t>(pixel_data_size * y *
7054 width) +
7055 channel_offset_list[c] *
7056 static_cast<size_t>(width)));
7057 for (int x = 0; x < width; x++) {
7058 tinyexr::FP32 f32;
7059 f32.f = reinterpret_cast<const float * const *>(
7060 images)[c][(y + start_y) * x_stride + x];
7061
7062 tinyexr::FP16 h16;
7063 h16 = float_to_half_full(f32);
7064
7065 tinyexr::swap2(reinterpret_cast<unsigned short *>(&h16.u));
7066
7067 // line_ptr[x] = h16.u;
7068 tinyexr::cpy2(line_ptr + x, &(h16.u));
7069 }
7070 }
7071 } else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_FLOAT) {
7072 for (int y = 0; y < num_lines; y++) {
7073 // Assume increasing Y
7074 float *line_ptr = reinterpret_cast<float *>(&buf.at(
7075 static_cast<size_t>(pixel_data_size * y * width) +
7076 channel_offset_list[c] *
7077 static_cast<size_t>(width)));
7078 for (int x = 0; x < width; x++) {
7079 float val = reinterpret_cast<const float * const *>(
7080 images)[c][(y + start_y) * x_stride + x];
7081
7082 tinyexr::swap4(&val);
7083
7084 // line_ptr[x] = val;
7085 tinyexr::cpy4(line_ptr + x, &val);
7086 }
7087 }
7088 } else {
7089 if (err) {
7090 (*err) += "Invalid requested_pixel_type.\n";
7091 }
7092 return false;
7093 }
7094 } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) {
7095 for (int y = 0; y < num_lines; y++) {
7096 // Assume increasing Y
7097 unsigned int *line_ptr = reinterpret_cast<unsigned int *>(&buf.at(
7098 static_cast<size_t>(pixel_data_size * y * width) +
7099 channel_offset_list[c] * static_cast<size_t>(width)));
7100 for (int x = 0; x < width; x++) {
7101 unsigned int val = reinterpret_cast<const unsigned int * const *>(
7102 images)[c][(y + start_y) * x_stride + x];
7103
7104 tinyexr::swap4(&val);
7105
7106 // line_ptr[x] = val;
7107 tinyexr::cpy4(line_ptr + x, &val);
7108 }
7109 }
7110 }
7111 }
7112
7113 if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) {
7114 // 4 byte: scan line
7115 // 4 byte: data size
7116 // ~ : pixel data(uncompressed)
7117 out_data.insert(out_data.end(), buf.begin(), buf.end());
7118
7119 } else if ((compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
7120 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) {
7121#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
7122 std::vector<unsigned char> block(mz_compressBound(
7123 static_cast<unsigned long>(buf.size())));
7124#elif TINYEXR_USE_STB_ZLIB
7125 // there is no compressBound() function, so we use a value that
7126 // is grossly overestimated, but should always work
7127 std::vector<unsigned char> block(256 + 2 * buf.size());
7128#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB == 1)
7129 std::vector<unsigned char> block(nanoz_compressBound(
7130 static_cast<unsigned long>(buf.size())));
7131#else
7132 std::vector<unsigned char> block(
7133 compressBound(static_cast<uLong>(buf.size())));
7134#endif
7135 tinyexr::tinyexr_uint64 outSize = block.size();
7136
7137 if (!tinyexr::CompressZip(&block.at(0), outSize,
7138 reinterpret_cast<const unsigned char *>(&buf.at(0)),
7139 static_cast<unsigned long>(buf.size()))) {
7140 if (err) {
7141 (*err) += "Zip compresssion failed.\n";
7142 }
7143 return false;
7144 }
7145
7146 // 4 byte: scan line
7147 // 4 byte: data size
7148 // ~ : pixel data(compressed)
7149 unsigned int data_len = static_cast<unsigned int>(outSize); // truncate
7150
7151 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7152
7153 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) {
7154 // (buf.size() * 3) / 2 would be enough.
7155 std::vector<unsigned char> block((buf.size() * 3) / 2);
7156
7157 tinyexr::tinyexr_uint64 outSize = block.size();
7158
7159 if (!tinyexr::CompressRle(&block.at(0), outSize,
7160 reinterpret_cast<const unsigned char *>(&buf.at(0)),
7161 static_cast<unsigned long>(buf.size()))) {
7162 if (err) {
7163 (*err) += "RLE compresssion failed.\n";
7164 }
7165 return false;
7166 }
7167
7168 // 4 byte: scan line
7169 // 4 byte: data size
7170 // ~ : pixel data(compressed)
7171 unsigned int data_len = static_cast<unsigned int>(outSize); // truncate
7172 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7173
7174 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
7175#if TINYEXR_USE_PIZ
7176 unsigned int bufLen =
7177 8192 + static_cast<unsigned int>(
7178 2 * static_cast<unsigned int>(
7179 buf.size())); // @fixme { compute good bound. }
7180 std::vector<unsigned char> block(bufLen);
7181 unsigned int outSize = static_cast<unsigned int>(block.size());
7182
7183 if (!CompressPiz(&block.at(0), &outSize,
7184 reinterpret_cast<const unsigned char *>(&buf.at(0)),
7185 buf.size(), channels, width, num_lines)) {
7186 if (err) {
7187 (*err) += "PIZ compresssion failed.\n";
7188 }
7189 return false;
7190 }
7191
7192 // 4 byte: scan line
7193 // 4 byte: data size
7194 // ~ : pixel data(compressed)
7195 unsigned int data_len = outSize;
7196 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7197
7198#else
7199 if (err) {
7200 (*err) += "PIZ compression is disabled in this build.\n";
7201 }
7202 return false;
7203#endif
7204 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
7205#if TINYEXR_USE_ZFP
7206 const ZFPCompressionParam* zfp_compression_param = reinterpret_cast<const ZFPCompressionParam*>(compression_param);
7207 std::vector<unsigned char> block;
7208 unsigned int outSize;
7209
7210 tinyexr::CompressZfp(
7211 &block, &outSize, reinterpret_cast<const float *>(&buf.at(0)),
7212 width, num_lines, static_cast<int>(channels.size()), *zfp_compression_param);
7213
7214 // 4 byte: scan line
7215 // 4 byte: data size
7216 // ~ : pixel data(compressed)
7217 unsigned int data_len = outSize;
7218 out_data.insert(out_data.end(), block.begin(), block.begin() + data_len);
7219
7220#else
7221 if (err) {
7222 (*err) += "ZFP compression is disabled in this build.\n";
7223 }
7224 (void)compression_param;
7225 return false;
7226#endif
7227 } else {
7228 return false;
7229 }
7230
7231 return true;
7232}
7233
7234static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_header,
7235 const std::vector<tinyexr::ChannelInfo>& channels,
7236 std::vector<std::vector<unsigned char> >& data_list,
7237 size_t start_index, // for data_list
7238 int num_x_tiles, int num_y_tiles,
7239 const std::vector<size_t>& channel_offset_list,
7240 int pixel_data_size,
7241 const void* compression_param, // must be set if zfp compression is enabled
7242 std::string* err) {
7243 int num_tiles = num_x_tiles * num_y_tiles;
7244 if (num_tiles != level_image->num_tiles) {
7245 if (err) {
7246 (*err) += "Invalid number of tiles in argument.\n";
7247 }
7249 }
7250
7251 if ((exr_header->tile_size_x > level_image->width || exr_header->tile_size_y > level_image->height) &&
7252 level_image->level_x == 0 && level_image->level_y == 0) {
7253 if (err) {
7254 (*err) += "Failed to encode tile data.\n";
7255 }
7257 }
7258
7259
7260#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7261 std::atomic<bool> invalid_data(false);
7262#else
7263 bool invalid_data(false);
7264#endif
7265
7266#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7267 std::vector<std::thread> workers;
7268 std::atomic<int> tile_count(0);
7269
7270 int num_threads = std::max(1, int(std::thread::hardware_concurrency()));
7271 if (num_threads > int(num_tiles)) {
7272 num_threads = int(num_tiles);
7273 }
7274
7275 for (int t = 0; t < num_threads; t++) {
7276 workers.emplace_back(std::thread([&]() {
7277 int i = 0;
7278 while ((i = tile_count++) < num_tiles) {
7279
7280#else
7281 // Use signed int since some OpenMP compiler doesn't allow unsigned type for
7282 // `parallel for`
7283#if TINYEXR_USE_OPENMP
7284#pragma omp parallel for
7285#endif
7286 for (int i = 0; i < num_tiles; i++) {
7287
7288#endif
7289 size_t tile_idx = static_cast<size_t>(i);
7290 size_t data_idx = tile_idx + start_index;
7291
7292 int x_tile = i % num_x_tiles;
7293 int y_tile = i / num_x_tiles;
7294
7295 EXRTile& tile = level_image->tiles[tile_idx];
7296
7297 const unsigned char* const* images =
7298 static_cast<const unsigned char* const*>(tile.images);
7299
7300 data_list[data_idx].resize(5*sizeof(int));
7301 size_t data_header_size = data_list[data_idx].size();
7302 bool ret = EncodePixelData(data_list[data_idx],
7303 images,
7304 exr_header->compression_type,
7305 0, // increasing y
7306 tile.width,
7307 exr_header->tile_size_y,
7308 exr_header->tile_size_x,
7309 0,
7310 tile.height,
7311 pixel_data_size,
7312 channels,
7313 channel_offset_list,
7314 err, compression_param);
7315 if (!ret) {
7316 invalid_data = true;
7317 continue;
7318 }
7319 if (data_list[data_idx].size() <= data_header_size) {
7320 invalid_data = true;
7321 continue;
7322 }
7323
7324 int data_len = static_cast<int>(data_list[data_idx].size() - data_header_size);
7325 //tileX, tileY, levelX, levelY // pixel_data_size(int)
7326 memcpy(&data_list[data_idx][0], &x_tile, sizeof(int));
7327 memcpy(&data_list[data_idx][4], &y_tile, sizeof(int));
7328 memcpy(&data_list[data_idx][8], &level_image->level_x, sizeof(int));
7329 memcpy(&data_list[data_idx][12], &level_image->level_y, sizeof(int));
7330 memcpy(&data_list[data_idx][16], &data_len, sizeof(int));
7331
7332 swap4(reinterpret_cast<int*>(&data_list[data_idx][0]));
7333 swap4(reinterpret_cast<int*>(&data_list[data_idx][4]));
7334 swap4(reinterpret_cast<int*>(&data_list[data_idx][8]));
7335 swap4(reinterpret_cast<int*>(&data_list[data_idx][12]));
7336 swap4(reinterpret_cast<int*>(&data_list[data_idx][16]));
7337
7338#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7339 }
7340}));
7341 }
7342
7343 for (auto &t : workers) {
7344 t.join();
7345 }
7346#else
7347 } // omp parallel
7348#endif
7349
7350 if (invalid_data) {
7351 if (err) {
7352 (*err) += "Failed to encode tile data.\n";
7353 }
7355 }
7356 return TINYEXR_SUCCESS;
7357}
7358
7359static int NumScanlines(int compression_type) {
7360 int num_scanlines = 1;
7361 if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
7362 num_scanlines = 16;
7363 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
7364 num_scanlines = 32;
7365 } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
7366 num_scanlines = 16;
7367 }
7368 return num_scanlines;
7369}
7370
7371static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header,
7372 const std::vector<ChannelInfo>& channels,
7373 int num_blocks,
7374 tinyexr_uint64 chunk_offset, // starting offset of current chunk
7375 bool is_multipart,
7376 OffsetData& offset_data, // output block offsets, must be initialized
7377 std::vector<std::vector<unsigned char> >& data_list, // output
7378 tinyexr_uint64& total_size, // output: ending offset of current chunk
7379 std::string* err) {
7380 int num_scanlines = NumScanlines(exr_header->compression_type);
7381
7382 data_list.resize(num_blocks);
7383
7384 std::vector<size_t> channel_offset_list(
7385 static_cast<size_t>(exr_header->num_channels));
7386
7387 int pixel_data_size = 0;
7388 {
7389 size_t channel_offset = 0;
7390 for (size_t c = 0; c < static_cast<size_t>(exr_header->num_channels); c++) {
7391 channel_offset_list[c] = channel_offset;
7392 if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) {
7393 pixel_data_size += sizeof(unsigned short);
7394 channel_offset += sizeof(unsigned short);
7395 } else if (channels[c].requested_pixel_type ==
7397 pixel_data_size += sizeof(float);
7398 channel_offset += sizeof(float);
7399 } else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_UINT) {
7400 pixel_data_size += sizeof(unsigned int);
7401 channel_offset += sizeof(unsigned int);
7402 } else {
7403 if (err) {
7404 (*err) += "Invalid requested_pixel_type.\n";
7405 }
7407 }
7408 }
7409 }
7410
7411 const void* compression_param = 0;
7412#if TINYEXR_USE_ZFP
7413 tinyexr::ZFPCompressionParam zfp_compression_param;
7414
7415 // Use ZFP compression parameter from custom attributes(if such a parameter
7416 // exists)
7417 {
7418 std::string e;
7419 bool ret = tinyexr::FindZFPCompressionParam(
7420 &zfp_compression_param, exr_header->custom_attributes,
7421 exr_header->num_custom_attributes, &e);
7422
7423 if (!ret) {
7424 // Use predefined compression parameter.
7425 zfp_compression_param.type = 0;
7426 zfp_compression_param.rate = 2;
7427 }
7428 compression_param = &zfp_compression_param;
7429 }
7430#endif
7431
7432 tinyexr_uint64 offset = chunk_offset;
7433 tinyexr_uint64 doffset = is_multipart ? 4u : 0u;
7434
7435 if (exr_image->tiles) {
7436 const EXRImage* level_image = exr_image;
7437 size_t block_idx = 0;
7438 //tinyexr::tinyexr_uint64 block_data_size = 0;
7439 int num_levels = (exr_header->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) ?
7440 offset_data.num_x_levels : (offset_data.num_x_levels * offset_data.num_y_levels);
7441 for (int level_index = 0; level_index < num_levels; ++level_index) {
7442 if (!level_image) {
7443 if (err) {
7444 (*err) += "Invalid number of tiled levels for EncodeChunk\n";
7445 }
7447 }
7448
7449 int level_index_from_image = LevelIndex(level_image->level_x, level_image->level_y,
7450 exr_header->tile_level_mode, offset_data.num_x_levels);
7451 if (level_index_from_image < 0) {
7452 if (err) {
7453 (*err) += "Invalid tile level mode\n";
7454 }
7456 }
7457
7458 if (level_index_from_image != level_index) {
7459 if (err) {
7460 (*err) += "Incorrect level ordering in tiled image\n";
7461 }
7463 }
7464 int num_y_tiles = int(offset_data.offsets[level_index].size());
7465 if (num_y_tiles <= 0) {
7466 if (err) {
7467 (*err) += "Invalid Y tile size\n";
7468 }
7470 }
7471
7472 int num_x_tiles = int(offset_data.offsets[level_index][0].size());
7473 if (num_x_tiles <= 0) {
7474 if (err) {
7475 (*err) += "Invalid X tile size\n";
7476 }
7478 }
7479
7480 std::string e;
7481 int ret = EncodeTiledLevel(level_image,
7482 exr_header,
7483 channels,
7484 data_list,
7485 block_idx,
7486 num_x_tiles,
7487 num_y_tiles,
7488 channel_offset_list,
7489 pixel_data_size,
7490 compression_param,
7491 &e);
7492 if (ret != TINYEXR_SUCCESS) {
7493 if (!e.empty() && err) {
7494 (*err) += e;
7495 }
7496 return ret;
7497 }
7498
7499 for (size_t j = 0; j < static_cast<size_t>(num_y_tiles); ++j)
7500 for (size_t i = 0; i < static_cast<size_t>(num_x_tiles); ++i) {
7501 offset_data.offsets[level_index][j][i] = offset;
7502 swap8(reinterpret_cast<tinyexr_uint64*>(&offset_data.offsets[level_index][j][i]));
7503 offset += data_list[block_idx].size() + doffset;
7504 //block_data_size += data_list[block_idx].size();
7505 ++block_idx;
7506 }
7507 level_image = level_image->next_level;
7508 }
7509 TINYEXR_CHECK_AND_RETURN_C(static_cast<int>(block_idx) == num_blocks, TINYEXR_ERROR_INVALID_DATA);
7510 total_size = offset;
7511 } else { // scanlines
7512 std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data.offsets[0][0];
7513
7514#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7515 std::atomic<bool> invalid_data(false);
7516 std::vector<std::thread> workers;
7517 std::atomic<int> block_count(0);
7518
7519 int num_threads = std::min(std::max(1, int(std::thread::hardware_concurrency())), num_blocks);
7520
7521 for (int t = 0; t < num_threads; t++) {
7522 workers.emplace_back(std::thread([&]() {
7523 int i = 0;
7524 while ((i = block_count++) < num_blocks) {
7525
7526#else
7527 bool invalid_data(false);
7528#if TINYEXR_USE_OPENMP
7529#pragma omp parallel for
7530#endif
7531 for (int i = 0; i < num_blocks; i++) {
7532
7533#endif
7534 int start_y = num_scanlines * i;
7535 int end_Y = (std::min)(num_scanlines * (i + 1), exr_image->height);
7536 int num_lines = end_Y - start_y;
7537
7538 const unsigned char* const* images =
7539 static_cast<const unsigned char* const*>(exr_image->images);
7540
7541 data_list[i].resize(2*sizeof(int));
7542 size_t data_header_size = data_list[i].size();
7543
7544 bool ret = EncodePixelData(data_list[i],
7545 images,
7546 exr_header->compression_type,
7547 0, // increasing y
7548 exr_image->width,
7549 exr_image->height,
7550 exr_image->width,
7551 start_y,
7552 num_lines,
7553 pixel_data_size,
7554 channels,
7555 channel_offset_list,
7556 err,
7557 compression_param);
7558 if (!ret) {
7559 invalid_data = true;
7560 continue; // "break" cannot be used with OpenMP
7561 }
7562 if (data_list[i].size() <= data_header_size) {
7563 invalid_data = true;
7564 continue; // "break" cannot be used with OpenMP
7565 }
7566 int data_len = static_cast<int>(data_list[i].size() - data_header_size);
7567 memcpy(&data_list[i][0], &start_y, sizeof(int));
7568 memcpy(&data_list[i][4], &data_len, sizeof(int));
7569
7570 swap4(reinterpret_cast<int*>(&data_list[i][0]));
7571 swap4(reinterpret_cast<int*>(&data_list[i][4]));
7572#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
7573 }
7574 }));
7575 }
7576
7577 for (auto &t : workers) {
7578 t.join();
7579 }
7580#else
7581 } // omp parallel
7582#endif
7583
7584 if (invalid_data) {
7585 if (err) {
7586 (*err) += "Failed to encode scanline data.\n";
7587 }
7589 }
7590
7591 for (size_t i = 0; i < static_cast<size_t>(num_blocks); i++) {
7592 offsets[i] = offset;
7593 tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64 *>(&offsets[i]));
7594 offset += data_list[i].size() + doffset;
7595 }
7596
7597 total_size = static_cast<size_t>(offset);
7598 }
7599 return TINYEXR_SUCCESS;
7600}
7601
7602// can save a single or multi-part image (no deep* formats)
7603static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images,
7604 const EXRHeader** exr_headers,
7605 unsigned int num_parts,
7606 unsigned char** memory_out, const char** err) {
7607 if (exr_images == NULL || exr_headers == NULL || num_parts == 0 ||
7608 memory_out == NULL) {
7609 SetErrorMessage("Invalid argument for SaveEXRNPartImageToMemory",
7610 err);
7611 return 0;
7612 }
7613 {
7614 for (unsigned int i = 0; i < num_parts; ++i) {
7615 if (exr_headers[i]->compression_type < 0) {
7616 SetErrorMessage("Invalid argument for SaveEXRNPartImageToMemory",
7617 err);
7618 return 0;
7619 }
7620#if !TINYEXR_USE_PIZ
7621 if (exr_headers[i]->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
7622 SetErrorMessage("PIZ compression is not supported in this build",
7623 err);
7624 return 0;
7625 }
7626#endif
7627 if (exr_headers[i]->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
7628#if !TINYEXR_USE_ZFP
7629 SetErrorMessage("ZFP compression is not supported in this build",
7630 err);
7631 return 0;
7632#else
7633 // All channels must be fp32.
7634 // No fp16 support in ZFP atm(as of 2023 June)
7635 // https://github.com/LLNL/fpzip/issues/2
7636 for (int c = 0; c < exr_headers[i]->num_channels; ++c) {
7637 if (exr_headers[i]->requested_pixel_types[c] != TINYEXR_PIXELTYPE_FLOAT) {
7638 SetErrorMessage("Pixel type must be FLOAT for ZFP compression",
7639 err);
7640 return 0;
7641 }
7642 }
7643#endif
7644 }
7645 }
7646 }
7647
7648 std::vector<unsigned char> memory;
7649
7650 // Header
7651 {
7652 const char header[] = { 0x76, 0x2f, 0x31, 0x01 };
7653 memory.insert(memory.end(), header, header + 4);
7654 }
7655
7656 // Version
7657 // using value from the first header
7658 int long_name = exr_headers[0]->long_name;
7659 {
7660 char marker[] = { 2, 0, 0, 0 };
7661 /* @todo
7662 if (exr_header->non_image) {
7663 marker[1] |= 0x8;
7664 }
7665 */
7666 // tiled
7667 if (num_parts == 1 && exr_images[0].tiles) {
7668 marker[1] |= 0x2;
7669 }
7670 // long_name
7671 if (long_name) {
7672 marker[1] |= 0x4;
7673 }
7674 // multipart
7675 if (num_parts > 1) {
7676 marker[1] |= 0x10;
7677 }
7678 memory.insert(memory.end(), marker, marker + 4);
7679 }
7680
7681 int total_chunk_count = 0;
7682 std::vector<int> chunk_count(num_parts);
7683 std::vector<OffsetData> offset_data(num_parts);
7684 for (unsigned int i = 0; i < num_parts; ++i) {
7685 if (!exr_images[i].tiles) {
7686 int num_scanlines = NumScanlines(exr_headers[i]->compression_type);
7687 chunk_count[i] =
7688 (exr_images[i].height + num_scanlines - 1) / num_scanlines;
7689 InitSingleResolutionOffsets(offset_data[i], chunk_count[i]);
7690 total_chunk_count += chunk_count[i];
7691 } else {
7692 {
7693 std::vector<int> num_x_tiles, num_y_tiles;
7694 if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) {
7695 SetErrorMessage("Failed to precalculate Tile info",
7696 err);
7698 }
7699 int ntiles = InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles);
7700 if (ntiles > 0) {
7701 chunk_count[i] = ntiles;
7702 } else {
7703 SetErrorMessage("Failed to compute Tile offsets",
7704 err);
7706
7707 }
7708 total_chunk_count += chunk_count[i];
7709 }
7710 }
7711 }
7712 // Write attributes to memory buffer.
7713 std::vector< std::vector<tinyexr::ChannelInfo> > channels(num_parts);
7714 {
7715 std::set<std::string> partnames;
7716 for (unsigned int i = 0; i < num_parts; ++i) {
7717 //channels
7718 {
7719 std::vector<unsigned char> data;
7720
7721 for (int c = 0; c < exr_headers[i]->num_channels; c++) {
7722 tinyexr::ChannelInfo info;
7723 info.p_linear = 0;
7724 info.pixel_type = exr_headers[i]->pixel_types[c];
7725 info.requested_pixel_type = exr_headers[i]->requested_pixel_types[c];
7726 info.x_sampling = 1;
7727 info.y_sampling = 1;
7728 info.name = std::string(exr_headers[i]->channels[c].name);
7729 channels[i].push_back(info);
7730 }
7731
7732 tinyexr::WriteChannelInfo(data, channels[i]);
7733
7734 tinyexr::WriteAttributeToMemory(&memory, "channels", "chlist", &data.at(0),
7735 static_cast<int>(data.size()));
7736 }
7737
7738 {
7739 int comp = exr_headers[i]->compression_type;
7740 swap4(&comp);
7741 WriteAttributeToMemory(
7742 &memory, "compression", "compression",
7743 reinterpret_cast<const unsigned char*>(&comp), 1);
7744 }
7745
7746 {
7747 int data[4] = { 0, 0, exr_images[i].width - 1, exr_images[i].height - 1 };
7748 swap4(&data[0]);
7749 swap4(&data[1]);
7750 swap4(&data[2]);
7751 swap4(&data[3]);
7752 WriteAttributeToMemory(
7753 &memory, "dataWindow", "box2i",
7754 reinterpret_cast<const unsigned char*>(data), sizeof(int) * 4);
7755
7756 int data0[4] = { 0, 0, exr_images[0].width - 1, exr_images[0].height - 1 };
7757 swap4(&data0[0]);
7758 swap4(&data0[1]);
7759 swap4(&data0[2]);
7760 swap4(&data0[3]);
7761 // Note: must be the same across parts (currently, using value from the first header)
7762 WriteAttributeToMemory(
7763 &memory, "displayWindow", "box2i",
7764 reinterpret_cast<const unsigned char*>(data0), sizeof(int) * 4);
7765 }
7766
7767 {
7768 unsigned char line_order = 0; // @fixme { read line_order from EXRHeader }
7769 WriteAttributeToMemory(&memory, "lineOrder", "lineOrder",
7770 &line_order, 1);
7771 }
7772
7773 {
7774 // Note: must be the same across parts
7775 float aspectRatio = 1.0f;
7776 swap4(&aspectRatio);
7777 WriteAttributeToMemory(
7778 &memory, "pixelAspectRatio", "float",
7779 reinterpret_cast<const unsigned char*>(&aspectRatio), sizeof(float));
7780 }
7781
7782 {
7783 float center[2] = { 0.0f, 0.0f };
7784 swap4(&center[0]);
7785 swap4(&center[1]);
7786 WriteAttributeToMemory(
7787 &memory, "screenWindowCenter", "v2f",
7788 reinterpret_cast<const unsigned char*>(center), 2 * sizeof(float));
7789 }
7790
7791 {
7792 float w = 1.0f;
7793 swap4(&w);
7794 WriteAttributeToMemory(&memory, "screenWindowWidth", "float",
7795 reinterpret_cast<const unsigned char*>(&w),
7796 sizeof(float));
7797 }
7798
7799 if (exr_images[i].tiles) {
7800 unsigned char tile_mode = static_cast<unsigned char>(exr_headers[i]->tile_level_mode & 0x3);
7801 if (exr_headers[i]->tile_rounding_mode) tile_mode |= (1u << 4u);
7802 //unsigned char data[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
7803 unsigned int datai[3] = { 0, 0, 0 };
7804 unsigned char* data = reinterpret_cast<unsigned char*>(&datai[0]);
7805 datai[0] = static_cast<unsigned int>(exr_headers[i]->tile_size_x);
7806 datai[1] = static_cast<unsigned int>(exr_headers[i]->tile_size_y);
7807 data[8] = tile_mode;
7808 swap4(reinterpret_cast<unsigned int*>(&data[0]));
7809 swap4(reinterpret_cast<unsigned int*>(&data[4]));
7810 WriteAttributeToMemory(
7811 &memory, "tiles", "tiledesc",
7812 reinterpret_cast<const unsigned char*>(data), 9);
7813 }
7814
7815 // must be present for multi-part files - according to spec.
7816 if (num_parts > 1) {
7817 // name
7818 {
7819 size_t len = 0;
7820 if ((len = strlen(exr_headers[i]->name)) > 0) {
7821#if TINYEXR_HAS_CXX11
7822 partnames.emplace(exr_headers[i]->name);
7823#else
7824 partnames.insert(std::string(exr_headers[i]->name));
7825#endif
7826 if (partnames.size() != i + 1) {
7827 SetErrorMessage("'name' attributes must be unique for a multi-part file", err);
7828 return 0;
7829 }
7830 WriteAttributeToMemory(
7831 &memory, "name", "string",
7832 reinterpret_cast<const unsigned char*>(exr_headers[i]->name),
7833 static_cast<int>(len));
7834 } else {
7835 SetErrorMessage("Invalid 'name' attribute for a multi-part file", err);
7836 return 0;
7837 }
7838 }
7839 // type
7840 {
7841 const char* type = "scanlineimage";
7842 if (exr_images[i].tiles) type = "tiledimage";
7843 WriteAttributeToMemory(
7844 &memory, "type", "string",
7845 reinterpret_cast<const unsigned char*>(type),
7846 static_cast<int>(strlen(type)));
7847 }
7848 // chunkCount
7849 {
7850 WriteAttributeToMemory(
7851 &memory, "chunkCount", "int",
7852 reinterpret_cast<const unsigned char*>(&chunk_count[i]),
7853 4);
7854 }
7855 }
7856
7857 // Custom attributes
7858 if (exr_headers[i]->num_custom_attributes > 0) {
7859 for (int j = 0; j < exr_headers[i]->num_custom_attributes; j++) {
7860 tinyexr::WriteAttributeToMemory(
7861 &memory, exr_headers[i]->custom_attributes[j].name,
7862 exr_headers[i]->custom_attributes[j].type,
7863 reinterpret_cast<const unsigned char*>(
7864 exr_headers[i]->custom_attributes[j].value),
7865 exr_headers[i]->custom_attributes[j].size);
7866 }
7867 }
7868
7869 { // end of header
7870 memory.push_back(0);
7871 }
7872 }
7873 }
7874 if (num_parts > 1) {
7875 // end of header list
7876 memory.push_back(0);
7877 }
7878
7879 tinyexr_uint64 chunk_offset = memory.size() + size_t(total_chunk_count) * sizeof(tinyexr_uint64);
7880
7881 tinyexr_uint64 total_size = 0;
7882 std::vector< std::vector< std::vector<unsigned char> > > data_lists(num_parts);
7883 for (unsigned int i = 0; i < num_parts; ++i) {
7884 std::string e;
7885 int ret = EncodeChunk(&exr_images[i], exr_headers[i],
7886 channels[i],
7887 chunk_count[i],
7888 // starting offset of current chunk after part-number
7889 chunk_offset,
7890 num_parts > 1,
7891 offset_data[i], // output: block offsets, must be initialized
7892 data_lists[i], // output
7893 total_size, // output
7894 &e);
7895 if (ret != TINYEXR_SUCCESS) {
7896 if (!e.empty()) {
7897 tinyexr::SetErrorMessage(e, err);
7898 }
7899 return 0;
7900 }
7901 chunk_offset = total_size;
7902 }
7903
7904 // Allocating required memory
7905 if (total_size == 0) { // something went wrong
7906 tinyexr::SetErrorMessage("Output memory size is zero", err);
7908 }
7909 (*memory_out) = static_cast<unsigned char*>(malloc(size_t(total_size)));
7910
7911 // Writing header
7912 memcpy((*memory_out), &memory[0], memory.size());
7913 unsigned char* memory_ptr = *memory_out + memory.size();
7914 size_t sum = memory.size();
7915
7916 // Writing offset data for chunks
7917 for (unsigned int i = 0; i < num_parts; ++i) {
7918 if (exr_images[i].tiles) {
7919 const EXRImage* level_image = &exr_images[i];
7920 int num_levels = (exr_headers[i]->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) ?
7921 offset_data[i].num_x_levels : (offset_data[i].num_x_levels * offset_data[i].num_y_levels);
7922 for (int level_index = 0; level_index < num_levels; ++level_index) {
7923 for (size_t j = 0; j < offset_data[i].offsets[level_index].size(); ++j) {
7924 size_t num_bytes = sizeof(tinyexr_uint64) * offset_data[i].offsets[level_index][j].size();
7925 sum += num_bytes;
7926 if (sum > total_size) {
7927 tinyexr::SetErrorMessage("Invalid offset bytes in Tiled Part image.", err);
7929 }
7930
7931 memcpy(memory_ptr,
7932 reinterpret_cast<unsigned char*>(&offset_data[i].offsets[level_index][j][0]),
7933 num_bytes);
7934 memory_ptr += num_bytes;
7935 }
7936 level_image = level_image->next_level;
7937 }
7938 } else {
7939 size_t num_bytes = sizeof(tinyexr::tinyexr_uint64) * static_cast<size_t>(chunk_count[i]);
7940 sum += num_bytes;
7941 if (sum > total_size) {
7942 tinyexr::SetErrorMessage("Invalid offset bytes in Part image.", err);
7944 }
7945 std::vector<tinyexr::tinyexr_uint64>& offsets = offset_data[i].offsets[0][0];
7946 memcpy(memory_ptr, reinterpret_cast<unsigned char*>(&offsets[0]), num_bytes);
7947 memory_ptr += num_bytes;
7948 }
7949 }
7950
7951 // Writing chunk data
7952 for (unsigned int i = 0; i < num_parts; ++i) {
7953 for (size_t j = 0; j < static_cast<size_t>(chunk_count[i]); ++j) {
7954 if (num_parts > 1) {
7955 sum += 4;
7956 if (sum > total_size) {
7957 tinyexr::SetErrorMessage("Buffer overrun in reading Part image chunk data.", err);
7959 }
7960 unsigned int part_number = i;
7961 swap4(&part_number);
7962 memcpy(memory_ptr, &part_number, 4);
7963 memory_ptr += 4;
7964 }
7965 sum += data_lists[i][j].size();
7966 if (sum > total_size) {
7967 tinyexr::SetErrorMessage("Buffer overrun in reading Part image chunk data.", err);
7969 }
7970 memcpy(memory_ptr, &data_lists[i][j][0], data_lists[i][j].size());
7971 memory_ptr += data_lists[i][j].size();
7972 }
7973 }
7974
7975 if (sum != total_size) {
7976 tinyexr::SetErrorMessage("Corrupted Part image chunk data.", err);
7978 }
7979
7980 return size_t(total_size); // OK
7981}
7982
7983#ifdef __clang__
7984#pragma clang diagnostic pop
7985#endif
7986
7987} // tinyexr
7988
7989size_t SaveEXRImageToMemory(const EXRImage* exr_image,
7990 const EXRHeader* exr_header,
7991 unsigned char** memory_out, const char** err) {
7992 return tinyexr::SaveEXRNPartImageToMemory(exr_image, &exr_header, 1, memory_out, err);
7993}
7994
7995int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header,
7996 const char *filename, const char **err) {
7997 if (exr_image == NULL || filename == NULL ||
7998 exr_header->compression_type < 0) {
7999 tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToFile", err);
8001 }
8002
8003#if !TINYEXR_USE_PIZ
8004 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) {
8005 tinyexr::SetErrorMessage("PIZ compression is not supported in this build",
8006 err);
8008 }
8009#endif
8010
8011#if !TINYEXR_USE_ZFP
8012 if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) {
8013 tinyexr::SetErrorMessage("ZFP compression is not supported in this build",
8014 err);
8016 }
8017#endif
8018
8019 FILE *fp = NULL;
8020#ifdef _WIN32
8021#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang
8022 errno_t errcode =
8023 _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"wb");
8024 if (errcode != 0) {
8025 tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename),
8026 err);
8028 }
8029#else
8030 // Unknown compiler or MinGW without MINGW_HAS_SECURE_API.
8031 fp = fopen(filename, "wb");
8032#endif
8033#else
8034 fp = fopen(filename, "wb");
8035#endif
8036 if (!fp) {
8037 tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename),
8038 err);
8040 }
8041
8042 unsigned char *mem = NULL;
8043 size_t mem_size = SaveEXRImageToMemory(exr_image, exr_header, &mem, err);
8044 if (mem_size == 0) {
8045 fclose(fp);
8047 }
8048
8049 size_t written_size = 0;
8050 if ((mem_size > 0) && mem) {
8051 written_size = fwrite(mem, 1, mem_size, fp);
8052 }
8053 free(mem);
8054
8055 fclose(fp);
8056
8057 if (written_size != mem_size) {
8058 tinyexr::SetErrorMessage("Cannot write a file", err);
8060 }
8061
8062 return TINYEXR_SUCCESS;
8063}
8064
8065size_t SaveEXRMultipartImageToMemory(const EXRImage* exr_images,
8066 const EXRHeader** exr_headers,
8067 unsigned int num_parts,
8068 unsigned char** memory_out, const char** err) {
8069 if (exr_images == NULL || exr_headers == NULL || num_parts < 2 ||
8070 memory_out == NULL) {
8071 tinyexr::SetErrorMessage("Invalid argument for SaveEXRNPartImageToMemory",
8072 err);
8073 return 0;
8074 }
8075 return tinyexr::SaveEXRNPartImageToMemory(exr_images, exr_headers, num_parts, memory_out, err);
8076}
8077
8078int SaveEXRMultipartImageToFile(const EXRImage* exr_images,
8079 const EXRHeader** exr_headers,
8080 unsigned int num_parts,
8081 const char* filename,
8082 const char** err) {
8083 if (exr_images == NULL || exr_headers == NULL || num_parts < 2) {
8084 tinyexr::SetErrorMessage("Invalid argument for SaveEXRMultipartImageToFile",
8085 err);
8087 }
8088
8089 FILE *fp = NULL;
8090#ifdef _WIN32
8091#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang.
8092 errno_t errcode =
8093 _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"wb");
8094 if (errcode != 0) {
8095 tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename),
8096 err);
8098 }
8099#else
8100 // Unknown compiler or MinGW without MINGW_HAS_SECURE_API.
8101 fp = fopen(filename, "wb");
8102#endif
8103#else
8104 fp = fopen(filename, "wb");
8105#endif
8106 if (!fp) {
8107 tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename),
8108 err);
8110 }
8111
8112 unsigned char *mem = NULL;
8113 size_t mem_size = SaveEXRMultipartImageToMemory(exr_images, exr_headers, num_parts, &mem, err);
8114 if (mem_size == 0) {
8115 fclose(fp);
8117 }
8118
8119 size_t written_size = 0;
8120 if ((mem_size > 0) && mem) {
8121 written_size = fwrite(mem, 1, mem_size, fp);
8122 }
8123 free(mem);
8124
8125 fclose(fp);
8126
8127 if (written_size != mem_size) {
8128 tinyexr::SetErrorMessage("Cannot write a file", err);
8130 }
8131
8132 return TINYEXR_SUCCESS;
8133}
8134
8135int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) {
8136 if (deep_image == NULL) {
8137 tinyexr::SetErrorMessage("Invalid argument for LoadDeepEXR", err);
8139 }
8140
8141 MemoryMappedFile file(filename);
8142 if (!file.valid()) {
8143 tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
8145 }
8146
8147 if (file.size == 0) {
8148 tinyexr::SetErrorMessage("File size is zero : " + std::string(filename),
8149 err);
8151 }
8152
8153 const char *head = reinterpret_cast<const char *>(file.data);
8154 const char *marker = reinterpret_cast<const char *>(file.data);
8155
8156 // Header check.
8157 {
8158 const char header[] = {0x76, 0x2f, 0x31, 0x01};
8159
8160 if (memcmp(marker, header, 4) != 0) {
8161 tinyexr::SetErrorMessage("Invalid magic number", err);
8163 }
8164 marker += 4;
8165 }
8166
8167 // Version, scanline.
8168 {
8169 // ver 2.0, scanline, deep bit on(0x800)
8170 // must be [2, 0, 0, 0]
8171 if (marker[0] != 2 || marker[1] != 8 || marker[2] != 0 || marker[3] != 0) {
8172 tinyexr::SetErrorMessage("Unsupported version or scanline", err);
8174 }
8175
8176 marker += 4;
8177 }
8178
8179 int dx = -1;
8180 int dy = -1;
8181 int dw = -1;
8182 int dh = -1;
8183 int num_scanline_blocks = 1; // 16 for ZIP compression.
8184 int compression_type = -1;
8185 int num_channels = -1;
8186 std::vector<tinyexr::ChannelInfo> channels;
8187
8188 // Read attributes
8189 size_t size = file.size - tinyexr::kEXRVersionSize;
8190 for (;;) {
8191 if (0 == size) {
8193 } else if (marker[0] == '\0') {
8194 marker++;
8195 size--;
8196 break;
8197 }
8198
8199 std::string attr_name;
8200 std::string attr_type;
8201 std::vector<unsigned char> data;
8202 size_t marker_size;
8203 if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size,
8204 marker, size)) {
8205 std::stringstream ss;
8206 ss << "Failed to parse attribute\n";
8207 tinyexr::SetErrorMessage(ss.str(), err);
8209 }
8210 marker += marker_size;
8211 size -= marker_size;
8212
8213 if (attr_name.compare("compression") == 0) {
8214 compression_type = data[0];
8215 if (compression_type > TINYEXR_COMPRESSIONTYPE_PIZ) {
8216 std::stringstream ss;
8217 ss << "Unsupported compression type : " << compression_type;
8218 tinyexr::SetErrorMessage(ss.str(), err);
8220 }
8221
8222 if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) {
8223 num_scanline_blocks = 16;
8224 }
8225
8226 } else if (attr_name.compare("channels") == 0) {
8227 // name: zero-terminated string, from 1 to 255 bytes long
8228 // pixel type: int, possible values are: UINT = 0 HALF = 1 FLOAT = 2
8229 // pLinear: unsigned char, possible values are 0 and 1
8230 // reserved: three chars, should be zero
8231 // xSampling: int
8232 // ySampling: int
8233
8234 if (!tinyexr::ReadChannelInfo(channels, data)) {
8235 tinyexr::SetErrorMessage("Failed to parse channel info", err);
8237 }
8238
8239 num_channels = static_cast<int>(channels.size());
8240
8241 if (num_channels < 1) {
8242 tinyexr::SetErrorMessage("Invalid channels format", err);
8244 }
8245
8246 } else if (attr_name.compare("dataWindow") == 0) {
8247 memcpy(&dx, &data.at(0), sizeof(int));
8248 memcpy(&dy, &data.at(4), sizeof(int));
8249 memcpy(&dw, &data.at(8), sizeof(int));
8250 memcpy(&dh, &data.at(12), sizeof(int));
8251 tinyexr::swap4(&dx);
8252 tinyexr::swap4(&dy);
8253 tinyexr::swap4(&dw);
8254 tinyexr::swap4(&dh);
8255
8256 } else if (attr_name.compare("displayWindow") == 0) {
8257 int x;
8258 int y;
8259 int w;
8260 int h;
8261 memcpy(&x, &data.at(0), sizeof(int));
8262 memcpy(&y, &data.at(4), sizeof(int));
8263 memcpy(&w, &data.at(8), sizeof(int));
8264 memcpy(&h, &data.at(12), sizeof(int));
8265 tinyexr::swap4(&x);
8266 tinyexr::swap4(&y);
8267 tinyexr::swap4(&w);
8268 tinyexr::swap4(&h);
8269 }
8270 }
8271
8272 TINYEXR_CHECK_AND_RETURN_C(dx >= 0, TINYEXR_ERROR_INVALID_DATA);
8273 TINYEXR_CHECK_AND_RETURN_C(dy >= 0, TINYEXR_ERROR_INVALID_DATA);
8274 TINYEXR_CHECK_AND_RETURN_C(dw >= 0, TINYEXR_ERROR_INVALID_DATA);
8275 TINYEXR_CHECK_AND_RETURN_C(dh >= 0, TINYEXR_ERROR_INVALID_DATA);
8276 TINYEXR_CHECK_AND_RETURN_C(num_channels >= 1, TINYEXR_ERROR_INVALID_DATA);
8277
8278 int data_width = dw - dx + 1;
8279 int data_height = dh - dy + 1;
8280
8281 // Read offset tables.
8282 int num_blocks = data_height / num_scanline_blocks;
8283 if (num_blocks * num_scanline_blocks < data_height) {
8284 num_blocks++;
8285 }
8286
8287 std::vector<tinyexr::tinyexr_int64> offsets(static_cast<size_t>(num_blocks));
8288
8289 for (size_t y = 0; y < static_cast<size_t>(num_blocks); y++) {
8290 tinyexr::tinyexr_int64 offset;
8291 memcpy(&offset, marker, sizeof(tinyexr::tinyexr_int64));
8292 tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64 *>(&offset));
8293 marker += sizeof(tinyexr::tinyexr_int64); // = 8
8294 offsets[y] = offset;
8295 }
8296
8297#if TINYEXR_USE_PIZ
8298 if ((compression_type == TINYEXR_COMPRESSIONTYPE_NONE) ||
8299 (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) ||
8300 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
8301 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) ||
8302 (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ)) {
8303#else
8304 if ((compression_type == TINYEXR_COMPRESSIONTYPE_NONE) ||
8305 (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) ||
8306 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
8307 (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) {
8308#endif
8309 // OK
8310 } else {
8311 tinyexr::SetErrorMessage("Unsupported compression format", err);
8313 }
8314
8315 deep_image->image = static_cast<float ***>(
8316 malloc(sizeof(float **) * static_cast<size_t>(num_channels)));
8317 for (int c = 0; c < num_channels; c++) {
8318 deep_image->image[c] = static_cast<float **>(
8319 malloc(sizeof(float *) * static_cast<size_t>(data_height)));
8320 for (int y = 0; y < data_height; y++) {
8321 }
8322 }
8323
8324 deep_image->offset_table = static_cast<int **>(
8325 malloc(sizeof(int *) * static_cast<size_t>(data_height)));
8326 for (int y = 0; y < data_height; y++) {
8327 deep_image->offset_table[y] = static_cast<int *>(
8328 malloc(sizeof(int) * static_cast<size_t>(data_width)));
8329 }
8330
8331 for (size_t y = 0; y < static_cast<size_t>(num_blocks); y++) {
8332 const unsigned char *data_ptr =
8333 reinterpret_cast<const unsigned char *>(head + offsets[y]);
8334
8335 // int: y coordinate
8336 // int64: packed size of pixel offset table
8337 // int64: packed size of sample data
8338 // int64: unpacked size of sample data
8339 // compressed pixel offset table
8340 // compressed sample data
8341 int line_no;
8342 tinyexr::tinyexr_int64 packedOffsetTableSize;
8343 tinyexr::tinyexr_int64 packedSampleDataSize;
8344 tinyexr::tinyexr_int64 unpackedSampleDataSize;
8345 memcpy(&line_no, data_ptr, sizeof(int));
8346 memcpy(&packedOffsetTableSize, data_ptr + 4,
8347 sizeof(tinyexr::tinyexr_int64));
8348 memcpy(&packedSampleDataSize, data_ptr + 12,
8349 sizeof(tinyexr::tinyexr_int64));
8350 memcpy(&unpackedSampleDataSize, data_ptr + 20,
8351 sizeof(tinyexr::tinyexr_int64));
8352
8353 tinyexr::swap4(&line_no);
8354 tinyexr::swap8(
8355 reinterpret_cast<tinyexr::tinyexr_uint64 *>(&packedOffsetTableSize));
8356 tinyexr::swap8(
8357 reinterpret_cast<tinyexr::tinyexr_uint64 *>(&packedSampleDataSize));
8358 tinyexr::swap8(
8359 reinterpret_cast<tinyexr::tinyexr_uint64 *>(&unpackedSampleDataSize));
8360
8361 std::vector<int> pixelOffsetTable(static_cast<size_t>(data_width));
8362
8363 // decode pixel offset table.
8364 {
8365 unsigned long dstLen =
8366 static_cast<unsigned long>(pixelOffsetTable.size() * sizeof(int));
8367 if (!tinyexr::DecompressZip(
8368 reinterpret_cast<unsigned char *>(&pixelOffsetTable.at(0)),
8369 &dstLen, data_ptr + 28,
8370 static_cast<unsigned long>(packedOffsetTableSize))) {
8371 return false;
8372 }
8373
8374 TINYEXR_CHECK_AND_RETURN_C(dstLen == pixelOffsetTable.size() * sizeof(int), TINYEXR_ERROR_INVALID_DATA);
8375 for (size_t i = 0; i < static_cast<size_t>(data_width); i++) {
8376 deep_image->offset_table[y][i] = pixelOffsetTable[i];
8377 }
8378 }
8379
8380 std::vector<unsigned char> sample_data(
8381 static_cast<size_t>(unpackedSampleDataSize));
8382
8383 // decode sample data.
8384 {
8385 unsigned long dstLen = static_cast<unsigned long>(unpackedSampleDataSize);
8386 if (dstLen) {
8387 if (!tinyexr::DecompressZip(
8388 reinterpret_cast<unsigned char *>(&sample_data.at(0)), &dstLen,
8389 data_ptr + 28 + packedOffsetTableSize,
8390 static_cast<unsigned long>(packedSampleDataSize))) {
8391 return false;
8392 }
8393 TINYEXR_CHECK_AND_RETURN_C(dstLen == static_cast<unsigned long>(unpackedSampleDataSize), TINYEXR_ERROR_INVALID_DATA);
8394 }
8395 }
8396
8397 // decode sample
8398 int sampleSize = -1;
8399 std::vector<int> channel_offset_list(static_cast<size_t>(num_channels));
8400 {
8401 int channel_offset = 0;
8402 for (size_t i = 0; i < static_cast<size_t>(num_channels); i++) {
8403 channel_offset_list[i] = channel_offset;
8404 if (channels[i].pixel_type == TINYEXR_PIXELTYPE_UINT) { // UINT
8405 channel_offset += 4;
8406 } else if (channels[i].pixel_type == TINYEXR_PIXELTYPE_HALF) { // half
8407 channel_offset += 2;
8408 } else if (channels[i].pixel_type ==
8409 TINYEXR_PIXELTYPE_FLOAT) { // float
8410 channel_offset += 4;
8411 } else {
8412 tinyexr::SetErrorMessage("Invalid pixel_type in chnnels.", err);
8414 }
8415 }
8416 sampleSize = channel_offset;
8417 }
8418 TINYEXR_CHECK_AND_RETURN_C(sampleSize >= 2, TINYEXR_ERROR_INVALID_DATA);
8419
8420 TINYEXR_CHECK_AND_RETURN_C(static_cast<size_t>(
8421 pixelOffsetTable[static_cast<size_t>(data_width - 1)] *
8422 sampleSize) == sample_data.size(), TINYEXR_ERROR_INVALID_DATA);
8423 int samples_per_line = static_cast<int>(sample_data.size()) / sampleSize;
8424
8425 //
8426 // Alloc memory
8427 //
8428
8429 //
8430 // pixel data is stored as image[channels][pixel_samples]
8431 //
8432 {
8433 tinyexr::tinyexr_uint64 data_offset = 0;
8434 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
8435 deep_image->image[c][y] = static_cast<float *>(
8436 malloc(sizeof(float) * static_cast<size_t>(samples_per_line)));
8437
8438 if (channels[c].pixel_type == 0) { // UINT
8439 for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
8440 unsigned int ui;
8441 unsigned int *src_ptr = reinterpret_cast<unsigned int *>(
8442 &sample_data.at(size_t(data_offset) + x * sizeof(int)));
8443 tinyexr::cpy4(&ui, src_ptr);
8444 deep_image->image[c][y][x] = static_cast<float>(ui); // @fixme
8445 }
8446 data_offset +=
8447 sizeof(unsigned int) * static_cast<size_t>(samples_per_line);
8448 } else if (channels[c].pixel_type == 1) { // half
8449 for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
8450 tinyexr::FP16 f16;
8451 const unsigned short *src_ptr = reinterpret_cast<unsigned short *>(
8452 &sample_data.at(size_t(data_offset) + x * sizeof(short)));
8453 tinyexr::cpy2(&(f16.u), src_ptr);
8454 tinyexr::FP32 f32 = half_to_float(f16);
8455 deep_image->image[c][y][x] = f32.f;
8456 }
8457 data_offset += sizeof(short) * static_cast<size_t>(samples_per_line);
8458 } else { // float
8459 for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) {
8460 float f;
8461 const float *src_ptr = reinterpret_cast<float *>(
8462 &sample_data.at(size_t(data_offset) + x * sizeof(float)));
8463 tinyexr::cpy4(&f, src_ptr);
8464 deep_image->image[c][y][x] = f;
8465 }
8466 data_offset += sizeof(float) * static_cast<size_t>(samples_per_line);
8467 }
8468 }
8469 }
8470 } // y
8471
8472 deep_image->width = data_width;
8473 deep_image->height = data_height;
8474
8475 deep_image->channel_names = static_cast<const char **>(
8476 malloc(sizeof(const char *) * static_cast<size_t>(num_channels)));
8477 for (size_t c = 0; c < static_cast<size_t>(num_channels); c++) {
8478#ifdef _WIN32
8479 deep_image->channel_names[c] = _strdup(channels[c].name.c_str());
8480#else
8481 deep_image->channel_names[c] = strdup(channels[c].name.c_str());
8482#endif
8483 }
8484 deep_image->num_channels = num_channels;
8485
8486 return TINYEXR_SUCCESS;
8487}
8488
8489void InitEXRImage(EXRImage *exr_image) {
8490 if (exr_image == NULL) {
8491 return;
8492 }
8493
8494 exr_image->width = 0;
8495 exr_image->height = 0;
8496 exr_image->num_channels = 0;
8497
8498 exr_image->images = NULL;
8499 exr_image->tiles = NULL;
8500 exr_image->next_level = NULL;
8501 exr_image->level_x = 0;
8502 exr_image->level_y = 0;
8503
8504 exr_image->num_tiles = 0;
8505}
8506
8507void FreeEXRErrorMessage(const char *msg) {
8508 if (msg) {
8509 free(reinterpret_cast<void *>(const_cast<char *>(msg)));
8510 }
8511 return;
8512}
8513
8514void InitEXRHeader(EXRHeader *exr_header) {
8515 if (exr_header == NULL) {
8516 return;
8517 }
8518
8519 memset(exr_header, 0, sizeof(EXRHeader));
8520}
8521
8522int FreeEXRHeader(EXRHeader *exr_header) {
8523 if (exr_header == NULL) {
8525 }
8526
8527 if (exr_header->channels) {
8528 free(exr_header->channels);
8529 }
8530
8531 if (exr_header->pixel_types) {
8532 free(exr_header->pixel_types);
8533 }
8534
8535 if (exr_header->requested_pixel_types) {
8536 free(exr_header->requested_pixel_types);
8537 }
8538
8539 for (int i = 0; i < exr_header->num_custom_attributes; i++) {
8540 if (exr_header->custom_attributes[i].value) {
8541 free(exr_header->custom_attributes[i].value);
8542 }
8543 }
8544
8545 if (exr_header->custom_attributes) {
8546 free(exr_header->custom_attributes);
8547 }
8548
8549 EXRSetNameAttr(exr_header, NULL);
8550
8551 return TINYEXR_SUCCESS;
8552}
8553
8554void EXRSetNameAttr(EXRHeader* exr_header, const char* name) {
8555 if (exr_header == NULL) {
8556 return;
8557 }
8558 memset(exr_header->name, 0, 256);
8559 if (name != NULL) {
8560 size_t len = std::min(strlen(name), size_t(255));
8561 if (len) {
8562 memcpy(exr_header->name, name, len);
8563 }
8564 }
8565}
8566
8567int EXRNumLevels(const EXRImage* exr_image) {
8568 if (exr_image == NULL) return 0;
8569 if(exr_image->images) return 1; // scanlines
8570 int levels = 1;
8571 const EXRImage* level_image = exr_image;
8572 while((level_image = level_image->next_level)) ++levels;
8573 return levels;
8574}
8575
8576int FreeEXRImage(EXRImage *exr_image) {
8577 if (exr_image == NULL) {
8579 }
8580
8581 if (exr_image->next_level) {
8582 FreeEXRImage(exr_image->next_level);
8583 delete exr_image->next_level;
8584 }
8585
8586 for (int i = 0; i < exr_image->num_channels; i++) {
8587 if (exr_image->images && exr_image->images[i]) {
8588 free(exr_image->images[i]);
8589 }
8590 }
8591
8592 if (exr_image->images) {
8593 free(exr_image->images);
8594 }
8595
8596 if (exr_image->tiles) {
8597 for (int tid = 0; tid < exr_image->num_tiles; tid++) {
8598 for (int i = 0; i < exr_image->num_channels; i++) {
8599 if (exr_image->tiles[tid].images && exr_image->tiles[tid].images[i]) {
8600 free(exr_image->tiles[tid].images[i]);
8601 }
8602 }
8603 if (exr_image->tiles[tid].images) {
8604 free(exr_image->tiles[tid].images);
8605 }
8606 }
8607 free(exr_image->tiles);
8608 }
8609
8610 return TINYEXR_SUCCESS;
8611}
8612
8613int ParseEXRHeaderFromFile(EXRHeader *exr_header, const EXRVersion *exr_version,
8614 const char *filename, const char **err) {
8615 if (exr_header == NULL || exr_version == NULL || filename == NULL) {
8616 tinyexr::SetErrorMessage("Invalid argument for ParseEXRHeaderFromFile",
8617 err);
8619 }
8620
8621 MemoryMappedFile file(filename);
8622 if (!file.valid()) {
8623 tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
8625 }
8626
8627 return ParseEXRHeaderFromMemory(exr_header, exr_version, file.data, file.size,
8628 err);
8629}
8630
8632 int *num_headers,
8633 const EXRVersion *exr_version,
8634 const unsigned char *memory, size_t size,
8635 const char **err) {
8636 if (memory == NULL || exr_headers == NULL || num_headers == NULL ||
8637 exr_version == NULL) {
8638 // Invalid argument
8639 tinyexr::SetErrorMessage(
8640 "Invalid argument for ParseEXRMultipartHeaderFromMemory", err);
8642 }
8643
8644 if (size < tinyexr::kEXRVersionSize) {
8645 tinyexr::SetErrorMessage("Data size too short", err);
8647 }
8648
8649 const unsigned char *marker = memory + tinyexr::kEXRVersionSize;
8650 size_t marker_size = size - tinyexr::kEXRVersionSize;
8651
8652 std::vector<tinyexr::HeaderInfo> infos;
8653
8654 for (;;) {
8655 tinyexr::HeaderInfo info;
8656 info.clear();
8657
8658 std::string err_str;
8659 bool empty_header = false;
8660 int ret = ParseEXRHeader(&info, &empty_header, exr_version, &err_str,
8661 marker, marker_size);
8662
8663 if (ret != TINYEXR_SUCCESS) {
8664
8665 // Free malloc-allocated memory here.
8666 for (size_t i = 0; i < info.attributes.size(); i++) {
8667 if (info.attributes[i].value) {
8668 free(info.attributes[i].value);
8669 }
8670 }
8671
8672 tinyexr::SetErrorMessage(err_str, err);
8673 return ret;
8674 }
8675
8676 if (empty_header) {
8677 marker += 1; // skip '\0'
8678 break;
8679 }
8680
8681 // `chunkCount` must exist in the header.
8682 if (info.chunk_count == 0) {
8683
8684 // Free malloc-allocated memory here.
8685 for (size_t i = 0; i < info.attributes.size(); i++) {
8686 if (info.attributes[i].value) {
8687 free(info.attributes[i].value);
8688 }
8689 }
8690
8691 tinyexr::SetErrorMessage(
8692 "`chunkCount' attribute is not found in the header.", err);
8694 }
8695
8696 infos.push_back(info);
8697
8698 // move to next header.
8699 marker += info.header_len;
8700 size -= info.header_len;
8701 }
8702
8703 // allocate memory for EXRHeader and create array of EXRHeader pointers.
8704 (*exr_headers) =
8705 static_cast<EXRHeader **>(malloc(sizeof(EXRHeader *) * infos.size()));
8706
8707
8708 int retcode = TINYEXR_SUCCESS;
8709
8710 for (size_t i = 0; i < infos.size(); i++) {
8711 EXRHeader *exr_header = static_cast<EXRHeader *>(malloc(sizeof(EXRHeader)));
8712 memset(exr_header, 0, sizeof(EXRHeader));
8713
8714 std::string warn;
8715 std::string _err;
8716 if (!ConvertHeader(exr_header, infos[i], &warn, &_err)) {
8717
8718 // Free malloc-allocated memory here.
8719 for (size_t k = 0; k < infos[i].attributes.size(); k++) {
8720 if (infos[i].attributes[k].value) {
8721 free(infos[i].attributes[k].value);
8722 }
8723 }
8724
8725 if (!_err.empty()) {
8726 tinyexr::SetErrorMessage(
8727 _err, err);
8728 }
8729 // continue to converting headers
8731 }
8732
8733 exr_header->multipart = exr_version->multipart ? 1 : 0;
8734
8735 (*exr_headers)[i] = exr_header;
8736 }
8737
8738 (*num_headers) = static_cast<int>(infos.size());
8739
8740 return retcode;
8741}
8742
8743int ParseEXRMultipartHeaderFromFile(EXRHeader ***exr_headers, int *num_headers,
8744 const EXRVersion *exr_version,
8745 const char *filename, const char **err) {
8746 if (exr_headers == NULL || num_headers == NULL || exr_version == NULL ||
8747 filename == NULL) {
8748 tinyexr::SetErrorMessage(
8749 "Invalid argument for ParseEXRMultipartHeaderFromFile()", err);
8751 }
8752
8753 MemoryMappedFile file(filename);
8754 if (!file.valid()) {
8755 tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
8757 }
8758
8760 exr_headers, num_headers, exr_version, file.data, file.size, err);
8761}
8762
8763int ParseEXRVersionFromMemory(EXRVersion *version, const unsigned char *memory,
8764 size_t size) {
8765 if (version == NULL || memory == NULL) {
8767 }
8768
8769 if (size < tinyexr::kEXRVersionSize) {
8771 }
8772
8773 const unsigned char *marker = memory;
8774
8775 // Header check.
8776 {
8777 const char header[] = {0x76, 0x2f, 0x31, 0x01};
8778
8779 if (memcmp(marker, header, 4) != 0) {
8781 }
8782 marker += 4;
8783 }
8784
8785 version->tiled = false;
8786 version->long_name = false;
8787 version->non_image = false;
8788 version->multipart = false;
8789
8790 // Parse version header.
8791 {
8792 // must be 2
8793 if (marker[0] != 2) {
8795 }
8796
8797 if (version == NULL) {
8798 return TINYEXR_SUCCESS; // May OK
8799 }
8800
8801 version->version = 2;
8802
8803 if (marker[1] & 0x2) { // 9th bit
8804 version->tiled = true;
8805 }
8806 if (marker[1] & 0x4) { // 10th bit
8807 version->long_name = true;
8808 }
8809 if (marker[1] & 0x8) { // 11th bit
8810 version->non_image = true; // (deep image)
8811 }
8812 if (marker[1] & 0x10) { // 12th bit
8813 version->multipart = true;
8814 }
8815 }
8816
8817 return TINYEXR_SUCCESS;
8818}
8819
8820int ParseEXRVersionFromFile(EXRVersion *version, const char *filename) {
8821 if (filename == NULL) {
8823 }
8824
8825 FILE *fp = NULL;
8826#ifdef _WIN32
8827#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang.
8828 errno_t err = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb");
8829 if (err != 0) {
8830 // TODO(syoyo): return wfopen_s erro code
8832 }
8833#else
8834 // Unknown compiler or MinGW without MINGW_HAS_SECURE_API.
8835 fp = fopen(filename, "rb");
8836#endif
8837#else
8838 fp = fopen(filename, "rb");
8839#endif
8840 if (!fp) {
8842 }
8843
8844 // Try to read kEXRVersionSize bytes; if the file is shorter than
8845 // kEXRVersionSize, this will produce an error. This avoids a call to
8846 // fseek(fp, 0, SEEK_END), which is not required to be supported by C
8847 // implementations.
8848 unsigned char buf[tinyexr::kEXRVersionSize];
8849 size_t ret = fread(&buf[0], 1, tinyexr::kEXRVersionSize, fp);
8850 fclose(fp);
8851
8852 if (ret != tinyexr::kEXRVersionSize) {
8854 }
8855
8856 return ParseEXRVersionFromMemory(version, buf, tinyexr::kEXRVersionSize);
8857}
8858
8860 const EXRHeader **exr_headers,
8861 unsigned int num_parts,
8862 const unsigned char *memory,
8863 const size_t size, const char **err) {
8864 if (exr_images == NULL || exr_headers == NULL || num_parts == 0 ||
8865 memory == NULL || (size <= tinyexr::kEXRVersionSize)) {
8866 tinyexr::SetErrorMessage(
8867 "Invalid argument for LoadEXRMultipartImageFromMemory()", err);
8869 }
8870
8871 // compute total header size.
8872 size_t total_header_size = 0;
8873 for (unsigned int i = 0; i < num_parts; i++) {
8874 if (exr_headers[i]->header_len == 0) {
8875 tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err);
8877 }
8878
8879 total_header_size += exr_headers[i]->header_len;
8880 }
8881
8882 const char *marker = reinterpret_cast<const char *>(
8883 memory + total_header_size + 4 +
8884 4); // +8 for magic number and version header.
8885
8886 marker += 1; // Skip empty header.
8887
8888 // NOTE 1:
8889 // In multipart image, There is 'part number' before chunk data.
8890 // 4 byte : part number
8891 // 4+ : chunk
8892 //
8893 // NOTE 2:
8894 // EXR spec says 'part number' is 'unsigned long' but actually this is
8895 // 'unsigned int(4 bytes)' in OpenEXR implementation...
8896 // http://www.openexr.com/openexrfilelayout.pdf
8897
8898 // Load chunk offset table.
8899 std::vector<tinyexr::OffsetData> chunk_offset_table_list;
8900 chunk_offset_table_list.reserve(num_parts);
8901 for (size_t i = 0; i < static_cast<size_t>(num_parts); i++) {
8902 chunk_offset_table_list.resize(chunk_offset_table_list.size() + 1);
8903 tinyexr::OffsetData& offset_data = chunk_offset_table_list.back();
8904 if (!exr_headers[i]->tiled || exr_headers[i]->tile_level_mode == TINYEXR_TILE_ONE_LEVEL) {
8905 tinyexr::InitSingleResolutionOffsets(offset_data, size_t(exr_headers[i]->chunk_count));
8906 std::vector<tinyexr::tinyexr_uint64>& offset_table = offset_data.offsets[0][0];
8907
8908 for (size_t c = 0; c < offset_table.size(); c++) {
8909 tinyexr::tinyexr_uint64 offset;
8910 memcpy(&offset, marker, 8);
8911 tinyexr::swap8(&offset);
8912
8913 if (offset >= size) {
8914 tinyexr::SetErrorMessage("Invalid offset size in EXR header chunks.",
8915 err);
8917 }
8918
8919 offset_table[c] = offset + 4; // +4 to skip 'part number'
8920 marker += 8;
8921 }
8922 } else {
8923 {
8924 std::vector<int> num_x_tiles, num_y_tiles;
8925 if (!tinyexr::PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) {
8926 tinyexr::SetErrorMessage("Invalid tile info.", err);
8928 }
8929 int num_blocks = InitTileOffsets(offset_data, exr_headers[i], num_x_tiles, num_y_tiles);
8930 if (num_blocks != exr_headers[i]->chunk_count) {
8931 tinyexr::SetErrorMessage("Invalid offset table size.", err);
8933 }
8934 }
8935 for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) {
8936 for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) {
8937 for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
8938 tinyexr::tinyexr_uint64 offset;
8939 memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64));
8940 tinyexr::swap8(&offset);
8941 if (offset >= size) {
8942 tinyexr::SetErrorMessage("Invalid offset size in EXR header chunks.",
8943 err);
8945 }
8946 offset_data.offsets[l][dy][dx] = offset + 4; // +4 to skip 'part number'
8947 marker += sizeof(tinyexr::tinyexr_uint64); // = 8
8948 }
8949 }
8950 }
8951 }
8952 }
8953
8954 // Decode image.
8955 for (size_t i = 0; i < static_cast<size_t>(num_parts); i++) {
8956 tinyexr::OffsetData &offset_data = chunk_offset_table_list[i];
8957
8958 // First check 'part number' is identical to 'i'
8959 for (unsigned int l = 0; l < offset_data.offsets.size(); ++l)
8960 for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy)
8961 for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
8962
8963 const unsigned char *part_number_addr =
8964 memory + offset_data.offsets[l][dy][dx] - 4; // -4 to move to 'part number' field.
8965 unsigned int part_no;
8966 memcpy(&part_no, part_number_addr, sizeof(unsigned int)); // 4
8967 tinyexr::swap4(&part_no);
8968
8969 if (part_no != i) {
8970 tinyexr::SetErrorMessage("Invalid `part number' in EXR header chunks.",
8971 err);
8973 }
8974 }
8975
8976 std::string e;
8977 int ret = tinyexr::DecodeChunk(&exr_images[i], exr_headers[i], offset_data,
8978 memory, size, &e);
8979 if (ret != TINYEXR_SUCCESS) {
8980 if (!e.empty()) {
8981 tinyexr::SetErrorMessage(e, err);
8982 }
8983 return ret;
8984 }
8985 }
8986
8987 return TINYEXR_SUCCESS;
8988}
8989
8991 const EXRHeader **exr_headers,
8992 unsigned int num_parts, const char *filename,
8993 const char **err) {
8994 if (exr_images == NULL || exr_headers == NULL || num_parts == 0) {
8995 tinyexr::SetErrorMessage(
8996 "Invalid argument for LoadEXRMultipartImageFromFile", err);
8998 }
8999
9000 MemoryMappedFile file(filename);
9001 if (!file.valid()) {
9002 tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err);
9004 }
9005
9006 return LoadEXRMultipartImageFromMemory(exr_images, exr_headers, num_parts,
9007 file.data, file.size, err);
9008}
9009
9010int SaveEXRToMemory(const float *data, int width, int height, int components,
9011 const int save_as_fp16, unsigned char **outbuf, const char **err) {
9012
9013 if ((components == 1) || components == 3 || components == 4) {
9014 // OK
9015 } else {
9016 std::stringstream ss;
9017 ss << "Unsupported component value : " << components << std::endl;
9018
9019 tinyexr::SetErrorMessage(ss.str(), err);
9021 }
9022
9023 EXRHeader header;
9024 InitEXRHeader(&header);
9025
9026 if ((width < 16) && (height < 16)) {
9027 // No compression for small image.
9029 } else {
9031 }
9032
9033 EXRImage image;
9034 InitEXRImage(&image);
9035
9036 image.num_channels = components;
9037
9038 std::vector<float> images[4];
9039
9040 if (components == 1) {
9041 images[0].resize(static_cast<size_t>(width * height));
9042 memcpy(images[0].data(), data, sizeof(float) * size_t(width * height));
9043 } else {
9044 images[0].resize(static_cast<size_t>(width * height));
9045 images[1].resize(static_cast<size_t>(width * height));
9046 images[2].resize(static_cast<size_t>(width * height));
9047 images[3].resize(static_cast<size_t>(width * height));
9048
9049 // Split RGB(A)RGB(A)RGB(A)... into R, G and B(and A) layers
9050 for (size_t i = 0; i < static_cast<size_t>(width * height); i++) {
9051 images[0][i] = data[static_cast<size_t>(components) * i + 0];
9052 images[1][i] = data[static_cast<size_t>(components) * i + 1];
9053 images[2][i] = data[static_cast<size_t>(components) * i + 2];
9054 if (components == 4) {
9055 images[3][i] = data[static_cast<size_t>(components) * i + 3];
9056 }
9057 }
9058 }
9059
9060 float *image_ptr[4] = {0, 0, 0, 0};
9061 if (components == 4) {
9062 image_ptr[0] = &(images[3].at(0)); // A
9063 image_ptr[1] = &(images[2].at(0)); // B
9064 image_ptr[2] = &(images[1].at(0)); // G
9065 image_ptr[3] = &(images[0].at(0)); // R
9066 } else if (components == 3) {
9067 image_ptr[0] = &(images[2].at(0)); // B
9068 image_ptr[1] = &(images[1].at(0)); // G
9069 image_ptr[2] = &(images[0].at(0)); // R
9070 } else if (components == 1) {
9071 image_ptr[0] = &(images[0].at(0)); // A
9072 }
9073
9074 image.images = reinterpret_cast<unsigned char **>(image_ptr);
9075 image.width = width;
9076 image.height = height;
9077
9078 header.num_channels = components;
9079 header.channels = static_cast<EXRChannelInfo *>(malloc(
9080 sizeof(EXRChannelInfo) * static_cast<size_t>(header.num_channels)));
9081 // Must be (A)BGR order, since most of EXR viewers expect this channel order.
9082 if (components == 4) {
9083#ifdef _MSC_VER
9084 strncpy_s(header.channels[0].name, "A", 255);
9085 strncpy_s(header.channels[1].name, "B", 255);
9086 strncpy_s(header.channels[2].name, "G", 255);
9087 strncpy_s(header.channels[3].name, "R", 255);
9088#else
9089 strncpy(header.channels[0].name, "A", 255);
9090 strncpy(header.channels[1].name, "B", 255);
9091 strncpy(header.channels[2].name, "G", 255);
9092 strncpy(header.channels[3].name, "R", 255);
9093#endif
9094 header.channels[0].name[strlen("A")] = '\0';
9095 header.channels[1].name[strlen("B")] = '\0';
9096 header.channels[2].name[strlen("G")] = '\0';
9097 header.channels[3].name[strlen("R")] = '\0';
9098 } else if (components == 3) {
9099#ifdef _MSC_VER
9100 strncpy_s(header.channels[0].name, "B", 255);
9101 strncpy_s(header.channels[1].name, "G", 255);
9102 strncpy_s(header.channels[2].name, "R", 255);
9103#else
9104 strncpy(header.channels[0].name, "B", 255);
9105 strncpy(header.channels[1].name, "G", 255);
9106 strncpy(header.channels[2].name, "R", 255);
9107#endif
9108 header.channels[0].name[strlen("B")] = '\0';
9109 header.channels[1].name[strlen("G")] = '\0';
9110 header.channels[2].name[strlen("R")] = '\0';
9111 } else {
9112#ifdef _MSC_VER
9113 strncpy_s(header.channels[0].name, "A", 255);
9114#else
9115 strncpy(header.channels[0].name, "A", 255);
9116#endif
9117 header.channels[0].name[strlen("A")] = '\0';
9118 }
9119
9120 header.pixel_types = static_cast<int *>(
9121 malloc(sizeof(int) * static_cast<size_t>(header.num_channels)));
9122 header.requested_pixel_types = static_cast<int *>(
9123 malloc(sizeof(int) * static_cast<size_t>(header.num_channels)));
9124 for (int i = 0; i < header.num_channels; i++) {
9125 header.pixel_types[i] =
9126 TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
9127
9128 if (save_as_fp16 > 0) {
9129 header.requested_pixel_types[i] =
9130 TINYEXR_PIXELTYPE_HALF; // save with half(fp16) pixel format
9131 } else {
9132 header.requested_pixel_types[i] =
9133 TINYEXR_PIXELTYPE_FLOAT; // save with float(fp32) pixel format(i.e.
9134 // no precision reduction)
9135 }
9136 }
9137
9138
9139 unsigned char *mem_buf;
9140 size_t mem_size = SaveEXRImageToMemory(&image, &header, &mem_buf, err);
9141
9142 if (mem_size == 0) {
9144 }
9145
9146 free(header.channels);
9147 free(header.pixel_types);
9148 free(header.requested_pixel_types);
9149
9150 if (mem_size > size_t(std::numeric_limits<int>::max())) {
9151 free(mem_buf);
9153 }
9154
9155 (*outbuf) = mem_buf;
9156
9157 return int(mem_size);
9158}
9159
9160int SaveEXR(const float *data, int width, int height, int components,
9161 const int save_as_fp16, const char *outfilename, const char **err) {
9162 if ((components == 1) || components == 3 || components == 4) {
9163 // OK
9164 } else {
9165 std::stringstream ss;
9166 ss << "Unsupported component value : " << components << std::endl;
9167
9168 tinyexr::SetErrorMessage(ss.str(), err);
9170 }
9171
9172 EXRHeader header;
9173 InitEXRHeader(&header);
9174
9175 if ((width < 16) && (height < 16)) {
9176 // No compression for small image.
9178 } else {
9180 }
9181
9182 EXRImage image;
9183 InitEXRImage(&image);
9184
9185 image.num_channels = components;
9186
9187 std::vector<float> images[4];
9188 const size_t pixel_count =
9189 static_cast<size_t>(width) * static_cast<size_t>(height);
9190
9191 if (components == 1) {
9192 images[0].resize(pixel_count);
9193 memcpy(images[0].data(), data, sizeof(float) * pixel_count);
9194 } else {
9195 images[0].resize(pixel_count);
9196 images[1].resize(pixel_count);
9197 images[2].resize(pixel_count);
9198 images[3].resize(pixel_count);
9199
9200 // Split RGB(A)RGB(A)RGB(A)... into R, G and B(and A) layers
9201 for (size_t i = 0; i < pixel_count; i++) {
9202 images[0][i] = data[static_cast<size_t>(components) * i + 0];
9203 images[1][i] = data[static_cast<size_t>(components) * i + 1];
9204 images[2][i] = data[static_cast<size_t>(components) * i + 2];
9205 if (components == 4) {
9206 images[3][i] = data[static_cast<size_t>(components) * i + 3];
9207 }
9208 }
9209 }
9210
9211 float *image_ptr[4] = {0, 0, 0, 0};
9212 if (components == 4) {
9213 image_ptr[0] = &(images[3].at(0)); // A
9214 image_ptr[1] = &(images[2].at(0)); // B
9215 image_ptr[2] = &(images[1].at(0)); // G
9216 image_ptr[3] = &(images[0].at(0)); // R
9217 } else if (components == 3) {
9218 image_ptr[0] = &(images[2].at(0)); // B
9219 image_ptr[1] = &(images[1].at(0)); // G
9220 image_ptr[2] = &(images[0].at(0)); // R
9221 } else if (components == 1) {
9222 image_ptr[0] = &(images[0].at(0)); // A
9223 }
9224
9225 image.images = reinterpret_cast<unsigned char **>(image_ptr);
9226 image.width = width;
9227 image.height = height;
9228
9229 header.num_channels = components;
9230 header.channels = static_cast<EXRChannelInfo *>(malloc(
9231 sizeof(EXRChannelInfo) * static_cast<size_t>(header.num_channels)));
9232 // Must be (A)BGR order, since most of EXR viewers expect this channel order.
9233 if (components == 4) {
9234#ifdef _MSC_VER
9235 strncpy_s(header.channels[0].name, "A", 255);
9236 strncpy_s(header.channels[1].name, "B", 255);
9237 strncpy_s(header.channels[2].name, "G", 255);
9238 strncpy_s(header.channels[3].name, "R", 255);
9239#else
9240 strncpy(header.channels[0].name, "A", 255);
9241 strncpy(header.channels[1].name, "B", 255);
9242 strncpy(header.channels[2].name, "G", 255);
9243 strncpy(header.channels[3].name, "R", 255);
9244#endif
9245 header.channels[0].name[strlen("A")] = '\0';
9246 header.channels[1].name[strlen("B")] = '\0';
9247 header.channels[2].name[strlen("G")] = '\0';
9248 header.channels[3].name[strlen("R")] = '\0';
9249 } else if (components == 3) {
9250#ifdef _MSC_VER
9251 strncpy_s(header.channels[0].name, "B", 255);
9252 strncpy_s(header.channels[1].name, "G", 255);
9253 strncpy_s(header.channels[2].name, "R", 255);
9254#else
9255 strncpy(header.channels[0].name, "B", 255);
9256 strncpy(header.channels[1].name, "G", 255);
9257 strncpy(header.channels[2].name, "R", 255);
9258#endif
9259 header.channels[0].name[strlen("B")] = '\0';
9260 header.channels[1].name[strlen("G")] = '\0';
9261 header.channels[2].name[strlen("R")] = '\0';
9262 } else {
9263#ifdef _MSC_VER
9264 strncpy_s(header.channels[0].name, "A", 255);
9265#else
9266 strncpy(header.channels[0].name, "A", 255);
9267#endif
9268 header.channels[0].name[strlen("A")] = '\0';
9269 }
9270
9271 header.pixel_types = static_cast<int *>(
9272 malloc(sizeof(int) * static_cast<size_t>(header.num_channels)));
9273 header.requested_pixel_types = static_cast<int *>(
9274 malloc(sizeof(int) * static_cast<size_t>(header.num_channels)));
9275 for (int i = 0; i < header.num_channels; i++) {
9276 header.pixel_types[i] =
9277 TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
9278
9279 if (save_as_fp16 > 0) {
9280 header.requested_pixel_types[i] =
9281 TINYEXR_PIXELTYPE_HALF; // save with half(fp16) pixel format
9282 } else {
9283 header.requested_pixel_types[i] =
9284 TINYEXR_PIXELTYPE_FLOAT; // save with float(fp32) pixel format(i.e.
9285 // no precision reduction)
9286 }
9287 }
9288
9289 int ret = SaveEXRImageToFile(&image, &header, outfilename, err);
9290
9291 free(header.channels);
9292 free(header.pixel_types);
9293 free(header.requested_pixel_types);
9294
9295 return ret;
9296}
9297
9298#ifdef __clang__
9299// zero-as-null-pointer-constant
9300#pragma clang diagnostic pop
9301#endif
9302
9303#endif // TINYEXR_IMPLEMENTATION_DEFINED
9304#endif // TINYEXR_IMPLEMENTATION
unsigned count
float y
float x
voidpf void uLong size
Definition ioapi.h:134
const char * filename
Definition ioapi.h:133
voidpf stream
Definition ioapi.h:134
voidpf uLong offset
Definition ioapi.h:140
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
voidpf void * buf
Definition ioapi.h:134
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
Definition miniz.c:350
mz_ulong mz_compressBound(mz_ulong source_len)
Definition miniz.c:355
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
Definition miniz.c:594
unsigned long mz_ulong
Definition miniz.h:235
#define uncompress
Definition miniz.h:507
mz_ulong uLong
Definition miniz.h:447
#define compressBound
Definition miniz.h:498
@ MZ_OK
Definition miniz.h:301
#define Z_OK
Definition miniz.h:463
Byte Bytef
Definition miniz.h:448
#define compress
Definition miniz.h:496
const char ** channel_names
Definition tinyexr.h:317
int width
Definition tinyexr.h:321
int ** offset_table
Definition tinyexr.h:319
int num_channels
Definition tinyexr.h:320
float *** image
Definition tinyexr.h:318
int height
Definition tinyexr.h:322
char type[256]
Definition tinyexr.h:203
char name[256]
Definition tinyexr.h:202
unsigned char * value
Definition tinyexr.h:204
int min_y
Definition tinyexr.h:232
int max_x
Definition tinyexr.h:233
int max_y
Definition tinyexr.h:234
int min_x
Definition tinyexr.h:231
unsigned char p_linear
Definition tinyexr.h:214
char name[256]
Definition tinyexr.h:210
unsigned char pad[3]
Definition tinyexr.h:215
EXRChannelInfo * channels
Definition tinyexr.h:267
int tile_level_mode
Definition tinyexr.h:251
int num_custom_attributes
Definition tinyexr.h:263
int tile_size_x
Definition tinyexr.h:249
float screen_window_center[2]
Definition tinyexr.h:242
EXRBox2i data_window
Definition tinyexr.h:240
float pixel_aspect_ratio
Definition tinyexr.h:238
int multipart
Definition tinyexr.h:258
int tile_rounding_mode
Definition tinyexr.h:252
int tile_size_y
Definition tinyexr.h:250
char name[256]
Definition tinyexr.h:283
unsigned int header_len
Definition tinyexr.h:259
int long_name
Definition tinyexr.h:254
int tiled
Definition tinyexr.h:248
int compression_type
Definition tinyexr.h:274
EXRBox2i display_window
Definition tinyexr.h:241
EXRAttribute * custom_attributes
Definition tinyexr.h:264
int num_channels
Definition tinyexr.h:272
float screen_window_width
Definition tinyexr.h:243
int non_image
Definition tinyexr.h:257
int * pixel_types
Definition tinyexr.h:269
int line_order
Definition tinyexr.h:239
int chunk_count
Definition tinyexr.h:245
int * requested_pixel_types
Definition tinyexr.h:275
int num_channels
Definition tinyexr.h:303
unsigned char ** images
Definition tinyexr.h:299
int width
Definition tinyexr.h:301
EXRTile * tiles
Definition tinyexr.h:293
int num_tiles
Definition tinyexr.h:306
int level_x
Definition tinyexr.h:296
struct TEXRImage * next_level
Definition tinyexr.h:295
int level_y
Definition tinyexr.h:297
int height
Definition tinyexr.h:302
EXRHeader * headers
Definition tinyexr.h:288
EXRImage * images
Definition tinyexr.h:312
int level_x
Definition tinyexr.h:221
int height
Definition tinyexr.h:225
int offset_y
Definition tinyexr.h:220
int offset_x
Definition tinyexr.h:219
int level_y
Definition tinyexr.h:222
unsigned char ** images
Definition tinyexr.h:227
int width
Definition tinyexr.h:224
int version
Definition tinyexr.h:190
int long_name
Definition tinyexr.h:194
int multipart
Definition tinyexr.h:198
int non_image
Definition tinyexr.h:197
int LoadEXRFromMemory(float **out_rgba, int *width, int *height, const unsigned char *memory, size_t size, const char **err)
int ParseEXRHeaderFromMemory(EXRHeader *header, const EXRVersion *version, const unsigned char *memory, size_t size, const char **err)
struct TEXRBox2i EXRBox2i
int FreeEXRHeader(EXRHeader *exr_header)
#define TINYEXR_ERROR_UNSUPPORTED_FEATURE
Definition tinyexr.h:155
#define TINYEXR_COMPRESSIONTYPE_ZFP
Definition tinyexr.h:176
int FreeEXRImage(EXRImage *exr_image)
int IsEXRFromMemory(const unsigned char *memory, size_t size)
int ParseEXRMultipartHeaderFromFile(EXRHeader ***headers, int *num_headers, const EXRVersion *version, const char *filename, const char **err)
#define TINYEXR_ERROR_INVALID_ARGUMENT
Definition tinyexr.h:148
struct TEXRVersion EXRVersion
#define TINYEXR_COMPRESSIONTYPE_PIZ
Definition tinyexr.h:175
int LoadDeepEXR(DeepImage *out_image, const char *filename, const char **err)
int SaveEXRToMemory(const float *data, const int width, const int height, const int components, const int save_as_fp16, unsigned char **buffer, const char **err)
#define TINYEXR_ERROR_DATA_TOO_LARGE
Definition tinyexr.h:159
#define TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION
Definition tinyexr.h:179
#define TINYEXR_COMPRESSIONTYPE_ZIPS
Definition tinyexr.h:173
#define TINYEXR_TILE_MIPMAP_LEVELS
Definition tinyexr.h:183
#define TINYEXR_TILE_RIPMAP_LEVELS
Definition tinyexr.h:184
#define TINYEXR_ERROR_INVALID_MAGIC_NUMBER
Definition tinyexr.h:146
#define TINYEXR_COMPRESSIONTYPE_RLE
Definition tinyexr.h:172
int ParseEXRVersionFromMemory(EXRVersion *version, const unsigned char *memory, size_t size)
#define TINYEXR_PIXELTYPE_UINT
Definition tinyexr.h:164
int SaveEXR(const float *data, const int width, const int height, const int components, const int save_as_fp16, const char *filename, const char **err)
int ParseEXRVersionFromFile(EXRVersion *version, const char *filename)
void FreeEXRErrorMessage(const char *msg)
#define TINYEXR_ZFP_COMPRESSIONTYPE_RATE
Definition tinyexr.h:178
int LoadEXRMultipartImageFromMemory(EXRImage *images, const EXRHeader **headers, unsigned int num_parts, const unsigned char *memory, const size_t size, const char **err)
int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, const char **err)
#define TINYEXR_ERROR_SERIALIZATION_FAILED
Definition tinyexr.h:157
#define TINYEXR_SUCCESS
Definition tinyexr.h:145
int LoadEXRWithLayer(float **out_rgba, int *width, int *height, const char *filename, const char *layer_name, const char **err)
struct TEXRImage EXRImage
int LoadEXRImageFromMemory(EXRImage *image, const EXRHeader *header, const unsigned char *memory, const size_t size, const char **err)
#define TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY
Definition tinyexr.h:180
#define TINYEXR_ERROR_INVALID_EXR_VERSION
Definition tinyexr.h:147
#define TINYEXR_COMPRESSIONTYPE_NONE
Definition tinyexr.h:171
#define TINYEXR_PIXELTYPE_HALF
Definition tinyexr.h:165
#define TINYEXR_MAX_CUSTOM_ATTRIBUTES
Definition tinyexr.h:169
int ParseEXRHeaderFromFile(EXRHeader *header, const EXRVersion *version, const char *filename, const char **err)
int EXRLayers(const char *filename, const char **layer_names[], int *num_layers, const char **err)
int IsEXR(const char *filename)
#define TINYEXR_ERROR_LAYER_NOT_FOUND
Definition tinyexr.h:158
size_t SaveEXRImageToMemory(const EXRImage *image, const EXRHeader *exr_header, unsigned char **memory, const char **err)
#define TINYEXR_TILE_ROUND_UP
Definition tinyexr.h:187
#define TINYEXR_COMPRESSIONTYPE_ZIP
Definition tinyexr.h:174
int ParseEXRMultipartHeaderFromMemory(EXRHeader ***headers, int *num_headers, const EXRVersion *version, const unsigned char *memory, size_t size, const char **err)
#define TINYEXR_ERROR_CANT_OPEN_FILE
Definition tinyexr.h:152
int EXRNumLevels(const EXRImage *exr_image)
struct TEXRTile EXRTile
struct TEXRAttribute EXRAttribute
#define TINYEXR_ERROR_INVALID_FILE
Definition tinyexr.h:150
#define TINYEXR_ERROR_INVALID_DATA
Definition tinyexr.h:149
struct TEXRHeader EXRHeader
struct TEXRMultiPartImage EXRMultiPartImage
int LoadEXRMultipartImageFromFile(EXRImage *images, const EXRHeader **headers, unsigned int num_parts, const char *filename, const char **err)
#define TINYEXR_MAX_HEADER_ATTRIBUTES
Definition tinyexr.h:168
void InitEXRHeader(EXRHeader *exr_header)
struct TEXRMultiPartHeader EXRMultiPartHeader
size_t SaveEXRMultipartImageToMemory(const EXRImage *images, const EXRHeader **exr_headers, unsigned int num_parts, unsigned char **memory, const char **err)
#define TINYEXR_PIXELTYPE_FLOAT
Definition tinyexr.h:166
struct TEXRChannelInfo EXRChannelInfo
#define TINYEXR_TILE_ROUND_DOWN
Definition tinyexr.h:186
#define TINYEXR_ERROR_UNSUPPORTED_FORMAT
Definition tinyexr.h:153
void InitEXRImage(EXRImage *exr_image)
#define TINYEXR_ERROR_INVALID_HEADER
Definition tinyexr.h:154
int SaveEXRImageToFile(const EXRImage *image, const EXRHeader *exr_header, const char *filename, const char **err)
#define TINYEXR_ERROR_CANT_WRITE_FILE
Definition tinyexr.h:156
#define TINYEXR_TILE_ONE_LEVEL
Definition tinyexr.h:182
struct TDeepImage DeepImage
int SaveEXRMultipartImageToFile(const EXRImage *images, const EXRHeader **exr_headers, unsigned int num_parts, const char *filename, const char **err)
int LoadEXRImageFromFile(EXRImage *image, const EXRHeader *header, const char *filename, const char **err)
void EXRSetNameAttr(EXRHeader *exr_header, const char *name)