Browse Source

Allow images to be imported "for editor use" and respect editor settings

Yuri Sizov 3 years ago
parent
commit
817d4db21f

+ 1 - 0
core/io/image_loader.h

@@ -52,6 +52,7 @@ public:
 	enum LoaderFlags {
 		FLAG_NONE = 0,
 		FLAG_FORCE_LINEAR = 1,
+		FLAG_CONVERT_COLORS = 2,
 	};
 
 	virtual ~ImageFormatLoader() {}

+ 9 - 0
core/io/resource_importer.cpp

@@ -108,6 +108,15 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
 		}
 	}
 
+#ifdef TOOLS_ENABLED
+	if (r_path_and_type.metadata && !r_path_and_type.path.is_empty()) {
+		Dictionary metadata = r_path_and_type.metadata;
+		if (metadata.has("has_editor_variant")) {
+			r_path_and_type.path = r_path_and_type.path.get_basename() + ".editor." + r_path_and_type.path.get_extension();
+		}
+	}
+#endif
+
 	if (r_path_and_type.path.is_empty() || r_path_and_type.type.is_empty()) {
 		return ERR_FILE_CORRUPT;
 	}

+ 2 - 0
editor/editor_node.cpp

@@ -3364,6 +3364,8 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed
 		Ref<Texture2D> icon = p_editor->get_icon();
 		if (icon.is_valid()) {
 			tb->set_icon(icon);
+			// Make sure the control is updated if the icon is reimported.
+			icon->connect("changed", callable_mp((Control *)tb, &Control::update_minimum_size));
 		} else if (singleton->gui_base->has_theme_icon(p_editor->get_name(), SNAME("EditorIcons"))) {
 			tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), SNAME("EditorIcons")));
 		}

+ 8 - 0
editor/editor_themes.cpp

@@ -447,6 +447,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 	// Colors
 	bool dark_theme = EditorSettings::get_singleton()->is_dark_theme();
 
+#ifdef MODULE_SVG_ENABLED
+	if (dark_theme) {
+		ImageLoaderSVG::set_forced_color_map(HashMap<Color, Color>());
+	} else {
+		ImageLoaderSVG::set_forced_color_map(EditorColorMap::get());
+	}
+#endif
+
 	// Ensure base colors are in the 0..1 luminance range to avoid 8-bit integer overflow or text rendering issues.
 	// Some places in the editor use 8-bit integer colors.
 	const Color dark_color_1 = base_color.lerp(Color(0, 0, 0, 1), contrast).clamp();

+ 131 - 62
editor/import/resource_importer_texture.cpp

@@ -36,6 +36,8 @@
 #include "core/version.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
 
 void ResourceImporterTexture::_texture_reimport_roughness(const Ref<CompressedTexture2D> &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) {
 	ERR_FAIL_COND(p_tex.is_null());
@@ -233,6 +235,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo
 
 	if (p_path.get_extension() == "svg") {
 		r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
+
+		// Editor use only, applies to SVG.
+		r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/scale_with_editor_scale"), false));
+		r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/convert_colors_with_editor_theme"), false));
 	}
 }
 
@@ -447,6 +453,14 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 		scale = p_options["svg/scale"];
 	}
 
+	// Editor-specific options.
+	bool use_editor_scale = p_options.has("editor/scale_with_editor_scale") && p_options["editor/scale_with_editor_scale"];
+	bool convert_editor_colors = p_options.has("editor/convert_colors_with_editor_theme") && p_options["editor/convert_colors_with_editor_theme"];
+
+	// Start importing images.
+	List<Ref<Image>> images_imported;
+
+	// Load the normal image.
 	Ref<Image> normal_image;
 	Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R;
 	if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) {
@@ -456,88 +470,117 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 		}
 	}
 
+	// Load the main image.
 	Ref<Image> image;
 	image.instantiate();
 	Error err = ImageLoader::load_image(p_source_file, image, nullptr, loader_flags, scale);
 	if (err != OK) {
 		return err;
 	}
+	images_imported.push_back(image);
 
