LCOV - code coverage report
Current view: top level - SDL/EXRSnapshotSupport - tinyexr.h (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 165 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          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 &param) {
    3453             :   size_t uncompressed_size =
    3454             :       size_t(dst_width) * size_t(dst_num_lines) * num_channels;
    3455             : 
    3456             :   if (uncompressed_size == src_size) {
    3457             :     // Data is not compressed(Issue 40).
    3458             :     memcpy(dst, src, src_size);
    3459             :   }
    3460             : 
    3461             :   zfp_stream *zfp = NULL;
    3462             :   zfp_field *field = NULL;
    3463             : 
    3464             :   TINYEXR_CHECK_AND_RETURN_C((dst_width % 4) == 0, false);
    3465             :   TINYEXR_CHECK_AND_RETURN_C((dst_num_lines % 4) == 0, false);
    3466             : 
    3467             :   if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) {
    3468             :     return false;
    3469             :   }
    3470             : 
    3471             :   field =
    3472             :       zfp_field_2d(reinterpret_cast<void *>(const_cast<unsigned char *>(src)),
    3473             :                    zfp_type_float, static_cast<unsigned int>(dst_width),
    3474             :                    static_cast<unsigned int>(dst_num_lines) *
    3475             :                        static_cast<unsigned int>(num_channels));
    3476             :   zfp = zfp_stream_open(NULL);
    3477             : 
    3478             :   if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
    3479             :     zfp_stream_set_rate(zfp, param.rate, zfp_type_float, /* dimension */ 2,
    3480             :                         /* write random access */ 0);
    3481             :   } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
    3482             :     zfp_stream_set_precision(zfp, param.precision);
    3483             :   } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
    3484             :     zfp_stream_set_accuracy(zfp, param.tolerance);
    3485             :   } else {
    3486             :     return false;
    3487             :   }
    3488             : 
    3489             :   size_t buf_size = zfp_stream_maximum_size(zfp, field);
    3490             :   std::vector<unsigned char> buf(buf_size);
    3491             :   memcpy(&buf.at(0), src, src_size);
    3492             : 
    3493             :   bitstream *stream = stream_open(&buf.at(0), buf_size);
    3494             :   zfp_stream_set_bit_stream(zfp, stream);
    3495             :   zfp_stream_rewind(zfp);
    3496             : 
    3497             :   size_t image_size = size_t(dst_width) * size_t(dst_num_lines);
    3498             : 
    3499             :   for (size_t c = 0; c < size_t(num_channels); c++) {
    3500             :     // decompress 4x4 pixel block.
    3501             :     for (size_t y = 0; y < size_t(dst_num_lines); y += 4) {
    3502             :       for (size_t x = 0; x < size_t(dst_width); x += 4) {
    3503             :         float fblock[16];
    3504             :         zfp_decode_block_float_2(zfp, fblock);
    3505             :         for (size_t j = 0; j < 4; j++) {
    3506             :           for (size_t i = 0; i < 4; i++) {
    3507             :             dst[c * image_size + ((y + j) * size_t(dst_width) + (x + i))] =
    3508             :                 fblock[j * 4 + i];
    3509             :           }
    3510             :         }
    3511             :       }
    3512             :     }
    3513             :   }
    3514             : 
    3515             :   zfp_field_free(field);
    3516             :   zfp_stream_close(zfp);
    3517             :   stream_close(stream);
    3518             : 
    3519             :   return true;
    3520             : }
    3521             : 
    3522             : // Assume pixel format is FLOAT for all channels.
    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 &param) {
    3527             :   zfp_stream *zfp = NULL;
    3528             :   zfp_field *field = NULL;
    3529             : 
    3530             :   TINYEXR_CHECK_AND_RETURN_C((width % 4) == 0, false);
    3531             :   TINYEXR_CHECK_AND_RETURN_C((num_lines % 4) == 0, false);
    3532             : 
    3533             :   if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) {
    3534             :     return false;
    3535             :   }
    3536             : 
    3537             :   // create input array.
    3538             :   field = zfp_field_2d(reinterpret_cast<void *>(const_cast<float *>(inPtr)),
    3539             :                        zfp_type_float, static_cast<unsigned int>(width),
    3540             :                        static_cast<unsigned int>(num_lines * num_channels));
    3541             : 
    3542             :   zfp = zfp_stream_open(NULL);
    3543             : 
    3544             :   if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) {
    3545             :     zfp_stream_set_rate(zfp, param.rate, zfp_type_float, 2, 0);
    3546             :   } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) {
    3547             :     zfp_stream_set_precision(zfp, param.precision);
    3548             :   } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) {
    3549             :     zfp_stream_set_accuracy(zfp, param.tolerance);
    3550             :   } else {
    3551             :     return false;
    3552             :   }
    3553             : 
    3554             :   size_t buf_size = zfp_stream_maximum_size(zfp, field);
    3555             : 
    3556             :   outBuf->resize(buf_size);
    3557             : 
    3558             :   bitstream *stream = stream_open(&outBuf->at(0), buf_size);
    3559             :   zfp_stream_set_bit_stream(zfp, stream);
    3560             :   zfp_field_free(field);
    3561             : 
    3562             :   size_t image_size = size_t(width) * size_t(num_lines);
    3563             : 
    3564             :   for (size_t c = 0; c < size_t(num_channels); c++) {
    3565             :     // compress 4x4 pixel block.
    3566             :     for (size_t y = 0; y < size_t(num_lines); y += 4) {
    3567             :       for (size_t x = 0; x < size_t(width); x += 4) {
    3568             :         float fblock[16];
    3569             :         for (size_t j = 0; j < 4; j++) {
    3570             :           for (size_t i = 0; i < 4; i++) {
    3571             :             fblock[j * 4 + i] =
    3572             :                 inPtr[c * image_size + ((y + j) * size_t(width) + (x + i))];
    3573             :           }
    3574             :         }
    3575             :         zfp_encode_block_float_2(zfp, fblock);
    3576             :       }
    3577             :     }
    3578             :   }
    3579             : 
    3580             :   zfp_stream_flush(zfp);
    3581             :   (*outSize) = static_cast<unsigned int>(zfp_stream_compressed_size(zfp));
    3582             : 
    3583             :   zfp_stream_close(zfp);
    3584             : 
    3585             :   return true;
    3586             : }
    3587             : 
    3588             : #endif
    3589             : 
    3590             : //
    3591             : // -----------------------------------------------------------------
    3592             : //
    3593             : 
    3594             : // heuristics
    3595             : #define TINYEXR_DIMENSION_THRESHOLD (1024 * 8192)
    3596             : 
    3597             : // TODO(syoyo): Refactor function arguments.
    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(&center[0]);
    7785             :         swap4(&center[1]);
    7786             :         WriteAttributeToMemory(
    7787             :           &memory, "screenWindowCenter", "v2f",
    7788             :           reinterpret_cast<const unsigned char*>(center), 2 * sizeof(float));
    7789             :       }
    7790             : 
    7791             :       {
    7792             :         float w = 1.0f;
    7793             :         swap4(&w);
    7794             :         WriteAttributeToMemory(&memory, "screenWindowWidth", "float",
    7795             :                                reinterpret_cast<const unsigned char*>(&w),
    7796             :                                sizeof(float));
    7797             :       }
    7798             : 
    7799             :       if (exr_images[i].tiles) {
    7800             :         unsigned char tile_mode = static_cast<unsigned char>(exr_headers[i]->tile_level_mode & 0x3);
    7801             :         if (exr_headers[i]->tile_rounding_mode) tile_mode |= (1u << 4u);
    7802             :         //unsigned char data[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    7803             :         unsigned int datai[3] = { 0, 0, 0 };
    7804             :         unsigned char* data = reinterpret_cast<unsigned char*>(&datai[0]);
    7805             :         datai[0] = static_cast<unsigned int>(exr_headers[i]->tile_size_x);
    7806             :         datai[1] = static_cast<unsigned int>(exr_headers[i]->tile_size_y);
    7807             :         data[8] = tile_mode;
    7808             :         swap4(reinterpret_cast<unsigned int*>(&data[0]));
    7809             :         swap4(reinterpret_cast<unsigned int*>(&data[4]));
    7810             :         WriteAttributeToMemory(
    7811             :           &memory, "tiles", "tiledesc",
    7812             :           reinterpret_cast<const unsigned char*>(data), 9);
    7813             :       }
    7814             : 
    7815             :       // must be present for multi-part files - according to spec.
    7816             :       if (num_parts > 1) {
    7817             :         // name
    7818             :         {
    7819             :           size_t len = 0;
    7820             :           if ((len = strlen(exr_headers[i]->name)) > 0) {
    7821             : #if TINYEXR_HAS_CXX11
    7822             :             partnames.emplace(exr_headers[i]->name);
    7823             : #else
    7824             :             partnames.insert(std::string(exr_headers[i]->name));
    7825             : #endif
    7826             :             if (partnames.size() != i + 1) {
    7827             :               SetErrorMessage("'name' attributes must be unique for a multi-part file", err);
    7828             :               return 0;
    7829             :             }
    7830             :             WriteAttributeToMemory(
    7831             :               &memory, "name", "string",
    7832             :               reinterpret_cast<const unsigned char*>(exr_headers[i]->name),
    7833             :               static_cast<int>(len));
    7834             :           } else {
    7835             :             SetErrorMessage("Invalid 'name' attribute for a multi-part file", err);
    7836             :             return 0;
    7837             :           }
    7838             :         }
    7839             :         // type
    7840             :         {
    7841             :           const char* type = "scanlineimage";
    7842             :           if (exr_images[i].tiles) type = "tiledimage";
    7843             :           WriteAttributeToMemory(
    7844             :             &memory, "type", "string",
    7845             :             reinterpret_cast<const unsigned char*>(type),
    7846             :             static_cast<int>(strlen(type)));
    7847             :         }
    7848             :         // chunkCount
    7849             :         {
    7850             :           WriteAttributeToMemory(
    7851             :             &memory, "chunkCount", "int",
    7852             :             reinterpret_cast<const unsigned char*>(&chunk_count[i]),
    7853             :             4);
    7854             :         }
    7855             :       }
    7856             : 
    7857             :       // Custom attributes
    7858             :       if (exr_headers[i]->num_custom_attributes > 0) {
    7859             :         for (int j = 0; j < exr_headers[i]->num_custom_attributes; j++) {
    7860             :           tinyexr::WriteAttributeToMemory(
    7861             :             &memory, exr_headers[i]->custom_attributes[j].name,
    7862             :             exr_headers[i]->custom_attributes[j].type,
    7863             :             reinterpret_cast<const unsigned char*>(
    7864             :               exr_headers[i]->custom_attributes[j].value),
    7865             :             exr_headers[i]->custom_attributes[j].size);
    7866             :         }
    7867             :       }
    7868             : 
    7869             :       {  // end of header
    7870             :         memory.push_back(0);
    7871             :       }
    7872             :     }
    7873             :   }
    7874             :   if (num_parts > 1) {
    7875             :     // end of header list
    7876             :     memory.push_back(0);
    7877             :   }
    7878             : 
    7879             :   tinyexr_uint64 chunk_offset = memory.size() + size_t(total_chunk_count) * sizeof(tinyexr_uint64);
    7880             : 
    7881             :   tinyexr_uint64 total_size = 0;
    7882             :   std::vector< std::vector< std::vector<unsigned char> > > data_lists(num_parts);
    7883             :   for (unsigned int i = 0; i < num_parts; ++i) {
    7884             :     std::string e;
    7885             :     int ret = EncodeChunk(&exr_images[i], exr_headers[i],
    7886             :                           channels[i],
    7887             :                           chunk_count[i],
    7888             :                           // starting offset of current chunk after part-number
    7889             :                           chunk_offset,
    7890             :                           num_parts > 1,
    7891             :                           offset_data[i], // output: block offsets, must be initialized
    7892             :                           data_lists[i], // output
    7893             :                           total_size, // output
    7894             :                           &e);
    7895             :     if (ret != TINYEXR_SUCCESS) {
    7896             :       if (!e.empty()) {
    7897             :         tinyexr::SetErrorMessage(e, err);
    7898             :       }
    7899             :       return 0;
    7900             :     }
    7901             :     chunk_offset = total_size;
    7902             :   }
    7903             : 
    7904             :   // Allocating required memory
    7905             :   if (total_size == 0) { // something went wrong
    7906             :     tinyexr::SetErrorMessage("Output memory size is zero", err);
    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

Generated by: LCOV version 1.14