summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/actions/godot-deps/action.yml2
-rw-r--r--doc/classes/Animation.xml4
-rw-r--r--doc/classes/Basis.xml10
-rw-r--r--doc/classes/Skeleton3D.xml7
-rw-r--r--doc/classes/SkeletonModifier3D.xml6
-rw-r--r--doc/classes/TileMap.xml4
-rw-r--r--doc/classes/TileMapLayer.xml7
-rw-r--r--editor/editor_file_system.cpp165
-rw-r--r--editor/editor_file_system.h1
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/gui/scene_tree_editor.cpp24
-rw-r--r--editor/gui/scene_tree_editor.h1
-rw-r--r--editor/import_dock.cpp16
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp14
-rw-r--r--editor/scene_tree_dock.cpp37
-rw-r--r--editor/scene_tree_dock.h3
-rw-r--r--misc/extension_api_validation/4.2-stable.expected6
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs2
-rw-r--r--modules/mono/glue/runtime_interop.cpp6
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp5
-rw-r--r--platform/macos/display_server_macos.mm5
-rw-r--r--platform/windows/display_server_windows.cpp5
-rw-r--r--scene/2d/tile_map_layer.cpp48
-rw-r--r--scene/2d/tile_map_layer.h9
-rw-r--r--scene/3d/bone_attachment_3d.cpp10
-rw-r--r--scene/3d/physics/physical_bone_3d.cpp10
-rw-r--r--scene/3d/skeleton_3d.cpp22
-rw-r--r--scene/3d/skeleton_3d.h4
-rw-r--r--scene/3d/skeleton_ik_3d.cpp10
-rw-r--r--scene/3d/skeleton_modifier_3d.cpp2
-rw-r--r--scene/animation/animation_mixer.cpp13
-rw-r--r--scene/animation/animation_player.cpp9
-rw-r--r--scene/gui/file_dialog.cpp4
-rw-r--r--scene/main/viewport.cpp4
-rw-r--r--scene/resources/animation.compat.inc6
-rw-r--r--scene/resources/animation.cpp22
-rw-r--r--scene/resources/animation.h4
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h48
-rw-r--r--thirdparty/thorvg/src/common/tvgCompressor.cpp15
-rw-r--r--thirdparty/thorvg/src/common/tvgCompressor.h1
-rw-r--r--thirdparty/thorvg/src/common/tvgMath.cpp63
-rw-r--r--thirdparty/thorvg/src/common/tvgMath.h81
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp22
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp24
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp4
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp2
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h8
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp1
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp7
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp197
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPaint.cpp10
-rw-r--r--thirdparty/thorvg/src/renderer/tvgRender.cpp4
-rw-r--r--thirdparty/thorvg/src/renderer/tvgRender.h2
-rw-r--r--thirdparty/thorvg/src/renderer/tvgShape.cpp35
-rw-r--r--thirdparty/thorvg/src/renderer/tvgShape.h63
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh2
61 files changed, 674 insertions, 440 deletions
diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml
index 07a364cd79..607666ec5f 100644
--- a/.github/actions/godot-deps/action.yml
+++ b/.github/actions/godot-deps/action.yml
@@ -3,7 +3,7 @@ description: Setup Python, install the pip version of SCons.
inputs:
python-version:
description: The Python version to use.
- default: "3.x"
+ default: "3.12.3"
python-arch:
description: The Python architecture.
default: "x64"
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index 26ed881502..3b7a6e66fe 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -360,9 +360,12 @@
<param index="1" name="time" type="float" />
<param index="2" name="find_mode" type="int" enum="Animation.FindMode" default="0" />
<param index="3" name="limit" type="bool" default="false" />
+ <param index="4" name="backward" type="bool" default="false" />
<description>
Finds the key index by time in a given track. Optionally, only find it if the approx/exact time is given.
If [param limit] is [code]true[/code], it does not return keys outside the animation range.
+ If [param backward] is [code]true[/code], the direction is reversed in methods that rely on one directional processing.
+ For example, in case [param find_mode] is [constant FIND_MODE_NEAREST], if there is no key in the current position just after seeked, the first key found is retrieved by searching before the position, but if [param backward] is [code]true[/code], the first key found is retrieved after the position.
</description>
</method>
<method name="track_get_interpolation_loop_wrap" qualifiers="const">
@@ -583,6 +586,7 @@
<param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated value at the given time (in seconds). The [param track_idx] must be the index of a value track.
+ A [param backward] mainly affects the direction of key retrieval of the track with [constant UPDATE_DISCRETE] converted by [constant AnimationMixer.ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to match the result with [method track_find_key].
</description>
</method>
<method name="value_track_set_update_mode">
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 338d9523fa..41bda1033d 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -444,12 +444,14 @@
Transforms (multiplies) the [param right] vector by this basis, returning a [Vector3].
[codeblocks]
[gdscript]
- var my_basis = Basis(Vector3(1, 1, 1), Vector3(1, 1, 1), Vector3(0, 2, 5))
- print(my_basis * Vector3(1, 2, 3)) # Prints (7, 3, 16)
+ # Basis that swaps the X/Z axes and doubles the scale.
+ var my_basis = Basis(Vector3(0, 2, 0), Vector3(2, 0, 0), Vector3(0, 0, 2))
+ print(my_basis * Vector3(1, 2, 3)) # Prints (4, 2, 6)
[/gdscript]
[csharp]
- var myBasis = new Basis(new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(0, 2, 5));
- GD.Print(my_basis * new Vector3(1, 2, 3)); // Prints (7, 3, 16)
+ // Basis that swaps the X/Z axes and doubles the scale.
+ var myBasis = new Basis(new Vector3(0, 2, 0), new Vector3(2, 0, 0), new Vector3(0, 0, 2));
+ GD.Print(myBasis * new Vector3(1, 2, 3)); // Prints (4, 2, 6)
[/csharp]
[/codeblocks]
</description>
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 610550d8bd..1167b70c8d 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -155,6 +155,13 @@
Returns the rest transform for a bone [param bone_idx].
</description>
</method>
+ <method name="get_concatenated_bone_names" qualifiers="const">
+ <return type="StringName" />
+ <description>
+ Returns all bone names concatenated with commas ([code],[/code]) as a single [StringName].
+ It is useful to set it as a hint for the enum property.
+ </description>
+ </method>
<method name="get_parentless_bones" qualifiers="const">
<return type="PackedInt32Array" />
<description>
diff --git a/doc/classes/SkeletonModifier3D.xml b/doc/classes/SkeletonModifier3D.xml
index c0b1b6fd53..620eed9b70 100644
--- a/doc/classes/SkeletonModifier3D.xml
+++ b/doc/classes/SkeletonModifier3D.xml
@@ -18,6 +18,12 @@
[method _process_modification] must not apply [member influence] to bone poses because the [Skeleton3D] automatically applies influence to all bone poses set by the modifier.
</description>
</method>
+ <method name="get_skeleton" qualifiers="const">
+ <return type="Skeleton3D" />
+ <description>
+ Get parent [Skeleton3D] node if found.
+ </description>
+ </method>
</methods>
<members>
<member name="active" type="bool" setter="set_active" getter="is_active" default="true">
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 687c7194cd..3f70810a7f 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -486,8 +486,8 @@
Show or hide the TileMap's navigation meshes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings.
</member>
<member name="rendering_quadrant_size" type="int" setter="set_rendering_quadrant_size" getter="get_rendering_quadrant_size" default="16">
- The TileMap's quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quandrant size groups together [code]16 * 16 = 256[/code] tiles.
- The quadrant size does not apply on Y-sorted layers, as tiles are be grouped by Y position instead in that case.
+ The TileMap's quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quadrant size groups together [code]16 * 16 = 256[/code] tiles.
+ The quadrant size does not apply on Y-sorted layers, as tiles are grouped by Y position instead in that case.
[b]Note:[/b] As quadrants are created according to the map's coordinate system, the quadrant's "square shape" might not look like square in the TileMap's local coordinate system.
</member>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml
index 1bff6d911b..b9acef2095 100644
--- a/doc/classes/TileMapLayer.xml
+++ b/doc/classes/TileMapLayer.xml
@@ -264,8 +264,8 @@
Show or hide the [TileMapLayer]'s navigation meshes. If set to [constant DEBUG_VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings.
</member>
<member name="rendering_quadrant_size" type="int" setter="set_rendering_quadrant_size" getter="get_rendering_quadrant_size" default="16">
- The [TileMapLayer]'s quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quandrant size groups together [code]16 * 16 = 256[/code] tiles.
- The quadrant size does not apply on a Y-sorted [TileMapLayer], as tiles are be grouped by Y position instead in that case.
+ The [TileMapLayer]'s quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quadrant size groups together [code]16 * 16 = 256[/code] tiles.
+ The quadrant size does not apply on a Y-sorted [TileMapLayer], as tiles are grouped by Y position instead in that case.
[b]Note:[/b] As quadrants are created according to the map's coordinate system, the quadrant's "square shape" might not look like square in the [TileMapLayer]'s local coordinate system.
</member>
<member name="tile_map_data" type="PackedByteArray" setter="set_tile_map_data_from_array" getter="get_tile_map_data_as_array" default="PackedByteArray()">
@@ -277,6 +277,9 @@
<member name="use_kinematic_bodies" type="bool" setter="set_use_kinematic_bodies" getter="is_using_kinematic_bodies" default="false">
If [code]true[/code], this [TileMapLayer] collision shapes will be instantiated as kinematic bodies. This can be needed for moving [TileMapLayer] nodes (i.e. moving platforms).
</member>
+ <member name="x_draw_order_reversed" type="bool" setter="set_x_draw_order_reversed" getter="is_x_draw_order_reversed" default="false">
+ If [member CanvasItem.y_sort_enabled] is enabled, setting this to [code]true[/code] will reverse the order the tiles are drawn on the X-axis.
+ </member>
<member name="y_sort_origin" type="int" setter="set_y_sort_origin" getter="get_y_sort_origin" default="0">
This Y-sort origin value is added to each tile's Y-sort origin value. This allows, for example, to fake a different height level. This can be useful for top-down view games.
</member>
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 3adff84e40..37e00bf042 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1714,105 +1714,112 @@ HashSet<StringName> EditorFileSystem::_get_scene_groups(const String &p_path) {
void EditorFileSystem::update_file(const String &p_file) {
ERR_FAIL_COND(p_file.is_empty());
- EditorFileSystemDirectory *fs = nullptr;
- int cpos = -1;
+ update_files({ p_file });
+}
- if (!_find_file(p_file, &fs, cpos)) {
- if (!fs) {
- return;
+void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
+ for (const String &file : p_script_paths) {
+ ERR_CONTINUE(file.is_empty());
+ EditorFileSystemDirectory *fs = nullptr;
+ int cpos = -1;
+
+ if (!_find_file(file, &fs, cpos)) {
+ if (!fs) {
+ return;
+ }
}
- }
- if (!FileAccess::exists(p_file)) {
- //was removed
- _delete_internal_files(p_file);
- if (cpos != -1) { // Might've never been part of the editor file system (*.* files deleted in Open dialog).
- if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) {
- if (ResourceUID::get_singleton()->has_id(fs->files[cpos]->uid)) {
- ResourceUID::get_singleton()->remove_id(fs->files[cpos]->uid);
+ if (!FileAccess::exists(file)) {
+ //was removed
+ _delete_internal_files(file);
+ if (cpos != -1) { // Might've never been part of the editor file system (*.* files deleted in Open dialog).
+ if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(fs->files[cpos]->uid)) {
+ ResourceUID::get_singleton()->remove_id(fs->files[cpos]->uid);
+ }
}
- }
- if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
- _queue_update_script_class(p_file);
- }
- if (fs->files[cpos]->type == SNAME("PackedScene")) {
- _queue_update_scene_groups(p_file);
+ if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
+ _queue_update_script_class(file);
+ }
+ if (fs->files[cpos]->type == SNAME("PackedScene")) {
+ _queue_update_scene_groups(file);
+ }
+
+ memdelete(fs->files[cpos]);
+ fs->files.remove_at(cpos);
}
- memdelete(fs->files[cpos]);
- fs->files.remove_at(cpos);
+ _update_pending_script_classes();
+ _update_pending_scene_groups();
+ call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
+ return;
}
- _update_pending_script_classes();
- _update_pending_scene_groups();
- call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
- return;
- }
-
- String type = ResourceLoader::get_resource_type(p_file);
- if (type.is_empty() && textfile_extensions.has(p_file.get_extension())) {
- type = "TextFile";
- }
- String script_class = ResourceLoader::get_resource_script_class(p_file);
+ String type = ResourceLoader::get_resource_type(file);
+ if (type.is_empty() && textfile_extensions.has(file.get_extension())) {
+ type = "TextFile";
+ }
+ String script_class = ResourceLoader::get_resource_script_class(file);
- ResourceUID::ID uid = ResourceLoader::get_resource_uid(p_file);
+ ResourceUID::ID uid = ResourceLoader::get_resource_uid(file);
- if (cpos == -1) {
- // The file did not exist, it was added.
- int idx = 0;
- String file_name = p_file.get_file();
+ if (cpos == -1) {
+ // The file did not exist, it was added.
+ int idx = 0;
+ String file_name = file.get_file();
- for (int i = 0; i < fs->files.size(); i++) {
- if (p_file.filenocasecmp_to(fs->files[i]->file) < 0) {
- break;
+ for (int i = 0; i < fs->files.size(); i++) {
+ if (file.filenocasecmp_to(fs->files[i]->file) < 0) {
+ break;
+ }
+ idx++;
}
- idx++;
- }
- EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo);
- fi->file = file_name;
- fi->import_modified_time = 0;
- fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file);
+ EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo);
+ fi->file = file_name;
+ fi->import_modified_time = 0;
+ fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file);
- if (idx == fs->files.size()) {
- fs->files.push_back(fi);
+ if (idx == fs->files.size()) {
+ fs->files.push_back(fi);
+ } else {
+ fs->files.insert(idx, fi);
+ }
+ cpos = idx;
} else {
- fs->files.insert(idx, fi);
+ //the file exists and it was updated, and was not added in this step.
+ //this means we must force upon next restart to scan it again, to get proper type and dependencies
+ late_update_files.insert(file);
+ _save_late_updated_files(); //files need to be updated in the re-scan
}
- cpos = idx;
- } else {
- //the file exists and it was updated, and was not added in this step.
- //this means we must force upon next restart to scan it again, to get proper type and dependencies
- late_update_files.insert(p_file);
- _save_late_updated_files(); //files need to be updated in the re-scan
- }
- fs->files[cpos]->type = type;
- fs->files[cpos]->resource_script_class = script_class;
- fs->files[cpos]->uid = uid;
- fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path);
- fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(p_file);
- fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
- fs->files[cpos]->deps = _get_dependencies(p_file);
- fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file);
+ fs->files[cpos]->type = type;
+ fs->files[cpos]->resource_script_class = script_class;
+ fs->files[cpos]->uid = uid;
+ fs->files[cpos]->script_class_name = _get_global_script_class(type, file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path);
+ fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(file);
+ fs->files[cpos]->modified_time = FileAccess::get_modified_time(file);
+ fs->files[cpos]->deps = _get_dependencies(file);
+ fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file);
- if (uid != ResourceUID::INVALID_ID) {
- if (ResourceUID::get_singleton()->has_id(uid)) {
- ResourceUID::get_singleton()->set_id(uid, p_file);
- } else {
- ResourceUID::get_singleton()->add_id(uid, p_file);
- }
+ if (uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(uid)) {
+ ResourceUID::get_singleton()->set_id(uid, file);
+ } else {
+ ResourceUID::get_singleton()->add_id(uid, file);
+ }
- ResourceUID::get_singleton()->update_cache();
- }
- // Update preview
- EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
+ ResourceUID::get_singleton()->update_cache();
+ }
+ // Update preview
+ EditorResourcePreview::get_singleton()->check_for_invalidation(file);
- if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
- _queue_update_script_class(p_file);
- }
- if (fs->files[cpos]->type == SNAME("PackedScene")) {
- _queue_update_scene_groups(p_file);
+ if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
+ _queue_update_script_class(file);
+ }
+ if (fs->files[cpos]->type == SNAME("PackedScene")) {
+ _queue_update_scene_groups(file);
+ }
}
_update_pending_script_classes();
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 84ae1e182c..cd95d5fb95 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -310,6 +310,7 @@ public:
void scan();
void scan_changes();
void update_file(const String &p_file);
+ void update_files(const Vector<String> &p_script_paths);
HashSet<String> get_valid_extensions() const;
void register_global_class_script(const String &p_search_path, const String &p_target_path);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 45d276dd79..94bd590fc1 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -7037,7 +7037,7 @@ EditorNode::EditorNode() {
ED_SHORTCUT_OVERRIDE("editor/take_screenshot", "macos", KeyModifierMask::META | Key::F12);
settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/take_screenshot"), EDITOR_SCREENSHOT);
- settings_menu->set_item_tooltip(-1, TTR("Screenshots are stored in the Editor Data/Settings Folder."));
+ settings_menu->set_item_tooltip(-1, TTR("Screenshots are stored in the user data folder (\"user://\")."));
#ifndef ANDROID_ENABLED
ED_SHORTCUT_AND_COMMAND("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KeyModifierMask::SHIFT | Key::F11);
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 7e2d6c9d1e..fc52d7a0ae 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -232,23 +232,27 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
item->set_icon(0, icon);
item->set_metadata(0, p_node->get_path());
+ if (connecting_signal) {
+ // Add script icons for all scripted nodes.
+ Ref<Script> scr = p_node->get_script();
+ if (scr.is_valid()) {
+ item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT);
+ if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr) {
+ // Disable button on custom scripts (pure visual cue).
+ item->set_button_disabled(0, item->get_button_count(0) - 1, true);
+ }
+ }
+ }
+
if (connect_to_script_mode) {
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
Ref<Script> scr = p_node->get_script();
- if (!scr.is_null() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) != scr) {
- //has script
- item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT);
- } else {
- //has no script (or script is a custom type)
+ bool has_custom_script = scr.is_valid() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr;
+ if (scr.is_null() || has_custom_script) {
_set_item_custom_color(item, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
item->set_selectable(0, false);
- if (!scr.is_null()) { // make sure to mark the script if a custom type
- item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT);
- item->set_button_disabled(0, item->get_button_count(0) - 1, true);
- }
-
accent.a *= 0.7;
}
diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h
index 9ae1e99a27..b4d9644f16 100644
--- a/editor/gui/scene_tree_editor.h
+++ b/editor/gui/scene_tree_editor.h
@@ -160,7 +160,6 @@ public:
void set_marked(const HashSet<Node *> &p_marked, bool p_selectable = true, bool p_children_selectable = true);
void set_marked(Node *p_marked, bool p_selectable = true, bool p_children_selectable = true);
- bool has_marked() const { return !marked.is_empty(); }
void set_selected(Node *p_node, bool p_emit_selected = true);
Node *get_selected();
void set_can_rename(bool p_can_rename) { can_rename = p_can_rename; }
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 3c06c68414..2347c715a8 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -189,13 +189,15 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_
params->base_options_path = p_path;
HashMap<StringName, Variant> import_options;
- List<String> section_keys;
- p_config->get_section_keys("params", &section_keys);
- for (const String &section_key : section_keys) {
- import_options[section_key] = p_config->get_value("params", section_key);
- }
- if (params->importer.is_valid()) {
- params->importer->handle_compatibility_options(import_options);
+ if (p_config.is_valid() && p_config->has_section("params")) {
+ List<String> section_keys;
+ p_config->get_section_keys("params", &section_keys);
+ for (const String &section_key : section_keys) {
+ import_options[section_key] = p_config->get_value("params", section_key);
+ }
+ if (params->importer.is_valid()) {
+ params->importer->handle_compatibility_options(import_options);
+ }
}
for (const ResourceImporter::ImportOption &E : options) {
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index fe56f48889..0a2c192ea4 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -243,12 +243,12 @@ void AnimationPlayerEditor::_play_from_pressed() {
String current = _get_current();
if (!current.is_empty()) {
- float time = player->get_current_animation_position();
+ double time = player->get_current_animation_position();
if (current == player->get_assigned_animation() && player->is_playing()) {
- player->stop(); //so it won't blend with itself
+ player->clear_caches(); //so it won't blend with itself
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
- player->seek(time);
+ player->seek(time, true, true);
player->play(current);
}
@@ -281,12 +281,12 @@ void AnimationPlayerEditor::_play_bw_from_pressed() {
String current = _get_current();
if (!current.is_empty()) {
- float time = player->get_current_animation_position();
- if (current == player->get_assigned_animation()) {
- player->stop(); //so it won't blend with itself
+ double time = player->get_current_animation_position();
+ if (current == player->get_assigned_animation() && player->is_playing()) {
+ player->clear_caches(); //so it won't blend with itself
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
- player->seek(time);
+ player->seek(time, true, true);
player->play_backwards(current);
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index c71cb9d4ac..4448f165d3 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -76,12 +76,15 @@ void SceneTreeDock::_quick_open() {
}
void SceneTreeDock::_inspect_hovered_node() {
- scene_tree->set_selected(node_hovered_now);
- scene_tree->set_marked(node_hovered_now);
+ select_node_hovered_at_end_of_drag = true;
+ if (tree_item_inspected != nullptr) {
+ tree_item_inspected->clear_custom_color(0);
+ }
Tree *tree = scene_tree->get_scene_tree();
- TreeItem *item = tree->get_item_at_position(tree->get_local_mouse_position());
+ TreeItem *item = tree->get_item_with_metadata(node_hovered_now->get_path());
if (item) {
- item->set_as_cursor(0);
+ tree_item_inspected = item;
+ tree_item_inspected->set_custom_color(0, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
}
InspectorDock::get_inspector_singleton()->edit(node_hovered_now);
InspectorDock::get_inspector_singleton()->propagate_notification(NOTIFICATION_DRAG_BEGIN); // Enable inspector drag preview after it updated.
@@ -130,8 +133,8 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) {
}
if (mb->is_released()) {
- if (scene_tree->has_marked()) {
- scene_tree->set_marked(nullptr);
+ if (tree_item_inspected != nullptr) {
+ tree_item_inspected->clear_custom_color(0);
}
_reset_hovering_timer();
}
@@ -1658,6 +1661,16 @@ void SceneTreeDock::_notification(int p_what) {
case NOTIFICATION_DRAG_END: {
_reset_hovering_timer();
+ if (select_node_hovered_at_end_of_drag && !hovered_but_reparenting) {
+ Node *node_inspected = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object());
+ if (node_inspected) {
+ editor_selection->clear();
+ editor_selection->add_node(node_inspected);
+ scene_tree->set_selected(node_inspected);
+ select_node_hovered_at_end_of_drag = false;
+ }
+ }
+ hovered_but_reparenting = false;
} break;
}
}
@@ -2185,6 +2198,7 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
ERR_FAIL_NULL(new_parent);
List<Node *> selection = editor_selection->get_selected_node_list();
+ List<Node *> full_selection = editor_selection->get_full_selected_node_list();
if (selection.is_empty()) {
return; // Nothing to reparent.
@@ -2197,6 +2211,10 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
}
_do_reparent(new_parent, -1, nodes, p_keep_global_xform);
+
+ for (Node *E : full_selection) {
+ editor_selection->add_node(E);
+ }
}
void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform) {
@@ -2238,6 +2256,9 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
return; // Position and parent didn't change.
}
+ // Prevent selecting the hovered node and keep the reparented node(s) selected instead.
+ hovered_but_reparenting = true;
+
Node *validate = new_parent;
while (validate) {
ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent.");
@@ -3400,6 +3421,7 @@ void SceneTreeDock::_nodes_dragged(const Array &p_nodes, NodePath p_to, int p_ty
}
List<Node *> selection = editor_selection->get_selected_node_list();
+ List<Node *> full_selection = editor_selection->get_full_selected_node_list();
if (selection.is_empty()) {
return; //nothing to reparent
@@ -3419,7 +3441,8 @@ void SceneTreeDock::_nodes_dragged(const Array &p_nodes, NodePath p_to, int p_ty
_normalize_drop(to_node, to_pos, p_type);
_do_reparent(to_node, to_pos, nodes, !Input::get_singleton()->is_key_pressed(Key::SHIFT));
- for (Node *E : nodes) {
+
+ for (Node *E : full_selection) {
editor_selection->add_node(E);
}
}
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index abef990995..5028cd5cc9 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -239,8 +239,11 @@ class SceneTreeDock : public VBoxContainer {
void _inspect_hovered_node();
void _reset_hovering_timer();
Timer *inspect_hovered_node_delay = nullptr;
+ TreeItem *tree_item_inspected = nullptr;
Node *node_hovered_now = nullptr;
Node *node_hovered_previously = nullptr;
+ bool select_node_hovered_at_end_of_drag = false;
+ bool hovered_but_reparenting = false;
virtual void input(const Ref<InputEvent> &p_event) override;
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index b27f80ee29..03c7a8291a 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -364,3 +364,9 @@ GH-92322
Validate extension JSON: Error: Field 'classes/EditorInspectorPlugin/methods/add_property_editor/arguments': size changed value in new API, from 3 to 4.
Optional arguments added. Compatibility methods registered.
+
+GH-86661
+--------
+Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/arguments': size changed value in new API, from 3 to 5.
+
+Added optional argument to track_find_key to handle backward seeking. Compatibility method registered.
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 771ccf47b7..f1a35c84b7 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -166,7 +166,7 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
panic_mode = true;
// TODO: Improve positional information.
if (p_origin == nullptr) {
- errors.push_back({ p_message, current.start_line, current.start_column });
+ errors.push_back({ p_message, previous.start_line, previous.start_column });
} else {
errors.push_back({ p_message, p_origin->start_line, p_origin->leftmost_column });
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index eef26cdd4e..5cc2a8026e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -427,10 +427,11 @@ namespace Godot.Bridge
// This method may be called before initialization.
if (NativeFuncs.godotsharp_dotnet_module_is_initialized().ToBool() && Engine.IsEditorHint())
{
- foreach (var scriptPath in _pathTypeBiMap.Paths)
+ if (_pathTypeBiMap.Paths.Count > 0)
{
- using godot_string nativeScriptPath = Marshaling.ConvertStringToNative(scriptPath);
- NativeFuncs.godotsharp_internal_editor_file_system_update_file(nativeScriptPath);
+ string[] scriptPaths = _pathTypeBiMap.Paths.ToArray();
+ using godot_packed_string_array scriptPathsNative = Marshaling.ConvertSystemArrayToNativePackedStringArray(scriptPaths);
+ NativeFuncs.godotsharp_internal_editor_file_system_update_files(scriptPathsNative);
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
index 1ec1a75516..29fa13d625 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -64,7 +65,7 @@ public static partial class ScriptManagerBridge
private System.Collections.Generic.Dictionary<string, Type> _pathTypeMap = new();
private System.Collections.Generic.Dictionary<Type, string> _typePathMap = new();
- public System.Collections.Generic.IEnumerable<string> Paths => _pathTypeMap.Keys;
+ public IReadOnlyCollection<string> Paths => _pathTypeMap.Keys;
public void Add(string scriptPath, Type scriptType)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index c4fd639cce..cfd9ed7acc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -59,7 +59,7 @@ namespace Godot.NativeInterop
internal static partial void godotsharp_stack_info_vector_destroy(
ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
- internal static partial void godotsharp_internal_editor_file_system_update_file(in godot_string p_script_path);
+ internal static partial void godotsharp_internal_editor_file_system_update_files(in godot_packed_string_array p_script_paths);
internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 1af462dafd..80e9fdf77f 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -315,13 +315,13 @@ void godotsharp_internal_new_csharp_script(Ref<CSharpScript> *r_dest) {
memnew_placement(r_dest, Ref<CSharpScript>(memnew(CSharpScript)));
}
-void godotsharp_internal_editor_file_system_update_file(const String *p_script_path) {
+void godotsharp_internal_editor_file_system_update_files(const PackedStringArray &p_script_paths) {
#ifdef TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
if (efs) {
- efs->update_file(*p_script_path);
+ efs->update_files(p_script_paths);
}
#else
// EditorFileSystem is only available when running in the Godot editor.
@@ -1450,7 +1450,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_engine_get_singleton,
(void *)godotsharp_stack_info_vector_resize,
(void *)godotsharp_stack_info_vector_destroy,
- (void *)godotsharp_internal_editor_file_system_update_file,
+ (void *)godotsharp_internal_editor_file_system_update_files,
(void *)godotsharp_internal_script_debugger_send_error,
(void *)godotsharp_internal_script_debugger_is_active,
(void *)godotsharp_internal_object_get_associated_gchandle,
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 4ca1b11094..0e4c723a87 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -4184,8 +4184,11 @@ void DisplayServerX11::popup_open(WindowID p_window) {
}
}
+ // Detect tooltips and other similar popups that shouldn't block input to their parent.
+ bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);
+
WindowData &wd = windows[p_window];
- if (wd.is_popup || has_popup_ancestor) {
+ if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {
// Find current popup parent, or root popup if new window is not transient.
List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 1032766480..a1a91345ac 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3366,8 +3366,11 @@ void DisplayServerMacOS::popup_open(WindowID p_window) {
}
}
+ // Detect tooltips and other similar popups that shouldn't block input to their parent.
+ bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);
+
WindowData &wd = windows[p_window];
- if (wd.is_popup || has_popup_ancestor) {
+ if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {
bool was_empty = popup_list.is_empty();
// Find current popup parent, or root popup if new window is not transient.
List<WindowID>::Element *C = nullptr;
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 73987c44db..10cec9f5ed 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3575,8 +3575,11 @@ void DisplayServerWindows::popup_open(WindowID p_window) {
}
}
+ // Detect tooltips and other similar popups that shouldn't block input to their parent.
+ bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);
+
WindowData &wd = windows[p_window];
- if (wd.is_popup || has_popup_ancestor) {
+ if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {
// Find current popup parent, or root popup if new window is not transient.
List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp
index 0ac236eaa7..cdd7b15110 100644
--- a/scene/2d/tile_map_layer.cpp
+++ b/scene/2d/tile_map_layer.cpp
@@ -205,11 +205,12 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
// Check if anything changed that might change the quadrant shape.
// If so, recreate everything.
- bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] ||
- (is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_TILE_SET]));
+ bool quadrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_SET] ||
+ (is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM])) ||
+ (!is_y_sort_enabled() && dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE]);
// Free all quadrants.
- if (forced_cleanup || quandrant_shape_changed) {
+ if (forced_cleanup || quadrant_shape_changed) {
for (const KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) {
for (const RID &ci : kv.value->canvas_items) {
if (ci.is_valid()) {
@@ -264,9 +265,8 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
rendering_quadrant->canvas_items.clear();
// Sort the quadrant cells.
- if (is_y_sort_enabled()) {
- // For compatibility reasons, we use another comparator for Y-sorted layers.
- rendering_quadrant->cells.sort_custom<CellDataYSortedComparator>();
+ if (is_y_sort_enabled() && x_draw_order_reversed) {
+ rendering_quadrant->cells.sort_custom<CellDataYSortedXReversedComparator>();
} else {
rendering_quadrant->cells.sort();
}
@@ -1770,6 +1770,8 @@ void TileMapLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileMapLayer::set_y_sort_origin);
ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileMapLayer::get_y_sort_origin);
+ ClassDB::bind_method(D_METHOD("set_x_draw_order_reversed", "x_draw_order_reversed"), &TileMapLayer::set_x_draw_order_reversed);
+ ClassDB::bind_method(D_METHOD("is_x_draw_order_reversed"), &TileMapLayer::is_x_draw_order_reversed);
ClassDB::bind_method(D_METHOD("set_rendering_quadrant_size", "size"), &TileMapLayer::set_rendering_quadrant_size);
ClassDB::bind_method(D_METHOD("get_rendering_quadrant_size"), &TileMapLayer::get_rendering_quadrant_size);
@@ -1796,6 +1798,7 @@ void TileMapLayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tile_set", "get_tile_set");
ADD_GROUP("Rendering", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "x_draw_order_reversed"), "set_x_draw_order_reversed", "is_x_draw_order_reversed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rendering_quadrant_size"), "set_rendering_quadrant_size", "get_rendering_quadrant_size");
ADD_GROUP("Physics", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled");
@@ -1814,6 +1817,18 @@ void TileMapLayer::_bind_methods() {
BIND_ENUM_CONSTANT(DEBUG_VISIBILITY_MODE_FORCE_SHOW);
}
+void TileMapLayer::_validate_property(PropertyInfo &p_property) const {
+ if (is_y_sort_enabled()) {
+ if (p_property.name == "rendering_quadrant_size") {
+ p_property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ } else {
+ if (p_property.name == "x_draw_order_reversed") {
+ p_property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ }
+}
+
void TileMapLayer::_update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter) {
// Set a default texture filter for the whole tilemap.
CanvasItem::_update_self_texture_filter(p_texture_filter);
@@ -2772,6 +2787,7 @@ void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) {
_queue_internal_update();
emit_signal(CoreStringName(changed));
+ notify_property_list_changed();
_update_notify_local_transform();
}
@@ -2789,6 +2805,20 @@ int TileMapLayer::get_y_sort_origin() const {
return y_sort_origin;
}
+void TileMapLayer::set_x_draw_order_reversed(bool p_x_draw_order_reversed) {
+ if (x_draw_order_reversed == p_x_draw_order_reversed) {
+ return;
+ }
+ x_draw_order_reversed = p_x_draw_order_reversed;
+ dirty.flags[DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED] = true;
+ _queue_internal_update();
+ emit_signal(CoreStringName(changed));
+}
+
+bool TileMapLayer::is_x_draw_order_reversed() const {
+ return x_draw_order_reversed;
+}
+
void TileMapLayer::set_z_index(int p_z_index) {
if (get_z_index() == p_z_index) {
return;
@@ -2813,10 +2843,9 @@ void TileMapLayer::set_rendering_quadrant_size(int p_size) {
if (rendering_quadrant_size == p_size) {
return;
}
- dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] = true;
ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
-
rendering_quadrant_size = p_size;
+ dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] = true;
_queue_internal_update();
emit_signal(CoreStringName(changed));
}
@@ -2840,6 +2869,9 @@ bool TileMapLayer::is_collision_enabled() const {
}
void TileMapLayer::set_use_kinematic_bodies(bool p_use_kinematic_bodies) {
+ if (use_kinematic_bodies == p_use_kinematic_bodies) {
+ return;
+ }
use_kinematic_bodies = p_use_kinematic_bodies;
dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] = p_use_kinematic_bodies;
_queue_internal_update();
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index 57c83d7c4c..19c6fc128b 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -160,8 +160,8 @@ struct CellData {
}
};
-// For compatibility reasons, we use another comparator for Y-sorted layers.
-struct CellDataYSortedComparator {
+// We use another comparator for Y-sorted layers with reversed X drawing order.
+struct CellDataYSortedXReversedComparator {
_FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const {
return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x);
}
@@ -245,6 +245,7 @@ public:
DIRTY_FLAGS_LAYER_SELF_MODULATE,
DIRTY_FLAGS_LAYER_Y_SORT_ENABLED,
DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN,
+ DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED,
DIRTY_FLAGS_LAYER_Z_INDEX,
DIRTY_FLAGS_LAYER_LIGHT_MASK,
DIRTY_FLAGS_LAYER_TEXTURE_FILTER,
@@ -280,6 +281,7 @@ private:
HighlightMode highlight_mode = HIGHLIGHT_MODE_DEFAULT;
int y_sort_origin = 0;
+ bool x_draw_order_reversed = false;
int rendering_quadrant_size = 16;
bool collision_enabled = true;
@@ -383,6 +385,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter) override;
virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat) override;
@@ -470,6 +473,8 @@ public:
virtual void set_y_sort_enabled(bool p_y_sort_enabled) override;
void set_y_sort_origin(int p_y_sort_origin);
int get_y_sort_origin() const;
+ void set_x_draw_order_reversed(bool p_x_draw_order_reversed);
+ bool is_x_draw_order_reversed() const;
virtual void set_z_index(int p_z_index) override;
virtual void set_light_mask(int p_light_mask) override;
void set_rendering_quadrant_size(int p_size);
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 2716738684..6aade24e4e 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -44,16 +44,8 @@ void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const {
}
if (parent) {
- String names;
- for (int i = 0; i < parent->get_bone_count(); i++) {
- if (i > 0) {
- names += ",";
- }
- names += parent->get_bone_name(i);
- }
-
p_property.hint = PROPERTY_HINT_ENUM;
- p_property.hint_string = names;
+ p_property.hint_string = parent->get_concatenated_bone_names();
} else {
p_property.hint = PROPERTY_HINT_NONE;
p_property.hint_string = "";
diff --git a/scene/3d/physics/physical_bone_3d.cpp b/scene/3d/physics/physical_bone_3d.cpp
index c6be2a9da8..c290f16c0d 100644
--- a/scene/3d/physics/physical_bone_3d.cpp
+++ b/scene/3d/physics/physical_bone_3d.cpp
@@ -738,15 +738,7 @@ bool PhysicalBone3D::_get(const StringName &p_name, Variant &r_ret) const {
void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const {
Skeleton3D *skeleton = get_skeleton();
if (skeleton) {
- String names;
- for (int i = 0; i < skeleton->get_bone_count(); i++) {
- if (i > 0) {
- names += ",";
- }
- names += skeleton->get_bone_name(i);
- }
-
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, names));
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, skeleton->get_concatenated_bone_names()));
} else {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name")));
}
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index a4804e928a..21e82adf47 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -265,11 +265,31 @@ void Skeleton3D::_update_process_order() {
bones_backup.resize(bones.size());
+ concatenated_bone_names = StringName();
+
process_order_dirty = false;
emit_signal("bone_list_changed");
}
+void Skeleton3D::_update_bone_names() const {
+ String names;
+ for (int i = 0; i < bones.size(); i++) {
+ if (i > 0) {
+ names += ",";
+ }
+ names += bones[i].name;
+ }
+ concatenated_bone_names = StringName(names);
+}
+
+StringName Skeleton3D::get_concatenated_bone_names() const {
+ if (concatenated_bone_names == StringName()) {
+ _update_bone_names();
+ }
+ return concatenated_bone_names;
+}
+
#ifndef DISABLE_DEPRECATED
void Skeleton3D::setup_simulator() {
if (simulator && simulator->get_parent() == this) {
@@ -983,6 +1003,8 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name);
ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "name"), &Skeleton3D::set_bone_name);
+ ClassDB::bind_method(D_METHOD("get_concatenated_bone_names"), &Skeleton3D::get_concatenated_bone_names);
+
ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &Skeleton3D::get_bone_parent);
ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "parent_idx"), &Skeleton3D::set_bone_parent);
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 23b9423993..b8e38242b9 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -150,6 +150,9 @@ private:
Vector<int> parentless_bones;
HashMap<String, int> name_to_bone_index;
+ mutable StringName concatenated_bone_names = StringName();
+ void _update_bone_names() const;
+
void _make_dirty();
bool dirty = false;
bool rest_dirty = false;
@@ -200,6 +203,7 @@ public:
int find_bone(const String &p_name) const;
String get_bone_name(int p_bone) const;
void set_bone_name(int p_bone, const String &p_name);
+ StringName get_concatenated_bone_names() const;
bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const;
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 78a21ba9e1..0d6316ee35 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -306,16 +306,8 @@ void SkeletonIK3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "root_bone" || p_property.name == "tip_bone") {
Skeleton3D *skeleton = get_skeleton();
if (skeleton) {
- String names("--,");
- for (int i = 0; i < skeleton->get_bone_count(); i++) {
- if (i > 0) {
- names += ",";
- }
- names += skeleton->get_bone_name(i);
- }
-
p_property.hint = PROPERTY_HINT_ENUM;
- p_property.hint_string = names;
+ p_property.hint_string = skeleton->get_concatenated_bone_names();
} else {
p_property.hint = PROPERTY_HINT_NONE;
p_property.hint_string = "";
diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp
index 8d806ef5fc..9851214194 100644
--- a/scene/3d/skeleton_modifier_3d.cpp
+++ b/scene/3d/skeleton_modifier_3d.cpp
@@ -123,6 +123,8 @@ void SkeletonModifier3D::_notification(int p_what) {
}
void SkeletonModifier3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModifier3D::get_skeleton);
+
ClassDB::bind_method(D_METHOD("set_active", "active"), &SkeletonModifier3D::set_active);
ClassDB::bind_method(D_METHOD("is_active"), &SkeletonModifier3D::is_active);
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index e600de6b8b..5074145168 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -1109,6 +1109,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
real_t weight = ai.playback_info.weight;
Vector<real_t> track_weights = ai.playback_info.track_weights;
bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream.
+ bool seeked_backward = signbit(p_delta);
#ifndef _3D_DISABLED
bool calc_root = !seeked || is_external_seeking;
#endif // _3D_DISABLED
@@ -1463,7 +1464,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
} else {
if (seeked) {
- int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true);
+ int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, false, seeked_backward);
if (idx < 0) {
continue;
}
@@ -1744,8 +1745,10 @@ void AnimationMixer::_blend_apply() {
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
- if (t->use_discrete && !t->use_continuous) {
- t->is_init = true; // If only disctere value is applied, no more RESET.
+ if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
+ t->is_init = false; // Always update in Force Continuous.
+ } else if (!t->use_continuous && (t->use_discrete || !deterministic)) {
+ t->is_init = true; // If there is no continuous value and only disctere value is applied or just started, don't RESET.
}
if ((t->is_init && (is_zero_amount || !t->use_continuous)) ||
@@ -1756,9 +1759,7 @@ void AnimationMixer::_blend_apply() {
break; // Don't overwrite the value set by UPDATE_DISCRETE.
}
- if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
- t->is_init = false; // Always update in Force Continuous.
- } else {
+ if (callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET.
}
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 0c24d79ad7..5756edaa48 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -234,6 +234,9 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
pi.delta = delta;
pi.seeked = p_seeked;
}
+ if (Math::is_zero_approx(pi.delta) && backwards) {
+ pi.delta = -0.0; // Sign is needed to handle converted Continuous track from Discrete track correctly.
+ }
// AnimationPlayer doesn't have internal seeking.
// However, immediately after playback, discrete keys should be retrieved with EXACT mode since behind keys must be ignored at that time.
pi.is_external_seeking = !p_started;
@@ -257,7 +260,7 @@ void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) {
bool seeked = c.seeked; // The animation may be changed during process, so it is safer that the state is changed before process.
- if (p_delta != 0) {
+ if (!Math::is_zero_approx(p_delta)) {
c.seeked = false;
}
@@ -581,6 +584,8 @@ void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) {
return;
}
+ bool is_backward = p_time < playback.current.pos;
+
_check_immediately_after_start();
playback.current.pos = p_time;
@@ -596,7 +601,7 @@ void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) {
playback.seeked = true;
if (p_update) {
- _process_animation(0, p_update_only);
+ _process_animation(is_backward ? -0.0 : 0.0, p_update_only);
playback.seeked = false; // If animation was proceeded here, no more seek in internal process.
}
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 0c146ce173..a23ee6db71 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -99,7 +99,9 @@ void FileDialog::set_visible(bool p_visible) {
#endif
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
- _native_popup();
+ if (p_visible) {
+ _native_popup();
+ }
} else {
ConfirmationDialog::set_visible(p_visible);
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 38bd7141c2..468d4e3c0f 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1430,7 +1430,7 @@ void Viewport::_gui_show_tooltip() {
Control *tooltip_owner = nullptr;
gui.tooltip_text = _gui_get_tooltip(
gui.tooltip_control,
- gui.tooltip_control->get_global_transform().xform_inv(gui.last_mouse_pos),
+ gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos),
&tooltip_owner);
gui.tooltip_text = gui.tooltip_text.strip_edges();
@@ -1910,7 +1910,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.tooltip_popup) {
if (gui.tooltip_control) {
- String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform().xform_inv(mpos));
+ String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(mpos));
tooltip = tooltip.strip_edges();
if (tooltip.is_empty() || tooltip != gui.tooltip_text) {
diff --git a/scene/resources/animation.compat.inc b/scene/resources/animation.compat.inc
index d7d8867748..fc2672bb25 100644
--- a/scene/resources/animation.compat.inc
+++ b/scene/resources/animation.compat.inc
@@ -50,8 +50,8 @@ Variant Animation::_value_track_interpolate_bind_compat_86629(int p_track, doubl
return value_track_interpolate(p_track, p_time, false);
}
-int Animation::_track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode) const {
- return track_find_key(p_track, p_time, p_find_mode, false);
+int Animation::_track_find_key_bind_compat_92861(int p_track, double p_time, FindMode p_find_mode) const {
+ return track_find_key(p_track, p_time, p_find_mode, false, false);
}
void Animation::_bind_compatibility_methods() {
@@ -60,7 +60,7 @@ void Animation::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::_scale_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::_blend_shape_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::_value_track_interpolate_bind_compat_86629);
- ClassDB::bind_compatibility_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::_track_find_key_bind_compat_86661, DEFVAL(FIND_MODE_NEAREST));
+ ClassDB::bind_compatibility_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::_track_find_key_bind_compat_92861, DEFVAL(FIND_MODE_NEAREST));
}
#endif // DISABLE_DEPRECATED
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index a3bfa987c6..254bd38be7 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -1496,7 +1496,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
emit_changed();
}
-int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, bool p_limit) const {
+int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, bool p_limit, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
@@ -1518,7 +1518,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(tt->positions, p_time, false, p_limit);
+ int k = _find(tt->positions, p_time, p_backward, p_limit);
if (k < 0 || k >= tt->positions.size()) {
return -1;
}
@@ -1545,7 +1545,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(rt->rotations, p_time, false, p_limit);
+ int k = _find(rt->rotations, p_time, p_backward, p_limit);
if (k < 0 || k >= rt->rotations.size()) {
return -1;
}
@@ -1572,7 +1572,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(st->scales, p_time, false, p_limit);
+ int k = _find(st->scales, p_time, p_backward, p_limit);
if (k < 0 || k >= st->scales.size()) {
return -1;
}
@@ -1599,7 +1599,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(bst->blend_shapes, p_time, false, p_limit);
+ int k = _find(bst->blend_shapes, p_time, p_backward, p_limit);
if (k < 0 || k >= bst->blend_shapes.size()) {
return -1;
}
@@ -1611,7 +1611,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
- int k = _find(vt->values, p_time, false, p_limit);
+ int k = _find(vt->values, p_time, p_backward, p_limit);
if (k < 0 || k >= vt->values.size()) {
return -1;
}
@@ -1623,7 +1623,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_METHOD: {
MethodTrack *mt = static_cast<MethodTrack *>(t);
- int k = _find(mt->methods, p_time, false, p_limit);
+ int k = _find(mt->methods, p_time, p_backward, p_limit);
if (k < 0 || k >= mt->methods.size()) {
return -1;
}
@@ -1635,7 +1635,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_BEZIER: {
BezierTrack *bt = static_cast<BezierTrack *>(t);
- int k = _find(bt->values, p_time, false, p_limit);
+ int k = _find(bt->values, p_time, p_backward, p_limit);
if (k < 0 || k >= bt->values.size()) {
return -1;
}
@@ -1647,7 +1647,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_AUDIO: {
AudioTrack *at = static_cast<AudioTrack *>(t);
- int k = _find(at->values, p_time, false, p_limit);
+ int k = _find(at->values, p_time, p_backward, p_limit);
if (k < 0 || k >= at->values.size()) {
return -1;
}
@@ -1659,7 +1659,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_ANIMATION: {
AnimationTrack *at = static_cast<AnimationTrack *>(t);
- int k = _find(at->values, p_time, false, p_limit);
+ int k = _find(at->values, p_time, p_backward, p_limit);
if (k < 0 || k >= at->values.size()) {
return -1;
}
@@ -3836,7 +3836,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count);
ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value);
ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time);
- ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode", "limit"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode", "limit", "backward"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type);
ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 604bce497a..e9bfc298a5 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -388,7 +388,7 @@ protected:
Vector3 _scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
float _blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
Variant _value_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
- int _track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const;
+ int _track_find_key_bind_compat_92861(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const;
static void _bind_compatibility_methods();
#endif // DISABLE_DEPRECATED
@@ -423,7 +423,7 @@ public:
void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition);
void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
void track_set_key_time(int p_track, int p_key_idx, double p_time);
- int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST, bool p_limit = false) const;
+ int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST, bool p_limit = false, bool p_backward = false) const;
void track_remove_key(int p_track, int p_idx);
void track_remove_key_at_time(int p_track, double p_time);
int track_get_key_count(int p_track) const;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 137e3a0cbc..6878af863f 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
-- Version: 0.13.3 (6235068cad8cad176ccd0cbcf82f25e985fbc258, 2024)
+- Version: 0.13.7 (d2c0428a99f7305c086caffe0c730add601ebd6e, 2024)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 9c83f25c79..699076fdf1 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -13,5 +13,5 @@
// For internal debugging:
//#define THORVG_LOG_ENABLED
-#define THORVG_VERSION_STRING "0.13.5"
+#define THORVG_VERSION_STRING "0.13.7"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index f7396050d7..8285aa1c4c 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -80,7 +80,7 @@ enum class Result
InsufficientCondition, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist.
FailedAllocation, ///< The value returned in case of unsuccessful memory allocation.
MemoryCorruption, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting
- NonSupport, ///< The value returned in case of choosing unsupported options.
+ NonSupport, ///< The value returned in case of choosing unsupported engine features(options).
Unknown ///< The value returned in all other cases.
};
@@ -982,7 +982,7 @@ public:
*
* @param[in] width The width of the stroke. The default value is 0.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(float width) noexcept;
@@ -994,7 +994,7 @@ public:
* @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
* @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
@@ -1004,8 +1004,7 @@ public:
* @param[in] f The gradient fill.
*
* @retval Result::Success When succeed.
- * @retval Result::FailedAllocation An internal error with a memory allocation for an object to be filled.
- * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument.
+ * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument or an error with accessing it.
*/
Result stroke(std::unique_ptr<Fill> f) noexcept;
@@ -1029,7 +1028,7 @@ public:
*
* @param[in] cap The cap style value. The default value is @c StrokeCap::Square.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(StrokeCap cap) noexcept;
@@ -1040,23 +1039,38 @@ public:
*
* @param[in] join The join style value. The default value is @c StrokeJoin::Bevel.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(StrokeJoin join) noexcept;
-
/**
* @brief Sets the stroke miterlimit.
*
* @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4.
*
- * @retval Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed or Result::InvalidArgument for @p miterlimit values less than zero.
*
* @since 0.11
*/
Result strokeMiterlimit(float miterlimit) noexcept;
/**
+ * @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible.
+ *
+ * The values of the arguments @p begin, @p end, and @p offset are in the range of 0.0 to 1.0, representing the beginning of the path and the end, respectively.
+ *
+ * @param[in] begin Specifies the start of the segment to display along the path.
+ * @param[in] end Specifies the end of the segment to display along the path.
+ * @param[in] simultaneous Determines how to trim multiple paths within a single shape. If set to @c true (default), trimming is applied simultaneously to all paths;
+ * Otherwise, all paths are treated as a single entity with a combined length equal to the sum of their individual lengths and are trimmed as such.
+ *
+ * @retval Result::Success when succeed.
+ *
+ * @note Experimental API
+ */
+ Result strokeTrim(float begin, float end, bool simultaneous = true) noexcept;
+
+ /**
* @brief Sets the solid color for all of the figures from the path.
*
* The parts of the shape defined as inner are colored.
@@ -1095,19 +1109,17 @@ public:
*/
Result fill(FillRule r) noexcept;
-
/**
* @brief Sets the rendering order of the stroke and the fill.
*
* @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option).
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*
* @since 0.10
*/
Result order(bool strokeFirst) noexcept;
-
/**
* @brief Gets the commands data of the path.
*
@@ -1211,6 +1223,18 @@ public:
float strokeMiterlimit() const noexcept;
/**
+ * @brief Gets the trim of the stroke along the defined path segment.
+ *
+ * @param[out] begin The starting point of the segment to display along the path.
+ * @param[out] end Specifies the end of the segment to display along the path.
+ *
+ * @retval @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise.
+ *
+ * @note Experimental API
+ */
+ bool strokeTrim(float* begin, float* end) const noexcept;
+
+ /**
* @brief Creates a new Shape object.
*
* @return A new Shape object.
diff --git a/thirdparty/thorvg/src/common/tvgCompressor.cpp b/thirdparty/thorvg/src/common/tvgCompressor.cpp
index 9a1dc54632..b61718f9a7 100644
--- a/thirdparty/thorvg/src/common/tvgCompressor.cpp
+++ b/thirdparty/thorvg/src/common/tvgCompressor.cpp
@@ -472,4 +472,19 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
}
+/************************************************************************/
+/* DJB2 Implementation */
+/************************************************************************/
+
+unsigned long djb2Encode(const char* str)
+{
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++)) {
+ hash = ((hash << 5) + hash) + c; // hash * 33 + c
+ }
+ return hash;
+}
+
}
diff --git a/thirdparty/thorvg/src/common/tvgCompressor.h b/thirdparty/thorvg/src/common/tvgCompressor.h
index 0756127ec6..b043cc77de 100644
--- a/thirdparty/thorvg/src/common/tvgCompressor.h
+++ b/thirdparty/thorvg/src/common/tvgCompressor.h
@@ -30,6 +30,7 @@ namespace tvg
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
size_t b64Decode(const char* encoded, const size_t len, char** decoded);
+ unsigned long djb2Encode(const char* str);
}
#endif //_TVG_COMPRESSOR_H_
diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp
index 37a8879cb5..e6b5d47050 100644
--- a/thirdparty/thorvg/src/common/tvgMath.cpp
+++ b/thirdparty/thorvg/src/common/tvgMath.cpp
@@ -47,23 +47,14 @@ bool mathInverse(const Matrix* m, Matrix* out)
}
-Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
+bool mathIdentity(const Matrix* m)
{
- Matrix m;
-
- m.e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31;
- m.e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32;
- m.e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33;
-
- m.e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31;
- m.e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32;
- m.e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33;
-
- m.e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31;
- m.e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32;
- m.e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33;
-
- return m;
+ if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
+ m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
+ m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
+ return false;
+ }
+ return true;
}
@@ -82,21 +73,41 @@ void mathRotate(Matrix* m, float degree)
}
-bool mathIdentity(const Matrix* m)
+Matrix operator*(const Matrix& lhs, const Matrix& rhs)
{
- if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
- m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
- m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
- return false;
+ Matrix m;
+
+ m.e11 = lhs.e11 * rhs.e11 + lhs.e12 * rhs.e21 + lhs.e13 * rhs.e31;
+ m.e12 = lhs.e11 * rhs.e12 + lhs.e12 * rhs.e22 + lhs.e13 * rhs.e32;
+ m.e13 = lhs.e11 * rhs.e13 + lhs.e12 * rhs.e23 + lhs.e13 * rhs.e33;
+
+ m.e21 = lhs.e21 * rhs.e11 + lhs.e22 * rhs.e21 + lhs.e23 * rhs.e31;
+ m.e22 = lhs.e21 * rhs.e12 + lhs.e22 * rhs.e22 + lhs.e23 * rhs.e32;
+ m.e23 = lhs.e21 * rhs.e13 + lhs.e22 * rhs.e23 + lhs.e23 * rhs.e33;
+
+ m.e31 = lhs.e31 * rhs.e11 + lhs.e32 * rhs.e21 + lhs.e33 * rhs.e31;
+ m.e32 = lhs.e31 * rhs.e12 + lhs.e32 * rhs.e22 + lhs.e33 * rhs.e32;
+ m.e33 = lhs.e31 * rhs.e13 + lhs.e32 * rhs.e23 + lhs.e33 * rhs.e33;
+
+ return m;
+}
+
+
+bool operator==(const Matrix& lhs, const Matrix& rhs)
+{
+ if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) ||
+ !mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) ||
+ !mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) {
+ return false;
}
return true;
}
-void mathMultiply(Point* pt, const Matrix* transform)
+void operator*=(Point& pt, const Matrix& m)
{
- auto tx = pt->x * transform->e11 + pt->y * transform->e12 + transform->e13;
- auto ty = pt->x * transform->e21 + pt->y * transform->e22 + transform->e23;
- pt->x = tx;
- pt->y = ty;
+ auto tx = pt.x * m.e11 + pt.y * m.e12 + m.e13;
+ auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23;
+ pt.x = tx;
+ pt.y = ty;
}
diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h
index 5c2966956a..0f877d919e 100644
--- a/thirdparty/thorvg/src/common/tvgMath.h
+++ b/thirdparty/thorvg/src/common/tvgMath.h
@@ -38,11 +38,9 @@
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
-bool mathInverse(const Matrix* m, Matrix* out);
-Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs);
-void mathRotate(Matrix* m, float degree);
-bool mathIdentity(const Matrix* m);
-void mathMultiply(Point* pt, const Matrix* transform);
+/************************************************************************/
+/* General functions */
+/************************************************************************/
static inline float mathDeg2Rad(float degree)
@@ -63,28 +61,21 @@ static inline bool mathZero(float a)
}
-static inline bool mathZero(const Point& p)
-{
- return mathZero(p.x) && mathZero(p.y);
-}
-
-
static inline bool mathEqual(float a, float b)
{
return mathZero(a - b);
}
-static inline bool mathEqual(const Matrix& a, const Matrix& b)
-{
- if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) ||
- !mathEqual(a.e21, b.e21) || !mathEqual(a.e22, b.e22) || !mathEqual(a.e23, b.e23) ||
- !mathEqual(a.e31, b.e31) || !mathEqual(a.e32, b.e32) || !mathEqual(a.e33, b.e33)) {
- return false;
- }
- return true;
-}
+/************************************************************************/
+/* Matrix functions */
+/************************************************************************/
+void mathRotate(Matrix* m, float degree);
+bool mathInverse(const Matrix* m, Matrix* out);
+bool mathIdentity(const Matrix* m);
+Matrix operator*(const Matrix& lhs, const Matrix& rhs);
+bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool mathRightAngle(const Matrix* m)
{
@@ -114,15 +105,6 @@ static inline void mathIdentity(Matrix* m)
}
-static inline void mathTransform(Matrix* transform, Point* coord)
-{
- auto x = coord->x;
- auto y = coord->y;
- coord->x = x * transform->e11 + y * transform->e12 + transform->e13;
- coord->y = x * transform->e21 + y * transform->e22 + transform->e23;
-}
-
-
static inline void mathScale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
@@ -158,12 +140,37 @@ static inline void mathTranslateR(Matrix* m, float x, float y)
}
+static inline bool operator!=(const Matrix& lhs, const Matrix& rhs)
+{
+ return !(lhs == rhs);
+}
+
+
+static inline void operator*=(Matrix& lhs, const Matrix& rhs)
+{
+ lhs = lhs * rhs;
+}
+
+
static inline void mathLog(Matrix* m)
{
TVGLOG("MATH", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m->e11, m->e12, m->e13, m->e21, m->e22, m->e23, m->e31, m->e32, m->e33);
}
+/************************************************************************/
+/* Point functions */
+/************************************************************************/
+
+void operator*=(Point& pt, const Matrix& m);
+
+
+static inline bool mathZero(const Point& p)
+{
+ return mathZero(p.x) && mathZero(p.y);
+}
+
+
static inline float mathLength(const Point* a, const Point* b)
{
auto x = b->x - a->x;
@@ -182,6 +189,18 @@ static inline float mathLength(const Point& a)
}
+static inline bool operator==(const Point& lhs, const Point& rhs)
+{
+ return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y);
+}
+
+
+static inline bool operator!=(const Point& lhs, const Point& rhs)
+{
+ return !(lhs == rhs);
+}
+
+
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
@@ -212,6 +231,10 @@ static inline Point operator/(const Point& lhs, const float rhs)
}
+/************************************************************************/
+/* Interpolation functions */
+/************************************************************************/
+
template <typename T>
static inline T mathLerp(const T &start, const T &end, float t)
{
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index 0a47112084..f59994aae6 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 th, ts, tb;
+ float_t th, ts, tb;
const char *content, *hue, *satuation, *brightness;
content = str + 4;
content = _skipSpace(content, nullptr);
@@ -840,14 +840,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
if (state == MatrixState::Matrix) {
if (ptCount != 6) goto error;
Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (state == MatrixState::Translate) {
if (ptCount == 1) {
Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (ptCount == 2) {
Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else goto error;
} else if (state == MatrixState::Rotate) {
//Transform to signed.
@@ -857,14 +857,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
auto s = sinf(mathDeg2Rad(points[0]));
if (ptCount == 1) {
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (ptCount == 3) {
Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else {
goto error;
}
@@ -874,17 +874,17 @@ static Matrix* _parseTransformationMatrix(const char* value)
auto sy = sx;
if (ptCount == 2) sy = points[1];
Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (state == MatrixState::SkewX) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (state == MatrixState::SkewY) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
}
}
return matrix;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index 0b2113dd07..7e7efed3fc 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -203,9 +203,9 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape*
if (node->transform) finalTransform = *node->transform;
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
- finalTransform = mathMultiply(&finalTransform, &m);
+ finalTransform *= m;
}
- if (child->transform) finalTransform = mathMultiply(child->transform, &finalTransform);
+ if (child->transform) finalTransform = *child->transform * finalTransform;
return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
}
@@ -228,13 +228,13 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
m = *node->transform;
}
if (compNode->transform) {
- m = mathMultiply(&m, compNode->transform);
+ m *= *compNode->transform;
}
if (!compNode->node.clip.userSpace) {
float x, y, w, h;
P(paint)->bounds(&x, &y, &w, &h, false, false);
Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1};
- m = mathMultiply(&m, &mBBox);
+ m *= mBBox;
}
return m;
}
@@ -474,7 +474,10 @@ static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* sh
auto ptsCnt = shape->pathCoords(&pts);
auto p = const_cast<Point*>(pts) + currentPtsCnt;
- while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m);
+ while (currentPtsCnt++ < ptsCnt) {
+ *p *= *m;
+ ++p;
+ }
}
_applyProperty(loaderData, node, shape, vBox, svgPath, true);
@@ -505,6 +508,7 @@ static constexpr struct
} imageMimeTypes[] = {
{"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64},
{"png", sizeof("png"), imageMimeTypeEncoding::base64},
+ {"webp", sizeof("webp"), imageMimeTypeEncoding::base64},
{"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8},
};
@@ -615,7 +619,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
auto sy = node->node.image.h / h;
m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
}
- if (node->transform) m = mathMultiply(node->transform, &m);
+ if (node->transform) m = *node->transform * m;
picture->transform(m);
_applyComposition(loaderData, picture.get(), node, vBox, svgPath);
@@ -708,7 +712,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
if (node->transform) mUseTransform = *node->transform;
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
- mUseTransform = mathMultiply(&mUseTransform, &mTranslate);
+ mUseTransform *= mTranslate;
}
if (node->node.use.symbol) {
@@ -732,9 +736,9 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
// mSceneTransform = mUseTransform * mSymbolTransform * mViewBox
Matrix mSceneTransform = mViewBox;
if (node->node.use.symbol->transform) {
- mSceneTransform = mathMultiply(node->node.use.symbol->transform, &mViewBox);
+ mSceneTransform = *node->node.use.symbol->transform * mViewBox;
}
- mSceneTransform = mathMultiply(&mUseTransform, &mSceneTransform);
+ mSceneTransform = mUseTransform * mSceneTransform;
scene->transform(mSceneTransform);
if (node->node.use.symbol->node.symbol.overflowVisible) {
@@ -746,7 +750,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
// mClipTransform = mUseTransform * mSymbolTransform
Matrix mClipTransform = mUseTransform;
if (node->node.use.symbol->transform) {
- mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform);
+ mClipTransform = mUseTransform * *node->node.use.symbol->transform;
}
viewBoxClip->transform(mClipTransform);
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
index 3d582d291c..be1662daeb 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
@@ -150,7 +150,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (isTransformation) {
- if (transform) gradTransform = mathMultiply(transform, &gradTransform);
+ if (transform) gradTransform = *transform * gradTransform;
} else if (transform) {
gradTransform = *transform;
isTransformation = true;
@@ -216,7 +216,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (transform) {
- if (isTransformation) gradTransform = mathMultiply(transform, &gradTransform);
+ if (isTransformation) gradTransform = *transform * gradTransform;
else {
gradTransform = *transform;
isTransformation = true;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
index b85d943873..3431f03411 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
@@ -81,7 +81,7 @@ SwMpool* mpoolInit(uint32_t threads)
{
auto allocSize = threads + 1;
- auto mpool = static_cast<SwMpool*>(calloc(sizeof(SwMpool), 1));
+ auto mpool = static_cast<SwMpool*>(calloc(1, sizeof(SwMpool)));
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
mpool->dashOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
index 731f6984e3..8ec2bc0c47 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
@@ -1108,7 +1108,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
float ys = FLT_MAX, ye = -1.0f;
for (int i = 0; i < 4; i++) {
- if (transform) mathMultiply(&vertices[i].pt, transform);
+ if (transform) vertices[i].pt *= *transform;
if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
}
@@ -1169,9 +1169,9 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
float ys = FLT_MAX, ye = -1.0f;
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
transformedTris[i] = mesh->triangles[i];
- mathMultiply(&transformedTris[i].vertex[0].pt, transform);
- mathMultiply(&transformedTris[i].vertex[1].pt, transform);
- mathMultiply(&transformedTris[i].vertex[2].pt, transform);
+ transformedTris[i].vertex[0].pt *= *transform;
+ transformedTris[i].vertex[1].pt *= *transform;
+ transformedTris[i].vertex[2].pt *= *transform;
if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
index 0b2940c32d..f689179928 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
@@ -78,7 +78,6 @@ struct SwShapeTask : SwTask
{
SwShape shape;
const RenderShape* rshape = nullptr;
- bool cmpStroking = false;
bool clipper = false;
/* We assume that if the stroke width is greater than 2,
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
index 386cc594b4..1e5c4ef409 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
@@ -928,7 +928,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.ySpan = 0;
rw.outline = const_cast<SwOutline*>(outline);
- rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
+ rw.bandSize = rw.bufferSize / (sizeof(Cell) * 2); //bandSize: 256
rw.bandShoot = 0;
rw.antiAlias = antiAlias;
@@ -966,10 +966,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
- auto cellEnd = rw.bufferSize;
- cellEnd -= cellEnd % sizeof(Cell);
-
- auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + cellEnd);
+ auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + rw.bufferSize);
rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
if (rw.cells >= cellsMax) goto reduce_bands;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
index b9327374b6..d8dd40d45b 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
@@ -107,7 +107,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
//draw the current line fully
- } else if (len < dash.curLen) {
+ } else if (len <= dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
@@ -168,7 +168,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
//draw the current line fully
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
- } else if (len < dash.curLen) {
+ } else if (len <= dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
@@ -245,7 +245,86 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
}
-static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length, SwMpool* mpool, unsigned tid)
+static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length)
+{
+ auto begin = length * rshape->stroke->trim.begin;
+ auto end = length * rshape->stroke->trim.end;
+
+ //default
+ if (end > begin) {
+ if (begin > 0.0f) dash->cnt = 4;
+ else dash->cnt = 2;
+ //looping
+ } else dash->cnt = 3;
+
+ if (dash->cnt == 2) {
+ dash->pattern[0] = end - begin;
+ dash->pattern[1] = length - (end - begin);
+ } else if (dash->cnt == 3) {
+ dash->pattern[0] = end;
+ dash->pattern[1] = (begin - end);
+ dash->pattern[2] = length - begin;
+ } else {
+ dash->pattern[0] = 0; //zero dash to start with a space.
+ dash->pattern[1] = begin;
+ dash->pattern[2] = end - begin;
+ dash->pattern[3] = length - end;
+ }
+}
+
+
+static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32_t shiftCmds, bool subpath)
+{
+ const PathCommand* cmds = rshape->path.cmds.data + shiftCmds;
+ auto cmdCnt = rshape->path.cmds.count - shiftCmds;
+ const Point* pts = rshape->path.pts.data + shiftPts;
+ auto ptsCnt = rshape->path.pts.count - shiftPts;
+
+ //No actual shape data
+ if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
+
+ const Point* close = nullptr;
+ auto length = 0.0f;
+
+ //must begin with moveTo
+ if (cmds[0] == PathCommand::MoveTo) {
+ close = pts;
+ cmds++;
+ pts++;
+ cmdCnt--;
+ }
+
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
+ case PathCommand::Close: {
+ length += mathLength(pts - 1, close);
+ if (subpath) return length;
+ break;
+ }
+ case PathCommand::MoveTo: {
+ if (subpath) return length;
+ close = pts;
+ ++pts;
+ break;
+ }
+ case PathCommand::LineTo: {
+ length += mathLength(pts - 1, pts);
+ ++pts;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
+ pts += 3;
+ break;
+ }
+ }
+ ++cmds;
+ }
+ return length;
+}
+
+
+static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, bool trimmed, SwMpool* mpool, unsigned tid)
{
const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count;
@@ -255,49 +334,23 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
+ auto startPts = pts;
+ auto startCmds = cmds;
+
SwDashStroke dash;
auto offset = 0.0f;
- auto trimmed = false;
-
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
+ auto simultaneous = rshape->stroke->trim.simultaneous;
- //dash by trimming.
- if (length > 0.0f && dash.cnt == 0) {
- auto begin = length * rshape->stroke->trim.begin;
- auto end = length * rshape->stroke->trim.end;
-
- //TODO: mix trimming + dash style
-
- //default
- if (end > begin) {
- if (begin > 0.0f) dash.cnt += 4;
- else dash.cnt += 2;
- //looping
- } else dash.cnt += 3;
-
- dash.pattern = (float*)malloc(sizeof(float) * dash.cnt);
-
- if (dash.cnt == 2) {
- dash.pattern[0] = end - begin;
- dash.pattern[1] = length - (end - begin);
- } else if (dash.cnt == 3) {
- dash.pattern[0] = end;
- dash.pattern[1] = (begin - end);
- dash.pattern[2] = length - begin;
- } else {
- dash.pattern[0] = 0; //zero dash to start with a space.
- dash.pattern[1] = begin;
- dash.pattern[2] = end - begin;
- dash.pattern[3] = length - end;
- }
-
- trimmed = true;
- //just a dasy style.
+ if (dash.cnt == 0) {
+ if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
+ else return nullptr;
} else {
- if (dash.cnt == 0) return nullptr;
+ //TODO: handle dash + trim - for now trimming ignoring is forced
+ trimmed = false;
}
- //offset?
+ //offset
auto patternLength = 0.0f;
uint32_t offIdx = 0;
if (!mathZero(offset)) {
@@ -319,6 +372,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) {
+ if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous));
_dashMoveTo(dash, offIdx, offset, pts);
cmds++;
pts++;
@@ -331,8 +385,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
break;
}
case PathCommand::MoveTo: {
- if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts);
- else _dashMoveTo(dash, offIdx, offset, pts);
+ if (trimmed) {
+ if (simultaneous) {
+ _trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true));
+ _dashMoveTo(dash, offIdx, offset, pts);
+ } else _dashMoveTo(dash, pts);
+ } else _dashMoveTo(dash, offIdx, offset, pts);
++pts;
break;
}
@@ -358,56 +416,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
}
-static float _outlineLength(const RenderShape* rshape)
-{
- const PathCommand* cmds = rshape->path.cmds.data;
- auto cmdCnt = rshape->path.cmds.count;
- const Point* pts = rshape->path.pts.data;
- auto ptsCnt = rshape->path.pts.count;
-
- //No actual shape data
- if (cmdCnt == 0 || ptsCnt == 0) return 0.0f;
-
- const Point* close = nullptr;
- auto length = 0.0f;
- auto slength = -1.0f;
- auto simultaneous = !rshape->stroke->trim.individual;
-
- //Compute the whole length
- while (cmdCnt-- > 0) {
- switch (*cmds) {
- case PathCommand::Close: {
- length += mathLength(pts - 1, close);
- //retrieve the max length of the shape if the simultaneous mode.
- if (simultaneous) {
- if (slength < length) slength = length;
- length = 0.0f;
- }
- break;
- }
- case PathCommand::MoveTo: {
- close = pts;
- ++pts;
- break;
- }
- case PathCommand::LineTo: {
- length += mathLength(pts - 1, pts);
- ++pts;
- break;
- }
- case PathCommand::CubicTo: {
- length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
- pts += 3;
- break;
- }
- }
- ++cmds;
- }
- if (simultaneous && slength > length) return slength;
- else return length;
-}
-
-
static bool _axisAlignedRect(const SwOutline* outline)
{
//Fast Track: axis-aligned rectangle?
@@ -584,11 +592,10 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
auto dashStroking = false;
auto ret = true;
- auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
-
//Dash style (+trimming)
- if (rshape->stroke->dashCnt > 0 || length > 0) {
- shapeOutline = _genDashOutline(rshape, transform, length, mpool, tid);
+ auto trimmed = rshape->strokeTrim();
+ if (rshape->stroke->dashCnt > 0 || trimmed) {
+ shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
if (!shapeOutline) return false;
dashStroking = true;
//Normal style
diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
index 227ce10a0d..fcb632e2b1 100644
--- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
@@ -75,13 +75,13 @@ static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform
auto v2 = *pt3;
if (rTransform) {
- mathMultiply(&v1, &rTransform->m);
- mathMultiply(&v2, &rTransform->m);
+ v1 *= rTransform->m;
+ v2 *= rTransform->m;
}
if (pTransform) {
- mathMultiply(&v1, &pTransform->m);
- mathMultiply(&v2, &pTransform->m);
+ v1 *= pTransform->m;
+ v2 *= pTransform->m;
}
//sorting
@@ -327,7 +327,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
//Compute the AABB after transformation
for (int i = 0; i < 4; i++) {
- mathMultiply(&pt[i], m);
+ pt[i] *= *m;
if (pt[i].x < x1) x1 = pt[i].x;
if (pt[i].x > x2) x2 = pt[i].x;
diff --git a/thirdparty/thorvg/src/renderer/tvgRender.cpp b/thirdparty/thorvg/src/renderer/tvgRender.cpp
index 14f77571fb..9c779f7c00 100644
--- a/thirdparty/thorvg/src/renderer/tvgRender.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgRender.cpp
@@ -47,7 +47,7 @@ void RenderTransform::update()
mathScale(&m, scale, scale);
- if (!mathZero(degree)) mathRotate(&m, degree);
+ mathRotate(&m, degree);
mathTranslate(&m, x, y);
}
@@ -55,7 +55,7 @@ void RenderTransform::update()
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
{
- if (lhs && rhs) m = mathMultiply(&lhs->m, &rhs->m);
+ if (lhs && rhs) m = lhs->m * rhs->m;
else if (lhs) m = lhs->m;
else if (rhs) m = rhs->m;
else mathIdentity(&m);
diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h
index 6ea516c2f9..8f28d37dbc 100644
--- a/thirdparty/thorvg/src/renderer/tvgRender.h
+++ b/thirdparty/thorvg/src/renderer/tvgRender.h
@@ -142,7 +142,7 @@ struct RenderStroke
struct {
float begin = 0.0f;
float end = 1.0f;
- bool individual = false;
+ bool simultaneous = true;
} trim;
~RenderStroke()
diff --git a/thirdparty/thorvg/src/renderer/tvgShape.cpp b/thirdparty/thorvg/src/renderer/tvgShape.cpp
index 4cc8f64900..c010aa7bbf 100644
--- a/thirdparty/thorvg/src/renderer/tvgShape.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgShape.cpp
@@ -287,16 +287,14 @@ const Fill* Shape::fill() const noexcept
Result Shape::order(bool strokeFirst) noexcept
{
- if (!pImpl->strokeFirst(strokeFirst)) return Result::FailedAllocation;
-
+ pImpl->strokeFirst(strokeFirst);
return Result::Success;
}
Result Shape::stroke(float width) noexcept
{
- if (!pImpl->strokeWidth(width)) return Result::FailedAllocation;
-
+ pImpl->strokeWidth(width);
return Result::Success;
}
@@ -309,8 +307,7 @@ float Shape::strokeWidth() const noexcept
Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{
- if (!pImpl->strokeColor(r, g, b, a)) return Result::FailedAllocation;
-
+ pImpl->strokeColor(r, g, b, a);
return Result::Success;
}
@@ -349,27 +346,25 @@ uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
Result Shape::stroke(StrokeCap cap) noexcept
{
- if (!pImpl->strokeCap(cap)) return Result::FailedAllocation;
-
+ pImpl->strokeCap(cap);
return Result::Success;
}
Result Shape::stroke(StrokeJoin join) noexcept
{
- if (!pImpl->strokeJoin(join)) return Result::FailedAllocation;
-
+ pImpl->strokeJoin(join);
return Result::Success;
}
+
Result Shape::strokeMiterlimit(float miterlimit) noexcept
{
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
// - A negative value for stroke-miterlimit must be treated as an illegal value.
- if (miterlimit < 0.0f) return Result::NonSupport;
+ if (miterlimit < 0.0f) return Result::InvalidArguments;
// TODO Find out a reasonable max value.
- if (!pImpl->strokeMiterlimit(miterlimit)) return Result::FailedAllocation;
-
+ pImpl->strokeMiterlimit(miterlimit);
return Result::Success;
}
@@ -385,12 +380,26 @@ StrokeJoin Shape::strokeJoin() const noexcept
return pImpl->rs.strokeJoin();
}
+
float Shape::strokeMiterlimit() const noexcept
{
return pImpl->rs.strokeMiterlimit();
}
+Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
+{
+ pImpl->strokeTrim(begin, end, simultaneous);
+ return Result::Success;
+}
+
+
+bool Shape::strokeTrim(float* begin, float* end) const noexcept
+{
+ return pImpl->strokeTrim(begin, end);
+}
+
+
Result Shape::fill(FillRule r) noexcept
{
pImpl->rs.rule = r;
diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h
index 55335214be..4e85db37d0 100644
--- a/thirdparty/thorvg/src/renderer/tvgShape.h
+++ b/thirdparty/thorvg/src/renderer/tvgShape.h
@@ -207,60 +207,81 @@ struct Shape::Impl
flag |= RenderUpdateFlag::Path;
}
- bool strokeWidth(float width)
+ void strokeWidth(float width)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeTrim(float begin, float end, bool individual)
+ void strokeTrim(float begin, float end, bool simultaneous)
{
if (!rs.stroke) {
- if (begin == 0.0f && end == 1.0f) return true;
+ if (begin == 0.0f && end == 1.0f) return;
rs.stroke = new RenderStroke();
}
- if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
+ if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
+ rs.stroke->trim.simultaneous == simultaneous) return;
+
+ auto loop = true;
+
+ if (begin > 1.0f && end > 1.0f) loop = false;
+ if (begin < 0.0f && end < 0.0f) loop = false;
+ if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false;
+
+ if (begin > 1.0f) begin -= 1.0f;
+ if (begin < 0.0f) begin += 1.0f;
+ if (end > 1.0f) end -= 1.0f;
+ if (end < 0.0f) end += 1.0f;
+
+ if ((loop && begin < end) || (!loop && begin > end)) {
+ auto tmp = begin;
+ begin = end;
+ end = tmp;
+ }
rs.stroke->trim.begin = begin;
rs.stroke->trim.end = end;
- rs.stroke->trim.individual = individual;
+ rs.stroke->trim.simultaneous = simultaneous;
flag |= RenderUpdateFlag::Stroke;
+ }
- return true;
+ bool strokeTrim(float* begin, float* end)
+ {
+ if (rs.stroke) {
+ if (begin) *begin = rs.stroke->trim.begin;
+ if (end) *end = rs.stroke->trim.end;
+ return rs.stroke->trim.simultaneous;
+ } else {
+ if (begin) *begin = 0.0f;
+ if (end) *end = 1.0f;
+ return false;
+ }
}
- bool strokeCap(StrokeCap cap)
+ void strokeCap(StrokeCap cap)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeJoin(StrokeJoin join)
+ void strokeJoin(StrokeJoin join)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->join = join;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeMiterlimit(float miterlimit)
+ void strokeMiterlimit(float miterlimit)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->miterlimit = miterlimit;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+ void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
if (rs.stroke->fill) {
@@ -275,8 +296,6 @@ struct Shape::Impl
rs.stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
Result strokeFill(unique_ptr<Fill> f)
@@ -335,13 +354,11 @@ struct Shape::Impl
return rs.stroke->strokeFirst;
}
- bool strokeFirst(bool strokeFirst)
+ void strokeFirst(bool strokeFirst)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->strokeFirst = strokeFirst;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
void update(RenderUpdateFlag flag)
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index cd1aeadec0..c980b89c4b 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-VERSION=0.13.5
+VERSION=0.13.7
cd thirdparty/thorvg/ || true
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/