🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

575 lines
18 KiB

  1. # Tiny OpenEXR image library.
  2. [![Total alerts](https://img.shields.io/lgtm/alerts/g/syoyo/tinyexr.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/syoyo/tinyexr/alerts/)
  3. ![Example](https://github.com/syoyo/tinyexr/blob/master/asakusa.png?raw=true)
  4. [![AppVeyor build status](https://ci.appveyor.com/api/projects/status/k07ftfe4ph057qau/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyexr/branch/master)
  5. [![Travis build Status](https://travis-ci.org/syoyo/tinyexr.svg)](https://travis-ci.org/syoyo/tinyexr)
  6. [![Coverity Scan Build Status](https://scan.coverity.com/projects/5827/badge.svg)](https://scan.coverity.com/projects/5827)
  7. `tinyexr` is a small, single header-only library to load and save OpenEXR (.exr) images.
  8. `tinyexr` is written in portable C++ (no library dependency except for STL), thus `tinyexr` is good to embed into your application.
  9. To use `tinyexr`, simply copy `tinyexr.h`, `miniz.c` and `miniz.h`(for zlib. You can use system-installed zlib instead of miniz, or the zlib implementation included in `stb_image[_write].h`. Controlled with `TINYEXR_USE_MINIZ` and `TINYEXR_USE_STB_ZLIB` compile flags) into your project.
  10. # Features
  11. Current status of `tinyexr` is:
  12. - OpenEXR v1 image
  13. - [x] Scanline format
  14. - [x] Tiled format
  15. - [x] Tile format with no LoD (load).
  16. - [x] Tile format with LoD (load).
  17. - [x] Tile format with no LoD (save).
  18. - [x] Tile format with LoD (save).
  19. - [x] Custom attributes
  20. - OpenEXR v2 image
  21. - [ ] Multipart format
  22. - [x] Load multi-part image
  23. - [x] Save multi-part image
  24. - [ ] Load multi-part deep image
  25. - [ ] Save multi-part deep image
  26. - OpenEXR v2 deep image
  27. - [x] Loading scanline + ZIPS + HALF or FLOAT pixel type.
  28. - Compression
  29. - [x] NONE
  30. - [x] RLE
  31. - [x] ZIP
  32. - [x] ZIPS
  33. - [x] PIZ
  34. - [x] ZFP (tinyexr extension)
  35. - [ ] B44?
  36. - [ ] B44A?
  37. - [ ] PIX24?
  38. - [ ] DWA (not planned, patent encumbered)
  39. - Line order.
  40. - [x] Increasing, decreasing (load)
  41. - [ ] Random?
  42. - [x] Increasing (save)
  43. - [ ] decreasing (save)
  44. - Pixel format (UINT, FLOAT).
  45. - [x] UINT, FLOAT (load)
  46. - [x] UINT, FLOAT (deep load)
  47. - [x] UINT, FLOAT (save)
  48. - [ ] UINT, FLOAT (deep save)
  49. - Support for big endian machine.
  50. - [x] Loading scanline image
  51. - [x] Saving scanline image
  52. - [x] Loading multi-part channel EXR (not tested)
  53. - [x] Saving multi-part channel EXR (not tested)
  54. - [ ] Loading deep image
  55. - [ ] Saving deep image
  56. - Optimization
  57. - [x] C++11 thread loading
  58. - [ ] C++11 thread saving
  59. - [ ] ISPC?
  60. - [x] OpenMP multi-threading in EXR loading.
  61. - [x] OpenMP multi-threading in EXR saving.
  62. - [ ] OpenMP multi-threading in deep image loading.
  63. - [ ] OpenMP multi-threading in deep image saving.
  64. * C interface.
  65. * You can easily write language bindings (e.g. golang)
  66. # Supported platform
  67. * [x] x86-64
  68. * [x] Windows 7 or later
  69. * [x] Linux(posix) system
  70. * [x] macOS
  71. * [x] AARCH64
  72. * [x] aarch64 linux(e.g. Raspberry Pi)
  73. * [x] Android
  74. * [x] iOS
  75. * [x] macOS
  76. * [ ] RISC-V(Should work)
  77. * [x] Big endian machine(not maintained, but should work)
  78. * SPARC, PowerPC, ...
  79. * [x] WebAssembly(JavaScript)
  80. * Loader only(See ![js](experimental/js/))
  81. * [x] Python binding
  82. * Loader only https://pypi.org/project/pytinyexr/
  83. # Requirements
  84. * C++ compiler(C++11 recommended. C++03 may work)
  85. # Use case
  86. ## New TinyEXR (v0.9.5+)
  87. * Godot. Multi-platform 2D and 3D game engine https://godotengine.org/
  88. * Filament. PBR engine(used in a converter tool). https://github.com/google/filament
  89. * PyEXR. Loading OpenEXR (.exr) images using Python. https://github.com/ialhashim/PyEXR
  90. * The-Forge. The Forge Cross-Platform Rendering Framework PC, Linux, Ray Tracing, macOS / iOS, Android, XBOX, PS4 https://github.com/ConfettiFX/The-Forge
  91. * psdr-cuda. Path-space differentiable renderer. https://github.com/uci-rendering/psdr-cuda
  92. * Studying Microfacets BSDFs https://virtualgonio.pages.xlim.fr/
  93. * Your project here!
  94. ## Older TinyEXR (v0.9.0)
  95. * mallie https://github.com/lighttransport/mallie
  96. * Cinder 0.9.0 https://libcinder.org/notes/v0.9.0
  97. * Piccante (develop branch) http://piccantelib.net/
  98. * Your project here!
  99. ## Examples
  100. * [examples/deepview/](examples/deepview) Deep image view
  101. * [examples/rgbe2exr/](examples/rgbe2exr) .hdr to EXR converter
  102. * [examples/exr2rgbe/](examples/exr2rgbe) EXR to .hdr converter
  103. * [examples/ldr2exr/](examples/exr2rgbe) LDR to EXR converter
  104. * [examples/exr2ldr/](examples/exr2ldr) EXR to LDR converter
  105. * [examples/exr2fptiff/](examples/exr2fptiff) EXR to 32bit floating point TIFF converter
  106. * for 32bit floating point TIFF to EXR convert, see https://github.com/syoyo/tinydngloader/tree/master/examples/fptiff2exr
  107. * [examples/cube2longlat/](examples/cube2longlat) Cubemap to longlat (equirectangler) converter
  108. ## Experimental
  109. * [experimental/js/](experimental/js) JavaScript port using Emscripten
  110. ## Usage
  111. NOTE: **API is still subject to change**. See the source code for details.
  112. Include `tinyexr.h` with `TINYEXR_IMPLEMENTATION` flag (do this only for **one** .cc file).
  113. ```cpp
  114. //Please include your own zlib-compatible API header before
  115. //including `tinyexr.h` when you disable `TINYEXR_USE_MINIZ`
  116. //#define TINYEXR_USE_MINIZ 0
  117. //#include "zlib.h"
  118. //Or, if your project uses `stb_image[_write].h`, use their
  119. //zlib implementation:
  120. //#define TINYEXR_USE_STB_ZLIB 1
  121. #define TINYEXR_IMPLEMENTATION
  122. #include "tinyexr.h"
  123. ```
  124. ### Compile flags
  125. * `TINYEXR_USE_MINIZ` Use miniz (default = 1). Please include `zlib.h` header before `tinyexr.h` if you disable miniz support(e.g. use system's zlib).
  126. * `TINYEXR_USE_STB_ZLIB` Use zlib from `stb_image[_write].h` instead of miniz or the system's zlib (default = 0).
  127. * `TINYEXR_USE_PIZ` Enable PIZ compression support (default = 1)
  128. * `TINYEXR_USE_ZFP` Enable ZFP compression supoort (TinyEXR extension, default = 0)
  129. * `TINYEXR_USE_THREAD` Enable threaded loading using C++11 thread (Requires C++11 compiler, default = 0)
  130. * `TINYEXR_USE_OPENMP` Enable OpenMP threading support (default = 1 if `_OPENMP` is defined)
  131. * Use `TINYEXR_USE_OPENMP=0` to force disable OpenMP code path even if OpenMP is available/enabled in the compiler.
  132. ### Quickly reading RGB(A) EXR file.
  133. ```cpp
  134. const char* input = "asakusa.exr";
  135. float* out; // width * height * RGBA
  136. int width;
  137. int height;
  138. const char* err = NULL; // or nullptr in C++11
  139. int ret = LoadEXR(&out, &width, &height, input, &err);
  140. if (ret != TINYEXR_SUCCESS) {
  141. if (err) {
  142. fprintf(stderr, "ERR : %s\n", err);
  143. FreeEXRErrorMessage(err); // release memory of error message.
  144. }
  145. } else {
  146. ...
  147. free(out); // release memory of image data
  148. }
  149. ```
  150. ### Reading layered RGB(A) EXR file.
  151. If you want to read EXR image with layer info (channel has a name with delimiter `.`), please use `LoadEXRWithLayer` API.
  152. You need to know layer name in advance (e.g. through `EXRLayers` API).
  153. ```cpp
  154. const char* input = ...;
  155. const char* layer_name = "diffuse"; // or use EXRLayers to get list of layer names in .exr
  156. float* out; // width * height * RGBA
  157. int width;
  158. int height;
  159. const char* err = NULL; // or nullptr in C++11
  160. // will read `diffuse.R`, `diffuse.G`, `diffuse.B`, (`diffuse.A`) channels
  161. int ret = LoadEXRWithLayer(&out, &width, &height, input, layer_name, &err);
  162. if (ret != TINYEXR_SUCCESS) {
  163. if (err) {
  164. fprintf(stderr, "ERR : %s\n", err);
  165. FreeEXRErrorMessage(err); // release memory of error message.
  166. }
  167. } else {
  168. ...
  169. free(out); // release memory of image data
  170. }
  171. ```
  172. ### Loading Singlepart EXR from a file.
  173. Scanline and tiled format are supported.
  174. ```cpp
  175. // 1. Read EXR version.
  176. EXRVersion exr_version;
  177. int ret = ParseEXRVersionFromFile(&exr_version, argv[1]);
  178. if (ret != 0) {
  179. fprintf(stderr, "Invalid EXR file: %s\n", argv[1]);
  180. return -1;
  181. }
  182. if (exr_version.multipart) {
  183. // must be multipart flag is false.
  184. return -1;
  185. }
  186. // 2. Read EXR header
  187. EXRHeader exr_header;
  188. InitEXRHeader(&exr_header);
  189. const char* err = NULL; // or `nullptr` in C++11 or later.
  190. ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, argv[1], &err);
  191. if (ret != 0) {
  192. fprintf(stderr, "Parse EXR err: %s\n", err);
  193. FreeEXRErrorMessage(err); // free's buffer for an error message
  194. return ret;
  195. }
  196. // // Read HALF channel as FLOAT.
  197. // for (int i = 0; i < exr_header.num_channels; i++) {
  198. // if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
  199. // exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
  200. // }
  201. // }
  202. EXRImage exr_image;
  203. InitEXRImage(&exr_image);
  204. ret = LoadEXRImageFromFile(&exr_image, &exr_header, argv[1], &err);
  205. if (ret != 0) {
  206. fprintf(stderr, "Load EXR err: %s\n", err);
  207. FreeEXRHeader(&exr_header);
  208. FreeEXRErrorMessage(err); // free's buffer for an error message
  209. return ret;
  210. }
  211. // 3. Access image data
  212. // `exr_image.images` will be filled when EXR is scanline format.
  213. // `exr_image.tiled` will be filled when EXR is tiled format.
  214. // 4. Free image data
  215. FreeEXRImage(&exr_image);
  216. FreeEXRHeader(&exr_header);
  217. ```
  218. ### Loading Multipart EXR from a file.
  219. Scanline and tiled format are supported.
  220. ```cpp
  221. // 1. Read EXR version.
  222. EXRVersion exr_version;
  223. int ret = ParseEXRVersionFromFile(&exr_version, argv[1]);
  224. if (ret != 0) {
  225. fprintf(stderr, "Invalid EXR file: %s\n", argv[1]);
  226. return -1;
  227. }
  228. if (!exr_version.multipart) {
  229. // must be multipart flag is true.
  230. return -1;
  231. }
  232. // 2. Read EXR headers in the EXR.
  233. EXRHeader **exr_headers; // list of EXRHeader pointers.
  234. int num_exr_headers;
  235. const char *err = NULL; // or nullptr in C++11 or later
  236. // Memory for EXRHeader is allocated inside of ParseEXRMultipartHeaderFromFile,
  237. ret = ParseEXRMultipartHeaderFromFile(&exr_headers, &num_exr_headers, &exr_version, argv[1], &err);
  238. if (ret != 0) {
  239. fprintf(stderr, "Parse EXR err: %s\n", err);
  240. FreeEXRErrorMessage(err); // free's buffer for an error message
  241. return ret;
  242. }
  243. printf("num parts = %d\n", num_exr_headers);
  244. // 3. Load images.
  245. // Prepare array of EXRImage.
  246. std::vector<EXRImage> images(num_exr_headers);
  247. for (int i =0; i < num_exr_headers; i++) {
  248. InitEXRImage(&images[i]);
  249. }
  250. ret = LoadEXRMultipartImageFromFile(&images.at(0), const_cast<const EXRHeader**>(exr_headers), num_exr_headers, argv[1], &err);
  251. if (ret != 0) {
  252. fprintf(stderr, "Parse EXR err: %s\n", err);
  253. FreeEXRErrorMessage(err); // free's buffer for an error message
  254. return ret;
  255. }
  256. printf("Loaded %d part images\n", num_exr_headers);
  257. // 4. Access image data
  258. // `exr_image.images` will be filled when EXR is scanline format.
  259. // `exr_image.tiled` will be filled when EXR is tiled format.
  260. // 5. Free images
  261. for (int i =0; i < num_exr_headers; i++) {
  262. FreeEXRImage(&images.at(i));
  263. }
  264. // 6. Free headers.
  265. for (int i =0; i < num_exr_headers; i++) {
  266. FreeEXRHeader(exr_headers[i]);
  267. free(exr_headers[i]);
  268. }
  269. free(exr_headers);
  270. ```
  271. Saving Scanline EXR file.
  272. ```cpp
  273. // See `examples/rgbe2exr/` for more details.
  274. bool SaveEXR(const float* rgb, int width, int height, const char* outfilename) {
  275. EXRHeader header;
  276. InitEXRHeader(&header);
  277. EXRImage image;
  278. InitEXRImage(&image);
  279. image.num_channels = 3;
  280. std::vector<float> images[3];
  281. images[0].resize(width * height);
  282. images[1].resize(width * height);
  283. images[2].resize(width * height);
  284. // Split RGBRGBRGB... into R, G and B layer
  285. for (int i = 0; i < width * height; i++) {
  286. images[0][i] = rgb[3*i+0];
  287. images[1][i] = rgb[3*i+1];
  288. images[2][i] = rgb[3*i+2];
  289. }
  290. float* image_ptr[3];
  291. image_ptr[0] = &(images[2].at(0)); // B
  292. image_ptr[1] = &(images[1].at(0)); // G
  293. image_ptr[2] = &(images[0].at(0)); // R
  294. image.images = (unsigned char**)image_ptr;
  295. image.width = width;
  296. image.height = height;
  297. header.num_channels = 3;
  298. header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
  299. // Must be (A)BGR order, since most of EXR viewers expect this channel order.
  300. strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0';
  301. strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0';
  302. strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0';
  303. header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
  304. header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
  305. for (int i = 0; i < header.num_channels; i++) {
  306. header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
  307. header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR
  308. }
  309. const char* err = NULL; // or nullptr in C++11 or later.
  310. int ret = SaveEXRImageToFile(&image, &header, outfilename, &err);
  311. if (ret != TINYEXR_SUCCESS) {
  312. fprintf(stderr, "Save EXR err: %s\n", err);
  313. FreeEXRErrorMessage(err); // free's buffer for an error message
  314. return ret;
  315. }
  316. printf("Saved exr file. [ %s ] \n", outfilename);
  317. free(rgb);
  318. free(header.channels);
  319. free(header.pixel_types);
  320. free(header.requested_pixel_types);
  321. }
  322. ```
  323. Reading deep image EXR file.
  324. See `example/deepview` for actual usage.
  325. ```cpp
  326. const char* input = "deepimage.exr";
  327. const char* err = NULL; // or nullptr
  328. DeepImage deepImage;
  329. int ret = LoadDeepEXR(&deepImage, input, &err);
  330. // access to each sample in the deep pixel.
  331. for (int y = 0; y < deepImage.height; y++) {
  332. int sampleNum = deepImage.offset_table[y][deepImage.width-1];
  333. for (int x = 0; x < deepImage.width-1; x++) {
  334. int s_start = deepImage.offset_table[y][x];
  335. int s_end = deepImage.offset_table[y][x+1];
  336. if (s_start >= sampleNum) {
  337. continue;
  338. }
  339. s_end = (s_end < sampleNum) ? s_end : sampleNum;
  340. for (int s = s_start; s < s_end; s++) {
  341. float val = deepImage.image[depthChan][y][s];
  342. ...
  343. }
  344. }
  345. }
  346. ```
  347. ### deepview
  348. `examples/deepview` is simple deep image viewer in OpenGL.
  349. ![DeepViewExample](https://github.com/syoyo/tinyexr/blob/master/examples/deepview/deepview_screencast.gif?raw=true)
  350. ## TinyEXR extension
  351. ### ZFP
  352. #### NOTE
  353. TinyEXR adds ZFP compression as an experimemtal support (Linux and MacOSX only).
  354. 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.
  355. #### Setup
  356. Checkout zfp repo as an submodule.
  357. $ git submodule update --init
  358. #### Build
  359. Then build ZFP
  360. $ cd deps/ZFP
  361. $ mkdir -p lib # Create `lib` directory if not exist
  362. $ make
  363. Set `1` to `TINYEXT_USE_ZFP` define in `tinyexr.h`
  364. Build your app with linking `deps/ZFP/lib/libzfp.a`
  365. #### ZFP attribute
  366. For ZFP EXR image, the following attribute must exist in its EXR image.
  367. * `zfpCompressionType` (uchar).
  368. * 0 = fixed rate compression
  369. * 1 = precision based variable rate compression
  370. * 2 = accuracy based variable rate compression
  371. And the one of following attributes must exist in EXR, depending on the `zfpCompressionType` value.
  372. * `zfpCompressionRate` (double)
  373. * Specifies compression rate for fixed rate compression.
  374. * `zfpCompressionPrecision` (int32)
  375. * Specifies the number of bits for precision based variable rate compression.
  376. * `zfpCompressionTolerance` (double)
  377. * Specifies the tolerance value for accuracy based variable rate compression.
  378. #### Note on ZFP compression.
  379. At least ZFP code itself works well on big endian machine.
  380. ## Unit tests
  381. See `test/unit` directory.
  382. ## TODO
  383. Contribution is welcome!
  384. - [ ] Compression
  385. - [ ] B44?
  386. - [ ] B44A?
  387. - [ ] PIX24?
  388. - [ ] Custom attributes
  389. - [x] Normal image (EXR 1.x)
  390. - [ ] Deep image (EXR 2.x)
  391. - [ ] JavaScript library (experimental, using Emscripten)
  392. - [x] LoadEXRFromMemory
  393. - [ ] SaveMultiChannelEXR
  394. - [ ] Deep image save/load
  395. - [ ] Write from/to memory buffer.
  396. - [ ] Deep image save/load
  397. - [ ] Tile format.
  398. - [x] Tile format with no LoD (load).
  399. - [ ] Tile format with LoD (load).
  400. - [ ] Tile format with no LoD (save).
  401. - [ ] Tile format with LoD (save).
  402. - [ ] Support for custom compression type.
  403. - [x] zfp compression (Not in OpenEXR spec, though)
  404. - [ ] zstd?
  405. - [x] Multi-channel.
  406. - [ ] Multi-part (EXR2.0)
  407. - [x] Load multi-part image
  408. - [ ] Load multi-part deep image
  409. - [ ] Line order.
  410. - [x] Increasing, decreasing (load)
  411. - [ ] Random?
  412. - [ ] Increasing, decreasing (save)
  413. - [ ] Pixel format (UINT, FLOAT).
  414. - [x] UINT, FLOAT (load)
  415. - [x] UINT, FLOAT (deep load)
  416. - [x] UINT, FLOAT (save)
  417. - [ ] UINT, FLOAT (deep save)
  418. - [ ] Support for big endian machine.
  419. - [ ] Loading multi-part channel EXR
  420. - [ ] Saving multi-part channel EXR
  421. - [ ] Loading deep image
  422. - [ ] Saving deep image
  423. - [ ] Optimization
  424. - [ ] ISPC?
  425. - [x] OpenMP multi-threading in EXR loading.
  426. - [x] OpenMP multi-threading in EXR saving.
  427. - [ ] OpenMP multi-threading in deep image loading.
  428. - [ ] OpenMP multi-threading in deep image saving.
  429. ## Python bindings
  430. `pytinyexr` is available: https://pypi.org/project/pytinyexr/ (loading only as of 0.9.1)
  431. ## Similar or related projects
  432. * miniexr: https://github.com/aras-p/miniexr (Write OpenEXR)
  433. * stb_image_resize.h: https://github.com/nothings/stb (Good for HDR image resizing)
  434. ## License
  435. 3-clause BSD
  436. `tinyexr` uses miniz, which is developed by Rich Geldreich <richgel99@gmail.com>, and licensed under public domain.
  437. `tinyexr` tools uses stb, which is licensed under public domain: https://github.com/nothings/stb
  438. `tinyexr` uses some code from OpenEXR, which is licensed under 3-clause BSD license.
  439. ## Author(s)
  440. Syoyo Fujita (syoyo@lighttransport.com)
  441. ## Contributor(s)
  442. * Matt Ebb (http://mattebb.com): deep image example. Thanks!
  443. * Matt Pharr (http://pharr.org/matt/): Testing tinyexr with OpenEXR(IlmImf). Thanks!
  444. * Andrew Bell (https://github.com/andrewfb) & Richard Eakin (https://github.com/richardeakin): Improving TinyEXR API. Thanks!
  445. * Mike Wong (https://github.com/mwkm): ZIPS compression support in loading. Thanks!