From 0d173deb2df40f5fe55177e7f5fe9e5cca8dfc21 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Thu, 22 Feb 2024 15:49:43 +0100 Subject: [PATCH] Add Alpha from Grayscale import option to Texture2D This can be used to import textures that are on a solid black background and use them for VFX. This also adds and exposes `Image::get_format_with_alpha()` and `Image::get_format_without_alpha()` static methods for convenience when working with image formats. --- core/io/image.cpp | 48 +++++++++++++++++++++ core/io/image.h | 3 ++ doc/classes/Image.xml | 26 +++++++++-- doc/classes/ResourceImporterTexture.xml | 3 ++ editor/import/resource_importer_texture.cpp | 21 +++++++++ 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/core/io/image.cpp b/core/io/image.cpp index c454f06d6701..8b90c3d57211 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -665,6 +665,52 @@ Image::Format Image::get_format() const { return format; } +Image::Format Image::get_format_with_alpha(Image::Format p_format) { + switch (p_format) { + case FORMAT_L8: + return FORMAT_LA8; + case FORMAT_R8: + return FORMAT_RGBA8; + case FORMAT_RG8: + return FORMAT_RGBA8; + case FORMAT_RGB8: + return FORMAT_RGBA8; + case FORMAT_RGB565: + return FORMAT_RGBA8; + case FORMAT_RF: + return FORMAT_RGBAF; + case FORMAT_RGF: + return FORMAT_RGBAF; + case FORMAT_RGBF: + return FORMAT_RGBAF; + case FORMAT_RH: + return FORMAT_RGBAH; + case FORMAT_RGH: + return FORMAT_RGBAH; + case FORMAT_RGBH: + return FORMAT_RGBAH; + case FORMAT_RGBE9995: + return FORMAT_RGBAF; + default: + return p_format; + } +} + +Image::Format Image::get_format_without_alpha(Image::Format p_format) { + switch (p_format) { + case FORMAT_LA8: + return FORMAT_L8; + case FORMAT_RGBA8: + return FORMAT_RGB8; + case FORMAT_RGBAF: + return FORMAT_RGBF; + case FORMAT_RGBAH: + return FORMAT_RGBH; + default: + return p_format; + } +} + static double _bicubic_interp_kernel(double x) { x = ABS(x); @@ -3422,6 +3468,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_size"), &Image::get_size); ClassDB::bind_method(D_METHOD("has_mipmaps"), &Image::has_mipmaps); ClassDB::bind_method(D_METHOD("get_format"), &Image::get_format); + ClassDB::bind_static_method("Image", D_METHOD("get_format_with_alpha", "format"), &Image::get_format_with_alpha); + ClassDB::bind_static_method("Image", D_METHOD("get_format_without_alpha", "format"), &Image::get_format_without_alpha); ClassDB::bind_method(D_METHOD("get_data"), &Image::get_data); ClassDB::bind_method(D_METHOD("convert", "format"), &Image::convert); diff --git a/core/io/image.h b/core/io/image.h index 2cabbb767a5e..dce3a0e7d05a 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -238,6 +238,9 @@ class Image : public Resource { */ Format get_format() const; + static Format get_format_with_alpha(Format p_format); + static Format get_format_without_alpha(Format p_format); + int get_mipmap_byte_size(int p_mipmap) const; //get where the mipmap begins in data int get_mipmap_offset(int p_mipmap) const; //get where the mipmap begins in data void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 68d4deaffdb1..2b3f2a4a3774 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -79,7 +79,7 @@ - Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. + Compresses the image to use less memory. Pixel data cannot be directly accessed on a compressed image. Returns error if the chosen compression mode is not available. See also [method is_compressed]. The [param source] parameter helps to pick the best compression method for DXT and ETC2 formats. It is ignored for ASTC compression. For ASTC compression, the [param astc_format] parameter must be supplied. @@ -90,7 +90,7 @@ - Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. + Compresses the image to use less memory. Pixel data cannot be directly accessed on a compressed image. Returns error if the chosen compression mode is not available. See also [method is_compressed]. This is an alternative to [method compress] that lets the user supply the channels used in order for the compressor to pick the best DXT and ETC2 formats. For other formats (non DXT or ETC2), this argument is ignored. For ASTC compression, the [param astc_format] parameter must be supplied. @@ -150,7 +150,7 @@ - Decompresses the image if it is VRAM compressed in a supported format. Returns [constant OK] if the format is supported, otherwise [constant ERR_UNAVAILABLE]. + Decompresses the image if it is VRAM compressed in a supported format. Returns [constant OK] if the format is supported, otherwise [constant ERR_UNAVAILABLE]. See also [method is_compressed]. [b]Note:[/b] The following formats can be decompressed: DXT, RGTC, BPTC. The formats ETC1 and ETC2 are not supported. @@ -220,6 +220,24 @@ Returns the image's format. See [enum Format] constants. + + + + + Returns the image format that is equivalent to [param format], but with an alpha channel. If no equivalent exists, returns the format that was originally passed. + For example, calling this method with [constant Image.FORMAT_RGB8] returns [constant Image.FORMAT_RGBA8]. + [b]Note:[/b] This method always returns the original format for VRAM-compressed formats (see [method is_compressed]). + + + + + + + Returns the image format that is equivalent to [param format], but without an alpha channel. If no equivalent exists or the equivalent would occupy more memory than [param format], returns the format that was originally passed. + For example, calling this method with [constant Image.FORMAT_RGBA8] returns [constant Image.FORMAT_RGB8]. + [b]Note:[/b] This method always returns the original format for VRAM-compressed formats (see [method is_compressed]). + + @@ -290,7 +308,7 @@ - Returns [code]true[/code] if the image is compressed. + Returns [code]true[/code] if the image is in a VRAM-compressed format. A VRAM-compressed format is any format that is listed [i]after[/i] [constant FORMAT_RGBE9995] in the [enum Format] enum (excluding [constant FORMAT_RGBE9995]). These formats are designed to use a fixed compression ratio scheme, which allows for fast random access on the GPU. VRAM-compressed images cannot have their data modified until decompressed with [method decompress]. diff --git a/doc/classes/ResourceImporterTexture.xml b/doc/classes/ResourceImporterTexture.xml index 678aa2101c60..9c186fefc012 100644 --- a/doc/classes/ResourceImporterTexture.xml +++ b/doc/classes/ResourceImporterTexture.xml @@ -65,6 +65,9 @@ Unimplemented. This currently has no effect when changed. + + If [code]true[/code], multiplies the image's alpha channel by the grayscale value of each pixel. This can be used to make images with a black background have a transparent background instead. + If [code]true[/code], puts pixels of the same surrounding color in transition from transparent to opaque areas. For textures displayed with bilinear filtering, this helps to reduce the outline effect when exporting images from an image editor. It's recommended to leave this enabled (as it is by default), unless this causes issues for a particular image. diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 8cf104725ada..364d28b47df2 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -238,6 +238,7 @@ void ResourceImporterTexture::get_import_options(const String &p_path, Listpush_back(ImportOption(PropertyInfo(Variant::STRING, "roughness/src_normal", PROPERTY_HINT_FILE, "*.bmp,*.dds,*.exr,*.jpeg,*.jpg,*.hdr,*.png,*.svg,*.tga,*.webp"), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/alpha_from_grayscale"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_as_srgb"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_clamp_exposure"), false)); @@ -451,6 +452,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String // Processing. const bool fix_alpha_border = p_options["process/fix_alpha_border"]; const bool premult_alpha = p_options["process/premult_alpha"]; + const bool alpha_from_grayscale = p_options["process/alpha_from_grayscale"]; const bool normal_map_invert_y = p_options["process/normal_map_invert_y"]; // Support for texture streaming is not implemented yet. const bool stream = false; @@ -546,6 +548,25 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String target_image->premultiply_alpha(); } + if (alpha_from_grayscale) { + // Multiply alpha channel by the pixel's grayscale value. + const int height = target_image->get_height(); + const int width = target_image->get_width(); + + if (target_image->detect_alpha() == Image::ALPHA_NONE) { + // This operation makes the image transparent, so convert to a format that can store transparency. + target_image->convert(Image::get_format_with_alpha(target_image->get_format())); + } + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + const Color color = target_image->get_pixel(i, j); + const float value = (color.r + color.g + color.b) / 3.0; + target_image->set_pixel(i, j, Color(color.r, color.g, color.b, color.a * value)); + } + } + } + // Invert the green channel of the image to flip the normal map it contains. if (normal_map_invert_y) { // Inverting the green channel can be used to flip a normal map's direction.