Path: blob/master/modules/tinyexr/image_loader_tinyexr.cpp
10277 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"3536Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {37Vector<uint8_t> src_image;38uint64_t src_image_len = f->get_length();39ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);40src_image.resize(src_image_len);4142uint8_t *w = src_image.ptrw();4344f->get_buffer(&w[0], src_image_len);4546// Re-implementation of tinyexr's LoadEXRFromMemory using Godot types to store the Image data47// and Godot's error codes.48// When debugging after updating the thirdparty library, check that we're still in sync with49// their API usage in LoadEXRFromMemory.5051EXRVersion exr_version;52EXRImage exr_image;53EXRHeader exr_header;54const char *err = nullptr;5556InitEXRHeader(&exr_header);5758int ret = ParseEXRVersionFromMemory(&exr_version, w, src_image_len);59if (ret != TINYEXR_SUCCESS) {60return ERR_FILE_CORRUPT;61}6263ret = ParseEXRHeaderFromMemory(&exr_header, &exr_version, w, src_image_len, &err);64if (ret != TINYEXR_SUCCESS) {65if (err) {66ERR_PRINT(String(err));67FreeEXRErrorMessage(err);68}69return ERR_FILE_CORRUPT;70}7172// Read HALF channel as FLOAT. (GH-13490)73bool use_float16 = false;74for (int i = 0; i < exr_header.num_channels; i++) {75if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {76use_float16 = true;77exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;78}79}8081InitEXRImage(&exr_image);82ret = LoadEXRImageFromMemory(&exr_image, &exr_header, w, src_image_len, &err);83if (ret != TINYEXR_SUCCESS) {84if (err) {85ERR_PRINT(String(err));86FreeEXRErrorMessage(err);87}88return ERR_FILE_CORRUPT;89}9091// RGBA92int idxR = -1;93int idxG = -1;94int idxB = -1;95int idxA = -1;96for (int c = 0; c < exr_header.num_channels; c++) {97if (strcmp(exr_header.channels[c].name, "R") == 0) {98idxR = c;99} else if (strcmp(exr_header.channels[c].name, "G") == 0) {100idxG = c;101} else if (strcmp(exr_header.channels[c].name, "B") == 0) {102idxB = c;103} else if (strcmp(exr_header.channels[c].name, "A") == 0) {104idxA = c;105} else if (strcmp(exr_header.channels[c].name, "Y") == 0) {106idxR = c;107idxG = c;108idxB = c;109}110}111112// EXR image data loaded, now parse it into Godot-friendly image data113114Vector<uint8_t> imgdata;115Image::Format format;116int output_channels = 0;117118int channel_size = use_float16 ? 2 : 4;119if (idxA != -1) {120imgdata.resize(exr_image.width * exr_image.height * 4 * channel_size); //RGBA121format = use_float16 ? Image::FORMAT_RGBAH : Image::FORMAT_RGBAF;122output_channels = 4;123} else if (idxB != -1) {124ERR_FAIL_COND_V(idxG == -1, ERR_FILE_CORRUPT);125ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);126imgdata.resize(exr_image.width * exr_image.height * 3 * channel_size); //RGB127format = use_float16 ? Image::FORMAT_RGBH : Image::FORMAT_RGBF;128output_channels = 3;129} else if (idxG != -1) {130ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);131imgdata.resize(exr_image.width * exr_image.height * 2 * channel_size); //RG132format = use_float16 ? Image::FORMAT_RGH : Image::FORMAT_RGF;133output_channels = 2;134} else {135ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);136imgdata.resize(exr_image.width * exr_image.height * 1 * channel_size); //R137format = use_float16 ? Image::FORMAT_RH : Image::FORMAT_RF;138output_channels = 1;139}140141EXRTile single_image_tile;142int num_tiles;143int tile_width = 0;144int tile_height = 0;145146const EXRTile *exr_tiles;147148if (!exr_header.tiled) {149single_image_tile.images = exr_image.images;150single_image_tile.width = exr_image.width;151single_image_tile.height = exr_image.height;152single_image_tile.level_x = exr_image.width;153single_image_tile.level_y = exr_image.height;154single_image_tile.offset_x = 0;155single_image_tile.offset_y = 0;156157exr_tiles = &single_image_tile;158num_tiles = 1;159tile_width = exr_image.width;160tile_height = exr_image.height;161} else {162tile_width = exr_header.tile_size_x;163tile_height = exr_header.tile_size_y;164num_tiles = exr_image.num_tiles;165exr_tiles = exr_image.tiles;166}167168//print_line("reading format: " + Image::get_format_name(format));169{170uint8_t *wd = imgdata.ptrw();171uint16_t *iw16 = (uint16_t *)wd;172float *iw32 = (float *)wd;173174// Assume `out_rgba` have enough memory allocated.175for (int tile_index = 0; tile_index < num_tiles; tile_index++) {176const EXRTile &tile = exr_tiles[tile_index];177178int tw = tile.width;179int th = tile.height;180181const float *r_channel_start = reinterpret_cast<const float *>(tile.images[idxR]);182const float *g_channel_start = nullptr;183const float *b_channel_start = nullptr;184const float *a_channel_start = nullptr;185186if (idxG != -1) {187g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]);188}189if (idxB != -1) {190b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]);191}192if (idxA != -1) {193a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]);194}195196uint16_t *first_row_w16 = iw16 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;197float *first_row_w32 = iw32 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;198199for (int y = 0; y < th; y++) {200const float *r_channel = r_channel_start + y * tile_width;201const float *g_channel = nullptr;202const float *b_channel = nullptr;203const float *a_channel = nullptr;204if (g_channel_start) {205g_channel = g_channel_start + y * tile_width;206}207if (b_channel_start) {208b_channel = b_channel_start + y * tile_width;209}210if (a_channel_start) {211a_channel = a_channel_start + y * tile_width;212}213214if (use_float16) {215uint16_t *row_w = first_row_w16 + (y * exr_image.width * output_channels);216217for (int x = 0; x < tw; x++) {218Color color;219color.r = *r_channel++;220if (g_channel) {221color.g = *g_channel++;222}223if (b_channel) {224color.b = *b_channel++;225}226if (a_channel) {227color.a = *a_channel++;228}229230if (p_flags & FLAG_FORCE_LINEAR) {231color = color.srgb_to_linear();232}233234*row_w++ = Math::make_half_float(color.r);235if (g_channel) {236*row_w++ = Math::make_half_float(color.g);237}238if (b_channel) {239*row_w++ = Math::make_half_float(color.b);240}241if (a_channel) {242*row_w++ = Math::make_half_float(color.a);243}244}245} else {246float *row_w = first_row_w32 + (y * exr_image.width * output_channels);247248for (int x = 0; x < tw; x++) {249Color color;250color.r = *r_channel++;251if (g_channel) {252color.g = *g_channel++;253}254if (b_channel) {255color.b = *b_channel++;256}257if (a_channel) {258color.a = *a_channel++;259}260261if (p_flags & FLAG_FORCE_LINEAR) {262color = color.srgb_to_linear();263}264265*row_w++ = color.r;266if (g_channel) {267*row_w++ = color.g;268}269if (b_channel) {270*row_w++ = color.b;271}272if (a_channel) {273*row_w++ = color.a;274}275}276}277}278}279}280281p_image->set_data(exr_image.width, exr_image.height, false, format, imgdata);282283FreeEXRHeader(&exr_header);284FreeEXRImage(&exr_image);285286return OK;287}288289void ImageLoaderTinyEXR::get_recognized_extensions(List<String> *p_extensions) const {290p_extensions->push_back("exr");291}292293ImageLoaderTinyEXR::ImageLoaderTinyEXR() {294}295296297