Răsfoiți Sursa

tinyexr: Fixes warning C5208 (VS2019 16.6.0)

Emmanuel Julien (Movida) 5 ani în urmă
părinte
comite
7af1497aeb
2 a modificat fișierele cu 603 adăugiri și 184 ștergeri
  1. 373 99
      3rdparty/tinyexr/README.md
  2. 230 85
      3rdparty/tinyexr/tinyexr.h

+ 373 - 99
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:
-
-* 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.
+# Features
+
+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)
+
+# Requirements
+
+* C++ compiler(C++11 recommended. C++03 may work)
 
-# Use case 
+# 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);
+
+  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 EXR from a file.
+### Reading layered RGB(A) EXR file.
+
+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
+  }
 
 ```
-  const char* input = "asakusa.exr";
-  const char* err;
 
-  EXRImage exrImage;
-  InitEXRImage(&exrImage);
+### Loading Singlepart EXR from a file.
+
+Scanline and tiled format are supported.
+
+```cpp
+  // 1. Read EXR version.
+  EXRVersion exr_version;
 
-  int ret = ParseMultiChannelEXRHeaderFromFile(&exrImage, input, &err);
+  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<EXRImage> images(num_exr_headers);
+  for (int i =0; i < num_exr_headers; i++) {
+    InitEXRImage(&images[i]);
+  }
+
+  ret = LoadEXRMultipartImageFromFile(&images.at(0), const_cast<const EXRHeader**>(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<float> 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([email protected])
+Syoyo Fujita ([email protected])
 
 ## 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!

+ 230 - 85
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 <windows.h>  // for UTF-8
+
+#endif
 
 #include <algorithm>
 #include <cassert>
@@ -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 [email protected]) 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<tinyexr::ChannelInfo> channels;
   std::vector<EXRAttribute> attributes;
 
@@ -7430,7 +7457,7 @@ typedef struct {
     header_len = 0;
     compression_type = 0;
   }
-} HeaderInfo;
+};
 
 static bool ReadChannelInfo(std::vector<ChannelInfo> &channels,
                             const std::vector<unsigned char> &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<char>(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<int>(attributes[i].value[0]);
-
-      foundType = true;
+    if ((strcmp(attributes[i].name, "zfpCompressionType") == 0)) {
+      if (attributes[i].size == 1) {
+        param->type = static_cast<int>(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 &param) {
-  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<void *>(const_cast<unsigned char *>(src)),
-                   zfp_type_float, dst_width, dst_num_lines * num_channels);
+                   zfp_type_float, static_cast<unsigned int>(dst_width), static_cast<unsigned int>(dst_num_lines) * static_cast<unsigned int>(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<unsigned char> *outBuf, unsigned int *outSize,
+static bool CompressZfp(std::vector<unsigned char> *outBuf, unsigned int *outSize,
                  const float *inPtr, int width, int num_lines, int num_channels,
                  const ZFPCompressionParam &param) {
   zfp_stream *zfp = NULL;
@@ -9635,22 +9691,22 @@ bool CompressZfp(std::vector<unsigned char> *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<void *>(const_cast<float *>(inPtr)),
-                       zfp_type_float, width, num_lines * num_channels);
+                       zfp_type_float, static_cast<unsigned int>(width), static_cast<unsigned int>(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<unsigned char> *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<unsigned char> *outBuf, unsigned int *outSize,
   }
 
   zfp_stream_flush(zfp);
-  (*outSize) = zfp_stream_compressed_size(zfp);
+  (*outSize) = static_cast<unsigned int>(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
+  // Unknown compiler
+  fp = fopen(filename, "wb");
+#endif
 #else
-  FILE *fp = fopen(filename, "wb");
+  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