Browse Source

Merge pull request #64628 from KoBeWi/a_new_meta

Add metadata to resource previews
Rémi Verschelde 2 years ago
parent
commit
104de1a750

+ 4 - 0
doc/classes/EditorResourcePreviewGenerator.xml

@@ -20,20 +20,24 @@
 			<return type="Texture2D" />
 			<return type="Texture2D" />
 			<param index="0" name="resource" type="Resource" />
 			<param index="0" name="resource" type="Resource" />
 			<param index="1" name="size" type="Vector2i" />
 			<param index="1" name="size" type="Vector2i" />
+			<param index="2" name="metadata" type="Dictionary" />
 			<description>
 			<description>
 				Generate a preview from a given resource with the specified size. This must always be implemented.
 				Generate a preview from a given resource with the specified size. This must always be implemented.
 				Returning an empty texture is an OK way to fail and let another generator take care.
 				Returning an empty texture is an OK way to fail and let another generator take care.
 				Care must be taken because this function is always called from a thread (not the main thread).
 				Care must be taken because this function is always called from a thread (not the main thread).
+				[param metadata] dictionary can modified to store file-specific metadata that can be used by the editor (like image size, sample length etc.).
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="_generate_from_path" qualifiers="virtual const">
 		<method name="_generate_from_path" qualifiers="virtual const">
 			<return type="Texture2D" />
 			<return type="Texture2D" />
 			<param index="0" name="path" type="String" />
 			<param index="0" name="path" type="String" />
 			<param index="1" name="size" type="Vector2i" />
 			<param index="1" name="size" type="Vector2i" />
+			<param index="2" name="metadata" type="Dictionary" />
 			<description>
 			<description>
 				Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call [method _generate].
 				Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call [method _generate].
 				Returning an empty texture is an OK way to fail and let another generator take care.
 				Returning an empty texture is an OK way to fail and let another generator take care.
 				Care must be taken because this function is always called from a thread (not the main thread).
 				Care must be taken because this function is always called from a thread (not the main thread).
+				[param metadata] dictionary can modified to store file-specific metadata that can be used by the editor (like image size, sample length etc.).
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="_generate_small_preview_automatically" qualifiers="virtual const">
 		<method name="_generate_small_preview_automatically" qualifiers="virtual const">

+ 63 - 52
editor/editor_resource_preview.cpp

@@ -35,6 +35,7 @@
 #include "core/io/resource_loader.h"
 #include "core/io/resource_loader.h"
 #include "core/io/resource_saver.h"
 #include "core/io/resource_saver.h"
 #include "core/object/message_queue.h"
 #include "core/object/message_queue.h"
+#include "core/variant/variant_utility.cpp"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_paths.h"
 #include "editor/editor_paths.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
@@ -48,17 +49,17 @@ bool EditorResourcePreviewGenerator::handles(const String &p_type) const {
 	ERR_FAIL_V_MSG(false, "EditorResourcePreviewGenerator::_handles needs to be overridden.");
 	ERR_FAIL_V_MSG(false, "EditorResourcePreviewGenerator::_handles needs to be overridden.");
 }
 }
 
 
-Ref<Texture2D> EditorResourcePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorResourcePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Texture2D> preview;
 	Ref<Texture2D> preview;
-	if (GDVIRTUAL_CALL(_generate, p_from, p_size, preview)) {
+	if (GDVIRTUAL_CALL(_generate, p_from, p_size, p_metadata, preview)) {
 		return preview;
 		return preview;
 	}
 	}
 	ERR_FAIL_V_MSG(Ref<Texture2D>(), "EditorResourcePreviewGenerator::_generate needs to be overridden.");
 	ERR_FAIL_V_MSG(Ref<Texture2D>(), "EditorResourcePreviewGenerator::_generate needs to be overridden.");
 }
 }
 
 
