summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--core/extension/extension_api_dump.cpp221
-rw-r--r--core/extension/extension_api_dump.h4
-rw-r--r--core/io/image.cpp20
-rw-r--r--core/io/resource.cpp3
-rw-r--r--core/io/resource.h2
-rw-r--r--core/math/bvh_split.inc18
-rw-r--r--core/variant/array.cpp2
-rw-r--r--core/variant/dictionary.cpp2
-rw-r--r--core/variant/variant.cpp9
-rw-r--r--core/variant/variant.h3
-rw-r--r--doc/classes/Button.xml20
-rw-r--r--doc/classes/CheckBox.xml52
-rw-r--r--doc/classes/CheckButton.xml52
-rw-r--r--doc/classes/CodeEdit.xml70
-rw-r--r--doc/classes/ColorPicker.xml2
-rw-r--r--doc/classes/ColorPickerButton.xml46
-rw-r--r--doc/classes/EditorFileDialog.xml8
-rw-r--r--doc/classes/GraphElement.xml5
-rw-r--r--doc/classes/GraphNode.xml3
-rw-r--r--doc/classes/HBoxContainer.xml5
-rw-r--r--doc/classes/HFlowContainer.xml8
-rw-r--r--doc/classes/HScrollBar.xml35
-rw-r--r--doc/classes/HSeparator.xml8
-rw-r--r--doc/classes/HSlider.xml29
-rw-r--r--doc/classes/HSplitContainer.xml14
-rw-r--r--doc/classes/LinkButton.xml6
-rw-r--r--doc/classes/MenuBar.xml19
-rw-r--r--doc/classes/MenuButton.xml48
-rw-r--r--doc/classes/OptionButton.xml61
-rw-r--r--doc/classes/Popup.xml5
-rw-r--r--doc/classes/PopupMenu.xml6
-rw-r--r--doc/classes/PopupPanel.xml5
-rw-r--r--doc/classes/ProgressBar.xml3
-rw-r--r--doc/classes/Resource.xml24
-rw-r--r--doc/classes/ScrollBar.xml37
-rw-r--r--doc/classes/Separator.xml8
-rw-r--r--doc/classes/Slider.xml29
-rw-r--r--doc/classes/SplitContainer.xml3
-rw-r--r--doc/classes/TabBar.xml1
-rw-r--r--doc/classes/TabContainer.xml1
-rw-r--r--doc/classes/VBoxContainer.xml5
-rw-r--r--doc/classes/VFlowContainer.xml8
-rw-r--r--doc/classes/VScrollBar.xml35
-rw-r--r--doc/classes/VSeparator.xml8
-rw-r--r--doc/classes/VSlider.xml29
-rw-r--r--doc/classes/VSplitContainer.xml14
-rw-r--r--editor/doc_tools.cpp93
-rw-r--r--editor/editor_themes.cpp2
-rw-r--r--editor/gui/editor_file_dialog.cpp31
-rw-r--r--editor/gui/editor_file_dialog.h3
-rw-r--r--editor/icons/FileTree.svg1
-rw-r--r--editor/import/scene_import_settings.cpp2
-rw-r--r--editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp1
-rw-r--r--editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp1
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp3
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp26
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp23
-rw-r--r--main/main.cpp17
-rw-r--r--misc/extension_api_validation/4.1-stable.expected7
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp90
-rw-r--r--modules/gdscript/gdscript_compiler.cpp7
-rw-r--r--modules/gdscript/gdscript_parser.cpp13
-rw-r--r--modules/gdscript/gdscript_parser.h3
-rw-r--r--modules/gdscript/gdscript_warning.cpp2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.gd37
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.out33
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/super.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/object_constructor.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/object_constructor.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_variables.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.gd3
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildDiagnostic.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs469
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsFilter.cs40
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs694
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs293
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs15
-rw-r--r--modules/svg/SCsub8
-rw-r--r--modules/text_server_adv/SCsub4
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct16
-rw-r--r--modules/text_server_fb/SCsub4
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct16
-rw-r--r--scene/3d/decal.cpp2
-rw-r--r--scene/3d/mesh_instance_3d.cpp1
-rw-r--r--scene/3d/ray_cast_3d.cpp1
-rw-r--r--scene/3d/shape_cast_3d.cpp1
-rw-r--r--scene/3d/voxelizer.cpp1
-rw-r--r--scene/gui/tab_bar.cpp15
-rw-r--r--scene/gui/tab_bar.h2
-rw-r--r--scene/main/scene_tree.cpp3
-rw-r--r--scene/main/viewport.cpp3
-rw-r--r--scene/theme/default_theme.cpp6
-rw-r--r--scene/theme/theme_db.cpp34
-rw-r--r--scene/theme/theme_db.h35
-rw-r--r--servers/audio/audio_stream.cpp4
-rw-r--r--servers/navigation_server_3d.cpp14
-rw-r--r--servers/physics_server_3d.h2
-rw-r--r--servers/register_server_types.cpp2
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h3
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h122
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp397
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp2
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp869
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h245
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp48
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp4
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp244
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp12
-rw-r--r--thirdparty/thorvg/src/lib/tvgAnimation.cpp30
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvasImpl.h5
-rw-r--r--thirdparty/thorvg/src/lib/tvgCommon.h8
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.cpp134
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.h23
-rw-r--r--thirdparty/thorvg/src/lib/tvgLinearGradient.cpp100
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.cpp36
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.cpp29
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.h23
-rw-r--r--thirdparty/thorvg/src/lib/tvgPictureImpl.h11
-rw-r--r--thirdparty/thorvg/src/lib/tvgRadialGradient.cpp98
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.h39
-rw-r--r--thirdparty/thorvg/src/lib/tvgSceneImpl.h12
-rw-r--r--thirdparty/thorvg/src/lib/tvgShape.cpp23
-rw-r--r--thirdparty/thorvg/src/lib/tvgShapeImpl.h54
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp13
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.h1
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp4
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp88
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h27
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp8
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp163
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp223
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h5
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp6
-rw-r--r--thirdparty/thorvg/src/utils/tvgArray.h (renamed from thirdparty/thorvg/src/lib/tvgArray.h)27
-rw-r--r--thirdparty/thorvg/src/utils/tvgBezier.cpp (renamed from thirdparty/thorvg/src/lib/tvgBezier.cpp)2
-rw-r--r--thirdparty/thorvg/src/utils/tvgBezier.h (renamed from thirdparty/thorvg/src/lib/tvgBezier.h)0
-rw-r--r--thirdparty/thorvg/src/utils/tvgCompressor.cpp (renamed from thirdparty/thorvg/src/lib/tvgLzw.cpp)75
-rw-r--r--thirdparty/thorvg/src/utils/tvgCompressor.h (renamed from thirdparty/thorvg/src/lib/tvgLzw.h)7
-rw-r--r--thirdparty/thorvg/src/utils/tvgMath.h (renamed from thirdparty/thorvg/src/lib/tvgMath.h)52
-rw-r--r--thirdparty/thorvg/src/utils/tvgStr.cpp239
-rw-r--r--thirdparty/thorvg/src/utils/tvgStr.h37
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh4
162 files changed, 3880 insertions, 2833 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index e79817a2e7..8288e79602 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -466,7 +466,7 @@ License: Expat
Files: ./thirdparty/thorvg/
Comment: ThorVG
-Copyright: 2020-2022, Samsung Electronics Co., Ltd.
+Copyright: 2020-2023, The ThorVG Project
License: Expat
Files: ./thirdparty/tinyexr/
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index f11a4bc9a4..58cb51245a 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -39,6 +39,7 @@
#include "core/version.h"
#ifdef TOOLS_ENABLED
+#include "editor/editor_help.h"
static String get_builtin_or_variant_type_name(const Variant::Type p_type) {
if (p_type == Variant::NIL) {
@@ -88,7 +89,16 @@ static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) {
return argmeta[metadata];
}
-Dictionary GDExtensionAPIDump::generate_extension_api() {
+static String fix_doc_description(const String &p_bbcode) {
+ // Based on what EditorHelp does.
+
+ return p_bbcode.dedent()
+ .replace("\t", "")
+ .replace("\r", "")
+ .strip_edges();
+}
+
+Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
Dictionary api_dump;
{
@@ -460,12 +470,22 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
api_dump["builtin_class_member_offsets"] = core_type_member_offsets;
}
+ if (p_include_docs) {
+ EditorHelp::generate_doc(false);
+ }
+
{
// Global enums and constants.
Array constants;
HashMap<String, List<Pair<String, int64_t>>> enum_list;
HashMap<String, bool> enum_is_bitfield;
+ const DocData::ClassDoc *global_scope_doc = nullptr;
+ if (p_include_docs) {
+ global_scope_doc = EditorHelp::get_doc_data()->class_list.getptr("@GlobalScope");
+ CRASH_COND_MSG(!global_scope_doc, "Could not find '@GlobalScope' in DocData.");
+ }
+
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
int64_t value = CoreConstants::get_global_constant_value(i);
String enum_name = CoreConstants::get_global_constant_enum(i);
@@ -479,6 +499,14 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["name"] = name;
d["value"] = value;
d["is_bitfield"] = bitfield;
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) {
+ if (constant_doc.name == name) {
+ d["documentation"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
constants.push_back(d);
}
}
@@ -490,11 +518,25 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary d1;
d1["name"] = E.key;
d1["is_bitfield"] = enum_is_bitfield[E.key];
+ if (p_include_docs) {
+ const DocData::EnumDoc *enum_doc = global_scope_doc->enums.getptr(E.key);
+ if (enum_doc) {
+ d1["documentation"] = fix_doc_description(enum_doc->description);
+ }
+ }
Array values;
for (const Pair<String, int64_t> &F : E.value) {
Dictionary d2;
d2["name"] = F.first;
d2["value"] = F.second;
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) {
+ if (constant_doc.name == F.first) {
+ d2["documentation"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
values.push_back(d2);
}
d1["values"] = values;
@@ -509,6 +551,12 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
List<StringName> utility_func_names;
Variant::get_utility_function_list(&utility_func_names);
+ const DocData::ClassDoc *global_scope_doc = nullptr;
+ if (p_include_docs) {
+ global_scope_doc = EditorHelp::get_doc_data()->class_list.getptr("@GlobalScope");
+ CRASH_COND_MSG(!global_scope_doc, "Could not find '@GlobalScope' in DocData.");
+ }
+
for (const StringName &name : utility_func_names) {
Dictionary func;
func["name"] = String(name);
@@ -545,6 +593,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
func["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : global_scope_doc->methods) {
+ if (method_doc.name == name) {
+ func["documentation"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
utility_funcs.push_back(func);
}
@@ -571,6 +628,12 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["is_keyed"] = Variant::is_keyed(type);
+ DocData::ClassDoc *builtin_doc = nullptr;
+ if (p_include_docs && d["name"] != "Nil") {
+ builtin_doc = EditorHelp::get_doc_data()->class_list.getptr(d["name"]);
+ CRASH_COND_MSG(!builtin_doc, vformat("Could not find '%s' in DocData.", d["name"]));
+ }
+
{
//members
Array members;
@@ -581,6 +644,14 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary d2;
d2["name"] = String(member_name);
d2["type"] = get_builtin_or_variant_type_name(Variant::get_member_type(type, member_name));
+ if (p_include_docs) {
+ for (const DocData::PropertyDoc &property_doc : builtin_doc->properties) {
+ if (property_doc.name == member_name) {
+ d2["documentation"] = fix_doc_description(property_doc.description);
+ break;
+ }
+ }
+ }
members.push_back(d2);
}
if (members.size()) {
@@ -599,6 +670,14 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Variant constant = Variant::get_constant_value(type, constant_name);
d2["type"] = get_builtin_or_variant_type_name(constant.get_type());
d2["value"] = constant.get_construct_string();
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) {
+ if (constant_doc.name == constant_name) {
+ d2["documentation"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
constants.push_back(d2);
}
if (constants.size()) {
@@ -624,9 +703,24 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary values_dict;
values_dict["name"] = String(enumeration);
values_dict["value"] = Variant::get_enum_value(type, enum_name, enumeration);
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) {
+ if (constant_doc.name == enumeration) {
+ values_dict["documentation"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
values.push_back(values_dict);
}
+ if (p_include_docs) {
+ const DocData::EnumDoc *enum_doc = builtin_doc->enums.getptr(enum_name);
+ if (enum_doc) {
+ enum_dict["documentation"] = fix_doc_description(enum_doc->description);
+ }
+ }
+
if (values.size()) {
enum_dict["values"] = values;
}
@@ -646,11 +740,22 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Variant::Type rt = Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j));
if (rt != Variant::NIL) {
Dictionary d2;
- d2["name"] = Variant::get_operator_name(Variant::Operator(k));
+ String operator_name = Variant::get_operator_name(Variant::Operator(k));
+ d2["name"] = operator_name;
if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) {
d2["right_type"] = get_builtin_or_variant_type_name(Variant::Type(j));
}
d2["return_type"] = get_builtin_or_variant_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)));
+
+ if (p_include_docs && builtin_doc != nullptr) {
+ for (const DocData::MethodDoc &operator_doc : builtin_doc->operators) {
+ if (operator_doc.name == "operator " + operator_name) {
+ d2["documentation"] = fix_doc_description(operator_doc.description);
+ break;
+ }
+ }
+ }
+
operators.push_back(d2);
}
}
@@ -697,6 +802,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : builtin_doc->methods) {
+ if (method_doc.name == method_name) {
+ d2["documentation"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
methods.push_back(d2);
}
if (methods.size()) {
@@ -722,6 +836,28 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
if (arguments.size()) {
d2["arguments"] = arguments;
}
+
+ if (p_include_docs && builtin_doc) {
+ for (const DocData::MethodDoc &constructor_doc : builtin_doc->constructors) {
+ if (constructor_doc.arguments.size() != argcount) {
+ continue;
+ }
+ bool constructor_found = true;
+ for (int k = 0; k < argcount; k++) {
+ const DocData::ArgumentDoc &argument_doc = constructor_doc.arguments[k];
+ const Dictionary &argument_dict = arguments[k];
+ const String &argument_string = argument_dict["type"];
+ if (argument_doc.type != argument_string) {
+ constructor_found = false;
+ break;
+ }
+ }
+ if (constructor_found) {
+ d2["documentation"] = fix_doc_description(constructor_doc.description);
+ }
+ }
+ }
+
constructors.push_back(d2);
}
@@ -734,6 +870,10 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["has_destructor"] = Variant::has_destructor(type);
}
+ if (p_include_docs && builtin_doc != nullptr) {
+ d["documentation"] = fix_doc_description(builtin_doc->description);
+ }
+
builtins.push_back(d);
}
@@ -763,6 +903,12 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["inherits"] = String(parent_class);
}
+ DocData::ClassDoc *class_doc = nullptr;
+ if (p_include_docs) {
+ class_doc = EditorHelp::get_doc_data()->class_list.getptr(class_name);
+ CRASH_COND_MSG(!class_doc, vformat("Could not find '%s' in DocData.", class_name));
+ }
+
{
ClassDB::APIType api = ClassDB::get_api_type(class_name);
static const char *api_type[5] = { "core", "editor", "extension", "editor_extension" };
@@ -784,6 +930,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["name"] = String(F);
d2["value"] = ClassDB::get_integer_constant(class_name, F);
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
+ if (constant_doc.name == F) {
+ d2["documentation"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
+
constants.push_back(d2);
}
@@ -808,11 +963,28 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary d3;
d3["name"] = String(G->get());
d3["value"] = ClassDB::get_integer_constant(class_name, G->get());
+
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
+ if (constant_doc.name == G->get()) {
+ d3["documentation"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
+
values.push_back(d3);
}
d2["values"] = values;
+ if (p_include_docs) {
+ const DocData::EnumDoc *enum_doc = class_doc->enums.getptr(F);
+ if (enum_doc) {
+ d2["documentation"] = fix_doc_description(enum_doc->description);
+ }
+ }
+
enums.push_back(d2);
}
@@ -864,6 +1036,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : class_doc->methods) {
+ if (method_doc.name == method_name) {
+ d2["documentation"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
methods.push_back(d2);
} else if (F.name.begins_with("_")) {
@@ -932,6 +1113,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : class_doc->methods) {
+ if (method_doc.name == method_name) {
+ d2["documentation"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
methods.push_back(d2);
}
}
@@ -966,6 +1156,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &signal_doc : class_doc->signals) {
+ if (signal_doc.name == signal_name) {
+ d2["documentation"] = fix_doc_description(signal_doc.description);
+ break;
+ }
+ }
+ }
+
signals.push_back(d2);
}
@@ -1005,6 +1204,16 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
if (index != -1) {
d2["index"] = index;
}
+
+ if (p_include_docs) {
+ for (const DocData::PropertyDoc &property_doc : class_doc->properties) {
+ if (property_doc.name == property_name) {
+ d2["documentation"] = fix_doc_description(property_doc.description);
+ break;
+ }
+ }
+ }
+
properties.push_back(d2);
}
@@ -1013,6 +1222,10 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
}
}
+ if (p_include_docs && class_doc != nullptr) {
+ d["documentation"] = fix_doc_description(class_doc->description);
+ }
+
classes.push_back(d);
}
@@ -1065,8 +1278,8 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
return api_dump;
}
-void GDExtensionAPIDump::generate_extension_json_file(const String &p_path) {
- Dictionary api = generate_extension_api();
+void GDExtensionAPIDump::generate_extension_json_file(const String &p_path, bool p_include_docs) {
+ Dictionary api = generate_extension_api(p_include_docs);
Ref<JSON> json;
json.instantiate();
diff --git a/core/extension/extension_api_dump.h b/core/extension/extension_api_dump.h
index 11ea2cf923..204a115f84 100644
--- a/core/extension/extension_api_dump.h
+++ b/core/extension/extension_api_dump.h
@@ -37,8 +37,8 @@
class GDExtensionAPIDump {
public:
- static Dictionary generate_extension_api();
- static void generate_extension_json_file(const String &p_path);
+ static Dictionary generate_extension_api(bool p_include_docs = false);
+ static void generate_extension_json_file(const String &p_path, bool p_include_docs = false);
static Error validate_extension_json_file(const String &p_path);
};
#endif
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 674af6b0a6..15d0182dfc 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -1930,8 +1930,7 @@ Error Image::generate_mipmaps(bool p_renormalize) {
}
Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) {
- Vector<double> normal_sat_vec; //summed area table
- double *normal_sat = nullptr; //summed area table for normal map
+ LocalVector<double> normal_sat_vec; //summed area table
int normal_w = 0, normal_h = 0;
ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->is_empty(), ERR_INVALID_PARAMETER, "Must provide a valid normal map for roughness mipmaps");
@@ -1945,8 +1944,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
normal_h = nm->get_height();
normal_sat_vec.resize(normal_w * normal_h * 3);
-
- normal_sat = normal_sat_vec.ptrw();
+ double *normal_sat = normal_sat_vec.ptr();
//create summed area table
@@ -2021,24 +2019,26 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
avg[2] += normal_sat[tofs + 2];
}
- if (from_y > 0) {
+ if (from_y > 0 && to_x > 0) {
uint32_t tofs = ((from_y - 1) * normal_w + to_x) * 3;
avg[0] -= normal_sat[tofs + 0];
avg[1] -= normal_sat[tofs + 1];
avg[2] -= normal_sat[tofs + 2];
}
- if (from_x > 0) {
+ if (from_x > 0 && to_y > 0) {
uint32_t tofs = (to_y * normal_w + (from_x - 1)) * 3;
avg[0] -= normal_sat[tofs + 0];
avg[1] -= normal_sat[tofs + 1];
avg[2] -= normal_sat[tofs + 2];
}
- uint32_t tofs = (to_y * normal_w + to_x) * 3;
- avg[0] += normal_sat[tofs + 0];
- avg[1] += normal_sat[tofs + 1];
- avg[2] += normal_sat[tofs + 2];
+ if (to_y > 0 && to_x > 0) {
+ uint32_t tofs = (to_y * normal_w + to_x) * 3;
+ avg[0] += normal_sat[tofs + 0];
+ avg[1] += normal_sat[tofs + 1];
+ avg[2] += normal_sat[tofs + 2];
+ }
double div = double(size_x * size_y);
Vector3 vec(avg[0] / div, avg[1] / div, avg[2] / div);
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 68cdeabac7..e0d42a274a 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -379,8 +379,8 @@ Node *Resource::get_local_scene() const {
}
void Resource::setup_local_to_scene() {
- // Can't use GDVIRTUAL in Resource, so this will have to be done with a signal
emit_signal(SNAME("setup_local_to_scene_requested"));
+ GDVIRTUAL_CALL(_setup_local_to_scene);
}
void Resource::reset_local_to_scene() {
@@ -460,6 +460,7 @@ void Resource::_bind_methods() {
get_rid_bind.return_val.type = Variant::RID;
::ClassDB::add_virtual_method(get_class_static(), get_rid_bind, true, Vector<String>(), true);
+ GDVIRTUAL_BIND(_setup_local_to_scene);
}
Resource::Resource() :
diff --git a/core/io/resource.h b/core/io/resource.h
index f848bdba99..a9b1a88f6b 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -33,6 +33,7 @@
#include "core/io/resource_uid.h"
#include "core/object/class_db.h"
+#include "core/object/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
#include "core/templates/safe_refcount.h"
#include "core/templates/self_list.h"
@@ -81,6 +82,7 @@ protected:
void _take_over_path(const String &p_path);
virtual void reset_local_to_scene();
+ GDVIRTUAL0(_setup_local_to_scene);
public:
static Node *(*_get_local_scene_func)(); //used by editor
diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc
index 875abedb70..2c85a63575 100644
--- a/core/math/bvh_split.inc
+++ b/core/math/bvh_split.inc
@@ -20,8 +20,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[0] = group_a[num_a - 1];
num_a--;
+ group_a[0] = group_a[num_a];
return;
}
@@ -30,15 +30,15 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
int order[POINT::AXIS_COUNT];
- order[0] = size.min_axis_index();
- order[POINT::AXIS_COUNT - 1] = size.max_axis_index();
+ order[0] = size.max_axis_index(); // The longest axis.
+ order[POINT::AXIS_COUNT - 1] = size.min_axis_index(); // The shortest axis.
static_assert(POINT::AXIS_COUNT <= 3, "BVH POINT::AXIS_COUNT has unexpected size");
if constexpr (POINT::AXIS_COUNT == 3) {
order[1] = 3 - (order[0] + order[2]);
}
- // simplest case, split on the longest axis
+ // Simplest case, split on the longest axis.
int split_axis = order[0];
for (int a = 0; a < num_a; a++) {
uint32_t ind = group_a[a];
@@ -48,8 +48,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[a] = group_a[num_a - 1];
num_a--;
+ group_a[a] = group_a[num_a];
// do this one again, as it has been replaced
a--;
@@ -67,7 +67,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
}
num_b = 0;
- // now calculate the best split
+ // Now calculate the best split.
for (int axis = 1; axis < POINT::AXIS_COUNT; axis++) {
split_axis = order[axis];
int count = 0;
@@ -105,8 +105,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[a] = group_a[num_a - 1];
num_a--;
+ group_a[a] = group_a[num_a];
// do this one again, as it has been replaced
a--;
@@ -123,8 +123,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[0] = group_a[num_a - 1];
num_a--;
+ group_a[0] = group_a[num_a];
}
// opposite problem! :)
if (!num_a) {
@@ -134,8 +134,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_a[num_a++] = ind;
// remove from b
- group_b[0] = group_b[num_b - 1];
num_b--;
+ group_b[0] = group_b[num_b];
}
}
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 4c9ee68ab5..ab0315ae34 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -137,7 +137,7 @@ bool Array::recursive_equal(const Array &p_array, int recursion_count) const {
}
recursion_count++;
for (int i = 0; i < size; i++) {
- if (!a1[i].hash_compare(a2[i], recursion_count)) {
+ if (!a1[i].hash_compare(a2[i], recursion_count, false)) {
return false;
}
}
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 2dda9fc1f8..141ce25fa6 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -210,7 +210,7 @@ bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_c
recursion_count++;
for (const KeyValue<Variant, Variant> &this_E : _p->variant_map) {
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator other_E(p_dictionary._p->variant_map.find(this_E.key));
- if (!other_E || !this_E.value.hash_compare(other_E->value, recursion_count)) {
+ if (!other_E || !this_E.value.hash_compare(other_E->value, recursion_count, false)) {
return false;
}
}
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 8a0289898d..63ea3274ce 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -3235,8 +3235,11 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
return 0;
}
+#define hash_compare_scalar_base(p_lhs, p_rhs, semantic_comparison) \
+ (((p_lhs) == (p_rhs)) || (semantic_comparison && Math::is_nan(p_lhs) && Math::is_nan(p_rhs)))
+
#define hash_compare_scalar(p_lhs, p_rhs) \
- (((p_lhs) == (p_rhs)) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)))
+ (hash_compare_scalar_base(p_lhs, p_rhs, true))
#define hash_compare_vector2(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
@@ -3282,7 +3285,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
\
return true
-bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const {
+bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool semantic_comparison) const {
if (type != p_variant.type) {
return false;
}
@@ -3293,7 +3296,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
} break;
case FLOAT: {
- return hash_compare_scalar(_data._float, p_variant._data._float);
+ return hash_compare_scalar_base(_data._float, p_variant._data._float, semantic_comparison);
} break;
case STRING: {
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 04c2fe2012..d698f85754 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -751,7 +751,8 @@ public:
uint32_t hash() const;
uint32_t recursive_hash(int recursion_count) const;
- bool hash_compare(const Variant &p_variant, int recursion_count = 0) const;
+ // By default, performs a semantic comparison. Otherwise, numeric/binary comparison (if appropriate).
+ bool hash_compare(const Variant &p_variant, int recursion_count = 0, bool semantic_comparison = true) const;
bool identity_compare(const Variant &p_variant) const;
bool booleanize() const;
String stringify(int recursion_count = 0) const;
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 25c71deb1f..bee0cbcf4a 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -131,20 +131,40 @@
<theme_item name="font_size" data_type="font_size" type="int">
Font size of the [Button]'s text.
</theme_item>
+ <theme_item name="icon" data_type="icon" type="Texture2D">
+ </theme_item>
<theme_item name="disabled" data_type="style" type="StyleBox">
[StyleBox] used when the [Button] is disabled.
</theme_item>
+ <theme_item name="disabled_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the [Button] is disabled (for right-to-left layouts).
+ </theme_item>
<theme_item name="focus" data_type="style" type="StyleBox">
[StyleBox] used when the [Button] is focused. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
</theme_item>
<theme_item name="hover" data_type="style" type="StyleBox">
[StyleBox] used when the [Button] is being hovered.
</theme_item>
+ <theme_item name="hover_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the [Button] is being hovered (for right-to-left layouts).
+ </theme_item>
+ <theme_item name="hover_pressed" data_type="style" type="StyleBox">
+ [StyleBox] used when the [Button] is being pressed and hovered at the same time.
+ </theme_item>
+ <theme_item name="hover_pressed_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the [Button] is being pressed and hovered at the same time (for right-to-left layouts).
+ </theme_item>
<theme_item name="normal" data_type="style" type="StyleBox">
Default [StyleBox] for the [Button].
</theme_item>
+ <theme_item name="normal_mirrored" data_type="style" type="StyleBox">
+ Default [StyleBox] for the [Button] (for right-to-left layouts).
+ </theme_item>
<theme_item name="pressed" data_type="style" type="StyleBox">
[StyleBox] used when the [Button] is being pressed.
</theme_item>
+ <theme_item name="pressed_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the [Button] is being pressed (for right-to-left layouts).
+ </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml
index fb733a95b8..5b2c93f7b3 100644
--- a/doc/classes/CheckBox.xml
+++ b/doc/classes/CheckBox.xml
@@ -15,43 +15,9 @@
<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" overrides="BaseButton" default="true" />
</members>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
- The [CheckBox] text's font color.
- </theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
- The [CheckBox] text's font color when it's disabled.
- </theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- The [CheckBox] text's font color when it's focused. Only replaces the normal text color of the checkbox. Disabled, hovered, and pressed states take precedence over this color.
- </theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- The [CheckBox] text's font color when it's hovered.
- </theme_item>
- <theme_item name="font_hover_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The [CheckBox] text's font color when it's hovered and pressed.
- </theme_item>
- <theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The tint of text outline of the [CheckBox].
- </theme_item>
- <theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The [CheckBox] text's font color when it's pressed.
- </theme_item>
<theme_item name="check_v_offset" data_type="constant" type="int" default="0">
The vertical offset used when rendering the check icons (in pixels).
</theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="4">
- The separation between the check icon and the text (in pixels). Negative values will be treated as [code]0[/code] when used.
- </theme_item>
- <theme_item name="outline_size" data_type="constant" type="int" default="0">
- The size of the text outline.
- [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
- </theme_item>
- <theme_item name="font" data_type="font" type="Font">
- The [Font] to use for the [CheckBox] text.
- </theme_item>
- <theme_item name="font_size" data_type="font_size" type="int">
- Font size of the [CheckBox]'s text.
- </theme_item>
<theme_item name="checked" data_type="icon" type="Texture2D">
The check icon to display when the [CheckBox] is checked.
</theme_item>
@@ -76,23 +42,5 @@
<theme_item name="unchecked_disabled" data_type="icon" type="Texture2D">
The check icon to display when the [CheckBox] is unchecked and is disabled.
</theme_item>
- <theme_item name="disabled" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckBox] is disabled.
- </theme_item>
- <theme_item name="focus" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckBox] is focused. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
- </theme_item>
- <theme_item name="hover" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckBox] is hovered.
- </theme_item>
- <theme_item name="hover_pressed" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckBox] is hovered and pressed.
- </theme_item>
- <theme_item name="normal" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background.
- </theme_item>
- <theme_item name="pressed" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckBox] is pressed.
- </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml
index 0fffe7f621..c2da6cfe3e 100644
--- a/doc/classes/CheckButton.xml
+++ b/doc/classes/CheckButton.xml
@@ -14,43 +14,9 @@
<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" overrides="BaseButton" default="true" />
</members>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
- The [CheckButton] text's font color.
- </theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
- The [CheckButton] text's font color when it's disabled.
- </theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- The [CheckButton] text's font color when it's focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
- </theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- The [CheckButton] text's font color when it's hovered.
- </theme_item>
- <theme_item name="font_hover_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The [CheckButton] text's font color when it's hovered and pressed.
- </theme_item>
- <theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The tint of text outline of the [CheckButton].
- </theme_item>
- <theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The [CheckButton] text's font color when it's pressed.
- </theme_item>
<theme_item name="check_v_offset" data_type="constant" type="int" default="0">
The vertical offset used when rendering the toggle icons (in pixels).
</theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="4">
- The separation between the toggle icon and the text (in pixels). Negative values will be treated as [code]0[/code] when used.
- </theme_item>
- <theme_item name="outline_size" data_type="constant" type="int" default="0">
- The size of the text outline.
- [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
- </theme_item>
- <theme_item name="font" data_type="font" type="Font">
- The [Font] to use for the [CheckButton] text.
- </theme_item>
- <theme_item name="font_size" data_type="font_size" type="int">
- Font size of the [CheckButton]'s text.
- </theme_item>
<theme_item name="checked" data_type="icon" type="Texture2D">
The icon to display when the [CheckButton] is checked (for left-to-right layouts).
</theme_item>
@@ -75,23 +41,5 @@
<theme_item name="unchecked_mirrored" data_type="icon" type="Texture2D">
The icon to display when the [CheckButton] is unchecked (for right-to-left layouts).
</theme_item>
- <theme_item name="disabled" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckButton] is disabled.
- </theme_item>
- <theme_item name="focus" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckButton] is focused. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
- </theme_item>
- <theme_item name="hover" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckButton] is hovered.
- </theme_item>
- <theme_item name="hover_pressed" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckButton] is hovered and pressed.
- </theme_item>
- <theme_item name="normal" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background.
- </theme_item>
- <theme_item name="pressed" data_type="style" type="StyleBox">
- The [StyleBox] to display as a background when the [CheckButton] is pressed.
- </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index 9b4b3aa36e..e5c255706f 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -633,9 +633,6 @@
</constant>
</constants>
<theme_items>
- <theme_item name="background_color" data_type="color" type="Color" default="Color(0, 0, 0, 0)">
- Sets the background [Color].
- </theme_item>
<theme_item name="bookmark_color" data_type="color" type="Color" default="Color(0.5, 0.64, 1, 0.8)">
[Color] of the bookmark icon for bookmarked lines.
</theme_item>
@@ -645,12 +642,6 @@
<theme_item name="breakpoint_color" data_type="color" type="Color" default="Color(0.9, 0.29, 0.3, 1)">
[Color] of the breakpoint icon for bookmarked lines.
</theme_item>
- <theme_item name="caret_background_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
- [Color] of the text behind the caret when block caret is enabled.
- </theme_item>
- <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
- [Color] of the caret.
- </theme_item>
<theme_item name="code_folding_color" data_type="color" type="Color" default="Color(0.8, 0.8, 0.8, 0.8)">
[Color] for all icons related to line folding.
</theme_item>
@@ -660,9 +651,6 @@
<theme_item name="completion_existing_color" data_type="color" type="Color" default="Color(0.87, 0.87, 0.87, 0.13)">
Background highlight [Color] for matching text in code completion options.
</theme_item>
- <theme_item name="completion_font_color" data_type="color" type="Color" default="Color(0.67, 0.67, 0.67, 1)">
- Font [Color] for the code completion popup.
- </theme_item>
<theme_item name="completion_scroll_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.29)">
[Color] of the scrollbar in the code completion popup.
</theme_item>
@@ -672,48 +660,18 @@
<theme_item name="completion_selected_color" data_type="color" type="Color" default="Color(0.26, 0.26, 0.27, 1)">
Background highlight [Color] for the current selected option item in the code completion popup.
</theme_item>
- <theme_item name="current_line_color" data_type="color" type="Color" default="Color(0.25, 0.25, 0.26, 0.8)">
- Background [Color] of the line containing the caret.
- </theme_item>
<theme_item name="executing_line_color" data_type="color" type="Color" default="Color(0.98, 0.89, 0.27, 1)">
[Color] of the executing icon for executing lines.
</theme_item>
<theme_item name="folded_code_region_color" data_type="color" type="Color" default="Color(0.68, 0.46, 0.77, 0.2)">
[Color] of background line highlight for folded code region.
</theme_item>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
- Sets the font [Color].
- </theme_item>
- <theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The tint of text outline of the [CodeEdit].
- </theme_item>
- <theme_item name="font_placeholder_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.6)">
- Font color for [member TextEdit.placeholder_text].
- </theme_item>
- <theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
- Sets the font [Color] when [member TextEdit.editable] is disabled.
- </theme_item>
- <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0, 0, 0, 0)">
- Sets the [Color] of the selected text. If equal to [code]Color(0, 0, 0, 0)[/code], it will be ignored.
- </theme_item>
<theme_item name="line_length_guideline_color" data_type="color" type="Color" default="Color(0.3, 0.5, 0.8, 0.1)">
[Color] of the main line length guideline, secondary guidelines will have 50% alpha applied.
</theme_item>
<theme_item name="line_number_color" data_type="color" type="Color" default="Color(0.67, 0.67, 0.67, 0.4)">
Sets the [Color] of line numbers.
</theme_item>
- <theme_item name="search_result_border_color" data_type="color" type="Color" default="Color(0.3, 0.3, 0.3, 0.4)">
- [Color] of the border around text that matches the search query.
- </theme_item>
- <theme_item name="search_result_color" data_type="color" type="Color" default="Color(0.3, 0.3, 0.3, 1)">
- [Color] behind the text that matches the search query.
- </theme_item>
- <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.5, 0.5, 0.5, 1)">
- Sets the highlight [Color] of text selections.
- </theme_item>
- <theme_item name="word_highlighted_color" data_type="color" type="Color" default="Color(0.8, 0.9, 0.9, 0.15)">
- Sets the highlight [Color] of multiple occurrences. [member TextEdit.highlight_all_occurrences] has to be enabled.
- </theme_item>
<theme_item name="completion_lines" data_type="constant" type="int" default="7">
Max number of options to display in the code completion popup at any one time.
</theme_item>
@@ -723,19 +681,6 @@
<theme_item name="completion_scroll_width" data_type="constant" type="int" default="6">
Width of the scrollbar in the code completion popup.
</theme_item>
- <theme_item name="line_spacing" data_type="constant" type="int" default="4">
- Sets the spacing between the lines.
- </theme_item>
- <theme_item name="outline_size" data_type="constant" type="int" default="0">
- The size of the text outline.
- [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
- </theme_item>
- <theme_item name="font" data_type="font" type="Font">
- Sets the default [Font].
- </theme_item>
- <theme_item name="font_size" data_type="font_size" type="int">
- Sets default font size.
- </theme_item>
<theme_item name="bookmark" data_type="icon" type="Texture2D">
Sets a custom [Texture2D] to draw in the bookmark gutter for bookmarked lines.
</theme_item>
@@ -760,23 +705,8 @@
<theme_item name="folded_eol_icon" data_type="icon" type="Texture2D">
Sets a custom [Texture2D] to draw at the end of a folded line.
</theme_item>
- <theme_item name="space" data_type="icon" type="Texture2D">
- Sets a custom [Texture2D] for space text characters.
- </theme_item>
- <theme_item name="tab" data_type="icon" type="Texture2D">
- Sets a custom [Texture2D] for tab text characters.
- </theme_item>
<theme_item name="completion" data_type="style" type="StyleBox">
[StyleBox] for the code completion popup.
</theme_item>
- <theme_item name="focus" data_type="style" type="StyleBox">
- Sets the [StyleBox] when in focus. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
- </theme_item>
- <theme_item name="normal" data_type="style" type="StyleBox">
- Sets the [StyleBox].
- </theme_item>
- <theme_item name="read_only" data_type="style" type="StyleBox">
- Sets the [StyleBox] when [member TextEdit.editable] is disabled.
- </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml
index 2f01e791a2..dfa8d7d840 100644
--- a/doc/classes/ColorPicker.xml
+++ b/doc/classes/ColorPicker.xml
@@ -142,7 +142,7 @@
</constants>
<theme_items>
<theme_item name="center_slider_grabbers" data_type="constant" type="int" default="1">
- Overrides the [theme_item HSlider.center_grabber] theme property of the sliders.
+ Overrides the [theme_item Slider.center_grabber] theme property of the sliders.
</theme_item>
<theme_item name="h_width" data_type="constant" type="int" default="30">
The width of the hue selection slider.
diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml
index f6af188998..89cb01407d 100644
--- a/doc/classes/ColorPickerButton.xml
+++ b/doc/classes/ColorPickerButton.xml
@@ -56,54 +56,8 @@
</signal>
</signals>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- Default text [Color] of the [ColorPickerButton].
- </theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.3)">
- Text [Color] used when the [ColorPickerButton] is disabled.
- </theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- Text [Color] used when the [ColorPickerButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
- </theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- Text [Color] used when the [ColorPickerButton] is being hovered.
- </theme_item>
- <theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The tint of text outline of the [ColorPickerButton].
- </theme_item>
- <theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(0.8, 0.8, 0.8, 1)">
- Text [Color] used when the [ColorPickerButton] is being pressed.
- </theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="4">
- The horizontal space between [ColorPickerButton]'s icon and text.
- </theme_item>
- <theme_item name="outline_size" data_type="constant" type="int" default="0">
- The size of the text outline.
- [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
- </theme_item>
- <theme_item name="font" data_type="font" type="Font">
- [Font] of the [ColorPickerButton]'s text.
- </theme_item>
- <theme_item name="font_size" data_type="font_size" type="int">
- Font size of the [ColorPickerButton]'s text.
- </theme_item>
<theme_item name="bg" data_type="icon" type="Texture2D">
The background of the color preview rect on the button.
</theme_item>
- <theme_item name="disabled" data_type="style" type="StyleBox">
- [StyleBox] used when the [ColorPickerButton] is disabled.
- </theme_item>
- <theme_item name="focus" data_type="style" type="StyleBox">
- [StyleBox] used when the [ColorPickerButton] is focused. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
- </theme_item>
- <theme_item name="hover" data_type="style" type="StyleBox">
- [StyleBox] used when the [ColorPickerButton] is being hovered.
- </theme_item>
- <theme_item name="normal" data_type="style" type="StyleBox">
- Default [StyleBox] for the [ColorPickerButton].
- </theme_item>
- <theme_item name="pressed" data_type="style" type="StyleBox">
- [StyleBox] used when the [ColorPickerButton] is being pressed.
- </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml
index 42cb420e74..59c0b7b912 100644
--- a/doc/classes/EditorFileDialog.xml
+++ b/doc/classes/EditorFileDialog.xml
@@ -19,6 +19,14 @@
For example, a [param filter] of [code]"*.tscn, *.scn"[/code] and a [param description] of [code]"Scenes"[/code] results in filter text "Scenes (*.tscn, *.scn)".
</description>
</method>
+ <method name="add_side_menu">
+ <return type="void" />
+ <param index="0" name="menu" type="Control" />
+ <param index="1" name="title" type="String" default="&quot;&quot;" />
+ <description>
+ Adds the given [param menu] to the side of the file dialog with the given [param title] text on top. Only one side menu is allowed.
+ </description>
+ </method>
<method name="clear_filters">
<return type="void" />
<description>
diff --git a/doc/classes/GraphElement.xml b/doc/classes/GraphElement.xml
index 6c3b6b727a..589b553cb6 100644
--- a/doc/classes/GraphElement.xml
+++ b/doc/classes/GraphElement.xml
@@ -66,4 +66,9 @@
</description>
</signal>
</signals>
+ <theme_items>
+ <theme_item name="resizer" data_type="icon" type="Texture2D">
+ The icon used for the resizer, visible when [member resizable] is enabled.
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml
index 5d52ea17e2..9e1392567a 100644
--- a/doc/classes/GraphNode.xml
+++ b/doc/classes/GraphNode.xml
@@ -263,9 +263,6 @@
<theme_item name="port" data_type="icon" type="Texture2D">
The icon used for representing ports.
</theme_item>
- <theme_item name="resizer" data_type="icon" type="Texture2D">
- The icon used for the resizer, visible when [member GraphElement.resizable] is enabled.
- </theme_item>
<theme_item name="panel" data_type="style" type="StyleBox">
The default background for the slot area of the [GraphNode].
</theme_item>
diff --git a/doc/classes/HBoxContainer.xml b/doc/classes/HBoxContainer.xml
index 331c1d23ad..f531f3f910 100644
--- a/doc/classes/HBoxContainer.xml
+++ b/doc/classes/HBoxContainer.xml
@@ -9,9 +9,4 @@
<tutorials>
<link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link>
</tutorials>
- <theme_items>
- <theme_item name="separation" data_type="constant" type="int" default="4">
- The horizontal space between the [HBoxContainer]'s elements.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/HFlowContainer.xml b/doc/classes/HFlowContainer.xml
index 44d1af37cf..36a87376ca 100644
--- a/doc/classes/HFlowContainer.xml
+++ b/doc/classes/HFlowContainer.xml
@@ -9,12 +9,4 @@
<tutorials>
<link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link>
</tutorials>
- <theme_items>
- <theme_item name="h_separation" data_type="constant" type="int" default="4">
- The horizontal separation of children nodes.
- </theme_item>
- <theme_item name="v_separation" data_type="constant" type="int" default="4">
- The vertical separation of children nodes.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/HScrollBar.xml b/doc/classes/HScrollBar.xml
index 307d244a40..84ed0c69ad 100644
--- a/doc/classes/HScrollBar.xml
+++ b/doc/classes/HScrollBar.xml
@@ -8,39 +8,4 @@
</description>
<tutorials>
</tutorials>
- <theme_items>
- <theme_item name="decrement" data_type="icon" type="Texture2D">
- Icon used as a button to scroll the [ScrollBar] left. Supports custom step using the [member ScrollBar.custom_step] property.
- </theme_item>
- <theme_item name="decrement_highlight" data_type="icon" type="Texture2D">
- Displayed when the mouse cursor hovers over the decrement button.
- </theme_item>
- <theme_item name="decrement_pressed" data_type="icon" type="Texture2D">
- Displayed when the decrement button is being pressed.
- </theme_item>
- <theme_item name="increment" data_type="icon" type="Texture2D">
- Icon used as a button to scroll the [ScrollBar] right. Supports custom step using the [member ScrollBar.custom_step] property.
- </theme_item>
- <theme_item name="increment_highlight" data_type="icon" type="Texture2D">
- Displayed when the mouse cursor hovers over the increment button.
- </theme_item>
- <theme_item name="increment_pressed" data_type="icon" type="Texture2D">
- Displayed when the increment button is being pressed.
- </theme_item>
- <theme_item name="grabber" data_type="style" type="StyleBox">
- Used as texture for the grabber, the draggable element representing current scroll.
- </theme_item>
- <theme_item name="grabber_highlight" data_type="style" type="StyleBox">
- Used when the mouse hovers over the grabber.
- </theme_item>
- <theme_item name="grabber_pressed" data_type="style" type="StyleBox">
- Used when the grabber is being dragged.
- </theme_item>
- <theme_item name="scroll" data_type="style" type="StyleBox">
- Used as background of this [ScrollBar].
- </theme_item>
- <theme_item name="scroll_focus" data_type="style" type="StyleBox">
- Used as background when the [ScrollBar] has the GUI focus.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/HSeparator.xml b/doc/classes/HSeparator.xml
index c749eaa10e..ffba571f90 100644
--- a/doc/classes/HSeparator.xml
+++ b/doc/classes/HSeparator.xml
@@ -8,12 +8,4 @@
</description>
<tutorials>
</tutorials>
- <theme_items>
- <theme_item name="separation" data_type="constant" type="int" default="4">
- The height of the area covered by the separator. Effectively works like a minimum height.
- </theme_item>
- <theme_item name="separator" data_type="style" type="StyleBox">
- The style for the separator line. Works best with [StyleBoxLine].
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/HSlider.xml b/doc/classes/HSlider.xml
index 352ab3773e..40ec408f9b 100644
--- a/doc/classes/HSlider.xml
+++ b/doc/classes/HSlider.xml
@@ -8,33 +8,4 @@
</description>
<tutorials>
</tutorials>
- <theme_items>
- <theme_item name="center_grabber" data_type="constant" type="int" default="0">
- Boolean constant. If [code]1[/code], the grabber texture size will be ignored and it will fit within slider's bounds based only on its center position.
- </theme_item>
- <theme_item name="grabber_offset" data_type="constant" type="int" default="0">
- Vertical offset of the grabber.
- </theme_item>
- <theme_item name="grabber" data_type="icon" type="Texture2D">
- The texture for the grabber (the draggable element).
- </theme_item>
- <theme_item name="grabber_disabled" data_type="icon" type="Texture2D">
- The texture for the grabber when it's disabled.
- </theme_item>
- <theme_item name="grabber_highlight" data_type="icon" type="Texture2D">
- The texture for the grabber when it's focused.
- </theme_item>
- <theme_item name="tick" data_type="icon" type="Texture2D">
- The texture for the ticks, visible when [member Slider.tick_count] is greater than 0.
- </theme_item>
- <theme_item name="grabber_area" data_type="style" type="StyleBox">
- The background of the area to the left of the grabber.
- </theme_item>
- <theme_item name="grabber_area_highlight" data_type="style" type="StyleBox">
- The background of the area to the left of the grabber that displays when it's being hovered or focused.
- </theme_item>
- <theme_item name="slider" data_type="style" type="StyleBox">
- The background for the whole slider. Determines the height of the [code]grabber_area[/code].
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/HSplitContainer.xml b/doc/classes/HSplitContainer.xml
index 7cc6d94af3..1d8dbcb17c 100644
--- a/doc/classes/HSplitContainer.xml
+++ b/doc/classes/HSplitContainer.xml
@@ -9,18 +9,4 @@
<tutorials>
<link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link>
</tutorials>
- <theme_items>
- <theme_item name="autohide" data_type="constant" type="int" default="1">
- Boolean value. If 1 ([code]true[/code]), the grabber will hide automatically when it isn't under the cursor. If 0 ([code]false[/code]), it's always visible.
- </theme_item>
- <theme_item name="minimum_grab_thickness" data_type="constant" type="int" default="6">
- The minimum thickness of the area users can click on to grab the splitting line. If [theme_item separation] or [theme_item grabber]'s thickness are too small, this ensure that the splitting line can still be dragged.
- </theme_item>
- <theme_item name="separation" data_type="constant" type="int" default="12">
- The space between sides of the container.
- </theme_item>
- <theme_item name="grabber" data_type="icon" type="Texture2D">
- The icon used for the grabber drawn in the middle area.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml
index 010760cae0..4f38b4d336 100644
--- a/doc/classes/LinkButton.xml
+++ b/doc/classes/LinkButton.xml
@@ -62,12 +62,18 @@
<theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Default text [Color] of the [LinkButton].
</theme_item>
+ <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ Text [Color] used when the [LinkButton] is disabled.
+ </theme_item>
<theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [LinkButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
</theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [LinkButton] is being hovered.
</theme_item>
+ <theme_item name="font_hover_pressed_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ Text [Color] used when the [LinkButton] is being hovered and pressed.
+ </theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [LinkButton].
</theme_item>
diff --git a/doc/classes/MenuBar.xml b/doc/classes/MenuBar.xml
index 7d1b73278e..07f64d0572 100644
--- a/doc/classes/MenuBar.xml
+++ b/doc/classes/MenuBar.xml
@@ -154,17 +154,32 @@
<theme_item name="disabled" data_type="style" type="StyleBox">
[StyleBox] used when the menu item is disabled.
</theme_item>
- <theme_item name="focus" data_type="style" type="StyleBox">
- [StyleBox] used when the menu item is focused. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
+ <theme_item name="disabled_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the menu item is disabled (for right-to-left layouts).
</theme_item>
<theme_item name="hover" data_type="style" type="StyleBox">
[StyleBox] used when the menu item is being hovered.
</theme_item>
+ <theme_item name="hover_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the menu item is being hovered (for right-to-left layouts).
+ </theme_item>
+ <theme_item name="hover_pressed" data_type="style" type="StyleBox">
+ [StyleBox] used when the menu item is being pressed and hovered at the same time.
+ </theme_item>
+ <theme_item name="hover_pressed_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the menu item is being pressed and hovered at the same time (for right-to-left layouts).
+ </theme_item>
<theme_item name="normal" data_type="style" type="StyleBox">
Default [StyleBox] for the menu item.
</theme_item>
+ <theme_item name="normal_mirrored" data_type="style" type="StyleBox">
+ Default [StyleBox] for the menu item (for right-to-left layouts).
+ </theme_item>
<theme_item name="pressed" data_type="style" type="StyleBox">
[StyleBox] used when the menu item is being pressed.
</theme_item>
+ <theme_item name="pressed_mirrored" data_type="style" type="StyleBox">
+ [StyleBox] used when the menu item is being pressed (for right-to-left layouts).
+ </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml
index e3a707ba72..6aa17c1e16 100644
--- a/doc/classes/MenuButton.xml
+++ b/doc/classes/MenuButton.xml
@@ -50,52 +50,4 @@
</description>
</signal>
</signals>
- <theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
- Default text [Color] of the [MenuButton].
- </theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.3)">
- Text [Color] used when the [MenuButton] is disabled.
- </theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- Text [Color] used when the [MenuButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
- </theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- Text [Color] used when the [MenuButton] is being hovered.
- </theme_item>
- <theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The tint of text outline of the [MenuButton].
- </theme_item>
- <theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- Text [Color] used when the [MenuButton] is being pressed.
- </theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="4">
- The horizontal space between [MenuButton]'s icon and text. Negative values will be treated as [code]0[/code] when used.
- </theme_item>
- <theme_item name="outline_size" data_type="constant" type="int" default="0">
- The size of the text outline.
- [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
- </theme_item>
- <theme_item name="font" data_type="font" type="Font">
- [Font] of the [MenuButton]'s text.
- </theme_item>
- <theme_item name="font_size" data_type="font_size" type="int">
- Font size of the [MenuButton]'s text.
- </theme_item>
- <theme_item name="disabled" data_type="style" type="StyleBox">
- [StyleBox] used when the [MenuButton] is disabled.
- </theme_item>
- <theme_item name="focus" data_type="style" type="StyleBox">
- [StyleBox] used when the [MenuButton] is focused. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
- </theme_item>
- <theme_item name="hover" data_type="style" type="StyleBox">
- [StyleBox] used when the [MenuButton] is being hovered.
- </theme_item>
- <theme_item name="normal" data_type="style" type="StyleBox">
- Default [StyleBox] for the [MenuButton].
- </theme_item>
- <theme_item name="pressed" data_type="style" type="StyleBox">
- [StyleBox] used when the [MenuButton] is being pressed.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index 47a68802aa..4e223c12fa 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -243,75 +243,14 @@
</signal>
</signals>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
- Default text [Color] of the [OptionButton].
- </theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
- Text [Color] used when the [OptionButton] is disabled.
- </theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- Text [Color] used when the [OptionButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
- </theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
- Text [Color] used when the [OptionButton] is being hovered.
- </theme_item>
- <theme_item name="font_hover_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- Text [Color] used when the [OptionButton] is being hovered and pressed.
- </theme_item>
- <theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- The tint of text outline of the [OptionButton].
- </theme_item>
- <theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
- Text [Color] used when the [OptionButton] is being pressed.
- </theme_item>
<theme_item name="arrow_margin" data_type="constant" type="int" default="4">
The horizontal space between the arrow icon and the right edge of the button.
</theme_item>
- <theme_item name="h_separation" data_type="constant" type="int" default="4">
- The horizontal space between [OptionButton]'s icon and text. Negative values will be treated as [code]0[/code] when used.
- </theme_item>
<theme_item name="modulate_arrow" data_type="constant" type="int" default="0">
If different than [code]0[/code], the arrow icon will be modulated to the font color.
</theme_item>
- <theme_item name="outline_size" data_type="constant" type="int" default="0">
- The size of the text outline.
- [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
- </theme_item>
- <theme_item name="font" data_type="font" type="Font">
- [Font] of the [OptionButton]'s text.
- </theme_item>
- <theme_item name="font_size" data_type="font_size" type="int">
- Font size of the [OptionButton]'s text.
- </theme_item>
<theme_item name="arrow" data_type="icon" type="Texture2D">
The arrow icon to be drawn on the right end of the button.
</theme_item>
- <theme_item name="disabled" data_type="style" type="StyleBox">
- [StyleBox] used when the [OptionButton] is disabled (for left-to-right layouts).
- </theme_item>
- <theme_item name="disabled_mirrored" data_type="style" type="StyleBox">
- [StyleBox] used when the [OptionButton] is disabled (for right-to-left layouts).
- </theme_item>
- <theme_item name="focus" data_type="style" type="StyleBox">
- [StyleBox] used when the [OptionButton] is focused. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
- </theme_item>
- <theme_item name="hover" data_type="style" type="StyleBox">
- [StyleBox] used when the [OptionButton] is being hovered (for left-to-right layouts).
- </theme_item>
- <theme_item name="hover_mirrored" data_type="style" type="StyleBox">
- [StyleBox] used when the [OptionButton] is being hovered (for right-to-left layouts).
- </theme_item>
- <theme_item name="normal" data_type="style" type="StyleBox">
- Default [StyleBox] for the [OptionButton] (for left-to-right layouts).
- </theme_item>
- <theme_item name="normal_mirrored" data_type="style" type="StyleBox">
- Default [StyleBox] for the [OptionButton] (for right-to-left layouts).
- </theme_item>
- <theme_item name="pressed" data_type="style" type="StyleBox">
- [StyleBox] used when the [OptionButton] is being pressed (for left-to-right layouts).
- </theme_item>
- <theme_item name="pressed_mirrored" data_type="style" type="StyleBox">
- [StyleBox] used when the [OptionButton] is being pressed (for right-to-left layouts).
- </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/Popup.xml b/doc/classes/Popup.xml
index 29b44a98f2..c435f3d291 100644
--- a/doc/classes/Popup.xml
+++ b/doc/classes/Popup.xml
@@ -23,4 +23,9 @@
</description>
</signal>
</signals>
+ <theme_items>
+ <theme_item name="panel" data_type="style" type="StyleBox">
+ Default [StyleBox] for the [Popup].
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index d1c1304509..6293fcb309 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -691,12 +691,6 @@
<theme_item name="labeled_separator_right" data_type="style" type="StyleBox">
[StyleBox] for the right side of labeled separator. See [method add_separator].
</theme_item>
- <theme_item name="panel" data_type="style" type="StyleBox">
- Default [StyleBox] of the [PopupMenu] items.
- </theme_item>
- <theme_item name="panel_disabled" data_type="style" type="StyleBox">
- [StyleBox] used when the [PopupMenu] item is disabled.
- </theme_item>
<theme_item name="separator" data_type="style" type="StyleBox">
[StyleBox] used for the separators. See [method add_separator].
</theme_item>
diff --git a/doc/classes/PopupPanel.xml b/doc/classes/PopupPanel.xml
index ad981116de..b86972e8af 100644
--- a/doc/classes/PopupPanel.xml
+++ b/doc/classes/PopupPanel.xml
@@ -8,9 +8,4 @@
</description>
<tutorials>
</tutorials>
- <theme_items>
- <theme_item name="panel" data_type="style" type="StyleBox">
- The background panel style of this [PopupPanel].
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml
index 595749f4b6..1102f0369a 100644
--- a/doc/classes/ProgressBar.xml
+++ b/doc/classes/ProgressBar.xml
@@ -37,9 +37,6 @@
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [ProgressBar].
</theme_item>
- <theme_item name="font_shadow_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
- The color of the text's shadow.
- </theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0">
The size of the text outline.
[b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml
index be04ebd893..75c258253d 100644
--- a/doc/classes/Resource.xml
+++ b/doc/classes/Resource.xml
@@ -19,6 +19,21 @@
Override this method to return a custom [RID] when [method get_rid] is called.
</description>
</method>
+ <method name="_setup_local_to_scene" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Override this method to customize the newly duplicated resource created from [method PackedScene.instantiate], if the original's [member resource_local_to_scene] is set to [code]true[/code].
+ [b]Example:[/b] Set a random [code]damage[/code] value to every local resource from an instantiated scene.
+ [codeblock]
+ extends Resource
+
+ var damage = 0
+
+ func _setup_local_to_scene():
+ damage = randi_range(10, 40)
+ [/codeblock]
+ </description>
+ </method>
<method name="duplicate" qualifiers="const">
<return type="Resource" />
<param index="0" name="subresources" type="bool" default="false" />
@@ -58,8 +73,8 @@
<method name="setup_local_to_scene" is_deprecated="true">
<return type="void" />
<description>
- Emits the [signal setup_local_to_scene_requested] signal. If [member resource_local_to_scene] is set to [code]true[/code], this method is called from [method PackedScene.instantiate] by the newly duplicated resource within the scene instance.
- For most resources, this method performs no logic of its own. Custom behavior can be defined by connecting [signal setup_local_to_scene_requested] from a script, [b]not[/b] by overriding this method.
+ Calls [method _setup_local_to_scene]. If [member resource_local_to_scene] is set to [code]true[/code], this method is automatically called from [method PackedScene.instantiate] by the newly duplicated resource within the scene instance.
+ [i]Deprecated.[/i] This method should only be called internally. Override [method _setup_local_to_scene] instead.
</description>
</method>
<method name="take_over_path">
@@ -90,9 +105,10 @@
[b]Note:[/b] This signal is not emitted automatically for properties of custom resources. If necessary, a setter needs to be created to emit the signal.
</description>
</signal>
- <signal name="setup_local_to_scene_requested">
+ <signal name="setup_local_to_scene_requested" is_deprecated="true">
<description>
- Emitted by the newly duplicated resource with [member resource_local_to_scene] set to [code]true[/code], when the scene is instantiated. Custom behavior can be defined by connecting this signal.
+ Emitted by a newly duplicated resource with [member resource_local_to_scene] set to [code]true[/code].
+ [i]Deprecated.[/i] This signal is only emitted when the resource is created. Override [method _setup_local_to_scene] instead.
</description>
</signal>
</signals>
diff --git a/doc/classes/ScrollBar.xml b/doc/classes/ScrollBar.xml
index 0cbb60198b..e8d2753a9a 100644
--- a/doc/classes/ScrollBar.xml
+++ b/doc/classes/ScrollBar.xml
@@ -21,4 +21,41 @@
</description>
</signal>
</signals>
+ <theme_items>
+ <theme_item name="decrement" data_type="icon" type="Texture2D">
+ Icon used as a button to scroll the [ScrollBar] left/up. Supports custom step using the [member ScrollBar.custom_step] property.
+ </theme_item>
+ <theme_item name="decrement_highlight" data_type="icon" type="Texture2D">
+ Displayed when the mouse cursor hovers over the decrement button.
+ </theme_item>
+ <theme_item name="decrement_pressed" data_type="icon" type="Texture2D">
+ Displayed when the decrement button is being pressed.
+ </theme_item>
+ <theme_item name="increment" data_type="icon" type="Texture2D">
+ Icon used as a button to scroll the [ScrollBar] right/down. Supports custom step using the [member ScrollBar.custom_step] property.
+ </theme_item>
+ <theme_item name="increment_highlight" data_type="icon" type="Texture2D">
+ Displayed when the mouse cursor hovers over the increment button.
+ </theme_item>
+ <theme_item name="increment_pressed" data_type="icon" type="Texture2D">
+ Displayed when the increment button is being pressed.
+ </theme_item>
+ <theme_item name="grabber" data_type="style" type="StyleBox">
+ Used as texture for the grabber, the draggable element representing current scroll.
+ </theme_item>
+ <theme_item name="grabber_highlight" data_type="style" type="StyleBox">
+ Used when the mouse hovers over the grabber.
+ </theme_item>
+ <theme_item name="grabber_pressed" data_type="style" type="StyleBox">
+ Used when the grabber is being dragged.
+ </theme_item>
+ <theme_item name="hscroll" data_type="style" type="StyleBox">
+ </theme_item>
+ <theme_item name="scroll" data_type="style" type="StyleBox">
+ Used as background of this [ScrollBar].
+ </theme_item>
+ <theme_item name="scroll_focus" data_type="style" type="StyleBox">
+ Used as background when the [ScrollBar] has the GUI focus.
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/Separator.xml b/doc/classes/Separator.xml
index fe6f6858b7..d0535d450f 100644
--- a/doc/classes/Separator.xml
+++ b/doc/classes/Separator.xml
@@ -8,4 +8,12 @@
</description>
<tutorials>
</tutorials>
+ <theme_items>
+ <theme_item name="separation" data_type="constant" type="int" default="0">
+ The size of the area covered by the separator. Effectively works like a minimum width/height.
+ </theme_item>
+ <theme_item name="separator" data_type="style" type="StyleBox">
+ The style for the separator line. Works best with [StyleBoxLine] (remember to enable [member StyleBoxLine.vertical] for [VSeparator]).
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml
index b946b6cedb..efb646b7ae 100644
--- a/doc/classes/Slider.xml
+++ b/doc/classes/Slider.xml
@@ -37,4 +37,33 @@
</description>
</signal>
</signals>
+ <theme_items>
+ <theme_item name="center_grabber" data_type="constant" type="int" default="0">
+ Boolean constant. If [code]1[/code], the grabber texture size will be ignored and it will fit within slider's bounds based only on its center position.
+ </theme_item>
+ <theme_item name="grabber_offset" data_type="constant" type="int" default="0">
+ Vertical/horizontal offset of the grabber.
+ </theme_item>
+ <theme_item name="grabber" data_type="icon" type="Texture2D">
+ The texture for the grabber (the draggable element).
+ </theme_item>
+ <theme_item name="grabber_disabled" data_type="icon" type="Texture2D">
+ The texture for the grabber when it's disabled.
+ </theme_item>
+ <theme_item name="grabber_highlight" data_type="icon" type="Texture2D">
+ The texture for the grabber when it's focused.
+ </theme_item>
+ <theme_item name="tick" data_type="icon" type="Texture2D">
+ The texture for the ticks, visible when [member Slider.tick_count] is greater than 0.
+ </theme_item>
+ <theme_item name="grabber_area" data_type="style" type="StyleBox">
+ The background of the area to the left/bottom of the grabber.
+ </theme_item>
+ <theme_item name="grabber_area_highlight" data_type="style" type="StyleBox">
+ The background of the area to the left/bottom of the grabber that displays when it's being hovered or focused.
+ </theme_item>
+ <theme_item name="slider" data_type="style" type="StyleBox">
+ The background for the whole slider. Determines the height/width of the [code]grabber_area[/code].
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/SplitContainer.xml b/doc/classes/SplitContainer.xml
index 5078685cce..454a542cc8 100644
--- a/doc/classes/SplitContainer.xml
+++ b/doc/classes/SplitContainer.xml
@@ -61,6 +61,9 @@
<theme_item name="separation" data_type="constant" type="int" default="12">
The space between sides of the container.
</theme_item>
+ <theme_item name="grabber" data_type="icon" type="Texture2D">
+ The icon used for the grabber drawn in the middle area.
+ </theme_item>
<theme_item name="h_grabber" data_type="icon" type="Texture2D">
The icon used for the grabber drawn in the middle area when [member vertical] is [code]false[/code].
</theme_item>
diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml
index bfcb1a0e69..9b462b9f50 100644
--- a/doc/classes/TabBar.xml
+++ b/doc/classes/TabBar.xml
@@ -414,6 +414,7 @@
</theme_item>
<theme_item name="tab_hovered" data_type="style" type="StyleBox">
The style of the currently hovered tab. Does not apply to the selected tab.
+ [b]Note:[/b] This style will be drawn with the same width as [theme_item tab_unselected] at minimum.
</theme_item>
<theme_item name="tab_selected" data_type="style" type="StyleBox">
The style of the currently selected tab.
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index 07f6dbd8c5..b08e075a23 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -318,6 +318,7 @@
</theme_item>
<theme_item name="tab_hovered" data_type="style" type="StyleBox">
The style of the currently hovered tab.
+ [b]Note:[/b] This style will be drawn with the same width as [theme_item tab_unselected] at minimum.
</theme_item>
<theme_item name="tab_selected" data_type="style" type="StyleBox">
The style of the currently selected tab.
diff --git a/doc/classes/VBoxContainer.xml b/doc/classes/VBoxContainer.xml
index 38541859a6..d3ea94c0eb 100644
--- a/doc/classes/VBoxContainer.xml
+++ b/doc/classes/VBoxContainer.xml
@@ -10,9 +10,4 @@
<link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link>
<link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link>
</tutorials>
- <theme_items>
- <theme_item name="separation" data_type="constant" type="int" default="4">
- The vertical space between the [VBoxContainer]'s elements.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/VFlowContainer.xml b/doc/classes/VFlowContainer.xml
index dcec1a58d9..5c896da729 100644
--- a/doc/classes/VFlowContainer.xml
+++ b/doc/classes/VFlowContainer.xml
@@ -9,12 +9,4 @@
<tutorials>
<link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link>
</tutorials>
- <theme_items>
- <theme_item name="h_separation" data_type="constant" type="int" default="4">
- The horizontal separation of children nodes.
- </theme_item>
- <theme_item name="v_separation" data_type="constant" type="int" default="4">
- The vertical separation of children nodes.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/VScrollBar.xml b/doc/classes/VScrollBar.xml
index 7cb1a3d16d..d6a69a6bba 100644
--- a/doc/classes/VScrollBar.xml
+++ b/doc/classes/VScrollBar.xml
@@ -12,39 +12,4 @@
<member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="0" />
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="1" />
</members>
- <theme_items>
- <theme_item name="decrement" data_type="icon" type="Texture2D">
- Icon used as a button to scroll the [ScrollBar] up. Supports custom step using the [member ScrollBar.custom_step] property.
- </theme_item>
- <theme_item name="decrement_highlight" data_type="icon" type="Texture2D">
- Displayed when the mouse cursor hovers over the decrement button.
- </theme_item>
- <theme_item name="decrement_pressed" data_type="icon" type="Texture2D">
- Displayed when the decrement button is being pressed.
- </theme_item>
- <theme_item name="increment" data_type="icon" type="Texture2D">
- Icon used as a button to scroll the [ScrollBar] down. Supports custom step using the [member ScrollBar.custom_step] property.
- </theme_item>
- <theme_item name="increment_highlight" data_type="icon" type="Texture2D">
- Displayed when the mouse cursor hovers over the increment button.
- </theme_item>
- <theme_item name="increment_pressed" data_type="icon" type="Texture2D">
- Displayed when the increment button is being pressed.
- </theme_item>
- <theme_item name="grabber" data_type="style" type="StyleBox">
- Used as texture for the grabber, the draggable element representing current scroll.
- </theme_item>
- <theme_item name="grabber_highlight" data_type="style" type="StyleBox">
- Used when the mouse hovers over the grabber.
- </theme_item>
- <theme_item name="grabber_pressed" data_type="style" type="StyleBox">
- Used when the grabber is being dragged.
- </theme_item>
- <theme_item name="scroll" data_type="style" type="StyleBox">
- Used as background of this [ScrollBar].
- </theme_item>
- <theme_item name="scroll_focus" data_type="style" type="StyleBox">
- Used as background when the [ScrollBar] has the GUI focus.
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/VSeparator.xml b/doc/classes/VSeparator.xml
index ffb4e76df8..57d1c9e704 100644
--- a/doc/classes/VSeparator.xml
+++ b/doc/classes/VSeparator.xml
@@ -8,12 +8,4 @@
</description>
<tutorials>
</tutorials>
- <theme_items>
- <theme_item name="separation" data_type="constant" type="int" default="4">
- The width of the area covered by the separator. Effectively works like a minimum width.
- </theme_item>
- <theme_item name="separator" data_type="style" type="StyleBox">
- The style for the separator line. Works best with [StyleBoxLine] (remember to enable [member StyleBoxLine.vertical]).
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/VSlider.xml b/doc/classes/VSlider.xml
index 7475e5ff7e..eea5ba5060 100644
--- a/doc/classes/VSlider.xml
+++ b/doc/classes/VSlider.xml
@@ -12,33 +12,4 @@
<member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="0" />
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="1" />
</members>
- <theme_items>
- <theme_item name="center_grabber" data_type="constant" type="int" default="0">
- Boolean constant. If [code]1[/code], the grabber texture size will be ignored and it will fit within slider's bounds based only on its center position.
- </theme_item>
- <theme_item name="grabber_offset" data_type="constant" type="int" default="0">
- Horizontal offset of the grabber.
- </theme_item>
- <theme_item name="grabber" data_type="icon" type="Texture2D">
- The texture for the grabber (the draggable element).
- </theme_item>
- <theme_item name="grabber_disabled" data_type="icon" type="Texture2D">
- The texture for the grabber when it's disabled.
- </theme_item>
- <theme_item name="grabber_highlight" data_type="icon" type="Texture2D">
- The texture for the grabber when it's focused.
- </theme_item>
- <theme_item name="tick" data_type="icon" type="Texture2D">
- The texture for the ticks, visible when [member Slider.tick_count] is greater than 0.
- </theme_item>
- <theme_item name="grabber_area" data_type="style" type="StyleBox">
- The background of the area below the grabber.
- </theme_item>
- <theme_item name="grabber_area_highlight" data_type="style" type="StyleBox">
- The background of the area below the grabber that displays when it's being hovered or focused.
- </theme_item>
- <theme_item name="slider" data_type="style" type="StyleBox">
- The background for the whole slider. Determines the width of the [code]grabber_area[/code].
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/VSplitContainer.xml b/doc/classes/VSplitContainer.xml
index 1e363d987c..5932ca7369 100644
--- a/doc/classes/VSplitContainer.xml
+++ b/doc/classes/VSplitContainer.xml
@@ -9,18 +9,4 @@
<tutorials>
<link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link>
</tutorials>
- <theme_items>
- <theme_item name="autohide" data_type="constant" type="int" default="1">
- Boolean value. If 1 ([code]true[/code]), the grabber will hide automatically when it isn't under the cursor. If 0 ([code]false[/code]), it's always visible.
- </theme_item>
- <theme_item name="minimum_grab_thickness" data_type="constant" type="int" default="6">
- The minimum thickness of the area users can click on to grab the splitting line. If [theme_item separation] or [theme_item grabber]'s thickness are too small, this ensure that the splitting line can still be dragged.
- </theme_item>
- <theme_item name="separation" data_type="constant" type="int" default="12">
- The space between sides of the container.
- </theme_item>
- <theme_item name="grabber" data_type="icon" type="Texture2D">
- The icon used for the grabber drawn in the middle area.
- </theme_item>
- </theme_items>
</class>
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index e0b344fc19..ef3981fd3f 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -624,66 +624,47 @@ void DocTools::generate(bool p_basic_types) {
// Theme items.
{
- List<StringName> l;
+ List<ThemeDB::ThemeItemBind> theme_items;
+ ThemeDB::get_singleton()->get_class_own_items(cname, &theme_items);
+ Ref<Theme> default_theme = ThemeDB::get_singleton()->get_default_theme();
- ThemeDB::get_singleton()->get_default_theme()->get_color_list(cname, &l);
- for (const StringName &E : l) {
+ for (const ThemeDB::ThemeItemBind &theme_item : theme_items) {
DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "Color";
- tid.data_type = "color";
- tid.default_value = DocData::get_default_value_string(ThemeDB::get_singleton()->get_default_theme()->get_color(E, cname));
- c.theme_properties.push_back(tid);
- }
-
- l.clear();
- ThemeDB::get_singleton()->get_default_theme()->get_constant_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "int";
- tid.data_type = "constant";
- tid.default_value = itos(ThemeDB::get_singleton()->get_default_theme()->get_constant(E, cname));
- c.theme_properties.push_back(tid);
- }
-
- l.clear();
- ThemeDB::get_singleton()->get_default_theme()->get_font_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "Font";
- tid.data_type = "font";
- c.theme_properties.push_back(tid);
- }
-
- l.clear();
- ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "int";
- tid.data_type = "font_size";
- c.theme_properties.push_back(tid);
- }
+ tid.name = theme_item.item_name;
+
+ switch (theme_item.data_type) {
+ case Theme::DATA_TYPE_COLOR:
+ tid.type = "Color";
+ tid.data_type = "color";
+ break;
+ case Theme::DATA_TYPE_CONSTANT:
+ tid.type = "int";
+ tid.data_type = "constant";
+ break;
+ case Theme::DATA_TYPE_FONT:
+ tid.type = "Font";
+ tid.data_type = "font";
+ break;
+ case Theme::DATA_TYPE_FONT_SIZE:
+ tid.type = "int";
+ tid.data_type = "font_size";
+ break;
+ case Theme::DATA_TYPE_ICON:
+ tid.type = "Texture2D";
+ tid.data_type = "icon";
+ break;
+ case Theme::DATA_TYPE_STYLEBOX:
+ tid.type = "StyleBox";
+ tid.data_type = "style";
+ break;
+ case Theme::DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
- l.clear();
- ThemeDB::get_singleton()->get_default_theme()->get_icon_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "Texture2D";
- tid.data_type = "icon";
- c.theme_properties.push_back(tid);
- }
+ if (theme_item.data_type == Theme::DATA_TYPE_COLOR || theme_item.data_type == Theme::DATA_TYPE_CONSTANT) {
+ tid.default_value = DocData::get_default_value_string(default_theme->get_theme_item(theme_item.data_type, theme_item.item_name, cname));
+ }
- l.clear();
- ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "StyleBox";
- tid.data_type = "style";
c.theme_properties.push_back(tid);
}
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 5f25dde9d1..8aa58a5d7f 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1130,7 +1130,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("normal", "MenuBar", style_widget);
theme->set_stylebox("hover", "MenuBar", style_widget_hover);
theme->set_stylebox("pressed", "MenuBar", style_widget_pressed);
- theme->set_stylebox("focus", "MenuBar", style_widget_focus);
theme->set_stylebox("disabled", "MenuBar", style_widget_disabled);
theme->set_color("font_color", "MenuBar", font_color);
@@ -2330,7 +2329,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("completion_existing_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_existing_color"));
theme->set_color("completion_scroll_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_scroll_color"));
theme->set_color("completion_scroll_hovered_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_scroll_hovered_color"));
- theme->set_color("completion_font_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_font_color"));
theme->set_color("font_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/text_color"));
theme->set_color("line_number_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/line_number_color"));
theme->set_color("caret_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/caret_color"));
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 8fb0c14e80..14d57cbd10 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -1619,6 +1619,7 @@ void EditorFileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_thumbnail_result"), &EditorFileDialog::_thumbnail_result);
ClassDB::bind_method(D_METHOD("set_disable_overwrite_warning", "disable"), &EditorFileDialog::set_disable_overwrite_warning);
ClassDB::bind_method(D_METHOD("is_overwrite_warning_disabled"), &EditorFileDialog::is_overwrite_warning_disabled);
+ ClassDB::bind_method(D_METHOD("add_side_menu", "menu", "title"), &EditorFileDialog::add_side_menu, DEFVAL(""));
ClassDB::bind_method(D_METHOD("invalidate"), &EditorFileDialog::invalidate);
@@ -1712,6 +1713,25 @@ bool EditorFileDialog::are_previews_enabled() {
return previews_enabled;
}
+void EditorFileDialog::add_side_menu(Control *p_menu, const String &p_title) {
+ // HSplitContainer has 3 children at maximum capacity, 1 of them is the SplitContainerDragger.
+ ERR_FAIL_COND_MSG(body_hsplit->get_child_count() > 2, "EditorFileDialog: Only one side menu can be added.");
+ // Everything for the side menu goes inside of a VBoxContainer.
+ VBoxContainer *side_vbox = memnew(VBoxContainer);
+ side_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ side_vbox->set_stretch_ratio(0.5);
+ body_hsplit->add_child(side_vbox);
+ // Add a Label to the VBoxContainer.
+ if (!p_title.is_empty()) {
+ Label *title_label = memnew(Label(p_title));
+ title_label->set_theme_type_variation("HeaderSmall");
+ side_vbox->add_child(title_label);
+ }
+ // Add the given menu to the VBoxContainer.
+ p_menu->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ side_vbox->add_child(p_menu);
+}
+
EditorFileDialog::EditorFileDialog() {
show_hidden_files = default_show_hidden_files;
display_mode = default_display_mode;
@@ -1739,6 +1759,7 @@ EditorFileDialog::EditorFileDialog() {
}
HBoxContainer *pathhb = memnew(HBoxContainer);
+ vbc->add_child(pathhb);
dir_prev = memnew(Button);
dir_prev->set_theme_type_variation("FlatButton");
@@ -1826,11 +1847,13 @@ EditorFileDialog::EditorFileDialog() {
makedir->connect("pressed", callable_mp(this, &EditorFileDialog::_make_dir));
pathhb->add_child(makedir);
- list_hb = memnew(HSplitContainer);
+ body_hsplit = memnew(HSplitContainer);
+ body_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ vbc->add_child(body_hsplit);
- vbc->add_child(pathhb);
- vbc->add_child(list_hb);
- list_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ list_hb = memnew(HSplitContainer);
+ list_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ body_hsplit->add_child(list_hb);
VSplitContainer *vsc = memnew(VSplitContainer);
list_hb->add_child(vsc);
diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h
index 923c7080c5..913b48a10b 100644
--- a/editor/gui/editor_file_dialog.h
+++ b/editor/gui/editor_file_dialog.h
@@ -103,6 +103,7 @@ private:
PopupMenu *item_menu = nullptr;
TextureRect *preview = nullptr;
VBoxContainer *preview_vb = nullptr;
+ HSplitContainer *body_hsplit = nullptr;
HSplitContainer *list_hb = nullptr;
HBoxContainer *file_box = nullptr;
LineEdit *file = nullptr;
@@ -282,6 +283,8 @@ public:
void set_previews_enabled(bool p_enabled);
bool are_previews_enabled();
+ void add_side_menu(Control *p_menu, const String &p_title = "");
+
EditorFileDialog();
~EditorFileDialog();
};
diff --git a/editor/icons/FileTree.svg b/editor/icons/FileTree.svg
new file mode 100644
index 0000000000..995715c993
--- /dev/null
+++ b/editor/icons/FileTree.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2v2h2v5h2v5h8v-2h-6v-3h6v-2h-8v-3h8v-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
index c4e1261a9c..2ef5e89bf7 100644
--- a/editor/import/scene_import_settings.cpp
+++ b/editor/import/scene_import_settings.cpp
@@ -1556,6 +1556,7 @@ SceneImportSettings::SceneImportSettings() {
Ref<StandardMaterial3D> selection_mat;
selection_mat.instantiate();
selection_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ selection_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
selection_mat->set_albedo(Color(1, 0.8, 1.0));
Ref<SurfaceTool> st;
@@ -1597,6 +1598,7 @@ SceneImportSettings::SceneImportSettings() {
{
collider_mat.instantiate();
collider_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ collider_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
collider_mat->set_albedo(Color(0.5, 0.5, 1.0));
}
diff --git a/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp b/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp
index b860f5c0ee..74e6e818c6 100644
--- a/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp
@@ -47,6 +47,7 @@ LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() {
mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, false);
+ mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
add_material("lightmap_probe_material", mat);
diff --git a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp
index fa2c95d8db..39ae020d53 100644
--- a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp
@@ -85,6 +85,7 @@ Marker3DGizmoPlugin::Marker3DGizmoPlugin() {
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
Array d;
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
index 4f5bc67c1b..9747ef4d48 100644
--- a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
@@ -546,6 +546,7 @@ NavigationObstacle3DEditor::NavigationObstacle3DEditor() {
line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
line_material->set_albedo(Color(1, 1, 1));
handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
@@ -554,6 +555,7 @@ NavigationObstacle3DEditor::NavigationObstacle3DEditor() {
handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));
handle_material->set_point_size(handle->get_width());
handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 0d3000a318..3bd786c04f 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -876,6 +876,7 @@ void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (p_use_vertex_color) {
material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
@@ -918,6 +919,7 @@ void EditorNode3DGizmoPlugin::create_icon_material(const String &p_name, const R
icon->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
icon->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
icon->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ icon->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
icon->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
icon->set_depth_draw_mode(StandardMaterial3D::DEPTH_DRAW_DISABLED);
icon->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
@@ -947,6 +949,7 @@ void EditorNode3DGizmoPlugin::create_handle_material(const String &p_name, bool
handle_material->set_albedo(Color(1, 1, 1));
handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
handle_material->set_on_top_of_alpha();
if (p_billboard) {
handle_material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 2eae48da97..d94ca9414a 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -5832,6 +5832,7 @@ void Node3DEditor::_generate_selection_boxes() {
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
const Color selection_box_color = EDITOR_GET("editors/3d/selection_box_color");
mat->set_albedo(selection_box_color);
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
@@ -5840,6 +5841,7 @@ void Node3DEditor::_generate_selection_boxes() {
Ref<StandardMaterial3D> mat_xray = memnew(StandardMaterial3D);
mat_xray->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ mat_xray->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
mat_xray->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
mat_xray->set_albedo(selection_box_color * Color(1, 1, 1, 0.15));
mat_xray->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
@@ -6484,6 +6486,7 @@ void Node3DEditor::_init_indicators() {
indicator_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
indicator_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
indicator_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ indicator_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
indicator_mat->set_transparency(StandardMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
Vector<Color> origin_colors;
@@ -6541,7 +6544,7 @@ void Node3DEditor::_init_indicators() {
shader_type spatial;
-render_mode unshaded;
+render_mode unshaded, fog_disabled;
uniform bool orthogonal;
uniform float grid_size;
@@ -6638,6 +6641,7 @@ void fragment() {
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
mat->set_on_top_of_alpha();
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
mat->set_albedo(col);
@@ -6722,6 +6726,7 @@ void fragment() {
Ref<StandardMaterial3D> plane_mat = memnew(StandardMaterial3D);
plane_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ plane_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
plane_mat->set_on_top_of_alpha();
plane_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
plane_mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
@@ -6782,7 +6787,7 @@ void fragment() {
shader_type spatial;
-render_mode unshaded, depth_test_disabled;
+render_mode unshaded, depth_test_disabled, fog_disabled;
uniform vec4 albedo;
@@ -6790,14 +6795,14 @@ mat3 orthonormalize(mat3 m) {
vec3 x = normalize(m[0]);
vec3 y = normalize(m[1] - x * dot(x, m[1]));
vec3 z = m[2] - x * dot(x, m[2]);
- z = normalize(z - y * (dot(y,m[2])));
+ z = normalize(z - y * (dot(y, m[2])));
return mat3(x,y,z);
}
void vertex() {
mat3 mv = orthonormalize(mat3(MODELVIEW_MATRIX));
vec3 n = mv * VERTEX;
- float orientation = dot(vec3(0, 0, -1), n);
+ float orientation = dot(vec3(0.0, 0.0, -1.0), n);
if (orientation <= 0.005) {
VERTEX += NORMAL * 0.02;
}
@@ -6832,7 +6837,7 @@ void fragment() {
shader_type spatial;
-render_mode unshaded, depth_test_disabled;
+render_mode unshaded, depth_test_disabled, fog_disabled;
uniform vec4 albedo;
@@ -6840,16 +6845,16 @@ mat3 orthonormalize(mat3 m) {
vec3 x = normalize(m[0]);
vec3 y = normalize(m[1] - x * dot(x, m[1]));
vec3 z = m[2] - x * dot(x, m[2]);
- z = normalize(z - y * (dot(y,m[2])));
- return mat3(x,y,z);
+ z = normalize(z - y * (dot(y, m[2])));
+ return mat3(x, y, z);
}
void vertex() {
mat3 mv = orthonormalize(mat3(MODELVIEW_MATRIX));
mv = inverse(mv);
- VERTEX += NORMAL*0.008;
- vec3 camera_dir_local = mv * vec3(0,0,1);
- vec3 camera_up_local = mv * vec3(0,1,0);
+ VERTEX += NORMAL * 0.008;
+ vec3 camera_dir_local = mv * vec3(0.0, 0.0, 1.0);
+ vec3 camera_up_local = mv * vec3(0.0, 1.0, 0.0);
mat3 rotation_matrix = mat3(cross(camera_dir_local, camera_up_local), camera_up_local, camera_dir_local);
VERTEX = rotation_matrix * VERTEX;
}
@@ -6944,6 +6949,7 @@ void fragment() {
Ref<StandardMaterial3D> plane_mat = memnew(StandardMaterial3D);
plane_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ plane_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
plane_mat->set_on_top_of_alpha();
plane_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
plane_mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp
index c1b1240708..2ea251c455 100644
--- a/editor/plugins/polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/polygon_3d_editor_plugin.cpp
@@ -561,6 +561,7 @@ Polygon3DEditor::Polygon3DEditor() {
line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
line_material->set_albedo(Color(1, 1, 1));
handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
@@ -569,6 +570,7 @@ Polygon3DEditor::Polygon3DEditor() {
handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));
handle_material->set_point_size(handle->get_width());
handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index c5fb8a0d8d..e6bb5532a3 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -916,24 +916,32 @@ Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, Skel
// Skeleton 3D gizmo handle shader.
shader_type spatial;
-render_mode unshaded, shadows_disabled, depth_draw_always;
+render_mode unshaded, shadows_disabled, depth_draw_always, fog_disabled;
+
uniform sampler2D texture_albedo : source_color;
-uniform float point_size : hint_range(0,128) = 32;
+uniform float point_size : hint_range(0, 128) = 32;
+
void vertex() {
if (!OUTPUT_IS_SRGB) {
- COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );
+ COLOR.rgb = mix(pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb * (1.0 / 12.92), lessThan(COLOR.rgb, vec3(0.04045)));
}
+
VERTEX = VERTEX;
POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0);
- POSITION.z = mix(POSITION.z, 0, 0.999);
+ POSITION.z = mix(POSITION.z, 0.0, 0.999);
POINT_SIZE = point_size;
}
+
void fragment() {
- vec4 albedo_tex = texture(texture_albedo,POINT_COORD);
+ vec4 albedo_tex = texture(texture_albedo, POINT_COORD);
vec3 col = albedo_tex.rgb + COLOR.rgb;
- col = vec3(min(col.r,1.0),min(col.g,1.0),min(col.b,1.0));
+ col = vec3(min(col.r, 1.0), min(col.g, 1.0), min(col.b, 1.0));
ALBEDO = col;
- if (albedo_tex.a < 0.5) { discard; }
+
+ if (albedo_tex.a < 0.5) {
+ discard;
+ }
+
ALPHA = albedo_tex.a;
}
)");
@@ -1184,6 +1192,7 @@ Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
selected_mat = Ref<ShaderMaterial>(memnew(ShaderMaterial));
selected_sh = Ref<Shader>(memnew(Shader));
diff --git a/main/main.cpp b/main/main.cpp
index 57b0c16258..f0a05fcd63 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -225,6 +225,7 @@ static bool print_fps = false;
#ifdef TOOLS_ENABLED
static bool dump_gdextension_interface = false;
static bool dump_extension_api = false;
+static bool include_docs_in_extension_api_dump = false;
static bool validate_extension_api = false;
static String validate_extension_api_file;
#endif
@@ -521,7 +522,8 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
OS::get_singleton()->print(" --dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\n");
OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
- OS::get_singleton()->print(" --validate-extension-api <path> Validate an extension API file dumped (with the option above) from a previous version of the engine to ensure API compatibility. If incompatibilities or errors are detected, the return code will be non zero.\n");
+ OS::get_singleton()->print(" --dump-extension-api-with-docs Generate JSON dump of the Godot API like the previous option, but including documentation.\n");
+ OS::get_singleton()->print(" --validate-extension-api <path> Validate an extension API file dumped (with one of the two previous options) from a previous version of the engine to ensure API compatibility. If incompatibilities or errors are detected, the return code will be non zero.\n");
OS::get_singleton()->print(" --benchmark Benchmark the run time and print it to console.\n");
OS::get_singleton()->print(" --benchmark-file <path> Benchmark the run time and save it to a given file in JSON format. The path should be absolute.\n");
#ifdef TESTS_ENABLED
@@ -1255,6 +1257,17 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// run the project instead of a cmdline tool.
// Needs full refactoring to fix properly.
main_args.push_back(I->get());
+ } else if (I->get() == "--dump-extension-api-with-docs") {
+ // Register as an editor instance to use low-end fallback if relevant.
+ editor = true;
+ cmdline_tool = true;
+ dump_extension_api = true;
+ include_docs_in_extension_api_dump = true;
+ print_line("Dumping Extension API including documentation");
+ // Hack. Not needed but otherwise we end up detecting that this should
+ // run the project instead of a cmdline tool.
+ // Needs full refactoring to fix properly.
+ main_args.push_back(I->get());
} else if (I->get() == "--validate-extension-api") {
// Register as an editor instance to use low-end fallback if relevant.
editor = true;
@@ -2921,7 +2934,7 @@ bool Main::start() {
}
if (dump_extension_api) {
- GDExtensionAPIDump::generate_extension_json_file("extension_api.json");
+ GDExtensionAPIDump::generate_extension_json_file("extension_api.json", include_docs_in_extension_api_dump);
}
if (dump_gdextension_interface || dump_extension_api) {
diff --git a/misc/extension_api_validation/4.1-stable.expected b/misc/extension_api_validation/4.1-stable.expected
index 376dfb145c..0fb834bbbf 100644
--- a/misc/extension_api_validation/4.1-stable.expected
+++ b/misc/extension_api_validation/4.1-stable.expected
@@ -198,3 +198,10 @@ GH-80410
Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/add_image/arguments': size changed value in new API, from 6 to 10.
Added optional argument. Compatibility method registered.
+
+
+GH-82403
+--------
+Validate extension JSON: Error: Field 'native_structures/PhysicsServer3DExtensionRayResult': format changed value in new API, from "Vector3 position;Vector3 normal;RID rid;ObjectID collider_id;Object *collider;int shape" to "Vector3 position;Vector3 normal;RID rid;ObjectID collider_id;Object *collider;int shape;int face_index".
+
+Added/moved face_index field (introduced in GH-71233) to end of struct. Should still be compatible with 4.1.
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 55bb99133a..02b6af1e87 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -248,7 +248,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me
return ERR_PARSE_ERROR;
}
- if (GDScriptParser::get_builtin_type(p_member_name) != Variant::VARIANT_MAX) {
+ if (GDScriptParser::get_builtin_type(p_member_name) < Variant::VARIANT_MAX) {
push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node);
return ERR_PARSE_ERROR;
}
@@ -673,11 +673,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return bad_type;
}
result.kind = GDScriptParser::DataType::VARIANT;
- } else if (first == SNAME("Object")) {
- // Object is treated like a native type, not a built-in.
- result.kind = GDScriptParser::DataType::NATIVE;
- result.builtin_type = Variant::OBJECT;
- result.native_type = SNAME("Object");
} else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
// Built-in types.
if (p_type->type_chain.size() > 1) {
@@ -1708,7 +1703,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
parent_signature += ") -> ";
- const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
+ const String return_type = parent_return_type.to_string_strict();
if (return_type == "null") {
parent_signature += "void";
} else {
@@ -2899,19 +2894,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
if (!p_call->is_super && callee_type == GDScriptParser::Node::IDENTIFIER) {
// Call to name directly.
StringName function_name = p_call->function_name;
- Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
+ if (function_name == SNAME("Object")) {
+ push_error(R"*(Invalid constructor "Object()", use "Object.new()" instead.)*", p_call);
+ p_call->set_datatype(call_type);
+ return;
+ }
+
+ Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
if (builtin_type < Variant::VARIANT_MAX) {
// Is a builtin constructor.
call_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
call_type.kind = GDScriptParser::DataType::BUILTIN;
call_type.builtin_type = builtin_type;
- if (builtin_type == Variant::OBJECT) {
- call_type.kind = GDScriptParser::DataType::NATIVE;
- call_type.native_type = function_name; // "Object".
- }
-
bool safe_to_fold = true;
switch (builtin_type) {
// Those are stored by reference so not suited for compile-time construction.
@@ -2947,7 +2943,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
- push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be "%s" but is "%s".)", Variant::get_type_name(builtin_type), err.argument + 1,
+ push_error(vformat(R"*(Invalid argument for "%s()" constructor: argument %d should be "%s" but is "%s".)*", Variant::get_type_name(builtin_type), err.argument + 1,
Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
break;
@@ -2963,10 +2959,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee);
} break;
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
- push_error(vformat(R"(Too many arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
+ push_error(vformat(R"*(Too many arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
- push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
+ push_error(vformat(R"*(Too few arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
@@ -2977,21 +2973,27 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
break;
}
} else {
- // TODO: Check constructors without constants.
-
// If there's one argument, try to use copy constructor (those aren't explicitly defined).
if (p_call->arguments.size() == 1) {
GDScriptParser::DataType arg_type = p_call->arguments[0]->get_datatype();
- if (arg_type.is_variant()) {
- mark_node_unsafe(p_call->arguments[0]);
- } else {
+ if (arg_type.is_hard_type() && !arg_type.is_variant()) {
if (arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == builtin_type) {
// Okay.
p_call->set_datatype(call_type);
return;
}
+ } else {
+#ifdef DEBUG_ENABLED
+ mark_node_unsafe(p_call);
+ // We don't know what type was expected since constructors support overloads.
+ // TODO: Improve this by checking for matching candidates?
+ parser->push_warning(p_call->arguments[0], GDScriptWarning::UNSAFE_CALL_ARGUMENT, "1", function_name, "<unknown type>", "Variant");
+#endif
+ p_call->set_datatype(call_type);
+ return;
}
}
+
List<MethodInfo> constructors;
Variant::get_constructor_list(builtin_type, &constructors);
bool match = false;
@@ -3008,14 +3010,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
for (int i = 0; i < p_call->arguments.size(); i++) {
GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
-
- if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
+ GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
+ if (!is_type_compatible(par_type, arg_type, true)) {
types_match = false;
break;
#ifdef DEBUG_ENABLED
} else {
- if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
- parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
+ if (par_type.builtin_type == Variant::INT && arg_type.builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
+ parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, function_name);
}
#endif
}
@@ -3023,9 +3025,19 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
if (types_match) {
for (int i = 0; i < p_call->arguments.size(); i++) {
+ GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
if (p_call->arguments[i]->is_constant) {
- update_const_expression_builtin_type(p_call->arguments[i], type_from_property(info.arguments[i], true), "pass");
+ update_const_expression_builtin_type(p_call->arguments[i], par_type, "pass");
+ }
+#ifdef DEBUG_ENABLED
+ if (!(par_type.is_variant() && par_type.is_hard_type())) {
+ GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
+ if (arg_type.is_variant() || !arg_type.is_hard_type() || !is_type_compatible(arg_type, par_type, true)) {
+ mark_node_unsafe(p_call);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), function_name, par_type.to_string(), arg_type.to_string_strict());
+ }
}
+#endif
}
match = true;
call_type = type_from_property(info.return_val);
@@ -3331,8 +3343,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
#else
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
#endif // SUGGEST_GODOT4_RENAMES
- } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
- push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
+ } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.is_meta_type)) {
+ push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.to_string()), p_call);
}
}
@@ -3820,6 +3832,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
#endif
// Not a local, so check members.
+
if (!found_source) {
reduce_identifier_from_base(p_identifier);
if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
@@ -3872,10 +3885,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
StringName name = p_identifier->name;
p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
- // Check globals. We make an exception for Variant::OBJECT because it's the base class for
- // non-builtin types so we allow doing e.g. Object.new()
+ // Not a local or a member, so check globals.
+
Variant::Type builtin_type = GDScriptParser::get_builtin_type(name);
- if (builtin_type != Variant::OBJECT && builtin_type < Variant::VARIANT_MAX) {
+ if (builtin_type < Variant::VARIANT_MAX) {
if (can_be_builtin) {
p_identifier->set_datatype(make_builtin_meta_type(builtin_type));
return;
@@ -5003,21 +5016,28 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
if (arg_type.is_variant() || !arg_type.is_hard_type()) {
+#ifdef DEBUG_ENABLED
// Argument can be anything, so this is unsafe (unless the parameter is a hard variant).
if (!(par_type.is_hard_type() && par_type.is_variant())) {
mark_node_unsafe(p_call->arguments[i]);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), p_call->function_name, par_type.to_string(), arg_type.to_string_strict());
}
+#endif
} else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) {
- // Supertypes are acceptable for dynamic compliance, but it's unsafe.
- mark_node_unsafe(p_call);
if (!is_type_compatible(arg_type, par_type)) {
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*",
p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
p_call->arguments[i]);
+#ifdef DEBUG_ENABLED
+ } else {
+ // Supertypes are acceptable for dynamic compliance, but it's unsafe.
+ mark_node_unsafe(p_call);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), p_call->function_name, par_type.to_string(), arg_type.to_string_strict());
+#endif
}
#ifdef DEBUG_ENABLED
} else if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) {
- parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
+ parser->push_warning(p_call->arguments[i], GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
#endif
}
}
@@ -5049,7 +5069,7 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier
String class_path = ScriptServer::get_global_class_path(name).get_file();
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, vformat(R"(global class defined in "%s")", class_path));
return;
- } else if (GDScriptParser::get_builtin_type(name) != Variant::VARIANT_MAX) {
+ } else if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) {
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type");
return;
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 97e02ac716..1278090c57 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -612,11 +612,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
arguments.push_back(arg);
}
- if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) {
- // Construct a built-in type.
- Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
-
- gen->write_construct(result, vtype, arguments);
+ if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
+ gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
// Variant utility function.
gen->write_call_utility(result, call->function_name, arguments);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 0801582dbd..c059d8b2fa 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -52,11 +52,18 @@
#include "editor/editor_settings.h"
#endif
+// This function is used to determine that a type is "built-in" as opposed to native
+// and custom classes. So `Variant::NIL` and `Variant::OBJECT` are excluded:
+// `Variant::NIL` - `null` is literal, not a type.
+// `Variant::OBJECT` - `Object` should be treated as a class, not as a built-in type.
static HashMap<StringName, Variant::Type> builtin_types;
Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
- if (builtin_types.is_empty()) {
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
+ if (unlikely(builtin_types.is_empty())) {
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ Variant::Type type = (Variant::Type)i;
+ if (type != Variant::NIL && type != Variant::OBJECT) {
+ builtin_types[Variant::get_type_name(type)] = type;
+ }
}
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 3bd3696e99..9b50c34ed2 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -149,6 +149,7 @@ public:
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
String to_string() const;
+ _FORCE_INLINE_ String to_string_strict() const { return is_hard_type() ? to_string() : "Variant"; }
PropertyInfo to_property_info(const String &p_name) const;
_FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
@@ -1530,7 +1531,7 @@ public:
bool is_tool() const { return _is_tool; }
ClassNode *find_class(const String &p_qualified_name) const;
bool has_class(const GDScriptParser::ClassNode *p_class) const;
- static Variant::Type get_builtin_type(const StringName &p_type);
+ static Variant::Type get_builtin_type(const StringName &p_type); // Excluding `Variant::NIL` and `Variant::OBJECT`.
CompletionContext get_completion_context() const { return completion_context; }
CompletionCall get_completion_call() const { return completion_call; }
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index a0078f84d6..cabac07ef9 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -108,7 +108,7 @@ String GDScriptWarning::get_message() const {
return vformat(R"(The value is cast to "%s" but has an unknown type.)", symbols[0]);
case UNSAFE_CALL_ARGUMENT:
CHECK_SYMBOLS(4);
- return vformat(R"*(The argument %s of the function "%s()" requires a the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3]);
+ return vformat(R"*(The argument %s of the function "%s()" requires the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3]);
case UNSAFE_VOID_RETURN:
CHECK_SYMBOLS(2);
return vformat(R"*(The method "%s()" returns "void" but it's trying to return a call to "%s()" that can't be ensured to also be "void".)*", symbols[0], symbols[1]);
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.gd
new file mode 100644
index 0000000000..87d1b9ea18
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.gd
@@ -0,0 +1,7 @@
+# GH-73283
+
+class MyClass:
+ pass
+
+func test():
+ MyClass.not_existing_method()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.out b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.out
new file mode 100644
index 0000000000..7340058dd4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Static function "not_existing_method()" not found in base "MyClass".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.gd b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.gd
new file mode 100644
index 0000000000..1600c3001f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.gd
@@ -0,0 +1,4 @@
+# GH-73213
+
+func test():
+ print(Object())
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.out b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.out
new file mode 100644
index 0000000000..27668fcd48
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid constructor "Object()", use "Object.new()" instead.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd
index b447180ea8..d0f895d784 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd
@@ -23,6 +23,7 @@ func test() -> void:
typed = variant()
inferred = variant()
+ @warning_ignore("unsafe_call_argument") # TODO: Hard vs Weak vs Unknown.
param_weak(typed)
param_typed(typed)
param_inferred(typed)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd
index 5a413e2015..08e7dc590e 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd
@@ -6,10 +6,12 @@ var prop = null
func check_arg(arg = null) -> void:
if arg != null:
+ @warning_ignore("unsafe_call_argument")
print(check(arg))
func check_recur() -> void:
if recur != null:
+ @warning_ignore("unsafe_call_argument")
print(check(recur))
else:
recur = 1
@@ -22,11 +24,13 @@ func test() -> void:
if prop == null:
set('prop', 1)
+ @warning_ignore("unsafe_call_argument")
print(check(prop))
set('prop', null)
var loop = null
while loop != 2:
if loop != null:
+ @warning_ignore("unsafe_call_argument")
print(check(loop))
loop = 1 if loop == null else 2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd
index 849df0921e..c1776fe1b4 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd
@@ -14,4 +14,5 @@ func test():
func do_add_node():
var node = Node.new()
node.name = "Node"
+ @warning_ignore("unsafe_call_argument")
add_child(node)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.gd b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.gd
new file mode 100644
index 0000000000..573060ae0f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.gd
@@ -0,0 +1,37 @@
+func variant_func(x: Variant) -> void:
+ print(x)
+
+func int_func(x: int) -> void:
+ print(x)
+
+func float_func(x: float) -> void:
+ print(x)
+
+# We don't want to execute it because of errors, just analyze.
+func no_exec_test():
+ var untyped_int = 42
+ var untyped_string = "abc"
+ var variant_int: Variant = 42
+ var variant_string: Variant = "abc"
+ var typed_int: int = 42
+
+ variant_func(untyped_int) # No warning.
+ variant_func(untyped_string) # No warning.
+ variant_func(variant_int) # No warning.
+ variant_func(variant_string) # No warning.
+ variant_func(typed_int) # No warning.
+
+ int_func(untyped_int)
+ int_func(untyped_string)
+ int_func(variant_int)
+ int_func(variant_string)
+ int_func(typed_int) # No warning.
+
+ float_func(untyped_int)
+ float_func(untyped_string)
+ float_func(variant_int)
+ float_func(variant_string)
+ float_func(typed_int) # No warning.
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.out b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.out
new file mode 100644
index 0000000000..b8fcb67158
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.out
@@ -0,0 +1,33 @@
+GDTEST_OK
+>> WARNING
+>> Line: 24
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
+>> WARNING
+>> Line: 25
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
+>> WARNING
+>> Line: 26
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
+>> WARNING
+>> Line: 27
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
+>> WARNING
+>> Line: 30
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
+>> WARNING
+>> Line: 31
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
+>> WARNING
+>> Line: 32
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
+>> WARNING
+>> Line: 33
+>> UNSAFE_CALL_ARGUMENT
+>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd
index f04f4de08d..19f6e08285 100644
--- a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd
@@ -3,27 +3,32 @@ extends Node
func test():
var child = Node.new()
child.name = "Child"
+ @warning_ignore("unsafe_call_argument")
add_child(child)
child.owner = self
var hey = Node.new()
hey.name = "Hey"
+ @warning_ignore("unsafe_call_argument")
child.add_child(hey)
hey.owner = self
hey.unique_name_in_owner = true
var fake_hey = Node.new()
fake_hey.name = "Hey"
+ @warning_ignore("unsafe_call_argument")
add_child(fake_hey)
fake_hey.owner = self
var sub_child = Node.new()
sub_child.name = "SubChild"
+ @warning_ignore("unsafe_call_argument")
hey.add_child(sub_child)
sub_child.owner = self
var howdy = Node.new()
howdy.name = "Howdy"
+ @warning_ignore("unsafe_call_argument")
sub_child.add_child(howdy)
howdy.owner = self
howdy.unique_name_in_owner = true
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd
index 8ba558e91d..3d9404b20b 100644
--- a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd
@@ -5,9 +5,11 @@ func test():
# Create the required node structure.
var hello = Node.new()
hello.name = "Hello"
+ @warning_ignore("unsafe_call_argument")
add_child(hello)
var world = Node.new()
world.name = "World"
+ @warning_ignore("unsafe_call_argument")
hello.add_child(world)
# All the ways of writing node paths below with the `$` operator are valid.
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
index df6001c7e2..f16c768f7f 100644
--- a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
@@ -26,6 +26,7 @@ func test():
if true: (v as Callable).call()
print()
+ @warning_ignore("unsafe_call_argument")
other(v)
print()
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd
index 59cdc7d6c2..31de73813f 100644
--- a/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd
+++ b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd
@@ -2,4 +2,5 @@ func foo(x):
return x + 1
func test():
+ @warning_ignore("unsafe_call_argument")
print(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(0)))))))))))))))))))))))))
diff --git a/modules/gdscript/tests/scripts/parser/features/super.gd b/modules/gdscript/tests/scripts/parser/features/super.gd
index f5ae2a74a7..33accd92a9 100644
--- a/modules/gdscript/tests/scripts/parser/features/super.gd
+++ b/modules/gdscript/tests/scripts/parser/features/super.gd
@@ -36,6 +36,7 @@ class SayNothing extends Say:
print("howdy, see above")
func say(name):
+ @warning_ignore("unsafe_call_argument")
super(name + " super'd")
print(prefix, " say nothing... or not? ", name)
diff --git a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd
index 523959a016..20cc0cee2e 100644
--- a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd
+++ b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd
@@ -29,6 +29,7 @@ func test():
const d = 1.1
_process(d)
+ @warning_ignore("unsafe_call_argument")
print(is_equal_approx(ㄥ, PI + (d * PI)))
func _process(Δ: float) -> void:
diff --git a/modules/gdscript/tests/scripts/runtime/features/object_constructor.gd b/modules/gdscript/tests/scripts/runtime/features/object_constructor.gd
new file mode 100644
index 0000000000..b875efef56
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/object_constructor.gd
@@ -0,0 +1,6 @@
+# GH-73213
+
+func test():
+ var object := Object.new() # Not `Object()`.
+ print(object.get_class())
+ object.free()
diff --git a/modules/gdscript/tests/scripts/runtime/features/object_constructor.out b/modules/gdscript/tests/scripts/runtime/features/object_constructor.out
new file mode 100644
index 0000000000..3673881576
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/object_constructor.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+Object
diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
index 2f55059334..fd1460a48f 100644
--- a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
@@ -12,6 +12,7 @@ func test():
print("end")
func test_construct(v, f):
+ @warning_ignore("unsafe_call_argument")
Vector2(v, v) # Built-in type construct.
assert(not f) # Test unary operator reading from `nil`.
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables.gd b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd
index 8da8bb7e53..7fa76ca4b0 100644
--- a/modules/gdscript/tests/scripts/runtime/features/static_variables.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd
@@ -44,6 +44,7 @@ func test():
@warning_ignore("unsafe_method_access")
var path = get_script().get_path().get_base_dir()
+ @warning_ignore("unsafe_call_argument")
var other = load(path + "/static_variables_load.gd")
prints("load.perm:", other.perm)
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
index fead2df854..1e66d8f34a 100644
--- a/modules/gdscript/tests/scripts/runtime/features/stringify.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
@@ -24,7 +24,8 @@ func test():
print(StringName("hello"))
print(NodePath("hello/world"))
var node := Node.new()
- print(RID(node))
+ @warning_ignore("unsafe_call_argument")
+ print(RID(node)) # TODO: Why is the constructor (or implicit cast) not documented?
print(node.get_name)
print(node.property_list_changed)
node.free()
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index b5a53fa1bf..f7c01ff840 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -1412,6 +1412,7 @@ GridMapEditor::GridMapEditor() {
inner_mat.instantiate();
inner_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.2));
inner_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ inner_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
inner_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
d[RS::ARRAY_VERTEX] = triangles;
@@ -1424,11 +1425,13 @@ GridMapEditor::GridMapEditor() {
outer_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
outer_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ outer_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
selection_floor_mat.instantiate();
selection_floor_mat->set_albedo(Color(0.80, 0.80, 1.0, 1));
selection_floor_mat->set_on_top_of_alpha();
selection_floor_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ selection_floor_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
d[RS::ARRAY_VERTEX] = lines;
RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_LINES, d);
@@ -1457,6 +1460,7 @@ GridMapEditor::GridMapEditor() {
indicator_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
indicator_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
indicator_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ indicator_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
indicator_mat->set_albedo(Color(0.8, 0.5, 0.1));
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildDiagnostic.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildDiagnostic.cs
new file mode 100644
index 0000000000..6e0c63dd43
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildDiagnostic.cs
@@ -0,0 +1,23 @@
+#nullable enable
+
+namespace GodotTools.Build
+{
+ public class BuildDiagnostic
+ {
+ public enum DiagnosticType
+ {
+ Hidden,
+ Info,
+ Warning,
+ Error,
+ }
+
+ public DiagnosticType Type { get; set; }
+ public string? File { get; set; }
+ public int Line { get; set; }
+ public int Column { get; set; }
+ public string? Code { get; set; }
+ public string Message { get; set; } = "";
+ public string? ProjectFile { get; set; }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 312c65e364..9bb4fd153b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -40,9 +40,6 @@ namespace GodotTools.Build
plugin.MakeBottomPanelItemVisible(plugin.MSBuildPanel);
}
- public static void RestartBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
- public static void StopBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
-
private static string GetLogFilePath(BuildInfo buildInfo)
{
return Path.Combine(buildInfo.LogsDirPath, MsBuildLogFileName);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index 54f7ed02f5..f9e85c36e5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -1,425 +1,150 @@
using Godot;
-using System;
-using System.Diagnostics.CodeAnalysis;
-using GodotTools.Internals;
-using File = GodotTools.Utils.File;
-using Path = System.IO.Path;
+using static GodotTools.Internals.Globals;
+
+#nullable enable
namespace GodotTools.Build
{
- public partial class BuildOutputView : VBoxContainer, ISerializationListener
+ public partial class BuildOutputView : HBoxContainer
{
- [Serializable]
- private partial class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
- {
- public bool Warning { get; set; }
- public string File { get; set; }
- public int Line { get; set; }
- public int Column { get; set; }
- public string Code { get; set; }
- public string Message { get; set; }
- public string ProjectFile { get; set; }
- }
-
- [Signal]
- public delegate void BuildStateChangedEventHandler();
-
- public bool HasBuildExited { get; private set; } = false;
+#nullable disable
+ private RichTextLabel _log;
- public BuildResult? BuildResult { get; private set; } = null;
+ private Button _clearButton;
+ private Button _copyButton;
+#nullable enable
- public int ErrorCount { get; private set; } = 0;
-
- public int WarningCount { get; private set; } = 0;
-
- public bool ErrorsVisible { get; set; } = true;
- public bool WarningsVisible { get; set; } = true;
-
- public Texture2D BuildStateIcon
+ public void Append(string text)
{
- get
- {
- if (!HasBuildExited)
- return GetThemeIcon("Stop", "EditorIcons");
-
- if (BuildResult == Build.BuildResult.Error)
- return GetThemeIcon("Error", "EditorIcons");
-
- if (WarningCount > 1)
- return GetThemeIcon("Warning", "EditorIcons");
-
- return null;
- }
+ _log.AddText(text);
}
- public bool LogVisible
+ public void Clear()
{
- set => _buildLog.Visible = value;
+ _log.Clear();
}
- // TODO Use List once we have proper serialization.
- private Godot.Collections.Array<BuildIssue> _issues = new();
- private ItemList _issuesList;
- private PopupMenu _issuesListContextMenu;
- private TextEdit _buildLog;
- private BuildInfo _buildInfo;
-
- private readonly object _pendingBuildLogTextLock = new object();
- [NotNull] private string _pendingBuildLogText = string.Empty;
-
- private void LoadIssuesFromFile(string csvFile)
+ private void CopyRequested()
{
- using var file = FileAccess.Open(csvFile, FileAccess.ModeFlags.Read);
-
- if (file == null)
- return;
+ string text = _log.GetSelectedText();
- while (!file.EofReached())
- {
- string[] csvColumns = file.GetCsvLine();
+ if (string.IsNullOrEmpty(text))
+ text = _log.GetParsedText();
- if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
- return;
-
- if (csvColumns.Length != 7)
- {
- GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
- continue;
- }
-
- var issue = new BuildIssue
- {
- Warning = csvColumns[0] == "warning",
- File = csvColumns[1],
- Line = int.Parse(csvColumns[2]),
- Column = int.Parse(csvColumns[3]),
- Code = csvColumns[4],
- Message = csvColumns[5],
- ProjectFile = csvColumns[6]
- };
-
- if (issue.Warning)
- WarningCount += 1;
- else
- ErrorCount += 1;
-
- _issues.Add(issue);
- }
+ if (!string.IsNullOrEmpty(text))
+ DisplayServer.ClipboardSet(text);
}
- private void IssueActivated(long idx)
+ public override void _Ready()
{
- if (idx < 0 || idx >= _issuesList.ItemCount)
- throw new ArgumentOutOfRangeException(nameof(idx), "Item list index out of range.");
-
- // Get correct issue idx from issue list
- int issueIndex = (int)_issuesList.GetItemMetadata((int)idx);
-
- if (issueIndex < 0 || issueIndex >= _issues.Count)
- throw new InvalidOperationException("Issue index out of range.");
-
- BuildIssue issue = _issues[issueIndex];
+ Name = "Output".TTR();
- if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File))
- return;
-
- string projectDir = !string.IsNullOrEmpty(issue.ProjectFile) ?
- issue.ProjectFile.GetBaseDir() :
- _buildInfo.Solution.GetBaseDir();
-
- string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
-
- if (!File.Exists(file))
- return;
-
- file = ProjectSettings.LocalizePath(file);
-
- if (file.StartsWith("res://"))
+ var vbLeft = new VBoxContainer
{
- var script = (Script)ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType);
-
- // Godot's ScriptEditor.Edit is 0-based but the issue lines are 1-based.
- if (script != null && Internal.ScriptEditorEdit(script, issue.Line - 1, issue.Column - 1))
- Internal.EditorNodeShowScriptScreen();
- }
- }
-
- public void UpdateIssuesList()
- {
- _issuesList.Clear();
+ CustomMinimumSize = new Vector2(0, 180 * EditorScale),
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ };
+ AddChild(vbLeft);
- using (var warningIcon = GetThemeIcon("Warning", "EditorIcons"))
- using (var errorIcon = GetThemeIcon("Error", "EditorIcons"))
+ // Log - Rich Text Label.
+ _log = new RichTextLabel
{
- for (int i = 0; i < _issues.Count; i++)
- {
- BuildIssue issue = _issues[i];
-
- if (!(issue.Warning ? WarningsVisible : ErrorsVisible))
- continue;
-
- string tooltip = string.Empty;
- tooltip += $"Message: {issue.Message}";
-
- if (!string.IsNullOrEmpty(issue.Code))
- tooltip += $"\nCode: {issue.Code}";
-
- tooltip += $"\nType: {(issue.Warning ? "warning" : "error")}";
-
- string text = string.Empty;
-
- if (!string.IsNullOrEmpty(issue.File))
- {
- text += $"{issue.File}({issue.Line},{issue.Column}): ";
-
- tooltip += $"\nFile: {issue.File}";
- tooltip += $"\nLine: {issue.Line}";
- tooltip += $"\nColumn: {issue.Column}";
- }
-
- if (!string.IsNullOrEmpty(issue.ProjectFile))
- tooltip += $"\nProject: {issue.ProjectFile}";
-
- text += issue.Message;
-
- int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal);
- string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx);
- _issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon);
-
- int index = _issuesList.ItemCount - 1;
- _issuesList.SetItemTooltip(index, tooltip);
- _issuesList.SetItemMetadata(index, i);
- }
- }
- }
-
- private void BuildLaunchFailed(BuildInfo buildInfo, string cause)
- {
- HasBuildExited = true;
- BuildResult = Build.BuildResult.Error;
-
- _issuesList.Clear();
-
- var issue = new BuildIssue { Message = cause, Warning = false };
-
- ErrorCount += 1;
- _issues.Add(issue);
-
- UpdateIssuesList();
-
- EmitSignal(nameof(BuildStateChanged));
- }
-
- private void BuildStarted(BuildInfo buildInfo)
- {
- _buildInfo = buildInfo;
- HasBuildExited = false;
-
- _issues.Clear();
- WarningCount = 0;
- ErrorCount = 0;
- _buildLog.Text = string.Empty;
-
- UpdateIssuesList();
-
- EmitSignal(nameof(BuildStateChanged));
- }
-
- private void BuildFinished(BuildResult result)
- {
- HasBuildExited = true;
- BuildResult = result;
-
- LoadIssuesFromFile(Path.Combine(_buildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
+ BbcodeEnabled = true,
+ ScrollFollowing = true,
+ SelectionEnabled = true,
+ ContextMenuEnabled = true,
+ FocusMode = FocusModeEnum.Click,
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ DeselectOnFocusLossEnabled = false,
- UpdateIssuesList();
+ };
+ vbLeft.AddChild(_log);
- EmitSignal(nameof(BuildStateChanged));
- }
+ var vbRight = new VBoxContainer();
+ AddChild(vbRight);
- private void UpdateBuildLogText()
- {
- lock (_pendingBuildLogTextLock)
+ // Tools grid
+ var hbTools = new HBoxContainer
{
- _buildLog.Text += _pendingBuildLogText;
- _pendingBuildLogText = string.Empty;
- ScrollToLastNonEmptyLogLine();
- }
- }
-
- private void StdOutputReceived(string text)
- {
- lock (_pendingBuildLogTextLock)
- {
- if (_pendingBuildLogText.Length == 0)
- CallDeferred(nameof(UpdateBuildLogText));
- _pendingBuildLogText += text + "\n";
- }
- }
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ };
+ vbRight.AddChild(hbTools);
- private void StdErrorReceived(string text)
- {
- lock (_pendingBuildLogTextLock)
+ // Clear.
+ _clearButton = new Button
{
- if (_pendingBuildLogText.Length == 0)
- CallDeferred(nameof(UpdateBuildLogText));
- _pendingBuildLogText += text + "\n";
- }
- }
+ ThemeTypeVariation = "FlatButton",
+ FocusMode = FocusModeEnum.None,
+ Shortcut = EditorDefShortcut("editor/clear_output", "Clear Output".TTR(), (Key)KeyModifierMask.MaskCmdOrCtrl | (Key)KeyModifierMask.MaskShift | Key.K),
+ };
+ _clearButton.Pressed += Clear;
+ hbTools.AddChild(_clearButton);
- private void ScrollToLastNonEmptyLogLine()
- {
- int line;
- for (line = _buildLog.GetLineCount(); line > 0; line--)
+ // Copy.
+ _copyButton = new Button
{
- string lineText = _buildLog.GetLine(line);
-
- if (!string.IsNullOrEmpty(lineText) || !string.IsNullOrEmpty(lineText?.Trim()))
- break;
- }
-
- _buildLog.SetCaretLine(line);
- }
-
- public void RestartBuild()
- {
- if (!HasBuildExited)
- throw new InvalidOperationException("Build already started.");
-
- BuildManager.RestartBuild(this);
- }
-
- public void StopBuild()
- {
- if (!HasBuildExited)
- throw new InvalidOperationException("Build is not in progress.");
+ ThemeTypeVariation = "FlatButton",
+ FocusMode = FocusModeEnum.None,
+ Shortcut = EditorDefShortcut("editor/copy_output", "Copy Selection".TTR(), (Key)KeyModifierMask.MaskCmdOrCtrl | Key.C),
+ ShortcutContext = this,
+ };
+ _copyButton.Pressed += CopyRequested;
+ hbTools.AddChild(_copyButton);
- BuildManager.StopBuild(this);
+ UpdateTheme();
}
- private enum IssuesContextMenuOption
+ public override void _Notification(int what)
{
- Copy
- }
+ base._Notification(what);
- private void IssuesListContextOptionPressed(long id)
- {
- switch ((IssuesContextMenuOption)id)
+ if (what == NotificationThemeChanged)
{
- case IssuesContextMenuOption.Copy:
- {
- // We don't allow multi-selection but just in case that changes later...
- string text = null;
-
- foreach (int issueIndex in _issuesList.GetSelectedItems())
- {
- if (text != null)
- text += "\n";
- text += _issuesList.GetItemText(issueIndex);
- }
-
- if (text != null)
- DisplayServer.ClipboardSet(text);
- break;
- }
- default:
- throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid issue context menu option");
+ UpdateTheme();
}
}
- private void IssuesListClicked(long index, Vector2 atPosition, long mouseButtonIndex)
+ private void UpdateTheme()
{
- if (mouseButtonIndex != (long)MouseButton.Right)
- {
+ // Nodes will be null until _Ready is called.
+ if (_log == null)
return;
- }
-
- _ = index; // Unused
-
- _issuesListContextMenu.Clear();
- _issuesListContextMenu.Size = new Vector2I(1, 1);
-
- if (_issuesList.IsAnythingSelected())
- {
- // Add menu entries for the selected item
- _issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"),
- label: "Copy Error".TTR(), (int)IssuesContextMenuOption.Copy);
- }
-
- if (_issuesListContextMenu.ItemCount > 0)
- {
- _issuesListContextMenu.Position = (Vector2I)(_issuesList.GlobalPosition + atPosition);
- _issuesListContextMenu.Popup();
- }
- }
-
- public override void _Ready()
- {
- base._Ready();
- SizeFlagsVertical = SizeFlags.ExpandFill;
+ var normalFont = GetThemeFont("output_source", "EditorFonts");
+ if (normalFont != null)
+ _log.AddThemeFontOverride("normal_font", normalFont);
- var hsc = new HSplitContainer
- {
- SizeFlagsHorizontal = SizeFlags.ExpandFill,
- SizeFlagsVertical = SizeFlags.ExpandFill
- };
- AddChild(hsc);
+ var boldFont = GetThemeFont("output_source_bold", "EditorFonts");
+ if (boldFont != null)
+ _log.AddThemeFontOverride("bold_font", boldFont);
- _issuesList = new ItemList
- {
- SizeFlagsVertical = SizeFlags.ExpandFill,
- SizeFlagsHorizontal = SizeFlags.ExpandFill // Avoid being squashed by the build log
- };
- _issuesList.ItemActivated += IssueActivated;
- _issuesList.AllowRmbSelect = true;
- _issuesList.ItemClicked += IssuesListClicked;
- hsc.AddChild(_issuesList);
+ var italicsFont = GetThemeFont("output_source_italic", "EditorFonts");
+ if (italicsFont != null)
+ _log.AddThemeFontOverride("italics_font", italicsFont);
- _issuesListContextMenu = new PopupMenu();
- _issuesListContextMenu.IdPressed += IssuesListContextOptionPressed;
- _issuesList.AddChild(_issuesListContextMenu);
+ var boldItalicsFont = GetThemeFont("output_source_bold_italic", "EditorFonts");
+ if (boldItalicsFont != null)
+ _log.AddThemeFontOverride("bold_italics_font", boldItalicsFont);
- _buildLog = new TextEdit
- {
- Editable = false,
- SizeFlagsVertical = SizeFlags.ExpandFill,
- SizeFlagsHorizontal = SizeFlags.ExpandFill // Avoid being squashed by the issues list
- };
- hsc.AddChild(_buildLog);
+ var monoFont = GetThemeFont("output_source_mono", "EditorFonts");
+ if (monoFont != null)
+ _log.AddThemeFontOverride("mono_font", monoFont);
- AddBuildEventListeners();
- }
-
- private void AddBuildEventListeners()
- {
- BuildManager.BuildLaunchFailed += BuildLaunchFailed;
- BuildManager.BuildStarted += BuildStarted;
- BuildManager.BuildFinished += BuildFinished;
- // StdOutput/Error can be received from different threads, so we need to use CallDeferred
- BuildManager.StdOutputReceived += StdOutputReceived;
- BuildManager.StdErrorReceived += StdErrorReceived;
- }
+ // Disable padding for highlighted background/foreground to prevent highlights from overlapping on close lines.
+ // This also better matches terminal output, which does not use any form of padding.
+ _log.AddThemeConstantOverride("text_highlight_h_padding", 0);
+ _log.AddThemeConstantOverride("text_highlight_v_padding", 0);
- public void OnBeforeSerialize()
- {
- // In case it didn't update yet. We don't want to have to serialize any pending output.
- UpdateBuildLogText();
-
- // NOTE:
- // Currently, GodotTools is loaded in its own load context. This load context is not reloaded, but the script still are.
- // Until that changes, we need workarounds like this one because events keep strong references to disposed objects.
- BuildManager.BuildLaunchFailed -= BuildLaunchFailed;
- BuildManager.BuildStarted -= BuildStarted;
- BuildManager.BuildFinished -= BuildFinished;
- // StdOutput/Error can be received from different threads, so we need to use CallDeferred
- BuildManager.StdOutputReceived -= StdOutputReceived;
- BuildManager.StdErrorReceived -= StdErrorReceived;
- }
+ int font_size = GetThemeFontSize("output_source_size", "EditorFonts");
+ _log.AddThemeFontSizeOverride("normal_font_size", font_size);
+ _log.AddThemeFontSizeOverride("bold_font_size", font_size);
+ _log.AddThemeFontSizeOverride("italics_font_size", font_size);
+ _log.AddThemeFontSizeOverride("mono_font_size", font_size);
- public void OnAfterDeserialize()
- {
- AddBuildEventListeners(); // Re-add them
+ _clearButton.Icon = GetThemeIcon("Clear", "EditorIcons");
+ _copyButton.Icon = GetThemeIcon("ActionCopy", "EditorIcons");
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsFilter.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsFilter.cs
new file mode 100644
index 0000000000..9c165e5767
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsFilter.cs
@@ -0,0 +1,40 @@
+using Godot;
+
+#nullable enable
+
+namespace GodotTools.Build
+{
+ public class BuildProblemsFilter
+ {
+ public BuildDiagnostic.DiagnosticType Type { get; }
+
+ public Button ToggleButton { get; }
+
+ private int _problemsCount;
+
+ public int ProblemsCount
+ {
+ get => _problemsCount;
+ set
+ {
+ _problemsCount = value;
+ ToggleButton.Text = _problemsCount.ToString();
+ }
+ }
+
+ public bool IsActive => ToggleButton.ButtonPressed;
+
+ public BuildProblemsFilter(BuildDiagnostic.DiagnosticType type)
+ {
+ Type = type;
+ ToggleButton = new Button
+ {
+ ToggleMode = true,
+ ButtonPressed = true,
+ Text = "0",
+ FocusMode = Control.FocusModeEnum.None,
+ ThemeTypeVariation = "EditorLogFilterButton",
+ };
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs
new file mode 100644
index 0000000000..b23b3f42ef
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs
@@ -0,0 +1,694 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Godot;
+using GodotTools.Internals;
+using static GodotTools.Internals.Globals;
+using FileAccess = Godot.FileAccess;
+
+#nullable enable
+
+namespace GodotTools.Build
+{
+ public partial class BuildProblemsView : HBoxContainer
+ {
+#nullable disable
+ private Button _clearButton;
+ private Button _copyButton;
+
+ private Button _toggleLayoutButton;
+
+ private Button _showSearchButton;
+ private LineEdit _searchBox;
+#nullable enable
+
+ private readonly Dictionary<BuildDiagnostic.DiagnosticType, BuildProblemsFilter> _filtersByType = new();
+
+#nullable disable
+ private Tree _problemsTree;
+ private PopupMenu _problemsContextMenu;
+#nullable enable
+
+ public enum ProblemsLayout { List, Tree }
+ private ProblemsLayout _layout = ProblemsLayout.Tree;
+
+ private readonly List<BuildDiagnostic> _diagnostics = new();
+
+ public int TotalDiagnosticCount => _diagnostics.Count;
+
+ private readonly Dictionary<BuildDiagnostic.DiagnosticType, int> _problemCountByType = new();
+
+ public int WarningCount =>
+ GetProblemCountForType(BuildDiagnostic.DiagnosticType.Warning);
+
+ public int ErrorCount =>
+ GetProblemCountForType(BuildDiagnostic.DiagnosticType.Error);
+
+ private int GetProblemCountForType(BuildDiagnostic.DiagnosticType type)
+ {
+ if (!_problemCountByType.TryGetValue(type, out int count))
+ {
+ count = _diagnostics.Count(d => d.Type == type);
+ _problemCountByType[type] = count;
+ }
+
+ return count;
+ }
+
+ private static IEnumerable<BuildDiagnostic> ReadDiagnosticsFromFile(string csvFile)
+ {
+ using var file = FileAccess.Open(csvFile, FileAccess.ModeFlags.Read);
+
+ if (file == null)
+ yield break;
+
+ while (!file.EofReached())
+ {
+ string[] csvColumns = file.GetCsvLine();
+
+ if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
+ yield break;
+
+ if (csvColumns.Length != 7)
+ {
+ GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
+ continue;
+ }
+
+ var diagnostic = new BuildDiagnostic
+ {
+ Type = csvColumns[0] switch
+ {
+ "warning" => BuildDiagnostic.DiagnosticType.Warning,
+ "error" or _ => BuildDiagnostic.DiagnosticType.Error,
+ },
+ File = csvColumns[1],
+ Line = int.Parse(csvColumns[2]),
+ Column = int.Parse(csvColumns[3]),
+ Code = csvColumns[4],
+ Message = csvColumns[5],
+ ProjectFile = csvColumns[6],
+ };
+
+ // If there's no ProjectFile but the File is a csproj, then use that.
+ if (string.IsNullOrEmpty(diagnostic.ProjectFile) &&
+ !string.IsNullOrEmpty(diagnostic.File) &&
+ diagnostic.File.EndsWith(".csproj"))
+ {
+ diagnostic.ProjectFile = diagnostic.File;
+ }
+
+ yield return diagnostic;
+ }
+ }
+
+ public void SetDiagnosticsFromFile(string csvFile)
+ {
+ var diagnostics = ReadDiagnosticsFromFile(csvFile);
+ SetDiagnostics(diagnostics);
+ }
+
+ public void SetDiagnostics(IEnumerable<BuildDiagnostic> diagnostics)
+ {
+ _diagnostics.Clear();
+ _problemCountByType.Clear();
+
+ _diagnostics.AddRange(diagnostics);
+ UpdateProblemsView();
+ }
+
+ public void Clear()
+ {
+ _problemsTree.Clear();
+ _diagnostics.Clear();
+ _problemCountByType.Clear();
+
+ UpdateProblemsView();
+ }
+
+ private void CopySelectedProblems()
+ {
+ var selectedItem = _problemsTree.GetNextSelected(null);
+ if (selectedItem == null)
+ return;
+
+ var selectedIdxs = new List<int>();
+ while (selectedItem != null)
+ {
+ int selectedIdx = (int)selectedItem.GetMetadata(0);
+ selectedIdxs.Add(selectedIdx);
+
+ selectedItem = _problemsTree.GetNextSelected(selectedItem);
+ }
+
+ if (selectedIdxs.Count == 0)
+ return;
+
+ var selectedDiagnostics = selectedIdxs.Select(i => _diagnostics[i]);
+
+ var sb = new StringBuilder();
+
+ foreach (var diagnostic in selectedDiagnostics)
+ {
+ if (!string.IsNullOrEmpty(diagnostic.Code))
+ sb.Append($"{diagnostic.Code}: ");
+
+ sb.AppendLine($"{diagnostic.Message} {diagnostic.File}({diagnostic.Line},{diagnostic.Column})");
+ }
+
+ string text = sb.ToString();
+
+ if (!string.IsNullOrEmpty(text))
+ DisplayServer.ClipboardSet(text);
+ }
+
+ private void ToggleLayout(bool pressed)
+ {
+ _layout = pressed ? ProblemsLayout.List : ProblemsLayout.Tree;
+
+ var editorSettings = EditorInterface.Singleton.GetEditorSettings();
+ editorSettings.SetSetting(GodotSharpEditor.Settings.ProblemsLayout, Variant.From(_layout));
+
+ _toggleLayoutButton.Icon = GetToggleLayoutIcon();
+ _toggleLayoutButton.TooltipText = GetToggleLayoutTooltipText();
+
+ UpdateProblemsView();
+ }
+
+ private bool GetToggleLayoutPressedState()
+ {
+ // If pressed: List layout.
+ // If not pressed: Tree layout.
+ return _layout == ProblemsLayout.List;
+ }
+
+ private Texture2D? GetToggleLayoutIcon()
+ {
+ return _layout switch
+ {
+ ProblemsLayout.List => GetThemeIcon("FileList", "EditorIcons"),
+ ProblemsLayout.Tree or _ => GetThemeIcon("FileTree", "EditorIcons"),
+ };
+ }
+
+ private string GetToggleLayoutTooltipText()
+ {
+ return _layout switch
+ {
+ ProblemsLayout.List => "View as a Tree".TTR(),
+ ProblemsLayout.Tree or _ => "View as a List".TTR(),
+ };
+ }
+
+ private void ToggleSearchBoxVisibility(bool pressed)
+ {
+ _searchBox.Visible = pressed;
+ if (pressed)
+ {
+ _searchBox.GrabFocus();
+ }
+ }
+
+ private void SearchTextChanged(string text)
+ {
+ UpdateProblemsView();
+ }
+
+ private void ToggleFilter(bool pressed)
+ {
+ UpdateProblemsView();
+ }
+
+ private void GoToSelectedProblem()
+ {
+ var selectedItem = _problemsTree.GetSelected();
+ if (selectedItem == null)
+ throw new InvalidOperationException("Item tree has no selected items.");
+
+ // Get correct diagnostic index from problems tree.
+ int diagnosticIndex = (int)selectedItem.GetMetadata(0);
+
+ if (diagnosticIndex < 0 || diagnosticIndex >= _diagnostics.Count)
+ throw new InvalidOperationException("Diagnostic index out of range.");
+
+ var diagnostic = _diagnostics[diagnosticIndex];
+
+ if (string.IsNullOrEmpty(diagnostic.ProjectFile) && string.IsNullOrEmpty(diagnostic.File))
+ return;
+
+ string? projectDir = !string.IsNullOrEmpty(diagnostic.ProjectFile) ?
+ diagnostic.ProjectFile.GetBaseDir() :
+ GodotSharpEditor.Instance.MSBuildPanel.LastBuildInfo?.Solution.GetBaseDir();
+ if (string.IsNullOrEmpty(projectDir))
+ return;
+
+ string file = Path.Combine(projectDir.SimplifyGodotPath(), diagnostic.File.SimplifyGodotPath());
+
+ if (!File.Exists(file))
+ return;
+
+ file = ProjectSettings.LocalizePath(file);
+
+ if (file.StartsWith("res://"))
+ {
+ var script = (Script)ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType);
+
+ // Godot's ScriptEditor.Edit is 0-based but the diagnostic lines are 1-based.
+ if (script != null && Internal.ScriptEditorEdit(script, diagnostic.Line - 1, diagnostic.Column - 1))
+ Internal.EditorNodeShowScriptScreen();
+ }
+ }
+
+ private void ShowProblemContextMenu(Vector2 position, long mouseButtonIndex)
+ {
+ if (mouseButtonIndex != (long)MouseButton.Right)
+ return;
+
+ _problemsContextMenu.Clear();
+ _problemsContextMenu.Size = new Vector2I(1, 1);
+
+ var selectedItem = _problemsTree.GetSelected();
+ if (selectedItem != null)
+ {
+ // Add menu entries for the selected item.
+ _problemsContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"),
+ label: "Copy Error".TTR(), (int)ProblemContextMenuOption.Copy);
+ }
+
+ if (_problemsContextMenu.ItemCount > 0)
+ {
+ _problemsContextMenu.Position = (Vector2I)(_problemsTree.GlobalPosition + position);
+ _problemsContextMenu.Popup();
+ }
+ }
+
+ private enum ProblemContextMenuOption
+ {
+ Copy,
+ }
+
+ private void ProblemContextOptionPressed(long id)
+ {
+ switch ((ProblemContextMenuOption)id)
+ {
+ case ProblemContextMenuOption.Copy:
+ CopySelectedProblems();
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid problem context menu option.");
+ }
+ }
+
+ private bool ShouldDisplayDiagnostic(BuildDiagnostic diagnostic)
+ {
+ if (!_filtersByType[diagnostic.Type].IsActive)
+ return false;
+
+ string searchText = _searchBox.Text;
+ if (!string.IsNullOrEmpty(searchText) &&
+ (!diagnostic.Message.Contains(searchText, StringComparison.OrdinalIgnoreCase) ||
+ !(diagnostic.File?.Contains(searchText, StringComparison.OrdinalIgnoreCase) ?? false)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private Color? GetProblemItemColor(BuildDiagnostic diagnostic)
+ {
+ return diagnostic.Type switch
+ {
+ BuildDiagnostic.DiagnosticType.Warning => GetThemeColor("warning_color", "Editor"),
+ BuildDiagnostic.DiagnosticType.Error => GetThemeColor("error_color", "Editor"),
+ _ => null,
+ };
+ }
+
+ public void UpdateProblemsView()
+ {
+ switch (_layout)
+ {
+ case ProblemsLayout.List:
+ UpdateProblemsList();
+ break;
+
+ case ProblemsLayout.Tree:
+ default:
+ UpdateProblemsTree();
+ break;
+ }
+
+ foreach (var (type, filter) in _filtersByType)
+ {
+ int count = _diagnostics.Count(d => d.Type == type);
+ filter.ProblemsCount = count;
+ }
+
+ if (_diagnostics.Count == 0)
+ Name = "Problems".TTR();
+ else
+ Name = $"{"Problems".TTR()} ({_diagnostics.Count})";
+ }
+
+ private void UpdateProblemsList()
+ {
+ _problemsTree.Clear();
+
+ var root = _problemsTree.CreateItem();
+
+ for (int i = 0; i < _diagnostics.Count; i++)
+ {
+ var diagnostic = _diagnostics[i];
+
+ if (!ShouldDisplayDiagnostic(diagnostic))
+ continue;
+
+ var item = CreateProblemItem(diagnostic, includeFileInText: true);
+
+ var problemItem = _problemsTree.CreateItem(root);
+ problemItem.SetIcon(0, item.Icon);
+ problemItem.SetText(0, item.Text);
+ problemItem.SetTooltipText(0, item.TooltipText);
+ problemItem.SetMetadata(0, i);
+
+ var color = GetProblemItemColor(diagnostic);
+ if (color.HasValue)
+ problemItem.SetCustomColor(0, color.Value);
+ }
+ }
+
+ private void UpdateProblemsTree()
+ {
+ _problemsTree.Clear();
+
+ var root = _problemsTree.CreateItem();
+
+ var groupedDiagnostics = _diagnostics.Select((d, i) => (Diagnostic: d, Index: i))
+ .Where(x => ShouldDisplayDiagnostic(x.Diagnostic))
+ .GroupBy(x => x.Diagnostic.ProjectFile)
+ .Select(g => (ProjectFile: g.Key, Diagnostics: g.GroupBy(x => x.Diagnostic.File)
+ .Select(x => (File: x.Key, Diagnostics: x.ToArray()))))
+ .ToArray();
+
+ if (groupedDiagnostics.Length == 0)
+ return;
+
+ foreach (var (projectFile, projectDiagnostics) in groupedDiagnostics)
+ {
+ TreeItem projectItem;
+
+ if (groupedDiagnostics.Length == 1)
+ {
+ // Don't create a project item if there's only one project.
+ projectItem = root;
+ }
+ else
+ {
+ string projectFilePath = !string.IsNullOrEmpty(projectFile)
+ ? projectFile
+ : "Unknown project".TTR();
+ projectItem = _problemsTree.CreateItem(root);
+ projectItem.SetText(0, projectFilePath);
+ projectItem.SetSelectable(0, false);
+ }
+
+ foreach (var (file, fileDiagnostics) in projectDiagnostics)
+ {
+ if (fileDiagnostics.Length == 0)
+ continue;
+
+ string? projectDir = Path.GetDirectoryName(projectFile);
+ string relativeFilePath = !string.IsNullOrEmpty(file) && !string.IsNullOrEmpty(projectDir)
+ ? Path.GetRelativePath(projectDir, file)
+ : "Unknown file".TTR();
+
+ string fileItemText = string.Format("{0} ({1} issues)".TTR(), relativeFilePath, fileDiagnostics.Length);
+
+ var fileItem = _problemsTree.CreateItem(projectItem);
+ fileItem.SetText(0, fileItemText);
+ fileItem.SetSelectable(0, false);
+
+ foreach (var (diagnostic, index) in fileDiagnostics)
+ {
+ var item = CreateProblemItem(diagnostic);
+
+ var problemItem = _problemsTree.CreateItem(fileItem);
+ problemItem.SetIcon(0, item.Icon);
+ problemItem.SetText(0, item.Text);
+ problemItem.SetTooltipText(0, item.TooltipText);
+ problemItem.SetMetadata(0, index);
+
+ var color = GetProblemItemColor(diagnostic);
+ if (color.HasValue)
+ problemItem.SetCustomColor(0, color.Value);
+ }
+ }
+ }
+ }
+
+ private class ProblemItem
+ {
+ public string? Text { get; set; }
+ public string? TooltipText { get; set; }
+ public Texture2D? Icon { get; set; }
+ }
+
+ private ProblemItem CreateProblemItem(BuildDiagnostic diagnostic, bool includeFileInText = false)
+ {
+ var text = new StringBuilder();
+ var tooltip = new StringBuilder();
+
+ ReadOnlySpan<char> shortMessage = diagnostic.Message.AsSpan();
+ int lineBreakIdx = shortMessage.IndexOf('\n');
+ if (lineBreakIdx != -1)
+ shortMessage = shortMessage[..lineBreakIdx];
+ text.Append(shortMessage);
+
+ tooltip.Append($"Message: {diagnostic.Message}");
+
+ if (!string.IsNullOrEmpty(diagnostic.Code))
+ tooltip.Append($"\nCode: {diagnostic.Code}");
+
+ string type = diagnostic.Type switch
+ {
+ BuildDiagnostic.DiagnosticType.Hidden => "hidden",
+ BuildDiagnostic.DiagnosticType.Info => "info",
+ BuildDiagnostic.DiagnosticType.Warning => "warning",
+ BuildDiagnostic.DiagnosticType.Error => "error",
+ _ => "unknown",
+ };
+ tooltip.Append($"\nType: {type}");
+
+ if (!string.IsNullOrEmpty(diagnostic.File))
+ {
+ text.Append(' ');
+ if (includeFileInText)
+ {
+ text.Append(diagnostic.File);
+ }
+
+ text.Append($"({diagnostic.Line},{diagnostic.Column})");
+
+ tooltip.Append($"\nFile: {diagnostic.File}");
+ tooltip.Append($"\nLine: {diagnostic.Line}");
+ tooltip.Append($"\nColumn: {diagnostic.Column}");
+ }
+
+ if (!string.IsNullOrEmpty(diagnostic.ProjectFile))
+ tooltip.Append($"\nProject: {diagnostic.ProjectFile}");
+
+ return new ProblemItem()
+ {
+ Text = text.ToString(),
+ TooltipText = tooltip.ToString(),
+ Icon = diagnostic.Type switch
+ {
+ BuildDiagnostic.DiagnosticType.Warning => GetThemeIcon("Warning", "EditorIcons"),
+ BuildDiagnostic.DiagnosticType.Error => GetThemeIcon("Error", "EditorIcons"),
+ _ => null,
+ },
+ };
+ }
+
+ public override void _Ready()
+ {
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ _layout = editorSettings.GetSetting(GodotSharpEditor.Settings.ProblemsLayout).As<ProblemsLayout>();
+
+ Name = "Problems".TTR();
+
+ var vbLeft = new VBoxContainer
+ {
+ CustomMinimumSize = new Vector2(0, 180 * EditorScale),
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ };
+ AddChild(vbLeft);
+
+ // Problem Tree.
+ _problemsTree = new Tree
+ {
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ AllowRmbSelect = true,
+ HideRoot = true,
+ };
+ _problemsTree.ItemActivated += GoToSelectedProblem;
+ _problemsTree.ItemMouseSelected += ShowProblemContextMenu;
+ vbLeft.AddChild(_problemsTree);
+
+ // Problem context menu.
+ _problemsContextMenu = new PopupMenu();
+ _problemsContextMenu.IdPressed += ProblemContextOptionPressed;
+ _problemsTree.AddChild(_problemsContextMenu);
+
+ // Search box.
+ _searchBox = new LineEdit
+ {
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ PlaceholderText = "Filter Problems".TTR(),
+ ClearButtonEnabled = true,
+ };
+ _searchBox.TextChanged += SearchTextChanged;
+ vbLeft.AddChild(_searchBox);
+
+ var vbRight = new VBoxContainer();
+ AddChild(vbRight);
+
+ // Tools grid.
+ var hbTools = new HBoxContainer
+ {
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ };
+ vbRight.AddChild(hbTools);
+
+ // Clear.
+ _clearButton = new Button
+ {
+ ThemeTypeVariation = "FlatButton",
+ FocusMode = FocusModeEnum.None,
+ Shortcut = EditorDefShortcut("editor/clear_output", "Clear Output".TTR(), (Key)KeyModifierMask.MaskCmdOrCtrl | (Key)KeyModifierMask.MaskShift | Key.K),
+ ShortcutContext = this,
+ };
+ _clearButton.Pressed += Clear;
+ hbTools.AddChild(_clearButton);
+
+ // Copy.
+ _copyButton = new Button
+ {
+ ThemeTypeVariation = "FlatButton",
+ FocusMode = FocusModeEnum.None,
+ Shortcut = EditorDefShortcut("editor/copy_output", "Copy Selection".TTR(), (Key)KeyModifierMask.MaskCmdOrCtrl | Key.C),
+ ShortcutContext = this,
+ };
+ _copyButton.Pressed += CopySelectedProblems;
+ hbTools.AddChild(_copyButton);
+
+ // A second hbox to make a 2x2 grid of buttons.
+ var hbTools2 = new HBoxContainer
+ {
+ SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
+ };
+ vbRight.AddChild(hbTools2);
+
+ // Toggle List/Tree.
+ _toggleLayoutButton = new Button
+ {
+ Flat = true,
+ FocusMode = FocusModeEnum.None,
+ TooltipText = GetToggleLayoutTooltipText(),
+ ToggleMode = true,
+ ButtonPressed = GetToggleLayoutPressedState(),
+ };
+ // Don't tint the icon even when in "pressed" state.
+ _toggleLayoutButton.AddThemeColorOverride("icon_pressed_color", Colors.White);
+ _toggleLayoutButton.Toggled += ToggleLayout;
+ hbTools2.AddChild(_toggleLayoutButton);
+
+ // Show Search.
+ _showSearchButton = new Button
+ {
+ ThemeTypeVariation = "FlatButton",
+ FocusMode = FocusModeEnum.None,
+ ToggleMode = true,
+ ButtonPressed = true,
+ Shortcut = EditorDefShortcut("editor/open_search", "Focus Search/Filter Bar".TTR(), (Key)KeyModifierMask.MaskCmdOrCtrl | Key.F),
+ ShortcutContext = this,
+ };
+ _showSearchButton.Toggled += ToggleSearchBoxVisibility;
+ hbTools2.AddChild(_showSearchButton);
+
+ // Diagnostic Type Filters.
+ vbRight.AddChild(new HSeparator());
+
+ var infoFilter = new BuildProblemsFilter(BuildDiagnostic.DiagnosticType.Info);
+ infoFilter.ToggleButton.TooltipText = "Toggle visibility of info diagnostics.".TTR();
+ infoFilter.ToggleButton.Toggled += ToggleFilter;
+ vbRight.AddChild(infoFilter.ToggleButton);
+ _filtersByType[BuildDiagnostic.DiagnosticType.Info] = infoFilter;
+
+ var errorFilter = new BuildProblemsFilter(BuildDiagnostic.DiagnosticType.Error);
+ errorFilter.ToggleButton.TooltipText = "Toggle visibility of errors.".TTR();
+ errorFilter.ToggleButton.Toggled += ToggleFilter;
+ vbRight.AddChild(errorFilter.ToggleButton);
+ _filtersByType[BuildDiagnostic.DiagnosticType.Error] = errorFilter;
+
+ var warningFilter = new BuildProblemsFilter(BuildDiagnostic.DiagnosticType.Warning);
+ warningFilter.ToggleButton.TooltipText = "Toggle visibility of warnings.".TTR();
+ warningFilter.ToggleButton.Toggled += ToggleFilter;
+ vbRight.AddChild(warningFilter.ToggleButton);
+ _filtersByType[BuildDiagnostic.DiagnosticType.Warning] = warningFilter;
+
+ UpdateTheme();
+
+ UpdateProblemsView();
+ }
+
+ public override void _Notification(int what)
+ {
+ base._Notification(what);
+
+ switch ((long)what)
+ {
+ case EditorSettings.NotificationEditorSettingsChanged:
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ _layout = editorSettings.GetSetting(GodotSharpEditor.Settings.ProblemsLayout).As<ProblemsLayout>();
+ _toggleLayoutButton.ButtonPressed = GetToggleLayoutPressedState();
+ UpdateProblemsView();
+ break;
+
+ case NotificationThemeChanged:
+ UpdateTheme();
+ break;
+ }
+ }
+
+ private void UpdateTheme()
+ {
+ // Nodes will be null until _Ready is called.
+ if (_clearButton == null)
+ return;
+
+ foreach (var (type, filter) in _filtersByType)
+ {
+ filter.ToggleButton.Icon = type switch
+ {
+ BuildDiagnostic.DiagnosticType.Info => GetThemeIcon("Popup", "EditorIcons"),
+ BuildDiagnostic.DiagnosticType.Warning => GetThemeIcon("StatusWarning", "EditorIcons"),
+ BuildDiagnostic.DiagnosticType.Error => GetThemeIcon("StatusError", "EditorIcons"),
+ _ => null,
+ };
+ }
+
+ _clearButton.Icon = GetThemeIcon("Clear", "EditorIcons");
+ _copyButton.Icon = GetThemeIcon("ActionCopy", "EditorIcons");
+ _toggleLayoutButton.Icon = GetToggleLayoutIcon();
+ _showSearchButton.Icon = GetThemeIcon("Search", "EditorIcons");
+ _searchBox.RightIcon = GetThemeIcon("Search", "EditorIcons");
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index cc11132a55..bae87dd1dd 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -5,28 +5,73 @@ using GodotTools.Internals;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
+#nullable enable
+
namespace GodotTools.Build
{
- public partial class MSBuildPanel : VBoxContainer
+ public partial class MSBuildPanel : MarginContainer, ISerializationListener
{
- public BuildOutputView BuildOutputView { get; private set; }
+ [Signal]
+ public delegate void BuildStateChangedEventHandler();
+
+#nullable disable
+ private MenuButton _buildMenuButton;
+ private Button _openLogsFolderButton;
+
+ private BuildProblemsView _problemsView;
+ private BuildOutputView _outputView;
+#nullable enable
+
+ public BuildInfo? LastBuildInfo { get; private set; }
+ public bool IsBuildingOngoing { get; private set; }
+ public BuildResult? BuildResult { get; private set; }
- private MenuButton _buildMenuBtn;
- private Button _errorsBtn;
- private Button _warningsBtn;
- private Button _viewLogBtn;
- private Button _openLogsFolderBtn;
+ private readonly object _pendingBuildLogTextLock = new object();
+ private string _pendingBuildLogText = string.Empty;
- private void WarningsToggled(bool pressed)
+ public Texture2D? GetBuildStateIcon()
{
- BuildOutputView.WarningsVisible = pressed;
- BuildOutputView.UpdateIssuesList();
+ if (IsBuildingOngoing)
+ return GetThemeIcon("Stop", "EditorIcons");
+
+ if (_problemsView.WarningCount > 0 && _problemsView.ErrorCount > 0)
+ return GetThemeIcon("ErrorWarning", "EditorIcons");
+
+ if (_problemsView.WarningCount > 0)
+ return GetThemeIcon("Warning", "EditorIcons");
+
+ if (_problemsView.ErrorCount > 0)
+ return GetThemeIcon("Error", "EditorIcons");
+
+ return null;
}
- private void ErrorsToggled(bool pressed)
+ private enum BuildMenuOptions
{
- BuildOutputView.ErrorsVisible = pressed;
- BuildOutputView.UpdateIssuesList();
+ BuildProject,
+ RebuildProject,
+ CleanProject,
+ }
+
+ private void BuildMenuOptionPressed(long id)
+ {
+ switch ((BuildMenuOptions)id)
+ {
+ case BuildMenuOptions.BuildProject:
+ BuildProject();
+ break;
+
+ case BuildMenuOptions.RebuildProject:
+ RebuildProject();
+ break;
+
+ case BuildMenuOptions.CleanProject:
+ CleanProject();
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option");
+ }
}
public void BuildProject()
@@ -73,108 +118,136 @@ namespace GodotTools.Build
_ = BuildManager.CleanProjectBlocking("Debug");
}
- private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
-
- private void OpenLogsFolderPressed() => OS.ShellOpen(
+ private void OpenLogsFolder() => OS.ShellOpen(
$"file://{GodotSharpDirs.LogsDirPathFor("Debug")}"
);
- private void BuildMenuOptionPressed(long id)
+ private void BuildLaunchFailed(BuildInfo buildInfo, string cause)
{
- switch ((BuildMenuOptions)id)
+ IsBuildingOngoing = false;
+ BuildResult = Build.BuildResult.Error;
+
+ _problemsView.Clear();
+ _outputView.Clear();
+
+ var diagnostic = new BuildDiagnostic
{
- case BuildMenuOptions.BuildProject:
- BuildProject();
- break;
- case BuildMenuOptions.RebuildProject:
- RebuildProject();
- break;
- case BuildMenuOptions.CleanProject:
- CleanProject();
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option");
+ Type = BuildDiagnostic.DiagnosticType.Error,
+ Message = cause,
+ };
+
+ _problemsView.SetDiagnostics(new[] { diagnostic });
+
+ EmitSignal(SignalName.BuildStateChanged);
+ }
+
+ private void BuildStarted(BuildInfo buildInfo)
+ {
+ LastBuildInfo = buildInfo;
+ IsBuildingOngoing = true;
+ BuildResult = null;
+
+ _problemsView.Clear();
+ _outputView.Clear();
+
+ _problemsView.UpdateProblemsView();
+
+ EmitSignal(SignalName.BuildStateChanged);
+ }
+
+ private void BuildFinished(BuildResult result)
+ {
+ IsBuildingOngoing = false;
+ BuildResult = result;
+
+ string csvFile = Path.Combine(LastBuildInfo!.LogsDirPath, BuildManager.MsBuildIssuesFileName);
+ _problemsView.SetDiagnosticsFromFile(csvFile);
+
+ _problemsView.UpdateProblemsView();
+
+ EmitSignal(SignalName.BuildStateChanged);
+ }
+
+ private void UpdateBuildLogText()
+ {
+ lock (_pendingBuildLogTextLock)
+ {
+ _outputView.Append(_pendingBuildLogText);
+ _pendingBuildLogText = string.Empty;
}
}
- private enum BuildMenuOptions
+ private void StdOutputReceived(string text)
{
- BuildProject,
- RebuildProject,
- CleanProject
+ lock (_pendingBuildLogTextLock)
+ {
+ if (_pendingBuildLogText.Length == 0)
+ CallDeferred(nameof(UpdateBuildLogText));
+ _pendingBuildLogText += text + "\n";
+ }
+ }
+
+ private void StdErrorReceived(string text)
+ {
+ lock (_pendingBuildLogTextLock)
+ {
+ if (_pendingBuildLogText.Length == 0)
+ CallDeferred(nameof(UpdateBuildLogText));
+ _pendingBuildLogText += text + "\n";
+ }
}
public override void _Ready()
{
base._Ready();
- CustomMinimumSize = new Vector2(0, 228 * EditorScale);
- SizeFlagsVertical = SizeFlags.ExpandFill;
+ var bottomPanelStylebox = EditorInterface.Singleton.GetBaseControl().GetThemeStylebox("BottomPanel", "EditorStyles");
+ AddThemeConstantOverride("margin_top", -(int)bottomPanelStylebox.ContentMarginTop);
+ AddThemeConstantOverride("margin_left", -(int)bottomPanelStylebox.ContentMarginLeft);
+ AddThemeConstantOverride("margin_right", -(int)bottomPanelStylebox.ContentMarginRight);
+
+ var tabs = new TabContainer();
+ AddChild(tabs);
- var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
- AddChild(toolBarHBox);
+ var tabActions = new HBoxContainer
+ {
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ Alignment = BoxContainer.AlignmentMode.End,
+ };
+ tabActions.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
+ tabs.GetTabBar().AddChild(tabActions);
- _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("BuildCSharp", "EditorIcons") };
- toolBarHBox.AddChild(_buildMenuBtn);
+ _buildMenuButton = new MenuButton
+ {
+ TooltipText = "Build".TTR(),
+ Flat = true,
+ };
+ tabActions.AddChild(_buildMenuButton);
- var buildMenu = _buildMenuBtn.GetPopup();
+ var buildMenu = _buildMenuButton.GetPopup();
buildMenu.AddItem("Build Project".TTR(), (int)BuildMenuOptions.BuildProject);
buildMenu.AddItem("Rebuild Project".TTR(), (int)BuildMenuOptions.RebuildProject);
buildMenu.AddItem("Clean Project".TTR(), (int)BuildMenuOptions.CleanProject);
buildMenu.IdPressed += BuildMenuOptionPressed;
- _errorsBtn = new Button
+ _openLogsFolderButton = new Button
{
- TooltipText = "Show Errors".TTR(),
- Icon = GetThemeIcon("StatusError", "EditorIcons"),
- ExpandIcon = false,
- ToggleMode = true,
- ButtonPressed = true,
- FocusMode = FocusModeEnum.None
+ TooltipText = "Show Logs in File Manager".TTR(),
+ Flat = true,
};
- _errorsBtn.Toggled += ErrorsToggled;
- toolBarHBox.AddChild(_errorsBtn);
+ _openLogsFolderButton.Pressed += OpenLogsFolder;
+ tabActions.AddChild(_openLogsFolderButton);
- _warningsBtn = new Button
- {
- TooltipText = "Show Warnings".TTR(),
- Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
- ExpandIcon = false,
- ToggleMode = true,
- ButtonPressed = true,
- FocusMode = FocusModeEnum.None
- };
- _warningsBtn.Toggled += WarningsToggled;
- toolBarHBox.AddChild(_warningsBtn);
+ _problemsView = new BuildProblemsView();
+ tabs.AddChild(_problemsView);
- _viewLogBtn = new Button
- {
- Text = "Show Output".TTR(),
- ToggleMode = true,
- ButtonPressed = true,
- FocusMode = FocusModeEnum.None
- };
- _viewLogBtn.Toggled += ViewLogToggled;
- toolBarHBox.AddChild(_viewLogBtn);
-
- // Horizontal spacer, push everything to the right.
- toolBarHBox.AddChild(new Control
- {
- SizeFlagsHorizontal = SizeFlags.ExpandFill,
- });
+ _outputView = new BuildOutputView();
+ tabs.AddChild(_outputView);
- _openLogsFolderBtn = new Button
- {
- Text = "Show Logs in File Manager".TTR(),
- Icon = GetThemeIcon("Filesystem", "EditorIcons"),
- ExpandIcon = false,
- FocusMode = FocusModeEnum.None,
- };
- _openLogsFolderBtn.Pressed += OpenLogsFolderPressed;
- toolBarHBox.AddChild(_openLogsFolderBtn);
+ UpdateTheme();
- BuildOutputView = new BuildOutputView();
- AddChild(BuildOutputView);
+ AddBuildEventListeners();
}
public override void _Notification(int what)
@@ -183,13 +256,49 @@ namespace GodotTools.Build
if (what == NotificationThemeChanged)
{
- if (_buildMenuBtn != null)
- _buildMenuBtn.Icon = GetThemeIcon("BuildCSharp", "EditorIcons");
- if (_errorsBtn != null)
- _errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons");
- if (_warningsBtn != null)
- _warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons");
+ UpdateTheme();
}
}
+
+ private void UpdateTheme()
+ {
+ // Nodes will be null until _Ready is called.
+ if (_buildMenuButton == null)
+ return;
+
+ _buildMenuButton.Icon = GetThemeIcon("BuildCSharp", "EditorIcons");
+ _openLogsFolderButton.Icon = GetThemeIcon("Filesystem", "EditorIcons");
+ }
+
+ private void AddBuildEventListeners()
+ {
+ BuildManager.BuildLaunchFailed += BuildLaunchFailed;
+ BuildManager.BuildStarted += BuildStarted;
+ BuildManager.BuildFinished += BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred.
+ BuildManager.StdOutputReceived += StdOutputReceived;
+ BuildManager.StdErrorReceived += StdErrorReceived;
+ }
+
+ public void OnBeforeSerialize()
+ {
+ // In case it didn't update yet. We don't want to have to serialize any pending output.
+ UpdateBuildLogText();
+
+ // NOTE:
+ // Currently, GodotTools is loaded in its own load context. This load context is not reloaded, but the script still are.
+ // Until that changes, we need workarounds like this one because events keep strong references to disposed objects.
+ BuildManager.BuildLaunchFailed -= BuildLaunchFailed;
+ BuildManager.BuildStarted -= BuildStarted;
+ BuildManager.BuildFinished -= BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred
+ BuildManager.StdOutputReceived -= StdOutputReceived;
+ BuildManager.StdErrorReceived -= StdErrorReceived;
+ }
+
+ public void OnAfterDeserialize()
+ {
+ AddBuildEventListeners(); // Re-add them.
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index e186c0302b..48e654c286 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -30,6 +30,7 @@ namespace GodotTools
public const string VerbosityLevel = "dotnet/build/verbosity_level";
public const string NoConsoleLogging = "dotnet/build/no_console_logging";
public const string CreateBinaryLog = "dotnet/build/create_binary_log";
+ public const string ProblemsLayout = "dotnet/build/problems_layout";
}
private EditorSettings _editorSettings;
@@ -437,7 +438,7 @@ namespace GodotTools
private void BuildStateChanged()
{
if (_bottomPanelBtn != null)
- _bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon;
+ _bottomPanelBtn.Icon = MSBuildPanel.GetBuildStateIcon();
}
public override void _EnablePlugin()
@@ -489,8 +490,7 @@ namespace GodotTools
editorBaseControl.AddChild(_confirmCreateSlnDialog);
MSBuildPanel = new MSBuildPanel();
- MSBuildPanel.Ready += () =>
- MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
+ MSBuildPanel.BuildStateChanged += BuildStateChanged;
_bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" });
@@ -535,6 +535,7 @@ namespace GodotTools
EditorDef(Settings.VerbosityLevel, Variant.From(VerbosityLevelId.Normal));
EditorDef(Settings.NoConsoleLogging, false);
EditorDef(Settings.CreateBinaryLog, false);
+ EditorDef(Settings.ProblemsLayout, Variant.From(BuildProblemsView.ProblemsLayout.Tree));
string settingsHintStr = "Disabled";
@@ -593,6 +594,14 @@ namespace GodotTools
["hint_string"] = string.Join(",", verbosityLevels),
});
+ _editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
+ {
+ ["type"] = (int)Variant.Type.Int,
+ ["name"] = Settings.ProblemsLayout,
+ ["hint"] = (int)PropertyHint.Enum,
+ ["hint_string"] = "View as List,View as Tree",
+ });
+
OnSettingsChanged();
_editorSettings.SettingsChanged += OnSettingsChanged;
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index c4d7671fb3..55b8c4f4a0 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -21,23 +21,22 @@ thirdparty_sources = [
"src/lib/sw_engine/tvgSwShape.cpp",
"src/lib/sw_engine/tvgSwStroke.cpp",
"src/lib/tvgAccessor.cpp",
- "src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgFill.cpp",
"src/lib/tvgGlCanvas.cpp",
"src/lib/tvgInitializer.cpp",
- "src/lib/tvgLinearGradient.cpp",
"src/lib/tvgLoader.cpp",
- "src/lib/tvgLzw.cpp",
"src/lib/tvgPaint.cpp",
"src/lib/tvgPicture.cpp",
- "src/lib/tvgRadialGradient.cpp",
"src/lib/tvgRender.cpp",
"src/lib/tvgSaver.cpp",
"src/lib/tvgScene.cpp",
"src/lib/tvgShape.cpp",
"src/lib/tvgSwCanvas.cpp",
"src/lib/tvgTaskScheduler.cpp",
+ "src/utils/tvgBezier.cpp",
+ "src/utils/tvgCompressor.cpp",
+ "src/utils/tvgStr.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
"src/loaders/svg/tvgSvgCssStyle.cpp",
"src/loaders/svg/tvgSvgLoader.cpp",
@@ -62,6 +61,7 @@ env_thirdparty.Prepend(
thirdparty_dir + "src/lib/sw_engine",
thirdparty_dir + "src/loaders/raw",
thirdparty_dir + "src/loaders/svg",
+ thirdparty_dir + "src/utils",
]
)
# Also requires libpng headers
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 360741363a..1acff68135 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -39,7 +39,9 @@ freetype_enabled = "freetype" in env.module_list
msdfgen_enabled = "msdfgen" in env.module_list
if "svg" in env.module_list:
- env_text_server_adv.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
+ env_text_server_adv.Prepend(
+ CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"]
+ )
# Enable ThorVG static object linking.
env_text_server_adv.Append(CPPDEFINES=["TVG_STATIC"])
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 38fd5f6403..bf29ad3016 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -52,23 +52,22 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"src/lib/sw_engine/tvgSwShape.cpp",
"src/lib/sw_engine/tvgSwStroke.cpp",
"src/lib/tvgAccessor.cpp",
- "src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgFill.cpp",
"src/lib/tvgGlCanvas.cpp",
"src/lib/tvgInitializer.cpp",
- "src/lib/tvgLinearGradient.cpp",
"src/lib/tvgLoader.cpp",
- "src/lib/tvgLzw.cpp",
"src/lib/tvgPaint.cpp",
"src/lib/tvgPicture.cpp",
- "src/lib/tvgRadialGradient.cpp",
"src/lib/tvgRender.cpp",
"src/lib/tvgSaver.cpp",
"src/lib/tvgScene.cpp",
"src/lib/tvgShape.cpp",
"src/lib/tvgSwCanvas.cpp",
"src/lib/tvgTaskScheduler.cpp",
+ "src/utils/tvgBezier.cpp",
+ "src/utils/tvgCompressor.cpp",
+ "src/utils/tvgStr.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
"src/loaders/svg/tvgSvgCssStyle.cpp",
"src/loaders/svg/tvgSvgLoader.cpp",
@@ -86,6 +85,7 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"../../../thirdparty/thorvg/src/lib/sw_engine",
"../../../thirdparty/thorvg/src/loaders/raw",
"../../../thirdparty/thorvg/src/loaders/svg",
+ "../../../thirdparty/thorvg/src/utils",
"../../../thirdparty/libpng",
]
)
@@ -93,7 +93,13 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
# Enable ThorVG static object linking.
env_tvg.Append(CPPDEFINES=["TVG_STATIC"])
- env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+ env.Append(
+ CPPPATH=[
+ "../../../thirdparty/thorvg/inc",
+ "../../../thirdparty/thorvg/src/lib",
+ "../../../thirdparty/thorvg/src/utils",
+ ]
+ )
env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
lib = env_tvg.Library(
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index 0da2a54bc2..8705bc430d 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -9,7 +9,9 @@ msdfgen_enabled = "msdfgen" in env.module_list
env_text_server_fb = env_modules.Clone()
if "svg" in env.module_list:
- env_text_server_fb.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
+ env_text_server_fb.Prepend(
+ CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"]
+ )
# Enable ThorVG static object linking.
env_text_server_fb.Append(CPPDEFINES=["TVG_STATIC"])
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 20e1afa2e5..40bb2dc1b9 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -47,23 +47,22 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"src/lib/sw_engine/tvgSwShape.cpp",
"src/lib/sw_engine/tvgSwStroke.cpp",
"src/lib/tvgAccessor.cpp",
- "src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgFill.cpp",
"src/lib/tvgGlCanvas.cpp",
"src/lib/tvgInitializer.cpp",
- "src/lib/tvgLinearGradient.cpp",
"src/lib/tvgLoader.cpp",
- "src/lib/tvgLzw.cpp",
"src/lib/tvgPaint.cpp",
"src/lib/tvgPicture.cpp",
- "src/lib/tvgRadialGradient.cpp",
"src/lib/tvgRender.cpp",
"src/lib/tvgSaver.cpp",
"src/lib/tvgScene.cpp",
"src/lib/tvgShape.cpp",
"src/lib/tvgSwCanvas.cpp",
"src/lib/tvgTaskScheduler.cpp",
+ "src/utils/tvgBezier.cpp",
+ "src/utils/tvgCompressor.cpp",
+ "src/utils/tvgStr.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
"src/loaders/svg/tvgSvgCssStyle.cpp",
"src/loaders/svg/tvgSvgLoader.cpp",
@@ -81,6 +80,7 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"../../../thirdparty/thorvg/src/lib/sw_engine",
"../../../thirdparty/thorvg/src/loaders/raw",
"../../../thirdparty/thorvg/src/loaders/svg",
+ "../../../thirdparty/thorvg/src/utils",
"../../../thirdparty/libpng",
]
)
@@ -88,7 +88,13 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
# Enable ThorVG static object linking.
env_tvg.Append(CPPDEFINES=["TVG_STATIC"])
- env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+ env.Append(
+ CPPPATH=[
+ "../../../thirdparty/thorvg/inc",
+ "../../../thirdparty/thorvg/src/lib",
+ "../../../thirdparty/thorvg/src/utils",
+ ]
+ )
env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
lib = env_tvg.Library(
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index 50a5b2da70..fecf70fe5d 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -32,7 +32,7 @@
void Decal::set_size(const Vector3 &p_size) {
size = Vector3(MAX(0.001, p_size.x), MAX(0.001, p_size.y), MAX(0.001, p_size.z));
- RS::get_singleton()->decal_set_size(decal, p_size);
+ RS::get_singleton()->decal_set_size(decal, size);
update_gizmos();
}
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index 0b0b098f65..78b02d74d5 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -458,6 +458,7 @@ MeshInstance3D *MeshInstance3D::create_debug_tangents_node() {
sm->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
sm->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
sm->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ sm->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
Ref<ArrayMesh> am;
am.instantiate();
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 13519ecec7..b821181d29 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -463,6 +463,7 @@ void RayCast3D::_update_debug_shape_material(bool p_check_collision) {
debug_material = material;
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
// Use double-sided rendering so that the RayCast can be seen if the camera is inside.
material->set_cull_mode(BaseMaterial3D::CULL_DISABLED);
material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp
index b6401832ed..4d9eeada06 100644
--- a/scene/3d/shape_cast_3d.cpp
+++ b/scene/3d/shape_cast_3d.cpp
@@ -547,6 +547,7 @@ void ShapeCast3D::_update_debug_shape_material(bool p_check_collision) {
debug_material = material;
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
// Use double-sided rendering so that the RayCast can be seen if the camera is inside.
material->set_cull_mode(BaseMaterial3D::CULL_DISABLED);
material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp
index 42b0748698..7c752545db 100644
--- a/scene/3d/voxelizer.cpp
+++ b/scene/3d/voxelizer.cpp
@@ -990,6 +990,7 @@ Ref<MultiMesh> Voxelizer::create_debug_multimesh() {
fsm->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
fsm->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
fsm->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ fsm->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
fsm->set_albedo(Color(1, 1, 1, 1));
mesh->surface_set_material(0, fsm);
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 14eed4c7a2..9a915939c2 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -948,7 +948,7 @@ void TabBar::_update_hover() {
}
}
-void TabBar::_update_cache() {
+void TabBar::_update_cache(bool p_update_hover) {
if (tabs.is_empty()) {
buttons_visible = false;
return;
@@ -1011,7 +1011,9 @@ void TabBar::_update_cache() {
buttons_visible = offset > 0 || missing_right;
if (tab_alignment == ALIGNMENT_LEFT) {
- _update_hover();
+ if (p_update_hover) {
+ _update_hover();
+ }
return;
}
@@ -1029,7 +1031,9 @@ void TabBar::_update_cache() {
}
}
- _update_hover();
+ if (p_update_hover) {
+ _update_hover();
+ }
}
void TabBar::_on_mouse_exited() {
@@ -1039,7 +1043,7 @@ void TabBar::_on_mouse_exited() {
highlight_arrow = -1;
dragging_valid_tab = false;
- _update_cache();
+ _update_cache(false);
queue_redraw();
}
@@ -1373,7 +1377,8 @@ int TabBar::get_tab_width(int p_idx) const {
style = theme_cache.tab_disabled_style;
} else if (current == p_idx) {
style = theme_cache.tab_selected_style;
- } else if (hover == p_idx) {
+ // Use the unselected style's width if the hovered one is shorter, to avoid an infinite loop when switching tabs with the mouse.
+ } else if (hover == p_idx && theme_cache.tab_hovered_style->get_minimum_size().width >= theme_cache.tab_unselected_style->get_minimum_size().width) {
style = theme_cache.tab_hovered_style;
} else {
style = theme_cache.tab_unselected_style;
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index b79c170a7b..4bce30ea52 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -148,7 +148,7 @@ private:
void _ensure_no_over_offset();
void _update_hover();
- void _update_cache();
+ void _update_cache(bool p_update_hover = true);
void _on_mouse_exited();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index d357e35c1d..ebe4f4c59d 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -789,6 +789,7 @@ Ref<Material> SceneTree::get_debug_paths_material() {
_debug_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
_debug_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
_debug_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ _debug_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
_debug_material->set_albedo(get_debug_paths_color());
debug_paths_material = _debug_material;
@@ -808,6 +809,7 @@ Ref<Material> SceneTree::get_debug_collision_material() {
line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
line_material->set_albedo(get_debug_collisions_color());
collision_material = line_material;
@@ -829,6 +831,7 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
mat->set_albedo(get_debug_collision_contact_color());
Vector3 diamond[6] = {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 3a4fe9a059..9472c75273 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -819,6 +819,9 @@ void Viewport::_process_picking() {
sorter.sort(res, rc);
}
for (int i = 0; i < rc; i++) {
+ if (is_input_handled()) {
+ break;
+ }
if (res[i].collider_id.is_valid() && res[i].collider) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
if (co && co->can_process()) {
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index b65c372fab..91d7e41cc9 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -191,7 +191,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("hover", "MenuBar", button_hover);
theme->set_stylebox("pressed", "MenuBar", button_pressed);
theme->set_stylebox("disabled", "MenuBar", button_disabled);
- theme->set_stylebox("focus", "MenuBar", focus);
theme->set_font("font", "MenuBar", Ref<Font>());
theme->set_font_size("font_size", "MenuBar", -1);
@@ -451,7 +450,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font_size("font_size", "ProgressBar", -1);
theme->set_color("font_color", "ProgressBar", control_font_hover_color);
- theme->set_color("font_shadow_color", "ProgressBar", Color(0, 0, 0));
theme->set_color("font_outline_color", "ProgressBar", Color(1, 1, 1));
theme->set_constant("outline_size", "ProgressBar", 0);
@@ -513,7 +511,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("completion_existing_color", "CodeEdit", Color(0.87, 0.87, 0.87, 0.13));
theme->set_color("completion_scroll_color", "CodeEdit", control_font_pressed_color * Color(1, 1, 1, 0.29));
theme->set_color("completion_scroll_hovered_color", "CodeEdit", control_font_pressed_color * Color(1, 1, 1, 0.4));
- theme->set_color("completion_font_color", "CodeEdit", Color(0.67, 0.67, 0.67));
theme->set_color("font_color", "CodeEdit", control_font_color);
theme->set_color("font_selected_color", "CodeEdit", Color(0, 0, 0, 0));
theme->set_color("font_readonly_color", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
@@ -680,11 +677,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Ref<StyleBoxFlat> style_popup_panel = make_flat_stylebox(style_popup_color);
style_popup_panel->set_border_width_all(2);
style_popup_panel->set_border_color(style_popup_border_color);
- Ref<StyleBoxFlat> style_popup_panel_disabled = style_popup_panel->duplicate();
- style_popup_panel_disabled->set_bg_color(style_disabled_color);
theme->set_stylebox("panel", "PopupMenu", style_popup_panel);
- theme->set_stylebox("panel_disabled", "PopupMenu", style_popup_panel_disabled);
theme->set_stylebox("hover", "PopupMenu", make_flat_stylebox(style_popup_hover_color));
theme->set_stylebox("separator", "PopupMenu", separator_horizontal);
theme->set_stylebox("labeled_separator_left", "PopupMenu", separator_horizontal);
diff --git a/scene/theme/theme_db.cpp b/scene/theme/theme_db.cpp
index c0e7636f5a..8dc9d288e2 100644
--- a/scene/theme/theme_db.cpp
+++ b/scene/theme/theme_db.cpp
@@ -38,7 +38,6 @@
#include "scene/resources/font.h"
#include "scene/resources/style_box.h"
#include "scene/resources/texture.h"
-#include "scene/resources/theme.h"
#include "scene/theme/default_theme.h"
#include "servers/text_server.h"
@@ -328,10 +327,11 @@ ThemeContext *ThemeDB::get_nearest_theme_context(Node *p_for_node) const {
// Theme item binding.
-void ThemeDB::bind_class_item(const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, ThemeItemSetter p_setter) {
+void ThemeDB::bind_class_item(Theme::DataType p_data_type, const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, ThemeItemSetter p_setter) {
ERR_FAIL_COND_MSG(theme_item_binds[p_class_name].has(p_prop_name), vformat("Failed to bind theme item '%s' in class '%s': already bound", p_prop_name, p_class_name));
ThemeItemBind bind;
+ bind.data_type = p_data_type;
bind.class_name = p_class_name;
bind.item_name = p_item_name;
bind.setter = p_setter;
@@ -339,10 +339,11 @@ void ThemeDB::bind_class_item(const StringName &p_class_name, const StringName &
theme_item_binds[p_class_name][p_prop_name] = bind;
}
-void ThemeDB::bind_class_external_item(const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, const StringName &p_type_name, ThemeItemSetter p_setter) {
+void ThemeDB::bind_class_external_item(Theme::DataType p_data_type, const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, const StringName &p_type_name, ThemeItemSetter p_setter) {
ERR_FAIL_COND_MSG(theme_item_binds[p_class_name].has(p_prop_name), vformat("Failed to bind theme item '%s' in class '%s': already bound", p_prop_name, p_class_name));
ThemeItemBind bind;
+ bind.data_type = p_data_type;
bind.class_name = p_class_name;
bind.item_name = p_item_name;
bind.type_name = p_type_name;
@@ -370,6 +371,33 @@ void ThemeDB::update_class_instance_items(Node *p_instance) {
}
}
+void ThemeDB::get_class_own_items(const StringName &p_class_name, List<ThemeItemBind> *r_list) {
+ List<StringName> class_hierarchy;
+ StringName class_name = p_class_name;
+ while (class_name != StringName()) {
+ class_hierarchy.push_front(class_name); // Put parent classes in front.
+ class_name = ClassDB::get_parent_class_nocheck(class_name);
+ }
+
+ HashSet<StringName> inherited_props;
+ for (const StringName &theme_type : class_hierarchy) {
+ HashMap<StringName, HashMap<StringName, ThemeItemBind>>::Iterator E = theme_item_binds.find(theme_type);
+ if (E) {
+ for (const KeyValue<StringName, ThemeItemBind> &F : E->value) {
+ if (inherited_props.has(F.value.item_name)) {
+ continue; // Skip inherited properties.
+ }
+ if (F.value.external || F.value.class_name != p_class_name) {
+ inherited_props.insert(F.value.item_name);
+ continue; // Track properties defined in parent classes, and skip them.
+ }
+
+ r_list->push_back(F.value);
+ }
+ }
+ }
+}
+
// Object methods.
void ThemeDB::_bind_methods() {
diff --git a/scene/theme/theme_db.h b/scene/theme/theme_db.h
index 07325e2d05..e472f4e935 100644
--- a/scene/theme/theme_db.h
+++ b/scene/theme/theme_db.h
@@ -33,6 +33,7 @@
#include "core/object/class_db.h"
#include "core/object/ref_counted.h"
+#include "scene/resources/theme.h"
#include <functional>
@@ -40,31 +41,30 @@ class Font;
class Node;
class StyleBox;
class Texture2D;
-class Theme;
class ThemeContext;
// Macros for binding theme items of this class. This information is used for the documentation, theme
// overrides, etc. This is also the basis for theme cache.
-#define BIND_THEME_ITEM(m_data_type, m_class, m_prop) \
- ThemeDB::get_singleton()->bind_class_item(get_class_static(), #m_prop, #m_prop, [](Node *p_instance) { \
- m_class *p_cast = Object::cast_to<m_class>(p_instance); \
- p_cast->theme_cache.m_prop = p_cast->get_theme_item(m_data_type, _scs_create(#m_prop)); \
+#define BIND_THEME_ITEM(m_data_type, m_class, m_prop) \
+ ThemeDB::get_singleton()->bind_class_item(m_data_type, get_class_static(), #m_prop, #m_prop, [](Node *p_instance) { \
+ m_class *p_cast = Object::cast_to<m_class>(p_instance); \
+ p_cast->theme_cache.m_prop = p_cast->get_theme_item(m_data_type, _scs_create(#m_prop)); \
})
-#define BIND_THEME_ITEM_CUSTOM(m_data_type, m_class, m_prop, m_item_name) \
- ThemeDB::get_singleton()->bind_class_item(get_class_static(), #m_prop, m_item_name, [](Node *p_instance) { \
- m_class *p_cast = Object::cast_to<m_class>(p_instance); \
- p_cast->theme_cache.m_prop = p_cast->get_theme_item(m_data_type, _scs_create(m_item_name)); \
+#define BIND_THEME_ITEM_CUSTOM(m_data_type, m_class, m_prop, m_item_name) \
+ ThemeDB::get_singleton()->bind_class_item(m_data_type, get_class_static(), #m_prop, m_item_name, [](Node *p_instance) { \
+ m_class *p_cast = Object::cast_to<m_class>(p_instance); \
+ p_cast->theme_cache.m_prop = p_cast->get_theme_item(m_data_type, _scs_create(m_item_name)); \
})
// Macro for binding theme items used by this class, but defined/binded by other classes. This is primarily used for
// the theme cache. Can also be used to list such items in documentation.
-#define BIND_THEME_ITEM_EXT(m_data_type, m_class, m_prop, m_item_name, m_type_name) \
- ThemeDB::get_singleton()->bind_class_external_item(get_class_static(), #m_prop, m_item_name, m_type_name, [](Node *p_instance) { \
- m_class *p_cast = Object::cast_to<m_class>(p_instance); \
- p_cast->theme_cache.m_prop = p_cast->get_theme_item(m_data_type, _scs_create(m_item_name), _scs_create(m_type_name)); \
+#define BIND_THEME_ITEM_EXT(m_data_type, m_class, m_prop, m_item_name, m_type_name) \
+ ThemeDB::get_singleton()->bind_class_external_item(m_data_type, get_class_static(), #m_prop, m_item_name, m_type_name, [](Node *p_instance) { \
+ m_class *p_cast = Object::cast_to<m_class>(p_instance); \
+ p_cast->theme_cache.m_prop = p_cast->get_theme_item(m_data_type, _scs_create(m_item_name), _scs_create(m_type_name)); \
})
class ThemeDB : public Object {
@@ -96,9 +96,11 @@ class ThemeDB : public Object {
// Binding of theme items to Node classes.
+public:
typedef std::function<void(Node *)> ThemeItemSetter;
struct ThemeItemBind {
+ Theme::DataType data_type;
StringName class_name;
StringName item_name;
StringName type_name;
@@ -107,6 +109,7 @@ class ThemeDB : public Object {
ThemeItemSetter setter;
};
+private:
HashMap<StringName, HashMap<StringName, ThemeItemBind>> theme_item_binds;
protected:
@@ -155,10 +158,12 @@ public:
// Theme item binding.
- void bind_class_item(const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, ThemeItemSetter p_setter);
- void bind_class_external_item(const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, const StringName &p_type_name, ThemeItemSetter p_setter);
+ void bind_class_item(Theme::DataType p_data_type, const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, ThemeItemSetter p_setter);
+ void bind_class_external_item(Theme::DataType p_data_type, const StringName &p_class_name, const StringName &p_prop_name, const StringName &p_item_name, const StringName &p_type_name, ThemeItemSetter p_setter);
void update_class_instance_items(Node *p_instance);
+ void get_class_own_items(const StringName &p_class_name, List<ThemeItemBind> *r_list);
+
// Memory management, reference, and initialization.
static ThemeDB *get_singleton();
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 70496ee65b..455a8b49a1 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -730,11 +730,11 @@ void AudioStreamRandomizer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_playback_mode", "mode"), &AudioStreamRandomizer::set_playback_mode);
ClassDB::bind_method(D_METHOD("get_playback_mode"), &AudioStreamRandomizer::get_playback_mode);
- ADD_ARRAY("streams", "stream_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "streams_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_streams_count", "get_streams_count");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_mode", PROPERTY_HINT_ENUM, "Random (Avoid Repeats),Random,Sequential"), "set_playback_mode", "get_playback_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_pitch", PROPERTY_HINT_RANGE, "1,16,0.01"), "set_random_pitch", "get_random_pitch");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_volume_offset_db", PROPERTY_HINT_RANGE, "0,40,0.01,suffix:dB"), "set_random_volume_offset_db", "get_random_volume_offset_db");
+ ADD_ARRAY("streams", "stream_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "streams_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_streams_count", "get_streams_count");
BIND_ENUM_CONSTANT(PLAYBACK_RANDOM_NO_REPEATS);
BIND_ENUM_CONSTANT(PLAYBACK_RANDOM);
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 75036b935b..eccacd3ea8 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -301,6 +301,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_face_m
face_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
face_material->set_albedo(get_debug_navigation_geometry_face_color());
face_material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ face_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_geometry_face_random_color) {
face_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
face_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
@@ -321,6 +322,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_edge_m
Ref<StandardMaterial3D> line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
line_material->set_albedo(get_debug_navigation_geometry_edge_color());
+ line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_edge_lines_xray) {
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
@@ -339,6 +341,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_face_d
face_disabled_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
face_disabled_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
face_disabled_material->set_albedo(get_debug_navigation_geometry_face_disabled_color());
+ face_disabled_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
debug_navigation_geometry_face_disabled_material = face_disabled_material;
@@ -355,6 +358,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_geometry_edge_d
Ref<StandardMaterial3D> line_disabled_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
line_disabled_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
line_disabled_material->set_albedo(get_debug_navigation_geometry_edge_disabled_color());
+ line_disabled_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_edge_lines_xray) {
line_disabled_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
@@ -374,6 +378,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_edge_connection
Ref<StandardMaterial3D> edge_connections_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
edge_connections_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
edge_connections_material->set_albedo(get_debug_navigation_edge_connection_color());
+ edge_connections_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (enabled_edge_connections_xray) {
edge_connections_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
@@ -392,6 +397,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_link_connection
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_albedo(debug_navigation_link_connection_color);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_link_connections_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
@@ -409,6 +415,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_link_connection
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_albedo(debug_navigation_link_connection_disabled_color);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_link_connections_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
@@ -427,6 +434,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_line
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_albedo(debug_navigation_agent_path_color);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_agent_paths_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
@@ -445,6 +453,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_poin
material->set_albedo(debug_navigation_agent_path_color);
material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
material->set_point_size(debug_navigation_agent_path_point_size);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
if (debug_navigation_enable_agent_paths_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
}
@@ -476,6 +485,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_obsta
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_obstacles_radius_color);
@@ -492,6 +502,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_stati
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_face_color);
@@ -508,6 +519,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_stati
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_face_color);
@@ -524,6 +536,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_stati
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
//material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
//material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_edge_color);
@@ -541,6 +554,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_stati
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
///material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
//material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_edge_color);
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index d78a59e17f..248b6cd8f8 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -150,11 +150,11 @@ public:
struct RayResult {
Vector3 position;
Vector3 normal;
- int face_index = -1;
RID rid;
ObjectID collider_id;
Object *collider = nullptr;
int shape = 0;
+ int face_index = -1;
};
virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) = 0;
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 67e48df9c9..50e14a1f37 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -157,7 +157,7 @@ void register_server_types() {
GDREGISTER_VIRTUAL_CLASS(PhysicsDirectSpaceState3DExtension)
GDREGISTER_VIRTUAL_CLASS(PhysicsServer3DRenderingServerHandler)
- GDREGISTER_NATIVE_STRUCT(PhysicsServer3DExtensionRayResult, "Vector3 position;Vector3 normal;RID rid;ObjectID collider_id;Object *collider;int shape");
+ GDREGISTER_NATIVE_STRUCT(PhysicsServer3DExtensionRayResult, "Vector3 position;Vector3 normal;RID rid;ObjectID collider_id;Object *collider;int shape;int face_index");
GDREGISTER_NATIVE_STRUCT(PhysicsServer3DExtensionShapeResult, "RID rid;ObjectID collider_id;Object *collider;int shape");
GDREGISTER_NATIVE_STRUCT(PhysicsServer3DExtensionShapeRestInfo, "Vector3 point;Vector3 normal;RID rid;ObjectID collider_id;int shape;Vector3 linear_velocity");
GDREGISTER_NATIVE_STRUCT(PhysicsServer3DExtensionMotionCollision, "Vector3 position;Vector3 normal;Vector3 collider_velocity;Vector3 collider_angular_velocity;real_t depth;int local_shape;ObjectID collider_id;RID collider;int collider_shape");
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 9da4905943..15d9ad4e8e 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -815,7 +815,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
-- Version: 0.10.0 (b8c605583fd7de73209a93a1238e1ba72cce2e8f, 2023)
+- Version: 0.10.7 (026ff4ce7eda10dd0cf80eeaef56fe3a5ed89f93, 2023)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 87125418fb..b2efe3def3 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -5,5 +5,5 @@
#define THORVG_SVG_LOADER_SUPPORT
-#define THORVG_VERSION_STRING "0.10.0"
+#define THORVG_VERSION_STRING "0.10.7"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index 897296fa9d..a5efc5ec17 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -1268,7 +1268,7 @@ public:
*
* @param[in] data A pointer to a memory location where the content of the picture file is stored.
* @param[in] size The size in bytes of the memory occupied by the @p data.
- * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
+ * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
* @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
*
* @retval Result::Success When succeed.
@@ -1278,6 +1278,7 @@ public:
*
* @warning: It's the user responsibility to release the @p data memory if the @p copy is @c true.
*
+ * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out.
* @since 0.5
*/
Result load(const char* data, uint32_t size, const std::string& mimeType, bool copy = false) noexcept;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
index 3d68b56fb8..4cee0b18e2 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
@@ -26,6 +26,8 @@
#include "tvgCommon.h"
#include "tvgRender.h"
+#include <algorithm>
+
#if 0
#include <sys/time.h>
static double timeStamp()
@@ -139,10 +141,11 @@ struct SwFill
};
struct SwRadial {
- float a11, a12, shiftX;
- float a21, a22, shiftY;
- float detSecDeriv;
- float a;
+ float a11, a12, a13;
+ float a21, a22, a23;
+ float fx, fy, fr;
+ float dx, dy, dr;
+ float invA, a;
};
union {
@@ -194,14 +197,14 @@ struct SwStroke
struct SwDashStroke
{
- SwOutline* outline;
- float curLen;
- int32_t curIdx;
- Point ptStart;
- Point ptCur;
- float* pattern;
- uint32_t cnt;
- bool curOpGap;
+ SwOutline* outline = nullptr;
+ float curLen = 0;
+ int32_t curIdx = 0;
+ Point ptStart = {0, 0};
+ Point ptCur = {0, 0};
+ float* pattern = nullptr;
+ uint32_t cnt = 0;
+ bool curOpGap = false;
};
struct SwShape
@@ -235,6 +238,7 @@ struct SwImage
bool scaled = false; //draw scaled image
};
+typedef uint8_t(*SwMask)(uint8_t s, uint8_t d, uint8_t a); //src, dst, alpha
typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t a); //src, dst, alpha
typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); //color channel join
typedef uint8_t(*SwAlpha)(uint8_t*); //blending alpha
@@ -295,7 +299,7 @@ static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a)
{
- return ((s * a + 0xff) >> 8) + ((d * ~a + 0xff) >> 8);
+ return (((s) * (a) + 0xff) >> 8) + (((d) * ~(a) + 0xff) >> 8);
}
static inline SwCoord HALF_STROKE(float width)
@@ -363,18 +367,18 @@ static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint
static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
//A + B - 2AB
- auto c1 = min(255, C1(s) + C1(d) - min(255, (C1(s) * C1(d)) << 1));
- auto c2 = min(255, C2(s) + C2(d) - min(255, (C2(s) * C2(d)) << 1));
- auto c3 = min(255, C3(s) + C3(d) - min(255, (C3(s) * C3(d)) << 1));
+ auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1));
+ auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1));
+ auto c3 = std::min(255, C3(s) + C3(d) - std::min(255, (C3(s) * C3(d)) << 1));
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// s + d
- auto c1 = min(C1(s) + C1(d), 255);
- auto c2 = min(C2(s) + C2(d), 255);
- auto c3 = min(C3(s) + C3(d), 255);
+ auto c1 = std::min(C1(s) + C1(d), 255);
+ auto c2 = std::min(C2(s) + C2(d), 255);
+ auto c3 = std::min(C3(s) + C3(d), 255);
return JOIN(255, c1, c2, c3);
}
@@ -402,27 +406,27 @@ static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
{
// if (2 * d < da) => 2 * s * d,
// else => 1 - 2 * (1 - s) * (1 - d)
- auto c1 = (C1(d) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
- auto c2 = (C2(d) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
- auto c3 = (C3(d) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
+ auto c1 = (C1(d) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+ auto c2 = (C2(d) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+ auto c3 = (C3(d) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendDarken(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// min(s, d)
- auto c1 = min(C1(s), C1(d));
- auto c2 = min(C2(s), C2(d));
- auto c3 = min(C3(s), C3(d));
+ auto c1 = std::min(C1(s), C1(d));
+ auto c2 = std::min(C2(s), C2(d));
+ auto c3 = std::min(C3(s), C3(d));
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// max(s, d)
- auto c1 = max(C1(s), C1(d));
- auto c2 = max(C2(s), C2(d));
- auto c3 = max(C3(s), C3(d));
+ auto c1 = std::max(C1(s), C1(d));
+ auto c2 = std::max(C2(s), C2(d));
+ auto c3 = std::max(C3(s), C3(d));
return JOIN(255, c1, c2, c3);
}
@@ -448,61 +452,21 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8
static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
- auto c1 = (C1(s) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
- auto c2 = (C2(s) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
- auto c3 = (C3(s) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
+ auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+ auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+ auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
//(255 - 2 * s) * (d * d) + (2 * s * b)
- auto c1 = min(255, MULTIPLY(255 - min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
- auto c2 = min(255, MULTIPLY(255 - min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
- auto c3 = min(255, MULTIPLY(255 - min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
+ auto c1 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
+ auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
+ auto c3 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
return JOIN(255, c1, c2, c3);
}
-static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
-{
- return opBlendNormal(s, d, a);
-}
-
-static inline uint32_t opMaskSubtract(uint32_t s, uint32_t d, uint8_t a)
-{
- return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
-}
-
-static inline uint32_t opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
-{
- auto t = ALPHA_BLEND(s, a);
- return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t));
-}
-
-static inline uint32_t opMaskIntersect(uint32_t s, uint32_t d, uint8_t a)
-{
- return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
-}
-
-static inline uint32_t opMaskPreAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
- return opBlendPreNormal(s, d, a);
-}
-
-static inline uint32_t opMaskPreSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
- return ALPHA_BLEND(d, IA(s));
-}
-
-static inline uint32_t opMaskPreDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
- return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, IA(s));
-}
-
-static inline uint32_t opMaskPreIntersect(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
-{
- return ALPHA_BLEND(d, MULTIPLY(a, IA(s)));
-}
int64_t mathMultiply(int64_t a, int64_t b);
int64_t mathDivide(int64_t a, int64_t b);
@@ -551,13 +515,19 @@ void imageFree(SwImage* image);
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
void fillReset(SwFill* fill);
void fillFree(SwFill* fill);
+
//OPTIMIZE_ME: Skip the function pointer access
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t opacity); //composite masking ver.
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t opacity); //direct masking ver.
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
-void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //masking ver.
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
+
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a); //composite masking ver.
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) ; //direct masking ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //masking ver.
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
SwRleData* rleRender(const SwBBox* bbox);
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
index 1c6eb4e428..cede9e6eb7 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
@@ -22,16 +22,46 @@
#include "tvgMath.h"
#include "tvgSwCommon.h"
-
+#include "tvgFill.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
+#define RADIAL_A_THRESHOLD 0.0005f
#define GRADIENT_STOP_SIZE 1024
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
+/*
+ * quadratic equation with the following coefficients (rx and ry defined in the _calculateCoefficients()):
+ * A = a // fill->radial.a
+ * B = 2 * (dr * fr + rx * dx + ry * dy)
+ * C = fr^2 - rx^2 - ry^2
+ * Derivatives are computed with respect to dx.
+ * This procedure aims to optimize and eliminate the need to calculate all values from the beginning
+ * for consecutive x values with a constant y. The Taylor series expansions are computed as long as
+ * its terms are non-zero.
+ */
+static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet)
+{
+ auto radial = &fill->radial;
+
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+ b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA;
+ deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA;
+
+ auto rr = rx * rx + ry * ry;
+ auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA;
+ auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
+
+ det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
+ deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr;
+ deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
+}
+
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
{
@@ -146,46 +176,62 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
{
- float radius, cx, cy;
- if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
- if (radius < FLT_EPSILON) return true;
+ auto cx = P(radial)->cx;
+ auto cy = P(radial)->cy;
+ auto r = P(radial)->r;
+ auto fx = P(radial)->fx;
+ auto fy = P(radial)->fy;
+ auto fr = P(radial)->fr;
+
+ if (r < FLT_EPSILON) return true;
+
+ fill->radial.dr = r - fr;
+ fill->radial.dx = cx - fx;
+ fill->radial.dy = cy - fy;
+ fill->radial.fr = fr;
+ fill->radial.fx = fx;
+ fill->radial.fy = fy;
+ fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
+
+ //This condition fulfills the SVG 1.1 std:
+ //the focal point, if outside the end circle, is moved to be on the end circle
+ //See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
+ if (fill->radial.a < 0) {
+ auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy);
+ fill->radial.fx = cx + r * (fx - cx) / dist;
+ fill->radial.fy = cy + r * (fy - cy) / dist;
+ fill->radial.dx = cx - fill->radial.fx;
+ fill->radial.dy = cy - fill->radial.fy;
+ fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
+ }
- float invR = 1.0f / radius;
- fill->radial.shiftX = -cx;
- fill->radial.shiftY = -cy;
- fill->radial.a = radius;
+ if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
auto gradTransform = radial->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
- if (isTransformation) {
- if (transform) gradTransform = mathMultiply(transform, &gradTransform);
- } else if (transform) {
- gradTransform = *transform;
- isTransformation = true;
+ if (transform) {
+ if (isTransformation) gradTransform = mathMultiply(transform, &gradTransform);
+ else {
+ gradTransform = *transform;
+ isTransformation = true;
+ }
}
if (isTransformation) {
Matrix invTransform;
if (!mathInverse(&gradTransform, &invTransform)) return false;
-
- fill->radial.a11 = invTransform.e11 * invR;
- fill->radial.a12 = invTransform.e12 * invR;
- fill->radial.shiftX += invTransform.e13;
- fill->radial.a21 = invTransform.e21 * invR;
- fill->radial.a22 = invTransform.e22 * invR;
- fill->radial.shiftY += invTransform.e23;
- fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
-
- fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
+ fill->radial.a11 = invTransform.e11;
+ fill->radial.a12 = invTransform.e12;
+ fill->radial.a13 = invTransform.e13;
+ fill->radial.a21 = invTransform.e21;
+ fill->radial.a22 = invTransform.e22;
+ fill->radial.a23 = invTransform.e23;
} else {
- fill->radial.a11 = fill->radial.a22 = invR;
- fill->radial.a12 = fill->radial.a21 = 0.0f;
- fill->radial.detSecDeriv = 2.0f * invR * invR;
+ fill->radial.a11 = fill->radial.a22 = 1.0f;
+ fill->radial.a12 = fill->radial.a13 = 0.0f;
+ fill->radial.a21 = fill->radial.a23 = 0.0f;
}
- fill->radial.shiftX *= invR;
- fill->radial.shiftY *= invR;
-
return true;
}
@@ -233,77 +279,181 @@ static inline uint32_t _pixel(const SwFill* fill, float pos)
/* External Class Implementation */
/************************************************************************/
+
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
{
- auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
- auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+ //edge case
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+ if (opacity == 255) {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ *dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp));
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ *dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp)));
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ }
+ } else {
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ if (opacity == 255) {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp));
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ } else {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp)));
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ }
+ }
+}
- // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
- auto detSecondDerivative = fill->radial.detSecDeriv;
- // detFirstDerivative = d(det)/dx
- auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
- auto det = rx * rx + ry * ry;
- if (opacity == 255) {
- for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
- *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, alpha(cmp));
- det += detFirstDerivative;
- detFirstDerivative += detSecondDerivative;
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
+{
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ *dst = op(_pixel(fill, x0), *dst, a);
+ rx += radial->a11;
+ ry += radial->a21;
}
} else {
- for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
- *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, MULTIPLY(opacity, alpha(cmp)));
- det += detFirstDerivative;
- detFirstDerivative += detSecondDerivative;
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ *dst = op(_pixel(fill, sqrtf(det) - b), *dst, a);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
}
}
}
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
{
- auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
- auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
-
- // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
- auto detSecondDerivative = fill->radial.detSecDeriv;
- // detFirstDerivative = d(det)/dx
- auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
- auto det = rx * rx + ry * ry;
-
- for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
- *dst = op(_pixel(fill, sqrtf(det)), *dst, a);
- det += detFirstDerivative;
- detFirstDerivative += detSecondDerivative;
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto src = MULTIPLY(a, A(_pixel(fill, x0)));
+ *dst = maskOp(src, *dst, ~src);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b)));
+ *dst = maskOp(src, *dst, ~src);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
}
}
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
{
- auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
- auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto src = MULTIPLY(A(A(_pixel(fill, x0))), a);
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
+ auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ }
+}
- // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
- auto detSecondDerivative = fill->radial.detSecDeriv;
- // detFirstDerivative = d(det)/dx
- auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
- auto det = rx * rx + ry * ry;
- if (a == 255) {
- for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
- auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
- *dst = op2(tmp, *dst, 255);
- det += detFirstDerivative;
- detFirstDerivative += detSecondDerivative;
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+{
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+ if (a == 255) {
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto tmp = op(_pixel(fill, x0), *dst, 255);
+ *dst = op2(tmp, *dst, 255);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto tmp = op(_pixel(fill, x0), *dst, 255);
+ auto tmp2 = op2(tmp, *dst, 255);
+ *dst = INTERPOLATE(tmp2, *dst, a);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
}
} else {
- for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
- auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
- auto tmp2 = op2(tmp, *dst, 255);
- *dst = INTERPOLATE(tmp2, *dst, a);
- det += detFirstDerivative;
- detFirstDerivative += detSecondDerivative;
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+ if (a == 255) {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
+ *dst = op2(tmp, *dst, 255);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ } else {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
+ auto tmp2 = op2(tmp, *dst, 255);
+ *dst = INTERPOLATE(tmp2, *dst, a);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
}
}
}
@@ -383,6 +533,95 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
}
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
+{
+ //Rotation
+ float rx = x + 0.5f;
+ float ry = y + 0.5f;
+ float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+ float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+ if (mathZero(inc)) {
+ auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ *dst = maskOp(src, *dst, ~src);
+ }
+ return;
+ }
+
+ auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+ auto vMin = -vMax;
+ auto v = t + (inc * len);
+
+ //we can use fixed point math
+ if (v < vMax && v > vMin) {
+ auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+ auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+ for (uint32_t j = 0; j < len; ++j, ++dst) {
+ auto src = MULTIPLY(_fixedPixel(fill, t2), a);
+ *dst = maskOp(src, *dst, ~src);
+ t2 += inc2;
+ }
+ //we have to fallback to float math
+ } else {
+ uint32_t counter = 0;
+ while (counter++ < len) {
+ auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
+ *dst = maskOp(src, *dst, ~src);
+ ++dst;
+ t += inc;
+ }
+ }
+}
+
+
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
+{
+ //Rotation
+ float rx = x + 0.5f;
+ float ry = y + 0.5f;
+ float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+ float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+ if (mathZero(inc)) {
+ auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
+ src = MULTIPLY(src, a);
+ for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ }
+ return;
+ }
+
+ auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+ auto vMin = -vMax;
+ auto v = t + (inc * len);
+
+ //we can use fixed point math
+ if (v < vMax && v > vMin) {
+ auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+ auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+ for (uint32_t j = 0; j < len; ++j, ++dst, ++cmp) {
+ auto src = MULTIPLY(a, A(_fixedPixel(fill, t2)));
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ t2 += inc2;
+ }
+ //we have to fallback to float math
+ } else {
+ uint32_t counter = 0;
+ while (counter++ < len) {
+ auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ ++cmp;
+ t += inc;
+ }
+ }
+}
+
+
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
{
//Rotation
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
index 4829a8c81d..fb8581b412 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
@@ -93,7 +93,7 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
outline->types.push(SW_CURVE_TYPE_POINT);
}
- outline->pts.push(outline->pts.data[0]);
+ outline->pts.push(outline->pts[0]);
outline->types.push(SW_CURVE_TYPE_POINT);
outline->cntrs.push(outline->pts.count - 1);
outline->closed.push(true);
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
index bd32bf0b23..8fd54c2a4f 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
@@ -39,6 +39,16 @@ constexpr auto DOWN_SCALE_TOLERANCE = 0.5f;
struct FillLinear
{
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a)
+ {
+ fillLinear(fill, dst, y, x, len, op, a);
+ }
+
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a)
+ {
+ fillLinear(fill, dst, y, x, len, cmp, op, a);
+ }
+
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
{
fillLinear(fill, dst, y, x, len, op, a);
@@ -58,6 +68,16 @@ struct FillLinear
struct FillRadial
{
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a)
+ {
+ fillRadial(fill, dst, y, x, len, op, a);
+ }
+
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a)
+ {
+ fillRadial(fill, dst, y, x, len, cmp, op, a);
+ }
+
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
{
fillRadial(fill, dst, y, x, len, op, a);
@@ -75,9 +95,6 @@ struct FillRadial
};
-static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity = 255);
-
-
static inline uint8_t _alpha(uint8_t* a)
{
return *a;
@@ -148,70 +165,70 @@ static inline bool _matting(const SwSurface* surface)
else return false;
}
-
-static inline bool _masking(const SwSurface* surface)
+static inline uint8_t _opMaskNone(uint8_t s, TVG_UNUSED uint8_t d, TVG_UNUSED uint8_t a)
{
- if ((int)surface->compositor->method >= (int)CompositeMethod::AddMask) return true;
- else return false;
+ return s;
}
-
-static inline uint32_t _opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a)
{
- return s + ALPHA_BLEND(d, a);
+ return s + MULTIPLY(d, a);
}
-static inline uint32_t _opMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskSubtract(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a)
{
- return ALPHA_BLEND(d, a);
+ return MULTIPLY(s, 255 - d);
}
-static inline uint32_t _opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskIntersect(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a)
{
- return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, a);
+ return MULTIPLY(s, d);
}
-static inline uint32_t _opAMaskAdd(uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskDifference(uint8_t s, uint8_t d, uint8_t a)
{
- return INTERPOLATE(s, d, a);
+ return MULTIPLY(s, 255 - d) + MULTIPLY(d, a);
}
-static inline uint32_t _opAMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a)
+static inline bool _direct(CompositeMethod method)
{
- return ALPHA_BLEND(d, IA(ALPHA_BLEND(s, a)));
-}
-
-
-static inline uint32_t _opAMaskDifference(uint32_t s, uint32_t d, uint8_t a)
-{
- auto t = ALPHA_BLEND(s, a);
- return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t));
+ //subtract & Intersect allows the direct composition
+ if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true;
+ return false;
}
-static inline SwBlender _getMaskOp(CompositeMethod method)
+static inline SwMask _getMaskOp(CompositeMethod method)
{
switch (method) {
case CompositeMethod::AddMask: return _opMaskAdd;
case CompositeMethod::SubtractMask: return _opMaskSubtract;
case CompositeMethod::DifferenceMask: return _opMaskDifference;
+ case CompositeMethod::IntersectMask: return _opMaskIntersect;
default: return nullptr;
}
}
-static inline SwBlender _getAMaskOp(CompositeMethod method)
+static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const SwBBox& region)
{
- switch (method) {
- case CompositeMethod::AddMask: return _opAMaskAdd;
- case CompositeMethod::SubtractMask: return _opAMaskSubtract;
- case CompositeMethod::DifferenceMask: return _opAMaskDifference;
- default: return nullptr;
+ auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
+ *dst = *src + MULTIPLY(*dst, ~*src);
+ }
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
}
+ return true;
}
@@ -284,78 +301,57 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
/* Rect */
/************************************************************************/
-static void _rasterMaskedRectDup(SwSurface* surface, const SwBBox& region, SwBlender opMask, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * surface->compositor->image.stride + region.min.x); //compositor buffer
auto cstride = surface->compositor->image.stride;
- auto color = surface->join(r, g, b, a);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer
auto ialpha = 255 - a;
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
for (uint32_t x = 0; x < w; ++x, ++cmp) {
- *cmp = opMask(color, *cmp, ialpha);
+ *cmp = maskOp(a, *cmp, ialpha);
}
cbuffer += cstride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterMaskedRectInt(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterDirectMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
- auto cstride = surface->compositor->image.stride;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x); //compositor buffer
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x);
- if (y == region.min.y) {
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto tmp = cmp;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- for (uint32_t i = 0; i < w; ++i, ++tmp) {
- *tmp = ALPHA_BLEND(*tmp, a);
- }
- x += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cmp += cstride;
- }
- y += (h - 1);
- } else {
- rasterPixel32(cmp, 0x00000000, 0, w);
- cmp += cstride;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto cmp = cbuffer;
+ auto dst = dbuffer;
+ for (uint32_t x = 0; x < w; ++x, ++cmp, ++dst) {
+ auto tmp = maskOp(a, *cmp, 0); //not use alpha.
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
+ cbuffer += surface->compositor->image.stride;
+ dbuffer += surface->stride;
}
+ return true;
}
static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
- //32bit channels composition
- if (surface->channelSize != sizeof(uint32_t)) return false;
-
- TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.max.y, region.min.y);
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterMaskedRectInt(surface, region, r, g, b, a);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterMaskedRectDup(surface, region, opMask, r, g, b, a);
- } else {
- return false;
- }
+ TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, region, maskOp, r, g, b, a);
+ else return _rasterCompositeMaskedRect(surface, region, maskOp, r, g, b, a);
+ return false;
}
@@ -444,7 +440,7 @@ static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r
//8bits grayscale
if (surface->channelSize == sizeof(uint8_t)) {
for (uint32_t y = 0; y < h; ++y) {
- rasterGrayscale8(surface->buf8, 255, region.min.y * surface->stride + region.min.x, w);
+ rasterGrayscale8(surface->buf8, 255, (y + region.min.y) * surface->stride + region.min.x, w);
}
return true;
}
@@ -471,53 +467,44 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin
/* Rle */
/************************************************************************/
-static void _rasterMaskedRleDup(SwSurface* surface, SwRleData* rle, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto cstride = surface->compositor->image.stride;
- auto color = surface->join(r, g, b, a);
- uint32_t src;
+ uint8_t src;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x];
- if (span->coverage == 255) src = color;
- else src = ALPHA_BLEND(color, span->coverage);
- auto ialpha = IA(src);
+ if (span->coverage == 255) src = a;
+ else src = MULTIPLY(a, span->coverage);
+ auto ialpha = 255 - src;
for (auto x = 0; x < span->len; ++x, ++cmp) {
*cmp = maskOp(src, *cmp, ialpha);
}
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterMaskedRleInt(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto cstride = surface->compositor->image.stride;
- auto color = surface->join(r, g, b, a);
- uint32_t src;
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * cstride];
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- if (span->coverage == 255) src = color;
- else src = ALPHA_BLEND(color, span->coverage);
- auto alpha = A(src);
- for (uint32_t i = 0; i < span->len; ++i) {
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], alpha);
- }
- x += span->len;
- ++span;
- } else {
- cmp[x] = 0;
- ++x;
- }
+ uint8_t src;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto cmp = &cbuffer[span->y * cstride + span->x];
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ if (span->coverage == 255) src = a;
+ else src = MULTIPLY(a, span->coverage);
+ for (auto x = 0; x < span->len; ++x, ++cmp, ++dst) {
+ auto tmp = maskOp(src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
}
+ return true;
}
@@ -525,20 +512,13 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
{
TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method);
- //32bit channels composition
- if (surface->channelSize != sizeof(uint32_t)) return false;
-
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterMaskedRleInt(surface, rle, r, g, b, a);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterMaskedRleDup(surface, rle, opMask, r, g, b, a);
- } else {
- return false;
- }
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, r, g, b, a);
+ else return _rasterCompositeMaskedRle(surface, rle, maskOp, r, g, b, a);
+ return false;
}
@@ -644,7 +624,15 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r,
//8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) {
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len);
+ if (span->coverage == 255) {
+ rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len);
+ } else {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = span->coverage + MULTIPLY(*dst, ialpha);
+ }
+ }
}
}
return true;
@@ -669,28 +657,11 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
/************************************************************************/
-/* RLE Transformed Image */
-/************************************************************************/
-
-static bool _transformedRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, uint8_t opacity)
-{
- auto ret = _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
-
- //Masking Composition
- if (_compositing(surface) && _masking(surface)) {
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
- }
-
- return ret;
-
-}
-
-
-/************************************************************************/
/* RLE Scaled Image */
/************************************************************************/
-static void _rasterScaledMaskedRleImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
@@ -700,82 +671,77 @@ static void _rasterScaledMaskedRleImageDup(SwSurface* surface, const SwImage* im
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
- auto cmp = &surface->compositor->image.buf32[span->y * surface->compositor->image.stride + span->x];
+ auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = maskOp(src, *cmp, 255);
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ *cmp = maskOp(src, *cmp, ~src);
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = amaskOp(src, *cmp, a);
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = MULTIPLY(src, a);
+ *cmp = maskOp(tmp, *cmp, ~tmp);
}
}
}
+ return true;
}
-static void _rasterScaledMaskedRleImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
+static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
- auto cstride = surface->compositor->image.stride;
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * cstride];
- for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- auto sy = span->y * itransform->e22 + itransform->e23;
- if ((uint32_t)sy >= image->h) continue;
- auto alpha = MULTIPLY(span->coverage, opacity);
- if (alpha == 255) {
- for (uint32_t i = 0; i < span->len; ++i) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(src));
- }
- } else {
- for (uint32_t i = 0; i < span->len; ++i) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(ALPHA_BLEND(src, alpha)));
- }
- }
- x += span->len - 1;
- ++span;
- } else {
- cmp[x] = 0;
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ auto a = MULTIPLY(span->coverage, opacity);
+ if (a == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = maskOp(src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = maskOp(MULTIPLY(src, a), *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
}
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-
+#endif
static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
+#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterScaledMaskedRleImageInt(surface, image, itransform, region, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterScaledMaskedRleImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
+
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
+ else return _rasterCompositeScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
+#endif
+ return false;
}
@@ -895,6 +861,11 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported scaled rle image!");
+ return false;
+ }
+
Matrix itransform;
if (transform) {
@@ -917,78 +888,72 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
/* RLE Direct Image */
/************************************************************************/
-static void _rasterDirectMaskedRleImageDup(SwSurface* surface, const SwImage* image, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
{
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto ctride = surface->compositor->image.stride;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
- auto src = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto cmp = &cbuffer[span->y * ctride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
- *cmp = maskOp(*src, *cmp, IA(*src));
+ *cmp = maskOp(*src, *cmp, ~*src);
}
} else {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
- *cmp = amaskOp(*src, *cmp, alpha);
+ auto tmp = MULTIPLY(*src, alpha);
+ *cmp = maskOp(*src, *cmp, ~tmp);
}
}
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterDirectMaskedRleImageInt(SwSurface* surface, const SwImage* image, uint8_t opacity)
+static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
{
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto ctride = surface->compositor->image.stride;
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * ctride];
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- auto alpha = MULTIPLY(span->coverage, opacity);
- auto src = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
- if (alpha == 255) {
- for (uint32_t i = 0; i < span->len; ++i, ++src) {
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(*src));
- }
- } else {
- for (uint32_t i = 0; i < span->len; ++i, ++src) {
- auto t = ALPHA_BLEND(*src, alpha);
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(t));
- }
- }
- x += span->len;
- ++span;
- } else {
- cmp[x] = 0;
- ++x;
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto cmp = &cbuffer[span->y * ctride + span->x];
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ auto alpha = MULTIPLY(span->coverage, opacity);
+ if (alpha == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(*src, *cmp, 0); //not use alpha
+ *dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(MULTIPLY(*src, alpha), *cmp, 0); //not use alpha
+ *dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
}
}
}
+ return true;
}
-
+#endif
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
+#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterDirectMaskedRleImageInt(surface, image, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterDirectMaskedRleImageDup(surface, image, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, opacity);
+ else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, opacity);
+#endif
+ return false;
}
@@ -1076,6 +1041,11 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint
static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale rle image!");
+ return false;
+ }
+
if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity);
else return _rasterDirectMaskedRleImage(surface, image, opacity);
@@ -1089,40 +1059,17 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op
/************************************************************************/
-/* Transformed Image */
-/************************************************************************/
-
-static bool _transformedImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
-{
- auto ret = _rasterTexmapPolygon(surface, image, transform, &region, opacity);
-
- //Masking Composition
- if (_compositing(surface) && _masking(surface)) {
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
- }
-
- return ret;
-}
-
-
-static bool _transformedImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
-{
- //TODO: Not completed for all cases.
- return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity);
-}
-
-
-/************************************************************************/
/*Scaled Image */
/************************************************************************/
-static void _rasterScaledMaskedImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
for (auto y = region.min.y; y < region.max.y; ++y) {
auto sy = y * itransform->e22 + itransform->e23;
@@ -1132,92 +1079,73 @@ static void _rasterScaledMaskedImageDup(SwSurface* surface, const SwImage* image
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = maskOp(src, *cmp, IA(src));
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ *cmp = maskOp(src, *cmp, ~src);
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = amaskOp(src, *cmp, opacity);
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = MULTIPLY(src, opacity);
+ *cmp = maskOp(tmp, *cmp, ~tmp);
}
}
cbuffer += cstride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterScaledMaskedImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
+
+static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
- auto h = static_cast<uint32_t>(region.max.y - region.min.y);
- auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x);
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- if (y == region.min.y) {
- auto cbuffer2 = cbuffer;
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto sy = y2 * itransform->e22 + itransform->e23;
- if ((uint32_t)sy >= image->h) continue;
- auto tmp = cbuffer2;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- if (opacity == 255) {
- for (uint32_t i = 0; i < w; ++i, ++tmp) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *tmp = ALPHA_BLEND(*tmp, A(src));
- }
- } else {
- for (uint32_t i = 0; i < w; ++i, ++tmp) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
- *tmp = ALPHA_BLEND(*tmp, A(src));
- }
- }
- x += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cbuffer2 += cstride;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto cmp = cbuffer;
+ auto dst = dbuffer;
+ if (opacity == 255) {
+ for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = maskOp(src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
- y += (h - 1);
} else {
- auto tmp = cbuffer;
- for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x, ++tmp) {
- *tmp = 0;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = MULTIPLY(src, opacity);
+ auto tmp2 = maskOp(tmp, *cmp, 0); //not use alpha
+ *dst = tmp2 + MULTIPLY(*dst, ~tmp2);
}
}
cbuffer += cstride;
+ dbuffer += surface->stride;
}
+ return true;
}
-
+#endif
static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
+#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterScaledMaskedImageInt(surface, image, itransform, region, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterScaledMaskedImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
-
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
+ else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
+#endif
+ return false;
}
@@ -1329,6 +1257,11 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
+ return false;
+ }
+
Matrix itransform;
if (transform) {
@@ -1351,125 +1284,135 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix*
/* Direct Image */
/************************************************************************/
-static void _rasterDirectMaskedImageDup(SwSurface* surface, const SwImage* image, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer
- auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer
+ auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
- *cmp = maskOp(*src, *cmp, IA(*src));
+ *cmp = maskOp(*src, *cmp, ~*src);
}
} else {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
- *cmp = amaskOp(*src, *cmp, opacity);
+ auto tmp = MULTIPLY(*src, opacity);
+ *cmp = maskOp(tmp, *cmp, ~tmp);
}
}
cbuffer += cstride;
sbuffer += image->stride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterDirectMaskedImageInt(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
+static bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x);
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- if (y == region.min.y) {
- auto cbuffer2 = cbuffer;
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto tmp = cbuffer2;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- auto src = &image->buf32[(y2 + image->oy) * image->stride + (x + image->ox)];
- if (opacity == 255) {
- for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
- *tmp = ALPHA_BLEND(*tmp, A(*src));
- }
- } else {
- for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
- auto t = ALPHA_BLEND(*src, opacity);
- *tmp = ALPHA_BLEND(*tmp, A(t));
- }
- }
- x += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cbuffer2 += cstride;
+
+ auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer
+ auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto cmp = cbuffer;
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ if (opacity == 255) {
+ for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(*src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
- y += (h - 1);
} else {
- rasterPixel32(cbuffer, 0x00000000, 0, surface->compositor->bbox.max.x - surface->compositor->bbox.min.x);
+ for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(MULTIPLY(*src, opacity), *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ }
}
cbuffer += cstride;
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
}
+ return true;
}
-
+#endif
static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
- TVGLOG("SW_ENGINE", "Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
+ TVGERR("SW_ENGINE", "Not Supported: Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterDirectMaskedImageInt(surface, image, region, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterDirectMaskedImageDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+#if 0 //Enable it when GRAYSCALE image is supported
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectDirectMaskedImage(surface, image, region, maskOp, opacity);
+ else return _rasterCompositeDirectMaskedImage(surface, image, region, maskOp, opacity);
+#endif
+ return false;
}
static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
- auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method);
-
- TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
-
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
- for (uint32_t y = 0; y < h; ++y) {
- auto dst = buffer;
- auto cmp = cbuffer;
- auto src = sbuffer;
- if (opacity == 255) {
- for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
- auto tmp = ALPHA_BLEND(*src, alpha(cmp));
- *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
+
+ //32 bits
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ if (opacity == 255) {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, alpha(cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ }
}
- } else {
- for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
- auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp)));
- *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride * csize;
+ sbuffer += image->stride;
+ }
+ //8 bits
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ if (opacity == 255) {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ *dst = MULTIPLY(A(*src), alpha(cmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ *dst = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp)));
+ }
}
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride * csize;
+ sbuffer += image->stride;
}
- buffer += surface->stride;
- cbuffer += surface->compositor->image.stride * csize;
- sbuffer += image->stride;
}
return true;
}
@@ -1477,6 +1420,11 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale image!");
+ return false;
+ }
+
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
@@ -1504,6 +1452,11 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale image!");
+ return false;
+ }
+
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
@@ -1549,12 +1502,12 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* trans
if (image->rle) {
if (image->direct) return _directRleImage(surface, image, opacity);
else if (image->scaled) return _scaledRleImage(surface, image, transform, region, opacity);
- else return _transformedRleImage(surface, image, transform, opacity);
+ else return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
//Whole Image
} else {
if (image->direct) return _directImage(surface, image, region, opacity);
else if (image->scaled) return _scaledImage(surface, image, transform, region, opacity);
- else return _transformedImage(surface, image, transform, region, opacity);
+ else return _rasterTexmapPolygon(surface, image, transform, &region, opacity);
}
}
@@ -1564,52 +1517,36 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* trans
/************************************************************************/
template<typename fillMethod>
-static void _rasterGradientMaskedRectDup(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwBlender maskOp)
+static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwMask maskOp)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255);
cbuffer += surface->stride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
template<typename fillMethod>
-static void _rasterGradientMaskedRectInt(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+static bool _rasterDirectGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwMask maskOp)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x);
- if (y == region.min.y) {
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto tmp = cmp;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- fillMethod()(fill, tmp, y2, x, w, opMaskPreIntersect, 255);
- x += w;
- tmp += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cmp += cstride;
- }
- y += (h - 1);
- } else {
- rasterPixel32(cmp, 0x00000000, 0, surface->compositor->bbox.max.x -surface->compositor->bbox.min.x);
- cmp += cstride;
- }
+ for (uint32_t y = 0; y < h; ++y) {
+ fillMethod()(fill, dbuffer, region.min.y + y, region.min.x, w, cbuffer, maskOp, 255);
+ cbuffer += cstride;
+ dbuffer += surface->stride;
}
+ return true;
}
@@ -1618,16 +1555,14 @@ static bool _rasterGradientMaskedRect(SwSurface* surface, const SwBBox& region,
{
auto method = surface->compositor->method;
- TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
+ TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- if (method == CompositeMethod::AddMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreAdd);
- else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreSubtract);
- else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreDifference);
- else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRectInt<fillMethod>(surface, region, fill);
- else return false;
+ auto maskOp = _getMaskOp(method);
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
+ if (_direct(method)) return _rasterDirectGradientMaskedRect<fillMethod>(surface, region, fill, maskOp);
+ else return _rasterCompositeGradientMaskedRect<fillMethod>(surface, region, fill, maskOp);
+
+ return false;
}
@@ -1719,8 +1654,6 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region,
static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{
- if (fill->radial.a < FLT_EPSILON) return false;
-
if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRect<FillRadial>(surface, region, fill);
else return _rasterGradientMaskedRect<FillRadial>(surface, region, fill);
@@ -1734,64 +1667,54 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
}
-
/************************************************************************/
/* Rle Gradient */
/************************************************************************/
template<typename fillMethod>
-static void _rasterGradientMaskedRleDup(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwBlender maskOp)
+static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x];
fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage);
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
template<typename fillMethod>
-static void _rasterGradientMaskedRleInt(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32;
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * cstride];
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- fillMethod()(fill, cmp, span->y, span->x, span->len, opMaskIntersect, span->coverage);
- x += span->len;
- ++span;
- } else {
- cmp[x] = 0;
- ++x;
- }
- }
+ auto cbuffer = surface->compositor->image.buf8;
+ auto dbuffer = surface->buf8;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto cmp = &cbuffer[span->y * cstride + span->x];
+ auto dst = &dbuffer[span->y * surface->stride + span->x];
+ fillMethod()(fill, dst, span->y, span->x, span->len, cmp, maskOp, span->coverage);
}
+ return true;
}
template<typename fillMethod>
static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
{
- TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)surface->compositor->method);
-
auto method = surface->compositor->method;
- if (method == CompositeMethod::AddMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskAdd);
- else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskSubtract);
- else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskDifference);
- else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRleInt<fillMethod>(surface, rle, fill);
- else return false;
+ TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)method);
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
+ auto maskOp = _getMaskOp(method);
+
+ if (_direct(method)) return _rasterDirectGradientMaskedRle<fillMethod>(surface, rle, fill, maskOp);
+ else return _rasterCompositeGradientMaskedRle<fillMethod>(surface, rle, fill, maskOp);
+ return false;
}
@@ -1832,10 +1755,19 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
{
auto span = rle->spans;
- for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buf32[span->y * surface->stride + span->x];
- if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, 255);
- else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendNormal, span->coverage);
+ //32 bits
+ if (surface->channelSize == sizeof(uint32_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, 255);
+ else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendNormal, span->coverage);
+ }
+ //8 bits
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, 255);
+ }
}
return true;
}
@@ -1846,11 +1778,22 @@ static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, co
{
auto span = rle->spans;
- for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buf32[span->y * surface->stride + span->x];
- if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendSrcOver, 255);
- else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendInterp, span->coverage);
+ //32 bits
+ if (surface->channelSize == sizeof(uint32_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendSrcOver, 255);
+ else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendInterp, span->coverage);
+ }
+ //8 bits
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskNone, 255);
+ else fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage);
+ }
}
+
return true;
}
@@ -1874,7 +1817,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c
static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
{
- if (!rle || fill->radial.a < FLT_EPSILON) return false;
+ if (!rle) return false;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
@@ -2018,11 +1961,6 @@ void rasterPremultiply(Surface* surface)
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
{
- if (surface->channelSize == sizeof(uint8_t)) {
- TVGERR("SW_ENGINE", "Not supported grayscale gradient!");
- return false;
- }
-
if (!shape->fill) return false;
if (shape->fastTrack) {
@@ -2038,11 +1976,6 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
{
- if (surface->channelSize == sizeof(uint8_t)) {
- TVGERR("SW_ENGINE", "Not supported grayscale gradient!");
- return false;
- }
-
if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
@@ -2059,7 +1992,6 @@ bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8
g = MULTIPLY(g, a);
b = MULTIPLY(b, a);
}
-
if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a);
else return _rasterRle(surface, shape->rle, r, g, b, a);
}
@@ -2079,19 +2011,10 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity)
{
- if (surface->channelSize == sizeof(uint8_t)) {
- TVGERR("SW_ENGINE", "Not supported grayscale image!");
- return false;
- }
-
//Verify Boundary
if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return false;
- //TOOD: switch (image->format)
- //TODO: case: _rasterRGBImageMesh()
- //TODO: case: _rasterGrayscaleImageMesh()
- //TODO: case: _rasterAlphaImageMesh()
- if (mesh && mesh->triangleCnt > 0) return _transformedImageMesh(surface, image, mesh, transform, &bbox, opacity);
+ if (mesh && mesh->triangleCnt > 0) return _rasterTexmapPolygonMesh(surface, image, mesh, transform, &bbox, opacity);
else return _rasterImage(surface, image, transform, bbox, opacity);
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
index 4b30c52ea3..698ab37da2 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
@@ -70,190 +70,17 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
}
-static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag)
+static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{
- float _dudx = dudx, _dvdx = dvdx;
- float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
- float _xa = xa, _xb = xb, _ua = ua, _va = va;
- auto sbuf = image->buf32;
- int32_t sw = static_cast<int32_t>(image->stride);
- int32_t sh = image->h;
- int32_t x1, x2, ar, ab, iru, irv, px, ay;
- int32_t vv = 0, uu = 0;
- int32_t minx = INT32_MAX, maxx = INT32_MIN;
- float dx, u, v, iptr;
- auto cbuffer = surface->compositor->image.buf32;
- SwSpan* span = nullptr; //used only when rle based.
-
- if (!_arrange(image, region, yStart, yEnd)) return;
-
- //Clear out of the Polygon vertical ranges
- auto size = surface->compositor->bbox.max.x - surface->compositor->bbox.min.x;
- if (dirFlag == 1) { //left top case.
- for(int y = surface->compositor->bbox.min.y; y < yStart; ++y) {
- rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
- }
- }
- if (dirFlag == 4) { //right bottom case.
- for(int y = yEnd; y < surface->compositor->bbox.max.y; ++y) {
- rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
- }
- }
-
- //Loop through all lines in the segment
- uint32_t spanIdx = 0;
-
- if (region) {
- minx = region->min.x;
- maxx = region->max.x;
- } else {
- span = image->rle->spans;
- while (span->y < yStart) {
- ++span;
- ++spanIdx;
- }
- }
-
- for (int32_t y = yStart; y < yEnd; ++y) {
- auto cmp = &cbuffer[y * surface->compositor->image.stride];
- x1 = (int32_t)_xa;
- x2 = (int32_t)_xb;
-
- if (!region) {
- minx = INT32_MAX;
- maxx = INT32_MIN;
- //one single row, could be consisted of multiple spans.
- while (span->y == y && spanIdx < image->rle->size) {
- if (minx > span->x) minx = span->x;
- if (maxx < span->x + span->len) maxx = span->x + span->len;
- ++span;
- ++spanIdx;
- }
- }
-
- if (x1 < minx) x1 = minx;
- if (x2 > maxx) x2 = maxx;
-
- //Anti-Aliasing frames
- //FIXME: this aa must be applied before masking op
- ay = y - aaSpans->yStart;
- if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
- if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
-
- //Range allowed
- if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
- for (int32_t x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
- //Range allowed
- if (x >= x1 && x < x2) {
- //Perform subtexel pre-stepping on UV
- dx = 1 - (_xa - x1);
- u = _ua + dx * _dudx;
- v = _va + dx * _dvdx;
- if ((uint32_t)v >= image->h) {
- cmp[x] = 0;
- } else {
- if (opacity == 255) {
- uu = (int) u;
- if (uu >= sw) continue;
- vv = (int) v;
- if (vv >= sh) continue;
-
- ar = (int)(255 * (1 - modff(u, &iptr)));
- ab = (int)(255 * (1 - modff(v, &iptr)));
- iru = uu + 1;
- irv = vv + 1;
-
- px = *(sbuf + (vv * sw) + uu);
-
- /* horizontal interpolate */
- if (iru < sw) {
- /* right pixel */
- int px2 = *(sbuf + (vv * sw) + iru);
- px = INTERPOLATE(px, px2, ar);
- }
- /* vertical interpolate */
- if (irv < sh) {
- /* bottom pixel */
- int px2 = *(sbuf + (irv * sw) + uu);
-
- /* horizontal interpolate */
- if (iru < sw) {
- /* bottom right pixel */
- int px3 = *(sbuf + (irv * sw) + iru);
- px2 = INTERPOLATE(px2, px3, ar);
- }
- px = INTERPOLATE(px, px2, ab);
- }
- cmp[x] = ALPHA_BLEND(cmp[x], A(px));
-
- //Step UV horizontally
- u += _dudx;
- v += _dvdx;
- } else {
- uu = (int) u;
- if (uu >= sw) continue;
- vv = (int) v;
- if (vv >= sh) continue;
-
- ar = (int)(255 * (1 - modff(u, &iptr)));
- ab = (int)(255 * (1 - modff(v, &iptr)));
- iru = uu + 1;
- irv = vv + 1;
-
- px = *(sbuf + (vv * sw) + uu);
-
- /* horizontal interpolate */
- if (iru < sw) {
- /* right pixel */
- int px2 = *(sbuf + (vv * sw) + iru);
- px = INTERPOLATE(px, px2, ar);
- }
- /* vertical interpolate */
- if (irv < sh) {
- /* bottom pixel */
- int px2 = *(sbuf + (irv * sw) + uu);
-
- /* horizontal interpolate */
- if (iru < sw) {
- /* bottom right pixel */
- int px3 = *(sbuf + (irv * sw) + iru);
- px2 = INTERPOLATE(px2, px3, ar);
- }
- px = INTERPOLATE(px, px2, ab);
- }
- cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity));
-
- //Step UV horizontally
- u += _dudx;
- v += _dvdx;
- }
- }
- } else {
- //Clear out of polygon horizontal range
- if (x < x1 && (dirFlag == 1 || dirFlag == 2)) cmp[x] = 0;
- else if (x >= x2 && (dirFlag == 3 || dirFlag == 4)) cmp[x] = 0;
- }
- }
- }
- //Step along both edges
- _xa += _dxdya;
- _xb += _dxdyb;
- _ua += _dudya;
- _va += _dvdya;
- }
- xa = _xa;
- xb = _xb;
- ua = _ua;
- va = _va;
-}
-
+ return false;
-static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
-{
+#if 0 //Enable it when GRAYSCALE image is supported
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ auto direct = _direct(surface->compositor->method);
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va;
- auto sbuf = image->buf32;
+ auto sbuf = image->buf8;
int32_t sw = static_cast<int32_t>(image->stride);
int32_t sh = image->h;
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
@@ -262,7 +89,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
float dx, u, v, iptr;
SwSpan* span = nullptr; //used only when rle based.
- if (!_arrange(image, region, yStart, yEnd)) return;
+ if (!_arrange(image, region, yStart, yEnd)) return false;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
@@ -313,7 +140,8 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
x = x1;
- auto cmp = &surface->compositor->image.buf32[y * surface->compositor->image.stride + x1];
+ auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
+ auto dst = &surface->buf8[y * surface->stride + x1];
if (opacity == 255) {
//Draw horizontal line
@@ -349,7 +177,13 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
}
px = INTERPOLATE(px, px2, ab);
}
- *cmp = maskOp(px, *cmp, IA(px));
+ if (direct) {
+ auto tmp = maskOp(px, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ } else {
+ *cmp = maskOp(px, *cmp, ~px);
+ }
++cmp;
//Step UV horizontally
@@ -392,7 +226,15 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
}
px = INTERPOLATE(px, px2, ab);
}
- *cmp = amaskOp(px, *cmp, opacity);
+
+ if (direct) {
+ auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ } else {
+ auto tmp = MULTIPLY(px, opacity);
+ *cmp = maskOp(tmp, *cmp, ~px);
+ }
++cmp;
//Step UV horizontally
@@ -418,17 +260,9 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
xb = _xb;
ua = _ua;
va = _va;
-}
-
-static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
-{
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterMaskedPolygonImageSegmentInt(surface, image, region, yStart, yEnd, aaSpans, opacity, dirFlag);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterMaskedPolygonImageSegmentDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), yStart, yEnd, aaSpans, opacity);
- }
+ return true;
+#endif
}
@@ -1256,6 +1090,11 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
*/
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
+ return false;
+ }
+
//Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
@@ -1294,6 +1133,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
+#if 0
+ if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+ _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ }
+#endif
return _apply(surface, aaSpans);
}
@@ -1313,6 +1157,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
*/
static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
+ return false;
+ }
+
//Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
@@ -1342,15 +1191,17 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
}
// Get AA spans and step polygons again to draw
- auto aaSpans = _AASpans(ys, ye, image, region);
- if (aaSpans) {
+ if (auto aaSpans = _AASpans(ys, ye, image, region)) {
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
_rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
}
- // Apply to surface (note: frees the AA spans)
+#if 0
+ if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+ _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ }
+#endif
_apply(surface, aaSpans);
}
free(transformedTris);
-
return true;
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
index 1115a41d64..091b72fa97 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
@@ -78,6 +78,30 @@ struct SwShapeTask : SwTask
bool cmpStroking = false;
bool clipper = false;
+ /* We assume that if the stroke width is greater than 2,
+ the shape's outline beneath the stroke could be adequately covered by the stroke drawing.
+ Therefore, antialiasing is disabled under this condition.
+ Additionally, the stroke style should not be dashed. */
+ bool antialiasing(float strokeWidth)
+ {
+ return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst;
+ }
+
+ float validStrokeWidth()
+ {
+ if (!rshape->stroke) return 0.0f;
+
+ auto width = rshape->stroke->width;
+ if (mathZero(width)) return 0.0f;
+
+ if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
+ if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
+
+ if (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12));
+ else return width;
+ }
+
+
bool clip(SwRleData* target) override
{
if (shape.fastTrack) rleClipRect(target, &bbox);
@@ -99,16 +123,10 @@ struct SwShapeTask : SwTask
{
if (opacity == 0 && !clipper) return; //Invisible
- uint8_t strokeAlpha = 0;
- auto visibleStroke = false;
+ auto strokeWidth = validStrokeWidth();
bool visibleFill = false;
auto clipRegion = bbox;
- if (HALF_STROKE(rshape->strokeWidth()) > 0) {
- rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
- visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0);
- }
-
//This checks also for the case, if the invisible shape turned to visible by alpha.
auto prepareShape = false;
if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
@@ -119,22 +137,15 @@ struct SwShapeTask : SwTask
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
alpha = MULTIPLY(alpha, opacity);
visibleFill = (alpha > 0 || rshape->fill);
- if (visibleFill || visibleStroke || clipper) {
+ if (visibleFill || clipper) {
shapeReset(&shape);
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
}
}
-
//Fill
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
if (visibleFill || clipper) {
- /* We assume that if stroke width is bigger than 2,
- shape outline below stroke could be full covered by stroke drawing.
- Thus it turns off antialising in that condition.
- Also, it shouldn't be dash style. */
- auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst);
-
- if (!shapeGenRle(&shape, rshape, antiAlias)) goto err;
+ if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
}
if (auto fill = rshape->fill) {
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
@@ -144,10 +155,9 @@ struct SwShapeTask : SwTask
shapeDelFill(&shape);
}
}
-
//Stroke
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
- if (visibleStroke) {
+ if (strokeWidth > 0.0f) {
shapeResetStroke(&shape, rshape, transform);
if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
@@ -641,8 +651,6 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
if (x + w > sw) w = (sw - x);
if (y + h > sh) h = (sh - y);
- TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
-
cmp->compositor->recoverSfc = surface;
cmp->compositor->recoverCmp = surface->compositor;
cmp->compositor->valid = false;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
index fe84983a76..a4a7fabdee 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
@@ -716,11 +716,11 @@ static void _decomposeOutline(RleWorker& rw)
for (auto cntr = outline->cntrs.data; cntr < outline->cntrs.end(); ++cntr) {
auto last = *cntr;
auto limit = outline->pts.data + last;
- auto start = UPSCALE(outline->pts.data[first]);
+ auto start = UPSCALE(outline->pts[first]);
auto pt = outline->pts.data + first;
auto types = outline->types.data + first;
- _moveTo(rw, UPSCALE(outline->pts.data[first]));
+ _moveTo(rw, UPSCALE(outline->pts[first]));
while (pt < limit) {
++pt;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
index 14dd68b906..651eaee452 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
@@ -21,9 +21,8 @@
*/
#include "tvgSwCommon.h"
+#include "tvgMath.h"
#include "tvgBezier.h"
-#include <float.h>
-#include <math.h>
/************************************************************************/
/* Internal Class Implementation */
@@ -108,7 +107,7 @@ static void _outlineClose(SwOutline& outline)
if (outline.pts.count == i) return;
//Close the path
- outline.pts.push(outline.pts.data[i]);
+ outline.pts.push(outline.pts[i]);
outline.types.push(SW_CURVE_TYPE_POINT);
outline.closed.push(true);
}
@@ -127,14 +126,18 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
}
} else {
while (len > dash.curLen) {
- len -= dash.curLen;
Line left, right;
- _lineSplitAt(cur, dash.curLen, left, right);;
- dash.curIdx = (dash.curIdx + 1) % dash.cnt;
- if (!dash.curOpGap) {
- _outlineMoveTo(*dash.outline, &left.pt1, transform);
- _outlineLineTo(*dash.outline, &left.pt2, transform);
+ if (dash.curLen > 0) {
+ len -= dash.curLen;
+ _lineSplitAt(cur, dash.curLen, left, right);
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &left.pt1, transform);
+ _outlineLineTo(*dash.outline, &left.pt2, transform);
+ }
+ } else {
+ right = cur;
}
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx];
dash.curOpGap = !dash.curOpGap;
cur = right;
@@ -169,16 +172,22 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
}
} else {
+ bool begin = true; //starting with move_to
while (len > dash.curLen) {
Bezier left, right;
- len -= dash.curLen;
- bezSplitAt(cur, dash.curLen, left, right);
- if (!dash.curOpGap) {
- // leftovers from a previous command don't require moveTo
- if (dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
- _outlineMoveTo(*dash.outline, &left.start, transform);
+ if (dash.curLen > 0) {
+ len -= dash.curLen;
+ bezSplitAt(cur, dash.curLen, left, right);
+ if (!dash.curOpGap) {
+ // leftovers from a previous command don't require moveTo
+ if (begin || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
+ _outlineMoveTo(*dash.outline, &left.start, transform);
+ begin = false;
+ }
+ _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
}
- _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
+ } else {
+ right = cur;
}
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx];
@@ -203,11 +212,10 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
}
-static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
+static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length)
{
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;
@@ -215,86 +223,161 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
SwDashStroke dash;
- dash.curIdx = 0;
- dash.curLen = 0;
- dash.ptStart = {0, 0};
- dash.ptCur = {0, 0};
- dash.curOpGap = false;
+ auto offset = 0.0f;
+ auto trimmed = false;
+
+ dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
+
+ //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) 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 - begin);
+ }
+
+ trimmed = true;
+ //just a dasy style.
+ } else {
+
+ if (dash.cnt == 0) return nullptr;
+ }
- const float* pattern;
- dash.cnt = rshape->strokeDash(&pattern);
- if (dash.cnt == 0) return nullptr;
+ //offset?
+ auto patternLength = 0.0f;
+ uint32_t offIdx = 0;
+ if (!mathZero(offset)) {
+ for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
+ bool isOdd = dash.cnt % 2;
+ if (isOdd) patternLength *= 2;
+
+ offset = fmod(offset, patternLength);
+ if (offset < 0) offset += patternLength;
+
+ for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) {
+ auto curPattern = dash.pattern[i % dash.cnt];
+ if (offset < curPattern) break;
+ offset -= curPattern;
+ }
+ }
//OPTMIZE ME: Use mempool???
- dash.pattern = const_cast<float*>(pattern);
dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
//smart reservation
- auto outlinePtsCnt = 0;
- auto outlineCntrsCnt = 0;
+ auto closeCnt = 0;
+ auto moveCnt = 0;
+
+ for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
+ if (*cmd == PathCommand::Close) ++closeCnt;
+ else if (*cmd == PathCommand::MoveTo) ++moveCnt;
+ }
- for (uint32_t i = 0; i < cmdCnt; ++i) {
- switch (*(cmds + i)) {
+ //No idea exact count.... Reserve Approximitely 20x...
+ //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
+ dash.outline->pts.grow(20 * (closeCnt + ptsCnt + 1));
+ dash.outline->types.grow(20 * (closeCnt + ptsCnt + 1));
+ dash.outline->cntrs.grow(20 * (moveCnt + 1));
+
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
case PathCommand::Close: {
- ++outlinePtsCnt;
+ _dashLineTo(dash, &dash.ptStart, transform);
break;
}
case PathCommand::MoveTo: {
- ++outlineCntrsCnt;
- ++outlinePtsCnt;
+ //reset the dash
+ dash.curIdx = offIdx % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx] - offset;
+ dash.curOpGap = offIdx % 2;
+ dash.ptStart = dash.ptCur = *pts;
+ ++pts;
break;
}
case PathCommand::LineTo: {
- ++outlinePtsCnt;
+ _dashLineTo(dash, pts, transform);
+ ++pts;
break;
}
case PathCommand::CubicTo: {
- outlinePtsCnt += 3;
+ _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+ pts += 3;
break;
}
}
+ ++cmds;
}
- ++outlinePtsCnt; //for close
- ++outlineCntrsCnt; //for end
+ _outlineEnd(*dash.outline);
- //No idea exact count.... Reserve Approximitely 20x...
- dash.outline->pts.grow(20 * outlinePtsCnt);
- dash.outline->types.grow(20 * outlinePtsCnt);
- dash.outline->cntrs.grow(20 * outlineCntrsCnt);
+ if (trimmed) free(dash.pattern);
+
+ return dash.outline;
+}
+
+
+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;
+
+ //Compute the whole length
while (cmdCnt-- > 0) {
switch (*cmds) {
case PathCommand::Close: {
- _dashLineTo(dash, &dash.ptStart, transform);
+ length += mathLength(pts - 1, close);
+ ++pts;
break;
}
case PathCommand::MoveTo: {
- //reset the dash
- dash.curIdx = 0;
- dash.curLen = *dash.pattern;
- dash.curOpGap = false;
- dash.ptStart = dash.ptCur = *pts;
+ close = pts;
++pts;
break;
}
case PathCommand::LineTo: {
- _dashLineTo(dash, pts, transform);
+ length += mathLength(pts - 1, pts);
++pts;
break;
}
case PathCommand::CubicTo: {
- _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+ length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
pts += 3;
break;
}
}
++cmds;
}
-
- _outlineEnd(*dash.outline);
-
- return dash.outline;
+ return length;
}
@@ -321,7 +404,6 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
{
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;
@@ -329,47 +411,21 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
if (cmdCnt == 0 || ptsCnt == 0) return false;
//smart reservation
- auto outlinePtsCnt = 0;
- auto outlineCntrsCnt = 0;
+ auto moveCnt = 0;
auto closeCnt = 0;
- for (uint32_t i = 0; i < cmdCnt; ++i) {
- switch (*(cmds + i)) {
- case PathCommand::Close: {
- ++outlinePtsCnt;
- ++closeCnt;
- break;
- }
- case PathCommand::MoveTo: {
- ++outlineCntrsCnt;
- ++outlinePtsCnt;
- break;
- }
- case PathCommand::LineTo: {
- ++outlinePtsCnt;
- break;
- }
- case PathCommand::CubicTo: {
- outlinePtsCnt += 3;
- break;
- }
- }
+ for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
+ if (*cmd == PathCommand::Close) ++closeCnt;
+ else if (*cmd == PathCommand::MoveTo) ++moveCnt;
}
- if (static_cast<uint32_t>(outlinePtsCnt - closeCnt) > ptsCnt) {
- TVGERR("SW_ENGINE", "Wrong a pair of the commands & points - required(%d), current(%d)", outlinePtsCnt - closeCnt, ptsCnt);
- return false;
- }
-
- ++outlinePtsCnt; //for close
- ++outlineCntrsCnt; //for end
-
shape->outline = mpoolReqOutline(mpool, tid);
auto outline = shape->outline;
- outline->pts.grow(outlinePtsCnt);
- outline->types.grow(outlinePtsCnt);
- outline->cntrs.grow(outlineCntrsCnt);
+ //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
+ outline->pts.grow(ptsCnt + closeCnt + 1);
+ outline->types.grow(ptsCnt + closeCnt + 1);
+ outline->cntrs.grow(moveCnt + 1);
//Dash outlines are always opened.
//Only normal outlines use this information, it sholud be same to their contour counts.
@@ -514,12 +570,14 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
bool freeOutline = false;
bool ret = true;
- //Dash Style Stroke
- if (rshape->strokeDash(nullptr) > 0) {
- shapeOutline = _genDashOutline(rshape, transform);
+ auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
+
+ //Dash style (+trimming)
+ if (rshape->stroke->dashCnt > 0 || length > 0) {
+ shapeOutline = _genDashOutline(rshape, transform, length);
if (!shapeOutline) return false;
freeOutline = true;
- //Normal Style stroke
+ //Normal style
} else {
if (!shape->outline) {
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
index 38626cb428..f46a4a5a1d 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
@@ -373,10 +373,6 @@ void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
static void _lineTo(SwStroke& stroke, const SwPoint& to)
{
auto delta = to - stroke.center;
-
- //a zero-length lineto is a no-op; avoid creating a spurious corner
- if (delta.zero()) return;
-
//compute length of line
auto angle = mathAtan(delta);
@@ -428,12 +424,6 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
{
- //if all control points are coincident, this is a no-op; avoid creating a spurious corner
- if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
- stroke.center = to;
- return;
- }
-
SwPoint bezStack[37]; //TODO: static?
auto limit = bezStack + 32;
auto arc = bezStack;
@@ -852,7 +842,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
continue;
}
- auto start = outline.pts.data[first];
+ auto start = outline.pts[first];
auto pt = outline.pts.data + first;
auto types = outline.types.data + first;
auto type = types[0];
diff --git a/thirdparty/thorvg/src/lib/tvgAnimation.cpp b/thirdparty/thorvg/src/lib/tvgAnimation.cpp
index f671daa727..4e8c8568d4 100644
--- a/thirdparty/thorvg/src/lib/tvgAnimation.cpp
+++ b/thirdparty/thorvg/src/lib/tvgAnimation.cpp
@@ -23,6 +23,7 @@
//#include "tvgAnimationImpl.h"
#include "tvgCommon.h"
#include "tvgFrameModule.h"
+#include "tvgPaint.h"
#include "tvgPictureImpl.h"
/************************************************************************/
@@ -31,8 +32,20 @@
struct Animation::Impl
{
- //TODO: Memory Safety
- Picture picture;
+ Picture* picture = nullptr;
+
+ Impl()
+ {
+ picture = Picture::gen().release();
+ static_cast<Paint*>(picture)->pImpl->ref();
+ }
+
+ ~Impl()
+ {
+ if (static_cast<Paint*>(picture)->pImpl->unref() == 0) {
+ delete(picture);
+ }
+ }
};
/************************************************************************/
@@ -41,19 +54,18 @@ struct Animation::Impl
Animation::~Animation()
{
-
+ delete(pImpl);
}
Animation::Animation() : pImpl(new Impl)
{
- pImpl->picture.pImpl->animated = true;
}
Result Animation::frame(uint32_t no) noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return Result::InsufficientCondition;
if (!loader->animatable()) return Result::NonSupport;
@@ -65,13 +77,13 @@ Result Animation::frame(uint32_t no) noexcept
Picture* Animation::picture() const noexcept
{
- return &pImpl->picture;
+ return pImpl->picture;
}
uint32_t Animation::curFrame() const noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
@@ -82,7 +94,7 @@ uint32_t Animation::curFrame() const noexcept
uint32_t Animation::totalFrame() const noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
@@ -93,7 +105,7 @@ uint32_t Animation::totalFrame() const noexcept
float Animation::duration() const noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
diff --git a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
index 9a3018e7a6..fe934ec352 100644
--- a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
@@ -65,8 +65,9 @@ struct Canvas::Impl
//Free paints
for (auto paint : paints) {
- paint->pImpl->dispose(*renderer);
- if (free) delete(paint);
+ if (paint->pImpl->dispose(*renderer)) {
+ if (free && paint->pImpl->unref() == 0) delete(paint);
+ }
}
paints.clear();
diff --git a/thirdparty/thorvg/src/lib/tvgCommon.h b/thirdparty/thorvg/src/lib/tvgCommon.h
index 23011ffed5..f36b4b9b30 100644
--- a/thirdparty/thorvg/src/lib/tvgCommon.h
+++ b/thirdparty/thorvg/src/lib/tvgCommon.h
@@ -76,10 +76,14 @@ using Size = Point;
#define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
#define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
#else
- #define TVGERR(...)
- #define TVGLOG(...)
+ #define TVGERR(...) do {} while(0)
+ #define TVGLOG(...) do {} while(0)
#endif
uint16_t THORVG_VERSION_NUMBER();
+
+#define P(A) ((A)->pImpl) //Access to pimpl.
+#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
+
#endif //_TVG_COMMON_H_
diff --git a/thirdparty/thorvg/src/lib/tvgFill.cpp b/thirdparty/thorvg/src/lib/tvgFill.cpp
index cc7e1ccaed..9215882c8d 100644
--- a/thirdparty/thorvg/src/lib/tvgFill.cpp
+++ b/thirdparty/thorvg/src/lib/tvgFill.cpp
@@ -26,6 +26,50 @@
/* Internal Class Implementation */
/************************************************************************/
+Fill* RadialGradient::Impl::duplicate()
+{
+ auto ret = RadialGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->cx = cx;
+ ret->pImpl->cy = cy;
+ ret->pImpl->r = r;
+ ret->pImpl->fx = fx;
+ ret->pImpl->fy = fy;
+ ret->pImpl->fr = fr;
+
+ return ret.release();
+}
+
+
+Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
+{
+ if (r < 0 || fr < 0) return Result::InvalidArguments;
+
+ this->cx = cx;
+ this->cy = cy;
+ this->r = r;
+ this->fx = fx;
+ this->fy = fy;
+ this->fr = fr;
+
+ return Result::Success;
+};
+
+
+Fill* LinearGradient::Impl::duplicate()
+{
+ auto ret = LinearGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->x1 = x1;
+ ret->pImpl->y1 = y1;
+ ret->pImpl->x2 = x2;
+ ret->pImpl->y2 = y2;
+
+ return ret.release();
+};
+
/************************************************************************/
/* External Class Implementation */
@@ -110,7 +154,97 @@ Fill* Fill::duplicate() const noexcept
return pImpl->duplicate();
}
+
uint32_t Fill::identifier() const noexcept
{
return pImpl->id;
}
+
+
+RadialGradient::RadialGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
+ Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
+}
+
+
+RadialGradient::~RadialGradient()
+{
+ delete(pImpl);
+}
+
+
+Result RadialGradient::radial(float cx, float cy, float r) noexcept
+{
+ return pImpl->radial(cx, cy, r, cx, cy, 0.0f);
+}
+
+
+Result RadialGradient::radial(float* cx, float* cy, float* r) const noexcept
+{
+ if (cx) *cx = pImpl->cx;
+ if (cy) *cy = pImpl->cy;
+ if (r) *r = pImpl->r;
+
+ return Result::Success;
+}
+
+
+unique_ptr<RadialGradient> RadialGradient::gen() noexcept
+{
+ return unique_ptr<RadialGradient>(new RadialGradient);
+}
+
+
+uint32_t RadialGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_RADIAL;
+}
+
+
+LinearGradient::LinearGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
+ Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
+}
+
+
+LinearGradient::~LinearGradient()
+{
+ delete(pImpl);
+}
+
+
+Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
+{
+ pImpl->x1 = x1;
+ pImpl->y1 = y1;
+ pImpl->x2 = x2;
+ pImpl->y2 = y2;
+
+ return Result::Success;
+}
+
+
+Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
+{
+ if (x1) *x1 = pImpl->x1;
+ if (x2) *x2 = pImpl->x2;
+ if (y1) *y1 = pImpl->y1;
+ if (y2) *y2 = pImpl->y2;
+
+ return Result::Success;
+}
+
+
+unique_ptr<LinearGradient> LinearGradient::gen() noexcept
+{
+ return unique_ptr<LinearGradient>(new LinearGradient);
+}
+
+
+uint32_t LinearGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_LINEAR;
+}
+
diff --git a/thirdparty/thorvg/src/lib/tvgFill.h b/thirdparty/thorvg/src/lib/tvgFill.h
index 20603b7333..ff3dd48c49 100644
--- a/thirdparty/thorvg/src/lib/tvgFill.h
+++ b/thirdparty/thorvg/src/lib/tvgFill.h
@@ -86,4 +86,27 @@ struct Fill::Impl
}
};
+
+struct RadialGradient::Impl
+{
+ float cx = 0.0f, cy = 0.0f;
+ float fx = 0.0f, fy = 0.0f;
+ float r = 0.0f, fr = 0.0f;
+
+ Fill* duplicate();
+ Result radial(float cx, float cy, float r, float fx, float fy, float fr);
+};
+
+
+struct LinearGradient::Impl
+{
+ float x1 = 0.0f;
+ float y1 = 0.0f;
+ float x2 = 0.0f;
+ float y2 = 0.0f;
+
+ Fill* duplicate();
+};
+
+
#endif //_TVG_FILL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
deleted file mode 100644
index 3e040c08f1..0000000000
--- a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
-
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
-
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
-
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <float.h>
-#include <math.h>
-#include "tvgFill.h"
-
-/************************************************************************/
-/* Internal Class Implementation */
-/************************************************************************/
-
-struct LinearGradient::Impl
-{
- float x1 = 0;
- float y1 = 0;
- float x2 = 0;
- float y2 = 0;
-
- Fill* duplicate()
- {
- auto ret = LinearGradient::gen();
- if (!ret) return nullptr;
-
- ret->pImpl->x1 = x1;
- ret->pImpl->y1 = y1;
- ret->pImpl->x2 = x2;
- ret->pImpl->y2 = y2;
-
- return ret.release();
- }
-};
-
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
-LinearGradient::LinearGradient():pImpl(new Impl())
-{
- Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
- Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
-}
-
-
-LinearGradient::~LinearGradient()
-{
- delete(pImpl);
-}
-
-
-Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
-{
- pImpl->x1 = x1;
- pImpl->y1 = y1;
- pImpl->x2 = x2;
- pImpl->y2 = y2;
-
- return Result::Success;
-}
-
-
-Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
-{
- if (x1) *x1 = pImpl->x1;
- if (x2) *x2 = pImpl->x2;
- if (y1) *y1 = pImpl->y1;
- if (y2) *y2 = pImpl->y2;
-
- return Result::Success;
-}
-
-
-unique_ptr<LinearGradient> LinearGradient::gen() noexcept
-{
- return unique_ptr<LinearGradient>(new LinearGradient);
-}
-
-
-uint32_t LinearGradient::identifier() noexcept
-{
- return TVG_CLASS_ID_LINEAR;
-}
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.cpp b/thirdparty/thorvg/src/lib/tvgLoader.cpp
index 16b604c89e..8ed0d5752e 100644
--- a/thirdparty/thorvg/src/lib/tvgLoader.cpp
+++ b/thirdparty/thorvg/src/lib/tvgLoader.cpp
@@ -164,7 +164,7 @@ static LoadModule* _findByType(const string& mimeType)
if (mimeType == "tvg") type = FileType::Tvg;
else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
- else if (mimeType == "lottie" || mimeType == "json") type = FileType::Lottie;
+ else if (mimeType == "lottie") type = FileType::Lottie;
else if (mimeType == "raw") type = FileType::Raw;
else if (mimeType == "png") type = FileType::Png;
else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
@@ -214,22 +214,24 @@ shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid)
shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
{
- //Try first with the given MimeType
- if (auto loader = _findByType(mimeType)) {
- if (loader->open(data, size, copy)) {
- return shared_ptr<LoadModule>(loader);
- } else {
- TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
- delete(loader);
- }
- }
-
- //Abnormal MimeType. Try with the candidates in the order
- for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
- auto loader = _find(static_cast<FileType>(i));
- if (loader) {
- if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
- else delete(loader);
+ //Try with the given MimeType
+ if (!mimeType.empty()) {
+ if (auto loader = _findByType(mimeType)) {
+ if (loader->open(data, size, copy)) {
+ return shared_ptr<LoadModule>(loader);
+ } else {
+ TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
+ delete(loader);
+ }
+ }
+ //Unkown MimeType. Try with the candidates in the order
+ } else {
+ for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
+ auto loader = _find(static_cast<FileType>(i));
+ if (loader) {
+ if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+ }
}
}
return nullptr;
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp
index 57da269cd7..bac5e434a5 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp
@@ -166,7 +166,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
Create a composition image. */
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
auto region = smethod->bounds(renderer);
- if (MASK_OPERATION(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
+ if (MASK_REGION_MERGING(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
@@ -206,23 +206,20 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
auto method = compData->method;
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
- /* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
- we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
+ /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
+ we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
auto tryFastTrack = false;
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
if (method == CompositeMethod::ClipPath) tryFastTrack = true;
- //OPTIMIZE HERE: Actually, this condition AlphaMask is useless. We can skip it?
- else if (method == CompositeMethod::AlphaMask) {
+ else {
auto shape = static_cast<Shape*>(target);
uint8_t a;
shape->fillColor(nullptr, nullptr, nullptr, &a);
- if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
- //OPTIMIZE HERE: Actually, this condition InvAlphaMask is useless. We can skip it?
- } else if (method == CompositeMethod::InvAlphaMask) {
- auto shape = static_cast<Shape*>(target);
- uint8_t a;
- shape->fillColor(nullptr, nullptr, nullptr, &a);
- if ((a == 0 || shape->opacity() == 0) && !shape->fill()) tryFastTrack = true;
+ //no gradient fill & no compositions of the composition target.
+ if (!shape->fill() && !(PP(shape)->compData)) {
+ if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
+ else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true;
+ }
}
if (tryFastTrack) {
RenderRegion viewport2;
@@ -263,12 +260,12 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
}
-bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed)
+bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
{
Matrix* m = nullptr;
//Case: No transformed, quick return!
- if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h);
+ if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h, stroking);
//Case: Transformed
auto tx = 0.0f;
@@ -276,7 +273,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
auto tw = 0.0f;
auto th = 0.0f;
- auto ret = smethod->bounds(&tx, &ty, &tw, &th);
+ auto ret = smethod->bounds(&tx, &ty, &tw, &th, stroking);
//Get vertices
Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
@@ -365,7 +362,7 @@ TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) cons
Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
{
- if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
+ if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success;
return Result::InsufficientCondition;
}
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h
index c00238a070..c020a7dffd 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.h
+++ b/thirdparty/thorvg/src/lib/tvgPaint.h
@@ -42,10 +42,10 @@ namespace tvg
{
virtual ~StrategyMethod() {}
- virtual bool dispose(RenderMethod& renderer) = 0;
+ virtual bool dispose(RenderMethod& renderer) = 0; //return true if the deletion is allowed.
virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has.
virtual bool render(RenderMethod& renderer) = 0;
- virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
+ virtual bool bounds(float* x, float* y, float* w, float* h, bool stroking) = 0;
virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
virtual Paint* duplicate() = 0;
virtual Iterator* iterator() = 0;
@@ -68,6 +68,7 @@ namespace tvg
uint8_t ctxFlag = ContextFlag::Invalid;
uint8_t id;
uint8_t opacity = 255;
+ uint8_t refCnt = 1;
~Impl()
{
@@ -79,6 +80,18 @@ namespace tvg
delete(rTransform);
}
+ uint8_t ref()
+ {
+ if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
+ return (++refCnt);
+ }
+
+ uint8_t unref()
+ {
+ if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
+ return (--refCnt);
+ }
+
void method(StrategyMethod* method)
{
smethod = method;
@@ -147,7 +160,7 @@ namespace tvg
bool rotate(float degree);
bool scale(float factor);
bool translate(float x, float y);
- bool bounds(float* x, float* y, float* w, float* h, bool transformed);
+ bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
bool render(RenderMethod& renderer);
Paint* duplicate();
@@ -162,9 +175,9 @@ namespace tvg
PaintMethod(T* _inst) : inst(_inst) {}
~PaintMethod() {}
- bool bounds(float* x, float* y, float* w, float* h) override
+ bool bounds(float* x, float* y, float* w, float* h, bool stroking) override
{
- return inst->bounds(x, y, w, h);
+ return inst->bounds(x, y, w, h, stroking);
}
RenderRegion bounds(RenderMethod& renderer) const override
diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
index d445c72c10..f29b8a1ec3 100644
--- a/thirdparty/thorvg/src/lib/tvgPictureImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
@@ -70,7 +70,6 @@ struct Picture::Impl
Picture* picture = nullptr;
bool resizing = false;
bool needComp = false; //need composition
- bool animated = false; //picture is belonged to Animation
Impl(Picture* p) : picture(p)
{
@@ -84,12 +83,10 @@ struct Picture::Impl
bool dispose(RenderMethod& renderer)
{
- bool ret = true;
- if (paint) ret = paint->pImpl->dispose(renderer);
- else if (surface) ret = renderer.dispose(rd);
+ if (paint) paint->pImpl->dispose(renderer);
+ else if (surface) renderer.dispose(rd);
rd = nullptr;
-
- return ret;
+ return true;
}
RenderUpdateFlag load()
@@ -191,7 +188,7 @@ struct Picture::Impl
return true;
}
- bool bounds(float* x, float* y, float* w, float* h)
+ bool bounds(float* x, float* y, float* w, float* h, bool stroking)
{
if (rm.triangleCnt > 0) {
auto triangles = rm.triangles;
diff --git a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
deleted file mode 100644
index a85f60e5d0..0000000000
--- a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
-
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
-
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
-
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <float.h>
-#include "tvgFill.h"
-
-/************************************************************************/
-/* Internal Class Implementation */
-/************************************************************************/
-
-struct RadialGradient::Impl
-{
- float cx = 0;
- float cy = 0;
- float radius = 0;
-
- Fill* duplicate()
- {
- auto ret = RadialGradient::gen();
- if (!ret) return nullptr;
-
- ret->pImpl->cx = cx;
- ret->pImpl->cy = cy;
- ret->pImpl->radius = radius;
-
- return ret.release();
- }
-};
-
-
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
-RadialGradient::RadialGradient():pImpl(new Impl())
-{
- Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
- Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
-}
-
-
-RadialGradient::~RadialGradient()
-{
- delete(pImpl);
-}
-
-
-Result RadialGradient::radial(float cx, float cy, float radius) noexcept
-{
- if (radius < 0) return Result::InvalidArguments;
-
- pImpl->cx = cx;
- pImpl->cy = cy;
- pImpl->radius = radius;
-
- return Result::Success;
-}
-
-
-Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
-{
- if (cx) *cx = pImpl->cx;
- if (cy) *cy = pImpl->cy;
- if (radius) *radius = pImpl->radius;
-
- return Result::Success;
-}
-
-
-unique_ptr<RadialGradient> RadialGradient::gen() noexcept
-{
- return unique_ptr<RadialGradient>(new RadialGradient);
-}
-
-
-uint32_t RadialGradient::identifier() noexcept
-{
- return TVG_CLASS_ID_RADIAL;
-}
diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h
index 9d7bafb613..1231089ff5 100644
--- a/thirdparty/thorvg/src/lib/tvgRender.h
+++ b/thirdparty/thorvg/src/lib/tvgRender.h
@@ -137,11 +137,17 @@ struct RenderStroke
Fill *fill = nullptr;
float* dashPattern = nullptr;
uint32_t dashCnt = 0;
+ float dashOffset = 0.0f;
StrokeCap cap = StrokeCap::Square;
StrokeJoin join = StrokeJoin::Bevel;
float miterlimit = 4.0f;
bool strokeFirst = false;
+ struct {
+ float begin = 0.0f;
+ float end = 1.0f;
+ } trim;
+
~RenderStroke()
{
free(dashPattern);
@@ -182,6 +188,14 @@ struct RenderShape
return stroke->width;
}
+ bool strokeTrim() const
+ {
+ if (!stroke) return false;
+ if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
+ if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false;
+ return true;
+ }
+
bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
{
if (!stroke) return false;
@@ -200,10 +214,11 @@ struct RenderShape
return stroke->fill;
}
- uint32_t strokeDash(const float** dashPattern) const
+ uint32_t strokeDash(const float** dashPattern, float* offset) const
{
if (!stroke) return 0;
if (dashPattern) *dashPattern = stroke->dashPattern;
+ if (offset) *offset = stroke->dashOffset;
return stroke->dashCnt;
}
@@ -253,21 +268,22 @@ public:
virtual bool endComposite(Compositor* cmp) = 0;
};
-static inline bool MASK_OPERATION(CompositeMethod method)
+static inline bool MASK_REGION_MERGING(CompositeMethod method)
{
switch(method) {
case CompositeMethod::AlphaMask:
case CompositeMethod::InvAlphaMask:
case CompositeMethod::LumaMask:
case CompositeMethod::InvLumaMask:
- return false;
- case CompositeMethod::AddMask:
case CompositeMethod::SubtractMask:
case CompositeMethod::IntersectMask:
+ return false;
+ //these might expand the rendering region
+ case CompositeMethod::AddMask:
case CompositeMethod::DifferenceMask:
return true;
default:
- TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+ TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
return false;
}
}
@@ -284,7 +300,7 @@ static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
return sizeof(uint8_t);
case ColorSpace::Unsupported:
default:
- TVGERR("SW_ENGINE", "Unsupported Channel Size! = %d", (int)cs);
+ TVGERR("RENDERER", "Unsupported Channel Size! = %d", (int)cs);
return 0;
}
}
@@ -294,16 +310,17 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi
switch(method) {
case CompositeMethod::AlphaMask:
case CompositeMethod::InvAlphaMask:
- return ColorSpace::Grayscale8;
- case CompositeMethod::LumaMask:
- case CompositeMethod::InvLumaMask:
case CompositeMethod::AddMask:
+ case CompositeMethod::DifferenceMask:
case CompositeMethod::SubtractMask:
case CompositeMethod::IntersectMask:
- case CompositeMethod::DifferenceMask:
+ return ColorSpace::Grayscale8;
+ //TODO: Optimize Luma/InvLuma colorspace to Grayscale8
+ case CompositeMethod::LumaMask:
+ case CompositeMethod::InvLumaMask:
return renderer.colorSpace();
default:
- TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+ TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method);
return ColorSpace::Unsupported;
}
}
diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
index 8fa38bcc85..90b1775610 100644
--- a/thirdparty/thorvg/src/lib/tvgSceneImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
@@ -75,7 +75,7 @@ struct Scene::Impl
~Impl()
{
for (auto paint : paints) {
- delete(paint);
+ if (paint->pImpl->unref() == 0) delete(paint);
}
}
@@ -85,11 +85,11 @@ struct Scene::Impl
paint->pImpl->dispose(renderer);
}
- auto ret = renderer.dispose(rd);
+ renderer.dispose(rd);
this->renderer = nullptr;
this->rd = nullptr;
- return ret;
+ return true;
}
bool needComposition(uint8_t opacity)
@@ -181,7 +181,7 @@ struct Scene::Impl
return {x1, y1, (x2 - x1), (y2 - y1)};
}
- bool bounds(float* px, float* py, float* pw, float* ph)
+ bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
{
if (paints.empty()) return false;
@@ -196,7 +196,7 @@ struct Scene::Impl
auto w = 0.0f;
auto h = 0.0f;
- if (paint->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
+ if (!P(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue;
//Merge regions
if (x < x1) x1 = x;
@@ -231,7 +231,7 @@ struct Scene::Impl
auto dispose = renderer ? true : false;
for (auto paint : paints) {
- if (dispose) paint->pImpl->dispose(*renderer);
+ if (dispose) free &= paint->pImpl->dispose(*renderer);
if (free) delete(paint);
}
paints.clear();
diff --git a/thirdparty/thorvg/src/lib/tvgShape.cpp b/thirdparty/thorvg/src/lib/tvgShape.cpp
index f32feb2133..336ac71d81 100644
--- a/thirdparty/thorvg/src/lib/tvgShape.cpp
+++ b/thirdparty/thorvg/src/lib/tvgShape.cpp
@@ -150,13 +150,13 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
//just circle
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
- startAngle = (startAngle * M_PI) / 180.0f;
- sweep = sweep * M_PI / 180.0f;
+ startAngle = (startAngle * MATH_PI) / 180.0f;
+ sweep = sweep * MATH_PI / 180.0f;
- auto nCurves = ceil(fabsf(sweep / float(M_PI_2)));
+ auto nCurves = ceil(fabsf(sweep / MATH_PI2));
auto sweepSign = (sweep < 0 ? -1 : 1);
- auto fract = fmodf(sweep, float(M_PI_2));
- fract = (mathZero(fract)) ? float(M_PI_2) * sweepSign : fract;
+ auto fract = fmodf(sweep, MATH_PI2);
+ fract = (mathZero(fract)) ? MATH_PI2 * sweepSign : fract;
//Start from here
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
@@ -342,22 +342,13 @@ const Fill* Shape::strokeFill() const noexcept
Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
{
- if ((cnt == 1) || (!dashPattern && cnt > 0) || (dashPattern && cnt == 0)) {
- return Result::InvalidArguments;
- }
-
- for (uint32_t i = 0; i < cnt; i++)
- if (dashPattern[i] < FLT_EPSILON) return Result::InvalidArguments;
-
- if (!pImpl->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
-
- return Result::Success;
+ return pImpl->strokeDash(dashPattern, cnt, 0);
}
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
{
- return pImpl->rs.strokeDash(dashPattern);
+ return pImpl->rs.strokeDash(dashPattern, nullptr);
}
diff --git a/thirdparty/thorvg/src/lib/tvgShapeImpl.h b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
index b05f85bf1c..bb266866d0 100644
--- a/thirdparty/thorvg/src/lib/tvgShapeImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
@@ -46,9 +46,9 @@ struct Shape::Impl
bool dispose(RenderMethod& renderer)
{
- auto ret = renderer.dispose(rd);
+ renderer.dispose(rd);
rd = nullptr;
- return ret;
+ return true;
}
bool render(RenderMethod& renderer)
@@ -70,7 +70,7 @@ struct Shape::Impl
if (opacity == 0) return false;
//Shape composition is only necessary when stroking & fill are valid.
- if (!rs.stroke || rs.stroke->width < FLT_EPSILON || rs.stroke->color[3] == 0) return false;
+ if (!rs.stroke || rs.stroke->width < FLT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false;
if (!rs.fill && rs.color[3] == 0) return false;
//translucent fill & stroke
@@ -104,7 +104,7 @@ struct Shape::Impl
return renderer.region(rd);
}
- bool bounds(float* x, float* y, float* w, float* h)
+ bool bounds(float* x, float* y, float* w, float* h, bool stroking)
{
//Path bounding size
if (rs.path.pts.count > 0 ) {
@@ -126,7 +126,7 @@ struct Shape::Impl
}
//Stroke feathering
- if (rs.stroke) {
+ if (stroking && rs.stroke) {
if (x) *x -= rs.stroke->width * 0.5f;
if (y) *y -= rs.stroke->width * 0.5f;
if (w) *w += rs.stroke->width;
@@ -199,8 +199,6 @@ struct Shape::Impl
bool strokeWidth(float width)
{
- //TODO: Size Exception?
-
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
@@ -208,6 +206,22 @@ struct Shape::Impl
return true;
}
+ bool strokeTrim(float begin, float end)
+ {
+ if (!rs.stroke) {
+ if (begin == 0.0f && end == 1.0f) return true;
+ rs.stroke = new RenderStroke();
+ }
+
+ if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
+
+ rs.stroke->trim.begin = begin;
+ rs.stroke->trim.end = end;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
bool strokeCap(StrokeCap cap)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
@@ -269,8 +283,16 @@ struct Shape::Impl
return Result::Success;
}
- bool strokeDash(const float* pattern, uint32_t cnt)
+ Result strokeDash(const float* pattern, uint32_t cnt, float offset)
{
+ if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) {
+ return Result::InvalidArguments;
+ }
+
+ for (uint32_t i = 0; i < cnt; i++) {
+ if (pattern[i] < FLT_EPSILON) return Result::InvalidArguments;
+ }
+
//Reset dash
if (!pattern && cnt == 0) {
free(rs.stroke->dashPattern);
@@ -283,16 +305,17 @@ struct Shape::Impl
}
if (!rs.stroke->dashPattern) {
rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
- if (!rs.stroke->dashPattern) return false;
+ if (!rs.stroke->dashPattern) return Result::FailedAllocation;
}
for (uint32_t i = 0; i < cnt; ++i) {
rs.stroke->dashPattern[i] = pattern[i];
}
}
rs.stroke->dashCnt = cnt;
+ rs.stroke->dashOffset = offset;
flag |= RenderUpdateFlag::Stroke;
- return true;
+ return Result::Success;
}
bool strokeFirst()
@@ -336,24 +359,17 @@ struct Shape::Impl
//Stroke
if (rs.stroke) {
dup->rs.stroke = new RenderStroke();
- dup->rs.stroke->width = rs.stroke->width;
- dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
- dup->rs.stroke->cap = rs.stroke->cap;
- dup->rs.stroke->join = rs.stroke->join;
- dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst;
+ *dup->rs.stroke = *rs.stroke;
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
-
if (rs.stroke->dashCnt > 0) {
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
}
-
- dup->flag |= RenderUpdateFlag::Stroke;
-
if (rs.stroke->fill) {
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
dup->flag |= RenderUpdateFlag::GradientStroke;
}
+ dup->flag |= RenderUpdateFlag::Stroke;
}
//Fill
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
index 019468083d..e3887c60fc 100644
--- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
@@ -100,6 +100,9 @@ struct TaskQueue {
};
+static thread_local bool _async = true; //toggle async tasking for each thread on/off
+
+
struct TaskSchedulerImpl
{
uint32_t threadCnt;
@@ -109,6 +112,8 @@ struct TaskSchedulerImpl
TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
{
+ threads.reserve(threadCnt);
+
for (unsigned i = 0; i < threadCnt; ++i) {
threads.emplace_back([&, i] { run(i); });
}
@@ -142,7 +147,7 @@ struct TaskSchedulerImpl
void request(Task* task)
{
//Async
- if (threadCnt > 0) {
+ if (threadCnt > 0 && _async) {
task->prepare();
auto i = idx++;
for (unsigned n = 0; n < threadCnt; ++n) {
@@ -190,3 +195,9 @@ unsigned TaskScheduler::threads()
if (inst) return inst->threadCnt;
return 0;
}
+
+
+void TaskScheduler::async(bool on)
+{
+ _async = on;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
index 7ada963b77..2dad80f5d0 100644
--- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
@@ -38,6 +38,7 @@ struct TaskScheduler
static void init(unsigned threads);
static void term();
static void request(Task* task);
+ static void async(bool on);
};
struct Task
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
index 6fdb641204..c3c477a263 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
@@ -123,7 +123,7 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
to->stroke.dash.array.clear();
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
- to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ to->stroke.dash.array.push(from->stroke.dash.array[i]);
}
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
@@ -236,7 +236,7 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style)
void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
{
for (uint32_t i = 0; i < postponeds.count; ++i) {
- auto nodeIdPair = postponeds.data[i];
+ auto nodeIdPair = postponeds[i];
//css styling: tag.name has higher priority than .name
if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index 998c98e83f..9825fd8cc4 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -59,7 +59,7 @@
#include "tvgXmlParser.h"
#include "tvgSvgLoader.h"
#include "tvgSvgSceneBuilder.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
#include "tvgSvgCssStyle.h"
#include "tvgMath.h"
@@ -110,7 +110,7 @@ static bool _parseNumber(const char** content, float* number)
{
char* end = nullptr;
- *number = svgUtilStrtof(*content, &end);
+ *number = strToFloat(*content, &end);
//If the start of string is not number
if ((*content) == end) return false;
//Skip comma if any
@@ -166,7 +166,7 @@ static void _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp
*/
static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
{
- float parsedValue = svgUtilStrtof(str, nullptr);
+ float parsedValue = strToFloat(str, nullptr);
if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
@@ -194,7 +194,7 @@ static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool&
{
char* end = nullptr;
- float parsedValue = svgUtilStrtof(str, &end);
+ float parsedValue = strToFloat(str, &end);
isPercentage = false;
if (strstr(str, "%")) {
@@ -217,7 +217,7 @@ static float _toOffset(const char* str)
char* end = nullptr;
auto strEnd = str + strlen(str);
- float parsedValue = svgUtilStrtof(str, &end);
+ float parsedValue = strToFloat(str, &end);
end = _skipSpace(end, nullptr);
auto ptr = strstr(str, "%");
@@ -234,7 +234,7 @@ static float _toOffset(const char* str)
static int _toOpacity(const char* str)
{
char* end = nullptr;
- float opacity = svgUtilStrtof(str, &end);
+ float opacity = strToFloat(str, &end);
if (end) {
if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
@@ -362,7 +362,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
while (*str) {
str = _skipComma(str);
- float parsedValue = svgUtilStrtof(str, &end);
+ float parsedValue = strToFloat(str, &end);
if (str == end) break;
if (parsedValue <= 0.0f) break;
if (*end == '%') {
@@ -375,7 +375,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
str = end;
}
//If dash array size is 1, it means that dash and gap size are the same.
- if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
+ if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]);
}
@@ -393,7 +393,7 @@ static char* _idFromUrl(const char* url)
int i = 0;
while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
- return svgUtilStrndup(url, i);
+ return strDuplicate(url, i);
}
@@ -401,7 +401,7 @@ static unsigned char _parseColor(const char* value, char** end)
{
float r;
- r = svgUtilStrtof(value, end);
+ r = strToFloat(value, end);
*end = _skipSpace(*end, nullptr);
if (**end == '%') {
r = 255 * r / 100;
@@ -643,7 +643,7 @@ static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
str = _skipSpace(str, nullptr);
while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
- points[count++] = svgUtilStrtof(str, &end);
+ points[count++] = strToFloat(str, &end);
str = end;
str = _skipSpace(str, nullptr);
if (*str == ',') ++str;
@@ -893,7 +893,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
} else if (!strcmp(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
#ifdef THORVG_LOG_ENABLED
- } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
+ } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(strToFloat(value, nullptr)) > FLT_EPSILON) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
#endif
} else {
@@ -956,6 +956,12 @@ static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, con
_parseDashArray(loader, value, &node->style->stroke.dash);
}
+static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset);
+ node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+}
+
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
@@ -979,7 +985,7 @@ static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode*
static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
char* end = nullptr;
- const float miterlimit = svgUtilStrtof(value, &end);
+ const float miterlimit = strToFloat(value, &end);
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
// - A negative value for stroke-miterlimit must be treated as an illegal value.
@@ -1112,6 +1118,7 @@ static constexpr struct
STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
+ STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset),
STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
@@ -1141,7 +1148,7 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
while (size > 0 && isspace(value[size - 1])) {
size--;
}
- value = svgUtilStrndup(value, size);
+ value = strDuplicate(value, size);
importance = true;
}
if (style) {
@@ -2097,6 +2104,12 @@ static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
}
+static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage);
+}
+
+
static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
{
radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
@@ -2127,6 +2140,13 @@ static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
}
+static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
+ if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
+}
+
+
static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
// scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
@@ -2170,6 +2190,15 @@ static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradien
}
+static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFrPercentage) {
+ if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ }
+}
+
+
static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
if (!radial->isRPercentage) {
@@ -2211,6 +2240,14 @@ static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
}
+static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fr = from->radial->fr;
+ to->radial->isFrPercentage = from->radial->isFrPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fr);
+}
+
+
static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
{
to->radial->r = from->radial->r;
@@ -2244,7 +2281,8 @@ static constexpr struct
RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
- RADIAL_DEF(r, R, SvgGradientFlags::R)
+ RADIAL_DEF(r, R, SvgGradientFlags::R),
+ RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr)
};
@@ -2312,6 +2350,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
grad->radial->isFxPercentage = true;
grad->radial->isFyPercentage = true;
grad->radial->isRPercentage = true;
+ grad->radial->isFrPercentage = true;
loader->svgParse->gradient.parsedFx = false;
loader->svgParse->gradient.parsedFy = false;
@@ -2619,7 +2658,7 @@ static GradientFactoryMethod _findGradientFactory(const char* name)
static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
{
for (uint32_t i = 0; i < src.count; ++i) {
- dst.push(src.data[i]);
+ dst.push(src[i]);
}
}
@@ -2773,10 +2812,13 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
child->stroke.dash.array.clear();
child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
- child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ child->stroke.dash.array.push(parent->stroke.dash.array[i]);
}
}
}
+ if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) {
+ child->stroke.dash.offset = parent->stroke.dash.offset;
+ }
if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
child->stroke.cap = parent->stroke.cap;
}
@@ -2839,17 +2881,19 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
to->stroke.dash.array.clear();
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
- to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ to->stroke.dash.array.push(from->stroke.dash.array[i]);
}
}
}
+ if (from->stroke.flags & SvgStrokeFlags::DashOffset) {
+ to->stroke.dash.offset = from->stroke.dash.offset;
+ }
if (from->stroke.flags & SvgStrokeFlags::Cap) {
to->stroke.cap = from->stroke.cap;
}
if (from->stroke.flags & SvgStrokeFlags::Join) {
to->stroke.join = from->stroke.join;
}
-
if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
to->stroke.miterlimit = from->stroke.miterlimit;
}
@@ -2983,7 +3027,7 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
{
for (uint32_t i = 0; i < cloneNodes->count; ++i) {
- auto nodeIdPair = cloneNodes->data[i];
+ auto nodeIdPair = (*cloneNodes)[i];
auto defs = _getDefsNode(nodeIdPair.node);
auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
@@ -3064,7 +3108,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
loader->doc = node;
} else {
if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
- if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ if (loader->stack.count > 0) parent = loader->stack.last();
else parent = loader->doc;
if (!strcmp(tagName, "style")) {
// TODO: For now only the first style node is saved. After the css id selector
@@ -3085,7 +3129,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
loader->stack.push(node);
}
} else if ((method = _findGraphicsFactory(tagName))) {
- if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ if (loader->stack.count > 0) parent = loader->stack.last();
else parent = loader->doc;
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
} else if ((gradientMethod = _findGradientFactory(tagName))) {
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
index 961438ff42..1809c7749c 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -100,7 +100,8 @@ enum class SvgStrokeFlags
Cap = 0x20,
Join = 0x40,
Dash = 0x80,
- Miterlimit = 0x100
+ Miterlimit = 0x100,
+ DashOffset = 0x200
};
constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
@@ -139,7 +140,8 @@ enum class SvgStyleFlags
MaskType = 0x4000,
Display = 0x8000,
PaintOrder = 0x10000,
- StrokeMiterlimit = 0x20000
+ StrokeMiterlimit = 0x20000,
+ StrokeDashOffset = 0x40000,
};
constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
@@ -182,7 +184,8 @@ enum class SvgGradientFlags
Cy = 0x80,
R = 0x100,
Fx = 0x200,
- Fy = 0x400
+ Fy = 0x400,
+ Fr = 0x800
};
constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b)
@@ -390,11 +393,13 @@ struct SvgRadialGradient
float fx;
float fy;
float r;
+ float fr;
bool isCxPercentage;
bool isCyPercentage;
bool isFxPercentage;
bool isFyPercentage;
bool isRPercentage;
+ bool isFrPercentage;
};
struct SvgComposite
@@ -423,6 +428,7 @@ struct SvgPaint
struct SvgDash
{
Array<float> array;
+ float offset;
};
struct SvgStyleGradient
@@ -469,7 +475,6 @@ struct SvgStyleStroke
StrokeJoin join;
float miterlimit;
SvgDash dash;
- int dashCount;
};
struct SvgStyleProperty
@@ -561,18 +566,4 @@ struct Box
float x, y, w, h;
};
-/*
- * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
- *
- * src should be one of the following form :
- *
- * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
- * [whitespace] [sign] {INF | INFINITY}
- * [whitespace] [sign] NAN [sequence]
- *
- * No hexadecimal form supported
- * no sequence supported after NAN
- */
-float customStrtof(const char *nptr, char **endptr);
-
#endif
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
index e044931b51..79a9c0771d 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -55,7 +55,7 @@
#include <ctype.h>
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgPath.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
/************************************************************************/
/* Internal Class Implementation */
@@ -74,7 +74,7 @@ static char* _skipComma(const char* content)
static bool _parseNumber(char** content, float* number)
{
char* end = NULL;
- *number = svgUtilStrtof(*content, &end);
+ *number = strToFloat(*content, &end);
//If the start of string is not number
if ((*content) == end) return false;
//Skip comma if any
@@ -382,7 +382,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
case 's':
case 'S': {
Point p[3], ctrl;
- if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
!(*isQuadratic)) {
ctrl.x = 2 * cur->x - curCtl->x;
ctrl.y = 2 * cur->y - curCtl->y;
@@ -423,7 +423,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
case 't':
case 'T': {
Point p[3], ctrl;
- if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
*isQuadratic) {
ctrl.x = 2 * cur->x - curCtl->x;
ctrl.y = 2 * cur->y - curCtl->y;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index e5beef8093..71712442e8 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -52,6 +52,11 @@
#include "tvgMath.h" /* to include math.h before cstring */
#include <cstring>
#include <string>
+#include "tvgShapeImpl.h"
+#include "tvgCompressor.h"
+#include "tvgPaint.h"
+#include "tvgFill.h"
+#include "tvgStr.h"
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgSceneBuilder.h"
#include "tvgSvgPath.h"
@@ -62,6 +67,7 @@
/************************************************************************/
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
+static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform);
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
@@ -138,7 +144,7 @@ static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient*
if (!stops) return fillGrad;
auto prevOffset = 0.0f;
for (uint32_t i = 0; i < g->stops.count; ++i) {
- auto colorStop = &g->stops.data[i];
+ auto colorStop = &g->stops[i];
//Use premultiplied color
stops[i].r = colorStop->r;
stops[i].g = colorStop->g;
@@ -175,6 +181,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
g->radial->fx = g->radial->fx * vBox.w;
g->radial->fy = g->radial->fy * vBox.h;
+ g->radial->fr = g->radial->fr * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
} else {
Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
if (isTransform) _transformMultiply(&m, &finalTransform);
@@ -186,11 +193,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
if (isTransform) fillGrad->transform(finalTransform);
- //TODO: Tvg is not support to focal
- //if (g->radial->fx != 0 && g->radial->fy != 0) {
- // fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
- //}
- fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
+ P(fillGrad)->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr);
fillGrad->spread(g->spread);
//Update the stops
@@ -200,7 +203,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
if (!stops) return fillGrad;
auto prevOffset = 0.0f;
for (uint32_t i = 0; i < g->stops.count; ++i) {
- auto colorStop = &g->stops.data[i];
+ auto colorStop = &g->stops[i];
//Use premultiplied color
stops[i].r = colorStop->r;
stops[i].g = colorStop->g;
@@ -219,20 +222,50 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
}
-static bool _appendChildShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+//The SVG standard allows only for 'use' nodes that point directly to a basic shape.
+static bool _appendClipUseNode(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
{
- auto valid = false;
+ if (node->child.count != 1) return false;
+ auto child = *(node->child.data);
- if (_appendShape(node, shape, vBox, svgPath)) valid = true;
+ Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ 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);
+ }
+ if (child->transform) finalTransform = mathMultiply(child->transform, &finalTransform);
- if (node->child.count > 0) {
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- if (_appendChildShape(*child, shape, vBox, svgPath)) valid = true;
- }
+ return _appendClipShape(child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
+}
+
+
+static bool _appendClipChild(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, bool clip)
+{
+ if (node->type == SvgNodeType::Use) {
+ return _appendClipUseNode(node, shape, vBox, svgPath);
}
+ return _appendClipShape(node, shape, vBox, svgPath, nullptr);
+}
- return valid;
+
+static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type)
+{
+ Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ //The initial mask transformation ignored according to the SVG standard.
+ if (node->transform && type != SvgNodeType::Mask) {
+ m = *node->transform;
+ }
+ if (compNode->transform) {
+ m = mathMultiply(&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);
+ }
+ return m;
}
@@ -251,19 +284,18 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
auto comp = Shape::gen();
auto child = compNode->child.data;
- auto valid = false; //Composite only when valid shapes are existed
+ auto valid = false; //Composite only when valid shapes exist
for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
- if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
+ if (_appendClipChild(*child, comp.get(), vBox, svgPath, compNode->child.count > 1)) valid = true;
}
- if (node->transform) {
- auto m = comp->transform();
- m = mathMultiply(node->transform, &m);
- comp->transform(m);
- }
+ if (valid) {
+ Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
+ comp->transform(finalTransform);
- if (valid) paint->composite(std::move(comp), CompositeMethod::ClipPath);
+ paint->composite(std::move(comp), CompositeMethod::ClipPath);
+ }
node->style->clipPath.applying = false;
}
@@ -280,9 +312,9 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
node->style->mask.applying = true;
bool isMaskWhite = true;
- auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite);
- if (comp) {
- if (node->transform) comp->transform(*node->transform);
+ if (auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite)) {
+ Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::Mask);
+ comp->transform(finalTransform);
if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) {
paint->composite(std::move(comp), CompositeMethod::LumaMask);
@@ -297,11 +329,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
}
-static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath)
+static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip)
{
SvgStyleProperty* style = node->style;
- if (node->transform) vg->transform(*node->transform);
+ //Clip transformation is applied directly to the path in the _appendClipShape function
+ if (node->transform && !clip) vg->transform(*node->transform);
if (node->type == SvgNodeType::Doc || !node->display) return;
//If fill property is nullptr then do nothing
@@ -344,7 +377,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
vg->stroke(style->stroke.join);
vg->strokeMiterlimit(style->stroke.miterlimit);
if (style->stroke.dash.array.count > 0) {
- vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count);
+ P(vg)->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset);
}
//If stroke property is nullptr then do nothing
@@ -383,14 +416,13 @@ static unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, const Box& vBox, const
}
-static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+static bool _recognizeShape(SvgNode* node, Shape* shape)
{
- Array<PathCommand> cmds;
- Array<Point> pts;
-
switch (node->type) {
case SvgNodeType::Path: {
if (node->node.path.path) {
+ Array<PathCommand> cmds;
+ Array<Point> pts;
if (svgPathToTvgPath(node->node.path.path, cmds, pts)) {
shape->appendPath(cmds.data, cmds.count, pts.data, pts.count);
}
@@ -437,8 +469,41 @@ static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const str
return false;
}
}
+ return true;
+}
+
- _applyProperty(node, shape, vBox, svgPath);
+static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+{
+ if (!_recognizeShape(node, shape)) return false;
+
+ _applyProperty(node, shape, vBox, svgPath, false);
+ return true;
+}
+
+
+static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform)
+{
+ //The 'transform' matrix has higher priority than the node->transform, since it already contains it
+ auto m = transform ? transform : (node->transform ? node->transform : nullptr);
+
+ uint32_t currentPtsCnt = 0;
+ if (m) {
+ const Point *tmp = nullptr;
+ currentPtsCnt = shape->pathCoords(&tmp);
+ }
+
+ if (!_recognizeShape(node, shape)) return false;
+
+ if (m) {
+ const Point *pts = nullptr;
+ auto ptsCnt = shape->pathCoords(&pts);
+
+ auto p = const_cast<Point*>(pts) + currentPtsCnt;
+ while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m);
+ }
+
+ _applyProperty(node, shape, vBox, svgPath, true);
return true;
}
@@ -514,12 +579,15 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
return false;
}
+#include "tvgTaskScheduler.h"
static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
{
if (!node->node.image.href) return nullptr;
auto picture = Picture::gen();
+ TaskScheduler::async(false); //force to load a picture on the same thread
+
const char* href = node->node.image.href;
if (!strncmp(href, "data:", sizeof("data:") - 1)) {
href += sizeof("data:") - 1;
@@ -527,11 +595,22 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
imageMimeTypeEncoding encoding;
if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding
if (encoding == imageMimeTypeEncoding::base64) {
- string decoded = svgUtilBase64Decode(href);
- if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ char* decoded = nullptr;
+ auto size = b64Decode(href, strlen(href), &decoded);
+ //OPTIMIZE: Skip data copy.
+ if (picture->load(decoded, size, mimetype, true) != Result::Success) {
+ free(decoded);
+ TaskScheduler::async(true);
+ return nullptr;
+ }
+ free(decoded);
} else {
string decoded = svgUtilURLDecode(href);
- if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ //OPTIMIZE: Skip data copy.
+ if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) {
+ TaskScheduler::async(true);
+ return nullptr;
+ }
}
} else {
if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1;
@@ -540,6 +619,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
const char *dot = strrchr(href, '.');
if (dot && !strcmp(dot, ".svg")) {
TVGLOG("SVG", "Embedded svg file is disabled.");
+ TaskScheduler::async(true);
return nullptr;
}
string imagePath = href;
@@ -547,9 +627,14 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
auto last = svgPath.find_last_of("/");
imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath;
}
- if (picture->load(imagePath) != Result::Success) return nullptr;
+ if (picture->load(imagePath) != Result::Success) {
+ TaskScheduler::async(true);
+ return nullptr;
+ }
}
+ TaskScheduler::async(true);
+
float w, h;
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
index 2aaeb2b25d..763a357f99 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -21,19 +21,12 @@
*/
#include <cstring>
-#include <math.h>
-#include <memory.h>
#include "tvgSvgUtil.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
-static inline bool _floatExact(float a, float b)
-{
- return memcmp(&a, &b, sizeof(float)) == 0;
-}
-
static uint8_t _hexCharToDec(const char c)
{
if (c >= 'a') return c - 'a' + 10;
@@ -41,181 +34,11 @@ static uint8_t _hexCharToDec(const char c)
else return c - '0';
}
-static uint8_t _base64Value(const char chr)
-{
- if (chr >= 'A' && chr <= 'Z') return chr - 'A';
- else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
- else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
- else if (chr == '+' || chr == '-') return 62;
- else return 63;
-}
-
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
-
-/*
- * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
- *
- * src should be one of the following form :
- *
- * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
- * [whitespace] [sign] {INF | INFINITY}
- * [whitespace] [sign] NAN [sequence]
- *
- * No hexadecimal form supported
- * no sequence supported after NAN
- */
-float svgUtilStrtof(const char *nPtr, char **endPtr)
-{
- if (endPtr) *endPtr = (char*)(nPtr);
- if (!nPtr) return 0.0f;
-
- auto a = nPtr;
- auto iter = nPtr;
- auto val = 0.0f;
- unsigned long long integerPart = 0;
- int minus = 1;
-
- //ignore leading whitespaces
- while (isspace(*iter)) iter++;
-
- //signed or not
- if (*iter == '-') {
- minus = -1;
- iter++;
- } else if (*iter == '+') {
- iter++;
- }
-
- if (tolower(*iter) == 'i') {
- if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
- else goto error;
-
- if (tolower(*(iter)) == 'i') {
- if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5;
- else goto error;
- }
- if (endPtr) *endPtr = (char *)(iter);
- return (minus == -1) ? -INFINITY : INFINITY;
- }
-
- if (tolower(*iter) == 'n') {
- if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
- else goto error;
-
- if (endPtr) *endPtr = (char *)(iter);
- return (minus == -1) ? -NAN : NAN;
- }
-
- //Optional: integer part before dot
- if (isdigit(*iter)) {
- for (; isdigit(*iter); iter++) {
- integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0');
- }
- a = iter;
- } else if (*iter != '.') {
- goto success;
- }
-
- val = static_cast<float>(integerPart);
-
- //Optional: decimal part after dot
- if (*iter == '.') {
- unsigned long long decimalPart = 0;
- unsigned long long pow10 = 1;
- int count = 0;
-
- iter++;
-
- if (isdigit(*iter)) {
- for (; isdigit(*iter); iter++, count++) {
- if (count < 19) {
- decimalPart = decimalPart * 10ULL + + static_cast<unsigned long long>(*iter - '0');
- pow10 *= 10ULL;
- }
- }
- } else if (isspace(*iter)) { //skip if there is a space after the dot.
- a = iter;
- goto success;
- }
-
- val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
- a = iter;
- }
-
- //Optional: exponent
- if (*iter == 'e' || *iter == 'E') {
- ++iter;
-
- //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
- if ((*iter == 'm') || (*iter == 'M')) {
- //TODO: We don't support font em unit now, but has to multiply val * font size later...
- a = iter + 1;
- goto success;
- }
-
- //signed or not
- int minus_e = 1;
-
- if (*iter == '-') {
- minus_e = -1;
- ++iter;
- } else if (*iter == '+') {
- iter++;
- }
-
- unsigned int exponentPart = 0;
-
- if (isdigit(*iter)) {
- while (*iter == '0') iter++;
- for (; isdigit(*iter); iter++) {
- exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
- }
- } else if (!isdigit(*(a - 1))) {
- a = nPtr;
- goto success;
- } else if (*iter == 0) {
- goto success;
- }
-
- //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
- if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
- //val *= 1.0e-308f;
- val *= 1.0e-38f;
- a = iter;
- goto success;
- }
-
- a = iter;
- auto scale = 1.0f;
-
- while (exponentPart >= 8U) {
- scale *= 1E8;
- exponentPart -= 8U;
- }
- while (exponentPart > 0U) {
- scale *= 10.0f;
- exponentPart--;
- }
- val = (minus_e == -1) ? (val / scale) : (val * scale);
- } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
- a = nPtr;
- goto success;
- }
-
-success:
- if (endPtr) *endPtr = (char*)(a);
- return minus * val;
-
-error:
- if (endPtr) *endPtr = (char*)(nPtr);
- return 0.0f;
-}
-
-
string svgUtilURLDecode(const char *src)
{
if (!src) return nullptr;
@@ -242,49 +65,3 @@ string svgUtilURLDecode(const char *src)
}
return decoded;
}
-
-
-string svgUtilBase64Decode(const char *src)
-{
- if (!src) return nullptr;
-
- auto length = strlen(src);
- if (length == 0) return nullptr;
-
- string decoded;
- decoded.reserve(3*(1+(length >> 2)));
-
- while (*src && *(src+1)) {
- if (*src <= 0x20) {
- ++src;
- continue;
- }
-
- auto value1 = _base64Value(src[0]);
- auto value2 = _base64Value(src[1]);
- decoded += (value1 << 2) + ((value2 & 0x30) >> 4);
-
- if (!src[2] || src[2] == '=' || src[2] == '.') break;
- auto value3 = _base64Value(src[2]);
- decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
-
- if (!src[3] || src[3] == '=' || src[3] == '.') break;
- auto value4 = _base64Value(src[3]);
- decoded += ((value3 & 0x03) << 6) + value4;
- src += 4;
- }
- return decoded;
-}
-
-
-char* svgUtilStrndup(const char* str, size_t n)
-{
- auto len = strlen(str);
- if (len < n) n = len;
-
- auto ret = (char*)malloc(n + 1);
- if (!ret) return nullptr;
- ret[n] = '\0';
-
- return (char*)memcpy(ret, str, n);
-}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
index 48be4649bc..e914eadc65 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
@@ -25,11 +25,6 @@
#include "tvgCommon.h"
-float svgUtilStrtof(const char *nPtr, char **endPtr);
-
string svgUtilURLDecode(const char *src);
-string svgUtilBase64Decode(const char *src);
-
-char* svgUtilStrndup(const char* str, size_t n);
#endif //_TVG_SVG_UTIL_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
index faacfcd223..dbc3b17b70 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -33,7 +33,7 @@
#endif
#include "tvgXmlParser.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
/************************************************************************/
/* Internal Class Implementation */
@@ -557,10 +557,10 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char
}
if (p == itr) *tag = strdup("all");
- else *tag = svgUtilStrndup(itr, p - itr);
+ else *tag = strDuplicate(itr, p - itr);
if (p == itrEnd) *name = nullptr;
- else *name = svgUtilStrndup(p + 1, itrEnd - p - 1);
+ else *name = strDuplicate(p + 1, itrEnd - p - 1);
return (nextElement ? nextElement + 1 : nullptr);
}
diff --git a/thirdparty/thorvg/src/lib/tvgArray.h b/thirdparty/thorvg/src/utils/tvgArray.h
index 0e8aef3071..919da7e108 100644
--- a/thirdparty/thorvg/src/lib/tvgArray.h
+++ b/thirdparty/thorvg/src/utils/tvgArray.h
@@ -73,11 +73,36 @@ struct Array
return reserve(count + size);
}
- T* end() const
+ const T& operator[](size_t idx) const
+ {
+ return data[idx];
+ }
+
+ T& operator[](size_t idx)
+ {
+ return data[idx];
+ }
+
+ T* end()
+ {
+ return data + count;
+ }
+
+ const T* end() const
{
return data + count;
}
+ const T& last() const
+ {
+ return data[count - 1];
+ }
+
+ const T& first() const
+ {
+ return data[0];
+ }
+
T& last()
{
return data[count - 1];
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.cpp b/thirdparty/thorvg/src/utils/tvgBezier.cpp
index 87511a06c7..f9daf07b84 100644
--- a/thirdparty/thorvg/src/lib/tvgBezier.cpp
+++ b/thirdparty/thorvg/src/utils/tvgBezier.cpp
@@ -116,7 +116,7 @@ float bezAt(const Bezier& bz, float at, float length)
//just in case to prevent an infinite loop
if (at <= 0) return 0.0f;
- if (at >= length) return length;
+ if (at >= length) return 1.0f;
while (true) {
auto right = bz;
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.h b/thirdparty/thorvg/src/utils/tvgBezier.h
index 539a63bdd3..539a63bdd3 100644
--- a/thirdparty/thorvg/src/lib/tvgBezier.h
+++ b/thirdparty/thorvg/src/utils/tvgBezier.h
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.cpp b/thirdparty/thorvg/src/utils/tvgCompressor.cpp
index 52f4ed6716..e38940f3d0 100644
--- a/thirdparty/thorvg/src/lib/tvgLzw.cpp
+++ b/thirdparty/thorvg/src/utils/tvgCompressor.cpp
@@ -55,17 +55,20 @@
*/
#include "config.h"
-#if defined(THORVG_TVG_SAVER_SUPPORT) || defined(THORVG_TVG_LOADER_SUPPORT)
-/************************************************************************/
-/* Internal Class Implementation */
-/************************************************************************/
#include <string>
#include <memory.h>
-#include "tvgLzw.h"
+#include "tvgCompressor.h"
+
+namespace tvg {
+
+
+/************************************************************************/
+/* LZW Implementation */
+/************************************************************************/
+
-namespace {
//LZW Dictionary helper:
constexpr int Nil = -1;
constexpr int MaxDictBits = 12;
@@ -334,15 +337,8 @@ static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, i
}
return true;
}
-}
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
-namespace tvg {
-
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
{
int code = Nil;
@@ -423,6 +419,57 @@ uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes,
return bitStream.release();
}
+
+/************************************************************************/
+/* B64 Implementation */
+/************************************************************************/
+
+
+size_t b64Decode(const char* encoded, const size_t len, char** decoded)
+{
+ static constexpr const char B64_INDEX[256] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+ };
+
+
+ if (!decoded || !encoded || len == 0) return 0;
+
+ auto reserved = 3 * (1 + (len >> 2)) + 1;
+ auto output = static_cast<char*>(malloc(reserved * sizeof(char)));
+ if (!output) return 0;
+ output[reserved - 1] = '\0';
+
+ size_t idx = 0;
+
+ while (*encoded && *(encoded + 1)) {
+ if (*encoded <= 0x20) {
+ ++encoded;
+ continue;
+ }
+
+ auto value1 = B64_INDEX[(size_t)encoded[0]];
+ auto value2 = B64_INDEX[(size_t)encoded[1]];
+ output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
+
+ if (!encoded[2] || encoded[2] == '=' || encoded[2] == '.') break;
+ auto value3 = B64_INDEX[(size_t)encoded[2]];
+ output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
+
+ if (!encoded[3] || encoded[3] == '=' || encoded[3] == '.') break;
+ auto value4 = B64_INDEX[(size_t)encoded[3]];
+ output[idx++] = ((value3 & 0x03) << 6) + value4;
+ encoded += 4;
+ }
+ *decoded = output;
+ return reserved;
}
-#endif
+
+}
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.h b/thirdparty/thorvg/src/utils/tvgCompressor.h
index bd4e783fbf..05d23f4293 100644
--- a/thirdparty/thorvg/src/lib/tvgLzw.h
+++ b/thirdparty/thorvg/src/utils/tvgCompressor.h
@@ -20,8 +20,8 @@
* SOFTWARE.
*/
-#ifndef _TVG_LZW_H_
-#define _TVG_LZW_H_
+#ifndef _TVG_COMPRESSOR_H_
+#define _TVG_COMPRESSOR_H_
#include <cstdint>
@@ -29,6 +29,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);
}
-#endif //_TVG_LZW_H
+#endif //_TVG_COMPRESSOR_H_
diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/utils/tvgMath.h
index afe1849825..897ff46427 100644
--- a/thirdparty/thorvg/src/lib/tvgMath.h
+++ b/thirdparty/thorvg/src/utils/tvgMath.h
@@ -29,6 +29,8 @@
#include <math.h>
#include "tvgCommon.h"
+#define MATH_PI 3.14159265358979323846f
+#define MATH_PI2 1.57079632679489661923f
#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
@@ -45,6 +47,7 @@ static inline bool mathEqual(float a, float b)
return (fabsf(a - b) < FLT_EPSILON);
}
+
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) ||
@@ -55,6 +58,7 @@ static inline bool mathEqual(const Matrix& a, const Matrix& b)
return true;
}
+
static inline bool mathRightAngle(const Matrix* m)
{
auto radian = fabsf(atan2f(m->e21, m->e11));
@@ -118,6 +122,15 @@ 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;
@@ -125,6 +138,19 @@ static inline void mathScale(Matrix* m, float sx, float sy)
}
+static inline void mathScaleR(Matrix* m, float x, float y)
+{
+ if (x != 1.0f) {
+ m->e11 *= x;
+ m->e21 *= x;
+ }
+ if (y != 1.0f) {
+ m->e22 *= y;
+ m->e12 *= y;
+ }
+}
+
+
static inline void mathTranslate(Matrix* m, float x, float y)
{
m->e13 += x;
@@ -174,6 +200,32 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
}
+static inline void mathTranslateR(Matrix* m, float x, float y)
+{
+ if (x == 0.0f && y == 0.0f) return;
+ m->e13 += (x * m->e11 + y * m->e12);
+ m->e23 += (x * m->e21 + y * m->e22);
+}
+
+
+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);
+}
+
+
+static inline float mathLength(const Point* a, const Point* b)
+{
+ auto x = b->x - a->x;
+ auto y = b->y - a->y;
+
+ if (x < 0) x = -x;
+ if (y < 0) y = -y;
+
+ return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x);
+}
+
+
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
diff --git a/thirdparty/thorvg/src/utils/tvgStr.cpp b/thirdparty/thorvg/src/utils/tvgStr.cpp
new file mode 100644
index 0000000000..eeed4fc404
--- /dev/null
+++ b/thirdparty/thorvg/src/utils/tvgStr.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+#include <cstring>
+#include <memory.h>
+#include "tvgMath.h"
+#include "tvgStr.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static inline bool _floatExact(float a, float b)
+{
+ return memcmp(&a, &b, sizeof(float)) == 0;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+namespace tvg {
+
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
+float strToFloat(const char *nPtr, char **endPtr)
+{
+ if (endPtr) *endPtr = (char *) (nPtr);
+ if (!nPtr) return 0.0f;
+
+ auto a = nPtr;
+ auto iter = nPtr;
+ auto val = 0.0f;
+ unsigned long long integerPart = 0;
+ int minus = 1;
+
+ //ignore leading whitespaces
+ while (isspace(*iter)) iter++;
+
+ //signed or not
+ if (*iter == '-') {
+ minus = -1;
+ iter++;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ if (tolower(*iter) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
+ else goto error;
+
+ if (tolower(*(iter)) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') &&
+ (tolower(*(iter + 4)) == 'y'))
+ iter += 5;
+ else goto error;
+ }
+ if (endPtr) *endPtr = (char *) (iter);
+ return (minus == -1) ? -INFINITY : INFINITY;
+ }
+
+ if (tolower(*iter) == 'n') {
+ if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
+ else goto error;
+
+ if (endPtr) *endPtr = (char *) (iter);
+ return (minus == -1) ? -NAN : NAN;
+ }
+
+ //Optional: integer part before dot
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++) {
+ integerPart = integerPart * 10ULL + (unsigned long long) (*iter - '0');
+ }
+ a = iter;
+ } else if (*iter != '.') {
+ goto success;
+ }
+
+ val = static_cast<float>(integerPart);
+
+ //Optional: decimal part after dot
+ if (*iter == '.') {
+ unsigned long long decimalPart = 0;
+ unsigned long long pow10 = 1;
+ int count = 0;
+
+ iter++;
+
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++, count++) {
+ if (count < 19) {
+ decimalPart = decimalPart * 10ULL + +static_cast<unsigned long long>(*iter - '0');
+ pow10 *= 10ULL;
+ }
+ }
+ } else if (isspace(*iter)) { //skip if there is a space after the dot.
+ a = iter;
+ goto success;
+ }
+
+ val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
+ a = iter;
+ }
+
+ //Optional: exponent
+ if (*iter == 'e' || *iter == 'E') {
+ ++iter;
+
+ //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
+ if ((*iter == 'm') || (*iter == 'M')) {
+ //TODO: We don't support font em unit now, but has to multiply val * font size later...
+ a = iter + 1;
+ goto success;
+ }
+
+ //signed or not
+ int minus_e = 1;
+
+ if (*iter == '-') {
+ minus_e = -1;
+ ++iter;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ unsigned int exponentPart = 0;
+
+ if (isdigit(*iter)) {
+ while (*iter == '0') iter++;
+ for (; isdigit(*iter); iter++) {
+ exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
+ }
+ } else if (!isdigit(*(a - 1))) {
+ a = nPtr;
+ goto success;
+ } else if (*iter == 0) {
+ goto success;
+ }
+
+ //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
+ if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
+ //val *= 1.0e-308f;
+ val *= 1.0e-38f;
+ a = iter;
+ goto success;
+ }
+
+ a = iter;
+ auto scale = 1.0f;
+
+ while (exponentPart >= 8U) {
+ scale *= 1E8;
+ exponentPart -= 8U;
+ }
+ while (exponentPart > 0U) {
+ scale *= 10.0f;
+ exponentPart--;
+ }
+ val = (minus_e == -1) ? (val / scale) : (val * scale);
+ } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
+ a = nPtr;
+ goto success;
+ }
+
+success:
+ if (endPtr) *endPtr = (char *)(a);
+ return minus * val;
+
+error:
+ if (endPtr) *endPtr = (char *)(nPtr);
+ return 0.0f;
+}
+
+
+int str2int(const char* str, size_t n)
+{
+ int ret = 0;
+ for(size_t i = 0; i < n; ++i) {
+ ret = ret * 10 + (str[i] - '0');
+ }
+ return ret;
+}
+
+char* strDuplicate(const char *str, size_t n)
+{
+ auto len = strlen(str);
+ if (len < n) n = len;
+
+ auto ret = (char *) malloc(n + 1);
+ if (!ret) return nullptr;
+ ret[n] = '\0';
+
+ return (char *) memcpy(ret, str, n);
+}
+
+char* strDirname(const char* path)
+{
+ const char *ptr = strrchr(path, '/');
+#ifdef _WIN32
+ if (ptr) ptr = strrchr(ptr + 1, '\\');
+#endif
+ int len = int(ptr + 1 - path); // +1 to include '/'
+ return strDuplicate(path, len);
+}
+
+}
diff --git a/thirdparty/thorvg/src/utils/tvgStr.h b/thirdparty/thorvg/src/utils/tvgStr.h
new file mode 100644
index 0000000000..448cc69336
--- /dev/null
+++ b/thirdparty/thorvg/src/utils/tvgStr.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_STR_H_
+#define _TVG_STR_H_
+
+#include <cstddef>
+
+namespace tvg
+{
+
+float strToFloat(const char *nPtr, char **endPtr); //convert to float
+int str2int(const char* str, size_t n); //convert to integer
+char* strDuplicate(const char *str, size_t n); //copy the string
+char* strDirname(const char* path); //return the full directory name
+
+}
+#endif //_TVG_STR_H_
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index f57824872b..804d3b76db 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-VERSION=0.10.0
+VERSION=0.10.7
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
@@ -32,7 +32,7 @@ cat << EOF > ../inc/config.h
EOF
mkdir ../src
-cp -rv src/lib ../src/
+cp -rv src/lib src/utils ../src/
# Only sw_engine is enabled.
rm -rfv ../src/lib/gl_engine