summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/gdscript.cpp66
-rw-r--r--modules/gdscript/gdscript_cache.cpp30
-rw-r--r--modules/gdscript/gdscript_cache.h2
-rw-r--r--modules/gdscript/gdscript_compiler.cpp10
-rw-r--r--modules/gdscript/gdscript_compiler.h1
-rw-r--r--modules/gdscript/gdscript_editor.cpp89
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd25
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out17
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp7
-rw-r--r--modules/gltf/gltf_document.cpp4
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp2
-rw-r--r--modules/openxr/openxr_api.cpp35
-rw-r--r--modules/openxr/openxr_api.h13
-rw-r--r--modules/openxr/openxr_interface.cpp2
-rw-r--r--modules/raycast/SCsub7
-rw-r--r--modules/raycast/config.py14
-rw-r--r--modules/text_server_adv/text_server_adv.cpp274
-rw-r--r--modules/text_server_adv/text_server_adv.h142
-rw-r--r--modules/text_server_fb/text_server_fb.cpp243
-rw-r--r--modules/text_server_fb/text_server_fb.h141
21 files changed, 1010 insertions, 117 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 60230257e0..c8195de640 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2019,6 +2019,42 @@ Error GDScriptLanguage::execute_file(const String &p_path) {
}
void GDScriptLanguage::finish() {
+ if (_call_stack) {
+ memdelete_arr(_call_stack);
+ _call_stack = nullptr;
+ }
+
+ // Clear the cache before parsing the script_list
+ GDScriptCache::clear();
+
+ // Clear dependencies between scripts, to ensure cyclic references are broken
+ // (to avoid leaks at exit).
+ SelfList<GDScript> *s = script_list.first();
+ while (s) {
+ // This ensures the current script is not released before we can check
+ // what's the next one in the list (we can't get the next upfront because we
+ // don't know if the reference breaking will cause it -or any other after
+ // it, for that matter- to be released so the next one is not the same as
+ // before).
+ Ref<GDScript> scr = s->self();
+ if (scr.is_valid()) {
+ for (KeyValue<StringName, GDScriptFunction *> &E : scr->member_functions) {
+ GDScriptFunction *func = E.value;
+ for (int i = 0; i < func->argument_types.size(); i++) {
+ func->argument_types.write[i].script_type_ref = Ref<Script>();
+ }
+ func->return_type.script_type_ref = Ref<Script>();
+ }
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) {
+ E.value.data_type.script_type_ref = Ref<Script>();
+ }
+
+ // Clear backup for scripts that could slip out of the cyclic reference
+ // check
+ scr->clear();
+ }
+ s = s->next();
+ }
}
void GDScriptLanguage::profiling_start() {
@@ -2530,36 +2566,6 @@ GDScriptLanguage::GDScriptLanguage() {
}
GDScriptLanguage::~GDScriptLanguage() {
- if (_call_stack) {
- memdelete_arr(_call_stack);
- }
-
- // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit).
- SelfList<GDScript> *s = script_list.first();
- while (s) {
- // This ensures the current script is not released before we can check what's the next one
- // in the list (we can't get the next upfront because we don't know if the reference breaking
- // will cause it -or any other after it, for that matter- to be released so the next one
- // is not the same as before).
- Ref<GDScript> scr = s->self();
- if (scr.is_valid()) {
- for (KeyValue<StringName, GDScriptFunction *> &E : scr->member_functions) {
- GDScriptFunction *func = E.value;
- for (int i = 0; i < func->argument_types.size(); i++) {
- func->argument_types.write[i].script_type_ref = Ref<Script>();
- }
- func->return_type.script_type_ref = Ref<Script>();
- }
- for (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) {
- E.value.data_type.script_type_ref = Ref<Script>();
- }
-
- // Clear backup for scripts that could slip out of the cyclic reference check
- scr->clear();
- }
- s = s->next();
- }
-
singleton = nullptr;
}
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 021504f242..699ce67933 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -381,15 +381,15 @@ void GDScriptCache::clear_unreferenced_packed_scenes() {
}
}
-GDScriptCache::GDScriptCache() {
- singleton = this;
-}
+void GDScriptCache::clear() {
+ if (singleton == nullptr) {
+ return;
+ }
-GDScriptCache::~GDScriptCache() {
- destructing = true;
+ MutexLock lock(singleton->mutex);
RBSet<Ref<GDScriptParserRef>> parser_map_refs;
- for (KeyValue<String, GDScriptParserRef *> &E : parser_map) {
+ for (KeyValue<String, GDScriptParserRef *> &E : singleton->parser_map) {
parser_map_refs.insert(E.value);
}
@@ -399,12 +399,20 @@ GDScriptCache::~GDScriptCache() {
}
parser_map_refs.clear();
- parser_map.clear();
- shallow_gdscript_cache.clear();
- full_gdscript_cache.clear();
+ singleton->parser_map.clear();
+ singleton->shallow_gdscript_cache.clear();
+ singleton->full_gdscript_cache.clear();
- packed_scene_cache.clear();
- packed_scene_dependencies.clear();
+ singleton->packed_scene_cache.clear();
+ singleton->packed_scene_dependencies.clear();
+}
+
+GDScriptCache::GDScriptCache() {
+ singleton = this;
+}
+GDScriptCache::~GDScriptCache() {
+ destructing = true;
+ clear();
singleton = nullptr;
}
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 2195932aa3..e7e1901d5d 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -111,6 +111,8 @@ public:
return singleton->destructing;
};
+ static void clear();
+
GDScriptCache();
~GDScriptCache();
};
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 24241b712b..ea93e1ebfc 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -43,7 +43,7 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN
return false;
}
- if (codegen.parameters.has(p_name) || codegen.locals.has(p_name)) {
+ if (_is_local_or_parameter(codegen, p_name)) {
return false; //shadowed
}
@@ -65,6 +65,10 @@ bool GDScriptCompiler::_is_class_member_property(GDScript *owner, const StringNa
return ClassDB::has_property(nc->get_name(), p_name);
}
+bool GDScriptCompiler::_is_local_or_parameter(CodeGen &codegen, const StringName &p_name) {
+ return codegen.parameters.has(p_name) || codegen.locals.has(p_name);
+}
+
void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::Node *p_node) {
if (!error.is_empty()) {
return;
@@ -920,7 +924,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName var_name = identifier->name;
if (_is_class_member_property(codegen, var_name)) {
assign_class_member_property = var_name;
- } else if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ } else if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
is_member_property = true;
member_property_setter_function = codegen.script->member_indices[var_name].setter;
member_property_has_setter = member_property_setter_function != StringName();
@@ -1131,7 +1135,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool is_in_setter = false;
StringName setter_function;
StringName var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
is_member = true;
setter_function = codegen.script->member_indices[var_name].setter;
has_setter = setter_function != StringName();
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 45ca4fe342..cba585e5a5 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -115,6 +115,7 @@ class GDScriptCompiler {
bool _is_class_member_property(CodeGen &codegen, const StringName &p_name);
bool _is_class_member_property(GDScript *owner, const StringName &p_name);
+ bool _is_local_or_parameter(CodeGen &codegen, const StringName &p_name);
void _set_error(const String &p_error, const GDScriptParser::Node *p_node);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index c02ee99a86..1456612915 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2512,50 +2512,62 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::SubscriptNode *p_subscript, GDScriptParser::DataType &r_base_type, Variant *r_base = nullptr) {
- if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER && p_context.base != nullptr) {
- const GDScriptParser::GetNodeNode *get_node = nullptr;
- const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);
+ if (p_context.base == nullptr) {
+ return false;
+ }
+ const GDScriptParser::GetNodeNode *get_node = nullptr;
+
+ switch (p_subscript->base->type) {
+ case GDScriptParser::Node::GET_NODE: {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(p_subscript->base);
+ } break;
- switch (identifier_node->source) {
- case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {
- if (p_context.current_class != nullptr) {
- const StringName &member_name = identifier_node->name;
- const GDScriptParser::ClassNode *current_class = p_context.current_class;
+ case GDScriptParser::Node::IDENTIFIER: {
+ const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);
- if (current_class->has_member(member_name)) {
- const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);
+ switch (identifier_node->source) {
+ case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {
+ if (p_context.current_class != nullptr) {
+ const StringName &member_name = identifier_node->name;
+ const GDScriptParser::ClassNode *current_class = p_context.current_class;
- if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
- const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);
+ if (current_class->has_member(member_name)) {
+ const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);
- if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {
- get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);
+ if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
+ const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);
+
+ if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);
+ }
}
}
}
- }
- } break;
- case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {
- if (identifier_node->next != nullptr && identifier_node->next->type == GDScriptParser::ClassNode::Node::GET_NODE) {
- get_node = static_cast<GDScriptParser::GetNodeNode *>(identifier_node->next);
- }
- } break;
- default:
- break;
- }
+ } break;
+ case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {
+ if (identifier_node->next != nullptr && identifier_node->next->type == GDScriptParser::ClassNode::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(identifier_node->next);
+ }
+ } break;
+ default: {
+ } break;
+ }
+ } break;
+ default: {
+ } break;
+ }
- if (get_node != nullptr) {
- const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));
- if (node != nullptr) {
- if (r_base != nullptr) {
- *r_base = node;
- }
- r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- r_base_type.kind = GDScriptParser::DataType::NATIVE;
- r_base_type.native_type = node->get_class_name();
- r_base_type.builtin_type = Variant::OBJECT;
- return true;
+ if (get_node != nullptr) {
+ const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));
+ if (node != nullptr) {
+ if (r_base != nullptr) {
+ *r_base = node;
}
+ r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_base_type.kind = GDScriptParser::DataType::NATIVE;
+ r_base_type.native_type = node->get_class_name();
+ r_base_type.builtin_type = Variant::OBJECT;
+ return true;
}
}
@@ -2612,7 +2624,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
- if (p_context.base != nullptr && subscript->is_attribute) {
+ if (subscript->is_attribute) {
bool found_type = _get_subscript_type(p_context, subscript, base_type, &base);
if (!found_type) {
@@ -3276,6 +3288,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
parser.parse(p_code, p_path, true);
GDScriptParser::CompletionContext context = parser.get_completion_context();
+ context.base = p_owner;
// Allows class functions with the names like built-ins to be handled properly.
if (context.type != GDScriptParser::COMPLETION_ATTRIBUTE) {
@@ -3448,7 +3461,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
break;
}
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(context, subscript->base, base)) {
+
+ bool found_type = _get_subscript_type(context, subscript, base.type);
+ if (!found_type && !_guess_expression_type(context, subscript->base, base)) {
break;
}
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index de3becbaf8..e442bf8159 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -844,8 +844,9 @@ Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
lines = p_code.split("\n");
Error err = GDScriptParser::parse(p_code, p_path, false);
+ GDScriptAnalyzer analyzer(this);
+
if (err == OK) {
- GDScriptAnalyzer analyzer(this);
err = analyzer.analyze();
}
update_diagnostics();
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
new file mode 100644
index 0000000000..f33ba7dffd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
@@ -0,0 +1,25 @@
+# https://github.com/godotengine/godot/pull/69620
+
+var a: int = 1
+
+func shadow_regular_assignment(a: Variant, b: Variant) -> void:
+ print(a)
+ print(self.a)
+ a = b
+ print(a)
+ print(self.a)
+
+
+var v := Vector2(0.0, 0.0)
+
+func shadow_subscript_assignment(v: Vector2, x: float) -> void:
+ print(v)
+ print(self.v)
+ v.x += x
+ print(v)
+ print(self.v)
+
+
+func test():
+ shadow_regular_assignment('a', 'b')
+ shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
new file mode 100644
index 0000000000..5b981bc8bb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
@@ -0,0 +1,17 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> SHADOWED_VARIABLE
+>> The local function parameter "a" is shadowing an already-declared variable at line 3.
+>> WARNING
+>> Line: 15
+>> SHADOWED_VARIABLE
+>> The local function parameter "v" is shadowing an already-declared variable at line 13.
+a
+1
+b
+1
+(1, 1)
+(0, 0)
+(6, 1)
+(0, 0)
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
index 3cf49a3046..a194719b91 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -63,7 +63,12 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
if (p_options.has("animation/import")) {
state->set_create_animations(bool(p_options["animation/import"]));
}
- return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
+
+ if (p_options.has("animation/trimming")) {
+ return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
+ } else {
+ return doc->generate_scene(state, (float)p_options["animation/fps"], false);
+ }
}
#endif // TOOLS_ENABLED
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 735e35ac1e..a3685daf0e 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -796,7 +796,7 @@ Error GLTFDocument::_parse_buffers(Ref<GLTFState> state, const String &p_base_pa
ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
uri = uri.uri_decode();
uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows.
- buffer_data = FileAccess::get_file_as_array(uri);
+ buffer_data = FileAccess::get_file_as_bytes(uri);
ERR_FAIL_COND_V_MSG(buffer.size() == 0, ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri);
}
@@ -3139,7 +3139,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
// Fallback to loading as byte array.
// This enables us to support the spec's requirement that we honor mimetype
// regardless of file URI.
- data = FileAccess::get_file_as_array(uri);
+ data = FileAccess::get_file_as_bytes(uri);
if (data.size() == 0) {
WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s. Skipping it.", i, mimetype, uri));
state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index 234c5f8391..3b7c130149 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -136,7 +136,7 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR;
graphics_binding_gl.next = p_next_pointer;
- graphics_binding_gl.display = eglGetCurrentDisplay();
+ graphics_binding_gl.display = (void *)display_server->window_get_native_handle(DisplayServer::DISPLAY_HANDLE);
graphics_binding_gl.config = (EGLConfig)0; // https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/tests/hello_xr/graphicsplugin_opengles.cpp#L122
graphics_binding_gl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
#else
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 59d3865acd..b652ca4617 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -740,9 +740,10 @@ bool OpenXRAPI::create_swapchains() {
ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views");
// We create our depth swapchain if:
+ // - we've enabled submitting depth buffer
// - we support our depth layer extension
// - we have our spacewarp extension (not yet implemented)
- if (OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
+ if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
// Build a vector with swapchain formats we want to use, from best fit to worst
Vector<int64_t> usable_swapchain_formats;
int64_t swapchain_format_to_use = 0;
@@ -790,13 +791,13 @@ bool OpenXRAPI::create_swapchains() {
projection_views[i].subImage.imageRect.extent.width = recommended_size.width;
projection_views[i].subImage.imageRect.extent.height = recommended_size.height;
- if (OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) {
+ if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) {
projection_views[i].next = &depth_views[i];
depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
depth_views[i].next = nullptr;
depth_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain;
- depth_views[i].subImage.imageArrayIndex = 0;
+ depth_views[i].subImage.imageArrayIndex = i;
depth_views[i].subImage.imageRect.offset.x = 0;
depth_views[i].subImage.imageRect.offset.y = 0;
depth_views[i].subImage.imageRect.extent.width = recommended_size.width;
@@ -1066,6 +1067,30 @@ bool OpenXRAPI::on_state_exiting() {
return true;
}
+void OpenXRAPI::set_form_factor(XrFormFactor p_form_factor) {
+ ERR_FAIL_COND(is_initialized());
+
+ form_factor = p_form_factor;
+}
+
+void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configuration) {
+ ERR_FAIL_COND(is_initialized());
+
+ view_configuration = p_view_configuration;
+}
+
+void OpenXRAPI::set_reference_space(XrReferenceSpaceType p_reference_space) {
+ ERR_FAIL_COND(is_initialized());
+
+ reference_space = p_reference_space;
+}
+
+void OpenXRAPI::set_submit_depth_buffer(bool p_submit_depth_buffer) {
+ ERR_FAIL_COND(is_initialized());
+
+ submit_depth_buffer = p_submit_depth_buffer;
+}
+
bool OpenXRAPI::is_initialized() {
return (instance != XR_NULL_HANDLE);
}
@@ -1684,7 +1709,7 @@ RID OpenXRAPI::get_color_texture() {
}
RID OpenXRAPI::get_depth_texture() {
- if (swapchains[OPENXR_SWAPCHAIN_DEPTH].image_acquired) {
+ if (submit_depth_buffer && swapchains[OPENXR_SWAPCHAIN_DEPTH].image_acquired) {
return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_DEPTH].image_index);
} else {
return RID();
@@ -1862,6 +1887,8 @@ OpenXRAPI::OpenXRAPI() {
default:
break;
}
+
+ submit_depth_buffer = GLOBAL_GET("xr/openxr/submit_depth_buffer");
}
// reset a few things that can't be done in our class definition
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 5dce749351..ac4bbff94c 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -104,6 +104,7 @@ private:
XrViewConfigurationType view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrReferenceSpaceType reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
// XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+ bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled.
// state
XrInstance instance = XR_NULL_HANDLE;
@@ -312,6 +313,18 @@ public:
void set_xr_interface(OpenXRInterface *p_xr_interface);
void register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper);
+ void set_form_factor(XrFormFactor p_form_factor);
+ XrFormFactor get_form_factor() const { return form_factor; }
+
+ void set_view_configuration(XrViewConfigurationType p_view_configuration);
+ XrViewConfigurationType get_view_configuration() const { return view_configuration; }
+
+ void set_reference_space(XrReferenceSpaceType p_reference_space);
+ XrReferenceSpaceType get_reference_space() const { return reference_space; }
+
+ void set_submit_depth_buffer(bool p_submit_depth_buffer);
+ bool get_submit_depth_buffer() const { return submit_depth_buffer; }
+
bool is_initialized();
bool is_running();
bool initialize(const String &p_rendering_driver);
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 77660eb6f0..40190ab2f3 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -45,7 +45,7 @@ void OpenXRInterface::_bind_methods() {
// Display refresh rate
ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate);
ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate");
ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates);
}
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index 20b05816e1..51d75d45b0 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -67,7 +67,7 @@ if env["builtin_embree"]:
env_raycast.AppendUnique(CPPDEFINES=["NDEBUG"]) # No assert() even in debug builds.
if not env.msvc:
- if env["arch"] == "x86_64":
+ if env["arch"] in ["x86_64", "x86_32"]:
env_raycast.Append(CPPFLAGS=["-msse2", "-mxsave"])
if env["platform"] == "windows":
@@ -87,10 +87,13 @@ if env["builtin_embree"]:
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
- if env["arch"] == "arm64" or env.msvc:
+ if env["arch"] != "x86_64" or env.msvc:
# Embree needs those, it will automatically use SSE2NEON in ARM
env_thirdparty.Append(CPPDEFINES=["__SSE2__", "__SSE__"])
+ if env["platform"] == "web":
+ env_thirdparty.Append(CPPFLAGS=["-msimd128"])
+
if not env.msvc:
env_thirdparty.Append(
CPPFLAGS=[
diff --git a/modules/raycast/config.py b/modules/raycast/config.py
index 833ad50018..f4243f01c5 100644
--- a/modules/raycast/config.py
+++ b/modules/raycast/config.py
@@ -1,9 +1,13 @@
def can_build(env, platform):
- # Depends on Embree library, which only supports x86_64 and arm64.
- if platform == "windows":
- return env["arch"] == "x86_64" # TODO build for Windows on ARM
-
- return env["arch"] in ["x86_64", "arm64"]
+ # Supported architectures depend on the Embree library.
+ # No ARM32 support planned.
+ if env["arch"] == "arm32":
+ return False
+ # x86_32 only seems supported on Windows for now.
+ if env["arch"] == "x86_32" and platform != "windows":
+ return False
+ # The rest works, even wasm32!
+ return True
def configure(env):
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index cf2d8c9986..27fab88956 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -34,6 +34,7 @@
// Headers for building as GDExtension plug-in.
#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/translation_server.hpp>
@@ -1437,11 +1438,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
if (fd->face->style_name != nullptr) {
p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
}
+ p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());
+ p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());
p_font_data->style_flags = 0;
- if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {
p_font_data->style_flags.set_flag(FONT_BOLD);
}
- if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {
p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
@@ -1967,6 +1970,46 @@ String TextServerAdvanced::_font_get_style_name(const RID &p_font_rid) const {
return fd->style_name;
}
+void TextServerAdvanced::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->weight = CLAMP(p_weight, 100, 999);
+}
+
+int64_t TextServerAdvanced::_font_get_weight(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 400);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400);
+ return fd->weight;
+}
+
+void TextServerAdvanced::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->stretch = CLAMP(p_stretch, 50, 200);
+}
+
+int64_t TextServerAdvanced::_font_get_stretch(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 100);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100);
+ return fd->stretch;
+}
+
void TextServerAdvanced::_font_set_name(const RID &p_font_rid, const String &p_name) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2103,6 +2146,25 @@ int64_t TextServerAdvanced::_font_get_fixed_size(const RID &p_font_rid) const {
return fd->fixed_size;
}
+void TextServerAdvanced::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->allow_system_fallback != p_allow_system_fallback) {
+ _font_clear_cache(fd);
+ fd->allow_system_fallback = p_allow_system_fallback;
+ }
+}
+
+bool TextServerAdvanced::_font_is_allow_system_fallback(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->allow_system_fallback;
+}
+
void TextServerAdvanced::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -4632,12 +4694,11 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
sd->breaks[pos] = true;
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
sd->breaks[pos] = false;
-
- int pos_p = pos - 1 - sd->start;
- char32_t c = sd->text[pos_p];
- if (pos - sd->start != sd->end && !is_whitespace(c) && (c != 0xfffc)) {
- sd->break_inserts++;
- }
+ }
+ int pos_p = pos - 1 - sd->start;
+ char32_t c = sd->text[pos_p];
+ if (pos - sd->start != sd->end && !is_whitespace(c) && (c != 0xfffc)) {
+ sd->break_inserts++;
}
}
}
@@ -5066,10 +5127,177 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source
}
}
-void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index) {
+void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end) {
+ RID f;
int fs = p_sd->spans[p_span].font_size;
- if (p_fb_index >= p_fonts.size()) {
- // Add fallback glyphs.
+
+ if (p_fb_index >= 0 && p_fb_index < p_fonts.size()) {
+ // Try font from list.
+ f = p_fonts[p_fb_index];
+ } else if (OS::get_singleton()->has_feature("system_fonts") && p_fonts.size() > 0 && ((p_fb_index == p_fonts.size()) || (p_fb_index > p_fonts.size() && p_start != p_prev_start))) {
+ // Try system fallback.
+ RID fdef = p_fonts[0];
+ if (_font_is_allow_system_fallback(fdef)) {
+ String text = p_sd->text.substr(p_start, 1);
+ String font_name = _font_get_name(fdef);
+ BitField<FontStyle> font_style = _font_get_style(fdef);
+ int font_weight = _font_get_weight(fdef);
+ int font_stretch = _font_get_stretch(fdef);
+ Dictionary dvar = _font_get_variation_coordinates(fdef);
+ static int64_t wgth_tag = _name_to_tag("weight");
+ static int64_t wdth_tag = _name_to_tag("width");
+ static int64_t ital_tag = _name_to_tag("italic");
+ if (dvar.has(wgth_tag)) {
+ font_weight = dvar[wgth_tag].operator int();
+ }
+ if (dvar.has(wdth_tag)) {
+ font_stretch = dvar[wdth_tag].operator int();
+ }
+ if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {
+ font_style.set_flag(TextServer::FONT_ITALIC);
+ }
+
+ char scr_buffer[5] = { 0, 0, 0, 0, 0 };
+ hb_tag_to_string(hb_script_to_iso15924_tag(p_script), scr_buffer);
+ String script_code = String(scr_buffer);
+ String locale = (p_sd->spans[p_span].language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_sd->spans[p_span].language;
+
+ PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, text, locale, script_code, font_weight, font_stretch, font_style & TextServer::FONT_ITALIC);
+#ifdef GDEXTENSION
+ for (int fb = 0; fb < fallback_font_name.size(); fb++) {
+ const String &E = fallback_font_name[fb];
+#else
+ for (const String &E : fallback_font_name) {
+#endif
+ SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, fdef, this);
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {
+ const SystemFontCacheRec &F = sysf_cache.var[face_idx];
+ if (unlikely(!_font_has_char(F.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(F.rid);
+ int weight = _font_get_weight(F.rid);
+ int stretch = _font_get_stretch(F.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match != -1) {
+ f = sysf_cache.var[best_match].rid;
+ }
+ }
+ if (!f.is_valid()) {
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ if (sysf_cache.max_var == sysf_cache.var.size()) {
+ // All subfonts already tested, skip.
+ continue;
+ }
+ }
+
+ if (!system_font_data.has(E)) {
+ system_font_data[E] = FileAccess::get_file_as_bytes(E);
+ }
+
+ const PackedByteArray &font_data = system_font_data[E];
+
+ SystemFontCacheRec sysf;
+ sysf.rid = _create_font();
+ _font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());
+
+ Dictionary var = dvar;
+ // Select matching style from collection.
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {
+ _font_set_face_index(sysf.rid, face_idx);
+ if (unlikely(!_font_has_char(sysf.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(sysf.rid);
+ int weight = _font_get_weight(sysf.rid);
+ int stretch = _font_get_stretch(sysf.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match == -1) {
+ _free_rid(sysf.rid);
+ continue;
+ } else {
+ _font_set_face_index(sysf.rid, best_match);
+ }
+ sysf.index = best_match;
+
+ // If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
+ if (best_score != 70) {
+ Dictionary ftr = _font_supported_variation_list(sysf.rid);
+ if (ftr.has(wdth_tag)) {
+ var[wdth_tag] = font_stretch;
+ _font_set_stretch(sysf.rid, font_stretch);
+ }
+ if (ftr.has(wgth_tag)) {
+ var[wgth_tag] = font_weight;
+ _font_set_weight(sysf.rid, font_weight);
+ }
+ if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {
+ var[ital_tag] = 1;
+ _font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);
+ }
+ }
+
+ _font_set_antialiasing(sysf.rid, key.antialiasing);
+ _font_set_generate_mipmaps(sysf.rid, key.mipmaps);
+ _font_set_multichannel_signed_distance_field(sysf.rid, key.msdf);
+ _font_set_msdf_pixel_range(sysf.rid, key.msdf_range);
+ _font_set_msdf_size(sysf.rid, key.msdf_source_size);
+ _font_set_fixed_size(sysf.rid, key.fixed_size);
+ _font_set_force_autohinter(sysf.rid, key.force_autohinter);
+ _font_set_hinting(sysf.rid, key.hinting);
+ _font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);
+ _font_set_variation_coordinates(sysf.rid, var);
+ _font_set_oversampling(sysf.rid, key.oversampling);
+ _font_set_embolden(sysf.rid, key.embolden);
+ _font_set_transform(sysf.rid, key.transform);
+
+ if (system_fonts.has(key)) {
+ system_fonts[key].var.push_back(sysf);
+ } else {
+ SystemFontCache &sysf_cache = system_fonts[key];
+ sysf_cache.max_var = _font_get_face_count(sysf.rid);
+ sysf_cache.var.push_back(sysf);
+ }
+ f = sysf.rid;
+ }
+ break;
+ }
+ }
+ }
+
+ if (!f.is_valid()) {
+ // No valid font, use fallback hex code boxes.
for (int i = p_start; i < p_end; i++) {
if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
Glyph gl;
@@ -5100,7 +5328,6 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
return;
}
- RID f = p_fonts[p_fb_index];
FontAdvanced *fd = font_owner.get_or_null(f);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -5195,7 +5422,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
gl.end = end;
gl.count = 0;
- gl.font_rid = p_fonts[p_fb_index];
+ gl.font_rid = f;
gl.font_size = fs;
if (glyph_info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) {
@@ -5262,7 +5489,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
for (unsigned int i = 0; i < glyph_count; i++) {
if ((w[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
if (failed_subrun_start != p_end + 1) {
- _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end);
failed_subrun_start = p_end + 1;
failed_subrun_end = p_start;
}
@@ -5292,7 +5519,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
memfree(w);
if (failed_subrun_start != p_end + 1) {
- _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end);
}
p_sd->ascent = MAX(p_sd->ascent, _font_get_ascent(f, fs));
p_sd->descent = MAX(p_sd->descent, _font_get_descent(f, fs));
@@ -5464,7 +5691,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
}
fonts.append_array(fonts_scr_only);
fonts.append_array(fonts_no_match);
- _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0);
+ _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0);
}
}
}
@@ -5961,7 +6188,11 @@ String TextServerAdvanced::_strip_diacritics(const String &p_string) const {
String result;
for (int i = 0; i < normalized_string.length(); i++) {
if (u_getCombiningClass(normalized_string[i]) == 0) {
+#ifdef GDEXTENSION
+ result = result + String::chr(normalized_string[i]);
+#else
result = result + normalized_string[i];
+#endif
}
}
return result;
@@ -6243,6 +6474,17 @@ TextServerAdvanced::TextServerAdvanced() {
_bmp_create_font_funcs();
}
+void TextServerAdvanced::_cleanup() {
+ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {
+ const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;
+ for (const SystemFontCacheRec &F : sysf_cache) {
+ _free_rid(F.rid);
+ }
+ }
+ system_fonts.clear();
+ system_font_data.clear();
+}
+
TextServerAdvanced::~TextServerAdvanced() {
_bmp_free_font_funcs();
#ifdef MODULE_FREETYPE_ENABLED
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 10fe3c2316..5e6d2cc2c0 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -300,6 +300,7 @@ class TextServerAdvanced : public TextServerExtension {
int msdf_range = 14;
int msdf_source_size = 48;
int fixed_size = 0;
+ bool allow_system_fallback = true;
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
@@ -311,6 +312,8 @@ class TextServerAdvanced : public TextServerExtension {
BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
+ int weight = 400;
+ int stretch = 100;
HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache;
@@ -372,6 +375,57 @@ class TextServerAdvanced : public TextServerExtension {
_FORCE_INLINE_ double _get_extra_advance(RID p_font_rid, int p_font_size) const;
_FORCE_INLINE_ Variant::Type _get_tag_type(int64_t p_tag) const;
_FORCE_INLINE_ bool _get_tag_hidden(int64_t p_tag) const;
+ _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("thin") >= 0 || sty_name.find("hairline") >= 0) {
+ return 100;
+ } else if (sty_name.find("extralight") >= 0 || sty_name.find("ultralight") >= 0) {
+ return 200;
+ } else if (sty_name.find("light") >= 0) {
+ return 300;
+ } else if (sty_name.find("semilight") >= 0) {
+ return 350;
+ } else if (sty_name.find("regular") >= 0) {
+ return 400;
+ } else if (sty_name.find("medium") >= 0) {
+ return 500;
+ } else if (sty_name.find("semibold") >= 0 || sty_name.find("demibold") >= 0) {
+ return 600;
+ } else if (sty_name.find("bold") >= 0) {
+ return 700;
+ } else if (sty_name.find("extrabold") >= 0 || sty_name.find("ultrabold") >= 0) {
+ return 800;
+ } else if (sty_name.find("black") >= 0 || sty_name.find("heavy") >= 0) {
+ return 900;
+ } else if (sty_name.find("extrablack") >= 0 || sty_name.find("ultrablack") >= 0) {
+ return 950;
+ }
+ return 400;
+ }
+ _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("ultracondensed") >= 0) {
+ return 50;
+ } else if (sty_name.find("extracondensed") >= 0) {
+ return 63;
+ } else if (sty_name.find("condensed") >= 0) {
+ return 75;
+ } else if (sty_name.find("semicondensed") >= 0) {
+ return 87;
+ } else if (sty_name.find("semiexpanded") >= 0) {
+ return 113;
+ } else if (sty_name.find("expanded") >= 0) {
+ return 125;
+ } else if (sty_name.find("extraexpanded") >= 0) {
+ return 150;
+ } else if (sty_name.find("ultraexpanded") >= 0) {
+ return 200;
+ }
+ return 100;
+ }
+ _FORCE_INLINE_ bool _is_ital_style(const String &p_sty_name) const {
+ return (p_sty_name.find("italic") >= 0) || (p_sty_name.find("oblique") >= 0);
+ }
// Shaped text cache data.
struct TrimData {
@@ -474,12 +528,87 @@ class TextServerAdvanced : public TextServerExtension {
mutable RID_PtrOwner<FontAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
+ struct SystemFontKey {
+ String font_name;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
+ bool italic = false;
+ bool mipmaps = false;
+ bool msdf = false;
+ bool force_autohinter = false;
+ int weight = 400;
+ int stretch = 100;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ Dictionary variation_coordinates;
+ double oversampling = 0.0;
+ double embolden = 0.0;
+ Transform2D transform;
+
+ bool operator==(const SystemFontKey &p_b) const {
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ }
+
+ SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerAdvanced *p_fb) {
+ font_name = p_font_name;
+ italic = p_italic;
+ weight = p_weight;
+ stretch = p_stretch;
+ antialiasing = p_fb->_font_get_antialiasing(p_font);
+ mipmaps = p_fb->_font_get_generate_mipmaps(p_font);
+ msdf = p_fb->_font_is_multichannel_signed_distance_field(p_font);
+ msdf_range = p_fb->_font_get_msdf_pixel_range(p_font);
+ msdf_source_size = p_fb->_font_get_msdf_size(p_font);
+ fixed_size = p_fb->_font_get_fixed_size(p_font);
+ force_autohinter = p_fb->_font_is_force_autohinter(p_font);
+ hinting = p_fb->_font_get_hinting(p_font);
+ subpixel_positioning = p_fb->_font_get_subpixel_positioning(p_font);
+ variation_coordinates = p_fb->_font_get_variation_coordinates(p_font);
+ oversampling = p_fb->_font_get_oversampling(p_font);
+ embolden = p_fb->_font_get_embolden(p_font);
+ transform = p_fb->_font_get_transform(p_font);
+ }
+ };
+
+ struct SystemFontCacheRec {
+ RID rid;
+ int index = 0;
+ };
+
+ struct SystemFontCache {
+ Vector<SystemFontCacheRec> var;
+ int max_var = 0;
+ };
+
+ struct SystemFontKeyHasher {
+ _FORCE_INLINE_ static uint32_t hash(const SystemFontKey &p_a) {
+ uint32_t hash = p_a.font_name.hash();
+ hash = hash_murmur3_one_32(p_a.variation_coordinates.hash(), hash);
+ hash = hash_murmur3_one_32(p_a.weight, hash);
+ hash = hash_murmur3_one_32(p_a.stretch, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_range, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_source_size, hash);
+ hash = hash_murmur3_one_32(p_a.fixed_size, hash);
+ hash = hash_murmur3_one_double(p_a.oversampling, hash);
+ hash = hash_murmur3_one_double(p_a.embolden, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
+ }
+ };
+ mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts;
+ mutable HashMap<String, PackedByteArray> system_font_data;
+
void _realign(ShapedTextDataAdvanced *p_sd) const;
int64_t _convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const;
int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
int64_t _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_length) const;
- void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index);
+ void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end);
Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size);
_FORCE_INLINE_ void _add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs);
@@ -568,6 +697,12 @@ public:
MODBIND2(font_set_style_name, const RID &, const String &);
MODBIND1RC(String, font_get_style_name, const RID &);
+ MODBIND2(font_set_weight, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_weight, const RID &);
+
+ MODBIND2(font_set_stretch, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_stretch, const RID &);
+
MODBIND2(font_set_name, const RID &, const String &);
MODBIND1RC(String, font_get_name, const RID &);
@@ -589,6 +724,9 @@ public:
MODBIND2(font_set_fixed_size, const RID &, int64_t);
MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
+ MODBIND2(font_set_allow_system_fallback, const RID &, bool);
+ MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
+
MODBIND2(font_set_force_autohinter, const RID &, bool);
MODBIND1RC(bool, font_is_force_autohinter, const RID &);
@@ -787,6 +925,8 @@ public:
MODBIND2RC(String, string_to_upper, const String &, const String &);
MODBIND2RC(String, string_to_lower, const String &, const String &);
+ MODBIND0(cleanup);
+
TextServerAdvanced();
~TextServerAdvanced();
};
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index aaef9c9a3d..9133c277bb 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -34,6 +34,7 @@
// Headers for building as GDExtension plug-in.
#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/translation_server.hpp>
@@ -49,6 +50,7 @@ using namespace godot;
#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
+#include "core/string/translation.h"
#include "core/string/ucaps.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
@@ -852,11 +854,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
if (fd->face->style_name != nullptr) {
p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
}
+ p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());
+ p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());
p_font_data->style_flags = 0;
- if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {
p_font_data->style_flags.set_flag(FONT_BOLD);
}
- if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {
p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
@@ -1061,6 +1065,46 @@ String TextServerFallback::_font_get_style_name(const RID &p_font_rid) const {
return fd->style_name;
}
+void TextServerFallback::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->weight = CLAMP(p_weight, 100, 999);
+}
+
+int64_t TextServerFallback::_font_get_weight(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 400);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400);
+ return fd->weight;
+}
+
+void TextServerFallback::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->stretch = CLAMP(p_stretch, 50, 200);
+}
+
+int64_t TextServerFallback::_font_get_stretch(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 100);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100);
+ return fd->stretch;
+}
+
void TextServerFallback::_font_set_name(const RID &p_font_rid, const String &p_name) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1197,6 +1241,25 @@ int64_t TextServerFallback::_font_get_fixed_size(const RID &p_font_rid) const {
return fd->fixed_size;
}
+void TextServerFallback::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->allow_system_fallback != p_allow_system_fallback) {
+ _font_clear_cache(fd);
+ fd->allow_system_fallback = p_allow_system_fallback;
+ }
+}
+
+bool TextServerFallback::_font_is_allow_system_fallback(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->allow_system_fallback;
+}
+
void TextServerFallback::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3603,6 +3666,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
sd->glyphs.push_back(gl);
} else {
// Text span.
+ RID prev_font;
for (int j = span.start; j < span.end; j++) {
Glyph gl;
gl.start = j;
@@ -3623,6 +3687,170 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
break;
}
}
+ if (!gl.font_rid.is_valid() && prev_font.is_valid()) {
+ if (_font_has_char(prev_font, gl.index)) {
+ gl.font_rid = prev_font;
+ }
+ }
+ if (!gl.font_rid.is_valid() && OS::get_singleton()->has_feature("system_fonts") && span.fonts.size() > 0) {
+ // Try system fallback.
+ RID fdef = span.fonts[0];
+ if (_font_is_allow_system_fallback(fdef)) {
+ String text = sd->text.substr(j, 1);
+ String font_name = _font_get_name(fdef);
+ BitField<FontStyle> font_style = _font_get_style(fdef);
+ int font_weight = _font_get_weight(fdef);
+ int font_stretch = _font_get_stretch(fdef);
+ Dictionary dvar = _font_get_variation_coordinates(fdef);
+ static int64_t wgth_tag = _name_to_tag("weight");
+ static int64_t wdth_tag = _name_to_tag("width");
+ static int64_t ital_tag = _name_to_tag("italic");
+ if (dvar.has(wgth_tag)) {
+ font_weight = dvar[wgth_tag].operator int();
+ }
+ if (dvar.has(wdth_tag)) {
+ font_stretch = dvar[wdth_tag].operator int();
+ }
+ if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {
+ font_style.set_flag(TextServer::FONT_ITALIC);
+ }
+
+ String locale = (span.language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : span.language;
+
+ PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, text, locale, String(), font_weight, font_stretch, font_style & TextServer::FONT_ITALIC);
+#ifdef GDEXTENSION
+ for (int fb = 0; fb < fallback_font_name.size(); fb++) {
+ const String &E = fallback_font_name[fb];
+#else
+ for (const String &E : fallback_font_name) {
+#endif
+ SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, fdef, this);
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {
+ const SystemFontCacheRec &F = sysf_cache.var[face_idx];
+ if (unlikely(!_font_has_char(F.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(F.rid);
+ int weight = _font_get_weight(F.rid);
+ int stretch = _font_get_stretch(F.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match != -1) {
+ gl.font_rid = sysf_cache.var[best_match].rid;
+ }
+ }
+ if (!gl.font_rid.is_valid()) {
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ if (sysf_cache.max_var == sysf_cache.var.size()) {
+ // All subfonts already tested, skip.
+ continue;
+ }
+ }
+
+ if (!system_font_data.has(E)) {
+ system_font_data[E] = FileAccess::get_file_as_bytes(E);
+ }
+
+ const PackedByteArray &font_data = system_font_data[E];
+
+ SystemFontCacheRec sysf;
+ sysf.rid = _create_font();
+ _font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());
+
+ Dictionary var = dvar;
+ // Select matching style from collection.
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {
+ _font_set_face_index(sysf.rid, face_idx);
+ if (unlikely(!_font_has_char(sysf.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(sysf.rid);
+ int weight = _font_get_weight(sysf.rid);
+ int stretch = _font_get_stretch(sysf.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match == -1) {
+ _free_rid(sysf.rid);
+ continue;
+ } else {
+ _font_set_face_index(sysf.rid, best_match);
+ }
+ sysf.index = best_match;
+
+ // If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
+ if (best_score != 70) {
+ Dictionary ftr = _font_supported_variation_list(sysf.rid);
+ if (ftr.has(wdth_tag)) {
+ var[wdth_tag] = font_stretch;
+ _font_set_stretch(sysf.rid, font_stretch);
+ }
+ if (ftr.has(wgth_tag)) {
+ var[wgth_tag] = font_weight;
+ _font_set_weight(sysf.rid, font_weight);
+ }
+ if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {
+ var[ital_tag] = 1;
+ _font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);
+ }
+ }
+
+ _font_set_antialiasing(sysf.rid, key.antialiasing);
+ _font_set_generate_mipmaps(sysf.rid, key.mipmaps);
+ _font_set_multichannel_signed_distance_field(sysf.rid, key.msdf);
+ _font_set_msdf_pixel_range(sysf.rid, key.msdf_range);
+ _font_set_msdf_size(sysf.rid, key.msdf_source_size);
+ _font_set_fixed_size(sysf.rid, key.fixed_size);
+ _font_set_force_autohinter(sysf.rid, key.force_autohinter);
+ _font_set_hinting(sysf.rid, key.hinting);
+ _font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);
+ _font_set_variation_coordinates(sysf.rid, var);
+ _font_set_oversampling(sysf.rid, key.oversampling);
+ _font_set_embolden(sysf.rid, key.embolden);
+ _font_set_transform(sysf.rid, key.transform);
+
+ if (system_fonts.has(key)) {
+ system_fonts[key].var.push_back(sysf);
+ } else {
+ SystemFontCache &sysf_cache = system_fonts[key];
+ sysf_cache.max_var = _font_get_face_count(sysf.rid);
+ sysf_cache.var.push_back(sysf);
+ }
+ gl.font_rid = sysf.rid;
+ }
+ break;
+ }
+ }
+ }
+ prev_font = gl.font_rid;
double scale = _font_get_scale(gl.font_rid, gl.font_size);
if (gl.font_rid.is_valid()) {
@@ -3893,6 +4121,17 @@ TextServerFallback::TextServerFallback() {
_insert_feature_sets();
};
+void TextServerFallback::_cleanup() {
+ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {
+ const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;
+ for (const SystemFontCacheRec &F : sysf_cache) {
+ _free_rid(F.rid);
+ }
+ }
+ system_fonts.clear();
+ system_font_data.clear();
+}
+
TextServerFallback::~TextServerFallback() {
#ifdef MODULE_FREETYPE_ENABLED
if (ft_library != nullptr) {
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 7e0bc99618..f8a05f9433 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -256,6 +256,7 @@ class TextServerFallback : public TextServerExtension {
int msdf_source_size = 48;
int fixed_size = 0;
bool force_autohinter = false;
+ bool allow_system_fallback = true;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
Dictionary variation_coordinates;
@@ -266,6 +267,8 @@ class TextServerFallback : public TextServerExtension {
BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
+ int weight = 400;
+ int stretch = 100;
HashMap<Vector2i, FontForSizeFallback *, VariantHasher, VariantComparator> cache;
@@ -322,6 +325,58 @@ class TextServerFallback : public TextServerExtension {
}
}
+ _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("thin") >= 0 || sty_name.find("hairline") >= 0) {
+ return 100;
+ } else if (sty_name.find("extralight") >= 0 || sty_name.find("ultralight") >= 0) {
+ return 200;
+ } else if (sty_name.find("light") >= 0) {
+ return 300;
+ } else if (sty_name.find("semilight") >= 0) {
+ return 350;
+ } else if (sty_name.find("regular") >= 0) {
+ return 400;
+ } else if (sty_name.find("medium") >= 0) {
+ return 500;
+ } else if (sty_name.find("semibold") >= 0 || sty_name.find("demibold") >= 0) {
+ return 600;
+ } else if (sty_name.find("bold") >= 0) {
+ return 700;
+ } else if (sty_name.find("extrabold") >= 0 || sty_name.find("ultrabold") >= 0) {
+ return 800;
+ } else if (sty_name.find("black") >= 0 || sty_name.find("heavy") >= 0) {
+ return 900;
+ } else if (sty_name.find("extrablack") >= 0 || sty_name.find("ultrablack") >= 0) {
+ return 950;
+ }
+ return 400;
+ }
+ _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("ultracondensed") >= 0) {
+ return 50;
+ } else if (sty_name.find("extracondensed") >= 0) {
+ return 63;
+ } else if (sty_name.find("condensed") >= 0) {
+ return 75;
+ } else if (sty_name.find("semicondensed") >= 0) {
+ return 87;
+ } else if (sty_name.find("semiexpanded") >= 0) {
+ return 113;
+ } else if (sty_name.find("expanded") >= 0) {
+ return 125;
+ } else if (sty_name.find("extraexpanded") >= 0) {
+ return 150;
+ } else if (sty_name.find("ultraexpanded") >= 0) {
+ return 200;
+ }
+ return 100;
+ }
+ _FORCE_INLINE_ bool _is_ital_style(const String &p_sty_name) const {
+ return (p_sty_name.find("italic") >= 0) || (p_sty_name.find("oblique") >= 0);
+ }
+
// Shaped text cache data.
struct TrimData {
int trim_pos = -1;
@@ -398,6 +453,81 @@ class TextServerFallback : public TextServerExtension {
mutable RID_PtrOwner<FontFallback> font_owner;
mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
+ struct SystemFontKey {
+ String font_name;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
+ bool italic = false;
+ bool mipmaps = false;
+ bool msdf = false;
+ bool force_autohinter = false;
+ int weight = 400;
+ int stretch = 100;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ Dictionary variation_coordinates;
+ double oversampling = 0.0;
+ double embolden = 0.0;
+ Transform2D transform;
+
+ bool operator==(const SystemFontKey &p_b) const {
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ }
+
+ SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerFallback *p_fb) {
+ font_name = p_font_name;
+ italic = p_italic;
+ weight = p_weight;
+ stretch = p_stretch;
+ antialiasing = p_fb->_font_get_antialiasing(p_font);
+ mipmaps = p_fb->_font_get_generate_mipmaps(p_font);
+ msdf = p_fb->_font_is_multichannel_signed_distance_field(p_font);
+ msdf_range = p_fb->_font_get_msdf_pixel_range(p_font);
+ msdf_source_size = p_fb->_font_get_msdf_size(p_font);
+ fixed_size = p_fb->_font_get_fixed_size(p_font);
+ force_autohinter = p_fb->_font_is_force_autohinter(p_font);
+ hinting = p_fb->_font_get_hinting(p_font);
+ subpixel_positioning = p_fb->_font_get_subpixel_positioning(p_font);
+ variation_coordinates = p_fb->_font_get_variation_coordinates(p_font);
+ oversampling = p_fb->_font_get_oversampling(p_font);
+ embolden = p_fb->_font_get_embolden(p_font);
+ transform = p_fb->_font_get_transform(p_font);
+ }
+ };
+
+ struct SystemFontCacheRec {
+ RID rid;
+ int index = 0;
+ };
+
+ struct SystemFontCache {
+ Vector<SystemFontCacheRec> var;
+ int max_var = 0;
+ };
+
+ struct SystemFontKeyHasher {
+ _FORCE_INLINE_ static uint32_t hash(const SystemFontKey &p_a) {
+ uint32_t hash = p_a.font_name.hash();
+ hash = hash_murmur3_one_32(p_a.variation_coordinates.hash(), hash);
+ hash = hash_murmur3_one_32(p_a.weight, hash);
+ hash = hash_murmur3_one_32(p_a.stretch, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_range, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_source_size, hash);
+ hash = hash_murmur3_one_32(p_a.fixed_size, hash);
+ hash = hash_murmur3_one_double(p_a.oversampling, hash);
+ hash = hash_murmur3_one_double(p_a.embolden, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
+ }
+ };
+ mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts;
+ mutable HashMap<String, PackedByteArray> system_font_data;
+
void _realign(ShapedTextDataFallback *p_sd) const;
protected:
@@ -442,6 +572,12 @@ public:
MODBIND2(font_set_style_name, const RID &, const String &);
MODBIND1RC(String, font_get_style_name, const RID &);
+ MODBIND2(font_set_weight, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_weight, const RID &);
+
+ MODBIND2(font_set_stretch, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_stretch, const RID &);
+
MODBIND2(font_set_name, const RID &, const String &);
MODBIND1RC(String, font_get_name, const RID &);
@@ -463,6 +599,9 @@ public:
MODBIND2(font_set_fixed_size, const RID &, int64_t);
MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
+ MODBIND2(font_set_allow_system_fallback, const RID &, bool);
+ MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
+
MODBIND2(font_set_force_autohinter, const RID &, bool);
MODBIND1RC(bool, font_is_force_autohinter, const RID &);
@@ -651,6 +790,8 @@ public:
MODBIND2RC(String, string_to_upper, const String &, const String &);
MODBIND2RC(String, string_to_lower, const String &, const String &);
+ MODBIND0(cleanup);
+
TextServerFallback();
~TextServerFallback();
};