2016-06-18 14:46:12 +02:00
/*************************************************************************/
/* editor_resource_preview.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2016-06-18 14:46:12 +02:00
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2016-06-18 14:46:12 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2015-05-31 06:59:42 +02:00
# include "editor_resource_preview.h"
2017-01-16 08:04:19 +01:00
2018-09-12 13:10:49 +02:00
# include "core/method_bind_ext.gen.inc"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
# include "core/io/resource_saver.h"
# include "core/message_queue.h"
# include "core/os/file_access.h"
# include "core/project_settings.h"
2018-09-12 13:10:49 +02:00
# include "editor_node.h"
2017-03-05 16:44:50 +01:00
# include "editor_scale.h"
2015-05-31 06:59:42 +02:00
# include "editor_settings.h"
2017-03-05 16:44:50 +01:00
bool EditorResourcePreviewGenerator : : handles ( const String & p_type ) const {
2016-09-10 20:32:17 +02:00
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " handles " ) ) {
2017-03-05 16:44:50 +01:00
return get_script_instance ( ) - > call ( " handles " , p_type ) ;
2016-09-10 20:32:17 +02:00
}
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( false , " EditorResourcePreviewGenerator::handles needs to be overridden. " ) ;
2016-09-10 20:32:17 +02:00
}
2018-09-12 13:10:49 +02:00
2019-07-10 11:54:12 +02:00
Ref < Texture > EditorResourcePreviewGenerator : : generate ( const RES & p_from , const Size2 & p_size ) const {
2016-09-10 20:32:17 +02:00
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " generate " ) ) {
2018-09-12 13:10:49 +02:00
return get_script_instance ( ) - > call ( " generate " , p_from , p_size ) ;
2016-09-10 20:32:17 +02:00
}
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( Ref < Texture > ( ) , " EditorResourcePreviewGenerator::generate needs to be overridden. " ) ;
2016-09-10 20:32:17 +02:00
}
2019-07-10 11:54:12 +02:00
Ref < Texture > EditorResourcePreviewGenerator : : generate_from_path ( const String & p_path , const Size2 & p_size ) const {
2015-05-31 06:59:42 +02:00
2016-09-10 20:32:17 +02:00
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " generate_from_path " ) ) {
2018-09-12 13:10:49 +02:00
return get_script_instance ( ) - > call ( " generate_from_path " , p_path , p_size ) ;
2016-09-10 20:32:17 +02:00
}
2015-05-31 06:59:42 +02:00
RES res = ResourceLoader : : load ( p_path ) ;
if ( ! res . is_valid ( ) )
return res ;
2018-09-12 13:10:49 +02:00
return generate ( res , p_size ) ;
}
2019-05-20 10:45:12 +02:00
bool EditorResourcePreviewGenerator : : generate_small_preview_automatically ( ) const {
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " generate_small_preview_automatically " ) ) {
return get_script_instance ( ) - > call ( " generate_small_preview_automatically " ) ;
}
return false ;
}
bool EditorResourcePreviewGenerator : : can_generate_small_preview ( ) const {
if ( get_script_instance ( ) & & get_script_instance ( ) - > has_method ( " can_generate_small_preview " ) ) {
return get_script_instance ( ) - > call ( " can_generate_small_preview " ) ;
}
2018-09-12 13:10:49 +02:00
return false ;
2015-05-31 06:59:42 +02:00
}
2016-09-10 20:32:17 +02:00
void EditorResourcePreviewGenerator : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( Variant : : BOOL , " handles " , PropertyInfo ( Variant : : STRING , " type " ) ) ) ;
2018-09-12 13:10:49 +02:00
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( CLASS_INFO ( Texture ) , " generate " , PropertyInfo ( Variant : : OBJECT , " from " , PROPERTY_HINT_RESOURCE_TYPE , " Resource " ) , PropertyInfo ( Variant : : VECTOR2 , " size " ) ) ) ;
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( CLASS_INFO ( Texture ) , " generate_from_path " , PropertyInfo ( Variant : : STRING , " path " , PROPERTY_HINT_FILE ) , PropertyInfo ( Variant : : VECTOR2 , " size " ) ) ) ;
2019-05-20 10:45:12 +02:00
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( Variant : : BOOL , " generate_small_preview_automatically " ) ) ;
ClassDB : : add_virtual_method ( get_class_static ( ) , MethodInfo ( Variant : : BOOL , " can_generate_small_preview " ) ) ;
2016-09-10 20:32:17 +02:00
}
2015-05-31 06:59:42 +02:00
EditorResourcePreviewGenerator : : EditorResourcePreviewGenerator ( ) {
}
2017-03-05 16:44:50 +01:00
EditorResourcePreview * EditorResourcePreview : : singleton = NULL ;
2015-05-31 06:59:42 +02:00
void EditorResourcePreview : : _thread_func ( void * ud ) {
2017-03-05 16:44:50 +01:00
EditorResourcePreview * erp = ( EditorResourcePreview * ) ud ;
2015-05-31 06:59:42 +02:00
erp - > _thread ( ) ;
}
2018-09-12 13:10:49 +02:00
void EditorResourcePreview : : _preview_ready ( const String & p_str , const Ref < Texture > & p_texture , const Ref < Texture > & p_small_texture , ObjectID id , const StringName & p_func , const Variant & p_ud ) {
2015-05-31 06:59:42 +02:00
preview_mutex - > lock ( ) ;
2016-05-27 19:18:40 +02:00
String path = p_str ;
2017-03-05 16:44:50 +01:00
uint32_t hash = 0 ;
uint64_t modified_time = 0 ;
2016-05-27 19:18:40 +02:00
if ( p_str . begins_with ( " ID: " ) ) {
2019-09-13 15:34:16 +02:00
hash = uint32_t ( p_str . get_slicec ( ' : ' , 2 ) . to_int64 ( ) ) ;
2017-03-05 16:44:50 +01:00
path = " ID: " + p_str . get_slicec ( ' : ' , 1 ) ;
2016-07-03 18:15:15 +02:00
} else {
modified_time = FileAccess : : get_modified_time ( path ) ;
2016-05-27 19:18:40 +02:00
}
2015-05-31 06:59:42 +02:00
Item item ;
2017-03-05 16:44:50 +01:00
item . order = order + + ;
item . preview = p_texture ;
2018-09-12 13:10:49 +02:00
item . small_preview = p_small_texture ;
2017-03-05 16:44:50 +01:00
item . last_hash = hash ;
item . modified_time = modified_time ;
2016-05-27 19:18:40 +02:00
2017-03-05 16:44:50 +01:00
cache [ path ] = item ;
2015-05-31 06:59:42 +02:00
preview_mutex - > unlock ( ) ;
2016-07-09 16:19:49 +02:00
2018-09-12 13:10:49 +02:00
MessageQueue : : get_singleton ( ) - > push_call ( id , p_func , path , p_texture , p_small_texture , p_ud ) ;
2015-05-31 06:59:42 +02:00
}
2018-09-12 13:10:49 +02:00
void EditorResourcePreview : : _generate_preview ( Ref < ImageTexture > & r_texture , Ref < ImageTexture > & r_small_texture , const QueueItem & p_item , const String & cache_base ) {
2016-05-27 19:18:40 +02:00
String type ;
if ( p_item . resource . is_valid ( ) )
2017-03-05 16:44:50 +01:00
type = p_item . resource - > get_class ( ) ;
2016-05-27 19:18:40 +02:00
else
2017-03-05 16:44:50 +01:00
type = ResourceLoader : : get_resource_type ( p_item . path ) ;
2015-05-31 06:59:42 +02:00
2018-09-12 13:10:49 +02:00
if ( type = = " " ) {
r_texture = Ref < ImageTexture > ( ) ;
r_small_texture = Ref < ImageTexture > ( ) ;
return ; //could not guess type
}
2015-05-31 06:59:42 +02:00
2018-09-12 13:10:49 +02:00
int thumbnail_size = EditorSettings : : get_singleton ( ) - > get ( " filesystem/file_dialog/thumbnail_size " ) ;
thumbnail_size * = EDSCALE ;
2015-05-31 06:59:42 +02:00
2018-09-12 13:10:49 +02:00
r_texture = Ref < ImageTexture > ( ) ;
r_small_texture = Ref < ImageTexture > ( ) ;
2016-07-09 16:19:49 +02:00
2018-09-12 13:10:49 +02:00
for ( int i = 0 ; i < preview_generators . size ( ) ; i + + ) {
2015-05-31 06:59:42 +02:00
if ( ! preview_generators [ i ] - > handles ( type ) )
continue ;
2018-09-12 13:10:49 +02:00
Ref < Texture > generated ;
2016-05-27 19:18:40 +02:00
if ( p_item . resource . is_valid ( ) ) {
2018-09-12 13:10:49 +02:00
generated = preview_generators [ i ] - > generate ( p_item . resource , Vector2 ( thumbnail_size , thumbnail_size ) ) ;
2016-05-27 19:18:40 +02:00
} else {
2018-09-12 13:10:49 +02:00
generated = preview_generators [ i ] - > generate_from_path ( p_item . path , Vector2 ( thumbnail_size , thumbnail_size ) ) ;
2016-05-27 19:18:40 +02:00
}
2018-09-12 13:10:49 +02:00
r_texture = generated ;
2015-05-31 06:59:42 +02:00
2020-08-14 12:15:58 +02:00
if ( ! EditorNode : : get_singleton ( ) - > get_theme_base ( ) )
return ;
2019-05-20 10:45:12 +02:00
int small_thumbnail_size = EditorNode : : get_singleton ( ) - > get_theme_base ( ) - > get_icon ( " Object " , " EditorIcons " ) - > get_width ( ) ; // Kind of a workaround to retrieve the default icon size
if ( preview_generators [ i ] - > can_generate_small_preview ( ) ) {
Ref < Texture > generated_small ;
if ( p_item . resource . is_valid ( ) ) {
generated_small = preview_generators [ i ] - > generate ( p_item . resource , Vector2 ( small_thumbnail_size , small_thumbnail_size ) ) ;
} else {
generated_small = preview_generators [ i ] - > generate_from_path ( p_item . path , Vector2 ( small_thumbnail_size , small_thumbnail_size ) ) ;
}
r_small_texture = generated_small ;
}
2018-09-12 13:10:49 +02:00
2019-05-20 10:45:12 +02:00
if ( ! r_small_texture . is_valid ( ) & & r_texture . is_valid ( ) & & preview_generators [ i ] - > generate_small_preview_automatically ( ) ) {
2018-09-12 13:10:49 +02:00
Ref < Image > small_image = r_texture - > get_data ( ) ;
2019-01-27 17:39:16 +01:00
small_image = small_image - > duplicate ( ) ;
2018-09-12 13:10:49 +02:00
small_image - > resize ( small_thumbnail_size , small_thumbnail_size , Image : : INTERPOLATE_CUBIC ) ;
r_small_texture . instance ( ) ;
r_small_texture - > create_from_image ( small_image ) ;
}
2019-05-20 10:45:12 +02:00
2015-05-31 06:59:42 +02:00
break ;
}
2016-05-27 19:18:40 +02:00
if ( ! p_item . resource . is_valid ( ) ) {
// cache the preview in case it's a resource on disk
2018-09-12 13:10:49 +02:00
if ( r_texture . is_valid ( ) ) {
2016-05-27 19:18:40 +02:00
//wow it generated a preview... save cache
2018-09-12 13:10:49 +02:00
bool has_small_texture = r_small_texture . is_valid ( ) ;
ResourceSaver : : save ( cache_base + " .png " , r_texture ) ;
if ( has_small_texture ) {
ResourceSaver : : save ( cache_base + " _small.png " , r_small_texture ) ;
}
2019-10-31 15:14:49 +01:00
FileAccess * f = FileAccess : : open ( cache_base + " .txt " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_MSG ( ! f , " Cannot create file ' " + cache_base + " .txt'. Check user write permissions. " ) ;
2016-05-27 19:18:40 +02:00
f - > store_line ( itos ( thumbnail_size ) ) ;
2018-09-12 13:10:49 +02:00
f - > store_line ( itos ( has_small_texture ) ) ;
2016-05-27 19:18:40 +02:00
f - > store_line ( itos ( FileAccess : : get_modified_time ( p_item . path ) ) ) ;
f - > store_line ( FileAccess : : get_md5 ( p_item . path ) ) ;
2019-11-10 09:49:13 +01:00
f - > close ( ) ;
2016-05-27 19:18:40 +02:00
memdelete ( f ) ;
}
2015-05-31 06:59:42 +02:00
}
}
void EditorResourcePreview : : _thread ( ) {
2019-05-31 08:05:44 +02:00
exited = false ;
2017-03-05 16:44:50 +01:00
while ( ! exit ) {
2015-05-31 06:59:42 +02:00
preview_sem - > wait ( ) ;
preview_mutex - > lock ( ) ;
if ( queue . size ( ) ) {
QueueItem item = queue . front ( ) - > get ( ) ;
queue . pop_front ( ) ;
if ( cache . has ( item . path ) ) {
//already has it because someone loaded it, just let it know it's ready
2017-08-25 04:36:11 +02:00
String path = item . path ;
2016-05-27 19:18:40 +02:00
if ( item . resource . is_valid ( ) ) {
2017-08-25 04:36:11 +02:00
path + = " : " + itos ( cache [ item . path ] . last_hash ) ; //keep last hash (see description of what this is in condition below)
2016-05-27 19:18:40 +02:00
}
2018-09-12 13:10:49 +02:00
_preview_ready ( path , cache [ item . path ] . preview , cache [ item . path ] . small_preview , item . id , item . function , item . userdata ) ;
2016-05-27 19:18:40 +02:00
2016-07-09 16:19:49 +02:00
preview_mutex - > unlock ( ) ;
2015-05-31 06:59:42 +02:00
} else {
2017-11-12 04:48:00 +01:00
2016-07-09 16:19:49 +02:00
preview_mutex - > unlock ( ) ;
2017-06-09 05:23:50 +02:00
Ref < ImageTexture > texture ;
2018-09-12 13:10:49 +02:00
Ref < ImageTexture > small_texture ;
2015-05-31 06:59:42 +02:00
2017-01-05 23:41:36 +01:00
int thumbnail_size = EditorSettings : : get_singleton ( ) - > get ( " filesystem/file_dialog/thumbnail_size " ) ;
2017-03-05 16:44:50 +01:00
thumbnail_size * = EDSCALE ;
2015-05-31 06:59:42 +02:00
2017-03-05 16:44:50 +01:00
if ( item . resource . is_valid ( ) ) {
2015-05-31 06:59:42 +02:00
2018-09-12 13:10:49 +02:00
_generate_preview ( texture , small_texture , item , String ( ) ) ;
2016-07-09 16:19:49 +02:00
//adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred
2018-09-12 13:10:49 +02:00
_preview_ready ( item . path + " : " + itos ( item . resource - > hash_edited_version ( ) ) , texture , small_texture , item . id , item . function , item . userdata ) ;
2015-05-31 06:59:42 +02:00
} else {
2017-11-17 21:48:24 +01:00
String temp_path = EditorSettings : : get_singleton ( ) - > get_cache_dir ( ) ;
2017-07-19 22:00:46 +02:00
String cache_base = ProjectSettings : : get_singleton ( ) - > globalize_path ( item . path ) . md5_text ( ) ;
2017-03-05 16:44:50 +01:00
cache_base = temp_path . plus_file ( " resthumb- " + cache_base ) ;
2016-07-09 16:19:49 +02:00
//does not have it, try to load a cached thumbnail
2015-05-31 06:59:42 +02:00
2017-03-05 16:44:50 +01:00
String file = cache_base + " .txt " ;
FileAccess * f = FileAccess : : open ( file , FileAccess : : READ ) ;
2016-07-09 16:19:49 +02:00
if ( ! f ) {
2018-09-12 13:10:49 +02:00
// No cache found, generate
_generate_preview ( texture , small_texture , item , cache_base ) ;
2016-07-09 16:19:49 +02:00
} else {
2015-05-31 06:59:42 +02:00
2016-07-09 16:19:49 +02:00
uint64_t modtime = FileAccess : : get_modified_time ( item . path ) ;
int tsize = f - > get_line ( ) . to_int64 ( ) ;
2018-09-12 13:10:49 +02:00
bool has_small_texture = f - > get_line ( ) . to_int ( ) ;
2016-07-09 16:19:49 +02:00
uint64_t last_modtime = f - > get_line ( ) . to_int64 ( ) ;
2015-05-31 06:59:42 +02:00
2016-07-09 16:19:49 +02:00
bool cache_valid = true ;
2015-05-31 06:59:42 +02:00
2017-03-05 16:44:50 +01:00
if ( tsize ! = thumbnail_size ) {
2017-06-09 05:23:50 +02:00
2017-03-05 16:44:50 +01:00
cache_valid = false ;
2016-07-09 16:19:49 +02:00
memdelete ( f ) ;
2017-03-05 16:44:50 +01:00
} else if ( last_modtime ! = modtime ) {
2015-05-31 06:59:42 +02:00
2016-07-09 16:19:49 +02:00
String last_md5 = f - > get_line ( ) ;
String md5 = FileAccess : : get_md5 ( item . path ) ;
memdelete ( f ) ;
2017-03-05 16:44:50 +01:00
if ( last_md5 ! = md5 ) {
2016-07-09 16:19:49 +02:00
2017-03-05 16:44:50 +01:00
cache_valid = false ;
2017-06-09 05:23:50 +02:00
2016-07-09 16:19:49 +02:00
} else {
//update modified time
2017-03-05 16:44:50 +01:00
f = FileAccess : : open ( file , FileAccess : : WRITE ) ;
2019-10-31 15:14:49 +01:00
if ( ! f ) {
// Not returning as this would leave the thread hanging and would require
// some proper cleanup/disabling of resource preview generation.
ERR_PRINTS ( " Cannot create file ' " + file + " '. Check user write permissions. " ) ;
} else {
f - > store_line ( itos ( thumbnail_size ) ) ;
f - > store_line ( itos ( has_small_texture ) ) ;
f - > store_line ( itos ( modtime ) ) ;
f - > store_line ( md5 ) ;
memdelete ( f ) ;
}
2016-07-09 16:19:49 +02:00
}
} else {
2015-05-31 06:59:42 +02:00
memdelete ( f ) ;
}
2016-07-09 16:19:49 +02:00
if ( cache_valid ) {
2015-05-31 06:59:42 +02:00
2017-06-09 05:23:50 +02:00
Ref < Image > img ;
img . instance ( ) ;
2018-09-12 13:10:49 +02:00
Ref < Image > small_img ;
small_img . instance ( ) ;
2017-06-09 05:23:50 +02:00
if ( img - > load ( cache_base + " .png " ) ! = OK ) {
2017-03-05 16:44:50 +01:00
cache_valid = false ;
2017-06-09 05:23:50 +02:00
} else {
texture . instance ( ) ;
texture - > create_from_image ( img , Texture : : FLAG_FILTER ) ;
2018-09-12 13:10:49 +02:00
if ( has_small_texture ) {
if ( small_img - > load ( cache_base + " _small.png " ) ! = OK ) {
cache_valid = false ;
} else {
small_texture . instance ( ) ;
small_texture - > create_from_image ( small_img , Texture : : FLAG_FILTER ) ;
}
}
2016-07-09 16:19:49 +02:00
}
2015-05-31 06:59:42 +02:00
}
2016-07-09 16:19:49 +02:00
if ( ! cache_valid ) {
2015-05-31 06:59:42 +02:00
2018-09-12 13:10:49 +02:00
_generate_preview ( texture , small_texture , item , cache_base ) ;
2016-07-09 16:19:49 +02:00
}
}
2018-09-12 13:10:49 +02:00
_preview_ready ( item . path , texture , small_texture , item . id , item . function , item . userdata ) ;
2016-07-09 16:19:49 +02:00
}
2015-05-31 06:59:42 +02:00
}
} else {
preview_mutex - > unlock ( ) ;
}
}
2019-03-01 20:32:49 +01:00
exited = true ;
2015-05-31 06:59:42 +02:00
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : queue_edited_resource_preview ( const Ref < Resource > & p_res , Object * p_receiver , const StringName & p_receiver_func , const Variant & p_userdata ) {
2016-05-27 19:18:40 +02:00
ERR_FAIL_NULL ( p_receiver ) ;
ERR_FAIL_COND ( ! p_res . is_valid ( ) ) ;
preview_mutex - > lock ( ) ;
2017-08-07 12:17:31 +02:00
String path_id = " ID: " + itos ( p_res - > get_instance_id ( ) ) ;
2016-07-03 18:15:15 +02:00
2017-03-05 16:44:50 +01:00
if ( cache . has ( path_id ) & & cache [ path_id ] . last_hash = = p_res - > hash_edited_version ( ) ) {
2016-05-27 19:18:40 +02:00
2017-03-05 16:44:50 +01:00
cache [ path_id ] . order = order + + ;
2018-12-06 16:02:40 +01:00
p_receiver - > call ( p_receiver_func , path_id , cache [ path_id ] . preview , cache [ path_id ] . small_preview , p_userdata ) ;
2016-05-27 19:18:40 +02:00
preview_mutex - > unlock ( ) ;
return ;
}
2016-07-03 18:15:15 +02:00
cache . erase ( path_id ) ; //erase if exists, since it will be regen
2016-05-27 19:18:40 +02:00
QueueItem item ;
2017-03-05 16:44:50 +01:00
item . function = p_receiver_func ;
2017-08-07 12:17:31 +02:00
item . id = p_receiver - > get_instance_id ( ) ;
2017-03-05 16:44:50 +01:00
item . resource = p_res ;
item . path = path_id ;
item . userdata = p_userdata ;
2016-05-27 19:18:40 +02:00
queue . push_back ( item ) ;
preview_mutex - > unlock ( ) ;
preview_sem - > post ( ) ;
}
2015-05-31 06:59:42 +02:00
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : queue_resource_preview ( const String & p_path , Object * p_receiver , const StringName & p_receiver_func , const Variant & p_userdata ) {
2015-05-31 06:59:42 +02:00
ERR_FAIL_NULL ( p_receiver ) ;
preview_mutex - > lock ( ) ;
if ( cache . has ( p_path ) ) {
2017-03-05 16:44:50 +01:00
cache [ p_path ] . order = order + + ;
2018-12-06 16:02:40 +01:00
p_receiver - > call ( p_receiver_func , p_path , cache [ p_path ] . preview , cache [ p_path ] . small_preview , p_userdata ) ;
2015-05-31 06:59:42 +02:00
preview_mutex - > unlock ( ) ;
return ;
}
QueueItem item ;
2017-03-05 16:44:50 +01:00
item . function = p_receiver_func ;
2017-08-07 12:17:31 +02:00
item . id = p_receiver - > get_instance_id ( ) ;
2017-03-05 16:44:50 +01:00
item . path = p_path ;
item . userdata = p_userdata ;
2015-05-31 06:59:42 +02:00
queue . push_back ( item ) ;
preview_mutex - > unlock ( ) ;
preview_sem - > post ( ) ;
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : add_preview_generator ( const Ref < EditorResourcePreviewGenerator > & p_generator ) {
2015-05-31 06:59:42 +02:00
preview_generators . push_back ( p_generator ) ;
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : remove_preview_generator ( const Ref < EditorResourcePreviewGenerator > & p_generator ) {
2016-09-10 20:32:17 +02:00
preview_generators . erase ( p_generator ) ;
}
2017-03-05 16:44:50 +01:00
EditorResourcePreview * EditorResourcePreview : : get_singleton ( ) {
2015-05-31 06:59:42 +02:00
return singleton ;
}
void EditorResourcePreview : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( " _preview_ready " , & EditorResourcePreview : : _preview_ready ) ;
2016-07-03 18:15:15 +02:00
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " queue_resource_preview " , " path " , " receiver " , " receiver_func " , " userdata " ) , & EditorResourcePreview : : queue_resource_preview ) ;
ClassDB : : bind_method ( D_METHOD ( " queue_edited_resource_preview " , " resource " , " receiver " , " receiver_func " , " userdata " ) , & EditorResourcePreview : : queue_edited_resource_preview ) ;
ClassDB : : bind_method ( D_METHOD ( " add_preview_generator " , " generator " ) , & EditorResourcePreview : : add_preview_generator ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_preview_generator " , " generator " ) , & EditorResourcePreview : : remove_preview_generator ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " check_for_invalidation " , " path " ) , & EditorResourcePreview : : check_for_invalidation ) ;
2016-07-03 18:15:15 +02:00
2017-03-05 16:44:50 +01:00
ADD_SIGNAL ( MethodInfo ( " preview_invalidated " , PropertyInfo ( Variant : : STRING , " path " ) ) ) ;
2016-07-03 18:15:15 +02:00
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : check_for_invalidation ( const String & p_path ) {
2016-07-03 18:15:15 +02:00
preview_mutex - > lock ( ) ;
2017-03-05 16:44:50 +01:00
bool call_invalidated = false ;
2016-07-03 18:15:15 +02:00
if ( cache . has ( p_path ) ) {
uint64_t modified_time = FileAccess : : get_modified_time ( p_path ) ;
2017-03-05 16:44:50 +01:00
if ( modified_time ! = cache [ p_path ] . modified_time ) {
2016-07-03 18:15:15 +02:00
cache . erase ( p_path ) ;
2017-03-05 16:44:50 +01:00
call_invalidated = true ;
2016-07-03 18:15:15 +02:00
}
}
preview_mutex - > unlock ( ) ;
2017-03-05 16:44:50 +01:00
if ( call_invalidated ) { //do outside mutex
call_deferred ( " emit_signal " , " preview_invalidated " , p_path ) ;
2016-07-03 18:15:15 +02:00
}
2015-05-31 06:59:42 +02:00
}
2019-02-27 17:31:11 +01:00
void EditorResourcePreview : : start ( ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( thread , " Thread already started. " ) ;
2019-02-27 17:31:11 +01:00
thread = Thread : : create ( _thread_func , this ) ;
}
2019-05-31 08:05:44 +02:00
2019-01-17 13:09:01 +01:00
void EditorResourcePreview : : stop ( ) {
if ( thread ) {
exit = true ;
preview_sem - > post ( ) ;
2019-03-01 20:32:49 +01:00
while ( ! exited ) {
OS : : get_singleton ( ) - > delay_usec ( 10000 ) ;
VisualServer : : get_singleton ( ) - > sync ( ) ; //sync pending stuff, as thread may be blocked on visual server
}
2019-01-17 13:09:01 +01:00
Thread : : wait_to_finish ( thread ) ;
memdelete ( thread ) ;
thread = NULL ;
}
}
2015-05-31 06:59:42 +02:00
EditorResourcePreview : : EditorResourcePreview ( ) {
2019-02-27 17:31:11 +01:00
thread = NULL ;
2017-03-05 16:44:50 +01:00
singleton = this ;
2015-05-31 06:59:42 +02:00
preview_mutex = Mutex : : create ( ) ;
2017-03-05 16:44:50 +01:00
preview_sem = Semaphore : : create ( ) ;
order = 0 ;
exit = false ;
2019-03-01 20:32:49 +01:00
exited = false ;
2015-05-31 06:59:42 +02:00
}
2017-03-05 16:44:50 +01:00
EditorResourcePreview : : ~ EditorResourcePreview ( ) {
2015-05-31 06:59:42 +02:00
2019-01-17 13:09:01 +01:00
stop ( ) ;
2015-05-31 06:59:42 +02:00
memdelete ( preview_mutex ) ;
memdelete ( preview_sem ) ;
}