-Ref<Texture2D> EditorResourcePreviewGenerator::generate_from_path(const String &p_path, const Size2 &p_size) const {
+Ref<Texture2D> EditorResourcePreviewGenerator::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Texture2D> preview;
 	Ref<Texture2D> preview;
-	if (GDVIRTUAL_CALL(_generate_from_path, p_path, p_size, preview)) {
+	if (GDVIRTUAL_CALL(_generate_from_path, p_path, p_size, p_metadata, preview)) {
 		return preview;
 		return preview;
 	}
 	}
 
 
@@ -66,7 +67,7 @@ Ref<Texture2D> EditorResourcePreviewGenerator::generate_from_path(const String &
 	if (!res.is_valid()) {
 	if (!res.is_valid()) {
 		return res;
 		return res;
 	}
 	}
-	return generate(res, p_size);
+	return generate(res, p_size, p_metadata);
 }
 }
 
 
 bool EditorResourcePreviewGenerator::generate_small_preview_automatically() const {
 bool EditorResourcePreviewGenerator::generate_small_preview_automatically() const {
@@ -83,8 +84,8 @@ bool EditorResourcePreviewGenerator::can_generate_small_preview() const {
 
 
 void EditorResourcePreviewGenerator::_bind_methods() {
 void EditorResourcePreviewGenerator::_bind_methods() {
 	GDVIRTUAL_BIND(_handles, "type");
 	GDVIRTUAL_BIND(_handles, "type");
-	GDVIRTUAL_BIND(_generate, "resource", "size");
-	GDVIRTUAL_BIND(_generate_from_path, "path", "size");
+	GDVIRTUAL_BIND(_generate, "resource", "size", "metadata");
+	GDVIRTUAL_BIND(_generate_from_path, "path", "size", "metadata");
 	GDVIRTUAL_BIND(_generate_small_preview_automatically);
 	GDVIRTUAL_BIND(_generate_small_preview_automatically);
 	GDVIRTUAL_BIND(_can_generate_small_preview);
 	GDVIRTUAL_BIND(_can_generate_small_preview);
 }
 }
@@ -99,35 +100,31 @@ void EditorResourcePreview::_thread_func(void *ud) {
 	erp->_thread();
 	erp->_thread();
 }
 }
 
 
-void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud) {
-	String path = p_str;
+void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata) {
 	{
 	{
 		MutexLock lock(preview_mutex);
 		MutexLock lock(preview_mutex);
 
 
-		uint32_t hash = 0;
 		uint64_t modified_time = 0;
 		uint64_t modified_time = 0;
 
 
-		if (p_str.begins_with("ID:")) {
-			hash = uint32_t(p_str.get_slicec(':', 2).to_int());
-			path = "ID:" + p_str.get_slicec(':', 1);
-		} else {
-			modified_time = FileAccess::get_modified_time(path);
+		if (!p_path.begins_with("ID:")) {
+			modified_time = FileAccess::get_modified_time(p_path);
 		}
 		}
 
 
 		Item item;
 		Item item;
 		item.order = order++;
 		item.order = order++;
 		item.preview = p_texture;
 		item.preview = p_texture;
 		item.small_preview = p_small_texture;
 		item.small_preview = p_small_texture;
-		item.last_hash = hash;
+		item.last_hash = p_hash;
 		item.modified_time = modified_time;
 		item.modified_time = modified_time;
+		item.preview_metadata = p_metadata;
 
 
-		cache[path] = item;
+		cache[p_path] = item;
 	}
 	}
 
 
-	MessageQueue::get_singleton()->push_call(id, p_func, path, p_texture, p_small_texture, p_ud);
+	MessageQueue::get_singleton()->push_call(id, p_func, p_path, p_texture, p_small_texture, p_ud);
 }
 }
 
 
