Skip to content

Commit

Permalink
Add Alpha from Grayscale import option to Texture2D
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Calinou committed Mar 21, 2024
1 parent fe01776 commit 0d173de
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 4 deletions.
48 changes: 48 additions & 0 deletions core/io/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions core/io/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 22 additions & 4 deletions doc/classes/Image.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
<param index="1" name="source" type="int" enum="Image.CompressSource" default="0" />
<param index="2" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" />
<description>
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.
</description>
Expand All @@ -90,7 +90,7 @@
<param index="1" name="channels" type="int" enum="Image.UsedChannels" />
<param index="2" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" />
<description>
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.
</description>
Expand Down Expand Up @@ -150,7 +150,7 @@
<method name="decompress">
<return type="int" enum="Error" />
<description>
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.
</description>
</method>
Expand Down Expand Up @@ -220,6 +220,24 @@
Returns the image's format. See [enum Format] constants.
</description>
</method>
<method name="get_format_with_alpha" qualifiers="static">
<return type="int" enum="Image.Format" />
<param index="0" name="format" type="int" enum="Image.Format" />
<description>
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]).
</description>
</method>
<method name="get_format_without_alpha" qualifiers="static">
<return type="int" enum="Image.Format" />
<param index="0" name="format" type="int" enum="Image.Format" />
<description>
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]).
</description>
</method>
<method name="get_height" qualifiers="const">
<return type="int" />
<description>
Expand Down Expand Up @@ -290,7 +308,7 @@
<method name="is_compressed" qualifiers="const">
<return type="bool" />
<description>
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].
</description>
</method>
<method name="is_empty" qualifiers="const">
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/ResourceImporterTexture.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
<member name="mipmaps/limit" type="int" setter="" getter="" default="-1">
Unimplemented. This currently has no effect when changed.
</member>
<member name="process/alpha_from_grayscale" type="bool" setter="" getter="" default="false">
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.
</member>
<member name="process/fix_alpha_border" type="bool" setter="" getter="" default="true">
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.
Expand Down
21 changes: 21 additions & 0 deletions editor/import/resource_importer_texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo
r_options->push_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));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 0d173de

Please sign in to comment.