diff --git a/3rdparty/tinyexr/README.md b/3rdparty/tinyexr/README.md index aea03fd..f64d03a 100644 --- a/3rdparty/tinyexr/README.md +++ b/3rdparty/tinyexr/README.md @@ -1,5 +1,7 @@ # Tiny OpenEXR image library. +[![Total alerts](https://img.shields.io/lgtm/alerts/g/syoyo/tinyexr.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/syoyo/tinyexr/alerts/) + ![Example](https://github.com/syoyo/tinyexr/blob/master/asakusa.png?raw=true) [![AppVeyor build status](https://ci.appveyor.com/api/projects/status/k07ftfe4ph057qau/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyexr/branch/master) @@ -8,38 +10,86 @@ [![Coverity Scan Build Status](https://scan.coverity.com/projects/5827/badge.svg)](https://scan.coverity.com/projects/5827) -`tinyexr` is a small, single header-only library to load and save OpenEXR(.exr) images. -`tinyexr` is written in portable C++(no library dependency except for STL), thus `tinyexr` is good to embed into your application. +`tinyexr` is a small, single header-only library to load and save OpenEXR (.exr) images. +`tinyexr` is written in portable C++ (no library dependency except for STL), thus `tinyexr` is good to embed into your application. To use `tinyexr`, simply copy `tinyexr.h` into your project. -`tinyexr` currently supports: +# Features -* OpenEXR version 1.x. -* Normal image - * Scanline format. - * Uncompress("compress" = 0), ZIPS("compress" = 2), ZIP compression("compress" = 3) and PIZ compression("compress" = 4). - * Half/Uint/Float pixel type. - * Custom attributes(up to 128) -* Deep image - * Scanline format. - * ZIPS compression("compress" = 2). - * Half, float pixel type. -* Litte endian machine. -* Limited support for big endian machine. - * read/write normal image. +Current status of `tinyexr` is: + +- OpenEXR v1 image + - [x] Scanline format + - [ ] Tiled format + - [x] Tile format with no LoD (load). + - [ ] Tile format with LoD (load). + - [ ] Tile format with no LoD (save). + - [ ] Tile format with LoD (save). + - [x] Custom attributes +- OpenEXR v2 image + - [ ] Multipart format + - [x] Load multi-part image + - [ ] Save multi-part image + - [ ] Load multi-part deep image + - [ ] Save multi-part deep image +- OpenEXR v2 deep image + - [x] Loading scanline + ZIPS + HALF or FLOAT pixel type. +- Compression + - [x] NONE + - [x] RLE + - [x] ZIP + - [x] ZIPS + - [x] PIZ + - [x] ZFP (tinyexr extension) + - [ ] B44? + - [ ] B44A? + - [ ] PIX24? +- Line order. + - [x] Increasing, decreasing (load) + - [ ] Random? + - [ ] Increasing, decreasing (save) +- Pixel format (UINT, FLOAT). + - [x] UINT, FLOAT (load) + - [x] UINT, FLOAT (deep load) + - [x] UINT, FLOAT (save) + - [ ] UINT, FLOAT (deep save) +- Support for big endian machine. + - [x] Loading scanline image + - [x] Saving scanline image + - [ ] Loading multi-part channel EXR + - [ ] Saving multi-part channel EXR + - [ ] Loading deep image + - [ ] Saving deep image +- Optimization + - [x] C++11 thread loading + - [ ] C++11 thread saving + - [ ] ISPC? + - [x] OpenMP multi-threading in EXR loading. + - [x] OpenMP multi-threading in EXR saving. + - [ ] OpenMP multi-threading in deep image loading. + - [ ] OpenMP multi-threading in deep image saving. * C interface. - * You can easily write language bindings(e.g. golang) -* EXR saving - * with ZIP compression. -* JavaScript library - * Through emscripten. + * You can easily write language bindings (e.g. golang) -# Use case +# Requirements + +* C++ compiler(C++11 recommended. C++03 may work) + +# Use case + +## New TinyEXR (v0.9.5+) + +* Godot. Multi-platform 2D and 3D game engine https://godotengine.org/ +* Filament. PBR engine. https://github.com/google/filament +* PyEXR. Loading OpenEXR (.exr) images using Python. https://github.com/ialhashim/PyEXR +* The-Forge. The Forge Cross-Platform Rendering Framework PC, Linux, Ray Tracing, macOS / iOS, Android, XBOX, PS4 https://github.com/ConfettiFX/The-Forge +* Your project here! + +## Older TinyEXR (v0.9.0) * mallie https://github.com/lighttransport/mallie -* PBRT v3 https://github.com/mmp/pbrt-v3 * Cinder 0.9.0 https://libcinder.org/notes/v0.9.0 -* Piccante(develop branch) http://piccantelib.net/ +* Piccante (develop branch) http://piccantelib.net/ * Your project here! ## Examples @@ -47,79 +97,240 @@ To use `tinyexr`, simply copy `tinyexr.h` into your project. * [examples/deepview/](examples/deepview) Deep image view * [examples/rgbe2exr/](examples/rgbe2exr) .hdr to EXR converter * [examples/exr2rgbe/](examples/exr2rgbe) EXR to .hdr converter +* [examples/ldr2exr/](examples/exr2rgbe) LDR to EXR converter +* [examples/exr2ldr/](examples/exr2ldr) EXR to LDR converter +* [examples/exr2fptiff/](examples/exr2fptiff) EXR to 32bit floating point TIFF converter + * for 32bit floating point TIFF to EXR convert, see https://github.com/syoyo/tinydngloader/tree/master/examples/fptiff2exr +* [examples/cube2longlat/](examples/cube2longlat) Cubemap to longlat (equirectangler) converter + +## Experimental + +* [experimental/js/](experimental/js) JavaScript port using Emscripten ## Usage NOTE: **API is still subject to change**. See the source code for details. -Include `tinyexr.h` with `TINYEXR_IMPLEMENTATION` flag(do this only for **one** .cc file). +Include `tinyexr.h` with `TINYEXR_IMPLEMENTATION` flag (do this only for **one** .cc file). -``` +```cpp +//Please include your own zlib-compatible API header before +//including `tinyexr.h` when you disable `TINYEXR_USE_MINIZ` +//#define TINYEXR_USE_MINIZ 0 +//#include "zlib.h" #define TINYEXR_IMPLEMENTATION #include "tinyexr.h" ``` -Quickly reading RGB(A) EXR file. +### Compile flags -``` +* `TINYEXR_USE_MINIZ` Use embedded miniz (default = 1). Please include `zlib.h` header (before `tinyexr.h`) if you disable miniz support. +* `TINYEXR_USE_PIZ` Enable PIZ compression support (default = 1) +* `TINYEXR_USE_ZFP` Enable ZFP compression supoort (TinyEXR extension, default = 0) +* `TINYEXR_USE_THREAD` Enable threaded loading using C++11 thread (Requires C++11 compiler, default = 0) +* `TINYEXR_USE_OPENMP` Enable OpenMP threading support (default = 1 if `_OPENMP` is defined) + * Use `TINYEXR_USE_OPENMP=0` to force disable OpenMP code path even if OpenMP is available/enabled in the compiler. + +### Quickly reading RGB(A) EXR file. + +```cpp const char* input = "asakusa.exr"; float* out; // width * height * RGBA int width; int height; - const char* err; + const char* err = NULL; // or nullptr in C++11 int ret = LoadEXR(&out, &width, &height, input, &err); -``` -Loading EXR from a file. + if (ret != TINYEXR_SUCCESS) { + if (err) { + fprintf(stderr, "ERR : %s\n", err); + FreeEXRErrorMessage(err); // release memory of error message. + } + } else { + ... + free(out); // release memory of image data + } ``` - const char* input = "asakusa.exr"; - const char* err; - EXRImage exrImage; - InitEXRImage(&exrImage); +### Reading layered RGB(A) EXR file. - int ret = ParseMultiChannelEXRHeaderFromFile(&exrImage, input, &err); +If you want to read EXR image with layer info (channel has a name with delimiter `.`), please use `LoadEXRWithLayer` API. + +You need to know layer name in advance (e.g. through `EXRLayers` API). + +```cpp + const char* input = ...; + const char* layer_name = "diffuse"; // or use EXRLayers to get list of layer names in .exr + float* out; // width * height * RGBA + int width; + int height; + const char* err = NULL; // or nullptr in C++11 + + // will read `diffuse.R`, `diffuse.G`, `diffuse.B`, (`diffuse.A`) channels + int ret = LoadEXRWithLayer(&out, &width, &height, input, layer_name, &err); + + if (ret != TINYEXR_SUCCESS) { + if (err) { + fprintf(stderr, "ERR : %s\n", err); + FreeEXRErrorMessage(err); // release memory of error message. + } + } else { + ... + free(out); // release memory of image data + } + +``` + +### Loading Singlepart EXR from a file. + +Scanline and tiled format are supported. + +```cpp + // 1. Read EXR version. + EXRVersion exr_version; + + int ret = ParseEXRVersionFromFile(&exr_version, argv[1]); + if (ret != 0) { + fprintf(stderr, "Invalid EXR file: %s\n", argv[1]); + return -1; + } + + if (exr_version.multipart) { + // must be multipart flag is false. + return -1; + } + + // 2. Read EXR header + EXRHeader exr_header; + InitEXRHeader(&exr_header); + + const char* err = NULL; // or `nullptr` in C++11 or later. + ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, argv[1], &err); if (ret != 0) { fprintf(stderr, "Parse EXR err: %s\n", err); - return; + FreeEXRErrorMessage(err); // free's buffer for an error message + return ret; } - //// Uncomment if you want reading HALF image as FLOAT. - //for (int i = 0; i < exrImage.num_channels; i++) { - // if (exrImage.pixel_types[i] = TINYEXR_PIXELTYPE_HALF) { - // exrImage.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; - // } - //} + // // Read HALF channel as FLOAT. + // for (int i = 0; i < exr_header.num_channels; i++) { + // if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { + // exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + // } + // } - ret = LoadMultiChannelEXRFromFile(&exrImage, input, &err); + EXRImage exr_image; + InitEXRImage(&exr_image); + + ret = LoadEXRImageFromFile(&exr_image, &exr_header, argv[1], &err); if (ret != 0) { fprintf(stderr, "Load EXR err: %s\n", err); - return; + FreeEXRHeader(&exr_header); + FreeEXRErrorMessage(err); // free's buffer for an error message + return ret; } + + // 3. Access image data + // `exr_image.images` will be filled when EXR is scanline format. + // `exr_image.tiled` will be filled when EXR is tiled format. + + // 4. Free image data + FreeEXRImage(&exr_image); + FreeEXRHeader(&exr_header); ``` -Saving EXR file. +### Loading Multipart EXR from a file. +Scanline and tiled format are supported. + +```cpp + // 1. Read EXR version. + EXRVersion exr_version; + + int ret = ParseEXRVersionFromFile(&exr_version, argv[1]); + if (ret != 0) { + fprintf(stderr, "Invalid EXR file: %s\n", argv[1]); + return -1; + } + + if (!exr_version.multipart) { + // must be multipart flag is true. + return -1; + } + + // 2. Read EXR headers in the EXR. + EXRHeader **exr_headers; // list of EXRHeader pointers. + int num_exr_headers; + const char *err = NULL; // or nullptr in C++11 or later + + // Memory for EXRHeader is allocated inside of ParseEXRMultipartHeaderFromFile, + ret = ParseEXRMultipartHeaderFromFile(&exr_headers, &num_exr_headers, &exr_version, argv[1], &err); + if (ret != 0) { + fprintf(stderr, "Parse EXR err: %s\n", err); + FreeEXRErrorMessage(err); // free's buffer for an error message + return ret; + } + + printf("num parts = %d\n", num_exr_headers); + + + // 3. Load images. + + // Prepare array of EXRImage. + std::vector images(num_exr_headers); + for (int i =0; i < num_exr_headers; i++) { + InitEXRImage(&images[i]); + } + + ret = LoadEXRMultipartImageFromFile(&images.at(0), const_cast(exr_headers), num_exr_headers, argv[1], &err); + if (ret != 0) { + fprintf(stderr, "Parse EXR err: %s\n", err); + FreeEXRErrorMessage(err); // free's buffer for an error message + return ret; + } + + printf("Loaded %d part images\n", num_exr_headers); + + // 4. Access image data + // `exr_image.images` will be filled when EXR is scanline format. + // `exr_image.tiled` will be filled when EXR is tiled format. + + // 5. Free images + for (int i =0; i < num_exr_headers; i++) { + FreeEXRImage(&images.at(i)); + } + + // 6. Free headers. + for (int i =0; i < num_exr_headers; i++) { + FreeEXRHeader(exr_headers[i]); + free(exr_headers[i]); + } + free(exr_headers); ``` + + +Saving Scanline EXR file. + +```cpp + // See `examples/rgbe2exr/` for more details. bool SaveEXR(const float* rgb, int width, int height, const char* outfilename) { - float* channels[3]; + EXRHeader header; + InitEXRHeader(&header); EXRImage image; InitEXRImage(&image); image.num_channels = 3; - // Must be BGR(A) order, since most of EXR viewers expect this channel order. - const char* channel_names[] = {"B", "G", "R"}; // "B", "G", "R", "A" for RGBA image - std::vector images[3]; images[0].resize(width * height); images[1].resize(width * height); images[2].resize(width * height); + // Split RGBRGBRGB... into R, G and B layer for (int i = 0; i < width * height; i++) { images[0][i] = rgb[3*i+0]; images[1][i] = rgb[3*i+1]; @@ -131,31 +342,38 @@ Saving EXR file. image_ptr[1] = &(images[1].at(0)); // G image_ptr[2] = &(images[0].at(0)); // R - image.channel_names = channel_names; image.images = (unsigned char**)image_ptr; image.width = width; image.height = height; - image.compression = TINYEXR_COMPRESSIONTYPE_ZIP; - image.pixel_types = (int *)malloc(sizeof(int) * image.num_channels); - image.requested_pixel_types = (int *)malloc(sizeof(int) * image.num_channels); - for (int i = 0; i < image.num_channels; i++) { - image.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image - image.requested_pixel_types[i] = TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR + header.num_channels = 3; + header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels); + // Must be (A)BGR order, since most of EXR viewers expect this channel order. + strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0'; + strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0'; + strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0'; + + header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels); + header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels); + for (int i = 0; i < header.num_channels; i++) { + header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image + header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR } - const char* err; - int ret = SaveMultiChannelEXRToFile(&image, outfilename, &err); - if (ret != 0) { + const char* err = NULL; // or nullptr in C++11 or later. + int ret = SaveEXRImageToFile(&image, &header, outfilename, &err); + if (ret != TINYEXR_SUCCESS) { fprintf(stderr, "Save EXR err: %s\n", err); + FreeEXRErrorMessage(err); // free's buffer for an error message return ret; } printf("Saved exr file. [ %s ] \n", outfilename); - free(image.pixel_types); - free(image.requested_pixel_types); + free(rgb); - return ret; + free(header.channels); + free(header.pixel_types); + free(header.requested_pixel_types); } ``` @@ -164,14 +382,14 @@ Saving EXR file. Reading deep image EXR file. See `example/deepview` for actual usage. -``` +```cpp const char* input = "deepimage.exr"; - const char* err; + const char* err = NULL; // or nullptr DeepImage deepImage; int ret = LoadDeepEXR(&deepImage, input, &err); - // acccess to each sample in the deep pixel. + // access to each sample in the deep pixel. for (int y = 0; y < deepImage.height; y++) { int sampleNum = deepImage.offset_table[y][deepImage.width-1]; for (int x = 0; x < deepImage.width-1; x++) { @@ -196,49 +414,101 @@ See `example/deepview` for actual usage. ![DeepViewExample](https://github.com/syoyo/tinyexr/blob/master/examples/deepview/deepview_screencast.gif?raw=true) +## TinyEXR extension + +### ZFP + +#### NOTE + +TinyEXR adds ZFP compression as an experimemtal support (Linux and MacOSX only). + +ZFP only supports FLOAT format pixel, and its image width and height must be the multiple of 4, since ZFP compresses pixels with 4x4 pixel block. + +#### Setup + +Checkout zfp repo as an submodule. + + $ git submodule update --init + +#### Build + +Then build ZFP + + $ cd deps/ZFP + $ mkdir -p lib # Create `lib` directory if not exist + $ make + +Set `1` to `TINYEXT_USE_ZFP` define in `tinyexr.h` + +Build your app with linking `deps/ZFP/lib/libzfp.a` + +#### ZFP attribute + +For ZFP EXR image, the following attribute must exist in its EXR image. + +* `zfpCompressionType` (uchar). + * 0 = fixed rate compression + * 1 = precision based variable rate compression + * 2 = accuracy based variable rate compression + +And the one of following attributes must exist in EXR, depending on the `zfpCompressionType` value. + +* `zfpCompressionRate` (double) + * Specifies compression rate for fixed rate compression. +* `zfpCompressionPrecision` (int32) + * Specifies the number of bits for precision based variable rate compression. +* `zfpCompressionTolerance` (double) + * Specifies the tolerance value for accuracy based variable rate compression. + +#### Note on ZFP compression. + +At least ZFP code itself works well on big endian machine. + +## Unit tests + +See `test/unit` directory. + ## TODO Contribution is welcome! - [ ] Compression - - [ ] NONE("compress" = 0, load) - - [ ] RLE("compress" = 1, load) - - [x] ZIPS("compress" = 2, load) - - [x] ZIP("compress" = 3, load) - - [x] PIZ("compress" = 4, load) - - [x] NONE("compress" = 0, save) - - [ ] RLE("compress" = 1, save) - - [x] ZIPS("compress" = 2, save) - - [x] ZIP("compress" = 3, save) - - [ ] PIZ("compress" = 4, save) + - [ ] B44? + - [ ] B44A? + - [ ] PIX24? - [ ] Custom attributes - - [x] Normal image(EXR 1.x) - - [ ] Deep image(EXR 2.x) -- [ ] JavaScript library + - [x] Normal image (EXR 1.x) + - [ ] Deep image (EXR 2.x) +- [ ] JavaScript library (experimental, using Emscripten) - [x] LoadEXRFromMemory - [ ] SaveMultiChannelEXR - [ ] Deep image save/load - [ ] Write from/to memory buffer. - - [x] SaveMultiChannelEXR - - [x] LoadMultiChannelEXR - [ ] Deep image save/load - [ ] Tile format. -- [ ] Support for various compression type. - - [x] zstd compression(Not in OpenEXR spec, though) + - [x] Tile format with no LoD (load). + - [ ] Tile format with LoD (load). + - [ ] Tile format with no LoD (save). + - [ ] Tile format with LoD (save). +- [ ] Support for custom compression type. + - [x] zfp compression (Not in OpenEXR spec, though) + - [ ] zstd? - [x] Multi-channel. -- [ ] Multi-part(EXR2.0) +- [ ] Multi-part (EXR2.0) + - [x] Load multi-part image + - [ ] Load multi-part deep image - [ ] Line order. - - [x] Increasing, decreasing(load) + - [x] Increasing, decreasing (load) - [ ] Random? - - [ ] Increasing, decreasing(save) -- [ ] Pixel format(UINT, FLOAT). - - [x] UINT, FLOAT(load) - - [x] UINT, FLOAT(deep load) - - [x] UINT, FLOAT(save) - - [ ] UINT, FLOAT(deep save) -- [ ] Full support for big endian machine. - - [x] Loading multi channel EXR - - [x] Saving multi channel EXR + - [ ] Increasing, decreasing (save) +- [ ] Pixel format (UINT, FLOAT). + - [x] UINT, FLOAT (load) + - [x] UINT, FLOAT (deep load) + - [x] UINT, FLOAT (save) + - [ ] UINT, FLOAT (deep save) +- [ ] Support for big endian machine. + - [ ] Loading multi-part channel EXR + - [ ] Saving multi-part channel EXR - [ ] Loading deep image - [ ] Saving deep image - [ ] Optimization @@ -248,6 +518,10 @@ Contribution is welcome! - [ ] OpenMP multi-threading in deep image loading. - [ ] OpenMP multi-threading in deep image saving. +## Python bindings + +`pytinyexr` is available: https://pypi.org/project/pytinyexr/ (loading only as of 0.9.1) + ## Similar or related projects * miniexr: https://github.com/aras-p/miniexr (Write OpenEXR) @@ -264,11 +538,11 @@ Contribution is welcome! ## Author(s) -Syoyo Fujita(syoyo@lighttransport.com) +Syoyo Fujita (syoyo@lighttransport.com) ## Contributor(s) -* Matt Ebb (http://mattebb.com) : deep image example. Thanks! -* Matt Pharr (http://pharr.org/matt/) : Testing tinyexr with OpenEXR(IlmImf). Thanks! -* Andrew Bell (https://github.com/andrewfb) & Richard Eakin (https://github.com/richardeakin) : Improving TinyEXR API. Thanks! -* Mike Wong (https://github.com/mwkm) : ZIPS compression support in loading. Thanks! +* Matt Ebb (http://mattebb.com): deep image example. Thanks! +* Matt Pharr (http://pharr.org/matt/): Testing tinyexr with OpenEXR(IlmImf). Thanks! +* Andrew Bell (https://github.com/andrewfb) & Richard Eakin (https://github.com/richardeakin): Improving TinyEXR API. Thanks! +* Mike Wong (https://github.com/mwkm): ZIPS compression support in loading. Thanks! diff --git a/3rdparty/tinyexr/tinyexr.h b/3rdparty/tinyexr/tinyexr.h index 7e8956f..32e1341 100644 --- a/3rdparty/tinyexr/tinyexr.h +++ b/3rdparty/tinyexr/tinyexr.h @@ -1,3 +1,5 @@ +#ifndef TINYEXR_H_ +#define TINYEXR_H_ /* Copyright (c) 2014 - 2019, Syoyo Fujita and many contributors. All rights reserved. @@ -63,8 +65,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // End of OpenEXR license ------------------------------------------------- -#ifndef TINYEXR_H_ -#define TINYEXR_H_ // // @@ -287,7 +287,7 @@ typedef struct _DeepImage { extern int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, const char **err); -// Loads single-frame OpenEXR image by specifing layer name. Assume EXR image contains A(single channel +// 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 @@ -302,7 +302,7 @@ extern int LoadEXRWithLayer(float **out_rgba, int *width, int *height, // // @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(wll be filled when the function returns error code). Free it using FreeEXRErrorMessage after using this value. +// @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. // @@ -336,13 +336,13 @@ extern void InitEXRHeader(EXRHeader *exr_header); // Initialize EXRImage struct extern void InitEXRImage(EXRImage *exr_image); -// Free's internal data of EXRHeader struct +// Frees internal data of EXRHeader struct extern int FreeEXRHeader(EXRHeader *exr_header); -// Free's internal data of EXRImage struct +// Frees internal data of EXRImage struct extern int FreeEXRImage(EXRImage *exr_image); -// Free's error message +// Frees error message extern void FreeEXRErrorMessage(const char *msg); // Parse EXR version header of a file. @@ -497,8 +497,17 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, #endif // TINYEXR_H_ #ifdef TINYEXR_IMPLEMENTATION -#ifndef TINYEXR_IMPLEMENTATION_DEIFNED -#define TINYEXR_IMPLEMENTATION_DEIFNED +#ifndef TINYEXR_IMPLEMENTATION_DEFINED +#define TINYEXR_IMPLEMENTATION_DEFINED + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include // for UTF-8 + +#endif #include #include @@ -536,7 +545,18 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, #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 namespace tinyexr { @@ -619,7 +639,7 @@ namespace miniz { - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug - would only have occured in earlier versions if you explicitly used this + would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you can't switch to v1.15 but want to fix @@ -7002,6 +7022,13 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, // Reuse MINIZ_LITTE_ENDIAN macro +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ + defined(__i386) || defined(__i486__) || defined(__i486) || \ + defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + #if defined(__sparcv9) // Big endian #else @@ -7378,7 +7405,7 @@ typedef struct { unsigned char pad[3]; } ChannelInfo; -typedef struct { +struct HeaderInfo { std::vector channels; std::vector attributes; @@ -7430,7 +7457,7 @@ typedef struct { header_len = 0; compression_type = 0; } -} HeaderInfo; +}; static bool ReadChannelInfo(std::vector &channels, const std::vector &data) { @@ -7712,7 +7739,7 @@ static int rleCompress(int inLength, const char in[], signed char out[]) { if (runEnd - runStart >= MIN_RUN_LENGTH) { // - // Compressable run + // Compressible run // *outWrite++ = static_cast(runEnd - runStart) - 1; @@ -8056,7 +8083,7 @@ static void wav2Encode( int p2 = 2; // == 1 << (level+1) // - // Hierachical loop on smaller dimension n + // Hierarchical loop on smaller dimension n // while (p2 <= n) { @@ -8287,9 +8314,9 @@ const int HUF_DECMASK = HUF_DECSIZE - 1; struct HufDec { // short code long code //------------------------------- - int len : 8; // code length 0 - int lit : 24; // lit p size - int *p; // 0 lits + 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; } @@ -8745,14 +8772,14 @@ static bool hufBuildDecTable(const long long *hcode, // i : encoding table pl->lit++; if (pl->p) { - int *p = pl->p; - pl->p = new int[pl->lit]; + unsigned int *p = pl->p; + pl->p = new unsigned int[pl->lit]; for (int i = 0; i < pl->lit - 1; ++i) pl->p[i] = p[i]; delete[] p; } else { - pl->p = new int[1]; + pl->p = new unsigned int[1]; } pl->p[pl->lit - 1] = im; @@ -9491,35 +9518,48 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, #endif // TINYEXR_USE_PIZ #if TINYEXR_USE_ZFP + struct ZFPCompressionParam { double rate; - int precision; + unsigned int precision; + unsigned int __pad0; double tolerance; int type; // TINYEXR_ZFP_COMPRESSIONTYPE_* + unsigned int __pad1; ZFPCompressionParam() { type = TINYEXR_ZFP_COMPRESSIONTYPE_RATE; rate = 2.0; precision = 0; - tolerance = 0.0f; + tolerance = 0.0; } }; -bool FindZFPCompressionParam(ZFPCompressionParam *param, +static bool FindZFPCompressionParam(ZFPCompressionParam *param, const EXRAttribute *attributes, - int num_attributes) { + int num_attributes, + std::string *err) { bool foundType = false; for (int i = 0; i < num_attributes; i++) { - if ((strcmp(attributes[i].name, "zfpCompressionType") == 0) && - (attributes[i].size == 1)) { - param->type = static_cast(attributes[i].value[0]); - - foundType = true; + if ((strcmp(attributes[i].name, "zfpCompressionType") == 0)) { + if (attributes[i].size == 1) { + param->type = static_cast(attributes[i].value[0]); + foundType = true; + break; + } else { + if (err) { + (*err) += "zfpCompressionType attribute must be uchar(1 byte) type.\n"; + } + return false; + } } } if (!foundType) { + if (err) { + (*err) += "`zfpCompressionType` attribute not found.\n"; + } return false; } @@ -9531,6 +9571,11 @@ bool FindZFPCompressionParam(ZFPCompressionParam *param, return true; } } + + if (err) { + (*err) += "`zfpCompressionRate` attribute not found.\n"; + } + } else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) { for (int i = 0; i < num_attributes; i++) { if ((strcmp(attributes[i].name, "zfpCompressionPrecision") == 0) && @@ -9539,6 +9584,11 @@ bool FindZFPCompressionParam(ZFPCompressionParam *param, return true; } } + + if (err) { + (*err) += "`zfpCompressionPrecision` attribute not found.\n"; + } + } else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { for (int i = 0; i < num_attributes; i++) { if ((strcmp(attributes[i].name, "zfpCompressionTolerance") == 0) && @@ -9547,8 +9597,14 @@ bool FindZFPCompressionParam(ZFPCompressionParam *param, return true; } } + + if (err) { + (*err) += "`zfpCompressionTolerance` attribute not found.\n"; + } } else { - assert(0); + if (err) { + (*err) += "Unknown value specified for `zfpCompressionType`.\n"; + } } return false; @@ -9556,10 +9612,10 @@ bool FindZFPCompressionParam(ZFPCompressionParam *param, // Assume pixel format is FLOAT for all channels. static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines, - int num_channels, const unsigned char *src, + size_t num_channels, const unsigned char *src, unsigned long src_size, const ZFPCompressionParam ¶m) { - size_t uncompressed_size = dst_width * dst_num_lines * num_channels; + size_t uncompressed_size = size_t(dst_width) * size_t(dst_num_lines) * num_channels; if (uncompressed_size == src_size) { // Data is not compressed(Issue 40). @@ -9572,22 +9628,22 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines, assert((dst_width % 4) == 0); assert((dst_num_lines % 4) == 0); - if ((dst_width & 3U) || (dst_num_lines & 3U)) { + if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) { return false; } field = zfp_field_2d(reinterpret_cast(const_cast(src)), - zfp_type_float, dst_width, dst_num_lines * num_channels); + zfp_type_float, static_cast(dst_width), static_cast(dst_num_lines) * static_cast(num_channels)); zfp = zfp_stream_open(NULL); if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) { - zfp_stream_set_rate(zfp, param.rate, zfp_type_float, /* dimention */ 2, + zfp_stream_set_rate(zfp, param.rate, zfp_type_float, /* dimension */ 2, /* write random access */ 0); } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) { - zfp_stream_set_precision(zfp, param.precision, zfp_type_float); + zfp_stream_set_precision(zfp, param.precision); } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { - zfp_stream_set_accuracy(zfp, param.tolerance, zfp_type_float); + zfp_stream_set_accuracy(zfp, param.tolerance); } else { assert(0); } @@ -9600,17 +9656,17 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines, zfp_stream_set_bit_stream(zfp, stream); zfp_stream_rewind(zfp); - size_t image_size = dst_width * dst_num_lines; + size_t image_size = size_t(dst_width) * size_t(dst_num_lines); - for (int c = 0; c < num_channels; c++) { + for (size_t c = 0; c < size_t(num_channels); c++) { // decompress 4x4 pixel block. - for (int y = 0; y < dst_num_lines; y += 4) { - for (int x = 0; x < dst_width; x += 4) { + for (size_t y = 0; y < size_t(dst_num_lines); y += 4) { + for (size_t x = 0; x < size_t(dst_width); x += 4) { float fblock[16]; zfp_decode_block_float_2(zfp, fblock); - for (int j = 0; j < 4; j++) { - for (int i = 0; i < 4; i++) { - dst[c * image_size + ((y + j) * dst_width + (x + i))] = + for (size_t j = 0; j < 4; j++) { + for (size_t i = 0; i < 4; i++) { + dst[c * image_size + ((y + j) * size_t(dst_width) + (x + i))] = fblock[j * 4 + i]; } } @@ -9626,7 +9682,7 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines, } // Assume pixel format is FLOAT for all channels. -bool CompressZfp(std::vector *outBuf, unsigned int *outSize, +static bool CompressZfp(std::vector *outBuf, unsigned int *outSize, const float *inPtr, int width, int num_lines, int num_channels, const ZFPCompressionParam ¶m) { zfp_stream *zfp = NULL; @@ -9635,22 +9691,22 @@ bool CompressZfp(std::vector *outBuf, unsigned int *outSize, assert((width % 4) == 0); assert((num_lines % 4) == 0); - if ((width & 3U) || (num_lines & 3U)) { + if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) { return false; } // create input array. field = zfp_field_2d(reinterpret_cast(const_cast(inPtr)), - zfp_type_float, width, num_lines * num_channels); + zfp_type_float, static_cast(width), static_cast(num_lines * num_channels)); zfp = zfp_stream_open(NULL); if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) { zfp_stream_set_rate(zfp, param.rate, zfp_type_float, 2, 0); } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) { - zfp_stream_set_precision(zfp, param.precision, zfp_type_float); + zfp_stream_set_precision(zfp, param.precision); } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { - zfp_stream_set_accuracy(zfp, param.tolerance, zfp_type_float); + zfp_stream_set_accuracy(zfp, param.tolerance); } else { assert(0); } @@ -9663,17 +9719,17 @@ bool CompressZfp(std::vector *outBuf, unsigned int *outSize, zfp_stream_set_bit_stream(zfp, stream); zfp_field_free(field); - size_t image_size = width * num_lines; + size_t image_size = size_t(width) * size_t(num_lines); - for (int c = 0; c < num_channels; c++) { + for (size_t c = 0; c < size_t(num_channels); c++) { // compress 4x4 pixel block. - for (int y = 0; y < num_lines; y += 4) { - for (int x = 0; x < width; x += 4) { + for (size_t y = 0; y < size_t(num_lines); y += 4) { + for (size_t x = 0; x < size_t(width); x += 4) { float fblock[16]; - for (int j = 0; j < 4; j++) { - for (int i = 0; i < 4; i++) { + for (size_t j = 0; j < 4; j++) { + for (size_t i = 0; i < 4; i++) { fblock[j * 4 + i] = - inPtr[c * image_size + ((y + j) * width + (x + i))]; + inPtr[c * image_size + ((y + j) * size_t(width) + (x + i))]; } } zfp_encode_block_float_2(zfp, fblock); @@ -9682,7 +9738,7 @@ bool CompressZfp(std::vector *outBuf, unsigned int *outSize, } zfp_stream_flush(zfp); - (*outSize) = zfp_stream_compressed_size(zfp); + (*outSize) = static_cast(zfp_stream_compressed_size(zfp)); zfp_stream_close(zfp); @@ -10122,8 +10178,10 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { #if TINYEXR_USE_ZFP tinyexr::ZFPCompressionParam zfp_compression_param; - if (!FindZFPCompressionParam(&zfp_compression_param, attributes, - num_attributes)) { + std::string e; + if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes, + int(num_attributes), &e)) { + // This code path should not be reachable. assert(0); return false; } @@ -10418,6 +10476,18 @@ static unsigned char **AllocateImage(int num_channels, return images; } +#ifdef _WIN32 +static inline std::wstring UTF8ToWchar(const std::string &str) { + int wstr_size = + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), NULL, 0); + std::wstring wstr(wstr_size, 0); + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], + (int)wstr.size()); + return wstr; +} +#endif + + static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, const EXRVersion *version, std::string *err, const unsigned char *buf, size_t size) { @@ -10798,7 +10868,7 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) { memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type, 256); exr_header->custom_attributes[i].size = info.attributes[i].size; - // Just copy poiner + // Just copy pointer exr_header->custom_attributes[i].value = info.attributes[i].value; } @@ -10822,8 +10892,17 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, num_scanline_blocks = 32; } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { num_scanline_blocks = 16; + +#if TINYEXR_USE_ZFP + tinyexr::ZFPCompressionParam zfp_compression_param; + if (!FindZFPCompressionParam(&zfp_compression_param, exr_header->custom_attributes, + int(exr_header->num_custom_attributes), err)) { + return TINYEXR_ERROR_INVALID_HEADER; + } +#endif } + int data_width = exr_header->data_window[2] - exr_header->data_window[0] + 1; int data_height = exr_header->data_window[3] - exr_header->data_window[1] + 1; @@ -11947,11 +12026,21 @@ int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header, return TINYEXR_ERROR_INVALID_ARGUMENT; } -#ifdef _WIN32 FILE *fp = NULL; - fopen_s(&fp, filename, "rb"); +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__MINGW32__) // MSVC, MinGW gcc or clang + errno_t errcode = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + // TODO(syoyo): return wfopen_s erro code + return TINYEXR_ERROR_CANT_OPEN_FILE; + } #else - FILE *fp = fopen(filename, "rb"); + // Unknown compiler + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); #endif if (!fp) { tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); @@ -12213,9 +12302,10 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, // Use ZFP compression parameter from custom attributes(if such a parameter // exists) { + std::string e; bool ret = tinyexr::FindZFPCompressionParam( &zfp_compression_param, exr_header->custom_attributes, - exr_header->num_custom_attributes); + exr_header->num_custom_attributes, &e); if (!ret) { // Use predefined compression parameter. @@ -12225,7 +12315,7 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, } #endif - // TOOD(LTE): C++11 thread + // TODO(LTE): C++11 thread // Use signed int since some OpenMP compiler doesn't allow unsigned type for // `parallel for` @@ -12538,14 +12628,23 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, } #endif -#ifdef _WIN32 FILE *fp = NULL; - fopen_s(&fp, filename, "wb"); +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__MINGW32__) // MSVC, MinGW gcc or clang + errno_t errcode = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"wb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename), err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } #else - FILE *fp = fopen(filename, "wb"); + // Unknown compiler + fp = fopen(filename, "wb"); +#endif +#else + fp = fopen(filename, "wb"); #endif if (!fp) { - tinyexr::SetErrorMessage("Cannot write a file", err); + tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename), err); return TINYEXR_ERROR_CANT_WRITE_FILE; } @@ -12577,10 +12676,20 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { return TINYEXR_ERROR_INVALID_ARGUMENT; } -#ifdef _MSC_VER +#ifdef _WIN32 FILE *fp = NULL; - errno_t errcode = fopen_s(&fp, filename, "rb"); - if ((0 != errcode) || (!fp)) { +#if defined(_MSC_VER) || defined(__MINGW32__) // MSVC, MinGW gcc or clang + errno_t errcode = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } +#else + // Unknown compiler + fp = fopen(filename, "rb"); +#endif + if (!fp) { tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename), err); return TINYEXR_ERROR_CANT_OPEN_FILE; @@ -13054,11 +13163,20 @@ int ParseEXRHeaderFromFile(EXRHeader *exr_header, const EXRVersion *exr_version, return TINYEXR_ERROR_INVALID_ARGUMENT; } -#ifdef _WIN32 FILE *fp = NULL; - fopen_s(&fp, filename, "rb"); +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__MINGW32__) // MSVC, MinGW gcc or clang + errno_t errcode = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_INVALID_FILE; + } #else - FILE *fp = fopen(filename, "rb"); + // Unknown compiler + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); #endif if (!fp) { tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); @@ -13174,11 +13292,20 @@ int ParseEXRMultipartHeaderFromFile(EXRHeader ***exr_headers, int *num_headers, return TINYEXR_ERROR_INVALID_ARGUMENT; } -#ifdef _WIN32 FILE *fp = NULL; - fopen_s(&fp, filename, "rb"); +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__MINGW32__) // MSVC, MinGW gcc or clang + errno_t errcode = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_INVALID_FILE; + } #else - FILE *fp = fopen(filename, "rb"); + // Unknown compiler + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); #endif if (!fp) { tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); @@ -13270,11 +13397,20 @@ int ParseEXRVersionFromFile(EXRVersion *version, const char *filename) { return TINYEXR_ERROR_INVALID_ARGUMENT; } -#ifdef _WIN32 FILE *fp = NULL; - fopen_s(&fp, filename, "rb"); +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__MINGW32__) // MSVC, MinGW gcc or clang + errno_t err = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (err != 0) { + // TODO(syoyo): return wfopen_s erro code + return TINYEXR_ERROR_CANT_OPEN_FILE; + } #else - FILE *fp = fopen(filename, "rb"); + // Unknown compiler + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); #endif if (!fp) { return TINYEXR_ERROR_CANT_OPEN_FILE; @@ -13408,11 +13544,20 @@ int LoadEXRMultipartImageFromFile(EXRImage *exr_images, return TINYEXR_ERROR_INVALID_ARGUMENT; } -#ifdef _WIN32 FILE *fp = NULL; - fopen_s(&fp, filename, "rb"); +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__MINGW32__) // MSVC, MinGW gcc or clang + errno_t errcode = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } #else - FILE *fp = fopen(filename, "rb"); + // Unknown compiler + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); #endif if (!fp) { tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); @@ -13582,5 +13727,5 @@ int SaveEXR(const float *data, int width, int height, int components, #pragma clang diagnostic pop #endif -#endif // TINYEXR_IMPLEMENTATION_DEIFNED +#endif // TINYEXR_IMPLEMENTATION_DEFINED #endif // TINYEXR_IMPLEMENTATION