-void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base) {
+void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base, Dictionary &p_metadata) {
 	String type;
 	String type;
 
 
 	if (p_item.resource.is_valid()) {
 	if (p_item.resource.is_valid()) {
@@ -155,9 +152,9 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
 
 
 		Ref<Texture2D> generated;
 		Ref<Texture2D> generated;
 		if (p_item.resource.is_valid()) {
 		if (p_item.resource.is_valid()) {
-			generated = preview_generators[i]->generate(p_item.resource, Vector2(thumbnail_size, thumbnail_size));
+			generated = preview_generators.write[i]->generate(p_item.resource, Vector2(thumbnail_size, thumbnail_size), p_metadata);
 		} else {
 		} else {
-			generated = preview_generators[i]->generate_from_path(p_item.path, Vector2(thumbnail_size, thumbnail_size));
+			generated = preview_generators.write[i]->generate_from_path(p_item.path, Vector2(thumbnail_size, thumbnail_size), p_metadata);
 		}
 		}
 		r_texture = generated;
 		r_texture = generated;
 
 
@@ -165,10 +162,11 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
 
 
 		if (preview_generators[i]->can_generate_small_preview()) {
 		if (preview_generators[i]->can_generate_small_preview()) {
 			Ref<Texture2D> generated_small;
 			Ref<Texture2D> generated_small;
+			Dictionary d;
 			if (p_item.resource.is_valid()) {
 			if (p_item.resource.is_valid()) {
-				generated_small = preview_generators[i]->generate(p_item.resource, Vector2(small_thumbnail_size, small_thumbnail_size));
+				generated_small = preview_generators.write[i]->generate(p_item.resource, Vector2(small_thumbnail_size, small_thumbnail_size), d);
 			} else {
 			} else {
-				generated_small = preview_generators[i]->generate_from_path(p_item.path, Vector2(small_thumbnail_size, small_thumbnail_size));
+				generated_small = preview_generators.write[i]->generate_from_path(p_item.path, Vector2(small_thumbnail_size, small_thumbnail_size), d);
 			}
 			}
 			r_small_texture = generated_small;
 			r_small_texture = generated_small;
 		}
 		}
@@ -185,9 +183,9 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
 	}
 	}
 
 
 	if (!p_item.resource.is_valid()) {
 	if (!p_item.resource.is_valid()) {
-		// cache the preview in case it's a resource on disk
+		// Cache the preview in case it's a resource on disk.
 		if (r_texture.is_valid()) {
 		if (r_texture.is_valid()) {
-			//wow it generated a preview... save cache
+			// Wow it generated a preview... save cache.
 			bool has_small_texture = r_small_texture.is_valid();
 			bool has_small_texture = r_small_texture.is_valid();
 			ResourceSaver::save(r_texture, cache_base + ".png");
 			ResourceSaver::save(r_texture, cache_base + ".png");
 			if (has_small_texture) {
 			if (has_small_texture) {
@@ -195,14 +193,16 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
 			}
 			}
 			Ref<FileAccess> f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE);
 			Ref<FileAccess> f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE);
 			ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions.");
 			ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions.");
-			f->store_line(itos(thumbnail_size));
-			f->store_line(itos(has_small_texture));
-			f->store_line(itos(FileAccess::get_modified_time(p_item.path)));
-			f->store_line(FileAccess::get_md5(p_item.path));
+			_write_preview_cache(f, thumbnail_size, has_small_texture, FileAccess::get_modified_time(p_item.path), FileAccess::get_md5(p_item.path), p_metadata);
 		}
 		}
 	}
 	}
 }
 }
 
 
