summaryrefslogtreecommitdiff
path: root/modules/text_server_fb
diff options
context:
space:
mode:
Diffstat (limited to 'modules/text_server_fb')
-rw-r--r--modules/text_server_fb/dynamic_font_fb.cpp4
-rw-r--r--modules/text_server_fb/dynamic_font_fb.h1
-rw-r--r--modules/text_server_fb/text_server_fb.cpp182
-rw-r--r--modules/text_server_fb/text_server_fb.h2
4 files changed, 176 insertions, 13 deletions
diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp
index dec1d6f83f..7e77987074 100644
--- a/modules/text_server_fb/dynamic_font_fb.cpp
+++ b/modules/text_server_fb/dynamic_font_fb.cpp
@@ -65,7 +65,7 @@ DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size(
ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
}
- size_t len = f->get_len();
+ uint64_t len = f->get_length();
font_mem_cache.resize(len);
f->get_buffer(font_mem_cache.ptrw(), len);
font_mem = font_mem_cache.ptr();
@@ -306,7 +306,7 @@ DynamicFontDataFallback::Character DynamicFontDataFallback::bitmap_to_character(
Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
if (tex.texture.is_null()) {
- tex.texture.instance();
+ tex.texture.instantiate();
tex.texture->create_from_image(img);
} else {
tex.texture->update(img); //update
diff --git a/modules/text_server_fb/dynamic_font_fb.h b/modules/text_server_fb/dynamic_font_fb.h
index b34c8cbed5..82e59fa607 100644
--- a/modules/text_server_fb/dynamic_font_fb.h
+++ b/modules/text_server_fb/dynamic_font_fb.h
@@ -34,7 +34,6 @@
#include "font_fb.h"
#include "modules/modules_enabled.gen.h"
-
#ifdef MODULE_FREETYPE_ENABLED
#include <ft2build.h>
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 98a67ef309..004cbc2bb3 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -46,7 +46,11 @@ _FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
}
_FORCE_INLINE_ bool is_punct(char32_t p_char) {
- return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F);
+ return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x005E) || (p_char == 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F);
+}
+
+_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
+ return (p_char == 0x005F);
}
/*************************************************************************/
@@ -469,8 +473,8 @@ void TextServerFallback::font_set_oversampling(float p_oversampling) {
oversampling = p_oversampling;
List<RID> fonts;
font_owner.get_owned_list(&fonts);
- for (List<RID>::Element *E = fonts.front(); E; E = E->next()) {
- font_owner.getornull(E->get())->clear_cache();
+ for (const RID &E : fonts) {
+ font_owner.getornull(E)->clear_cache();
}
}
}
@@ -634,17 +638,17 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
span.start = sd->text.length();
span.end = span.start + p_text.length();
// Pre-sort fonts, push fonts with the language support first.
- for (int i = 0; i < p_fonts.size(); i++) {
+ Vector<RID> fonts_no_match;
+ int font_count = p_fonts.size();
+ for (int i = 0; i < font_count; i++) {
if (font_is_language_supported(p_fonts[i], p_language)) {
span.fonts.push_back(p_fonts[i]);
+ } else {
+ fonts_no_match.push_back(p_fonts[i]);
}
}
- // Push the rest valid fonts.
- for (int i = 0; i < p_fonts.size(); i++) {
- if (!font_is_language_supported(p_fonts[i], p_language)) {
- span.fonts.push_back(p_fonts[i]);
- }
- }
+ span.fonts.append_array(fonts_no_match);
+
ERR_FAIL_COND_V(span.fonts.is_empty(), false);
span.font_size = p_size;
span.language = p_language;
@@ -1108,6 +1112,9 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
if (is_punct(c)) {
sd->glyphs.write[i].flags |= GRAPHEME_IS_PUNCTUATION;
}
+ if (is_underscore(c)) {
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_UNDERSCORE;
+ }
if (is_whitespace(c) && !is_linebreak(c)) {
sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE;
sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT;
@@ -1141,6 +1148,161 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
return true;
}
+void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_clip_flags) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped_line);
+ ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid.");
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped_line);
+ }
+
+ bool add_ellipsis = (p_clip_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
+ bool cut_per_word = (p_clip_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
+ bool enforce_ellipsis = (p_clip_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
+
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+ if ((p_clip_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ return;
+ }
+
+ int sd_size = sd->glyphs.size();
+ RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
+ int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
+ uint32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.');
+ Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, dot_gl_idx, last_gl_font_size);
+ uint32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ');
+ Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, whitespace_gl_idx, last_gl_font_size);
+
+ int ellipsis_advance = 0;
+ if (add_ellipsis) {
+ ellipsis_advance = 3 * dot_adv.x + font_get_spacing_glyph(last_gl_font_rid) + (cut_per_word ? whitespace_adv.x : 0);
+ }
+
+ int ell_min_characters = 6;
+ float width = sd->width;
+
+ bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL);
+
+ int trim_pos = (is_rtl) ? sd_size : 0;
+ int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
+
+ int last_valid_cut = 0;
+ bool found = false;
+
+ int glyphs_from = (is_rtl) ? 0 : sd_size - 1;
+ int glyphs_to = (is_rtl) ? sd_size - 1 : -1;
+ int glyphs_delta = (is_rtl) ? +1 : -1;
+
+ for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) {
+ if (!is_rtl) {
+ width -= sd_glyphs[i].advance;
+ }
+ if (sd_glyphs[i].count > 0) {
+ bool above_min_char_treshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters;
+
+ if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_advance : 0) <= p_width) {
+ if (cut_per_word && above_min_char_treshold) {
+ if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
+ last_valid_cut = i;
+ found = true;
+ }
+ } else {
+ last_valid_cut = i;
+ found = true;
+ }
+ if (found) {
+ trim_pos = last_valid_cut;
+
+ if (above_min_char_treshold && width - ellipsis_advance <= p_width) {
+ ellipsis_pos = trim_pos;
+ }
+ break;
+ }
+ }
+ }
+ if (is_rtl) {
+ width -= sd_glyphs[i].advance;
+ }
+ }
+
+ if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) {
+ int added_glyphs = 0;
+ if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) {
+ // Insert an additional space when cutting word bound for aesthetics.
+ if (cut_per_word && (ellipsis_pos > 0)) {
+ TextServer::Glyph gl;
+ gl.start = sd_glyphs[ellipsis_pos].start;
+ gl.end = sd_glyphs[ellipsis_pos].end;
+ gl.count = 1;
+ gl.advance = whitespace_adv.x;
+ gl.index = whitespace_gl_idx;
+ gl.font_rid = last_gl_font_rid;
+ gl.font_size = last_gl_font_size;
+ gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
+
+ // Optimized glyph insertion by replacing a glyph whenever possible.
+ int glyph_idx = trim_pos + ((is_rtl) ? -added_glyphs : added_glyphs);
+ if (is_rtl) {
+ if (glyph_idx < 0) {
+ sd->glyphs.insert(0, gl);
+ } else {
+ sd->glyphs.set(glyph_idx, gl);
+ }
+ } else {
+ if (glyph_idx > (sd_size - 1)) {
+ sd->glyphs.append(gl);
+ } else {
+ sd->glyphs.set(glyph_idx, gl);
+ }
+ }
+ added_glyphs++;
+ }
+ // Add ellipsis dots.
+ for (int d = 0; d < 3; d++) {
+ TextServer::Glyph gl;
+ gl.start = sd_glyphs[ellipsis_pos].start;
+ gl.end = sd_glyphs[ellipsis_pos].end;
+ gl.count = 1;
+ gl.advance = dot_adv.x;
+ gl.index = dot_gl_idx;
+ gl.font_rid = last_gl_font_rid;
+ gl.font_size = last_gl_font_size;
+ gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
+
+ // Optimized glyph insertion by replacing a glyph whenever possible.
+ int glyph_idx = trim_pos + ((is_rtl) ? -added_glyphs : added_glyphs);
+ if (is_rtl) {
+ if (glyph_idx < 0) {
+ sd->glyphs.insert(0, gl);
+ } else {
+ sd->glyphs.set(glyph_idx, gl);
+ }
+ } else {
+ if (glyph_idx > (sd_size - 1)) {
+ sd->glyphs.append(gl);
+ } else {
+ sd->glyphs.set(glyph_idx, gl);
+ }
+ }
+ added_glyphs++;
+ }
+ }
+
+ // Cut the remaining glyphs off.
+ if (!is_rtl) {
+ sd->glyphs.resize(trim_pos + added_glyphs);
+ } else {
+ for (int ridx = 0; ridx <= trim_pos - added_glyphs; ridx++) {
+ sd->glyphs.remove(0);
+ }
+ }
+
+ // Update to correct width.
+ sd->width = width + ((ellipsis_pos != -1) ? ellipsis_advance : 0);
+ }
+}
+
bool TextServerFallback::shaped_text_shape(RID p_shaped) {
_THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 8f5eb1d315..b70c8f4ec0 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -178,6 +178,8 @@ public:
virtual bool shaped_text_update_breaks(RID p_shaped) override;
virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
+ virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_clip_flags) override;
+
virtual bool shaped_text_is_ready(RID p_shaped) const override;
virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;