diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/gltf/gltf_document.cpp | 4 | ||||
-rw-r--r-- | modules/text_server_adv/text_server_adv.cpp | 274 | ||||
-rw-r--r-- | modules/text_server_adv/text_server_adv.h | 142 | ||||
-rw-r--r-- | modules/text_server_fb/text_server_fb.cpp | 243 | ||||
-rw-r--r-- | modules/text_server_fb/text_server_fb.h | 141 |
5 files changed, 783 insertions, 21 deletions
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/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(); }; |