#if defined(_WIN32)
|
|
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <tchar.h>
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
// Uncomment if you want to use system provided zlib.
|
|
// #define TINYEXR_USE_MINIZ (0)
|
|
// #include <zlib.h>
|
|
|
|
#define TINYEXR_IMPLEMENTATION
|
|
#include "tinyexr.h"
|
|
|
|
#ifdef __clang__
|
|
#if __has_warning("-Wzero-as-null-pointer-constant")
|
|
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
|
#endif
|
|
#endif
|
|
|
|
#define SIMPLE_API_EXAMPLE
|
|
//#define TEST_ZFP_COMPRESSION
|
|
|
|
#ifdef SIMPLE_API_EXAMPLE
|
|
|
|
#if 0
|
|
static void
|
|
SaveAsPFM(const char* filename, int width, int height, float* data)
|
|
{
|
|
#ifdef _WIN32
|
|
FILE* fp = NULL;
|
|
fopen_s(&fp, filename, "wb");
|
|
#else
|
|
FILE* fp = fopen(filename, "wb");
|
|
#endif
|
|
if (!fp) {
|
|
fprintf(stderr, "failed to write a PFM file.\n");
|
|
return;
|
|
}
|
|
|
|
fprintf(fp, "PF\n");
|
|
fprintf(fp, "%d %d\n", width, height);
|
|
fprintf(fp, "-1\n"); // -1: little endian, 1: big endian
|
|
|
|
// RGBA -> RGB
|
|
std::vector<float> rgb(static_cast<size_t>(width*height*3));
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(width * height); i++) {
|
|
rgb[3*i+0] = data[4*i+0];
|
|
rgb[3*i+1] = data[4*i+1];
|
|
rgb[3*i+2] = data[4*i+2];
|
|
}
|
|
|
|
fwrite(&rgb.at(0), sizeof(float), static_cast<size_t>(width * height * 3), fp);
|
|
|
|
fclose(fp);
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
|
|
static const char* GetPixelType(int id) {
|
|
if (id == TINYEXR_PIXELTYPE_HALF) {
|
|
return "HALF";
|
|
} else if (id == TINYEXR_PIXELTYPE_FLOAT) {
|
|
return "FLOAT";
|
|
} else if (id == TINYEXR_PIXELTYPE_UINT) {
|
|
return "UINT";
|
|
}
|
|
|
|
return "???";
|
|
}
|
|
|
|
// Simple tile -> scanline converter. Assumes FLOAT pixel type for all channels.
|
|
static void TiledImageToScanlineImage(EXRImage* src, const EXRHeader* header) {
|
|
assert(header->data_window.max_x - header->data_window.min_x + 1 >= 0);
|
|
assert(header->data_window.max_y - header->data_window.min_y + 1 >= 0);
|
|
size_t data_width =
|
|
static_cast<size_t>(header->data_window.max_x - header->data_window.min_x + 1);
|
|
size_t data_height =
|
|
static_cast<size_t>(header->data_window.max_y - header->data_window.min_y + 1);
|
|
|
|
src->images = static_cast<unsigned char**>(
|
|
malloc(sizeof(float*) * static_cast<size_t>(header->num_channels)));
|
|
for (size_t c = 0; c < static_cast<size_t>(header->num_channels); c++) {
|
|
assert(header->pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT);
|
|
src->images[c] = static_cast<unsigned char*>(
|
|
malloc(sizeof(float) * data_width * data_height));
|
|
memset(src->images[c], 0, sizeof(float) * data_width * data_height);
|
|
}
|
|
|
|
for (size_t tile_idx = 0; tile_idx < static_cast<size_t>(src->num_tiles);
|
|
tile_idx++) {
|
|
size_t sx = static_cast<size_t>(src->tiles[tile_idx].offset_x *
|
|
header->tile_size_x);
|
|
size_t sy = static_cast<size_t>(src->tiles[tile_idx].offset_y *
|
|
header->tile_size_y);
|
|
size_t ex = static_cast<size_t>(src->tiles[tile_idx].offset_x *
|
|
header->tile_size_x +
|
|
src->tiles[tile_idx].width);
|
|
size_t ey = static_cast<size_t>(src->tiles[tile_idx].offset_y *
|
|
header->tile_size_y +
|
|
src->tiles[tile_idx].height);
|
|
|
|
for (size_t c = 0; c < static_cast<size_t>(header->num_channels); c++) {
|
|
float* dst_image = reinterpret_cast<float*>(src->images[c]);
|
|
const float* src_image =
|
|
reinterpret_cast<const float*>(src->tiles[tile_idx].images[c]);
|
|
for (size_t y = 0; y < static_cast<size_t>(ey - sy); y++) {
|
|
for (size_t x = 0; x < static_cast<size_t>(ex - sx); x++) {
|
|
dst_image[(y + sy) * data_width + (x + sx)] =
|
|
src_image[y * static_cast<size_t>(header->tile_size_x) + x];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
|
|
#if defined(__MINGW32__)
|
|
// __wgetmainargs is not defined in windows.h
|
|
extern "C" int __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
|
|
#endif
|
|
|
|
// https://gist.github.com/trueroad/fb4d0c3f67285bf66804
|
|
namespace {
|
|
std::vector<char> utf16_to_utf8(const wchar_t* wc) {
|
|
int size = WideCharToMultiByte(CP_UTF8, 0, wc, -1, NULL, 0, NULL, NULL);
|
|
std::vector<char> retval(size);
|
|
if (size) {
|
|
WideCharToMultiByte(CP_UTF8, 0, wc, -1, retval.data(), retval.size(), NULL,
|
|
NULL);
|
|
} else
|
|
retval.push_back('\0');
|
|
return retval;
|
|
}
|
|
} // namespace
|
|
#endif
|
|
|
|
static int test_main(int argc, char** argv);
|
|
|
|
#if defined(_WIN32)
|
|
#if defined(__MINGW32__)
|
|
int main() {
|
|
wchar_t** wargv;
|
|
wchar_t** wenpv;
|
|
int argc = 0, si = 0;
|
|
__wgetmainargs(&argc, &wargv, &wenpv, 1, &si);
|
|
|
|
std::vector<std::vector<char> > argv_vvc(argc);
|
|
std::vector<char*> argv_vc(argc);
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
argv_vvc.at(i) = utf16_to_utf8(wargv[i]);
|
|
argv_vc.at(i) = argv_vvc.at(i).data();
|
|
}
|
|
|
|
// TODO(syoyo): envp
|
|
|
|
return test_main(argc, argv_vc.data());
|
|
}
|
|
#else // Assume MSVC
|
|
int _tmain(int argc, _TCHAR** wargv) {
|
|
std::vector<std::vector<char> > argv_vvc(argc);
|
|
std::vector<char*> argv_vc(argc);
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
argv_vvc.at(i) = utf16_to_utf8(wargv[i]);
|
|
#else
|
|
size_t slen = _tcslen(wargv[i]);
|
|
std::vector<char> buf(slen + 1);
|
|
memcpy(buf.data(), wargv[i], slen);
|
|
buf[slen] = '\0';
|
|
argv_vvc.at(i) = buf;
|
|
#endif
|
|
|
|
argv_vc.at(i) = argv_vvc.at(i).data();
|
|
}
|
|
|
|
return test_main(argc, argv_vc.data());
|
|
}
|
|
#endif
|
|
#else
|
|
int main(int argc, char** argv) { return test_main(argc, argv); }
|
|
#endif
|
|
|
|
int test_main(int argc, char** argv) {
|
|
const char* outfilename = "output_test.exr";
|
|
const char* err = NULL;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Needs input.exr.\n");
|
|
exit(-1);
|
|
}
|
|
|
|
if (argc > 2) {
|
|
outfilename = argv[2];
|
|
}
|
|
|
|
const char* input_filename = argv[1];
|
|
|
|
#ifdef SIMPLE_API_EXAMPLE
|
|
(void)outfilename;
|
|
int width, height;
|
|
float* image;
|
|
|
|
int ret = IsEXR(input_filename);
|
|
if (ret != TINYEXR_SUCCESS) {
|
|
fprintf(stderr, "Header err. code %d\n", ret);
|
|
exit(-1);
|
|
}
|
|
|
|
ret = LoadEXR(&image, &width, &height, input_filename, &err);
|
|
if (ret != TINYEXR_SUCCESS) {
|
|
if (err) {
|
|
fprintf(stderr, "Load EXR err: %s(code %d)\n", err, ret);
|
|
} else {
|
|
fprintf(stderr, "Load EXR err: code = %d\n", ret);
|
|
}
|
|
FreeEXRErrorMessage(err);
|
|
return ret;
|
|
}
|
|
// SaveAsPFM("output.pfm", width, height, image);
|
|
ret = SaveEXR(image, width, height, 4 /* =RGBA*/,
|
|
1 /* = save as fp16 format */, "output.exr", &err);
|
|
if (ret != TINYEXR_SUCCESS) {
|
|
if (err) {
|
|
fprintf(stderr, "Save EXR err: %s(code %d)\n", err, ret);
|
|
} else {
|
|
fprintf(stderr, "Failed to save EXR image. code = %d\n", ret);
|
|
}
|
|
}
|
|
free(image);
|
|
|
|
std::cout << "Wrote output.exr." << std::endl;
|
|
#else
|
|
|
|
EXRVersion exr_version;
|
|
|
|
int ret = ParseEXRVersionFromFile(&exr_version, input_filename);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Invalid EXR file: %s\n", input_filename);
|
|
return -1;
|
|
}
|
|
|
|
printf(
|
|
"version: tiled = %d, long_name = %d, non_image = %d, multipart = %d\n",
|
|
exr_version.tiled, exr_version.long_name, exr_version.non_image,
|
|
exr_version.multipart);
|
|
|
|
if (exr_version.multipart) {
|
|
EXRHeader** exr_headers; // list of EXRHeader pointers.
|
|
int num_exr_headers;
|
|
|
|
ret = ParseEXRMultipartHeaderFromFile(&exr_headers, &num_exr_headers,
|
|
&exr_version, argv[1], &err);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Parse EXR err: %s\n", err);
|
|
return ret;
|
|
}
|
|
|
|
printf("num parts = %d\n", num_exr_headers);
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
|
|
const EXRHeader& exr_header = *(exr_headers[i]);
|
|
|
|
printf("Part: %lu\n", static_cast<unsigned long>(i));
|
|
|
|
printf("dataWindow = %d, %d, %d, %d\n", exr_header.data_window.min_x,
|
|
exr_header.data_window.min_y, exr_header.data_window.max_x,
|
|
exr_header.data_window.max_y);
|
|
printf("displayWindow = %d, %d, %d, %d\n", exr_header.display_window.min_x,
|
|
exr_header.display_window.min_y, exr_header.display_window.max_x,
|
|
exr_header.display_window.max_y);
|
|
printf("screenWindowCenter = %f, %f\n",
|
|
static_cast<double>(exr_header.screen_window_center[0]),
|
|
static_cast<double>(exr_header.screen_window_center[1]));
|
|
printf("screenWindowWidth = %f\n",
|
|
static_cast<double>(exr_header.screen_window_width));
|
|
printf("pixelAspectRatio = %f\n",
|
|
static_cast<double>(exr_header.pixel_aspect_ratio));
|
|
printf("lineOrder = %d\n", exr_header.line_order);
|
|
|
|
if (exr_header.num_custom_attributes > 0) {
|
|
printf("# of custom attributes = %d\n",
|
|
exr_header.num_custom_attributes);
|
|
for (int a = 0; a < exr_header.num_custom_attributes; a++) {
|
|
printf(" [%d] name = %s, type = %s, size = %d\n", a,
|
|
exr_header.custom_attributes[a].name,
|
|
exr_header.custom_attributes[a].type,
|
|
exr_header.custom_attributes[a].size);
|
|
// if (strcmp(exr_header.custom_attributes[i].type, "float") == 0) {
|
|
// printf(" value = %f\n", *reinterpret_cast<float
|
|
// *>(exr_header.custom_attributes[i].value));
|
|
//}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<EXRImage> images(static_cast<size_t>(num_exr_headers));
|
|
for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
|
|
InitEXRImage(&images[i]);
|
|
}
|
|
|
|
ret = LoadEXRMultipartImageFromFile(
|
|
&images.at(0), const_cast<const EXRHeader**>(exr_headers),
|
|
static_cast<unsigned int>(num_exr_headers), input_filename, &err);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Load EXR err: %s\n", err);
|
|
FreeEXRErrorMessage(err);
|
|
return ret;
|
|
}
|
|
|
|
printf("Loaded %d part images\n", num_exr_headers);
|
|
printf(
|
|
"There is no saving feature for multi-part images, thus just exit an "
|
|
"application...\n");
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
|
|
FreeEXRImage(&images.at(i));
|
|
}
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(num_exr_headers); i++) {
|
|
FreeEXRHeader(exr_headers[i]);
|
|
free(exr_headers[i]);
|
|
}
|
|
free(exr_headers);
|
|
|
|
} else { // single-part EXR
|
|
|
|
EXRHeader exr_header;
|
|
InitEXRHeader(&exr_header);
|
|
|
|
ret =
|
|
ParseEXRHeaderFromFile(&exr_header, &exr_version, input_filename, &err);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Parse single-part EXR err: %s\n", err);
|
|
FreeEXRErrorMessage(err);
|
|
return ret;
|
|
}
|
|
|
|
printf("dataWindow = %d, %d, %d, %d\n", exr_header.data_window.min_x,
|
|
exr_header.data_window.min_y, exr_header.data_window.max_x,
|
|
exr_header.data_window.max_y);
|
|
printf("displayWindow = %d, %d, %d, %d\n", exr_header.display_window.min_x,
|
|
exr_header.display_window.min_y, exr_header.display_window.max_x,
|
|
exr_header.display_window.max_y);
|
|
printf("screenWindowCenter = %f, %f\n",
|
|
static_cast<double>(exr_header.screen_window_center[0]),
|
|
static_cast<double>(exr_header.screen_window_center[1]));
|
|
printf("screenWindowWidth = %f\n",
|
|
static_cast<double>(exr_header.screen_window_width));
|
|
printf("pixelAspectRatio = %f\n",
|
|
static_cast<double>(exr_header.pixel_aspect_ratio));
|
|
printf("lineOrder = %d\n", exr_header.line_order);
|
|
|
|
if (exr_header.num_custom_attributes > 0) {
|
|
printf("# of custom attributes = %d\n", exr_header.num_custom_attributes);
|
|
for (int i = 0; i < exr_header.num_custom_attributes; i++) {
|
|
printf(" [%d] name = %s, type = %s, size = %d\n", i,
|
|
exr_header.custom_attributes[i].name,
|
|
exr_header.custom_attributes[i].type,
|
|
exr_header.custom_attributes[i].size);
|
|
// if (strcmp(exr_header.custom_attributes[i].type, "float") == 0) {
|
|
// printf(" value = %f\n", *reinterpret_cast<float
|
|
// *>(exr_header.custom_attributes[i].value));
|
|
//}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
EXRImage exr_image;
|
|
InitEXRImage(&exr_image);
|
|
|
|
ret = LoadEXRImageFromFile(&exr_image, &exr_header, input_filename, &err);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Load EXR err: %s\n", err);
|
|
FreeEXRHeader(&exr_header);
|
|
FreeEXRErrorMessage(err);
|
|
return ret;
|
|
}
|
|
|
|
printf("EXR: %d x %d\n", exr_image.width, exr_image.height);
|
|
|
|
for (int i = 0; i < exr_header.num_channels; i++) {
|
|
printf("pixelType[%d]: %s\n", i, GetPixelType(exr_header.pixel_types[i]));
|
|
printf("chan[%d] = %s\n", i, exr_header.channels[i].name);
|
|
printf("requestedPixelType[%d]: %s\n", i,
|
|
GetPixelType(exr_header.requested_pixel_types[i]));
|
|
}
|
|
|
|
#if 0 // example to write custom attribute
|
|
int version_minor = 3;
|
|
exr_header.num_custom_attributes = 1;
|
|
exr_header.custom_attributes = reinterpret_cast<EXRAttribute *>(malloc(sizeof(EXRAttribute) * exr_header.custom_attributes));
|
|
strcpy(exr_header.custom_attributes[0].name, "tinyexr_version_minor");
|
|
exr_header.custom_attributes[0].name[strlen("tinyexr_version_minor")] = '\0';
|
|
strcpy(exr_header.custom_attributes[0].type, "int");
|
|
exr_header.custom_attributes[0].type[strlen("int")] = '\0';
|
|
exr_header.custom_attributes[0].size = sizeof(int);
|
|
exr_header.custom_attributes[0].value = (unsigned char*)malloc(sizeof(int));
|
|
memcpy(exr_header.custom_attributes[0].value, &version_minor, sizeof(int));
|
|
#endif
|
|
|
|
if (exr_header.tiled) {
|
|
TiledImageToScanlineImage(&exr_image, &exr_header);
|
|
}
|
|
|
|
exr_header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE;
|
|
|
|
#ifdef TEST_ZFP_COMPRESSION
|
|
// Assume input image is FLOAT pixel type.
|
|
for (int i = 0; i < exr_header.num_channels; i++) {
|
|
exr_header.channels[i].pixel_type = TINYEXR_PIXELTYPE_FLOAT;
|
|
exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
|
|
}
|
|
|
|
unsigned char zfp_compression_type = TINYEXR_ZFP_COMPRESSIONTYPE_RATE;
|
|
double zfp_compression_rate = 4;
|
|
exr_header.num_custom_attributes = 2;
|
|
strcpy(exr_header.custom_attributes[0].name, "zfpCompressionType");
|
|
exr_header.custom_attributes[0].name[strlen("zfpCompressionType")] = '\0';
|
|
exr_header.custom_attributes[0].size = 1;
|
|
exr_header.custom_attributes[0].value =
|
|
(unsigned char*)malloc(sizeof(unsigned char));
|
|
exr_header.custom_attributes[0].value[0] = zfp_compression_type;
|
|
|
|
strcpy(exr_header.custom_attributes[1].name, "zfpCompressionRate");
|
|
exr_header.custom_attributes[1].name[strlen("zfpCompressionRate")] = '\0';
|
|
exr_header.custom_attributes[1].size = sizeof(double);
|
|
exr_header.custom_attributes[1].value =
|
|
(unsigned char*)malloc(sizeof(double));
|
|
memcpy(exr_header.custom_attributes[1].value, &zfp_compression_rate,
|
|
sizeof(double));
|
|
exr_header.compression_type = TINYEXR_COMPRESSIONTYPE_ZFP;
|
|
#endif
|
|
|
|
ret = SaveEXRImageToFile(&exr_image, &exr_header, outfilename, &err);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Save EXR err: %s\n", err);
|
|
FreeEXRHeader(&exr_header);
|
|
FreeEXRErrorMessage(err);
|
|
return ret;
|
|
}
|
|
printf("Saved exr file. [ %s ] \n", outfilename);
|
|
|
|
FreeEXRHeader(&exr_header);
|
|
FreeEXRImage(&exr_image);
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|