diff --git a/core/io/image.cpp b/core/io/image.cpp index 45f9599cbe3..14b2a955265 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -105,6 +105,7 @@ ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr; ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_exr_mem_loader_func = nullptr; // External VRAM compression function pointers. @@ -3577,6 +3578,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer); + ClassDB::bind_method(D_METHOD("load_exr_from_buffer", "buffer"), &Image::load_exr_from_buffer); ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0)); @@ -4052,6 +4054,14 @@ Error Image::load_jpg_from_buffer(const Vector &p_array) { return _load_from_buffer(p_array, _jpg_mem_loader_func); } +Error Image::load_exr_from_buffer(const Vector &p_array) { + ERR_FAIL_NULL_V_MSG( + _exr_mem_loader_func, + ERR_UNAVAILABLE, + "The TinyEXR module isn't enabled. Recompile the Godot editor or export template binary with the `tinyexr_export_templates=yes` SCons option."); + return _load_from_buffer(p_array, _exr_mem_loader_func); +} + Error Image::load_webp_from_buffer(const Vector &p_array) { return _load_from_buffer(p_array, _webp_mem_loader_func); } diff --git a/core/io/image.h b/core/io/image.h index 992c43597df..b23076dc47c 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -201,6 +201,7 @@ public: static ImageMemLoadFunc _bmp_mem_loader_func; static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func; static ImageMemLoadFunc _ktx_mem_loader_func; + static ImageMemLoadFunc _exr_mem_loader_func; // External VRAM compression function pointers. @@ -403,6 +404,7 @@ public: Error load_tga_from_buffer(const Vector &p_array); Error load_bmp_from_buffer(const Vector &p_array); Error load_ktx_from_buffer(const Vector &p_array); + Error load_exr_from_buffer(const Vector &p_array); Error load_svg_from_buffer(const Vector &p_array, float scale = 1.0); Error load_svg_from_string(const String &p_svg_str, float scale = 1.0); diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 4421318be70..520a3489f8f 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -346,6 +346,13 @@ [b]Note:[/b] This method is only available in engine builds with the BMP module enabled. By default, the BMP module is enabled, but it can be disabled at build-time using the [code]module_bmp_enabled=no[/code] SCons option. + + + + + Loads an image from the binary contents of a OpenEXR file. + + @@ -464,7 +471,6 @@ Saves the image as an EXR file to [param path]. If [param grayscale] is [code]true[/code] and the image has only one channel, it will be saved explicitly as monochrome rather than one red channel. This function will return [constant ERR_UNAVAILABLE] if Godot was compiled without the TinyEXR module. - [b]Note:[/b] The TinyEXR module is disabled in non-editor builds, which means [method save_exr] will return [constant ERR_UNAVAILABLE] when it is called from an exported project. @@ -472,7 +478,6 @@ Saves the image as an EXR file to a byte array. If [param grayscale] is [code]true[/code] and the image has only one channel, it will be saved explicitly as monochrome rather than one red channel. This function will return an empty byte array if Godot was compiled without the TinyEXR module. - [b]Note:[/b] The TinyEXR module is disabled in non-editor builds, which means [method save_exr] will return an empty byte array when it is called from an exported project. diff --git a/modules/tinyexr/config.py b/modules/tinyexr/config.py index eb565b85b90..d22f9454ed2 100644 --- a/modules/tinyexr/config.py +++ b/modules/tinyexr/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return env.editor_build + return True def configure(env): diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 2a9d4323d88..7ba54e5cf10 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -34,6 +34,8 @@ #include "thirdparty/tinyexr/tinyexr.h" +#include "core/io/file_access_memory.h" + Error ImageLoaderTinyEXR::load_image(Ref p_image, Ref f, BitField p_flags, float p_scale) { Vector src_image; uint64_t src_image_len = f->get_length(); @@ -291,5 +293,19 @@ void ImageLoaderTinyEXR::get_recognized_extensions(List *p_extensions) c p_extensions->push_back("exr"); } -ImageLoaderTinyEXR::ImageLoaderTinyEXR() { +static Ref _tinyexr_mem_loader_func(const uint8_t *p_exr, int p_size) { + Ref memfile; + memfile.instantiate(); + Error open_memfile_error = memfile->open_custom(p_exr, p_size); + ERR_FAIL_COND_V_MSG(open_memfile_error, Ref(), "Could not create memfile for EXR image buffer."); + + Ref img; + img.instantiate(); + Error load_error = ImageLoaderTinyEXR().load_image(img, memfile, false, 1.0f); + ERR_FAIL_COND_V_MSG(load_error, Ref(), "Failed to load EXR image."); + return img; +} + +ImageLoaderTinyEXR::ImageLoaderTinyEXR() { + Image::_exr_mem_loader_func = _tinyexr_mem_loader_func; } diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 5334446b224..4e96c8b2b57 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -494,6 +494,8 @@ Ref DisplayServerWayland::clipboard_get_image() const { err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-targa")); } else if (wayland_thread.selection_has_mime("image/ktx")) { err = image->load_ktx_from_buffer(wayland_thread.selection_get_mime("image/ktx")); + } else if (wayland_thread.selection_has_mime("image/x-exr")) { + err = image->load_exr_from_buffer(wayland_thread.selection_get_mime("image/x-exr")); } ERR_FAIL_COND_V(err != OK, Ref()); diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index fb56d181673..ff84a1406ba 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -123,6 +123,20 @@ TEST_CASE("[Image] Saving and loading") { "The BMP image should load successfully."); #endif // MODULE_BMP_ENABLED +#ifdef MODULE_EXR_ENABLED + // Load EXR + Ref image_exr; + image_exr.instantiate(); + Ref f_exr = FileAccess::open(TestUtils::get_data_path("images/icon.exr"), FileAccess::READ, &err); + REQUIRE(f_exr.is_valid()); + PackedByteArray data_exr; + data_exr.resize(f_exr->get_length() + 1); + f_exr->get_buffer(data_exr.ptrw(), f_exr->get_length()); + CHECK_MESSAGE( + image_exr->load_exr_from_buffer(data_exr) == OK, + "The EXR image should load successfully."); +#endif // MODULE_EXR_ENABLED + #ifdef MODULE_JPG_ENABLED // Load JPG Ref image_jpg = memnew(Image()); diff --git a/tests/data/images/icon.exr b/tests/data/images/icon.exr new file mode 100644 index 00000000000..980a5454f6b Binary files /dev/null and b/tests/data/images/icon.exr differ