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.cpp27
-rw-r--r--modules/text_server_fb/dynamic_font_fb.h3
-rw-r--r--modules/text_server_fb/font_fb.h2
-rw-r--r--modules/text_server_fb/text_server_fb.cpp416
-rw-r--r--modules/text_server_fb/text_server_fb.h9
5 files changed, 330 insertions, 127 deletions
diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp
index 66d36bc885..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
@@ -680,6 +680,29 @@ Vector2 DynamicFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, in
return advance;
}
+bool DynamicFontDataFallback::get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
+ _THREAD_SAFE_METHOD_
+ DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size);
+ ERR_FAIL_COND_V(fds == nullptr, false);
+
+ int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ ERR_FAIL_COND_V(error, false);
+
+ r_points.clear();
+ r_contours.clear();
+
+ float h = fds->ascent;
+ float scale = (1.0 / 64.0) / oversampling * fds->scale_color_font;
+ for (short i = 0; i < fds->face->glyph->outline.n_points; i++) {
+ r_points.push_back(Vector3(fds->face->glyph->outline.points[i].x * scale, h - fds->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fds->face->glyph->outline.tags[i])));
+ }
+ for (short i = 0; i < fds->face->glyph->outline.n_contours; i++) {
+ r_contours.push_back(fds->face->glyph->outline.contours[i]);
+ }
+ r_orientation = (FT_Outline_Get_Orientation(&fds->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
+ return true;
+}
+
DynamicFontDataFallback::~DynamicFontDataFallback() {
clear_cache();
if (library != nullptr) {
diff --git a/modules/text_server_fb/dynamic_font_fb.h b/modules/text_server_fb/dynamic_font_fb.h
index eb70f46666..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>
@@ -164,6 +163,8 @@ public:
virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override;
+ virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
+
virtual ~DynamicFontDataFallback() override;
};
diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h
index 218f3df03a..fe9888b7f4 100644
--- a/modules/text_server_fb/font_fb.h
+++ b/modules/text_server_fb/font_fb.h
@@ -93,6 +93,8 @@ struct FontDataFallback {
virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0;
+ virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { return false; };
+
virtual ~FontDataFallback(){};
};
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index f46f96d30d..110194c373 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);
}
/*************************************************************************/
@@ -452,6 +456,13 @@ Vector2 TextServerFallback::font_draw_glyph_outline(RID p_font, RID p_canvas, in
return fd->draw_glyph_outline(p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
}
+bool TextServerFallback::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
+ _THREAD_SAFE_METHOD_
+ const FontDataFallback *fd = font_owner.getornull(p_font);
+ ERR_FAIL_COND_V(!fd, false);
+ return fd->get_glyph_contours(p_size, p_index, r_points, r_contours, r_orientation);
+}
+
float TextServerFallback::font_get_oversampling() const {
return oversampling;
}
@@ -462,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();
}
}
}
@@ -559,7 +570,7 @@ void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::O
}
void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
- //No BiDi support, ignore.
+ // No BiDi support, ignore.
}
TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const {
@@ -627,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;
@@ -650,7 +661,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
return true;
}
-bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) {
+bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
_THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -680,7 +691,7 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
return true;
}
-bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align) {
+bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
_THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -713,34 +724,10 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
if (sd->orientation == ORIENTATION_HORIZONTAL) {
sd->objects[key].rect.position.x = sd->width;
sd->width += sd->objects[key].rect.size.x;
- switch (sd->objects[key].inline_align) {
- case VALIGN_TOP: {
- sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y);
- } break;
- case VALIGN_CENTER: {
- sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.y / 2));
- sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.y / 2));
- } break;
- case VALIGN_BOTTOM: {
- sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y);
- } break;
- }
sd->glyphs.write[i].advance = sd->objects[key].rect.size.x;
} else {
sd->objects[key].rect.position.y = sd->width;
sd->width += sd->objects[key].rect.size.y;
- switch (sd->objects[key].inline_align) {
- case VALIGN_TOP: {
- sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x);
- } break;
- case VALIGN_CENTER: {
- sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.x / 2));
- sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.x / 2));
- } break;
- case VALIGN_BOTTOM: {
- sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x);
- } break;
- }
sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
}
} else {
@@ -773,35 +760,71 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
// Align embedded objects to baseline.
+ float full_ascent = sd->ascent;
+ float full_descent = sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align) {
- case VALIGN_TOP: {
+ switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
E->get().rect.position.y = -sd->ascent;
} break;
- case VALIGN_CENTER: {
- E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ case INLINE_ALIGN_TO_CENTER: {
+ E->get().rect.position.y = (-sd->ascent + sd->descent) / 2;
} break;
- case VALIGN_BOTTOM: {
- E->get().rect.position.y = sd->descent - E->get().rect.size.y;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E->get().rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E->get().rect.position.y = sd->descent;
+ } break;
+ }
+ switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E->get().rect.position.y -= E->get().rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E->get().rect.position.y -= E->get().rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ //NOP
} break;
}
+ full_ascent = MAX(full_ascent, -E->get().rect.position.y);
+ full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
} else {
- switch (E->get().inline_align) {
- case VALIGN_TOP: {
+ switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
E->get().rect.position.x = -sd->ascent;
} break;
- case VALIGN_CENTER: {
- E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ case INLINE_ALIGN_TO_CENTER: {
+ E->get().rect.position.x = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E->get().rect.position.x = 0;
} break;
- case VALIGN_BOTTOM: {
- E->get().rect.position.x = sd->descent - E->get().rect.size.x;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E->get().rect.position.x = sd->descent;
} break;
}
+ switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E->get().rect.position.x -= E->get().rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E->get().rect.position.x -= E->get().rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ //NOP
+ } break;
+ }
+ full_ascent = MAX(full_ascent, -E->get().rect.position.x);
+ full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
}
}
}
+ sd->ascent = full_ascent;
+ sd->descent = full_descent;
}
return true;
}
@@ -858,33 +881,9 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
new_sd->objects[key].rect.position.x = new_sd->width;
new_sd->width += new_sd->objects[key].rect.size.x;
- switch (new_sd->objects[key].inline_align) {
- case VALIGN_TOP: {
- new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y);
- } break;
- case VALIGN_CENTER: {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.y / 2));
- new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.y / 2));
- } break;
- case VALIGN_BOTTOM: {
- new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y);
- } break;
- }
} else {
new_sd->objects[key].rect.position.y = new_sd->width;
new_sd->width += new_sd->objects[key].rect.size.y;
- switch (new_sd->objects[key].inline_align) {
- case VALIGN_TOP: {
- new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x);
- } break;
- case VALIGN_CENTER: {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.x / 2));
- new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.x / 2));
- } break;
- case VALIGN_BOTTOM: {
- new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x);
- } break;
- }
}
} else {
const FontDataFallback *fd = font_owner.getornull(gl.font_rid);
@@ -912,35 +911,72 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
}
+ // Align embedded objects to baseline.
+ float full_ascent = new_sd->ascent;
+ float full_descent = new_sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) {
if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align) {
- case VALIGN_TOP: {
+ switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
E->get().rect.position.y = -new_sd->ascent;
} break;
- case VALIGN_CENTER: {
- E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ case INLINE_ALIGN_TO_CENTER: {
+ E->get().rect.position.y = (-new_sd->ascent + new_sd->descent) / 2;
} break;
- case VALIGN_BOTTOM: {
- E->get().rect.position.y = new_sd->descent - E->get().rect.size.y;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E->get().rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E->get().rect.position.y = new_sd->descent;
+ } break;
+ }
+ switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E->get().rect.position.y -= E->get().rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E->get().rect.position.y -= E->get().rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ //NOP
} break;
}
+ full_ascent = MAX(full_ascent, -E->get().rect.position.y);
+ full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
} else {
- switch (E->get().inline_align) {
- case VALIGN_TOP: {
+ switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
E->get().rect.position.x = -new_sd->ascent;
} break;
- case VALIGN_CENTER: {
- E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ case INLINE_ALIGN_TO_CENTER: {
+ E->get().rect.position.x = (-new_sd->ascent + new_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E->get().rect.position.x = 0;
} break;
- case VALIGN_BOTTOM: {
- E->get().rect.position.x = new_sd->descent - E->get().rect.size.x;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E->get().rect.position.x = new_sd->descent;
} break;
}
+ switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E->get().rect.position.x -= E->get().rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E->get().rect.position.x -= E->get().rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ //NOP
+ } break;
+ }
+ full_ascent = MAX(full_ascent, -E->get().rect.position.x);
+ full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
}
}
}
+ new_sd->ascent = full_ascent;
+ new_sd->descent = full_descent;
}
new_sd->valid = true;
@@ -1101,6 +1137,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;
@@ -1134,6 +1173,128 @@ 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_trim_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);
+ }
+
+ sd->overrun_trim_data.ellipsis_glyph_buf.clear();
+
+ bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
+ bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
+ bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
+ bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE;
+
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+ if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ sd->overrun_trim_data.trim_pos = -1;
+ sd->overrun_trim_data.ellipsis_pos = -1;
+ return;
+ }
+
+ if (justification_aware && !sd->fit_width_minimum_reached) {
+ 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_width = 0;
+ if (add_ellipsis) {
+ ellipsis_width = 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;
+
+ int trim_pos = 0;
+ int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
+
+ int last_valid_cut = 0;
+ bool found = false;
+
+ for (int i = sd_size - 1; i != -1; i--) {
+ width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+
+ if (sd_glyphs[i].count > 0) {
+ bool above_min_char_treshold = (i >= ell_min_characters);
+
+ if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 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 (add_ellipsis && (above_min_char_treshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
+ ellipsis_pos = trim_pos;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ sd->overrun_trim_data.trim_pos = trim_pos;
+ sd->overrun_trim_data.ellipsis_pos = ellipsis_pos;
+ if (trim_pos == 0 && enforce_ellipsis && add_ellipsis) {
+ sd->overrun_trim_data.ellipsis_pos = 0;
+ }
+
+ if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) {
+ 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.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;
+
+ sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
+ }
+ // Add ellipsis dots.
+ TextServer::Glyph gl;
+ gl.count = 1;
+ gl.repeat = 3;
+ 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;
+
+ sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
+ }
+
+ sd->text_trimmed = true;
+ sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);
+ }
+}
+
+TextServer::TrimData TextServerFallback::shaped_text_get_trim_data(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataAdvanced invalid.");
+ return sd->overrun_trim_data;
+}
+
bool TextServerFallback::shaped_text_shape(RID p_shaped) {
_THREAD_SAFE_METHOD_
ShapedTextData *sd = shaped_owner.getornull(p_shaped);
@@ -1167,33 +1328,9 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
sd->objects[span.embedded_key].rect.position.x = sd->width;
sd->width += sd->objects[span.embedded_key].rect.size.x;
- switch (sd->objects[span.embedded_key].inline_align) {
- case VALIGN_TOP: {
- sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y);
- } break;
- case VALIGN_CENTER: {
- sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2));
- sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2));
- } break;
- case VALIGN_BOTTOM: {
- sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y);
- } break;
- }
} else {
sd->objects[span.embedded_key].rect.position.y = sd->width;
sd->width += sd->objects[span.embedded_key].rect.size.y;
- switch (sd->objects[span.embedded_key].inline_align) {
- case VALIGN_TOP: {
- sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x);
- } break;
- case VALIGN_CENTER: {
- sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2));
- sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2));
- } break;
- case VALIGN_BOTTOM: {
- sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x);
- } break;
- }
}
Glyph gl;
gl.start = span.start;
@@ -1287,34 +1424,69 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
// Align embedded objects to baseline.
+ float full_ascent = sd->ascent;
+ float full_descent = sd->descent;
for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align) {
- case VALIGN_TOP: {
+ switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
E->get().rect.position.y = -sd->ascent;
} break;
- case VALIGN_CENTER: {
- E->get().rect.position.y = -(E->get().rect.size.y / 2);
+ case INLINE_ALIGN_TO_CENTER: {
+ E->get().rect.position.y = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E->get().rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E->get().rect.position.y = sd->descent;
+ } break;
+ }
+ switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E->get().rect.position.y -= E->get().rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E->get().rect.position.y -= E->get().rect.size.y / 2;
} break;
- case VALIGN_BOTTOM: {
- E->get().rect.position.y = sd->descent - E->get().rect.size.y;
+ case INLINE_ALIGN_TOP_TO: {
+ //NOP
} break;
}
+ full_ascent = MAX(full_ascent, -E->get().rect.position.y);
+ full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
} else {
- switch (E->get().inline_align) {
- case VALIGN_TOP: {
+ switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
E->get().rect.position.x = -sd->ascent;
} break;
- case VALIGN_CENTER: {
- E->get().rect.position.x = -(E->get().rect.size.x / 2);
+ case INLINE_ALIGN_TO_CENTER: {
+ E->get().rect.position.x = (-sd->ascent + sd->descent) / 2;
} break;
- case VALIGN_BOTTOM: {
- E->get().rect.position.x = sd->descent - E->get().rect.size.x;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E->get().rect.position.x = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E->get().rect.position.x = sd->descent;
+ } break;
+ }
+ switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E->get().rect.position.x -= E->get().rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E->get().rect.position.x -= E->get().rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ //NOP
} break;
}
+ full_ascent = MAX(full_ascent, -E->get().rect.position.x);
+ full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
}
}
-
+ sd->ascent = full_ascent;
+ sd->descent = full_descent;
sd->valid = true;
return sd->valid;
}
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index b10369d172..d4cab2409a 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -137,6 +137,8 @@ public:
virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
+ virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
+
virtual float font_get_oversampling() const override;
virtual void font_set_oversampling(float p_oversampling) override;
@@ -163,8 +165,8 @@ public:
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
- virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override;
- virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override;
+ virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override;
+ virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) override;
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
@@ -176,6 +178,9 @@ 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_trim_flags) override;
+ virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override;
+
virtual bool shaped_text_is_ready(RID p_shaped) const override;
virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;