test_tinyexr.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. #if defined(_WIN32)
  2. #ifndef NOMINMAX
  3. #define NOMINMAX
  4. #endif
  5. #include <tchar.h>
  6. #include <windows.h>
  7. #endif
  8. #include <cstdio>
  9. #include <cstdlib>
  10. #include <iostream>
  11. #include <vector>
  12. // Uncomment if you want to use system provided zlib.
  13. // #define TINYEXR_USE_MINIZ (0)
  14. // #include <zlib.h>
  15. #define TINYEXR_IMPLEMENTATION
  16. #include "tinyexr.h"
  17. #ifdef __clang__
  18. #if __has_warning("-Wzero-as-null-pointer-constant")
  19. #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
  20. #endif
  21. #endif
  22. #define SIMPLE_API_EXAMPLE
  23. //#define TEST_ZFP_COMPRESSION
  24. #ifdef SIMPLE_API_EXAMPLE
  25. #if 0
  26. static void
  27. SaveAsPFM(const char* filename, int width, int height, float* data)
  28. {
  29. #ifdef _WIN32
  30. FILE* fp = NULL;
  31. fopen_s(&fp, filename, "wb");
  32. #else
  33. FILE* fp = fopen(filename, "wb");
  34. #endif
  35. if (!fp) {
  36. fprintf(stderr, "failed to write a PFM file.\n");
  37. return;
  38. }
  39. fprintf(fp, "PF\n");
  40. fprintf(fp, "%d %d\n", width, height);
  41. fprintf(fp, "-1\n"); // -1: little endian, 1: big endian
  42. // RGBA -> RGB
  43. std::vector<float> rgb(static_cast<size_t>(width*height*3));
  44. for (size_t i = 0; i < static_cast<size_t>(width * height); i++) {
  45. rgb[3*i+0] = data[4*i+0];
  46. rgb[3*i+1] = data[4*i+1];
  47. rgb[3*i+2] = data[4*i+2];
  48. }
  49. fwrite(&rgb.at(0), sizeof(float), static_cast<size_t>(width * height * 3), fp);
  50. fclose(fp);
  51. }
  52. #endif
  53. #else
  54. static const char* GetPixelType(int id) {
  55. if (id == TINYEXR_PIXELTYPE_HALF) {
  56. return "HALF";
  57. } else if (id == TINYEXR_PIXELTYPE_FLOAT) {
  58. return "FLOAT";
  59. } else if (id == TINYEXR_PIXELTYPE_UINT) {
  60. return "UINT";
  61. }
  62. return "???";
  63. }
  64. // Simple tile -> scanline converter. Assumes FLOAT pixel type for all channels.
  65. static void TiledImageToScanlineImage(EXRImage* src, const EXRHeader* header) {
  66. assert(header->data_window.max_x - header->data_window.min_x + 1 >= 0);
  67. assert(header->data_window.max_y - header->data_window.min_y + 1 >= 0);
  68. size_t data_width =
  69. static_cast<size_t>(header->data_window.max_x - header->data_window.min_x + 1);
  70. size_t data_height =
  71. static_cast<size_t>(header->data_window.max_y - header->data_window.min_y + 1);
  72. src->images = static_cast<unsigned char**>(
  73. malloc(sizeof(float*) * static_cast<size_t>(header->num_channels)));
  74. for (size_t c = 0; c < static_cast<size_t>(header->num_channels); c++) {
  75. assert(header->pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT);
  76. src->images[c] = static_cast<unsigned char*>(
  77. malloc(sizeof(float) * data_width * data_height));
  78. memset(src->images[c], 0, sizeof(float) * data_width * data_height);
  79. }
  80. for (size_t tile_idx = 0; tile_idx < static_cast<size_t>(src->num_tiles);
  81. tile_idx++) {
  82. size_t sx = static_cast<size_t>(src->tiles[tile_idx].offset_x *
  83. header->tile_size_x);
  84. size_t sy = static_cast<size_t>(src->tiles[tile_idx].offset_y *
  85. header->tile_size_y);
  86. size_t ex = static_cast<size_t>(src->tiles[tile_idx].offset_x *
  87. header->tile_size_x +
  88. src->tiles[tile_idx].width);
  89. size_t ey = static_cast<size_t>(src->tiles[tile_idx].offset_y *
  90. header->tile_size_y +
  91. src->tiles[tile_idx].height);
  92. for (size_t c = 0; c < static_cast<size_t>(header->num_channels); c++) {
  93. float* dst_image = reinterpret_cast<float*>(src->images[c]);
  94. const float* src_image =
  95. reinterpret_cast<const float*>(src->tiles[tile_idx].images[c]);
  96. for (size_t y = 0; y < static_cast<size_t>(ey - sy); y++) {
  97. for (size_t x = 0; x < static_cast<size_t>(ex - sx); x++) {
  98. dst_image[(y + sy) * data_width + (x + sx)] =
  99. src_image[y * static_cast<size_t>(header->tile_size_x) + x];
  100. }
  101. }
  102. }
  103. }
  104. }
  105. #endif
  106. #if defined(_WIN32)
  107. #if defined(__MINGW32__)
  108. // __wgetmainargs is not defined in windows.h
  109. extern "C" int __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
  110. #endif
  111. // https://gist.github.com/trueroad/fb4d0c3f67285bf66804
  112. namespace {
  113. std::vector<char> utf16_to_utf8(const wchar_t* wc) {
  114. int size = WideCharToMultiByte(CP_UTF8, 0, wc, -1, NULL, 0, NULL, NULL);
  115. std::vector<char> retval(size);
  116. if (size) {
  117. WideCharToMultiByte(CP_UTF8, 0, wc, -1, retval.data(), retval.size(), NULL,
  118. NULL);
  119. } else
  120. retval.push_back('\0');
  121. return retval;
  122. }
  123. } // namespace
  124. #endif
  125. static int test_main(int argc, char** argv);
  126. #if defined(_WIN32)
  127. #if defined(__MINGW32__)
  128. int main() {
  129. wchar_t** wargv;
  130. wchar_t** wenpv;
  131. int argc = 0, si = 0;
  132. __wgetmainargs(&argc, &wargv, &wenpv, 1, &si);
  133. std::vector<std::vector<char> > argv_vvc(argc);
  134. std::vector<char*> argv_vc(argc);
  135. for (int i = 0; i < argc; i++) {
  136. argv_vvc.at(i) = utf16_to_utf8(wargv[i]);
  137. argv_vc.at(i) = argv_vvc.at(i).data();
  138. }
  139. // TODO(syoyo): envp
  140. return test_main(argc, argv_vc.data());
  141. }
  142. #else // Assume MSVC
  143. int _tmain(int argc, _TCHAR** wargv) {
  144. std::vector<std::vector<char> > argv_vvc(argc);
  145. std::vector<char*> argv_vc(argc);
  146. for (int i = 0; i < argc; i++) {
  147. #if defined(UNICODE) || defined(_UNICODE)
  148. argv_vvc.at(i) = utf16_to_utf8(wargv[i]);
  149. #else
  150. size_t slen = _tcslen(wargv[i]);
  151. std::vector<char> buf(slen + 1);
  152. memcpy(buf.data(), wargv[i], slen);
  153. buf[slen] = '\0';
  154. argv_vvc.at(i) = buf;
  155. #endif
  156. argv_vc.at(i) = argv_vvc.at(i).data();
  157. }
  158. return test_main(argc, argv_vc.data());
  159. }
  160. #endif
  161. #else
  162. int main(int argc, char** argv) { return test_main(argc, argv); }
  163. #endif
  164. int test_main(int argc, char** argv) {
  165. const char* outfilename = "output_test.exr";
  166. const char* err = NULL;
  167. if (argc < 2) {
  168. fprintf(stderr, "Needs input.exr.\n");
  169. exit(-1);
  170. }
  171. if (argc > 2) {
  172. outfilename = argv[2];
  173. }
  174. const char* input_filename = argv[1];
  175. #ifdef SIMPLE_API_EXAMPLE
  176. (void)outfilename;
  177. int width, height;
  178. float* image;
  179. int ret = IsEXR(input_filename);
  180. if (ret != TINYEXR_SUCCESS) {
  181. fprintf(stderr, "Header err. code %d\n", ret);
  182. exit(-1);
  183. }
  184. ret = LoadEXR(&image, &width, &height, input_filename, &err);
  185. if (ret != TINYEXR_SUCCESS) {
  186. if (err) {
  187. fprintf(stderr, "Load EXR err: %s(code %d)\n", err, ret);
  188. } else {
  189. fprintf(stderr, "Load EXR err: code = %d\n", ret);
  190. }
  191. FreeEXRErrorMessage(err);
  192. return ret;
  193. }
  194. // SaveAsPFM("output.pfm", width, height, image);
  195. ret = SaveEXR(image, width, height, 4 /* =RGBA*/,
  196. 1 /* = save as fp16 format */, "output.exr", &err);
  197. if (ret != TINYEXR_SUCCESS) {
  198. if (err) {
  199. fprintf(stderr, "Save EXR err: %s(code %d)\n", err, ret);
  200. } else {
  201. fprintf(stderr, "Failed to save EXR image. code = %d\n", ret);
  202. }
  203. }
  204. free(image);
  205. std::cout << "Wrote output.exr." << std::endl;
  206. #else
  207. EXRVersion exr_version;
  208. int ret = ParseEXRVersionFromFile(&exr_version, input_filename);
  209. if (ret != 0) {
  210. fprintf(stderr, "Invalid EXR file: %s\n", input_filename);
  211. return -1;
  212. }
  213. printf(
  214. "version: tiled = %d, long_name = %d, non_image = %d, multipart = %d\n",
  215. exr_version.tiled, exr_version.long_name, exr_version.non_image,
  216. exr_version.multipart);
  217. if (exr_version.multipart) {
  218. EXRHeader** exr_headers; // list of EXRHeader pointers.
  219. int num_exr_headers;
  220. ret = ParseEXRMultipartHeaderFromFile(&exr_headers, &num_exr_headers,
  221. &exr_version, argv[1], &err);
  222. if (ret != 0) {
  223. fprintf(stderr, "Parse EXR err: %s\n", err);
  224. return ret;
  225. }
  226. printf("num parts = %d\n", num_exr_headers);
  227. for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
  228. const EXRHeader& exr_header = *(exr_headers[i]);
  229. printf("Part: %lu\n", static_cast<unsigned long>(i));
  230. printf("dataWindow = %d, %d, %d, %d\n", exr_header.data_window.min_x,
  231. exr_header.data_window.min_y, exr_header.data_window.max_x,
  232. exr_header.data_window.max_y);
  233. printf("displayWindow = %d, %d, %d, %d\n", exr_header.display_window.min_x,
  234. exr_header.display_window.min_y, exr_header.display_window.max_x,
  235. exr_header.display_window.max_y);
  236. printf("screenWindowCenter = %f, %f\n",
  237. static_cast<double>(exr_header.screen_window_center[0]),
  238. static_cast<double>(exr_header.screen_window_center[1]));
  239. printf("screenWindowWidth = %f\n",
  240. static_cast<double>(exr_header.screen_window_width));
  241. printf("pixelAspectRatio = %f\n",
  242. static_cast<double>(exr_header.pixel_aspect_ratio));
  243. printf("lineOrder = %d\n", exr_header.line_order);
  244. if (exr_header.num_custom_attributes > 0) {
  245. printf("# of custom attributes = %d\n",
  246. exr_header.num_custom_attributes);
  247. for (int a = 0; a < exr_header.num_custom_attributes; a++) {
  248. printf(" [%d] name = %s, type = %s, size = %d\n", a,
  249. exr_header.custom_attributes[a].name,
  250. exr_header.custom_attributes[a].type,
  251. exr_header.custom_attributes[a].size);
  252. // if (strcmp(exr_header.custom_attributes[i].type, "float") == 0) {
  253. // printf(" value = %f\n", *reinterpret_cast<float
  254. // *>(exr_header.custom_attributes[i].value));
  255. //}
  256. }
  257. }
  258. }
  259. std::vector<EXRImage> images(static_cast<size_t>(num_exr_headers));
  260. for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
  261. InitEXRImage(&images[i]);
  262. }
  263. ret = LoadEXRMultipartImageFromFile(
  264. &images.at(0), const_cast<const EXRHeader**>(exr_headers),
  265. static_cast<unsigned int>(num_exr_headers), input_filename, &err);
  266. if (ret != 0) {
  267. fprintf(stderr, "Load EXR err: %s\n", err);
  268. FreeEXRErrorMessage(err);
  269. return ret;
  270. }
  271. printf("Loaded %d part images\n", num_exr_headers);
  272. printf(
  273. "There is no saving feature for multi-part images, thus just exit an "
  274. "application...\n");
  275. for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
  276. FreeEXRImage(&images.at(i));
  277. }
  278. for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
  279. FreeEXRHeader(exr_headers[i]);
  280. free(exr_headers[i]);
  281. }
  282. free(exr_headers);
  283. } else { // single-part EXR
  284. EXRHeader exr_header;
  285. InitEXRHeader(&exr_header);
  286. ret =
  287. ParseEXRHeaderFromFile(&exr_header, &exr_version, input_filename, &err);
  288. if (ret != 0) {
  289. fprintf(stderr, "Parse single-part EXR err: %s\n", err);
  290. FreeEXRErrorMessage(err);
  291. return ret;
  292. }
  293. printf("dataWindow = %d, %d, %d, %d\n", exr_header.data_window.min_x,
  294. exr_header.data_window.min_y, exr_header.data_window.max_x,
  295. exr_header.data_window.max_y);
  296. printf("displayWindow = %d, %d, %d, %d\n", exr_header.display_window.min_x,
  297. exr_header.display_window.min_y, exr_header.display_window.max_x,
  298. exr_header.display_window.max_y);
  299. printf("screenWindowCenter = %f, %f\n",
  300. static_cast<double>(exr_header.screen_window_center[0]),
  301. static_cast<double>(exr_header.screen_window_center[1]));
  302. printf("screenWindowWidth = %f\n",
  303. static_cast<double>(exr_header.screen_window_width));
  304. printf("pixelAspectRatio = %f\n",
  305. static_cast<double>(exr_header.pixel_aspect_ratio));
  306. printf("lineOrder = %d\n", exr_header.line_order);
  307. if (exr_header.num_custom_attributes > 0) {
  308. printf("# of custom attributes = %d\n", exr_header.num_custom_attributes);
  309. for (int i = 0; i < exr_header.num_custom_attributes; i++) {
  310. printf(" [%d] name = %s, type = %s, size = %d\n", i,
  311. exr_header.custom_attributes[i].name,
  312. exr_header.custom_attributes[i].type,
  313. exr_header.custom_attributes[i].size);
  314. // if (strcmp(exr_header.custom_attributes[i].type, "float") == 0) {
  315. // printf(" value = %f\n", *reinterpret_cast<float
  316. // *>(exr_header.custom_attributes[i].value));
  317. //}
  318. }
  319. }
  320. // Read HALF channel as FLOAT.
  321. for (int i = 0; i < exr_header.num_channels; i++) {
  322. if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
  323. exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
  324. }
  325. }
  326. EXRImage exr_image;
  327. InitEXRImage(&exr_image);
  328. ret = LoadEXRImageFromFile(&exr_image, &exr_header, input_filename, &err);
  329. if (ret != 0) {
  330. fprintf(stderr, "Load EXR err: %s\n", err);
  331. FreeEXRHeader(&exr_header);
  332. FreeEXRErrorMessage(err);
  333. return ret;
  334. }
  335. printf("EXR: %d x %d\n", exr_image.width, exr_image.height);
  336. for (int i = 0; i < exr_header.num_channels; i++) {
  337. printf("pixelType[%d]: %s\n", i, GetPixelType(exr_header.pixel_types[i]));
  338. printf("chan[%d] = %s\n", i, exr_header.channels[i].name);
  339. printf("requestedPixelType[%d]: %s\n", i,
  340. GetPixelType(exr_header.requested_pixel_types[i]));
  341. }
  342. #if 0 // example to write custom attribute
  343. int version_minor = 3;
  344. exr_header.num_custom_attributes = 1;
  345. exr_header.custom_attributes = reinterpret_cast<EXRAttribute *>(malloc(sizeof(EXRAttribute) * exr_header.custom_attributes));
  346. strcpy(exr_header.custom_attributes[0].name, "tinyexr_version_minor");
  347. exr_header.custom_attributes[0].name[strlen("tinyexr_version_minor")] = '\0';
  348. strcpy(exr_header.custom_attributes[0].type, "int");
  349. exr_header.custom_attributes[0].type[strlen("int")] = '\0';
  350. exr_header.custom_attributes[0].size = sizeof(int);
  351. exr_header.custom_attributes[0].value = (unsigned char*)malloc(sizeof(int));
  352. memcpy(exr_header.custom_attributes[0].value, &version_minor, sizeof(int));
  353. #endif
  354. if (exr_header.tiled) {
  355. TiledImageToScanlineImage(&exr_image, &exr_header);
  356. }
  357. exr_header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE;
  358. #ifdef TEST_ZFP_COMPRESSION
  359. // Assume input image is FLOAT pixel type.
  360. for (int i = 0; i < exr_header.num_channels; i++) {
  361. exr_header.channels[i].pixel_type = TINYEXR_PIXELTYPE_FLOAT;
  362. exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
  363. }
  364. unsigned char zfp_compression_type = TINYEXR_ZFP_COMPRESSIONTYPE_RATE;
  365. double zfp_compression_rate = 4;
  366. exr_header.num_custom_attributes = 2;
  367. strcpy(exr_header.custom_attributes[0].name, "zfpCompressionType");
  368. exr_header.custom_attributes[0].name[strlen("zfpCompressionType")] = '\0';
  369. exr_header.custom_attributes[0].size = 1;
  370. exr_header.custom_attributes[0].value =
  371. (unsigned char*)malloc(sizeof(unsigned char));
  372. exr_header.custom_attributes[0].value[0] = zfp_compression_type;
  373. strcpy(exr_header.custom_attributes[1].name, "zfpCompressionRate");
  374. exr_header.custom_attributes[1].name[strlen("zfpCompressionRate")] = '\0';
  375. exr_header.custom_attributes[1].size = sizeof(double);
  376. exr_header.custom_attributes[1].value =
  377. (unsigned char*)malloc(sizeof(double));
  378. memcpy(exr_header.custom_attributes[1].value, &zfp_compression_rate,
  379. sizeof(double));
  380. exr_header.compression_type = TINYEXR_COMPRESSIONTYPE_ZFP;
  381. #endif
  382. ret = SaveEXRImageToFile(&exr_image, &exr_header, outfilename, &err);
  383. if (ret != 0) {
  384. fprintf(stderr, "Save EXR err: %s\n", err);
  385. FreeEXRHeader(&exr_header);
  386. FreeEXRErrorMessage(err);
  387. return ret;
  388. }
  389. printf("Saved exr file. [ %s ] \n", outfilename);
  390. FreeEXRHeader(&exr_header);
  391. FreeEXRImage(&exr_image);
  392. }
  393. #endif
  394. return ret;
  395. }