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