mirror of
https://github.com/tiennm99/godot.git
synced 2026-08-05 16:24:39 +00:00
Use system fonts as fallback and improve system font handling.
Add support for font weight and stretch selection when using system fonts. Add function to get system fallback font from a font name, style, text, and language code. Implement system font support for Android. Use system fonts as a last resort fallback.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user