tinyexr: Fixes warning C5208 (VS2019 16.6.0)

This commit is contained in:
Emmanuel Julien (Movida)
2020-06-16 12:33:36 +02:00
parent 9854bc55bb
commit 7af1497aeb
2 changed files with 603 additions and 184 deletions

View File

@@ -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)
# 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<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(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!

View File

@@ -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 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<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)) {
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,7 +10892,16 @@ 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