-	Array formats_imported;
-
-	if (size_limit > 0 && (image->get_width() > size_limit || image->get_height() > size_limit)) {
-		//limit size
-		if (image->get_width() >= image->get_height()) {
-			int new_width = size_limit;
-			int new_height = image->get_height() * new_width / image->get_width();
-
-			image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
-		} else {
-			int new_height = size_limit;
-			int new_width = image->get_width() * new_height / image->get_height();
+	// Load the editor-only image.
+	Ref<Image> editor_image;
+	bool import_editor_image = use_editor_scale || convert_editor_colors;
+	if (import_editor_image) {
+		float editor_scale = scale;
+		if (use_editor_scale) {
+			editor_scale = scale * EDSCALE;
+		}
 
-			image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
+		int32_t editor_loader_flags = loader_flags;
+		if (convert_editor_colors) {
+			editor_loader_flags |= ImageFormatLoader::FLAG_CONVERT_COLORS;
 		}
 
-		if (normal == 1) {
-			image->normalize();
+		editor_image.instantiate();
+		err = ImageLoader::load_image(p_source_file, editor_image, nullptr, editor_loader_flags, editor_scale);
+		if (err != OK) {
+			WARN_PRINT("Failed to import an image resource for editor use from '" + p_source_file + "'");
+		} else {
+			images_imported.push_back(editor_image);
 		}
 	}
 
-	if (fix_alpha_border) {
-		image->fix_alpha_edges();
-	}
+	for (Ref<Image> &target_image : images_imported) {
+		// Apply the size limit.
+		if (size_limit > 0 && (target_image->get_width() > size_limit || target_image->get_height() > size_limit)) {
+			if (target_image->get_width() >= target_image->get_height()) {
+				int new_width = size_limit;
+				int new_height = target_image->get_height() * new_width / target_image->get_width();
 
-	if (premult_alpha) {
-		image->premultiply_alpha();
-	}
+				target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
+			} else {
+				int new_height = size_limit;
+				int new_width = target_image->get_width() * new_height / target_image->get_height();
 
-	if (normal_map_invert_y) {
-		// Inverting the green channel can be used to flip a normal map's direction.
-		// There's no standard when it comes to normal map Y direction, so this is
-		// sometimes needed when using a normal map exported from another program.
-		// See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>.
-		const int height = image->get_height();
-		const int width = image->get_width();
+				target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
+			}
 
-		for (int i = 0; i < width; i++) {
-			for (int j = 0; j < height; j++) {
-				const Color color = image->get_pixel(i, j);
-				image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b));
+			if (normal == 1) {
+				target_image->normalize();
 			}
 		}
-	}
 
-	if (hdr_clamp_exposure) {
-		// Clamp HDR exposure following Filament's tonemapping formula.
-		// This can be used to reduce fireflies in environment maps or reduce the influence
-		// of the sun from an HDRI panorama on environment lighting (when a DirectionalLight3D is used instead).
-		const int height = image->get_height();
-		const int width = image->get_width();
-
-		// These values are chosen arbitrarily and seem to produce good results with 4,096 samples.
-		const float linear = 4096.0;
-		const float compressed = 16384.0;
+		// Fix alpha border.
+		if (fix_alpha_border) {
+			target_image->fix_alpha_edges();
+		}
 
-		for (int i = 0; i < width; i++) {
-			for (int j = 0; j < height; j++) {
-				const Color color = image->get_pixel(i, j);
-				const float luma = color.get_luminance();
+		// Premultiply the alpha.
+		if (premult_alpha) {
+			target_image->premultiply_alpha();
+		}
 
-				Color clamped_color;
-				if (luma <= linear) {
-					clamped_color = color;
-				} else {
-					clamped_color = (color / luma) * ((linear * linear - compressed * luma) / (2 * linear - compressed - luma));
+		// 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.
+			// There's no standard when it comes to normal map Y direction, so this is
+			// sometimes needed when using a normal map exported from another program.
+			// See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>.
+			const int height = target_image->get_height();
+			const int width = target_image->get_width();
+
+			for (int i = 0; i < width; i++) {
+				for (int j = 0; j < height; j++) {
+					const Color color = target_image->get_pixel(i, j);
+					target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b));
 				}
+			}
+		}
 
-				image->set_pixel(i, j, clamped_color);
+		// Clamp HDR exposure.
+		if (hdr_clamp_exposure) {
+			// Clamp HDR exposure following Filament's tonemapping formula.
+			// This can be used to reduce fireflies in environment maps or reduce the influence
+			// of the sun from an HDRI panorama on environment lighting (when a DirectionalLight3D is used instead).
+			const int height = target_image->get_height();
+			const int width = target_image->get_width();
+
+			// These values are chosen arbitrarily and seem to produce good results with 4,096 samples.
+			const float linear = 4096.0;
+			const float compressed = 16384.0;
+
+			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 luma = color.get_luminance();
+
+					Color clamped_color;
+					if (luma <= linear) {
+						clamped_color = color;
+					} else {
+						clamped_color = (color / luma) * ((linear * linear - compressed * luma) / (2 * linear - compressed - luma));
+					}
+
+					target_image->set_pixel(i, j, clamped_color);
+				}
 			}
 		}
 	}
 
 	if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
-		//basis universal does not support float formats, fall back
+		// Basis universal does not support float formats, fallback.
 		compress_mode = COMPRESS_VRAM_COMPRESSED;
 	}
 
@@ -547,9 +590,11 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 	bool force_normal = normal == 1;
 	bool srgb_friendly_pack = pack_channels == 0;
 
+	Array formats_imported;
+
 	if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
-		//must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
-		//Android, GLES 2.x
+		// Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
+		// Android, GLES 2.x
 
 		const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
 		bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565);
@@ -557,7 +602,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 		const bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc");
 
 		if (can_bptc) {
-			//add to the list anyway
+			// Add to the list anyway.
 			formats_imported.push_back("bptc");
 		}
 
@@ -566,9 +611,9 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 
 		if (is_hdr && can_compress_hdr) {
 			if (has_alpha) {
-				//can compress hdr, but hdr with alpha is not compressible
+				// Can compress HDR, but HDR with alpha is not compressible.
 				if (hdr_compression == 2) {
-					//but user selected to compress hdr anyway, so force an alpha-less format.
+					// But user selected to compress HDR anyway, so force an alpha-less format.
 					if (image->get_format() == Image::FORMAT_RGBAF) {
 						image->convert(Image::FORMAT_RGBF);
 					} else if (image->get_format() == Image::FORMAT_RGBAH) {
@@ -580,7 +625,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 			}
 
 			if (!can_compress_hdr) {
-				//fallback to RGBE99995
+				// Fallback to RGBE99995.
 				if (image->get_format() != Image::FORMAT_RGBE9995) {
 					image->convert(Image::FORMAT_RGBE9995);
 				}
@@ -615,16 +660,31 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 			EditorNode::add_io_error(vformat(TTR("%s: No suitable desktop VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file));
 		}
 	} else {
-		//import normally
+		// Import normally.
 		_save_ctex(image, p_save_path + ".ctex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
 	}
 
+	if (editor_image.is_valid()) {
+		_save_ctex(editor_image, p_save_path + ".editor.ctex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
+	}
+
 	if (r_metadata) {
 		Dictionary metadata;
 		metadata["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED;
 		if (formats_imported.size()) {
 			metadata["imported_formats"] = formats_imported;
 		}
+
+		if (editor_image.is_valid()) {
+			metadata["has_editor_variant"] = true;
+			if (use_editor_scale) {
+				metadata["editor_scale"] = EDSCALE;
+			}
+			if (convert_editor_colors) {
+				metadata["editor_dark_theme"] = EditorSettings::get_singleton()->is_dark_theme();
+			}
+		}
+
 		*r_metadata = metadata;
 	}
 	return OK;
@@ -657,13 +717,22 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
 	//will become invalid if formats are missing to import
 	Dictionary metadata = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
 
+	if (metadata.has("has_editor_variant")) {
+		if (metadata.has("editor_scale") && (float)metadata["editor_scale"] != EDSCALE) {
+			return false;
+		}
+		if (metadata.has("editor_dark_theme") && (bool)metadata["editor_dark_theme"] != EditorSettings::get_singleton()->is_dark_theme()) {
+			return false;
+		}
+	}
+
 	if (!metadata.has("vram_texture")) {
 		return false;
 	}
 
 	bool vram = metadata["vram_texture"];
 	if (!vram) {
-		return true; //do not care about non vram
+		return true; // Do not care about non-VRAM.
 	}
 
 	Vector<String> formats_imported;

+ 13 - 1
modules/svg/image_loader_svg.cpp

@@ -35,6 +35,12 @@
 
 #include <thorvg.h>
 
+HashMap<Color, Color> ImageLoaderSVG::forced_color_map = HashMap<Color, Color>();
+
+void ImageLoaderSVG::set_forced_color_map(const HashMap<Color, Color> &p_color_map) {
+	forced_color_map = p_color_map;
+}
+
 void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string) {
 	// Replace colors in the SVG based on what is passed in `p_color_map`.
 	// Used to change the colors of editor icons based on the used theme.
@@ -138,7 +144,13 @@ void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const
 
 Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) {
 	String svg = p_fileaccess->get_as_utf8_string();
-	create_image_from_string(p_image, svg, p_scale, false, HashMap<Color, Color>());
+
+	if (p_flags & FLAG_CONVERT_COLORS) {
+		create_image_from_string(p_image, svg, p_scale, false, forced_color_map);
+	} else {
+		create_image_from_string(p_image, svg, p_scale, false, HashMap<Color, Color>());
+	}
+
 	ERR_FAIL_COND_V(p_image->is_empty(), FAILED);
 	if (p_flags & FLAG_FORCE_LINEAR) {
 		p_image->srgb_to_linear();

+ 4 - 0
modules/svg/image_loader_svg.h

@@ -34,9 +34,13 @@
 #include "core/io/image_loader.h"
 
 class ImageLoaderSVG : public ImageFormatLoader {
+	static HashMap<Color, Color> forced_color_map;
+
 	void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string);
 
 public:
+	static void set_forced_color_map(const HashMap<Color, Color> &p_color_map);
+
 	void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);
 
 	virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) override;