+Variant EditorResourcePreview::get_preview_metadata(const String &p_path, const String &p_meta) const {
+	ERR_FAIL_COND_V(!cache.has(p_path), Variant());
+	return cache[p_path].preview_metadata.get(p_meta, Variant());
+}
+
 void EditorResourcePreview::_iterate() {
 void EditorResourcePreview::_iterate() {
 	preview_mutex.lock();
 	preview_mutex.lock();
 
 
@@ -211,13 +211,8 @@ void EditorResourcePreview::_iterate() {
 		queue.pop_front();
 		queue.pop_front();
 
 
 		if (cache.has(item.path)) {
 		if (cache.has(item.path)) {
-			//already has it because someone loaded it, just let it know it's ready
-			String path = item.path;
-			if (item.resource.is_valid()) {
-				path += ":" + itos(cache[item.path].last_hash); //keep last hash (see description of what this is in condition below)
-			}
-
-			_preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata);
+			// Already has it because someone loaded it, just let it know it's ready.
+			_preview_ready(item.path, cache[item.path].last_hash, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata, cache[item.path].preview_metadata);
 
 
 			preview_mutex.unlock();
 			preview_mutex.unlock();
 		} else {
 		} else {
@@ -230,28 +225,31 @@ void EditorResourcePreview::_iterate() {
 			thumbnail_size *= EDSCALE;
 			thumbnail_size *= EDSCALE;
 
 
 			if (item.resource.is_valid()) {
 			if (item.resource.is_valid()) {
-				_generate_preview(texture, small_texture, item, String());
+				Dictionary preview_metadata;
+				_generate_preview(texture, small_texture, item, String(), preview_metadata);
 
 
-				//adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred
-				_preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata);
+				_preview_ready(item.path, item.resource->hash_edited_version(), texture, small_texture, item.id, item.function, item.userdata, preview_metadata);
 
 
 			} else {
 			} else {
+				Dictionary preview_metadata;
 				String temp_path = EditorPaths::get_singleton()->get_cache_dir();
 				String temp_path = EditorPaths::get_singleton()->get_cache_dir();
 				String cache_base = ProjectSettings::get_singleton()->globalize_path(item.path).md5_text();
 				String cache_base = ProjectSettings::get_singleton()->globalize_path(item.path).md5_text();
 				cache_base = temp_path.path_join("resthumb-" + cache_base);
 				cache_base = temp_path.path_join("resthumb-" + cache_base);
 
 
-				//does not have it, try to load a cached thumbnail
+				// Does not have it, try to load a cached thumbnail.
 
 
 				String file = cache_base + ".txt";
 				String file = cache_base + ".txt";
 				Ref<FileAccess> f = FileAccess::open(file, FileAccess::READ);
 				Ref<FileAccess> f = FileAccess::open(file, FileAccess::READ);
 				if (f.is_null()) {
 				if (f.is_null()) {
-					// No cache found, generate
-					_generate_preview(texture, small_texture, item, cache_base);
+					// No cache found, generate.
+					_generate_preview(texture, small_texture, item, cache_base, preview_metadata);
 				} else {
 				} else {
 					uint64_t modtime = FileAccess::get_modified_time(item.path);
 					uint64_t modtime = FileAccess::get_modified_time(item.path);
-					int tsize = f->get_line().to_int();
-					bool has_small_texture = f->get_line().to_int();
-					uint64_t last_modtime = f->get_line().to_int();
+					int tsize;
+					bool has_small_texture;
+					uint64_t last_modtime;
+					String hash;
+					_read_preview_cache(f, &tsize, &has_small_texture, &last_modtime, &hash, &preview_metadata);
 
 
 					bool cache_valid = true;
 					bool cache_valid = true;
 
 
@@ -266,7 +264,7 @@ void EditorResourcePreview::_iterate() {
 						if (last_md5 != md5) {
 						if (last_md5 != md5) {
 							cache_valid = false;
 							cache_valid = false;
 						} else {
 						} else {
-							//update modified time
+							// Update modified time.
 
 
 							Ref<FileAccess> f2 = FileAccess::open(file, FileAccess::WRITE);
 							Ref<FileAccess> f2 = FileAccess::open(file, FileAccess::WRITE);
 							if (f2.is_null()) {
 							if (f2.is_null()) {
@@ -274,10 +272,7 @@ void EditorResourcePreview::_iterate() {
 								// some proper cleanup/disabling of resource preview generation.
 								// some proper cleanup/disabling of resource preview generation.
 								ERR_PRINT("Cannot create file '" + file + "'. Check user write permissions.");
 								ERR_PRINT("Cannot create file '" + file + "'. Check user write permissions.");
 							} else {
 							} else {
-								f2->store_line(itos(thumbnail_size));
-								f2->store_line(itos(has_small_texture));
-								f2->store_line(itos(modtime));
-								f2->store_line(md5);
+								_write_preview_cache(f2, thumbnail_size, has_small_texture, modtime, md5, preview_metadata);
 							}
 							}
 						}
 						}
 					} else {
 					} else {
@@ -308,10 +303,10 @@ void EditorResourcePreview::_iterate() {
 					}
 					}
 
 
 					if (!cache_valid) {
 					if (!cache_valid) {
-						_generate_preview(texture, small_texture, item, cache_base);
+						_generate_preview(texture, small_texture, item, cache_base, preview_metadata);
 					}
 					}
 				}
 				}
-				_preview_ready(item.path, texture, small_texture, item.id, item.function, item.userdata);
+				_preview_ready(item.path, 0, texture, small_texture, item.id, item.function, item.userdata, preview_metadata);
 			}
 			}
 		}
 		}
 
 
@@ -320,6 +315,22 @@ void EditorResourcePreview::_iterate() {
 	}
 	}
 }
 }
 
 
