From b2a9b5fdbed77426559cacc0839ea89e9107324e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Sun, 25 Nov 2018 20:31:28 -0800 Subject: [PATCH] Updated tinyexr. --- 3rdparty/tinyexr/tinyexr.h | 351 +++++++++++++++++++++++-------------- 1 file changed, 221 insertions(+), 130 deletions(-) diff --git a/3rdparty/tinyexr/tinyexr.h b/3rdparty/tinyexr/tinyexr.h index 7ef09af..3255bbc 100644 --- a/3rdparty/tinyexr/tinyexr.h +++ b/3rdparty/tinyexr/tinyexr.h @@ -116,6 +116,8 @@ extern "C" { #define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-7) #define TINYEXR_ERROR_INVALID_HEADER (-8) #define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-9) +#define TINYEXR_ERROR_CANT_WRITE_FILE (-10) +#define TINYEXR_ERROR_SERIALZATION_FAILED (-11) // @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf } @@ -279,9 +281,12 @@ extern int LoadEXR(float **out_rgba, int *width, int *height, // Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero // value. // Save image as fp32(FLOAT) format when `save_as_fp16` is 0. +// Use ZIP compression by default. +// Returns negative value and may set error string in `err` when there's an +// error extern int SaveEXR(const float *data, const int width, const int height, const int components, const int save_as_fp16, - const char *filename); + const char *filename, const char **err); // Initialize EXRHeader struct extern void InitEXRHeader(EXRHeader *exr_header); @@ -400,9 +405,9 @@ extern int SaveEXRImageToFile(const EXRImage *image, // Saves multi-channel, single-frame OpenEXR image to a memory. // Image is compressed using EXRImage.compression value. -// Return the number of bytes if succes. -// Returns negative value and may set error string in `err` when there's an -// error +// Return the number of bytes if success. +// Return zero and will set error string in `err` when there's an +// error. // When there was an error message, Application must free `err` with // FreeEXRErrorMessage() extern size_t SaveEXRImageToMemory(const EXRImage *image, @@ -529,15 +534,23 @@ namespace miniz { #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" #endif + #if __has_warning("-Wmacro-redefined") #pragma clang diagnostic ignored "-Wmacro-redefined" #endif + #if __has_warning("-Wcast-qual") #pragma clang diagnostic ignored "-Wcast-qual" #endif + #if __has_warning("-Wzero-as-null-pointer-constant") #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #endif + +#if __has_warning("-Wtautological-constant-compare") +#pragma clang diagnostic ignored "-Wtautological-constant-compare" +#endif + #endif /* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP @@ -7253,7 +7266,21 @@ static bool ReadAttribute(std::string *name, std::string *type, tinyexr::swap4(reinterpret_cast(&data_len)); if (data_len == 0) { - return false; + if ((*type).compare("string") == 0) { + // Accept empty string attribute. + + marker += sizeof(uint32_t); + size -= sizeof(uint32_t); + + *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t); + + data->resize(1); + (*data)[0] = '\0'; + + return true; + } else { + return false; + } } marker += sizeof(uint32_t); @@ -9587,6 +9614,7 @@ bool CompressZfp(std::vector *outBuf, unsigned int *outSize, // ----------------------------------------------------------------- // +// TODO(syoyo): Refactor function arguments. static bool DecodePixelData(/* out */ unsigned char **out_images, const int *requested_pixel_types, const unsigned char *data_ptr, size_t data_len, @@ -9636,6 +9664,8 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, FP16 hf; // hf.u = line_ptr[u]; + // use `cpy` to avoid unaligned memory access when compiler's + // optimization is on. tinyexr::cpy2(&(hf.u), line_ptr + u); tinyexr::swap2(reinterpret_cast(&hf.u)); @@ -10070,37 +10100,76 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, #endif } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) { for (size_t c = 0; c < num_channels; c++) { - if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { - const unsigned short *line_ptr = - reinterpret_cast( - data_ptr + - c * static_cast(width) * sizeof(unsigned short)); + for (size_t v = 0; v < static_cast(num_lines); v++) { + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + const unsigned short *line_ptr = + reinterpret_cast( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast(width)); - if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { - unsigned short *outLine = - reinterpret_cast(out_images[c]); - if (line_order == 0) { - outLine += y * x_stride; + if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { + unsigned short *outLine = + reinterpret_cast(out_images[c]); + if (line_order == 0) { + outLine += (size_t(y) + v) * size_t(x_stride); + } else { + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); + } + + for (int u = 0; u < width; u++) { + tinyexr::FP16 hf; + + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast(&hf.u)); + + outLine[u] = hf.u; + } + } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { + float *outLine = reinterpret_cast(out_images[c]); + if (line_order == 0) { + outLine += (size_t(y) + v) * size_t(x_stride); + } else { + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); + } + + if (reinterpret_cast(line_ptr + width) > + (data_ptr + data_len)) { + // Insufficient data size + return false; + } + + for (int u = 0; u < width; u++) { + tinyexr::FP16 hf; + + // address may not be aliged. use byte-wise copy for safety.#76 + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast(&hf.u)); + + tinyexr::FP32 f32 = half_to_float(hf); + + outLine[u] = f32.f; + } } else { - outLine += (height - 1 - y) * x_stride; + assert(0); + return false; } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + const float *line_ptr = reinterpret_cast( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast(width)); - for (int u = 0; u < width; u++) { - tinyexr::FP16 hf; - - // hf.u = line_ptr[u]; - tinyexr::cpy2(&(hf.u), line_ptr + u); - - tinyexr::swap2(reinterpret_cast(&hf.u)); - - outLine[u] = hf.u; - } - } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { float *outLine = reinterpret_cast(out_images[c]); if (line_order == 0) { - outLine += y * x_stride; + outLine += (size_t(y) + v) * size_t(x_stride); } else { - outLine += (height - 1 - y) * x_stride; + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); } if (reinterpret_cast(line_ptr + width) > @@ -10110,71 +10179,41 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } for (int u = 0; u < width; u++) { - tinyexr::FP16 hf; + float val; + tinyexr::cpy4(&val, line_ptr + u); - // address may not be aliged. use byte-wise copy for safety.#76 - // hf.u = line_ptr[u]; - tinyexr::cpy2(&(hf.u), line_ptr + u); + tinyexr::swap4(reinterpret_cast(&val)); - tinyexr::swap2(reinterpret_cast(&hf.u)); - - tinyexr::FP32 f32 = half_to_float(hf); - - outLine[u] = f32.f; + outLine[u] = val; } - } else { - assert(0); - return false; - } - } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { - const float *line_ptr = reinterpret_cast( - data_ptr + c * static_cast(width) * sizeof(float)); + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + const unsigned int *line_ptr = reinterpret_cast( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast(width)); - float *outLine = reinterpret_cast(out_images[c]); - if (line_order == 0) { - outLine += y * x_stride; - } else { - outLine += (height - 1 - y) * x_stride; - } - - if (reinterpret_cast(line_ptr + width) > - (data_ptr + data_len)) { - // Insufficient data size - return false; - } - - for (int u = 0; u < width; u++) { - float val; - tinyexr::cpy4(&val, line_ptr + u); - - tinyexr::swap4(reinterpret_cast(&val)); - - outLine[u] = val; - } - } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { - const unsigned int *line_ptr = reinterpret_cast( - data_ptr + c * static_cast(width) * sizeof(unsigned int)); - - unsigned int *outLine = reinterpret_cast(out_images[c]); - if (line_order == 0) { - outLine += y * x_stride; - } else { - outLine += (height - 1 - y) * x_stride; - } - - for (int u = 0; u < width; u++) { - if (reinterpret_cast(line_ptr + u) >= - (data_ptr + data_len)) { - // Corrupsed data? - return false; + unsigned int *outLine = + reinterpret_cast(out_images[c]); + if (line_order == 0) { + outLine += (size_t(y) + v) * size_t(x_stride); + } else { + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); } - unsigned int val; - tinyexr::cpy4(&val, line_ptr + u); + for (int u = 0; u < width; u++) { + if (reinterpret_cast(line_ptr + u) >= + (data_ptr + data_len)) { + // Corrupsed data? + return false; + } - tinyexr::swap4(reinterpret_cast(&val)); + unsigned int val; + tinyexr::cpy4(&val, line_ptr + u); - outLine[u] = val; + tinyexr::swap4(reinterpret_cast(&val)); + + outLine[u] = val; + } } } } @@ -10680,7 +10719,8 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) { static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, const std::vector &offsets, - const unsigned char *head, const size_t size, std::string *err) { + const unsigned char *head, const size_t size, + std::string *err) { int num_channels = exr_header->num_channels; int num_scanline_blocks = 1; @@ -11045,7 +11085,6 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, free(exr_image->images); exr_image->images = NULL; } - } return ret; @@ -11240,9 +11279,7 @@ int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version, } if (size < tinyexr::kEXRVersionSize) { - tinyexr::SetErrorMessage( - "Insufficient header/data size.\n", - err); + tinyexr::SetErrorMessage("Insufficient header/data size.\n", err); return TINYEXR_ERROR_INVALID_DATA; } @@ -11347,18 +11384,53 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height, malloc(4 * sizeof(float) * static_cast(exr_image.width) * static_cast(exr_image.height))); - for (int i = 0; i < exr_image.width * exr_image.height; i++) { - (*out_rgba)[4 * i + 0] = - reinterpret_cast(exr_image.images)[idxR][i]; - (*out_rgba)[4 * i + 1] = - reinterpret_cast(exr_image.images)[idxG][i]; - (*out_rgba)[4 * i + 2] = - reinterpret_cast(exr_image.images)[idxB][i]; - if (idxA != -1) { - (*out_rgba)[4 * i + 3] = - reinterpret_cast(exr_image.images)[idxA][i]; - } else { - (*out_rgba)[4 * i + 3] = 1.0; + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = + exr_image.tiles[it].offset_x * exr_header.tile_size_x + i; + const int jj = + exr_image.tiles[it].offset_y * exr_header.tile_size_y + j; + const int idx = ii + jj * exr_image.width; + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast(src)[idxR][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast(src)[idxG][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast(src)[idxB][srcIdx]; + if (idxA != -1) { + (*out_rgba)[4 * idx + 3] = + reinterpret_cast(src)[idxA][srcIdx]; + } else { + (*out_rgba)[4 * idx + 3] = 1.0; + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + (*out_rgba)[4 * i + 0] = + reinterpret_cast(exr_image.images)[idxR][i]; + (*out_rgba)[4 * i + 1] = + reinterpret_cast(exr_image.images)[idxG][i]; + (*out_rgba)[4 * i + 2] = + reinterpret_cast(exr_image.images)[idxB][i]; + if (idxA != -1) { + (*out_rgba)[4 * i + 3] = + reinterpret_cast(exr_image.images)[idxA][i]; + } else { + (*out_rgba)[4 * i + 3] = 1.0; + } } } @@ -11396,7 +11468,8 @@ int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header, fseek(fp, 0, SEEK_SET); if (filesize < 16) { - tinyexr::SetErrorMessage("File size too short " + std::string(filename), err); + tinyexr::SetErrorMessage("File size too short " + std::string(filename), + err); return TINYEXR_ERROR_INVALID_FILE; } @@ -11442,7 +11515,7 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, if (exr_image == NULL || memory_out == NULL || exr_header->compression_type < 0) { tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToMemory", err); - return 0; // @fixme + return 0; } #if !TINYEXR_USE_PIZ @@ -11613,8 +11686,6 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, sizeof( tinyexr::tinyexr_int64); // sizeof(header) + sizeof(offsetTable) - std::vector data; - std::vector > data_list( static_cast(num_blocks)); std::vector channel_offset_list( @@ -11853,9 +11924,9 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { #if TINYEXR_USE_PIZ unsigned int bufLen = - 1024 + static_cast( - 1.2 * static_cast( - buf.size())); // @fixme { compute good bound. } + 8192 + static_cast( + 2 * static_cast( + buf.size())); // @fixme { compute good bound. } std::vector block(bufLen); unsigned int outSize = static_cast(block.size()); @@ -11914,13 +11985,12 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, } // omp parallel for (size_t i = 0; i < static_cast(num_blocks); i++) { - data.insert(data.end(), data_list[i].begin(), data_list[i].end()); - offsets[i] = offset; tinyexr::swap8(reinterpret_cast(&offsets[i])); offset += data_list[i].size(); } + size_t totalSize = static_cast(offset); { memory.insert( memory.end(), reinterpret_cast(&offsets.at(0)), @@ -11928,14 +11998,21 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, sizeof(tinyexr::tinyexr_uint64) * static_cast(num_blocks)); } - { memory.insert(memory.end(), data.begin(), data.end()); } + if ( memory.size() == 0 ) { + tinyexr::SetErrorMessage("Output memory size is zero", err); + return 0; + } - assert(memory.size() > 0); - - (*memory_out) = static_cast(malloc(memory.size())); + (*memory_out) = static_cast(malloc(totalSize)); memcpy((*memory_out), &memory.at(0), memory.size()); + unsigned char *memory_ptr = *memory_out + memory.size(); - return memory.size(); // OK + for (size_t i = 0; i < static_cast(num_blocks); i++) { + memcpy(memory_ptr, &data_list[i].at(0), data_list[i].size()); + memory_ptr += data_list[i].size(); + } + + return totalSize; // OK } int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, @@ -11950,7 +12027,7 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { tinyexr::SetErrorMessage("PIZ compression is not supported in this build", err); - return 0; + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; } #endif @@ -11958,7 +12035,7 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { tinyexr::SetErrorMessage("ZFP compression is not supported in this build", err); - return 0; + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; } #endif @@ -11970,19 +12047,28 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, #endif if (!fp) { tinyexr::SetErrorMessage("Cannot write a file", err); - return TINYEXR_ERROR_CANT_OPEN_FILE; + return TINYEXR_ERROR_CANT_WRITE_FILE; } unsigned char *mem = NULL; size_t mem_size = SaveEXRImageToMemory(exr_image, exr_header, &mem, err); + if (mem_size == 0) { + return TINYEXR_ERROR_SERIALZATION_FAILED; + } + size_t written_size = 0; if ((mem_size > 0) && mem) { - fwrite(mem, 1, mem_size, fp); + written_size = fwrite(mem, 1, mem_size, fp); } free(mem); fclose(fp); + if (written_size != mem_size) { + tinyexr::SetErrorMessage("Cannot write a file", err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } + return TINYEXR_SUCCESS; } @@ -12515,8 +12601,7 @@ int ParseEXRMultipartHeaderFromMemory(EXRHeader ***exr_headers, } if (size < tinyexr::kEXRVersionSize) { - tinyexr::SetErrorMessage( - "Data size too short", err); + tinyexr::SetErrorMessage("Data size too short", err); return TINYEXR_ERROR_INVALID_DATA; } @@ -12852,20 +12937,27 @@ int LoadEXRMultipartImageFromFile(EXRImage *exr_images, } int SaveEXR(const float *data, int width, int height, int components, - const int save_as_fp16, const char *outfilename) { + const int save_as_fp16, const char *outfilename, const char **err) { if ((components == 1) || components == 3 || components == 4) { // OK } else { + std::stringstream ss; + ss << "Unsupported component value : " << components << std::endl; + + tinyexr::SetErrorMessage(ss.str(), err); return TINYEXR_ERROR_INVALID_ARGUMENT; } - // Assume at least 16x16 pixels. - if (width < 16) return TINYEXR_ERROR_INVALID_ARGUMENT; - if (height < 16) return TINYEXR_ERROR_INVALID_ARGUMENT; - EXRHeader header; InitEXRHeader(&header); + if ((width < 16) && (height < 16)) { + // No compression for small image. + header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE; + } else { + header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP; + } + EXRImage image; InitEXRImage(&image); @@ -12971,8 +13063,7 @@ int SaveEXR(const float *data, int width, int height, int components, } } - const char *err; - int ret = SaveEXRImageToFile(&image, &header, outfilename, &err); + int ret = SaveEXRImageToFile(&image, &header, outfilename, err); if (ret != TINYEXR_SUCCESS) { return ret; }