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 f100670e08b..b678a04e345 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 4fa9700f9cf..9d476691bfd 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 c148cdad528..3c27404f8e3 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 c52f53146e0..fa222b27902 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 499ddb703b0..54a7c9ef8d0 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 92bdb93bcf1..fdebb8e4cd3 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 b45c004011a..baffd02d477 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 2235247b31a..1b76c6fa0f0 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 a5ab444f554..1a945ac221f 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 509d49a1e42..d387c8ff7ec 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 16a03b65928..52654c010ce 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 e7a1511064a..b17d3d4096e 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 a77953e6f2a..ba3fdaa35e9 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;