From 32bc1c2f3334d4de83a26f86eb0855dff725a1b9 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:12:00 +0300 Subject: [PATCH] [Font Import] Detect pixel fonts and disable subpixel positioning. --- doc/classes/ResourceImporterDynamicFont.xml | 3 +- doc/classes/TextServer.xml | 7 ++++ doc/classes/TextServerExtension.xml | 8 ++++ .../import/resource_importer_dynamic_font.cpp | 37 ++++++++++++++++++- modules/text_server_adv/text_server_adv.cpp | 31 ++++++++++++++++ modules/text_server_adv/text_server_adv.h | 1 + modules/text_server_fb/text_server_fb.cpp | 31 ++++++++++++++++ modules/text_server_fb/text_server_fb.h | 1 + servers/text/text_server_dummy.h | 1 + servers/text/text_server_extension.cpp | 7 ++++ servers/text/text_server_extension.h | 2 + servers/text_server.cpp | 1 + servers/text_server.h | 1 + 13 files changed, 128 insertions(+), 3 deletions(-) diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml index f100670e08b7..b678a04e345a 100644 --- a/doc/classes/ResourceImporterDynamicFont.xml +++ b/doc/classes/ResourceImporterDynamicFont.xml @@ -69,12 +69,13 @@ Override the list of language scripts supported by this font. If left empty, this is supplied by the font metadata. There is usually no need to change this. See also [member language_support]. - + Subpixel positioning improves font rendering appearance, especially at smaller font sizes. The downside is that it takes more time to initially render the font, which can cause stuttering during gameplay, especially if used with large font sizes. This should be set to [b]Disabled[/b] for fonts with a pixel art appearance. [b]Disabled:[/b] No subpixel positioning. Lowest quality, fastest rendering. [b]Auto:[/b] Use subpixel positioning at small font sizes (the chosen quality varies depending on font size). Large fonts will not use subpixel positioning. This is a good tradeoff between performance and quality. [b]One Half of a Pixel:[/b] Always perform intermediate subpixel positioning regardless of font size. High quality, slow rendering. [b]One Quarter of a Pixel:[/b] Always perform precise subpixel positioning regardless of font size. Highest quality, slowest rendering. + [b]Auto (Except Pixel Fonts):[/b] [b]Disabled[/b] for the pixel style fonts (each glyph contours contain only straight horizontal and vertical lines), [b]Auto[/b] for the other fonts. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 4fa9700f9cfc..9d476691bfdc 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -459,6 +459,13 @@ Returns a string containing all the characters available in the font. + + + + + Returns an array containing all glyph indices in the font. + + diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index c148cdad528d..3c27404f8e38 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -496,6 +496,14 @@ Returns a string containing all the characters available in the font. + + + + + [b]Required.[/b] + Returns an array containing all glyph indices in the font. + + diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp index c52f53146e03..fa222b279020 100644 --- a/editor/import/resource_importer_dynamic_font.cpp +++ b/editor/import/resource_importer_dynamic_font.cpp @@ -118,7 +118,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List< r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel,Auto (Except Pixel Fonts)"), 4)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0)); r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Fallbacks", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant())); @@ -176,11 +176,44 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str font->set_fixed_size(0); font->set_force_autohinter(autohinter); font->set_allow_system_fallback(allow_system_fallback); - font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning); font->set_hinting((TextServer::Hinting)hinting); font->set_oversampling(oversampling); font->set_fallbacks(fallbacks); + if (subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) { + PackedInt32Array glyphs = TS->font_get_supported_glyphs(font->get_rids()[0]); + bool is_pixel = true; + for (int32_t gl : glyphs) { + Dictionary ct = TS->font_get_glyph_contours(font->get_rids()[0], 16, gl); + PackedInt32Array contours = ct["contours"]; + PackedVector3Array points = ct["points"]; + int prev_start = 0; + for (int i = 0; i < contours.size(); i++) { + for (int j = prev_start; j <= contours[i]; j++) { + int next_point = (j < contours[i]) ? (j + 1) : prev_start; + if ((points[j].z != TextServer::CONTOUR_CURVE_TAG_ON) || (!Math::is_equal_approx(points[j].x, points[next_point].x) && !Math::is_equal_approx(points[j].y, points[next_point].y))) { + is_pixel = false; + break; + } + } + prev_start = contours[i] + 1; + if (!is_pixel) { + break; + } + } + if (!is_pixel) { + break; + } + } + if (is_pixel && !glyphs.is_empty()) { + print_line(vformat("%s: Pixel font detected, disabling subpixel positioning.", p_source_file)); + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; + } else { + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; + } + } + font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning); + Dictionary langs = p_options["language_support"]; for (int i = 0; i < langs.size(); i++) { String key = langs.get_key_at_index(i); diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 499ddb703b0f..54a7c9ef8d0b 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -3528,6 +3528,37 @@ String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) cons return chars; } +PackedInt32Array TextServerAdvanced::_font_get_supported_glyphs(const RID &p_font_rid) const { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array()); + } + FontForSizeAdvanced *at_size = fd->cache.begin()->value; + + PackedInt32Array glyphs; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + glyphs.push_back(gindex); + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return glyphs; + } +#endif + if (at_size) { + const HashMap &gl = at_size->glyph_map; + for (const KeyValue &E : gl) { + glyphs.push_back(E.key); + } + } + return glyphs; +} + void TextServerAdvanced::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { FontAdvanced *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL(fd); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 92bdb93bcf17..fdebb8e4cd37 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -871,6 +871,7 @@ public: MODBIND2RC(bool, font_has_char, const RID &, int64_t); MODBIND1RC(String, font_get_supported_chars, const RID &); + MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &); MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t); MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index b45c004011a1..baffd02d477d 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -2477,6 +2477,37 @@ String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) cons return chars; } +PackedInt32Array TextServerFallback::_font_get_supported_glyphs(const RID &p_font_rid) const { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array()); + } + FontForSizeFallback *at_size = fd->cache.begin()->value; + + PackedInt32Array glyphs; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + glyphs.push_back(gindex); + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return glyphs; + } +#endif + if (at_size) { + const HashMap &gl = at_size->glyph_map; + for (const KeyValue &E : gl) { + glyphs.push_back(E.key); + } + } + return glyphs; +} + void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { FontFallback *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL(fd); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 2235247b31a0..1b76c6fa0f06 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -739,6 +739,7 @@ public: MODBIND2RC(bool, font_has_char, const RID &, int64_t); MODBIND1RC(String, font_get_supported_chars, const RID &); + MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &); MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t); MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t); diff --git a/servers/text/text_server_dummy.h b/servers/text/text_server_dummy.h index a5ab444f554d..1a945ac221f5 100644 --- a/servers/text/text_server_dummy.h +++ b/servers/text/text_server_dummy.h @@ -88,6 +88,7 @@ public: virtual int64_t font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const override { return 0; } virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override { return false; } virtual String font_get_supported_chars(const RID &p_font_rid) const override { return String(); } + virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override { return PackedInt32Array(); }; virtual void font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {} virtual void font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {} diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index 509d49a1e42a..d387c8ff7ec7 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -196,6 +196,7 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_font_has_char, "font_rid", "char"); GDVIRTUAL_BIND(_font_get_supported_chars, "font_rid"); + GDVIRTUAL_BIND(_font_get_supported_glyphs, "font_rid"); GDVIRTUAL_BIND(_font_render_range, "font_rid", "size", "start", "end"); GDVIRTUAL_BIND(_font_render_glyph, "font_rid", "size", "index"); @@ -927,6 +928,12 @@ String TextServerExtension::font_get_supported_chars(const RID &p_font_rid) cons return ret; } +PackedInt32Array TextServerExtension::font_get_supported_glyphs(const RID &p_font_rid) const { + PackedInt32Array ret; + GDVIRTUAL_REQUIRED_CALL(_font_get_supported_glyphs, p_font_rid, ret); + return ret; +} + void TextServerExtension::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { GDVIRTUAL_CALL(_font_render_range, p_font_rid, p_size, p_start, p_end); } diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 16a03b65928b..52654c010ce4 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -323,8 +323,10 @@ public: virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override; virtual String font_get_supported_chars(const RID &p_font_rid) const override; + virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override; GDVIRTUAL2RC(bool, _font_has_char, RID, int64_t); GDVIRTUAL1RC(String, _font_get_supported_chars, RID); + GDVIRTUAL1RC(PackedInt32Array, _font_get_supported_glyphs, RID); virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override; virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override; diff --git a/servers/text_server.cpp b/servers/text_server.cpp index e7a1511064ae..b17d3d4096ea 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -352,6 +352,7 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_has_char", "font_rid", "char"), &TextServer::font_has_char); ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font_rid"), &TextServer::font_get_supported_chars); + ClassDB::bind_method(D_METHOD("font_get_supported_glyphs", "font_rid"), &TextServer::font_get_supported_glyphs); ClassDB::bind_method(D_METHOD("font_render_range", "font_rid", "size", "start", "end"), &TextServer::font_render_range); ClassDB::bind_method(D_METHOD("font_render_glyph", "font_rid", "size", "index"), &TextServer::font_render_glyph); diff --git a/servers/text_server.h b/servers/text_server.h index a77953e6f2a5..ba3fdaa35e9b 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -396,6 +396,7 @@ public: virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const = 0; virtual String font_get_supported_chars(const RID &p_font_rid) const = 0; + virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const = 0; virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) = 0; virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) = 0;