diff options
Diffstat (limited to 'modules')
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(); }; |