+void EditorResourcePreview::_write_preview_cache(Ref<FileAccess> p_file, int p_thumbnail_size, bool p_has_small_texture, uint64_t p_modified_time, String p_hash, const Dictionary &p_metadata) {
+	p_file->store_line(itos(p_thumbnail_size));
+	p_file->store_line(itos(p_has_small_texture));
+	p_file->store_line(itos(p_modified_time));
+	p_file->store_line(p_hash);
+	p_file->store_line(VariantUtilityFunctions::var_to_str(p_metadata).replace("\n", " "));
+}
+
+void EditorResourcePreview::_read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata) {
+	*r_thumbnail_size = p_file->get_line().to_int();
+	*r_has_small_texture = p_file->get_line().to_int();
+	*r_modified_time = p_file->get_line().to_int();
+	*r_hash = p_file->get_line();
+	*r_metadata = VariantUtilityFunctions::str_to_var(p_file->get_line());
+}
+
 void EditorResourcePreview::_thread() {
 void EditorResourcePreview::_thread() {
 	exited.clear();
 	exited.clear();
 	while (!exit.is_set()) {
 	while (!exit.is_set()) {

+ 11 - 6
editor/editor_resource_preview.h

@@ -44,15 +44,15 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 	GDVIRTUAL1RC(bool, _handles, String)
 	GDVIRTUAL1RC(bool, _handles, String)
-	GDVIRTUAL2RC(Ref<Texture2D>, _generate, Ref<Resource>, Vector2i)
-	GDVIRTUAL2RC(Ref<Texture2D>, _generate_from_path, String, Vector2i)
+	GDVIRTUAL3RC(Ref<Texture2D>, _generate, Ref<Resource>, Vector2i, Dictionary)
+	GDVIRTUAL3RC(Ref<Texture2D>, _generate_from_path, String, Vector2i, Dictionary)
 	GDVIRTUAL0RC(bool, _generate_small_preview_automatically)
 	GDVIRTUAL0RC(bool, _generate_small_preview_automatically)
 	GDVIRTUAL0RC(bool, _can_generate_small_preview)
 	GDVIRTUAL0RC(bool, _can_generate_small_preview)
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const;
 	virtual bool handles(const String &p_type) const;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const;
-	virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const;
+	virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const;
 
 
 	virtual bool generate_small_preview_automatically() const;
 	virtual bool generate_small_preview_automatically() const;
 	virtual bool can_generate_small_preview() const;
 	virtual bool can_generate_small_preview() const;
@@ -84,6 +84,7 @@ class EditorResourcePreview : public Node {
 	struct Item {
 	struct Item {
 		Ref<Texture2D> preview;
 		Ref<Texture2D> preview;
 		Ref<Texture2D> small_preview;
 		Ref<Texture2D> small_preview;
+		Dictionary preview_metadata;
 		int order = 0;
 		int order = 0;
 		uint32_t last_hash = 0;
 		uint32_t last_hash = 0;
 		uint64_t modified_time = 0;
 		uint64_t modified_time = 0;
@@ -93,13 +94,16 @@ class EditorResourcePreview : public Node {
 
 
 	HashMap<String, Item> cache;
 	HashMap<String, Item> cache;
 
 
-	void _preview_ready(const String &p_str, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud);
-	void _generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base);
+	void _preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata);
+	void _generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base, Dictionary &p_metadata);
 
 
 	static void _thread_func(void *ud);
 	static void _thread_func(void *ud);
 	void _thread();
 	void _thread();
 	void _iterate();
 	void _iterate();
 
 
+	void _write_preview_cache(Ref<FileAccess> p_file, int p_thumbnail_size, bool p_has_small_texture, uint64_t p_modified_time, String p_hash, const Dictionary &p_metadata);
+	void _read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata);
+
 	Vector<Ref<EditorResourcePreviewGenerator>> preview_generators;
 	Vector<Ref<EditorResourcePreviewGenerator>> preview_generators;
 
 
 protected:
 protected:
@@ -112,6 +116,7 @@ public:
 	// p_preview will be null if there was an error
 	// p_preview will be null if there was an error
 	void queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
 	void queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
 	void queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
 	void queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
+	Variant get_preview_metadata(const String &p_path, const String &p_meta) const;
 
 
 	void add_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);
 	void add_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);
 	void remove_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);
 	void remove_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);

