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