diff options
55 files changed, 892 insertions, 256 deletions
diff --git a/drivers/d3d12/d3d12ma.cpp b/drivers/d3d12/d3d12ma.cpp index 51171141de..b7c9eb7ec0 100644 --- a/drivers/d3d12/d3d12ma.cpp +++ b/drivers/d3d12/d3d12ma.cpp @@ -43,6 +43,18 @@ #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wnonnull-compare" #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#pragma clang diagnostic ignored "-Wstring-plus-int" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#pragma clang diagnostic ignored "-Wtautological-undefined-compare" +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wunused-private-field" +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" #endif #if defined(_MSC_VER) diff --git a/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp index 128b8bcd03..c4cb99fcaa 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -43,12 +43,20 @@ #pragma GCC diagnostic ignored "-Wshadow" #pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#pragma clang diagnostic ignored "-Wstring-plus-int" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif #include "dxcapi.h" #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop +#elif defined(__clang__) +#pragma clang diagnostic pop #endif #if !defined(_MSC_VER) diff --git a/drivers/d3d12/rendering_context_driver_d3d12.h b/drivers/d3d12/rendering_context_driver_d3d12.h index 2e286b6927..a2d828ded1 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.h +++ b/drivers/d3d12/rendering_context_driver_d3d12.h @@ -46,6 +46,13 @@ #pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#pragma clang diagnostic ignored "-Wstring-plus-int" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" #endif #if defined(AS) @@ -59,6 +66,8 @@ #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop +#elif defined(__clang__) +#pragma clang diagnostic pop #endif using Microsoft::WRL::ComPtr; diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index fb278a4d56..ca2b392e66 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -51,6 +51,12 @@ #pragma GCC diagnostic ignored "-Wshadow" #pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#pragma clang diagnostic ignored "-Wstring-plus-int" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif #include "dxil_validator.h" @@ -63,6 +69,8 @@ extern "C" { #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop +#elif defined(__clang__) +#pragma clang diagnostic pop #endif #if defined(_MSC_VER) @@ -96,11 +104,6 @@ static const D3D12_RANGE VOID_RANGE = {}; static const uint32_t ROOT_CONSTANT_REGISTER = GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER * (RDD::MAX_UNIFORM_SETS + 1); static const uint32_t RUNTIME_DATA_REGISTER = GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER * (RDD::MAX_UNIFORM_SETS + 2); -#ifdef DEV_ENABLED -//#define DEBUG_COUNT_BARRIERS -#define CUSTOM_INFO_QUEUE_ENABLED 0 -#endif - /*****************/ /**** GENERIC ****/ /*****************/ @@ -1462,7 +1465,7 @@ RDD::TextureID RenderingDeviceDriverD3D12::_texture_create_shared_from_slice(Tex uav_desc.Format = RD_TO_D3D12_FORMAT[p_view.format].general_format; } - if (p_slice_type != -1) { + if (p_slice_type != (TextureSliceType)-1) { // Complete description with slicing. switch (p_slice_type) { @@ -1560,7 +1563,7 @@ RDD::TextureID RenderingDeviceDriverD3D12::_texture_create_shared_from_slice(Tex tex_info->states_ptr = owner_tex_info->states_ptr; tex_info->format = p_view.format; tex_info->desc = new_tex_resource_desc; - if (p_slice_type == -1) { + if (p_slice_type == (TextureSliceType)-1) { tex_info->base_layer = owner_tex_info->base_layer; tex_info->layers = owner_tex_info->layers; tex_info->base_mip = owner_tex_info->base_mip; @@ -1741,7 +1744,7 @@ RDD::SamplerID RenderingDeviceDriverD3D12::sampler_create(const SamplerState &p_ slot = 1; } else { for (uint32_t i = 1; i < samplers.size(); i++) { - if (samplers[i].Filter == INT_MAX) { + if ((int)samplers[i].Filter == INT_MAX) { slot = i; break; } @@ -2703,6 +2706,8 @@ D3D12_UNORDERED_ACCESS_VIEW_DESC RenderingDeviceDriverD3D12::_make_ranged_uav_fo uav_desc.Texture3D.MipSlice = mip; uav_desc.Texture3D.WSize >>= p_mipmap_offset; } break; + default: + break; } return uav_desc; @@ -4094,7 +4099,6 @@ RDD::UniformSetID RenderingDeviceDriverD3D12::uniform_set_create(VectorView<Boun { uniform_set_info->resource_states.reserve(resource_states.size()); - uint32_t i = 0; for (const KeyValue<ResourceInfo *, NeededState> &E : resource_states) { UniformSetInfo::StateRequirement sr; sr.resource = E.key; @@ -4102,7 +4106,6 @@ RDD::UniformSetID RenderingDeviceDriverD3D12::uniform_set_create(VectorView<Boun sr.states = E.value.states; sr.shader_uniform_idx_mask = E.value.shader_uniform_idx_mask; uniform_set_info->resource_states.push_back(sr); - i++; } } @@ -6635,7 +6638,7 @@ Error RenderingDeviceDriverD3D12::_initialize_frames(uint32_t p_frame_count) { D3D12MA::ALLOCATION_DESC allocation_desc = {}; allocation_desc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + //CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); uint32_t resource_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_resource_descriptors_per_frame"); uint32_t sampler_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame"); uint32_t misc_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_misc_descriptors_per_frame"); diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index 1782819238..92e8e494d4 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -43,6 +43,13 @@ #pragma GCC diagnostic ignored "-Wswitch" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#pragma clang diagnostic ignored "-Wstring-plus-int" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" #endif #include "d3dx12.h" @@ -59,12 +66,19 @@ #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop +#elif defined(__clang__) +#pragma clang diagnostic pop #endif using Microsoft::WRL::ComPtr; #define D3D12_BITCODE_OFFSETS_NUM_STAGES 3 +#ifdef DEV_ENABLED +//#define DEBUG_COUNT_BARRIERS +#define CUSTOM_INFO_QUEUE_ENABLED 0 +#endif + struct dxil_validator; class RenderingContextDriverD3D12; @@ -257,7 +271,7 @@ private: LocalVector<D3D12_RESOURCE_BARRIER> res_barriers; uint32_t res_barriers_count = 0; uint32_t res_barriers_batch = 0; -#ifdef DEV_ENABLED +#ifdef DEBUG_COUNT_BARRIERS int frame_barriers_count = 0; int frame_barriers_batches_count = 0; uint64_t frame_barriers_cpu_time = 0; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index a1e392cd7d..4bca42bf8b 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -161,6 +161,10 @@ String EditorFileSystemDirectory::get_file_script_class_icon_path(int p_idx) con return files[p_idx]->script_class_icon_path; } +String EditorFileSystemDirectory::get_file_icon_path(int p_idx) const { + return files[p_idx]->icon_path; +} + StringName EditorFileSystemDirectory::get_file_type(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, files.size(), ""); return files[p_idx]->type; @@ -740,6 +744,8 @@ void EditorFileSystem::scan() { scanning = false; _update_pending_script_classes(); _update_pending_scene_groups(); + // Update all icons so they are loaded for the FileSystemDock. + _update_files_icon_path(); emit_signal(SNAME("filesystem_changed")); emit_signal(SNAME("sources_changed"), sources_changed.size() > 0); first_scan = false; @@ -1298,6 +1304,8 @@ void EditorFileSystem::_notification(int p_what) { _update_scan_actions(); _update_pending_script_classes(); _update_pending_scene_groups(); + // Update all icons so they are loaded for the FileSystemDock. + _update_files_icon_path(); emit_signal(SNAME("filesystem_changed")); emit_signal(SNAME("sources_changed"), sources_changed.size() > 0); first_scan = false; @@ -1578,6 +1586,43 @@ String EditorFileSystem::_get_global_script_class(const String &p_type, const St return String(); } +void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info) { + String icon_path; + if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) { + const String &script_path = file_info->deps[0]; // Assuming the first dependency is a script. + if (!script_path.is_empty()) { + String *cached = file_icon_cache.getptr(script_path); + if (cached) { + icon_path = *cached; + } else { + if (ClassDB::is_parent_class(ResourceLoader::get_resource_type(script_path), SNAME("Script"))) { + int script_file; + EditorFileSystemDirectory *efsd = find_file(script_path, &script_file); + if (efsd) { + icon_path = efsd->files[script_file]->script_class_icon_path; + } + } + file_icon_cache.insert(script_path, icon_path); + } + } + } + + file_info->icon_path = icon_path; +} + +void EditorFileSystem::_update_files_icon_path(EditorFileSystemDirectory *edp) { + if (!edp) { + edp = filesystem; + file_icon_cache.clear(); + } + for (EditorFileSystemDirectory *sub_dir : edp->subdirs) { + _update_files_icon_path(sub_dir); + } + for (EditorFileSystemDirectory::FileInfo *fi : edp->files) { + _update_file_icon_path(fi); + } +} + void EditorFileSystem::_update_script_classes() { update_script_mutex.lock(); @@ -1719,6 +1764,8 @@ void EditorFileSystem::update_file(const String &p_file) { void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { bool updated = false; + bool update_files_icon_cache = false; + Vector<EditorFileSystemDirectory::FileInfo *> files_to_update_icon_path; for (const String &file : p_script_paths) { ERR_CONTINUE(file.is_empty()); EditorFileSystemDirectory *fs = nullptr; @@ -1741,6 +1788,9 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { } if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { _queue_update_script_class(file); + if (!fs->files[cpos]->script_class_icon_path.is_empty()) { + update_files_icon_cache = true; + } } if (fs->files[cpos]->type == SNAME("PackedScene")) { _queue_update_scene_groups(file); @@ -1789,6 +1839,7 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { _save_late_updated_files(); //files need to be updated in the re-scan } + const String old_script_class_icon_path = fs->files[cpos]->script_class_icon_path; fs->files[cpos]->type = type; fs->files[cpos]->resource_script_class = script_class; fs->files[cpos]->uid = uid; @@ -1816,6 +1867,11 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { if (fs->files[cpos]->type == SNAME("PackedScene")) { _queue_update_scene_groups(file); } + if (fs->files[cpos]->type == SNAME("Resource")) { + files_to_update_icon_path.push_back(fs->files[cpos]); + } else if (old_script_class_icon_path != fs->files[cpos]->script_class_icon_path) { + update_files_icon_cache = true; + } updated = true; } } @@ -1823,6 +1879,13 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { if (updated) { _update_pending_script_classes(); _update_pending_scene_groups(); + if (update_files_icon_cache) { + _update_files_icon_path(); + } else { + for (EditorFileSystemDirectory::FileInfo *fi : files_to_update_icon_path) { + _update_file_icon_path(fi); + } + } call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later } } diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index cd95d5fb95..88cd67296b 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -66,6 +66,7 @@ class EditorFileSystemDirectory : public Object { String script_class_name; String script_class_extends; String script_class_icon_path; + String icon_path; }; Vector<FileInfo *> files; @@ -91,6 +92,7 @@ public: String get_file_script_class_name(int p_idx) const; //used for scripts String get_file_script_class_extends(int p_idx) const; //used for scripts String get_file_script_class_icon_path(int p_idx) const; //used for scripts + String get_file_icon_path(int p_idx) const; //used for FileSystemDock EditorFileSystemDirectory *get_parent(); @@ -279,6 +281,7 @@ class EditorFileSystem : public Node { void _move_group_files(EditorFileSystemDirectory *efd, const String &p_group_file, const String &p_new_location); HashSet<String> group_file_cache; + HashMap<String, String> file_icon_cache; struct ImportThreadData { const ImportFile *reimport_files; @@ -295,6 +298,9 @@ class EditorFileSystem : public Node { Vector<Ref<EditorFileSystemImportFormatSupportQuery>> import_support_queries; + void _update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info); + void _update_files_icon_path(EditorFileSystemDirectory *edp = nullptr); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 1c22ecd673..44ff95b0f9 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -200,33 +200,6 @@ Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, const String } } -String FileSystemDock::_get_entry_script_icon(const EditorFileSystemDirectory *p_dir, int p_file) { - const PackedStringArray &deps = p_dir->get_file_deps(p_file); - if (deps.is_empty()) { - return String(); - } - - const String &script_path = deps[0]; // Assuming the first dependency is a script. - if (script_path.is_empty() || !ClassDB::is_parent_class(ResourceLoader::get_resource_type(script_path), SNAME("Script"))) { - return String(); - } - - String *cached = icon_cache.getptr(script_path); - if (cached) { - return *cached; - } - - HashMap<String, String>::Iterator I; - int script_file; - EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->find_file(script_path, &script_file); - if (efsd) { - I = icon_cache.insert(script_path, efsd->get_file_script_class_icon_path(script_file)); - } else { - I = icon_cache.insert(script_path, String()); - } - return I->value; -} - bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path) { bool parent_should_expand = false; @@ -316,7 +289,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory FileInfo fi; fi.name = p_dir->get_file(i); fi.type = p_dir->get_file_type(i); - fi.icon_path = _get_entry_script_icon(p_dir, i); + fi.icon_path = p_dir->get_file_icon_path(i); fi.import_broken = !p_dir->get_file_import_is_valid(i); fi.modified_time = p_dir->get_file_modified_time(i); @@ -414,8 +387,6 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo updating_tree = true; TreeItem *root = tree->create_item(); - icon_cache.clear(); - // Handles the favorites. TreeItem *favorites_item = tree->create_item(root); favorites_item->set_icon(0, get_editor_theme_icon(SNAME("Favorites"))); @@ -463,7 +434,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo int index; EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->find_file(favorite, &index); if (dir) { - icon = _get_tree_item_icon(dir->get_file_import_is_valid(index), dir->get_file_type(index), _get_entry_script_icon(dir, index)); + icon = _get_tree_item_icon(dir->get_file_import_is_valid(index), dir->get_file_type(index), dir->get_file_icon_path(index)); } else { icon = get_editor_theme_icon(SNAME("File")); } @@ -1017,7 +988,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { fi.path = favorite; if (efd) { fi.type = efd->get_file_type(index); - fi.icon_path = _get_entry_script_icon(efd, index); + fi.icon_path = efd->get_file_icon_path(index); fi.import_broken = !efd->get_file_import_is_valid(index); fi.modified_time = efd->get_file_modified_time(index); } else { @@ -1110,7 +1081,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { fi.name = efd->get_file(i); fi.path = directory.path_join(fi.name); fi.type = efd->get_file_type(i); - fi.icon_path = _get_entry_script_icon(efd, i); + fi.icon_path = efd->get_file_icon_path(i); fi.import_broken = !efd->get_file_import_is_valid(i); fi.modified_time = efd->get_file_modified_time(i); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 3fbff3ef19..959ace8eba 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -142,7 +142,6 @@ private: FILE_NEW_SCENE, }; - HashMap<String, String> icon_cache; HashMap<String, Color> folder_colors; Dictionary assigned_folder_colors; @@ -250,7 +249,6 @@ private: void _reselect_items_selected_on_drag_begin(bool reset = false); Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, const String &p_file_type, const String &p_icon_path); - String _get_entry_script_icon(const EditorFileSystemDirectory *p_dir, int p_file); bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false); void _update_tree(const Vector<String> &p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false, bool p_select_in_favorites = false, bool p_unfold_path = false); void _navigate_to_path(const String &p_path, bool p_select_in_favorites = false); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 1f3bf60d24..d7d51d6a04 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -3043,29 +3043,45 @@ void Node3DEditorViewport::_notification(int p_what) { preview_camera->set_icon(get_editor_theme_icon(SNAME("Camera3D"))); Control *gui_base = EditorNode::get_singleton()->get_gui_base(); + const Ref<StyleBox> &information_3d_stylebox = gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)); + view_menu->begin_bulk_theme_override(); - view_menu->add_theme_style_override(CoreStringName(normal), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - view_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - view_menu->add_theme_style_override(SceneStringName(pressed), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - view_menu->add_theme_style_override("focus", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - view_menu->add_theme_style_override("disabled", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); + view_menu->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); + view_menu->add_theme_style_override("normal_mirrored", information_3d_stylebox); + view_menu->add_theme_style_override("hover", information_3d_stylebox); + view_menu->add_theme_style_override("hover_mirrored", information_3d_stylebox); + view_menu->add_theme_style_override("hover_pressed", information_3d_stylebox); + view_menu->add_theme_style_override("hover_pressed_mirrored", information_3d_stylebox); + view_menu->add_theme_style_override(SceneStringName(pressed), information_3d_stylebox); + view_menu->add_theme_style_override("pressed_mirrored", information_3d_stylebox); + view_menu->add_theme_style_override("focus", information_3d_stylebox); + view_menu->add_theme_style_override("focus_mirrored", information_3d_stylebox); + view_menu->add_theme_style_override("disabled", information_3d_stylebox); + view_menu->add_theme_style_override("disabled_mirrored", information_3d_stylebox); view_menu->end_bulk_theme_override(); preview_camera->begin_bulk_theme_override(); - preview_camera->add_theme_style_override(CoreStringName(normal), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - preview_camera->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - preview_camera->add_theme_style_override(SceneStringName(pressed), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - preview_camera->add_theme_style_override("focus", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - preview_camera->add_theme_style_override("disabled", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); + preview_camera->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); + preview_camera->add_theme_style_override("normal_mirrored", information_3d_stylebox); + preview_camera->add_theme_style_override("hover", information_3d_stylebox); + preview_camera->add_theme_style_override("hover_mirrored", information_3d_stylebox); + preview_camera->add_theme_style_override("hover_pressed", information_3d_stylebox); + preview_camera->add_theme_style_override("hover_pressed_mirrored", information_3d_stylebox); + preview_camera->add_theme_style_override(SceneStringName(pressed), information_3d_stylebox); + preview_camera->add_theme_style_override("pressed_mirrored", information_3d_stylebox); + preview_camera->add_theme_style_override("focus", information_3d_stylebox); + preview_camera->add_theme_style_override("focus_mirrored", information_3d_stylebox); + preview_camera->add_theme_style_override("disabled", information_3d_stylebox); + preview_camera->add_theme_style_override("disabled_mirrored", information_3d_stylebox); preview_camera->end_bulk_theme_override(); frame_time_gradient->set_color(0, get_theme_color(SNAME("success_color"), EditorStringName(Editor))); frame_time_gradient->set_color(1, get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); frame_time_gradient->set_color(2, get_theme_color(SNAME("error_color"), EditorStringName(Editor))); - info_label->add_theme_style_override(CoreStringName(normal), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); + info_label->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); - frame_time_panel->add_theme_style_override(SceneStringName(panel), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); + frame_time_panel->add_theme_style_override(SceneStringName(panel), information_3d_stylebox); // Set a minimum width to prevent the width from changing all the time // when numbers vary rapidly. This minimum width is set based on a // GPU time of 999.99 ms in the current editor language. @@ -3073,8 +3089,8 @@ void Node3DEditorViewport::_notification(int p_what) { frame_time_panel->set_custom_minimum_size(Size2(min_width, 0) * EDSCALE); frame_time_vbox->add_theme_constant_override("separation", Math::round(-1 * EDSCALE)); - cinema_label->add_theme_style_override(CoreStringName(normal), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); - locked_label->add_theme_style_override(CoreStringName(normal), gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles))); + cinema_label->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); + locked_label->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); } break; case NOTIFICATION_DRAG_END: { @@ -5372,6 +5388,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p cinema_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 10 * EDSCALE); cinema_label->set_h_grow_direction(GROW_DIRECTION_END); cinema_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + cinema_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); surface->add_child(cinema_label); cinema_label->set_text(TTR("Cinematic Preview")); cinema_label->hide(); @@ -5379,6 +5396,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p locked_label = memnew(Label); locked_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + locked_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); locked_label->set_h_size_flags(SIZE_SHRINK_CENTER); bottom_center_vbox->add_child(locked_label); locked_label->set_text(TTR("View Rotation Locked")); diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 169a3e2373..b6697b6d2c 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -1782,11 +1782,6 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme style_content_panel_vp->set_content_margin_individual(p_config.border_width * 2, p_config.base_margin * EDSCALE, p_config.border_width * 2, p_config.border_width * 2); p_theme->set_stylebox("Content", EditorStringName(EditorStyles), style_content_panel_vp); - // 2D/CanvasItem editor - Ref<StyleBoxFlat> style_canvas_editor_info = make_flat_stylebox(Color(0.0, 0.0, 0.0, 0.2)); - style_canvas_editor_info->set_expand_margin_all(4 * EDSCALE); - p_theme->set_stylebox("CanvasItemInfoOverlay", EditorStringName(EditorStyles), style_canvas_editor_info); - // 3D/Spatial editor. Ref<StyleBoxFlat> style_info_3d_viewport = p_config.base_style->duplicate(); style_info_3d_viewport->set_bg_color(style_info_3d_viewport->get_bg_color() * Color(1, 1, 1, 0.5)); diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers.cfg b/modules/gdscript/tests/scripts/completion/common/identifiers.cfg new file mode 100644 index 0000000000..871a404e3a --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers.cfg @@ -0,0 +1,21 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: self.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers.gd b/modules/gdscript/tests/scripts/completion/common/identifiers.gd new file mode 100644 index 0000000000..efbafbee8e --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers.gd @@ -0,0 +1,16 @@ +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(): + t➡ diff --git a/modules/gdscript/tests/scripts/completion/common/self.cfg b/modules/gdscript/tests/scripts/completion/common/self.cfg new file mode 100644 index 0000000000..871a404e3a --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/self.cfg @@ -0,0 +1,21 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: self.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/self.gd b/modules/gdscript/tests/scripts/completion/common/self.gd new file mode 100644 index 0000000000..9ad2fbea51 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/self.gd @@ -0,0 +1,16 @@ +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(): + self.➡ diff --git a/modules/gdscript/tests/scripts/completion/filter/organized_export.cfg b/modules/gdscript/tests/scripts/completion/filter/organized_export.cfg new file mode 100644 index 0000000000..961a9ea58d --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/filter/organized_export.cfg @@ -0,0 +1,6 @@ +[output] +exclude=[ + {"display": "Test Category"}, + {"display": "Test Group"}, + {"display": "Test Subgroup"}, +] diff --git a/modules/gdscript/tests/scripts/completion/filter/organized_export.gd b/modules/gdscript/tests/scripts/completion/filter/organized_export.gd new file mode 100644 index 0000000000..189608904c --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/filter/organized_export.gd @@ -0,0 +1,8 @@ +extends CPUParticles2D + +@export_category("Test Category") +@export_group("Test Group") +@export_subgroup("Test Subgroup") + +func _init(): + ➡ diff --git a/modules/gdscript/tests/scripts/completion/filter/usage_internal.cfg b/modules/gdscript/tests/scripts/completion/filter/usage_internal.cfg new file mode 100644 index 0000000000..8c5bff5eac --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/filter/usage_internal.cfg @@ -0,0 +1,4 @@ +[output] +exclude=[ + {"display": "messages"}, +] diff --git a/modules/gdscript/tests/scripts/completion/filter/usage_internal.gd b/modules/gdscript/tests/scripts/completion/filter/usage_internal.gd new file mode 100644 index 0000000000..484c1c0d10 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/filter/usage_internal.gd @@ -0,0 +1,3 @@ +func test(): + var trans = Translation.new() + trans.➡ diff --git a/modules/gdscript/tests/scripts/completion/types/local/interfered.cfg b/modules/gdscript/tests/scripts/completion/types/local/interfered.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/interfered.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/local/interfered.gd b/modules/gdscript/tests/scripts/completion/types/local/interfered.gd new file mode 100644 index 0000000000..f003c366a4 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/interfered.gd @@ -0,0 +1,8 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +func a(): + var test := A.new() + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/local/no_type.cfg b/modules/gdscript/tests/scripts/completion/types/local/no_type.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/no_type.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/local/no_type.gd b/modules/gdscript/tests/scripts/completion/types/local/no_type.gd new file mode 100644 index 0000000000..f6b5ae3aef --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/no_type.gd @@ -0,0 +1,8 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +func a(): + var test = A.new() + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/local/typehint.cfg b/modules/gdscript/tests/scripts/completion/types/local/typehint.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/typehint.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/local/typehint.gd b/modules/gdscript/tests/scripts/completion/types/local/typehint.gd new file mode 100644 index 0000000000..24bcfc04fc --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/typehint.gd @@ -0,0 +1,8 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +func a(): + var test: A + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/local/typehint_broad.cfg b/modules/gdscript/tests/scripts/completion/types/local/typehint_broad.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/typehint_broad.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/local/typehint_broad.gd b/modules/gdscript/tests/scripts/completion/types/local/typehint_broad.gd new file mode 100644 index 0000000000..88b4812c30 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/typehint_broad.gd @@ -0,0 +1,8 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +func a(): + var test: Node = A.new() + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/local/typehint_incompatible.cfg b/modules/gdscript/tests/scripts/completion/types/local/typehint_incompatible.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/typehint_incompatible.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/local/typehint_incompatible.gd b/modules/gdscript/tests/scripts/completion/types/local/typehint_incompatible.gd new file mode 100644 index 0000000000..8e226546f3 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/local/typehint_incompatible.gd @@ -0,0 +1,8 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +func a(): + var test: A = Node.new() + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/member/interfered.cfg b/modules/gdscript/tests/scripts/completion/types/member/interfered.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/interfered.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/member/interfered.gd b/modules/gdscript/tests/scripts/completion/types/member/interfered.gd new file mode 100644 index 0000000000..069abd7891 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/interfered.gd @@ -0,0 +1,9 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +var test := A.new() + +func a(): + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/member/no_type.cfg b/modules/gdscript/tests/scripts/completion/types/member/no_type.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/no_type.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/member/no_type.gd b/modules/gdscript/tests/scripts/completion/types/member/no_type.gd new file mode 100644 index 0000000000..9bb9549e97 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/no_type.gd @@ -0,0 +1,9 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +var test = A.new() + +func a(): + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/member/typehint.cfg b/modules/gdscript/tests/scripts/completion/types/member/typehint.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/typehint.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/member/typehint.gd b/modules/gdscript/tests/scripts/completion/types/member/typehint.gd new file mode 100644 index 0000000000..7763a2e898 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/typehint.gd @@ -0,0 +1,9 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +var test: A + +func a(): + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/member/typehint_broad.cfg b/modules/gdscript/tests/scripts/completion/types/member/typehint_broad.cfg new file mode 100644 index 0000000000..81401316ec --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/typehint_broad.cfg @@ -0,0 +1,13 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, +] +exclude=[ + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/member/typehint_broad.gd b/modules/gdscript/tests/scripts/completion/types/member/typehint_broad.gd new file mode 100644 index 0000000000..a8506705a1 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/typehint_broad.gd @@ -0,0 +1,9 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +var test: Node = A.new() + +func a(): + test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/types/member/typehint_incompatible.cfg b/modules/gdscript/tests/scripts/completion/types/member/typehint_incompatible.cfg new file mode 100644 index 0000000000..8b68d51a89 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/typehint_incompatible.cfg @@ -0,0 +1,12 @@ +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, +] diff --git a/modules/gdscript/tests/scripts/completion/types/member/typehint_incompatible.gd b/modules/gdscript/tests/scripts/completion/types/member/typehint_incompatible.gd new file mode 100644 index 0000000000..8b5a80cfb3 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/types/member/typehint_incompatible.gd @@ -0,0 +1,9 @@ +extends Node + +const A := preload("res://completion/class_a.notest.gd") + +var test: A = Node.new() + +func a(): + test.➡ + pass diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 2408d7231b..682d20022f 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -99,7 +99,7 @@ void MultiplayerSpawner::add_spawnable_scene(const String &p_path) { SpawnableScene sc; sc.path = p_path; if (Engine::get_singleton()->is_editor_hint()) { - ERR_FAIL_COND(!FileAccess::exists(p_path)); + ERR_FAIL_COND(!ResourceLoader::exists(p_path)); } spawnable_scenes.push_back(sc); #ifdef TOOLS_ENABLED diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js index d7baece781..4bca13d2d6 100644 --- a/platform/web/js/libs/library_godot_audio.js +++ b/platform/web/js/libs/library_godot_audio.js @@ -423,37 +423,32 @@ class SampleNode { this.streamObjectId = params.streamObjectId; /** @type {number} */ this.offset = options.offset ?? 0; - /** @type {LoopMode} */ + /** @type {number} */ this.startTime = options.startTime ?? 0; + /** @type {boolean} */ + this.isPaused = false; /** @type {number} */ this.pauseTime = 0; /** @type {number} */ this._playbackRate = 44100; /** @type {LoopMode} */ - this._loopMode = 'disabled'; + this.loopMode = 'disabled'; /** @type {number} */ this._pitchScale = 1; + /** @type {number} */ + this._sourceStartTime = 0; /** @type {Map<Bus, SampleNodeBus>} */ this._sampleNodeBuses = new Map(); - /** @type {AudioBufferSourceNode} */ + /** @type {AudioBufferSourceNode | null} */ this._source = GodotAudio.ctx.createBufferSource(); + /** @type {AudioBufferSourceNode["onended"]} */ + this._onended = null; this.setPlaybackRate(options.playbackRate ?? 44100); - this.setLoopMode(options.loopMode ?? this.getSample().loopMode ?? 'disabled'); + this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled'; this._source.buffer = this.getSample().getAudioBuffer(); - /** @type {SampleNode} */ - // eslint-disable-next-line consistent-this - const self = this; - this._source.addEventListener('ended', (_) => { - switch (self.getSample().loopMode) { - case 'disabled': - GodotAudio.SampleNode.stopSampleNode(self.id); - break; - default: - // do nothing - } - }); + this._addEndedListener(); const bus = GodotAudio.Bus.getBus(params.busIndex); const sampleNodeBus = this.getSampleNodeBus(bus); @@ -461,31 +456,6 @@ class SampleNode { } /** - * Gets the loop mode of the current instance. - * @returns {LoopMode} - */ - getLoopMode() { - return this._loopMode; - } - - /** - * Sets the loop mode of the current instance. - * @param {LoopMode} val Value to set. - * @returns {void} - */ - setLoopMode(val) { - this._loopMode = val; - switch (val) { - case 'forward': - case 'backward': - this._source.loop = true; - break; - default: - this._source.loop = false; - } - } - - /** * Gets the playback rate. * @returns {number} */ @@ -542,7 +512,8 @@ class SampleNode { * @returns {void} */ start() { - this._source.start(this.offset); + this._resetSourceStartTime(); + this._source.start(this.startTime, this.offset); } /** @@ -550,32 +521,31 @@ class SampleNode { * @returns {void} */ stop() { - this._source.stop(); this.clear(); } /** + * Restarts the `SampleNode`. + */ + restart() { + this.isPaused = false; + this.pauseTime = 0; + this._resetSourceStartTime(); + this._restart(); + } + + /** * Pauses the `SampleNode`. * @param {boolean} [enable=true] State of the pause. * @returns {void} */ pause(enable = true) { if (enable) { - this.pauseTime = (GodotAudio.ctx.currentTime - this.startTime) / this.playbackRate; - this._source.stop(); - return; - } - - if (this.pauseTime === 0) { + this._pause(); return; } - this._source.disconnect(); - this._source = GodotAudio.ctx.createBufferSource(); - - this._source.buffer = this.getSample().getAudioBuffer(); - this._source.connect(this._gain); - this._source.start(this.offset + this.pauseTime); + this._unpause(); } /** @@ -623,26 +593,114 @@ class SampleNode { * @returns {void} */ clear() { - this._source.stop(); - this._source.disconnect(); - this._source = null; + this.isPaused = false; + this.pauseTime = 0; + + if (this._source != null) { + this._source.removeEventListener('ended', this._onended); + this._onended = null; + this._source.stop(); + this._source.disconnect(); + this._source = null; + } for (const sampleNodeBus of this._sampleNodeBuses.values()) { sampleNodeBus.clear(); } this._sampleNodeBuses.clear(); - this._sampleNodeBuses = null; GodotAudio.SampleNode.delete(this.id); } /** + * Resets the source start time + * @returns {void} + */ + _resetSourceStartTime() { + this._sourceStartTime = GodotAudio.ctx.currentTime; + } + + /** * Syncs the `AudioNode` playback rate based on the `SampleNode` playback rate and pitch scale. * @returns {void} */ _syncPlaybackRate() { this._source.playbackRate.value = this.getPlaybackRate() * this.getPitchScale(); } + + /** + * Restarts the `SampleNode`. + * Honors `isPaused` and `pauseTime`. + * @returns {void} + */ + _restart() { + this._source.disconnect(); + this._source = GodotAudio.ctx.createBufferSource(); + this._source.buffer = this.getSample().getAudioBuffer(); + + // Make sure that we connect the new source to the sample node bus. + for (const sampleNodeBus of this._sampleNodeBuses.values()) { + this.connect(sampleNodeBus.getInputNode()); + } + + this._addEndedListener(); + const pauseTime = this.isPaused + ? this.pauseTime + : 0; + this._source.start(this.startTime, this.offset + pauseTime); + } + + /** + * Pauses the `SampleNode`. + * @returns {void} + */ + _pause() { + this.isPaused = true; + this.pauseTime = (GodotAudio.ctx.currentTime - this._sourceStartTime) / this.getPlaybackRate(); + this._source.stop(); + } + + /** + * Unpauses the `SampleNode`. + * @returns {void} + */ + _unpause() { + this._restart(); + this.isPaused = false; + this.pauseTime = 0; + } + + /** + * Adds an "ended" listener to the source node to repeat it if necessary. + * @returns {void} + */ + _addEndedListener() { + if (this._onended != null) { + this._source.removeEventListener('ended', this._onended); + } + + /** @type {SampleNode} */ + // eslint-disable-next-line consistent-this + const self = this; + this._onended = (_) => { + if (self.isPaused) { + return; + } + + switch (self.getSample().loopMode) { + case 'disabled': + self.stop(); + break; + case 'forward': + case 'backward': + self.restart(); + break; + default: + // do nothing + } + }; + this._source.addEventListener('ended', this._onended); + } } /** diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index e2a61a9384..fc7f3ac36d 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -15,5 +15,5 @@ // For internal debugging: //#define THORVG_LOG_ENABLED -#define THORVG_VERSION_STRING "0.13.7" +#define THORVG_VERSION_STRING "0.13.8" #endif diff --git a/thirdparty/thorvg/patches/pr2338-float_t.patch b/thirdparty/thorvg/patches/pr2338-float_t.patch deleted file mode 100644 index 20b9050cbb..0000000000 --- a/thirdparty/thorvg/patches/pr2338-float_t.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp -index f59994aae6..b2ce38852c 100644 ---- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp -+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp -@@ -709,7 +709,7 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** - *ref = _idFromUrl((const char*)(str + 3)); - return true; - } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') { -- float_t th, ts, tb; -+ float th, ts, tb; - const char *content, *hue, *satuation, *brightness; - content = str + 4; - content = _skipSpace(content, nullptr); diff --git a/thirdparty/thorvg/src/common/tvgLock.h b/thirdparty/thorvg/src/common/tvgLock.h index 5dd3d5a624..d8bf7269f6 100644 --- a/thirdparty/thorvg/src/common/tvgLock.h +++ b/thirdparty/thorvg/src/common/tvgLock.h @@ -25,6 +25,8 @@ #ifdef THORVG_THREAD_SUPPORT +#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR + #include <mutex> namespace tvg { diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index b2ce38852c..f29bc09b77 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -175,14 +175,14 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt else if (strstr(str, "pc")) parsedValue *= PX_PER_PC; else if (strstr(str, "in")) parsedValue *= PX_PER_IN; else if (strstr(str, "%")) { - if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h; - else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w; - else //if other then it's radius + if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h; + else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w; + else //if other than it's radius { float max = svgParse->global.w; if (max < svgParse->global.h) max = svgParse->global.h; - parsedValue = (parsedValue / 100.0) * max; + parsedValue = (parsedValue / 100.0f) * max; } } //TODO: Implement 'em', 'ex' attributes @@ -580,7 +580,7 @@ static constexpr struct }; -static bool _hslToRgb(float hue, float satuation, float brightness, uint8_t* red, uint8_t* green, uint8_t* blue) +static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* red, uint8_t* green, uint8_t* blue) { if (!red || !green || !blue) return false; @@ -588,12 +588,12 @@ static bool _hslToRgb(float hue, float satuation, float brightness, uint8_t* red float _red = 0, _green = 0, _blue = 0; uint32_t i = 0; - if (mathZero(satuation)) _red = _green = _blue = brightness; + if (mathZero(saturation)) _red = _green = _blue = brightness; else { if (mathEqual(hue, 360.0)) hue = 0.0f; hue /= 60.0f; - v = (brightness <= 0.5f) ? (brightness * (1.0f + satuation)) : (brightness + satuation - (brightness * satuation)); + v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation)); p = brightness + brightness - v; if (!mathZero(v)) sv = (v - p) / v; @@ -662,7 +662,7 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** unsigned char tr, tg, tb; if (len == 4 && str[0] == '#') { - //Case for "#456" should be interprete as "#445566" + //Case for "#456" should be interpreted as "#445566" if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) { char tmp[3] = { '\0', '\0', '\0' }; tmp[0] = str[1]; @@ -710,7 +710,7 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** return true; } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') { float th, ts, tb; - const char *content, *hue, *satuation, *brightness; + const char *content, *hue, *saturation, *brightness; content = str + 4; content = _skipSpace(content, nullptr); if (_parseNumber(&content, &hue, &th) && hue) { @@ -718,12 +718,12 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** hue = _skipSpace(hue, nullptr); hue = (char*)_skipComma(hue); hue = _skipSpace(hue, nullptr); - if (_parseNumber(&hue, &satuation, &ts) && satuation && *satuation == '%') { + if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') { ts /= 100.0f; - satuation = _skipSpace(satuation + 1, nullptr); - satuation = (char*)_skipComma(satuation); - satuation = _skipSpace(satuation, nullptr); - if (_parseNumber(&satuation, &brightness, &tb) && brightness && *brightness == '%') { + saturation = _skipSpace(saturation + 1, nullptr); + saturation = (char*)_skipComma(saturation); + saturation = _skipSpace(saturation, nullptr); + if (_parseNumber(&saturation, &brightness, &tb) && brightness && *brightness == '%') { tb /= 100.0f; brightness = _skipSpace(brightness + 1, nullptr); if (brightness && brightness[0] == ')' && brightness[1] == '\0') { @@ -2119,7 +2119,72 @@ static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const cha } -//TODO: Implement 'text' primitive +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} textTags[] = { + {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgTextNode, x)}, + {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgTextNode, y)}, + {"font-size", SvgParserLengthType::Vertical, sizeof("font-size"), offsetof(SvgTextNode, fontSize)} +}; + + +static bool _attrParseTextNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgTextNode* text = &(node->node.text); + + unsigned char* array; + int sz = strlen(key); + + array = (unsigned char*)text; + for (unsigned int i = 0; i < sizeof(textTags) / sizeof(textTags[0]); i++) { + if (textTags[i].sz - 1 == sz && !strncmp(textTags[i].tag, key, sz)) { + *((float*)(array + textTags[i].offset)) = _toFloat(loader->svgParse, value, textTags[i].type); + return true; + } + } + + if (!strcmp(key, "font-family")) { + if (text->fontFamily && value) free(text->fontFamily); + text->fontFamily = strdup(value); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static SvgNode* _createTextNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Text); + if (!loader->svgParse->node) return nullptr; + + //TODO: support the def font and size as used in a system? + loader->svgParse->node->node.text.fontSize = 10.0f; + loader->svgParse->node->node.text.fontFamily = nullptr; + + func(buf, bufLength, _attrParseTextNode, loader); + + return loader->svgParse->node; +} + + static constexpr struct { const char* tag; @@ -2134,7 +2199,8 @@ static constexpr struct {"rect", sizeof("rect"), _createRectNode}, {"polyline", sizeof("polyline"), _createPolylineNode}, {"line", sizeof("line"), _createLineNode}, - {"image", sizeof("image"), _createImageNode} + {"image", sizeof("image"), _createImageNode}, + {"text", sizeof("text"), _createTextNode} }; @@ -3122,6 +3188,20 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) to->node.use.symbol = from->node.use.symbol; break; } + case SvgNodeType::Text: { + to->node.text.x = from->node.text.x; + to->node.text.y = from->node.text.y; + to->node.text.fontSize = from->node.text.fontSize; + if (from->node.text.text) { + if (to->node.text.text) free(to->node.text.text); + to->node.text.text = strdup(from->node.text.text); + } + if (from->node.text.fontFamily) { + if (to->node.text.fontFamily) free(to->node.text.fontFamily); + to->node.text.fontFamily = strdup(from->node.text.fontFamily); + } + break; + } default: { break; } @@ -3174,27 +3254,12 @@ static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc) } -static constexpr struct -{ - const char* tag; - size_t sz; -} popArray[] = { - {"g", sizeof("g")}, - {"svg", sizeof("svg")}, - {"defs", sizeof("defs")}, - {"mask", sizeof("mask")}, - {"clipPath", sizeof("clipPath")}, - {"style", sizeof("style")}, - {"symbol", sizeof("symbol")} -}; - - static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content) { content = _skipSpace(content, nullptr); - for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) { - if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) { + for (unsigned int i = 0; i < sizeof(groupTags) / sizeof(groupTags[0]); i++) { + if (!strncmp(content, groupTags[i].tag, groupTags[i].sz - 1)) { loader->stack.pop(); break; } @@ -3259,7 +3324,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes); loader->cssStyle = node; loader->doc->node.doc.style = node; - loader->style = true; + loader->openedTag = OpenedTagType::Style; } } else { node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); @@ -3275,9 +3340,12 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, else parent = loader->doc; node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); if (node && !empty) { - auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr); - loader->stack.push(defs); - loader->currentGraphicsNode = node; + if (!strcmp(tagName, "text")) loader->openedTag = OpenedTagType::Text; + else { + auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr); + loader->stack.push(defs); + loader->currentGraphicsNode = node; + } } } else if ((gradientMethod = _findGradientFactory(tagName))) { SvgStyleGradient* gradient; @@ -3310,6 +3378,15 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, } +static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length) +{ + auto text = &loader->svgParse->node->node.text; + if (text->text) free(text->text); + text->text = strDuplicate(content, length); + loader->openedTag = OpenedTagType::Other; +} + + static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length) { char* tag; @@ -3342,7 +3419,7 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte free(tag); free(name); } - loader->style = false; + loader->openedTag = OpenedTagType::Other; } @@ -3365,7 +3442,8 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content } case SimpleXMLType::Data: case SimpleXMLType::CData: { - if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length); + if (loader->openedTag == OpenedTagType::Style) _svgLoaderParserXmlCssStyle(loader, content, length); + else if (loader->openedTag == OpenedTagType::Text) _svgLoaderParserText(loader, content, length); break; } case SimpleXMLType::DoctypeChild: { @@ -3587,6 +3665,11 @@ static void _freeNode(SvgNode* node) free(node->node.image.href); break; } + case SvgNodeType::Text: { + free(node->node.text.text); + free(node->node.text.fontFamily); + break; + } default: { break; } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index 283f56561b..5661c8ae82 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h @@ -374,6 +374,14 @@ struct SvgCssStyleNode { }; +struct SvgTextNode +{ + char* text; + char* fontFamily; + float x, y; + float fontSize; +}; + struct SvgLinearGradient { float x1; @@ -518,6 +526,7 @@ struct SvgNode SvgClipNode clip; SvgCssStyleNode cssStyle; SvgSymbolNode symbol; + SvgTextNode text; } node; ~SvgNode(); }; @@ -545,11 +554,18 @@ struct SvgNodeIdPair char *id; }; +enum class OpenedTagType : uint8_t +{ + Other = 0, + Style, + Text +}; + struct SvgLoaderData { Array<SvgNode*> stack; SvgNode* doc = nullptr; - SvgNode* def = nullptr; + SvgNode* def = nullptr; //also used to store nested graphic nodes SvgNode* cssStyle = nullptr; Array<SvgStyleGradient*> gradients; SvgStyleGradient* latestGradient = nullptr; //For stops @@ -559,7 +575,7 @@ struct SvgLoaderData Array<char*> images; //embedded images int level = 0; bool result = false; - bool style = false; + OpenedTagType openedTag = OpenedTagType::Other; SvgNode* currentGraphicsNode = nullptr; }; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp index 7e7efed3fc..b048695a23 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -67,6 +67,14 @@ static Box _boundingBox(const Shape* shape) } +static Box _boundingBox(const Text* text) +{ + float x, y, w, h; + text->bounds(&x, &y, &w, &h, false); + return {x, y, w, h}; +} + + static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf) { gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13; @@ -79,7 +87,7 @@ static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf) } -static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity) +static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop* stops; int stopCount = 0; @@ -134,7 +142,7 @@ static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* } -static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity) +static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop *stops; int stopCount = 0; @@ -320,14 +328,15 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg); if (style->fill.paint.gradient->type == SvgGradientType::Linear) { - auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity); - vg->fill(std::move(linear)); + auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + vg->fill(std::move(linear)); } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { - auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity); - vg->fill(std::move(radial)); + auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + vg->fill(std::move(radial)); } } else if (style->fill.paint.url) { //TODO: Apply the color pointed by url + TVGLOG("SVG", "The fill's url not supported."); } else if (style->fill.paint.curColor) { //Apply the current style color vg->fill(style->color.r, style->color.g, style->color.b, style->fill.opacity); @@ -363,14 +372,15 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg); if (style->stroke.paint.gradient->type == SvgGradientType::Linear) { - auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity); + auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity); vg->stroke(std::move(linear)); } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) { - auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity); + auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity); vg->stroke(std::move(radial)); } } else if (style->stroke.paint.url) { //TODO: Apply the color pointed by url + TVGLOG("SVG", "The stroke's url not supported."); } else if (style->stroke.paint.curColor) { //Apply the current style color vg->stroke(style->color.r, style->color.g, style->color.b, style->stroke.opacity); @@ -772,6 +782,61 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod } +static void _applyTextFill(SvgStyleProperty* style, Text* text, const Box& vBox) +{ + //If fill property is nullptr then do nothing + if (style->fill.paint.none) { + //Do nothing + } else if (style->fill.paint.gradient) { + Box bBox = vBox; + if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(text); + + if (style->fill.paint.gradient->type == SvgGradientType::Linear) { + auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + text->fill(std::move(linear)); + } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { + auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); + text->fill(std::move(radial)); + } + } else if (style->fill.paint.url) { + //TODO: Apply the color pointed by url + TVGLOG("SVG", "The fill's url not supported."); + } else if (style->fill.paint.curColor) { + //Apply the current style color + text->fill(style->color.r, style->color.g, style->color.b); + text->opacity(style->fill.opacity); + } else { + //Apply the fill color + text->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b); + text->opacity(style->fill.opacity); + } +} + + +static unique_ptr<Text> _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath) +{ + auto textNode = &node->node.text; + if (!textNode->text) return nullptr; + auto text = Text::gen(); + + Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (node->transform) textTransform = *node->transform; + mathTranslateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize); + text->transform(textTransform); + + //TODO: handle def values of font and size as used in a system? + const float ptPerPx = 0.75f; //1 pt = 1/72; 1 in = 96 px; -> 72/96 = 0.75 + auto fontSizePt = textNode->fontSize * ptPerPx; + if (textNode->fontFamily) text->font(textNode->fontFamily, fontSizePt); + text->text(textNode->text); + + _applyTextFill(node->style, text.get(), vBox); + _applyComposition(loaderData, text.get(), node, vBox, svgPath); + + return text; +} + + static unique_ptr<Scene> _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite) { /* Exception handling: Prevent invalid SVG data input. @@ -800,6 +865,9 @@ static unique_ptr<Scene> _sceneBuildHelper(SvgLoaderData& loaderData, const SvgN scene->push(std::move(image)); if (isMaskWhite) *isMaskWhite = false; } + } else if ((*child)->type == SvgNodeType::Text) { + auto text = _textBuildHelper(loaderData, *child, vBox, svgPath); + if (text) scene->push(std::move(text)); } else if ((*child)->type != SvgNodeType::Mask) { auto shape = _shapeBuildHelper(loaderData, *child, vBox, svgPath); if (shape) { diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h index 3d73075a4a..231410cdac 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h @@ -26,18 +26,6 @@ #include "tvgCommon.h" #include "tvgRender.h" -#include <algorithm> - -#if 0 -#include <sys/time.h> -static double timeStamp() -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (tv.tv_sec + tv.tv_usec / 1000000.0); -} -#endif - #define SW_CURVE_TYPE_POINT 0 #define SW_CURVE_TYPE_CUBIC 1 #define SW_ANGLE_PI (180L << 16) diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h index 8ec2bc0c47..bab534bba2 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h @@ -34,14 +34,6 @@ struct AASpans int32_t yEnd; }; -static inline void _swap(float& a, float& b, float& tmp) -{ - tmp = a; - a = b; - b = tmp; -} - - //Careful! Shared resource, No support threading static float dudx, dvdx; static float dxdya, dxdyb, dudya, dvdya; @@ -85,7 +77,7 @@ static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* int32_t sh = image->h; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t vv = 0, uu = 0; - int32_t minx = INT32_MAX, maxx = INT32_MIN; + int32_t minx = INT32_MAX, maxx = 0; float dx, u, v, iptr; SwSpan* span = nullptr; //used only when rle based. @@ -113,7 +105,7 @@ static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* if (!region) { minx = INT32_MAX; - maxx = INT32_MIN; + maxx = 0; //one single row, could be consisted of multiple spans. while (span->y == y && spanIdx < image->rle->size) { if (minx > span->x) minx = span->x; @@ -278,7 +270,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage int32_t dw = surface->stride; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t vv = 0, uu = 0; - int32_t minx = INT32_MAX, maxx = INT32_MIN; + int32_t minx = INT32_MAX, maxx = 0; float dx, u, v, iptr; uint32_t* buf; SwSpan* span = nullptr; //used only when rle based. @@ -307,7 +299,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage if (!region) { minx = INT32_MAX; - maxx = INT32_MIN; + maxx = 0; //one single row, could be consisted of multiple spans. while (span->y == y && spanIdx < image->rle->size) { if (minx > span->x) minx = span->x; @@ -455,7 +447,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, int32_t dw = surface->stride; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t vv = 0, uu = 0; - int32_t minx = INT32_MAX, maxx = INT32_MIN; + int32_t minx = INT32_MAX, maxx = 0; float dx, u, v, iptr; uint32_t* buf; SwSpan* span = nullptr; //used only when rle based. @@ -489,7 +481,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, if (!region) { minx = INT32_MAX; - maxx = INT32_MIN; + maxx = 0; //one single row, could be consisted of multiple spans. while (span->y == y && spanIdx < image->rle->size) { if (minx > span->x) minx = span->x; @@ -650,28 +642,27 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const float off_y; float dxdy[3] = {0.0f, 0.0f, 0.0f}; - float tmp; auto upper = false; //Sort the vertices in ascending Y order if (y[0] > y[1]) { - _swap(x[0], x[1], tmp); - _swap(y[0], y[1], tmp); - _swap(u[0], u[1], tmp); - _swap(v[0], v[1], tmp); + std::swap(x[0], x[1]); + std::swap(y[0], y[1]); + std::swap(u[0], u[1]); + std::swap(v[0], v[1]); } if (y[0] > y[2]) { - _swap(x[0], x[2], tmp); - _swap(y[0], y[2], tmp); - _swap(u[0], u[2], tmp); - _swap(v[0], v[2], tmp); + std::swap(x[0], x[2]); + std::swap(y[0], y[2]); + std::swap(u[0], u[2]); + std::swap(v[0], v[2]); } if (y[1] > y[2]) { - _swap(x[1], x[2], tmp); - _swap(y[1], y[2], tmp); - _swap(u[1], u[2], tmp); - _swap(v[1], v[2], tmp); + std::swap(x[1], x[2]); + std::swap(y[1], y[2]); + std::swap(u[1], u[2]); + std::swap(v[1], v[2]); } //Y indexes @@ -837,7 +828,9 @@ static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwB for (int32_t i = 0; i < height; i++) { aaSpans->lines[i].x[0] = INT32_MAX; - aaSpans->lines[i].x[1] = INT32_MIN; + aaSpans->lines[i].x[1] = 0; + aaSpans->lines[i].length[0] = 0; + aaSpans->lines[i].length[1] = 0; } return aaSpans; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index f689179928..78108af095 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -86,7 +86,7 @@ struct SwShapeTask : SwTask Additionally, the stroke style should not be dashed. */ bool antialiasing(float strokeWidth) { - return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst; + return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst || rshape->strokeTrim(); } float validStrokeWidth() diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index d8dd40d45b..4f069ece97 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp @@ -420,6 +420,7 @@ static bool _axisAlignedRect(const SwOutline* outline) { //Fast Track: axis-aligned rectangle? if (outline->pts.count != 5) return false; + if (outline->types[2] == SW_CURVE_TYPE_CUBIC) return false; auto pt1 = outline->pts.data + 0; auto pt2 = outline->pts.data + 1; diff --git a/thirdparty/thorvg/src/renderer/tvgCommon.h b/thirdparty/thorvg/src/renderer/tvgCommon.h index d080ea19cf..15a2cc4ef0 100644 --- a/thirdparty/thorvg/src/renderer/tvgCommon.h +++ b/thirdparty/thorvg/src/renderer/tvgCommon.h @@ -87,4 +87,16 @@ uint16_t THORVG_VERSION_NUMBER(); #define P(A) ((A)->pImpl) //Access to pimpl. #define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl. + +//for debugging +#if 0 +#include <sys/time.h> +static inline double THORVG_TIMESTAMP() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec + tv.tv_usec / 1000000.0); +} +#endif + #endif //_TVG_COMMON_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp index fcb632e2b1..ff0f75dc0f 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp @@ -41,8 +41,39 @@ } +static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before) +{ + //sorting + Point tmp[4]; + Point min = {FLT_MAX, FLT_MAX}; + Point max = {0.0f, 0.0f}; + + for (int i = 0; i < 4; ++i) { + tmp[i] = pts[i]; + if (rTransform) tmp[i] *= rTransform->m; + if (pTransform) tmp[i] *= pTransform->m; + if (tmp[i].x < min.x) min.x = tmp[i].x; + if (tmp[i].x > max.x) max.x = tmp[i].x; + if (tmp[i].y < min.y) min.y = tmp[i].y; + if (tmp[i].y > max.y) max.y = tmp[i].y; + } -static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport) + float region[4] = {float(before.x), float(before.x + before.w), float(before.y), float(before.y + before.h)}; + + //figure out if the clipper is a superset of the current viewport(before) region + if (min.x <= region[0] && max.x >= region[1] && min.y <= region[2] && max.y >= region[3]) { + //viewport region is same, nothing to do. + return Result::Success; + //figure out if the clipper is totally outside of the viewport + } else if (max.x <= region[0] || min.x >= region[1] || max.y <= region[2] || min.y >= region[3]) { + renderer->viewport({0, 0, 0, 0}); + return Result::Success; + } + return Result::InsufficientCondition; +} + + +static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before) { /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */ auto shape = static_cast<Shape*>(cmpTarget); @@ -58,9 +89,13 @@ static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform if (rTransform) rTransform->update(); - //No rotation and no skewing - if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) return Result::InsufficientCondition; - if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) return Result::InsufficientCondition; + //No rotation and no skewing, still can try out clipping the rect region. + auto tryClip = false; + + if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) tryClip = true; + if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) tryClip = true; + + if (tryClip) return _clipRect(renderer, pts, pTransform, rTransform, before); //Perpendicular Rectangle? auto pt1 = pts + 0; @@ -71,6 +106,8 @@ static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) || (mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) { + RenderRegion after; + auto v1 = *pt1; auto v2 = *pt3; @@ -85,25 +122,19 @@ static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform } //sorting - if (v1.x > v2.x) { - auto tmp = v2.x; - v2.x = v1.x; - v1.x = tmp; - } + if (v1.x > v2.x) std::swap(v1.x, v2.x); + if (v1.y > v2.y) std::swap(v1.y, v2.y); - if (v1.y > v2.y) { - auto tmp = v2.y; - v2.y = v1.y; - v1.y = tmp; - } + after.x = static_cast<int32_t>(v1.x); + after.y = static_cast<int32_t>(v1.y); + after.w = static_cast<int32_t>(ceil(v2.x - after.x)); + after.h = static_cast<int32_t>(ceil(v2.y - after.y)); - viewport.x = static_cast<int32_t>(v1.x); - viewport.y = static_cast<int32_t>(v1.y); - viewport.w = static_cast<int32_t>(ceil(v2.x - viewport.x)); - viewport.h = static_cast<int32_t>(ceil(v2.y - viewport.y)); + if (after.w < 0) after.w = 0; + if (after.h < 0) after.h = 0; - if (viewport.w < 0) viewport.w = 0; - if (viewport.h < 0) viewport.h = 0; + after.intersect(before); + renderer->viewport(after); return Result::Success; } @@ -264,11 +295,8 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT } } if (tryFastTrack) { - RenderRegion viewport2; - if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2)) == Result::Success) { - viewport = renderer->viewport(); - viewport2.intersect(viewport); - renderer->viewport(viewport2); + viewport = renderer->viewport(); + if ((compFastTrack = _compFastTrack(renderer, target, pTransform, target->pImpl->rTransform, viewport)) == Result::Success) { target->pImpl->ctxFlag |= ContextFlag::FastTrack; } } diff --git a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h index 58918e88f0..93f8481707 100644 --- a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h +++ b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h @@ -23,6 +23,8 @@ #ifndef _TVG_TASK_SCHEDULER_H_ #define _TVG_TASK_SCHEDULER_H_ +#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR + #include <mutex> #include <condition_variable> diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index c0034ba888..663d685d01 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.13.7 +VERSION=0.13.8 cd thirdparty/thorvg/ || true rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ |