Path: blob/master/modules/tinyexr/image_loader_tinyexr.cpp
20837 views
/**************************************************************************/1/* image_loader_tinyexr.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "image_loader_tinyexr.h"3132#include <zlib.h> // Should come before including tinyexr.3334#include "thirdparty/tinyexr/tinyexr.h"3536#include "core/io/file_access_memory.h"3738Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {39Vector<uint8_t> src_image;40uint64_t src_image_len = f->get_length();41ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);42src_image.resize(src_image_len);4344uint8_t *w = src_image.ptrw();4546f->get_buffer(&w[0], src_image_len);4748// Re-implementation of tinyexr's LoadEXRFromMemory using Godot types to store the Image data49// and Godot's error codes.50// When debugging after updating the thirdparty library, check that we're still in sync with51// their API usage in LoadEXRFromMemory.5253EXRVersion exr_version;54EXRImage exr_image;55EXRHeader exr_header;56const char *err = nullptr;5758InitEXRHeader(&exr_header);5960int ret = ParseEXRVersionFromMemory(&exr_version, w, src_image_len);61if (ret != TINYEXR_SUCCESS) {62return ERR_FILE_CORRUPT;63}6465ret = ParseEXRHeaderFromMemory(&exr_header, &exr_version, w, src_image_len, &err);66if (ret != TINYEXR_SUCCESS) {67if (err) {68ERR_PRINT(String(err));69FreeEXRErrorMessage(err);70}71return ERR_FILE_CORRUPT;72}7374// Read HALF channel as FLOAT. (GH-13490)75bool use_float16 = false;76for (int i = 0; i < exr_header.num_channels; i++) {77if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {78use_float16 = true;79exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;80}81}8283InitEXRImage(&exr_image);84ret = LoadEXRImageFromMemory(&exr_image, &exr_header, w, src_image_len, &err);85if (ret != TINYEXR_SUCCESS) {86if (err) {87ERR_PRINT(String(err));88FreeEXRErrorMessage(err);89}90return ERR_FILE_CORRUPT;91}9293// RGBA94int idxR = -1;95int idxG = -1;96int idxB = -1;97int idxA = -1;98for (int c = 0; c < exr_header.num_channels; c++) {99if (strcmp(exr_header.channels[c].name, "R") == 0) {100idxR = c;101} else if (strcmp(exr_header.channels[c].name, "G") == 0) {102idxG = c;103} else if (strcmp(exr_header.channels[c].name, "B") == 0) {104idxB = c;105} else if (strcmp(exr_header.channels[c].name, "A") == 0) {106idxA = c;107} else if (strcmp(exr_header.channels[c].name, "Y") == 0) {108idxR = c;109idxG = c;110idxB = c;111}112}113114// EXR image data loaded, now parse it into Godot-friendly image data115116Vector<uint8_t> imgdata;117Image::Format format;118int output_channels = 0;119120int channel_size = use_float16 ? 2 : 4;121if (idxA != -1) {122imgdata.resize(exr_image.width * exr_image.height * 4 * channel_size); //RGBA123format = use_float16 ? Image::FORMAT_RGBAH : Image::FORMAT_RGBAF;124output_channels = 4;125} else if (idxB != -1) {126ERR_FAIL_COND_V(idxG == -1, ERR_FILE_CORRUPT);127ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);128imgdata.resize(exr_image.width * exr_image.height * 3 * channel_size); //RGB129format = use_float16 ? Image::FORMAT_RGBH : Image::FORMAT_RGBF;130output_channels = 3;131} else if (idxG != -1) {132ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);133imgdata.resize(exr_image.width * exr_image.height * 2 * channel_size); //RG134format = use_float16 ? Image::FORMAT_RGH : Image::FORMAT_RGF;135output_channels = 2;136} else {137ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);138imgdata.resize(exr_image.width * exr_image.height * 1 * channel_size); //R139format = use_float16 ? Image::FORMAT_RH : Image::FORMAT_RF;140output_channels = 1;141}142143EXRTile single_image_tile;144int num_tiles;145int tile_width = 0;146int tile_height = 0;147148const EXRTile *exr_tiles;149150if (!exr_header.tiled) {151single_image_tile.images = exr_image.images;152single_image_tile.width = exr_image.width;153single_image_tile.height = exr_image.height;154single_image_tile.level_x = exr_image.width;155single_image_tile.level_y = exr_image.height;156single_image_tile.offset_x = 0;157single_image_tile.offset_y = 0;158159exr_tiles = &single_image_tile;160num_tiles = 1;161tile_width = exr_image.width;162tile_height = exr_image.height;163} else {164tile_width = exr_header.tile_size_x;165tile_height = exr_header.tile_size_y;166num_tiles = exr_image.num_tiles;167exr_tiles = exr_image.tiles;168}169170//print_line("reading format: " + Image::get_format_name(format));171{172uint8_t *wd = imgdata.ptrw();173uint16_t *iw16 = (uint16_t *)wd;174float *iw32 = (float *)wd;175176// Assume `out_rgba` have enough memory allocated.177for (int tile_index = 0; tile_index < num_tiles; tile_index++) {178const EXRTile &tile = exr_tiles[tile_index];179180int tw = tile.width;181int th = tile.height;182183const float *r_channel_start = reinterpret_cast<const float *>(tile.images[idxR]);184const float *g_channel_start = nullptr;185const float *b_channel_start = nullptr;186const float *a_channel_start = nullptr;187188if (idxG != -1) {189g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]);190}191if (idxB != -1) {192b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]);193}194if (idxA != -1) {195a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]);196}197198uint16_t *first_row_w16 = iw16 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;199float *first_row_w32 = iw32 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;200201for (int y = 0; y < th; y++) {202const float *r_channel = r_channel_start + y * tile_width;203const float *g_channel = nullptr;204const float *b_channel = nullptr;205const float *a_channel = nullptr;206if (g_channel_start) {207g_channel = g_channel_start + y * tile_width;208}209if (b_channel_start) {210b_channel = b_channel_start + y * tile_width;211}212if (a_channel_start) {213a_channel = a_channel_start + y * tile_width;214}215216if (use_float16) {217uint16_t *row_w = first_row_w16 + (y * exr_image.width * output_channels);218219for (int x = 0; x < tw; x++) {220Color color;221color.r = *r_channel++;222if (g_channel) {223color.g = *g_channel++;224}225if (b_channel) {226color.b = *b_channel++;227}228if (a_channel) {229color.a = *a_channel++;230}231232if (p_flags & FLAG_FORCE_LINEAR) {233color = color.srgb_to_linear();234}235236*row_w++ = Math::make_half_float(color.r);237if (g_channel) {238*row_w++ = Math::make_half_float(color.g);239}240if (b_channel) {241*row_w++ = Math::make_half_float(color.b);242}243if (a_channel) {244*row_w++ = Math::make_half_float(color.a);245}246}247} else {248float *row_w = first_row_w32 + (y * exr_image.width * output_channels);249250for (int x = 0; x < tw; x++) {251Color color;252color.r = *r_channel++;253if (g_channel) {254color.g = *g_channel++;255}256if (b_channel) {257color.b = *b_channel++;258}259if (a_channel) {260color.a = *a_channel++;261}262263if (p_flags & FLAG_FORCE_LINEAR) {264color = color.srgb_to_linear();265}266267*row_w++ = color.r;268if (g_channel) {269*row_w++ = color.g;270}271if (b_channel) {272*row_w++ = color.b;273}274if (a_channel) {275*row_w++ = color.a;276}277}278}279}280}281}282283p_image->set_data(exr_image.width, exr_image.height, false, format, imgdata);284285FreeEXRHeader(&exr_header);286FreeEXRImage(&exr_image);287288return OK;289}290291void ImageLoaderTinyEXR::get_recognized_extensions(List<String> *p_extensions) const {292p_extensions->push_back("exr");293}294295static Ref<Image> _tinyexr_mem_loader_func(const uint8_t *p_exr, int p_size) {296Ref<FileAccessMemory> memfile;297memfile.instantiate();298Error open_memfile_error = memfile->open_custom(p_exr, p_size);299ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for EXR image buffer.");300301Ref<Image> img;302img.instantiate();303Error load_error = ImageLoaderTinyEXR().load_image(img, memfile, false, 1.0f);304ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load EXR image.");305return img;306}307308ImageLoaderTinyEXR::ImageLoaderTinyEXR() {309Image::_exr_mem_loader_func = _tinyexr_mem_loader_func;310}311312313