+ 1 - 1
editor/plugins/curve_editor_plugin.cpp

@@ -798,7 +798,7 @@ bool CurvePreviewGenerator::handles(const String &p_type) const {
 	return p_type == "Curve";
 	return p_type == "Curve";
 }
 }
 
 
-Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Curve> curve_ref = p_from;
 	Ref<Curve> curve_ref = p_from;
 	ERR_FAIL_COND_V_MSG(curve_ref.is_null(), Ref<Texture2D>(), "It's not a reference to a valid Resource object.");
 	ERR_FAIL_COND_V_MSG(curve_ref.is_null(), Ref<Texture2D>(), "It's not a reference to a valid Resource object.");
 	Curve &curve = **curve_ref;
 	Curve &curve = **curve_ref;

+ 1 - 1
editor/plugins/curve_editor_plugin.h

@@ -142,7 +142,7 @@ class CurvePreviewGenerator : public EditorResourcePreviewGenerator {
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 };
 };
 
 
 #endif // CURVE_EDITOR_PLUGIN_H
 #endif // CURVE_EDITOR_PLUGIN_H

+ 15 - 14
editor/plugins/editor_preview_plugins.cpp

@@ -79,7 +79,7 @@ bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const {
 	return true;
 	return true;
 }
 }
 
 
-Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Image> img;
 	Ref<Image> img;
 	Ref<AtlasTexture> atex = p_from;
 	Ref<AtlasTexture> atex = p_from;
 	if (atex.is_valid()) {
 	if (atex.is_valid()) {
@@ -107,6 +107,7 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from,
 	if (img.is_null() || img->is_empty()) {
 	if (img.is_null() || img->is_empty()) {
 		return Ref<Texture2D>();
 		return Ref<Texture2D>();
 	}
 	}
+	p_metadata["dimensions"] = img->get_size();
 
 
 	img->clear_mipmaps();
 	img->clear_mipmaps();
 
 
@@ -141,7 +142,7 @@ bool EditorImagePreviewPlugin::handles(const String &p_type) const {
 	return p_type == "Image";
 	return p_type == "Image";
 }
 }
 
 
-Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Image> img = p_from;
 	Ref<Image> img = p_from;
 
 
 	if (img.is_null() || img->is_empty()) {
 	if (img.is_null() || img->is_empty()) {
@@ -185,7 +186,7 @@ bool EditorBitmapPreviewPlugin::handles(const String &p_type) const {
 	return ClassDB::is_parent_class(p_type, "BitMap");
 	return ClassDB::is_parent_class(p_type, "BitMap");
 }
 }
 
 
-Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<BitMap> bm = p_from;
 	Ref<BitMap> bm = p_from;
 
 
 	if (bm->get_size() == Size2()) {
 	if (bm->get_size() == Size2()) {
@@ -246,11 +247,11 @@ bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const {
 	return ClassDB::is_parent_class(p_type, "PackedScene");
 	return ClassDB::is_parent_class(p_type, "PackedScene");
 }
 }
 
 
-Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
-	return generate_from_path(p_from->get_path(), p_size);
+Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
+	return generate_from_path(p_from->get_path(), p_size, p_metadata);
 }
 }
 
 
-Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const {
+Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
 	String temp_path = EditorPaths::get_singleton()->get_cache_dir();
 	String temp_path = EditorPaths::get_singleton()->get_cache_dir();
 	String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
 	String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
 	cache_base = temp_path.path_join("resthumb-" + cache_base);
 	cache_base = temp_path.path_join("resthumb-" + cache_base);
@@ -298,7 +299,7 @@ bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const {
 	return true;
 	return true;
 }
 }
 
 
-Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Material> material = p_from;
 	Ref<Material> material = p_from;
 	ERR_FAIL_COND_V(material.is_null(), Ref<Texture2D>());
 	ERR_FAIL_COND_V(material.is_null(), Ref<Texture2D>());
 
 
@@ -455,7 +456,7 @@ bool EditorScriptPreviewPlugin::handles(const String &p_type) const {
 	return ClassDB::is_parent_class(p_type, "Script");
 	return ClassDB::is_parent_class(p_type, "Script");
 }
 }
 
 
-Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Script> scr = p_from;
 	Ref<Script> scr = p_from;
 	if (scr.is_null()) {
 	if (scr.is_null()) {
 		return Ref<Texture2D>();
 		return Ref<Texture2D>();
@@ -590,7 +591,7 @@ bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const {
 	return ClassDB::is_parent_class(p_type, "AudioStream");
 	return ClassDB::is_parent_class(p_type, "AudioStream");
 }
 }
 
 
-Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<AudioStream> stream = p_from;
 	Ref<AudioStream> stream = p_from;
 	ERR_FAIL_COND_V(stream.is_null(), Ref<Texture2D>());
 	ERR_FAIL_COND_V(stream.is_null(), Ref<Texture2D>());
 
 
@@ -680,7 +681,7 @@ bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
 	return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh.
 	return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh.
 }
 }
 
 
-Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Mesh> mesh = p_from;
 	Ref<Mesh> mesh = p_from;
 	ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture2D>());
 	ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture2D>());
 
 
@@ -797,7 +798,7 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const {
 	return ClassDB::is_parent_class(p_type, "Font");
 	return ClassDB::is_parent_class(p_type, "Font");
 }
 }
 
 
-Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const {
+Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Font> sampled_font = ResourceLoader::load(p_path);
 	Ref<Font> sampled_font = ResourceLoader::load(p_path);
 	ERR_FAIL_COND_V(sampled_font.is_null(), Ref<Texture2D>());
 	ERR_FAIL_COND_V(sampled_font.is_null(), Ref<Texture2D>());
 
 
@@ -846,12 +847,12 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path,
 	return ImageTexture::create_from_image(img);
 	return ImageTexture::create_from_image(img);
 }
 }
 
 
-Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	String path = p_from->get_path();
 	String path = p_from->get_path();
 	if (!FileAccess::exists(path)) {
 	if (!FileAccess::exists(path)) {
 		return Ref<Texture2D>();
 		return Ref<Texture2D>();
 	}
 	}
-	return generate_from_path(path, p_size);
+	return generate_from_path(path, p_size, p_metadata);
 }
 }
 
 
 EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
 EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
@@ -887,7 +888,7 @@ bool EditorGradientPreviewPlugin::generate_small_preview_automatically() const {
 	return true;
 	return true;
 }
 }
 
 
-Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
 	Ref<Gradient> gradient = p_from;
 	Ref<Gradient> gradient = p_from;
 	if (gradient.is_valid()) {
 	if (gradient.is_valid()) {
 		Ref<GradientTexture1D> ptex;
 		Ref<GradientTexture1D> ptex;

+ 13 - 13
editor/plugins/editor_preview_plugins.h

@@ -42,7 +42,7 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
 	virtual bool generate_small_preview_automatically() const override;
 	virtual bool generate_small_preview_automatically() const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorTexturePreviewPlugin();
 	EditorTexturePreviewPlugin();
 };
 };
@@ -53,7 +53,7 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator {
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
 	virtual bool generate_small_preview_automatically() const override;
 	virtual bool generate_small_preview_automatically() const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorImagePreviewPlugin();
 	EditorImagePreviewPlugin();
 };
 };
@@ -64,7 +64,7 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator {
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
 	virtual bool generate_small_preview_automatically() const override;
 	virtual bool generate_small_preview_automatically() const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorBitmapPreviewPlugin();
 	EditorBitmapPreviewPlugin();
 };
 };
@@ -74,8 +74,8 @@ class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator {
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
-	virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
+	virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorPackedScenePreviewPlugin();
 	EditorPackedScenePreviewPlugin();
 };
 };
@@ -102,7 +102,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
 	virtual bool generate_small_preview_automatically() const override;
 	virtual bool generate_small_preview_automatically() const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorMaterialPreviewPlugin();
 	EditorMaterialPreviewPlugin();
 	~EditorMaterialPreviewPlugin();
 	~EditorMaterialPreviewPlugin();
@@ -113,7 +113,7 @@ class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator {
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorScriptPreviewPlugin();
 	EditorScriptPreviewPlugin();
 };
 };
@@ -123,7 +123,7 @@ class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator {
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorAudioStreamPreviewPlugin();
 	EditorAudioStreamPreviewPlugin();
 };
 };
@@ -148,7 +148,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorMeshPreviewPlugin();
 	EditorMeshPreviewPlugin();
 	~EditorMeshPreviewPlugin();
 	~EditorMeshPreviewPlugin();
@@ -168,8 +168,8 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator {
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
-	virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
+	virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorFontPreviewPlugin();
 	EditorFontPreviewPlugin();
 	~EditorFontPreviewPlugin();
 	~EditorFontPreviewPlugin();
@@ -185,7 +185,7 @@ class EditorTileMapPatternPreviewPlugin : public EditorResourcePreviewGenerator
 
 
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorTileMapPatternPreviewPlugin();
 	EditorTileMapPatternPreviewPlugin();
 	~EditorTileMapPatternPreviewPlugin();
 	~EditorTileMapPatternPreviewPlugin();
@@ -197,7 +197,7 @@ class EditorGradientPreviewPlugin : public EditorResourcePreviewGenerator {
 public:
 public:
 	virtual bool handles(const String &p_type) const override;
 	virtual bool handles(const String &p_type) const override;
 	virtual bool generate_small_preview_automatically() const override;
 	virtual bool generate_small_preview_automatically() const override;
-	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+	virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
 
 
 	EditorGradientPreviewPlugin();
 	EditorGradientPreviewPlugin();
 };
 };