| // rg 8/23/2024: I fixed some minor undefined behavior in this module (signed 32-bit left shifts). |
| |
| #ifndef TINYEXR_H_ |
| #define TINYEXR_H_ |
| /* |
| Copyright (c) 2014 - 2021, Syoyo Fujita and many contributors. |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are met: |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| * Neither the name of the Syoyo Fujita nor the |
| names of its contributors may be used to endorse or promote products |
| derived from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY |
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| // TinyEXR contains some OpenEXR code, which is licensed under ------------ |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // |
| // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas |
| // Digital Ltd. LLC |
| // |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Industrial Light & Magic nor the names of |
| // its contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| // |
| /////////////////////////////////////////////////////////////////////////// |
| |
| // End of OpenEXR license ------------------------------------------------- |
| |
| |
| // |
| // |
| // Do this: |
| // #define TINYEXR_IMPLEMENTATION |
| // before you include this file in *one* C or C++ file to create the |
| // implementation. |
| // |
| // // i.e. it should look like this: |
| // #include ... |
| // #include ... |
| // #include ... |
| // #define TINYEXR_IMPLEMENTATION |
| // #include "tinyexr.h" |
| // |
| // |
| |
| #include <stddef.h> // for size_t |
| #include <stdint.h> // guess stdint.h is available(C99) |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ |
| defined(__i386) || defined(__i486__) || defined(__i486) || \ |
| defined(i386) || defined(__ia64__) || defined(__x86_64__) |
| #define TINYEXR_X86_OR_X64_CPU 1 |
| #else |
| #define TINYEXR_X86_OR_X64_CPU 0 |
| #endif |
| |
| #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || TINYEXR_X86_OR_X64_CPU |
| #define TINYEXR_LITTLE_ENDIAN 1 |
| #else |
| #define TINYEXR_LITTLE_ENDIAN 0 |
| #endif |
| |
| // Use miniz or not to decode ZIP format pixel. Linking with zlib |
| // required if this flag is 0 and TINYEXR_USE_STB_ZLIB is 0. |
| #ifndef TINYEXR_USE_MINIZ |
| #define TINYEXR_USE_MINIZ (1) |
| #ifndef MINIZ_HEADER_FILE_ONLY |
| #define MINIZ_HEADER_FILE_ONLY (1) |
| #endif |
| #endif |
| |
| // Use the ZIP implementation of stb_image.h and stb_image_write.h. |
| #ifndef TINYEXR_USE_STB_ZLIB |
| #define TINYEXR_USE_STB_ZLIB (0) |
| #endif |
| |
| // Use nanozlib. |
| #ifndef TINYEXR_USE_NANOZLIB |
| #define TINYEXR_USE_NANOZLIB (0) |
| #endif |
| |
| // Disable PIZ compression when applying cpplint. |
| #ifndef TINYEXR_USE_PIZ |
| #define TINYEXR_USE_PIZ (1) |
| #endif |
| |
| #ifndef TINYEXR_USE_ZFP |
| #define TINYEXR_USE_ZFP (0) // TinyEXR extension. |
| // http://computation.llnl.gov/projects/floating-point-compression |
| #endif |
| |
| #ifndef TINYEXR_USE_THREAD |
| #define TINYEXR_USE_THREAD (0) // No threaded loading. |
| // http://computation.llnl.gov/projects/floating-point-compression |
| #endif |
| |
| #ifndef TINYEXR_USE_OPENMP |
| #ifdef _OPENMP |
| #define TINYEXR_USE_OPENMP (1) |
| #else |
| #define TINYEXR_USE_OPENMP (0) |
| #endif |
| #endif |
| |
| #define TINYEXR_SUCCESS (0) |
| #define TINYEXR_ERROR_INVALID_MAGIC_NUMBER (-1) |
| #define TINYEXR_ERROR_INVALID_EXR_VERSION (-2) |
| #define TINYEXR_ERROR_INVALID_ARGUMENT (-3) |
| #define TINYEXR_ERROR_INVALID_DATA (-4) |
| #define TINYEXR_ERROR_INVALID_FILE (-5) |
| #define TINYEXR_ERROR_INVALID_PARAMETER (-6) |
| #define TINYEXR_ERROR_CANT_OPEN_FILE (-7) |
| #define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-8) |
| #define TINYEXR_ERROR_INVALID_HEADER (-9) |
| #define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-10) |
| #define TINYEXR_ERROR_CANT_WRITE_FILE (-11) |
| #define TINYEXR_ERROR_SERIALIZATION_FAILED (-12) |
| #define TINYEXR_ERROR_LAYER_NOT_FOUND (-13) |
| #define TINYEXR_ERROR_DATA_TOO_LARGE (-14) |
| |
| // @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf } |
| |
| // pixel type: possible values are: UINT = 0 HALF = 1 FLOAT = 2 |
| #define TINYEXR_PIXELTYPE_UINT (0) |
| #define TINYEXR_PIXELTYPE_HALF (1) |
| #define TINYEXR_PIXELTYPE_FLOAT (2) |
| |
| #define TINYEXR_MAX_HEADER_ATTRIBUTES (1024) |
| #define TINYEXR_MAX_CUSTOM_ATTRIBUTES (128) |
| |
| #define TINYEXR_COMPRESSIONTYPE_NONE (0) |
| #define TINYEXR_COMPRESSIONTYPE_RLE (1) |
| #define TINYEXR_COMPRESSIONTYPE_ZIPS (2) |
| #define TINYEXR_COMPRESSIONTYPE_ZIP (3) |
| #define TINYEXR_COMPRESSIONTYPE_PIZ (4) |
| #define TINYEXR_COMPRESSIONTYPE_ZFP (128) // TinyEXR extension |
| |
| #define TINYEXR_ZFP_COMPRESSIONTYPE_RATE (0) |
| #define TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION (1) |
| #define TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY (2) |
| |
| #define TINYEXR_TILE_ONE_LEVEL (0) |
| #define TINYEXR_TILE_MIPMAP_LEVELS (1) |
| #define TINYEXR_TILE_RIPMAP_LEVELS (2) |
| |
| #define TINYEXR_TILE_ROUND_DOWN (0) |
| #define TINYEXR_TILE_ROUND_UP (1) |
| |
| typedef struct TEXRVersion { |
| int version; // this must be 2 |
| // tile format image; |
| // not zero for only a single-part "normal" tiled file (according to spec.) |
| int tiled; |
| int long_name; // long name attribute |
| // deep image(EXR 2.0); |
| // for a multi-part file, indicates that at least one part is of type deep* (according to spec.) |
| int non_image; |
| int multipart; // multi-part(EXR 2.0) |
| } EXRVersion; |
| |
| typedef struct TEXRAttribute { |
| char name[256]; // name and type are up to 255 chars long. |
| char type[256]; |
| unsigned char *value; // uint8_t* |
| int size; |
| int pad0; |
| } EXRAttribute; |
| |
| typedef struct TEXRChannelInfo { |
| char name[256]; // less than 255 bytes long |
| int pixel_type; |
| int x_sampling; |
| int y_sampling; |
| unsigned char p_linear; |
| unsigned char pad[3]; |
| } EXRChannelInfo; |
| |
| typedef struct TEXRTile { |
| int offset_x; |
| int offset_y; |
| int level_x; |
| int level_y; |
| |
| int width; // actual width in a tile. |
| int height; // actual height int a tile. |
| |
| unsigned char **images; // image[channels][pixels] |
| } EXRTile; |
| |
| typedef struct TEXRBox2i { |
| int min_x; |
| int min_y; |
| int max_x; |
| int max_y; |
| } EXRBox2i; |
| |
| typedef struct TEXRHeader { |
| float pixel_aspect_ratio; |
| int line_order; |
| EXRBox2i data_window; |
| EXRBox2i display_window; |
| float screen_window_center[2]; |
| float screen_window_width; |
| |
| int chunk_count; |
| |
| // Properties for tiled format(`tiledesc`). |
| int tiled; |
| int tile_size_x; |
| int tile_size_y; |
| int tile_level_mode; |
| int tile_rounding_mode; |
| |
| int long_name; |
| // for a single-part file, agree with the version field bit 11 |
| // for a multi-part file, it is consistent with the type of part |
| int non_image; |
| int multipart; |
| unsigned int header_len; |
| |
| // Custom attributes(exludes required attributes(e.g. `channels`, |
| // `compression`, etc) |
| int num_custom_attributes; |
| EXRAttribute *custom_attributes; // array of EXRAttribute. size = |
| // `num_custom_attributes`. |
| |
| EXRChannelInfo *channels; // [num_channels] |
| |
| int *pixel_types; // Loaded pixel type(TINYEXR_PIXELTYPE_*) of `images` for |
| // each channel. This is overwritten with `requested_pixel_types` when |
| // loading. |
| int num_channels; |
| |
| int compression_type; // compression type(TINYEXR_COMPRESSIONTYPE_*) |
| int *requested_pixel_types; // Filled initially by |
| // ParseEXRHeaderFrom(Meomory|File), then users |
| // can edit it(only valid for HALF pixel type |
| // channel) |
| // name attribute required for multipart files; |
| // must be unique and non empty (according to spec.); |
| // use EXRSetNameAttr for setting value; |
| // max 255 character allowed - excluding terminating zero |
| char name[256]; |
| } EXRHeader; |
| |
| typedef struct TEXRMultiPartHeader { |
| int num_headers; |
| EXRHeader *headers; |
| |
| } EXRMultiPartHeader; |
| |
| typedef struct TEXRImage { |
| EXRTile *tiles; // Tiled pixel data. The application must reconstruct image |
| // from tiles manually. NULL if scanline format. |
| struct TEXRImage* next_level; // NULL if scanline format or image is the last level. |
| int level_x; // x level index |
| int level_y; // y level index |
| |
| unsigned char **images; // image[channels][pixels]. NULL if tiled format. |
| |
| int width; |
| int height; |
| int num_channels; |
| |
| // Properties for tile format. |
| int num_tiles; |
| |
| } EXRImage; |
| |
| typedef struct TEXRMultiPartImage { |
| int num_images; |
| EXRImage *images; |
| |
| } EXRMultiPartImage; |
| |
| typedef struct TDeepImage { |
| const char **channel_names; |
| float ***image; // image[channels][scanlines][samples] |
| int **offset_table; // offset_table[scanline][offsets] |
| int num_channels; |
| int width; |
| int height; |
| int pad0; |
| } DeepImage; |
| |
| // @deprecated { For backward compatibility. Not recommended to use. } |
| // Loads single-frame OpenEXR image. Assume EXR image contains A(single channel |
| // alpha) or RGB(A) channels. |
| // Application must free image data as returned by `out_rgba` |
| // Result image format is: float x RGBA x width x hight |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| extern int LoadEXR(float **out_rgba, int *width, int *height, |
| const char *filename, const char **err); |
| |
| // Loads single-frame OpenEXR image by specifying layer name. Assume EXR image |
| // contains A(single channel alpha) or RGB(A) channels. Application must free |
| // image data as returned by `out_rgba` Result image format is: float x RGBA x |
| // width x hight Returns negative value and may set error string in `err` when |
| // there's an error When the specified layer name is not found in the EXR file, |
| // the function will return `TINYEXR_ERROR_LAYER_NOT_FOUND`. |
| extern int LoadEXRWithLayer(float **out_rgba, int *width, int *height, |
| const char *filename, const char *layer_name, |
| const char **err, int *num_chans = NULL); |
| |
| // |
| // Get layer infos from EXR file. |
| // |
| // @param[out] layer_names List of layer names. Application must free memory |
| // after using this. |
| // @param[out] num_layers The number of layers |
| // @param[out] err Error string(will be filled when the function returns error |
| // code). Free it using FreeEXRErrorMessage after using this value. |
| // |
| // @return TINYEXR_SUCCEES upon success. |
| // |
| extern int EXRLayers(const char *filename, const char **layer_names[], |
| int *num_layers, const char **err); |
| |
| // @deprecated |
| // Simple wrapper API for ParseEXRHeaderFromFile. |
| // checking given file is a EXR file(by just look up header) |
| // @return TINYEXR_SUCCEES for EXR image, TINYEXR_ERROR_INVALID_HEADER for |
| // others |
| extern int IsEXR(const char *filename); |
| |
| // Simple wrapper API for ParseEXRHeaderFromMemory. |
| // Check if given data is a EXR image(by just looking up a header section) |
| // @return TINYEXR_SUCCEES for EXR image, TINYEXR_ERROR_INVALID_HEADER for |
| // others |
| extern int IsEXRFromMemory(const unsigned char *memory, size_t size); |
| |
| // @deprecated |
| // Saves single-frame OpenEXR image to a buffer. Assume EXR image contains RGB(A) channels. |
| // components must be 1(Grayscale), 3(RGB) or 4(RGBA). |
| // Input image format is: `float x width x height`, or `float x RGB(A) x width x |
| // hight` |
| // Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero |
| // value. |
| // Save image as fp32(FLOAT) format when `save_as_fp16` is 0. |
| // Use ZIP compression by default. |
| // `buffer` is the pointer to write EXR data. |
| // Memory for `buffer` is allocated internally in SaveEXRToMemory. |
| // Returns the data size of EXR file when the value is positive(up to 2GB EXR data). |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| extern int SaveEXRToMemory(const float *data, const int width, const int height, |
| const int components, const int save_as_fp16, |
| const unsigned char **buffer, const char **err); |
| |
| // @deprecated { Not recommended, but handy to use. } |
| // Saves single-frame OpenEXR image to a buffer. Assume EXR image contains RGB(A) channels. |
| // components must be 1(Grayscale), 3(RGB) or 4(RGBA). |
| // Input image format is: `float x width x height`, or `float x RGB(A) x width x |
| // hight` |
| // Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero |
| // value. |
| // Save image as fp32(FLOAT) format when `save_as_fp16` is 0. |
| // Use ZIP compression by default. |
| // Returns TINYEXR_SUCCEES(0) when success. |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| extern int SaveEXR(const float *data, const int width, const int height, |
| const int components, const int save_as_fp16, |
| const char *filename, const char **err); |
| |
| // Returns the number of resolution levels of the image (including the base) |
| extern int EXRNumLevels(const EXRImage* exr_image); |
| |
| // Initialize EXRHeader struct |
| extern void InitEXRHeader(EXRHeader *exr_header); |
| |
| // Set name attribute of EXRHeader struct (it makes a copy) |
| extern void EXRSetNameAttr(EXRHeader *exr_header, const char* name); |
| |
| // Initialize EXRImage struct |
| extern void InitEXRImage(EXRImage *exr_image); |
| |
| // Frees internal data of EXRHeader struct |
| extern int FreeEXRHeader(EXRHeader *exr_header); |
| |
| // Frees internal data of EXRImage struct |
| extern int FreeEXRImage(EXRImage *exr_image); |
| |
| // Frees error message |
| extern void FreeEXRErrorMessage(const char *msg); |
| |
| // Parse EXR version header of a file. |
| extern int ParseEXRVersionFromFile(EXRVersion *version, const char *filename); |
| |
| // Parse EXR version header from memory-mapped EXR data. |
| extern int ParseEXRVersionFromMemory(EXRVersion *version, |
| const unsigned char *memory, size_t size); |
| |
| // Parse single-part OpenEXR header from a file and initialize `EXRHeader`. |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int ParseEXRHeaderFromFile(EXRHeader *header, const EXRVersion *version, |
| const char *filename, const char **err); |
| |
| // Parse single-part OpenEXR header from a memory and initialize `EXRHeader`. |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int ParseEXRHeaderFromMemory(EXRHeader *header, |
| const EXRVersion *version, |
| const unsigned char *memory, size_t size, |
| const char **err); |
| |
| // Parse multi-part OpenEXR headers from a file and initialize `EXRHeader*` |
| // array. |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int ParseEXRMultipartHeaderFromFile(EXRHeader ***headers, |
| int *num_headers, |
| const EXRVersion *version, |
| const char *filename, |
| const char **err); |
| |
| // Parse multi-part OpenEXR headers from a memory and initialize `EXRHeader*` |
| // array |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int ParseEXRMultipartHeaderFromMemory(EXRHeader ***headers, |
| int *num_headers, |
| const EXRVersion *version, |
| const unsigned char *memory, |
| size_t size, const char **err); |
| |
| // Loads single-part OpenEXR image from a file. |
| // Application must setup `ParseEXRHeaderFromFile` before calling this function. |
| // Application can free EXRImage using `FreeEXRImage` |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int LoadEXRImageFromFile(EXRImage *image, const EXRHeader *header, |
| const char *filename, const char **err); |
| |
| // Loads single-part OpenEXR image from a memory. |
| // Application must setup `EXRHeader` with |
| // `ParseEXRHeaderFromMemory` before calling this function. |
| // Application can free EXRImage using `FreeEXRImage` |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int LoadEXRImageFromMemory(EXRImage *image, const EXRHeader *header, |
| const unsigned char *memory, |
| const size_t size, const char **err); |
| |
| // Loads multi-part OpenEXR image from a file. |
| // Application must setup `ParseEXRMultipartHeaderFromFile` before calling this |
| // function. |
| // Application can free EXRImage using `FreeEXRImage` |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int LoadEXRMultipartImageFromFile(EXRImage *images, |
| const EXRHeader **headers, |
| unsigned int num_parts, |
| const char *filename, |
| const char **err); |
| |
| // Loads multi-part OpenEXR image from a memory. |
| // Application must setup `EXRHeader*` array with |
| // `ParseEXRMultipartHeaderFromMemory` before calling this function. |
| // Application can free EXRImage using `FreeEXRImage` |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int LoadEXRMultipartImageFromMemory(EXRImage *images, |
| const EXRHeader **headers, |
| unsigned int num_parts, |
| const unsigned char *memory, |
| const size_t size, const char **err); |
| |
| // Saves multi-channel, single-frame OpenEXR image to a file. |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int SaveEXRImageToFile(const EXRImage *image, |
| const EXRHeader *exr_header, const char *filename, |
| const char **err); |
| |
| // Saves multi-channel, single-frame OpenEXR image to a memory. |
| // Image is compressed using EXRImage.compression value. |
| // Return the number of bytes if success. |
| // Return zero and will set error string in `err` when there's an |
| // error. |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern size_t SaveEXRImageToMemory(const EXRImage *image, |
| const EXRHeader *exr_header, |
| unsigned char **memory, const char **err); |
| |
| // Saves multi-channel, multi-frame OpenEXR image to a memory. |
| // Image is compressed using EXRImage.compression value. |
| // File global attributes (eg. display_window) must be set in the first header. |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int SaveEXRMultipartImageToFile(const EXRImage *images, |
| const EXRHeader **exr_headers, |
| unsigned int num_parts, |
| const char *filename, const char **err); |
| |
| // Saves multi-channel, multi-frame OpenEXR image to a memory. |
| // Image is compressed using EXRImage.compression value. |
| // File global attributes (eg. display_window) must be set in the first header. |
| // Return the number of bytes if success. |
| // Return zero and will set error string in `err` when there's an |
| // error. |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern size_t SaveEXRMultipartImageToMemory(const EXRImage *images, |
| const EXRHeader **exr_headers, |
| unsigned int num_parts, |
| unsigned char **memory, const char **err); |
| // Loads single-frame OpenEXR deep image. |
| // Application must free memory of variables in DeepImage(image, offset_table) |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int LoadDeepEXR(DeepImage *out_image, const char *filename, |
| const char **err); |
| |
| // NOT YET IMPLEMENTED: |
| // Saves single-frame OpenEXR deep image. |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // extern int SaveDeepEXR(const DeepImage *in_image, const char *filename, |
| // const char **err); |
| |
| // NOT YET IMPLEMENTED: |
| // Loads multi-part OpenEXR deep image. |
| // Application must free memory of variables in DeepImage(image, offset_table) |
| // extern int LoadMultiPartDeepEXR(DeepImage **out_image, int num_parts, const |
| // char *filename, |
| // const char **err); |
| |
| // For emscripten. |
| // Loads single-frame OpenEXR image from memory. Assume EXR image contains |
| // RGB(A) channels. |
| // Returns negative value and may set error string in `err` when there's an |
| // error |
| // When there was an error message, Application must free `err` with |
| // FreeEXRErrorMessage() |
| extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, |
| const unsigned char *memory, size_t size, |
| const char **err); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif // TINYEXR_H_ |
| |
| #ifdef TINYEXR_IMPLEMENTATION |
| #ifndef TINYEXR_IMPLEMENTATION_DEFINED |
| #define TINYEXR_IMPLEMENTATION_DEFINED |
| |
| #ifdef _WIN32 |
| |
| #ifndef WIN32_LEAN_AND_MEAN |
| #define WIN32_LEAN_AND_MEAN |
| #endif |
| #ifndef NOMINMAX |
| #define NOMINMAX |
| #endif |
| #include <windows.h> // for UTF-8 and memory-mapping |
| |
| #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) |
| #define TINYEXR_USE_WIN32_MMAP (1) |
| #endif |
| |
| #elif defined(__linux__) || defined(__unix__) |
| #include <fcntl.h> // for open() |
| #include <sys/mman.h> // for memory-mapping |
| #include <sys/stat.h> // for stat |
| #include <unistd.h> // for close() |
| #define TINYEXR_USE_POSIX_MMAP (1) |
| #endif |
| |
| #include <algorithm> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <sstream> |
| |
| //#include <iostream> // debug |
| |
| #include <limits> |
| #include <string> |
| #include <vector> |
| #include <set> |
| |
| // https://stackoverflow.com/questions/5047971/how-do-i-check-for-c11-support |
| #if __cplusplus > 199711L || (defined(_MSC_VER) && _MSC_VER >= 1900) |
| #define TINYEXR_HAS_CXX11 (1) |
| // C++11 |
| #include <cstdint> |
| |
| #if TINYEXR_USE_THREAD |
| #include <atomic> |
| #include <thread> |
| #endif |
| |
| #else // __cplusplus > 199711L |
| #define TINYEXR_HAS_CXX11 (0) |
| #endif // __cplusplus > 199711L |
| |
| #if TINYEXR_USE_OPENMP |
| #include <omp.h> |
| #endif |
| |
| #if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1) |
| #include "../basisu_miniz.h" |
| #else |
| // Issue #46. Please include your own zlib-compatible API header before |
| // including `tinyexr.h` |
| //#include "zlib.h" |
| #endif |
| |
| #if defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1) |
| #define NANOZLIB_IMPLEMENTATION |
| #include "nanozlib.h" |
| #endif |
| |
| #if TINYEXR_USE_STB_ZLIB |
| // Since we don't know where a project has stb_image.h and stb_image_write.h |
| // and whether they are in the include path, we don't include them here, and |
| // instead declare the two relevant functions manually. |
| // from stb_image.h: |
| extern "C" int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); |
| // from stb_image_write.h: |
| extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality); |
| #endif |
| |
| |
| #if TINYEXR_USE_ZFP |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Weverything" |
| #endif |
| |
| #include "zfp.h" |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| #endif |
| |
| // cond: conditional expression |
| // msg: std::string |
| // err: std::string* |
| #define TINYEXR_CHECK_AND_RETURN_MSG(cond, msg, err) do { \ |
| if (!(cond)) { \ |
| if (!err) { \ |
| std::ostringstream ss_e; \ |
| ss_e << __func__ << "():" << __LINE__ << msg << "\n"; \ |
| (*err) += ss_e.str(); \ |
| } \ |
| return false;\ |
| } \ |
| } while(0) |
| |
| // no error message. |
| #define TINYEXR_CHECK_AND_RETURN_C(cond, retcode) do { \ |
| if (!(cond)) { \ |
| return retcode; \ |
| } \ |
| } while(0) |
| |
| namespace tinyexr { |
| |
| #if __cplusplus > 199711L |
| // C++11 |
| typedef uint64_t tinyexr_uint64; |
| typedef int64_t tinyexr_int64; |
| #else |
| // Although `long long` is not a standard type pre C++11, assume it is defined |
| // as a compiler's extension. |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wc++11-long-long" |
| #endif |
| typedef unsigned long long tinyexr_uint64; |
| typedef long long tinyexr_int64; |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| #endif |
| |
| // static bool IsBigEndian(void) { |
| // union { |
| // unsigned int i; |
| // char c[4]; |
| // } bint = {0x01020304}; |
| // |
| // return bint.c[0] == 1; |
| //} |
| |
| static void SetErrorMessage(const std::string &msg, const char **err) { |
| if (err) { |
| #ifdef _WIN32 |
| (*err) = _strdup(msg.c_str()); |
| #else |
| (*err) = strdup(msg.c_str()); |
| #endif |
| } |
| } |
| |
| #if 0 |
| static void SetWarningMessage(const std::string &msg, const char **warn) { |
| if (warn) { |
| #ifdef _WIN32 |
| (*warn) = _strdup(msg.c_str()); |
| #else |
| (*warn) = strdup(msg.c_str()); |
| #endif |
| } |
| } |
| #endif |
| |
| static const int kEXRVersionSize = 8; |
| |
| static void cpy2(unsigned short *dst_val, const unsigned short *src_val) { |
| unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); |
| const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); |
| |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| } |
| |
| static void swap2(unsigned short *val) { |
| #if TINYEXR_LITTLE_ENDIAN |
| (void)val; |
| #else |
| unsigned short tmp = *val; |
| unsigned char *dst = reinterpret_cast<unsigned char *>(val); |
| unsigned char *src = reinterpret_cast<unsigned char *>(&tmp); |
| |
| dst[0] = src[1]; |
| dst[1] = src[0]; |
| #endif |
| } |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wunused-function" |
| #endif |
| |
| #ifdef __GNUC__ |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wunused-function" |
| #endif |
| static void cpy4(int *dst_val, const int *src_val) { |
| unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); |
| const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); |
| |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = src[2]; |
| dst[3] = src[3]; |
| } |
| |
| static void cpy4(unsigned int *dst_val, const unsigned int *src_val) { |
| unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); |
| const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); |
| |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = src[2]; |
| dst[3] = src[3]; |
| } |
| |
| static void cpy4(float *dst_val, const float *src_val) { |
| unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); |
| const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); |
| |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = src[2]; |
| dst[3] = src[3]; |
| } |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| #ifdef __GNUC__ |
| #pragma GCC diagnostic pop |
| #endif |
| |
| static void swap4(unsigned int *val) { |
| #if TINYEXR_LITTLE_ENDIAN |
| (void)val; |
| #else |
| unsigned int tmp = *val; |
| unsigned char *dst = reinterpret_cast<unsigned char *>(val); |
| unsigned char *src = reinterpret_cast<unsigned char *>(&tmp); |
| |
| dst[0] = src[3]; |
| dst[1] = src[2]; |
| dst[2] = src[1]; |
| dst[3] = src[0]; |
| #endif |
| } |
| |
| static void swap4(int *val) { |
| #if TINYEXR_LITTLE_ENDIAN |
| (void)val; |
| #else |
| int tmp = *val; |
| unsigned char *dst = reinterpret_cast<unsigned char *>(val); |
| unsigned char *src = reinterpret_cast<unsigned char *>(&tmp); |
| |
| dst[0] = src[3]; |
| dst[1] = src[2]; |
| dst[2] = src[1]; |
| dst[3] = src[0]; |
| #endif |
| } |
| |
| static void swap4(float *val) { |
| #if TINYEXR_LITTLE_ENDIAN |
| (void)val; |
| #else |
| float tmp = *val; |
| unsigned char *dst = reinterpret_cast<unsigned char *>(val); |
| unsigned char *src = reinterpret_cast<unsigned char *>(&tmp); |
| |
| dst[0] = src[3]; |
| dst[1] = src[2]; |
| dst[2] = src[1]; |
| dst[3] = src[0]; |
| #endif |
| } |
| |
| #if 0 |
| static void cpy8(tinyexr::tinyexr_uint64 *dst_val, const tinyexr::tinyexr_uint64 *src_val) { |
| unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); |
| const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); |
| |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = src[2]; |
| dst[3] = src[3]; |
| dst[4] = src[4]; |
| dst[5] = src[5]; |
| dst[6] = src[6]; |
| dst[7] = src[7]; |
| } |
| #endif |
| |
| static void swap8(tinyexr::tinyexr_uint64 *val) { |
| #if TINYEXR_LITTLE_ENDIAN |
| (void)val; |
| #else |
| tinyexr::tinyexr_uint64 tmp = (*val); |
| unsigned char *dst = reinterpret_cast<unsigned char *>(val); |
| unsigned char *src = reinterpret_cast<unsigned char *>(&tmp); |
| |
| dst[0] = src[7]; |
| dst[1] = src[6]; |
| dst[2] = src[5]; |
| dst[3] = src[4]; |
| dst[4] = src[3]; |
| dst[5] = src[2]; |
| dst[6] = src[1]; |
| dst[7] = src[0]; |
| #endif |
| } |
| |
| // https://gist.github.com/rygorous/2156668 |
| union FP32 { |
| unsigned int u; |
| float f; |
| struct { |
| #if TINYEXR_LITTLE_ENDIAN |
| unsigned int Mantissa : 23; |
| unsigned int Exponent : 8; |
| unsigned int Sign : 1; |
| #else |
| unsigned int Sign : 1; |
| unsigned int Exponent : 8; |
| unsigned int Mantissa : 23; |
| #endif |
| } s; |
| }; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wpadded" |
| #endif |
| |
| union FP16 { |
| unsigned short u; |
| struct { |
| #if TINYEXR_LITTLE_ENDIAN |
| unsigned int Mantissa : 10; |
| unsigned int Exponent : 5; |
| unsigned int Sign : 1; |
| #else |
| unsigned int Sign : 1; |
| unsigned int Exponent : 5; |
| unsigned int Mantissa : 10; |
| #endif |
| } s; |
| }; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| static FP32 half_to_float(FP16 h) { |
| static const FP32 magic = {113 << 23}; |
| static const unsigned int shifted_exp = 0x7c00 |
| << 13; // exponent mask after shift |
| FP32 o; |
| |
| o.u = (h.u & 0x7fffU) << 13U; // exponent/mantissa bits |
| unsigned int exp_ = shifted_exp & o.u; // just the exponent |
| o.u += (127 - 15) << 23; // exponent adjust |
| |
| // handle exponent special cases |
| if (exp_ == shifted_exp) // Inf/NaN? |
| o.u += (128 - 16) << 23; // extra exp adjust |
| else if (exp_ == 0) // Zero/Denormal? |
| { |
| o.u += 1 << 23; // extra exp adjust |
| o.f -= magic.f; // renormalize |
| } |
| |
| o.u |= (h.u & 0x8000U) << 16U; // sign bit |
| return o; |
| } |
| |
| static FP16 float_to_half_full(FP32 f) { |
| FP16 o = {0}; |
| |
| // Based on ISPC reference code (with minor modifications) |
| if (f.s.Exponent == 0) // Signed zero/denormal (which will underflow) |
| o.s.Exponent = 0; |
| else if (f.s.Exponent == 255) // Inf or NaN (all exponent bits set) |
| { |
| o.s.Exponent = 31; |
| o.s.Mantissa = f.s.Mantissa ? 0x200 : 0; // NaN->qNaN and Inf->Inf |
| } else // Normalized number |
| { |
| // Exponent unbias the single, then bias the halfp |
| int newexp = f.s.Exponent - 127 + 15; |
| if (newexp >= 31) // Overflow, return signed infinity |
| o.s.Exponent = 31; |
| else if (newexp <= 0) // Underflow |
| { |
| if ((14 - newexp) <= 24) // Mantissa might be non-zero |
| { |
| unsigned int mant = f.s.Mantissa | 0x800000; // Hidden 1 bit |
| o.s.Mantissa = mant >> (14 - newexp); |
| if ((mant >> (13 - newexp)) & 1) // Check for rounding |
| o.u++; // Round, might overflow into exp bit, but this is OK |
| } |
| } else { |
| o.s.Exponent = static_cast<unsigned int>(newexp); |
| o.s.Mantissa = f.s.Mantissa >> 13; |
| if (f.s.Mantissa & 0x1000) // Check for rounding |
| o.u++; // Round, might overflow to inf, this is OK |
| } |
| } |
| |
| o.s.Sign = f.s.Sign; |
| return o; |
| } |
| |
| // NOTE: From OpenEXR code |
| // #define IMF_INCREASING_Y 0 |
| // #define IMF_DECREASING_Y 1 |
| // #define IMF_RAMDOM_Y 2 |
| // |
| // #define IMF_NO_COMPRESSION 0 |
| // #define IMF_RLE_COMPRESSION 1 |
| // #define IMF_ZIPS_COMPRESSION 2 |
| // #define IMF_ZIP_COMPRESSION 3 |
| // #define IMF_PIZ_COMPRESSION 4 |
| // #define IMF_PXR24_COMPRESSION 5 |
| // #define IMF_B44_COMPRESSION 6 |
| // #define IMF_B44A_COMPRESSION 7 |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| |
| #if __has_warning("-Wzero-as-null-pointer-constant") |
| #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" |
| #endif |
| |
| #endif |
| |
| static const char *ReadString(std::string *s, const char *ptr, size_t len) { |
| // Read untile NULL(\0). |
| const char *p = ptr; |
| const char *q = ptr; |
| while ((size_t(q - ptr) < len) && (*q) != 0) { |
| q++; |
| } |
| |
| if (size_t(q - ptr) >= len) { |
| (*s).clear(); |
| return NULL; |
| } |
| |
| (*s) = std::string(p, q); |
| |
| return q + 1; // skip '\0' |
| } |
| |
| static bool ReadAttribute(std::string *name, std::string *type, |
| std::vector<unsigned char> *data, size_t *marker_size, |
| const char *marker, size_t size) { |
| size_t name_len = strnlen(marker, size); |
| if (name_len == size) { |
| // String does not have a terminating character. |
| return false; |
| } |
| *name = std::string(marker, name_len); |
| |
| marker += name_len + 1; |
| size -= name_len + 1; |
| |
| size_t type_len = strnlen(marker, size); |
| if (type_len == size) { |
| return false; |
| } |
| *type = std::string(marker, type_len); |
| |
| marker += type_len + 1; |
| size -= type_len + 1; |
| |
| if (size < sizeof(uint32_t)) { |
| return false; |
| } |
| |
| uint32_t data_len; |
| memcpy(&data_len, marker, sizeof(uint32_t)); |
| tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len)); |
| |
| if (data_len == 0) { |
| if ((*type).compare("string") == 0) { |
| // Accept empty string attribute. |
| |
| marker += sizeof(uint32_t); |
| size -= sizeof(uint32_t); |
| |
| *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t); |
| |
| data->resize(1); |
| (*data)[0] = '\0'; |
| |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| marker += sizeof(uint32_t); |
| size -= sizeof(uint32_t); |
| |
| if (size < data_len) { |
| return false; |
| } |
| |
| data->resize(static_cast<size_t>(data_len)); |
| memcpy(&data->at(0), marker, static_cast<size_t>(data_len)); |
| |
| *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t) + data_len; |
| return true; |
| } |
| |
| static void WriteAttributeToMemory(std::vector<unsigned char> *out, |
| const char *name, const char *type, |
| const unsigned char *data, int len) { |
| out->insert(out->end(), name, name + strlen(name) + 1); |
| out->insert(out->end(), type, type + strlen(type) + 1); |
| |
| int outLen = len; |
| tinyexr::swap4(&outLen); |
| out->insert(out->end(), reinterpret_cast<unsigned char *>(&outLen), |
| reinterpret_cast<unsigned char *>(&outLen) + sizeof(int)); |
| out->insert(out->end(), data, data + len); |
| } |
| |
| typedef struct TChannelInfo { |
| std::string name; // less than 255 bytes long |
| int pixel_type; |
| int requested_pixel_type; |
| int x_sampling; |
| int y_sampling; |
| unsigned char p_linear; |
| unsigned char pad[3]; |
| } ChannelInfo; |
| |
| typedef struct { |
| int min_x; |
| int min_y; |
| int max_x; |
| int max_y; |
| } Box2iInfo; |
| |
| struct HeaderInfo { |
| std::vector<tinyexr::ChannelInfo> channels; |
| std::vector<EXRAttribute> attributes; |
| |
| Box2iInfo data_window; |
| int line_order; |
| Box2iInfo display_window; |
| float screen_window_center[2]; |
| float screen_window_width; |
| float pixel_aspect_ratio; |
| |
| int chunk_count; |
| |
| // Tiled format |
| int tiled; // Non-zero if the part is tiled. |
| int tile_size_x; |
| int tile_size_y; |
| int tile_level_mode; |
| int tile_rounding_mode; |
| |
| unsigned int header_len; |
| |
| int compression_type; |
| |
| // required for multi-part or non-image files |
| std::string name; |
| // required for multi-part or non-image files |
| std::string type; |
| |
| void clear() { |
| channels.clear(); |
| attributes.clear(); |
| |
| data_window.min_x = 0; |
| data_window.min_y = 0; |
| data_window.max_x = 0; |
| data_window.max_y = 0; |
| line_order = 0; |
| display_window.min_x = 0; |
| display_window.min_y = 0; |
| display_window.max_x = 0; |
| display_window.max_y = 0; |
| screen_window_center[0] = 0.0f; |
| screen_window_center[1] = 0.0f; |
| screen_window_width = 0.0f; |
| pixel_aspect_ratio = 0.0f; |
| |
| chunk_count = 0; |
| |
| // Tiled format |
| tiled = 0; |
| tile_size_x = 0; |
| tile_size_y = 0; |
| tile_level_mode = 0; |
| tile_rounding_mode = 0; |
| |
| header_len = 0; |
| compression_type = 0; |
| |
| name.clear(); |
| type.clear(); |
| } |
| }; |
| |
| static bool ReadChannelInfo(std::vector<ChannelInfo> &channels, |
| const std::vector<unsigned char> &data) { |
| const char *p = reinterpret_cast<const char *>(&data.at(0)); |
| |
| for (;;) { |
| if ((*p) == 0) { |
| break; |
| } |
| ChannelInfo info; |
| info.requested_pixel_type = 0; |
| |
| tinyexr_int64 data_len = static_cast<tinyexr_int64>(data.size()) - |
| (p - reinterpret_cast<const char *>(data.data())); |
| if (data_len < 0) { |
| return false; |
| } |
| |
| p = ReadString(&info.name, p, size_t(data_len)); |
| if ((p == NULL) && (info.name.empty())) { |
| // Buffer overrun. Issue #51. |
| return false; |
| } |
| |
| const unsigned char *data_end = |
| reinterpret_cast<const unsigned char *>(p) + 16; |
| if (data_end >= (data.data() + data.size())) { |
| return false; |
| } |
| |
| memcpy(&info.pixel_type, p, sizeof(int)); |
| p += 4; |
| info.p_linear = static_cast<unsigned char>(p[0]); // uchar |
| p += 1 + 3; // reserved: uchar[3] |
| memcpy(&info.x_sampling, p, sizeof(int)); // int |
| p += 4; |
| memcpy(&info.y_sampling, p, sizeof(int)); // int |
| p += 4; |
| |
| tinyexr::swap4(&info.pixel_type); |
| tinyexr::swap4(&info.x_sampling); |
| tinyexr::swap4(&info.y_sampling); |
| |
| channels.push_back(info); |
| } |
| |
| return true; |
| } |
| |
| static void WriteChannelInfo(std::vector<unsigned char> &data, |
| const std::vector<ChannelInfo> &channels) { |
| size_t sz = 0; |
| |
| // Calculate total size. |
| for (size_t c = 0; c < channels.size(); c++) { |
| sz += channels[c].name.length() + 1; // +1 for \0 |
| sz += 16; // 4 * int |
| } |
| data.resize(sz + 1); |
| |
| unsigned char *p = &data.at(0); |
| |
| for (size_t c = 0; c < channels.size(); c++) { |
| memcpy(p, channels[c].name.c_str(), channels[c].name.length()); |
| p += channels[c].name.length(); |
| (*p) = '\0'; |
| p++; |
| |
| int pixel_type = channels[c].requested_pixel_type; |
| int x_sampling = channels[c].x_sampling; |
| int y_sampling = channels[c].y_sampling; |
| tinyexr::swap4(&pixel_type); |
| tinyexr::swap4(&x_sampling); |
| tinyexr::swap4(&y_sampling); |
| |
| memcpy(p, &pixel_type, sizeof(int)); |
| p += sizeof(int); |
| |
| (*p) = channels[c].p_linear; |
| p += 4; |
| |
| memcpy(p, &x_sampling, sizeof(int)); |
| p += sizeof(int); |
| |
| memcpy(p, &y_sampling, sizeof(int)); |
| p += sizeof(int); |
| } |
| |
| (*p) = '\0'; |
| } |
| |
| static bool CompressZip(unsigned char *dst, |
| tinyexr::tinyexr_uint64 &compressedSize, |
| const unsigned char *src, unsigned long src_size) { |
| std::vector<unsigned char> tmpBuf(src_size); |
| |
| // |
| // Apply EXR-specific? postprocess. Grabbed from OpenEXR's |
| // ImfZipCompressor.cpp |
| // |
| |
| // |
| // Reorder the pixel data. |
| // |
| |
| const char *srcPtr = reinterpret_cast<const char *>(src); |
| |
| { |
| char *t1 = reinterpret_cast<char *>(&tmpBuf.at(0)); |
| char *t2 = reinterpret_cast<char *>(&tmpBuf.at(0)) + (src_size + 1) / 2; |
| const char *stop = srcPtr + src_size; |
| |
| for (;;) { |
| if (srcPtr < stop) |
| *(t1++) = *(srcPtr++); |
| else |
| break; |
| |
| if (srcPtr < stop) |
| *(t2++) = *(srcPtr++); |
| else |
| break; |
| } |
| } |
| |
| // |
| // Predictor. |
| // |
| |
| { |
| unsigned char *t = &tmpBuf.at(0) + 1; |
| unsigned char *stop = &tmpBuf.at(0) + src_size; |
| int p = t[-1]; |
| |
| while (t < stop) { |
| int d = int(t[0]) - p + (128 + 256); |
| p = t[0]; |
| t[0] = static_cast<unsigned char>(d); |
| ++t; |
| } |
| } |
| |
| #if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1) |
| // |
| // Compress the data using miniz |
| // |
| |
| buminiz::mz_ulong outSize = buminiz::mz_compressBound(src_size); |
| int ret = buminiz::mz_compress( |
| dst, &outSize, static_cast<const unsigned char *>(&tmpBuf.at(0)), |
| src_size); |
| if (ret != buminiz::MZ_OK) { |
| return false; |
| } |
| |
| compressedSize = outSize; |
| #elif defined(TINYEXR_USE_STB_ZLIB) && (TINYEXR_USE_STB_ZLIB==1) |
| int outSize; |
| unsigned char* ret = stbi_zlib_compress(const_cast<unsigned char*>(&tmpBuf.at(0)), src_size, &outSize, 8); |
| if (!ret) { |
| return false; |
| } |
| memcpy(dst, ret, outSize); |
| free(ret); |
| |
| compressedSize = outSize; |
| #elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1) |
| uint64_t dstSize = nanoz_compressBound(static_cast<uint64_t>(src_size)); |
| int outSize{0}; |
| unsigned char *ret = nanoz_compress(&tmpBuf.at(0), src_size, &outSize, /* quality */8); |
| if (!ret) { |
| return false; |
| } |
| |
| memcpy(dst, ret, outSize); |
| free(ret); |
| |
| compressedSize = outSize; |
| #else |
| uLong outSize = compressBound(static_cast<uLong>(src_size)); |
| int ret = compress(dst, &outSize, static_cast<const Bytef *>(&tmpBuf.at(0)), |
| src_size); |
| if (ret != Z_OK) { |
| return false; |
| } |
| |
| compressedSize = outSize; |
| #endif |
| |
| // Use uncompressed data when compressed data is larger than uncompressed. |
| // (Issue 40) |
| if (compressedSize >= src_size) { |
| compressedSize = src_size; |
| memcpy(dst, src, src_size); |
| } |
| |
| return true; |
| } |
| |
| static bool DecompressZip(unsigned char *dst, |
| unsigned long *uncompressed_size /* inout */, |
| const unsigned char *src, unsigned long src_size) { |
| if ((*uncompressed_size) == src_size) { |
| // Data is not compressed(Issue 40). |
| memcpy(dst, src, src_size); |
| return true; |
| } |
| std::vector<unsigned char> tmpBuf(*uncompressed_size); |
| |
| #if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1) |
| int ret = |
| buminiz::mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); |
| if (buminiz::MZ_OK != ret) { |
| return false; |
| } |
| #elif TINYEXR_USE_STB_ZLIB |
| int ret = stbi_zlib_decode_buffer(reinterpret_cast<char*>(&tmpBuf.at(0)), |
| *uncompressed_size, reinterpret_cast<const char*>(src), src_size); |
| if (ret < 0) { |
| return false; |
| } |
| #elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1) |
| uint64_t dest_size = (*uncompressed_size); |
| uint64_t uncomp_size{0}; |
| nanoz_status_t ret = |
| nanoz_uncompress(src, src_size, dest_size, &tmpBuf.at(0), &uncomp_size); |
| if (NANOZ_SUCCESS != ret) { |
| return false; |
| } |
| if ((*uncompressed_size) != uncomp_size) { |
| return false; |
| } |
| #else |
| int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); |
| if (Z_OK != ret) { |
| return false; |
| } |
| #endif |
| |
| // |
| // Apply EXR-specific? postprocess. Grabbed from OpenEXR's |
| // ImfZipCompressor.cpp |
| // |
| |
| // Predictor. |
| { |
| unsigned char *t = &tmpBuf.at(0) + 1; |
| unsigned char *stop = &tmpBuf.at(0) + (*uncompressed_size); |
| |
| while (t < stop) { |
| int d = int(t[-1]) + int(t[0]) - 128; |
| t[0] = static_cast<unsigned char>(d); |
| ++t; |
| } |
| } |
| |
| // Reorder the pixel data. |
| { |
| const char *t1 = reinterpret_cast<const char *>(&tmpBuf.at(0)); |
| const char *t2 = reinterpret_cast<const char *>(&tmpBuf.at(0)) + |
| (*uncompressed_size + 1) / 2; |
| char *s = reinterpret_cast<char *>(dst); |
| char *stop = s + (*uncompressed_size); |
| |
| for (;;) { |
| if (s < stop) |
| *(s++) = *(t1++); |
| else |
| break; |
| |
| if (s < stop) |
| *(s++) = *(t2++); |
| else |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| // RLE code from OpenEXR -------------------------------------- |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wsign-conversion" |
| #if __has_warning("-Wextra-semi-stmt") |
| #pragma clang diagnostic ignored "-Wextra-semi-stmt" |
| #endif |
| #endif |
| |
| #ifdef _MSC_VER |
| #pragma warning(push) |
| #pragma warning(disable : 4204) // nonstandard extension used : non-constant |
| // aggregate initializer (also supported by GNU |
| // C and C99, so no big deal) |
| #pragma warning(disable : 4244) // 'initializing': conversion from '__int64' to |
| // 'int', possible loss of data |
| #pragma warning(disable : 4267) // 'argument': conversion from '__int64' to |
| // 'int', possible loss of data |
| #pragma warning(disable : 4996) // 'strdup': The POSIX name for this item is |
| // deprecated. Instead, use the ISO C and C++ |
| // conformant name: _strdup. |
| #endif |
| |
| const int MIN_RUN_LENGTH = 3; |
| const int MAX_RUN_LENGTH = 127; |
| |
| // |
| // Compress an array of bytes, using run-length encoding, |
| // and return the length of the compressed data. |
| // |
| |
| static int rleCompress(int inLength, const char in[], signed char out[]) { |
| const char *inEnd = in + inLength; |
| const char *runStart = in; |
| const char *runEnd = in + 1; |
| signed char *outWrite = out; |
| |
| while (runStart < inEnd) { |
| while (runEnd < inEnd && *runStart == *runEnd && |
| runEnd - runStart - 1 < MAX_RUN_LENGTH) { |
| ++runEnd; |
| } |
| |
| if (runEnd - runStart >= MIN_RUN_LENGTH) { |
| // |
| // Compressible run |
| // |
| |
| *outWrite++ = static_cast<char>(runEnd - runStart) - 1; |
| *outWrite++ = *(reinterpret_cast<const signed char *>(runStart)); |
| runStart = runEnd; |
| } else { |
| // |
| // Uncompressable run |
| // |
| |
| while (runEnd < inEnd && |
| ((runEnd + 1 >= inEnd || *runEnd != *(runEnd + 1)) || |
| (runEnd + 2 >= inEnd || *(runEnd + 1) != *(runEnd + 2))) && |
| runEnd - runStart < MAX_RUN_LENGTH) { |
| ++runEnd; |
| } |
| |
| *outWrite++ = static_cast<char>(runStart - runEnd); |
| |
| while (runStart < runEnd) { |
| *outWrite++ = *(reinterpret_cast<const signed char *>(runStart++)); |
| } |
| } |
| |
| ++runEnd; |
| } |
| |
| return static_cast<int>(outWrite - out); |
| } |
| |
| // |
| // Uncompress an array of bytes compressed with rleCompress(). |
| // Returns the length of the uncompressed data, or 0 if the |
| // length of the uncompressed data would be more than maxLength. |
| // |
| |
| static int rleUncompress(int inLength, int maxLength, const signed char in[], |
| char out[]) { |
| char *outStart = out; |
| |
| while (inLength > 0) { |
| if (*in < 0) { |
| int count = -(static_cast<int>(*in++)); |
| inLength -= count + 1; |
| |
| // Fixes #116: Add bounds check to in buffer. |
| if ((0 > (maxLength -= count)) || (inLength < 0)) return 0; |
| |
| memcpy(out, in, count); |
| out += count; |
| in += count; |
| } else { |
| int count = *in++; |
| inLength -= 2; |
| |
| if ((0 > (maxLength -= count + 1)) || (inLength < 0)) return 0; |
| |
| memset(out, *reinterpret_cast<const char *>(in), count + 1); |
| out += count + 1; |
| |
| in++; |
| } |
| } |
| |
| return static_cast<int>(out - outStart); |
| } |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| // End of RLE code from OpenEXR ----------------------------------- |
| |
| static bool CompressRle(unsigned char *dst, |
| tinyexr::tinyexr_uint64 &compressedSize, |
| const unsigned char *src, unsigned long src_size) { |
| std::vector<unsigned char> tmpBuf(src_size); |
| |
| // |
| // Apply EXR-specific? postprocess. Grabbed from OpenEXR's |
| // ImfRleCompressor.cpp |
| // |
| |
| // |
| // Reorder the pixel data. |
| // |
| |
| const char *srcPtr = reinterpret_cast<const char *>(src); |
| |
| { |
| char *t1 = reinterpret_cast<char *>(&tmpBuf.at(0)); |
| char *t2 = reinterpret_cast<char *>(&tmpBuf.at(0)) + (src_size + 1) / 2; |
| const char *stop = srcPtr + src_size; |
| |
| for (;;) { |
| if (srcPtr < stop) |
| *(t1++) = *(srcPtr++); |
| else |
| break; |
| |
| if (srcPtr < stop) |
| *(t2++) = *(srcPtr++); |
| else |
| break; |
| } |
| } |
| |
| // |
| // Predictor. |
| // |
| |
| { |
| unsigned char *t = &tmpBuf.at(0) + 1; |
| unsigned char *stop = &tmpBuf.at(0) + src_size; |
| int p = t[-1]; |
| |
| while (t < stop) { |
| int d = int(t[0]) - p + (128 + 256); |
| p = t[0]; |
| t[0] = static_cast<unsigned char>(d); |
| ++t; |
| } |
| } |
| |
| // outSize will be (srcSiz * 3) / 2 at max. |
| int outSize = rleCompress(static_cast<int>(src_size), |
| reinterpret_cast<const char *>(&tmpBuf.at(0)), |
| reinterpret_cast<signed char *>(dst)); |
| TINYEXR_CHECK_AND_RETURN_C(outSize > 0, false); |
| |
| compressedSize = static_cast<tinyexr::tinyexr_uint64>(outSize); |
| |
| // Use uncompressed data when compressed data is larger than uncompressed. |
| // (Issue 40) |
| if (compressedSize >= src_size) { |
| compressedSize = src_size; |
| memcpy(dst, src, src_size); |
| } |
| |
| return true; |
| } |
| |
| static bool DecompressRle(unsigned char *dst, |
| const unsigned long uncompressed_size, |
| const unsigned char *src, unsigned long src_size) { |
| if (uncompressed_size == src_size) { |
| // Data is not compressed(Issue 40). |
| memcpy(dst, src, src_size); |
| return true; |
| } |
| |
| // Workaround for issue #112. |
| // TODO(syoyo): Add more robust out-of-bounds check in `rleUncompress`. |
| if (src_size <= 2) { |
| return false; |
| } |
| |
| std::vector<unsigned char> tmpBuf(uncompressed_size); |
| |
| int ret = rleUncompress(static_cast<int>(src_size), |
| static_cast<int>(uncompressed_size), |
| reinterpret_cast<const signed char *>(src), |
| reinterpret_cast<char *>(&tmpBuf.at(0))); |
| if (ret != static_cast<int>(uncompressed_size)) { |
| return false; |
| } |
| |
| // |
| // Apply EXR-specific? postprocess. Grabbed from OpenEXR's |
| // ImfRleCompressor.cpp |
| // |
| |
| // Predictor. |
| { |
| unsigned char *t = &tmpBuf.at(0) + 1; |
| unsigned char *stop = &tmpBuf.at(0) + uncompressed_size; |
| |
| while (t < stop) { |
| int d = int(t[-1]) + int(t[0]) - 128; |
| t[0] = static_cast<unsigned char>(d); |
| ++t; |
| } |
| } |
| |
| // Reorder the pixel data. |
| { |
| const char *t1 = reinterpret_cast<const char *>(&tmpBuf.at(0)); |
| const char *t2 = reinterpret_cast<const char *>(&tmpBuf.at(0)) + |
| (uncompressed_size + 1) / 2; |
| char *s = reinterpret_cast<char *>(dst); |
| char *stop = s + uncompressed_size; |
| |
| for (;;) { |
| if (s < stop) |
| *(s++) = *(t1++); |
| else |
| break; |
| |
| if (s < stop) |
| *(s++) = *(t2++); |
| else |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| #if TINYEXR_USE_PIZ |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wc++11-long-long" |
| #pragma clang diagnostic ignored "-Wold-style-cast" |
| #pragma clang diagnostic ignored "-Wpadded" |
| #pragma clang diagnostic ignored "-Wsign-conversion" |
| #pragma clang diagnostic ignored "-Wc++11-extensions" |
| #pragma clang diagnostic ignored "-Wconversion" |
| #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" |
| |
| #if __has_warning("-Wcast-qual") |
| #pragma clang diagnostic ignored "-Wcast-qual" |
| #endif |
| |
| #if __has_warning("-Wextra-semi-stmt") |
| #pragma clang diagnostic ignored "-Wextra-semi-stmt" |
| #endif |
| |
| #endif |
| |
| // |
| // PIZ compress/uncompress, based on OpenEXR's ImfPizCompressor.cpp |
| // |
| // ----------------------------------------------------------------- |
| // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas |
| // Digital Ltd. LLC) |
| // (3 clause BSD license) |
| // |
| |
| struct PIZChannelData { |
| unsigned short *start; |
| unsigned short *end; |
| int nx; |
| int ny; |
| int ys; |
| int size; |
| }; |
| |
| //----------------------------------------------------------------------------- |
| // |
| // 16-bit Haar Wavelet encoding and decoding |
| // |
| // The source code in this file is derived from the encoding |
| // and decoding routines written by Christian Rouet for his |
| // PIZ image file format. |
| // |
| //----------------------------------------------------------------------------- |
| |
| // |
| // Wavelet basis functions without modulo arithmetic; they produce |
| // the best compression ratios when the wavelet-transformed data are |
| // Huffman-encoded, but the wavelet transform works only for 14-bit |
| // data (untransformed data values must be less than (1 << 14)). |
| // |
| |
| inline void wenc14(unsigned short a, unsigned short b, unsigned short &l, |
| unsigned short &h) { |
| short as = static_cast<short>(a); |
| short bs = static_cast<short>(b); |
| |
| short ms = (as + bs) >> 1; |
| short ds = as - bs; |
| |
| l = static_cast<unsigned short>(ms); |
| h = static_cast<unsigned short>(ds); |
| } |
| |
| inline void wdec14(unsigned short l, unsigned short h, unsigned short &a, |
| unsigned short &b) { |
| short ls = static_cast<short>(l); |
| short hs = static_cast<short>(h); |
| |
| int hi = hs; |
| int ai = ls + (hi & 1) + (hi >> 1); |
| |
| short as = static_cast<short>(ai); |
| short bs = static_cast<short>(ai - hi); |
| |
| a = static_cast<unsigned short>(as); |
| b = static_cast<unsigned short>(bs); |
| } |
| |
| // |
| // Wavelet basis functions with modulo arithmetic; they work with full |
| // 16-bit data, but Huffman-encoding the wavelet-transformed data doesn't |
| // compress the data quite as well. |
| // |
| |
| const int NBITS = 16; |
| const int A_OFFSET = 1 << (NBITS - 1); |
| const int M_OFFSET = 1 << (NBITS - 1); |
| const int MOD_MASK = (1 << NBITS) - 1; |
| |
| inline void wenc16(unsigned short a, unsigned short b, unsigned short &l, |
| unsigned short &h) { |
| int ao = (a + A_OFFSET) & MOD_MASK; |
| int m = ((ao + b) >> 1); |
| int d = ao - b; |
| |
| if (d < 0) m = (m + M_OFFSET) & MOD_MASK; |
| |
| d &= MOD_MASK; |
| |
| l = static_cast<unsigned short>(m); |
| h = static_cast<unsigned short>(d); |
| } |
| |
| inline void wdec16(unsigned short l, unsigned short h, unsigned short &a, |
| unsigned short &b) { |
| int m = l; |
| int d = h; |
| int bb = (m - (d >> 1)) & MOD_MASK; |
| int aa = (d + bb - A_OFFSET) & MOD_MASK; |
| b = static_cast<unsigned short>(bb); |
| a = static_cast<unsigned short>(aa); |
| } |
| |
| // |
| // 2D Wavelet encoding: |
| // |
| |
| static void wav2Encode( |
| unsigned short *in, // io: values are transformed in place |
| int nx, // i : x size |
| int ox, // i : x offset |
| int ny, // i : y size |
| int oy, // i : y offset |
| unsigned short mx) // i : maximum in[x][y] value |
| { |
| bool w14 = (mx < (1 << 14)); |
| int n = (nx > ny) ? ny : nx; |
| int p = 1; // == 1 << level |
| int p2 = 2; // == 1 << (level+1) |
| |
| // |
| // Hierarchical loop on smaller dimension n |
| // |
| |
| while (p2 <= n) { |
| unsigned short *py = in; |
| unsigned short *ey = in + oy * (ny - p2); |
| int oy1 = oy * p; |
| int oy2 = oy * p2; |
| int ox1 = ox * p; |
| int ox2 = ox * p2; |
| unsigned short i00, i01, i10, i11; |
| |
| // |
| // Y loop |
| // |
| |
| for (; py <= ey; py += oy2) { |
| unsigned short *px = py; |
| unsigned short *ex = py + ox * (nx - p2); |
| |
| // |
| // X loop |
| // |
| |
| for (; px <= ex; px += ox2) { |
| unsigned short *p01 = px + ox1; |
| unsigned short *p10 = px + oy1; |
| unsigned short *p11 = p10 + ox1; |
| |
| // |
| // 2D wavelet encoding |
| // |
| |
| if (w14) { |
| wenc14(*px, *p01, i00, i01); |
| wenc14(*p10, *p11, i10, i11); |
| wenc14(i00, i10, *px, *p10); |
| wenc14(i01, i11, *p01, *p11); |
| } else { |
| wenc16(*px, *p01, i00, i01); |
| wenc16(*p10, *p11, i10, i11); |
| wenc16(i00, i10, *px, *p10); |
| wenc16(i01, i11, *p01, *p11); |
| } |
| } |
| |
| // |
| // Encode (1D) odd column (still in Y loop) |
| // |
| |
| if (nx & p) { |
| unsigned short *p10 = px + oy1; |
| |
| if (w14) |
| wenc14(*px, *p10, i00, *p10); |
| else |
| wenc16(*px, *p10, i00, *p10); |
| |
| *px = i00; |
| } |
| } |
| |
| // |
| // Encode (1D) odd line (must loop in X) |
| // |
| |
| if (ny & p) { |
| unsigned short *px = py; |
| unsigned short *ex = py + ox * (nx - p2); |
| |
| for (; px <= ex; px += ox2) { |
| unsigned short *p01 = px + ox1; |
| |
| if (w14) |
| wenc14(*px, *p01, i00, *p01); |
| else |
| wenc16(*px, *p01, i00, *p01); |
| |
| *px = i00; |
| } |
| } |
| |
| // |
| // Next level |
| // |
| |
| p = p2; |
| p2 <<= 1; |
| } |
| } |
| |
| // |
| // 2D Wavelet decoding: |
| // |
| |
| static void wav2Decode( |
| unsigned short *in, // io: values are transformed in place |
| int nx, // i : x size |
| int ox, // i : x offset |
| int ny, // i : y size |
| int oy, // i : y offset |
| unsigned short mx) // i : maximum in[x][y] value |
| { |
| bool w14 = (mx < (1 << 14)); |
| int n = (nx > ny) ? ny : nx; |
| int p = 1; |
| int p2; |
| |
| // |
| // Search max level |
| // |
| |
| while (p <= n) p <<= 1; |
| |
| p >>= 1; |
| p2 = p; |
| p >>= 1; |
| |
| // |
| // Hierarchical loop on smaller dimension n |
| // |
| |
| while (p >= 1) { |
| unsigned short *py = in; |
| unsigned short *ey = in + oy * (ny - p2); |
| int oy1 = oy * p; |
| int oy2 = oy * p2; |
| int ox1 = ox * p; |
| int ox2 = ox * p2; |
| unsigned short i00, i01, i10, i11; |
| |
| // |
| // Y loop |
| // |
| |
| for (; py <= ey; py += oy2) { |
| unsigned short *px = py; |
| unsigned short *ex = py + ox * (nx - p2); |
| |
| // |
| // X loop |
| // |
| |
| for (; px <= ex; px += ox2) { |
| unsigned short *p01 = px + ox1; |
| unsigned short *p10 = px + oy1; |
| unsigned short *p11 = p10 + ox1; |
| |
| // |
| // 2D wavelet decoding |
| // |
| |
| if (w14) { |
| wdec14(*px, *p10, i00, i10); |
| wdec14(*p01, *p11, i01, i11); |
| wdec14(i00, i01, *px, *p01); |
| wdec14(i10, i11, *p10, *p11); |
| } else { |
| wdec16(*px, *p10, i00, i10); |
| wdec16(*p01, *p11, i01, i11); |
| wdec16(i00, i01, *px, *p01); |
| wdec16(i10, i11, *p10, *p11); |
| } |
| } |
| |
| // |
| // Decode (1D) odd column (still in Y loop) |
| // |
| |
| if (nx & p) { |
| unsigned short *p10 = px + oy1; |
| |
| if (w14) |
| wdec14(*px, *p10, i00, *p10); |
| else |
| wdec16(*px, *p10, i00, *p10); |
| |
| *px = i00; |
| } |
| } |
| |
| // |
| // Decode (1D) odd line (must loop in X) |
| // |
| |
| if (ny & p) { |
| unsigned short *px = py; |
| unsigned short *ex = py + ox * (nx - p2); |
| |
| for (; px <= ex; px += ox2) { |
| unsigned short *p01 = px + ox1; |
| |
| if (w14) |
| wdec14(*px, *p01, i00, *p01); |
| else |
| wdec16(*px, *p01, i00, *p01); |
| |
| *px = i00; |
| } |
| } |
| |
| // |
| // Next level |
| // |
| |
| p2 = p; |
| p >>= 1; |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // |
| // 16-bit Huffman compression and decompression. |
| // |
| // The source code in this file is derived from the 8-bit |
| // Huffman compression and decompression routines written |
| // by Christian Rouet for his PIZ image file format. |
| // |
| //----------------------------------------------------------------------------- |
| |
| // Adds some modification for tinyexr. |
| |
| const int HUF_ENCBITS = 16; // literal (value) bit length |
| const int HUF_DECBITS = 14; // decoding bit size (>= 8) |
| |
| const int HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; // encoding table size |
| const int HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size |
| const int HUF_DECMASK = HUF_DECSIZE - 1; |
| |
| struct HufDec { // short code long code |
| //------------------------------- |
| unsigned int len : 8; // code length 0 |
| unsigned int lit : 24; // lit p size |
| unsigned int *p; // 0 lits |
| }; |
| |
| inline long long hufLength(long long code) { return code & 63; } |
| |
| inline long long hufCode(long long code) { return code >> 6; } |
| |
| inline void outputBits(int nBits, long long bits, long long &c, int &lc, |
| char *&out) { |
| c <<= nBits; |
| lc += nBits; |
| |
| c |= bits; |
| |
| while (lc >= 8) *out++ = static_cast<char>((c >> (lc -= 8))); |
| } |
| |
| inline long long getBits(int nBits, long long &c, int &lc, const char *&in) { |
| while (lc < nBits) { |
| c = (long long)((unsigned long long)c << 8) | *(reinterpret_cast<const unsigned char *>(in++)); |
| lc += 8; |
| } |
| |
| lc -= nBits; |
| return (c >> lc) & ((1 << nBits) - 1); |
| } |
| |
| // |
| // ENCODING TABLE BUILDING & (UN)PACKING |
| // |
| |
| // |
| // Build a "canonical" Huffman code table: |
| // - for each (uncompressed) symbol, hcode contains the length |
| // of the corresponding code (in the compressed data) |
| // - canonical codes are computed and stored in hcode |
| // - the rules for constructing canonical codes are as follows: |
| // * shorter codes (if filled with zeroes to the right) |
| // have a numerically higher value than longer codes |
| // * for codes with the same length, numerical values |
| // increase with numerical symbol values |
| // - because the canonical code table can be constructed from |
| // symbol lengths alone, the code table can be transmitted |
| // without sending the actual code values |
| // - see http://www.compressconsult.com/huffman/ |
| // |
| |
| static void hufCanonicalCodeTable(long long hcode[HUF_ENCSIZE]) { |
| long long n[59]; |
| |
| // |
| // For each i from 0 through 58, count the |
| // number of different codes of length i, and |
| // store the count in n[i]. |
| // |
| |
| for (int i = 0; i <= 58; ++i) n[i] = 0; |
| |
| for (int i = 0; i < HUF_ENCSIZE; ++i) n[hcode[i]] += 1; |
| |
| // |
| // For each i from 58 through 1, compute the |
| // numerically lowest code with length i, and |
| // store that code in n[i]. |
| // |
| |
| long long c = 0; |
| |
| for (int i = 58; i > 0; --i) { |
| long long nc = ((c + n[i]) >> 1); |
| n[i] = c; |
| c = nc; |
| } |
| |
| // |
| // hcode[i] contains the length, l, of the |
| // code for symbol i. Assign the next available |
| // code of length l to the symbol and store both |
| // l and the code in hcode[i]. |
| // |
| |
| for (int i = 0; i < HUF_ENCSIZE; ++i) { |
| int l = static_cast<int>(hcode[i]); |
| |
| if (l > 0) hcode[i] = l | (n[l]++ << 6); |
| } |
| } |
| |
| // |
| // Compute Huffman codes (based on frq input) and store them in frq: |
| // - code structure is : [63:lsb - 6:msb] | [5-0: bit length]; |
| // - max code length is 58 bits; |
| // - codes outside the range [im-iM] have a null length (unused values); |
| // - original frequencies are destroyed; |
| // - encoding tables are used by hufEncode() and hufBuildDecTable(); |
| // |
| |
| struct FHeapCompare { |
| bool operator()(long long *a, long long *b) { return *a > *b; } |
| }; |
| |
| static bool hufBuildEncTable( |
| long long *frq, // io: input frequencies [HUF_ENCSIZE], output table |
| int *im, // o: min frq index |
| int *iM) // o: max frq index |
| { |
| // |
| // This function assumes that when it is called, array frq |
| // indicates the frequency of all possible symbols in the data |
| // that are to be Huffman-encoded. (frq[i] contains the number |
| // of occurrences of symbol i in the data.) |
| // |
| // The loop below does three things: |
| // |
| // 1) Finds the minimum and maximum indices that point |
| // to non-zero entries in frq: |
| // |
| // frq[im] != 0, and frq[i] == 0 for all i < im |
| // frq[iM] != 0, and frq[i] == 0 for all i > iM |
| // |
| // 2) Fills array fHeap with pointers to all non-zero |
| // entries in frq. |
| // |
| // 3) Initializes array hlink such that hlink[i] == i |
| // for all array entries. |
| // |
| |
| std::vector<int> hlink(HUF_ENCSIZE); |
| std::vector<long long *> fHeap(HUF_ENCSIZE); |
| |
| *im = 0; |
| |
| while (!frq[*im]) (*im)++; |
| |
| int nf = 0; |
| |
| for (int i = *im; i < HUF_ENCSIZE; i++) { |
| hlink[i] = i; |
| |
| if (frq[i]) { |
| fHeap[nf] = &frq[i]; |
| nf++; |
| *iM = i; |
| } |
| } |
| |
| // |
| // Add a pseudo-symbol, with a frequency count of 1, to frq; |
| // adjust the fHeap and hlink array accordingly. Function |
| // hufEncode() uses the pseudo-symbol for run-length encoding. |
| // |
| |
| (*iM)++; |
| frq[*iM] = 1; |
| fHeap[nf] = &frq[*iM]; |
| nf++; |
| |
| // |
| // Build an array, scode, such that scode[i] contains the number |
| // of bits assigned to symbol i. Conceptually this is done by |
| // constructing a tree whose leaves are the symbols with non-zero |
| // frequency: |
| // |
| // Make a heap that contains all symbols with a non-zero frequency, |
| // with the least frequent symbol on top. |
| // |
| // Repeat until only one symbol is left on the heap: |
| // |
| // Take the two least frequent symbols off the top of the heap. |
| // Create a new node that has first two nodes as children, and |
| // whose frequency is the sum of the frequencies of the first |
| // two nodes. Put the new node back into the heap. |
| // |
| // The last node left on the heap is the root of the tree. For each |
| // leaf node, the distance between the root and the leaf is the length |
| // of the code for the corresponding symbol. |
| // |
| // The loop below doesn't actually build the tree; instead we compute |
| // the distances of the leaves from the root on the fly. When a new |
| // node is added to the heap, then that node's descendants are linked |
| // into a single linear list that starts at the new node, and the code |
| // lengths of the descendants (that is, their distance from the root |
| // of the tree) are incremented by one. |
| // |
| |
| std::make_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); |
| |
| std::vector<long long> scode(HUF_ENCSIZE); |
| memset(scode.data(), 0, sizeof(long long) * HUF_ENCSIZE); |
| |
| while (nf > 1) { |
| // |
| // Find the indices, mm and m, of the two smallest non-zero frq |
| // values in fHeap, add the smallest frq to the second-smallest |
| // frq, and remove the smallest frq value from fHeap. |
| // |
| |
| int mm = fHeap[0] - frq; |
| std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); |
| --nf; |
| |
| int m = fHeap[0] - frq; |
| std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); |
| |
| frq[m] += frq[mm]; |
| std::push_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); |
| |
| // |
| // The entries in scode are linked into lists with the |
| // entries in hlink serving as "next" pointers and with |
| // the end of a list marked by hlink[j] == j. |
| // |
| // Traverse the lists that start at scode[m] and scode[mm]. |
| // For each element visited, increment the length of the |
| // corresponding code by one bit. (If we visit scode[j] |
| // during the traversal, then the code for symbol j becomes |
| // one bit longer.) |
| // |
| // Merge the lists that start at scode[m] and scode[mm] |
| // into a single list that starts at scode[m]. |
| // |
| |
| // |
| // Add a bit to all codes in the first list. |
| // |
| |
| for (int j = m;; j = hlink[j]) { |
| scode[j]++; |
| |
| TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false); |
| |
| if (hlink[j] == j) { |
| // |
| // Merge the two lists. |
| // |
| |
| hlink[j] = mm; |
| break; |
| } |
| } |
| |
| // |
| // Add a bit to all codes in the second list |
| // |
| |
| for (int j = mm;; j = hlink[j]) { |
| scode[j]++; |
| |
| TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false); |
| |
| if (hlink[j] == j) break; |
| } |
| } |
| |
| // |
| // Build a canonical Huffman code table, replacing the code |
| // lengths in scode with (code, code length) pairs. Copy the |
| // code table from scode into frq. |
| // |
| |
| hufCanonicalCodeTable(scode.data()); |
| memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE); |
| |
| return true; |
| } |
| |
| // |
| // Pack an encoding table: |
| // - only code lengths, not actual codes, are stored |
| // - runs of zeroes are compressed as follows: |
| // |
| // unpacked packed |
| // -------------------------------- |
| // 1 zero 0 (6 bits) |
| // 2 zeroes 59 |
| // 3 zeroes 60 |
| // 4 zeroes 61 |
| // 5 zeroes 62 |
| // n zeroes (6 or more) 63 n-6 (6 + 8 bits) |
| // |
| |
| const int SHORT_ZEROCODE_RUN = 59; |
| const int LONG_ZEROCODE_RUN = 63; |
| const int SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN; |
| const int LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN; |
| |
| static void hufPackEncTable( |
| const long long *hcode, // i : encoding table [HUF_ENCSIZE] |
| int im, // i : min hcode index |
| int iM, // i : max hcode index |
| char **pcode) // o: ptr to packed table (updated) |
| { |
| char *p = *pcode; |
| long long c = 0; |
| int lc = 0; |
| |
| for (; im <= iM; im++) { |
| int l = hufLength(hcode[im]); |
| |
| if (l == 0) { |
| int zerun = 1; |
| |
| while ((im < iM) && (zerun < LONGEST_LONG_RUN)) { |
| if (hufLength(hcode[im + 1]) > 0) break; |
| im++; |
| zerun++; |
| } |
| |
| if (zerun >= 2) { |
| if (zerun >= SHORTEST_LONG_RUN) { |
| outputBits(6, LONG_ZEROCODE_RUN, c, lc, p); |
| outputBits(8, zerun - SHORTEST_LONG_RUN, c, lc, p); |
| } else { |
| outputBits(6, SHORT_ZEROCODE_RUN + zerun - 2, c, lc, p); |
| } |
| continue; |
| } |
| } |
| |
| outputBits(6, l, c, lc, p); |
| } |
| |
| if (lc > 0) *p++ = (unsigned char)(c << (8 - lc)); |
| |
| *pcode = p; |
| } |
| |
| // |
| // Unpack an encoding table packed by hufPackEncTable(): |
| // |
| |
| static bool hufUnpackEncTable( |
| const char **pcode, // io: ptr to packed table (updated) |
| int ni, // i : input size (in bytes) |
| int im, // i : min hcode index |
| int iM, // i : max hcode index |
| long long *hcode) // o: encoding table [HUF_ENCSIZE] |
| { |
| memset(hcode, 0, sizeof(long long) * HUF_ENCSIZE); |
| |
| const char *p = *pcode; |
| long long c = 0; |
| int lc = 0; |
| |
| for (; im <= iM; im++) { |
| if (p - *pcode >= ni) { |
| return false; |
| } |
| |
| long long l = hcode[im] = getBits(6, c, lc, p); // code length |
| |
| if (l == (long long)LONG_ZEROCODE_RUN) { |
| if (p - *pcode > ni) { |
| return false; |
| } |
| |
| int zerun = getBits(8, c, lc, p) + SHORTEST_LONG_RUN; |
| |
| if (im + zerun > iM + 1) { |
| return false; |
| } |
| |
| while (zerun--) hcode[im++] = 0; |
| |
| im--; |
| } else if (l >= (long long)SHORT_ZEROCODE_RUN) { |
| int zerun = l - SHORT_ZEROCODE_RUN + 2; |
| |
| if (im + zerun > iM + 1) { |
| return false; |
| } |
| |
| while (zerun--) hcode[im++] = 0; |
| |
| im--; |
| } |
| } |
| |
| *pcode = const_cast<char *>(p); |
| |
| hufCanonicalCodeTable(hcode); |
| |
| return true; |
| } |
| |
| // |
| // DECODING TABLE BUILDING |
| // |
| |
| // |
| // Clear a newly allocated decoding table so that it contains only zeroes. |
| // |
| |
| static void hufClearDecTable(HufDec *hdecod) // io: (allocated by caller) |
| // decoding table [HUF_DECSIZE] |
| { |
| for (int i = 0; i < HUF_DECSIZE; i++) { |
| hdecod[i].len = 0; |
| hdecod[i].lit = 0; |
| hdecod[i].p = NULL; |
| } |
| // memset(hdecod, 0, sizeof(HufDec) * HUF_DECSIZE); |
| } |
| |
| // |
| // Build a decoding hash table based on the encoding table hcode: |
| // - short codes (<= HUF_DECBITS) are resolved with a single table access; |
| // - long code entry allocations are not optimized, because long codes are |
| // unfrequent; |
| // - decoding tables are used by hufDecode(); |
| // |
| |
| static bool hufBuildDecTable(const long long *hcode, // i : encoding table |
| int im, // i : min index in hcode |
| int iM, // i : max index in hcode |
| HufDec *hdecod) // o: (allocated by caller) |
| // decoding table [HUF_DECSIZE] |
| { |
| // |
| // Init hashtable & loop on all codes. |
| // Assumes that hufClearDecTable(hdecod) has already been called. |
| // |
| |
| for (; im <= iM; im++) { |
| long long c = hufCode(hcode[im]); |
| int l = hufLength(hcode[im]); |
| |
| if (c >> l) { |
| // |
| // Error: c is supposed to be an l-bit code, |
| // but c contains a value that is greater |
| // than the largest l-bit number. |
| // |
| |
| // invalidTableEntry(); |
| return false; |
| } |
| |
| if (l > HUF_DECBITS) { |
| // |
| // Long code: add a secondary entry |
| // |
| |
| HufDec *pl = hdecod + (c >> (l - HUF_DECBITS)); |
| |
| if (pl->len) { |
| // |
| // Error: a short code has already |
| // been stored in table entry *pl. |
| // |
| |
| // invalidTableEntry(); |
| return false; |
| } |
| |
| pl->lit++; |
| |
| if (pl->p) { |
| unsigned int *p = pl->p; |
| pl->p = new unsigned int[pl->lit]; |
| |
| for (unsigned int i = 0; i < pl->lit - 1u; ++i) pl->p[i] = p[i]; |
| |
| delete[] p; |
| } else { |
| pl->p = new unsigned int[1]; |
| } |
| |
| pl->p[pl->lit - 1] = im; |
| } else if (l) { |
| // |
| // Short code: init all primary entries |
| // |
| |
| HufDec *pl = hdecod + (c << (HUF_DECBITS - l)); |
| |
| for (long long i = 1ULL << (HUF_DECBITS - l); i > 0; i--, pl++) { |
| if (pl->len || pl->p) { |
| // |
| // Error: a short code or a long code has |
| // already been stored in table entry *pl. |
| // |
| |
| // invalidTableEntry(); |
| return false; |
| } |
| |
| pl->len = l; |
| pl->lit = im; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| // |
| // Free the long code entries of a decoding table built by hufBuildDecTable() |
| // |
| |
| static void hufFreeDecTable(HufDec *hdecod) // io: Decoding table |
| { |
| for (int i = 0; i < HUF_DECSIZE; i++) { |
| if (hdecod[i].p) { |
| delete[] hdecod[i].p; |
| hdecod[i].p = 0; |
| } |
| } |
| } |
| |
| // |
| // ENCODING |
| // |
| |
| inline void outputCode(long long code, long long &c, int &lc, char *&out) { |
| outputBits(hufLength(code), hufCode(code), c, lc, out); |
| } |
| |
| inline void sendCode(long long sCode, int runCount, long long runCode, |
| long long &c, int &lc, char *&out) { |
| // |
| // Output a run of runCount instances of the symbol sCount. |
| // Output the symbols explicitly, or if that is shorter, output |
| // the sCode symbol once followed by a runCode symbol and runCount |
| // expressed as an 8-bit number. |
| // |
| |
| if (hufLength(sCode) + hufLength(runCode) + 8 < hufLength(sCode) * runCount) { |
| outputCode(sCode, c, lc, out); |
| outputCode(runCode, c, lc, out); |
| outputBits(8, runCount, c, lc, out); |
| } else { |
| while (runCount-- >= 0) outputCode(sCode, c, lc, out); |
| } |
| } |
| |
| // |
| // Encode (compress) ni values based on the Huffman encoding table hcode: |
| // |
| |
| static int hufEncode // return: output size (in bits) |
| (const long long *hcode, // i : encoding table |
| const unsigned short *in, // i : uncompressed input buffer |
| const int ni, // i : input buffer size (in bytes) |
| int rlc, // i : rl code |
| char *out) // o: compressed output buffer |
| { |
| char *outStart = out; |
| long long c = 0; // bits not yet written to out |
| int lc = 0; // number of valid bits in c (LSB) |
| int s = in[0]; |
| int cs = 0; |
| |
| // |
| // Loop on input values |
| // |
| |
| for (int i = 1; i < ni; i++) { |
| // |
| // Count same values or send code |
| // |
| |
| if (s == in[i] && cs < 255) { |
| cs++; |
| } else { |
| sendCode(hcode[s], cs, hcode[rlc], c, lc, out); |
| cs = 0; |
| } |
| |
| s = in[i]; |
| } |
| |
| // |
| // Send remaining code |
| // |
| |
| sendCode(hcode[s], cs, hcode[rlc], c, lc, out); |
| |
| if (lc) *out = (c << (8 - lc)) & 0xff; |
| |
| return (out - outStart) * 8 + lc; |
| } |
| |
| // |
| // DECODING |
| // |
| |
| // |
| // In order to force the compiler to inline them, |
| // getChar() and getCode() are implemented as macros |
| // instead of "inline" functions. |
| // |
| |
| #define getChar(c, lc, in) \ |
| { \ |
| c = ((unsigned long long)c << 8) | *(unsigned char *)(in++); \ |
| lc += 8; \ |
| } |
| |
| #if 0 |
| #define getCode(po, rlc, c, lc, in, out, ob, oe) \ |
| { \ |
| if (po == rlc) { \ |
| if (lc < 8) getChar(c, lc, in); \ |
| \ |
| lc -= 8; \ |
| \ |
| unsigned char cs = (c >> lc); \ |
| \ |
| if (out + cs > oe) return false; \ |
| \ |
| /* TinyEXR issue 78 */ \ |
| unsigned short s = out[-1]; \ |
| \ |
| while (cs-- > 0) *out++ = s; \ |
| } else if (out < oe) { \ |
| *out++ = po; \ |
| } else { \ |
| return false; \ |
| } \ |
| } |
| #else |
| static bool getCode(int po, int rlc, long long &c, int &lc, const char *&in, |
| const char *in_end, unsigned short *&out, |
| const unsigned short *ob, const unsigned short *oe) { |
| (void)ob; |
| if (po == rlc) { |
| if (lc < 8) { |
| /* TinyEXR issue 78 */ |
| /* TinyEXR issue 160. in + 1 -> in */ |
| if (in >= in_end) { |
| return false; |
| } |
| |
| getChar(c, lc, in); |
| } |
| |
| lc -= 8; |
| |
| unsigned char cs = (c >> lc); |
| |
| if (out + cs > oe) return false; |
| |
| // Bounds check for safety |
| // Issue 100. |
| if ((out - 1) < ob) return false; |
| unsigned short s = out[-1]; |
| |
| while (cs-- > 0) *out++ = s; |
| } else if (out < oe) { |
| *out++ = po; |
| } else { |
| return false; |
| } |
| return true; |
| } |
| #endif |
| |
| // |
| // Decode (uncompress) ni bits based on encoding & decoding tables: |
| // |
| |
| static bool hufDecode(const long long *hcode, // i : encoding table |
| const HufDec *hdecod, // i : decoding table |
| const char *in, // i : compressed input buffer |
| int ni, // i : input size (in bits) |
| int rlc, // i : run-length code |
| int no, // i : expected output size (in bytes) |
| unsigned short *out) // o: uncompressed output buffer |
| { |
| long long c = 0; |
| int lc = 0; |
| unsigned short *outb = out; // begin |
| unsigned short *oe = out + no; // end |
| const char *ie = in + (ni + 7) / 8; // input byte size |
| |
| // |
| // Loop on input bytes |
| // |
| |
| while (in < ie) { |
| getChar(c, lc, in); |
| |
| // |
| // Access decoding table |
| // |
| |
| while (lc >= HUF_DECBITS) { |
| const HufDec pl = hdecod[(c >> (lc - HUF_DECBITS)) & HUF_DECMASK]; |
| |
| if (pl.len) { |
| // |
| // Get short code |
| // |
| |
| lc -= pl.len; |
| // std::cout << "lit = " << pl.lit << std::endl; |
| // std::cout << "rlc = " << rlc << std::endl; |
| // std::cout << "c = " << c << std::endl; |
| // std::cout << "lc = " << lc << std::endl; |
| // std::cout << "in = " << in << std::endl; |
| // std::cout << "out = " << out << std::endl; |
| // std::cout << "oe = " << oe << std::endl; |
| if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) { |
| return false; |
| } |
| } else { |
| if (!pl.p) { |
| return false; |
| } |
| // invalidCode(); // wrong code |
| |
| // |
| // Search long code |
| // |
| |
| unsigned int j; |
| |
| for (j = 0; j < pl.lit; j++) { |
| int l = hufLength(hcode[pl.p[j]]); |
| |
| while (lc < l && in < ie) // get more bits |
| getChar(c, lc, in); |
| |
| if (lc >= l) { |
| if (hufCode(hcode[pl.p[j]]) == |
| ((c >> (lc - l)) & (((long long)(1) << l) - 1))) { |
| // |
| // Found : get long code |
| // |
| |
| lc -= l; |
| if (!getCode(pl.p[j], rlc, c, lc, in, ie, out, outb, oe)) { |
| return false; |
| } |
| break; |
| } |
| } |
| } |
| |
| if (j == pl.lit) { |
| return false; |
| // invalidCode(); // Not found |
| } |
| } |
| } |
| } |
| |
| // |
| // Get remaining (short) codes |
| // |
| |
| int i = (8 - ni) & 7; |
| c >>= i; |
| lc -= i; |
| |
| while (lc > 0) { |
| const HufDec pl = hdecod[((unsigned long long)c << (HUF_DECBITS - lc)) & HUF_DECMASK]; |
| |
| if (pl.len) { |
| lc -= pl.len; |
| if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) { |
| return false; |
| } |
| } else { |
| return false; |
| // invalidCode(); // wrong (long) code |
| } |
| } |
| |
| if (out - outb != no) { |
| return false; |
| } |
| // notEnoughData (); |
| |
| return true; |
| } |
| |
| static void countFrequencies(std::vector<long long> &freq, |
| const unsigned short data[/*n*/], int n) { |
| for (int i = 0; i < HUF_ENCSIZE; ++i) freq[i] = 0; |
| |
| for (int i = 0; i < n; ++i) ++freq[data[i]]; |
| } |
| |
| static void writeUInt(char buf[4], unsigned int i) { |
| unsigned char *b = (unsigned char *)buf; |
| |
| b[0] = i; |
| b[1] = i >> 8; |
| b[2] = i >> 16; |
| b[3] = i >> 24; |
| } |
| |
| static unsigned int readUInt(const char buf[4]) { |
| const unsigned char *b = (const unsigned char *)buf; |
| |
| return (b[0] & 0x000000ff) | ((b[1] << 8) & 0x0000ff00) | |
| ((b[2] << 16) & 0x00ff0000) | ((b[3] << 24) & 0xff000000); |
| } |
| |
| // |
| // EXTERNAL INTERFACE |
| // |
| |
| static int hufCompress(const unsigned short raw[], int nRaw, |
| char compressed[]) { |
| if (nRaw == 0) return 0; |
| |
| std::vector<long long> freq(HUF_ENCSIZE); |
| |
| countFrequencies(freq, raw, nRaw); |
| |
| int im = 0; |
| int iM = 0; |
| hufBuildEncTable(freq.data(), &im, &iM); |
| |
| char *tableStart = compressed + 20; |
| char *tableEnd = tableStart; |
| hufPackEncTable(freq.data(), im, iM, &tableEnd); |
| int tableLength = tableEnd - tableStart; |
| |
| char *dataStart = tableEnd; |
| int nBits = hufEncode(freq.data(), raw, nRaw, iM, dataStart); |
| int data_length = (nBits + 7) / 8; |
| |
| writeUInt(compressed, im); |
| writeUInt(compressed + 4, iM); |
| writeUInt(compressed + 8, tableLength); |
| writeUInt(compressed + 12, nBits); |
| writeUInt(compressed + 16, 0); // room for future extensions |
| |
| return dataStart + data_length - compressed; |
| } |
| |
| static bool hufUncompress(const char compressed[], int nCompressed, |
| std::vector<unsigned short> *raw) { |
| if (nCompressed == 0) { |
| if (raw->size() != 0) return false; |
| |
| return false; |
| } |
| |
| int im = readUInt(compressed); |
| int iM = readUInt(compressed + 4); |
| // int tableLength = readUInt (compressed + 8); |
| int nBits = readUInt(compressed + 12); |
| |
| if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) return false; |
| |
| const char *ptr = compressed + 20; |
| |
| // |
| // Fast decoder needs at least 2x64-bits of compressed data, and |
| // needs to be run-able on this platform. Otherwise, fall back |
| // to the original decoder |
| // |
| |
| // if (FastHufDecoder::enabled() && nBits > 128) |
| //{ |
| // FastHufDecoder fhd (ptr, nCompressed - (ptr - compressed), im, iM, iM); |
| // fhd.decode ((unsigned char*)ptr, nBits, raw, nRaw); |
| //} |
| // else |
| { |
| std::vector<long long> freq(HUF_ENCSIZE); |
| std::vector<HufDec> hdec(HUF_DECSIZE); |
| |
| hufClearDecTable(&hdec.at(0)); |
| |
| hufUnpackEncTable(&ptr, nCompressed - (ptr - compressed), im, iM, |
| &freq.at(0)); |
| |
| { |
| if (nBits > 8 * (nCompressed - (ptr - compressed))) { |
| return false; |
| } |
| |
| hufBuildDecTable(&freq.at(0), im, iM, &hdec.at(0)); |
| hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, raw->size(), |
| raw->data()); |
| } |
| // catch (...) |
| //{ |
| // hufFreeDecTable (hdec); |
| // throw; |
| //} |
|