2
0
Эх сурвалжийг харах

Merge pull request #43981 from bruvzg/ctl_font_spacing

[Complex Text Layouts] Adds missing Font::SPACING_* to the controls, align glyphs to pixel grid.
Rémi Verschelde 4 жил өмнө
parent
commit
93e9c9c470

+ 10 - 4
modules/text_server_adv/bitmap_font_adv.cpp

@@ -175,8 +175,9 @@ static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *font, void *font_data, hb_
 	return true;
 }
 
-static hb_font_funcs_t *_hb_bmp_get_font_funcs() {
-	hb_font_funcs_t *funcs = hb_font_funcs_create();
+static hb_font_funcs_t *funcs = nullptr;
+void hb_bmp_create_font_funcs() {
+	funcs = hb_font_funcs_create();
 
 	hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr);
 	//hb_font_funcs_set_font_v_extents_func (funcs, hb_bmp_get_font_v_extents, nullptr, nullptr);
@@ -194,12 +195,17 @@ static hb_font_funcs_t *_hb_bmp_get_font_funcs() {
 	//hb_font_funcs_set_glyph_from_name_func (funcs, hb_bmp_get_glyph_from_name, nullptr, nullptr);
 
 	hb_font_funcs_make_immutable(funcs);
+}
 
-	return funcs;
+void hb_bmp_free_font_funcs() {
+	if (funcs != nullptr) {
+		hb_font_funcs_destroy(funcs);
+		funcs = nullptr;
+	}
 }
 
 static void _hb_bmp_font_set_funcs(hb_font_t *p_font, BitmapFontDataAdvanced *p_face, int p_size, bool p_unref) {
-	hb_font_set_funcs(p_font, _hb_bmp_get_font_funcs(), _hb_bmp_font_create(p_face, p_size, p_unref), _hb_bmp_font_destroy);
+	hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_size, p_unref), _hb_bmp_font_destroy);
 }
 
 hb_font_t *hb_bmp_font_create(BitmapFontDataAdvanced *p_face, int p_size, hb_destroy_func_t p_destroy) {

+ 3 - 0
modules/text_server_adv/bitmap_font_adv.h

@@ -33,6 +33,9 @@
 
 #include "font_adv.h"
 
+void hb_bmp_create_font_funcs();
+void hb_bmp_free_font_funcs();
+
 struct BitmapFontDataAdvanced : public FontDataAdvanced {
 	_THREAD_SAFE_CLASS_
 

+ 2 - 1
modules/text_server_adv/script_iterator.cpp

@@ -56,11 +56,12 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
 	int paren_sp = -1;
 	int start_sp = paren_sp;
 	UErrorCode err = U_ZERO_ERROR;
+	const char32_t *str = p_string.ptr();
 
 	do {
 		script_code = USCRIPT_COMMON;
 		for (script_start = script_end; script_end < p_length; script_end++) {
-			UChar32 ch = p_string[script_end];
+			UChar32 ch = str[script_end];
 			UScriptCode sc = uscript_getScript(ch, &err);
 			if (U_FAILURE(err)) {
 				ERR_FAIL_MSG(u_errorName(err));

+ 105 - 78
modules/text_server_adv/text_server_adv.cpp

@@ -1108,7 +1108,9 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
 		sd->width = 0;
 		sd->upos = 0;
 		sd->uthk = 0;
-		for (int i = 0; i < sd->glyphs.size(); i++) {
+		int sd_size = sd->glyphs.size();
+
+		for (int i = 0; i < sd_size; i++) {
 			Glyph gl = sd->glyphs[i];
 			Variant key;
 			if (gl.count == 1) {
@@ -1128,8 +1130,8 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
 							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y);
 						} break;
 						case VALIGN_CENTER: {
-							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y / 2);
-							sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y / 2);
+							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);
@@ -1144,8 +1146,8 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
 							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x);
 						} break;
 						case VALIGN_CENTER: {
-							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x / 2);
-							sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x / 2);
+							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);
@@ -1160,19 +1162,19 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
 						sd->ascent = MAX(sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off));
 						sd->descent = MAX(sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off));
 					} else {
-						sd->ascent = MAX(sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
-						sd->descent = MAX(sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
+						sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+						sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
 					}
 					sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
 					sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
 				} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
 					// Glyph not found, replace with hex code box.
 					if (sd->orientation == ORIENTATION_HORIZONTAL) {
-						sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f);
-						sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f);
+						sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+						sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
 					} else {
-						sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
-						sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
+						sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+						sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
 					}
 				}
 				sd->width += gl.advance * gl.repeat;
@@ -1248,6 +1250,11 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 		new_sd->utf16 = new_sd->text.utf16();
 		new_sd->script_iter = memnew(ScriptIterator(new_sd->text, 0, new_sd->text.length()));
 
+		int sd_size = sd->glyphs.size();
+		const Glyph *sd_glyphs = sd->glyphs.ptr();
+		const FontDataAdvanced *fd = nullptr;
+		RID prev_rid = RID();
+
 		for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
 			UErrorCode err = U_ZERO_ERROR;
 
@@ -1280,21 +1287,23 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 				int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start);
 				int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start + _bidi_run_length);
 
-				for (int j = 0; j < sd->glyphs.size(); j++) {
-					if ((sd->glyphs[j].start >= bidi_run_start) && (sd->glyphs[j].end <= bidi_run_end)) {
+				for (int j = 0; j < sd_size; j++) {
+					if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) {
 						// Copy glyphs.
-						Glyph gl = sd->glyphs[j];
+						Glyph gl = sd_glyphs[j];
 						Variant key;
+						bool find_embedded = false;
 						if (gl.count == 1) {
 							for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
 								if (E->get().pos == gl.start) {
+									find_embedded = true;
 									key = E->key();
 									new_sd->objects[key] = E->get();
 									break;
 								}
 							}
 						}
-						if (key != Variant()) {
+						if (find_embedded) {
 							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;
@@ -1303,8 +1312,8 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 										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, new_sd->objects[key].rect.size.y / 2);
-										new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y / 2);
+										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);
@@ -1318,8 +1327,8 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 										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, new_sd->objects[key].rect.size.x / 2);
-										new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x / 2);
+										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);
@@ -1327,23 +1336,26 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 								}
 							}
 						} else {
-							const FontDataAdvanced *fd = font_owner.getornull(gl.font_rid);
+							if (prev_rid != gl.font_rid) {
+								fd = font_owner.getornull(gl.font_rid);
+								prev_rid = gl.font_rid;
+							}
 							if (fd != nullptr) {
 								if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
 									new_sd->ascent = MAX(new_sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off));
 									new_sd->descent = MAX(new_sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off));
 								} else {
-									new_sd->ascent = MAX(new_sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
-									new_sd->descent = MAX(new_sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
+									new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+									new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
 								}
 							} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
 								// Glyph not found, replace with hex code box.
 								if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
-									new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f);
-									new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f);
+									new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+									new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
 								} else {
-									new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
-									new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
+									new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+									new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
 								}
 							}
 							new_sd->width += gl.advance * gl.repeat;
@@ -1491,9 +1503,9 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
 				if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
 					float old_adv = gl.advance;
 					if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
-						gl.advance = MAX(gl.advance + delta_width_per_space, 0.f);
+						gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.f));
 					} else {
-						gl.advance = MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size);
+						gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size));
 					}
 					sd->width += (gl.advance - old_adv);
 				}
@@ -1529,8 +1541,10 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float
 		delta = -1;
 	}
 
+	Glyph *gl = sd->glyphs.ptrw();
+
 	for (int i = start; i != end; i += delta) {
-		if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+		if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
 			float tab_off = 0.f;
 			while (tab_off <= off) {
 				tab_off += p_tab_stops[tab_index];
@@ -1539,13 +1553,13 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float
 					tab_index = 0;
 				}
 			}
-			float old_adv = sd->glyphs.write[i].advance;
-			sd->glyphs.write[i].advance = (tab_off - off);
-			sd->width += sd->glyphs.write[i].advance - old_adv;
+			float old_adv = gl[i].advance;
+			gl[i].advance = tab_off - off;
+			sd->width += gl[i].advance - old_adv;
 			off = 0;
 			continue;
 		}
-		off += sd->glyphs[i].advance * sd->glyphs[i].repeat;
+		off += gl[i].advance * gl[i].repeat;
 	}
 
 	return 0.f;
@@ -1565,8 +1579,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
 
 	const UChar *data = sd->utf16.ptr();
 
-	Map<int, bool> breaks;
-
+	HashMap<int, bool> breaks;
 	UErrorCode err = U_ZERO_ERROR;
 	for (int i = 0; i < sd->spans.size(); i++) {
 		UBreakIterator *bi = ubrk_open(UBRK_LINE, sd->spans[i].language.ascii().get_data(), data + _convert_pos_inv(sd, sd->spans[i].start), _convert_pos_inv(sd, sd->spans[i].end - sd->spans[i].start), &err);
@@ -1598,40 +1611,48 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
 
 	sd->sort_valid = false;
 	sd->glyphs_logical.clear();
+	int sd_size = sd->glyphs.size();
+	const char32_t *ch = sd->text.ptr();
+	Glyph *sd_glyphs = sd->glyphs.ptrw();
 
-	for (int i = 0; i < sd->glyphs.size(); i++) {
-		if (sd->glyphs[i].count > 0) {
-			char32_t c = sd->text[sd->glyphs[i].start - sd->start];
+	for (int i = 0; i < sd_size; i++) {
+		if (sd_glyphs[i].count > 0) {
+			char32_t c = ch[sd_glyphs[i].start - sd->start];
 			if (c == 0xfffc) {
 				continue;
 			}
 			if (c == 0x0009 || c == 0x000b) {
-				sd->glyphs.write[i].flags |= GRAPHEME_IS_TAB;
+				sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
 			}
 			if (breaks.has(sd->glyphs[i].start)) {
 				if (breaks[sd->glyphs[i].start]) {
-					sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_HARD;
+					sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
 				} else {
 					if (is_whitespace(c)) {
-						sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT;
-						sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE;
+						sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+						sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
 					} else {
 						TextServer::Glyph gl;
-						gl.start = sd->glyphs[i].start;
-						gl.end = sd->glyphs[i].end;
+						gl.start = sd_glyphs[i].start;
+						gl.end = sd_glyphs[i].end;
 						gl.count = 1;
 						gl.repeat = 1;
-						gl.font_rid = sd->glyphs[i].font_rid;
-						gl.font_size = sd->glyphs[i].font_size;
+						gl.font_rid = sd_glyphs[i].font_rid;
+						gl.font_size = sd_glyphs[i].font_size;
 						gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;
-						sd->glyphs.insert(i + sd->glyphs[i].count, gl); // insert after
-						i += sd->glyphs[i].count;
+						sd->glyphs.insert(i + sd_glyphs[i].count, gl); // insert after
+
+						// Update write pointer and size.
+						sd_size = sd->glyphs.size();
+						sd_glyphs = sd->glyphs.ptrw();
+
+						i += sd_glyphs[i].count;
 						continue;
 					}
 				}
 			}
 
-			i += (sd->glyphs[i].count - 1);
+			i += (sd_glyphs[i].count - 1);
 		}
 	}
 
@@ -1767,9 +1788,10 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
 
 	sd->sort_valid = false;
 	sd->glyphs_logical.clear();
+	int sd_size = sd->glyphs.size();
 
 	if (jstops.size() > 0) {
-		for (int i = 0; i < sd->glyphs.size(); i++) {
+		for (int i = 0; i < sd_size; i++) {
 			if (sd->glyphs[i].count > 0) {
 				if (jstops.has(sd->glyphs[i].start)) {
 					char32_t c = sd->text[sd->glyphs[i].start - sd->start];
@@ -1850,15 +1872,15 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced
 
 	if (glyph_count > 0) {
 		if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
-			gl.advance = glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size));
+			gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size)));
 		} else {
-			gl.advance = -glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size));
+			gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size)));
 		}
 		gl.count = 1;
 
 		gl.index = glyph_info[0].codepoint;
-		gl.x_off = glyph_pos[0].x_offset / (64.0 / fd->get_font_scale(p_font_size));
-		gl.y_off = -glyph_pos[0].y_offset / (64.0 / fd->get_font_scale(p_font_size));
+		gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / fd->get_font_scale(p_font_size)));
+		gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / fd->get_font_scale(p_font_size)));
 
 		if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) {
 			gl.flags |= GRAPHEME_IS_VALID;
@@ -1873,8 +1895,9 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 		fd = font_owner.getornull(p_fonts[p_fb_index]);
 	}
 
+	int fs = p_sd->spans[p_span].font_size;
 	if (fd == nullptr) {
-		// Add fallback glyohs
+		// Add fallback glyphs
 		for (int i = p_start; i < p_end; i++) {
 			if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
 				TextServer::Glyph gl;
@@ -1882,20 +1905,20 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 				gl.end = i + 1;
 				gl.count = 1;
 				gl.index = p_sd->text[i];
-				gl.font_size = p_sd->spans[p_span].font_size;
+				gl.font_size = fs;
 				gl.font_rid = RID();
 				gl.flags = 0;
 				if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
 					gl.flags |= TextServer::GRAPHEME_IS_RTL;
 				}
 				if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
-					gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x;
-					p_sd->ascent = MAX(p_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f);
-					p_sd->descent = MAX(p_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f);
+					gl.advance = get_hex_code_box_size(fs, gl.index).x;
+					p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.75f));
+					p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.25f));
 				} else {
-					gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y;
-					p_sd->ascent = MAX(p_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
-					p_sd->descent = MAX(p_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
+					gl.advance = get_hex_code_box_size(fs, gl.index).y;
+					p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f));
+					p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f));
 				}
 				p_sd->width += gl.advance;
 
@@ -1905,7 +1928,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 		return;
 	}
 
-	hb_font_t *hb_font = fd->get_hb_handle(p_sd->spans[p_span].font_size);
+	hb_font_t *hb_font = fd->get_hb_handle(fs);
 	ERR_FAIL_COND(hb_font == nullptr);
 
 	hb_buffer_clear_contents(p_sd->hb_buffer);
@@ -1981,17 +2004,17 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 			gl.count = 0;
 
 			gl.font_rid = p_fonts[p_fb_index];
-			gl.font_size = p_sd->spans[p_span].font_size;
+			gl.font_size = fs;
 
 			gl.index = glyph_info[i].codepoint;
 			if (gl.index != 0) {
 				if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
-					gl.advance = glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(gl.font_size));
+					gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(fs)));
 				} else {
-					gl.advance = -glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(gl.font_size));
+					gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(fs)));
 				}
-				gl.x_off = glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(gl.font_size));
-				gl.y_off = -glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(gl.font_size));
+				gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(fs)));
+				gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(fs)));
 			}
 
 			if (p_sd->preserve_control) {
@@ -2026,14 +2049,12 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 				}
 				for (int j = 0; j < w[i].count; j++) {
 					if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
-						p_sd->ascent = MAX(p_sd->ascent, MAX(fd->get_ascent(w[i + j].font_size), w[i + j].y_off));
-						p_sd->descent = MAX(p_sd->descent, MAX(fd->get_descent(w[i + j].font_size), w[i + j].y_off));
+						p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off);
+						p_sd->descent = MAX(p_sd->descent, w[i + j].y_off);
 					} else {
-						p_sd->ascent = MAX(p_sd->ascent, fd->get_advance(w[i + j].index, w[i + j].font_size).x * 0.5);
-						p_sd->descent = MAX(p_sd->descent, fd->get_advance(w[i + j].index, w[i + j].font_size).x * 0.5);
+						p_sd->ascent = MAX(p_sd->ascent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5));
+						p_sd->descent = MAX(p_sd->descent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5));
 					}
-					p_sd->upos = MAX(p_sd->upos, font_get_underline_position(w[i + j].font_rid, w[i + j].font_size));
-					p_sd->uthk = MAX(p_sd->uthk, font_get_underline_thickness(w[i + j].font_rid, w[i + j].font_size));
 					p_sd->width += w[i + j].advance;
 					p_sd->glyphs.push_back(w[i + j]);
 				}
@@ -2051,6 +2072,10 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 		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);
 		}
+		p_sd->ascent = MAX(p_sd->ascent, fd->get_ascent(fs));
+		p_sd->descent = MAX(p_sd->descent, fd->get_descent(fs));
+		p_sd->upos = MAX(p_sd->upos, fd->get_underline_position(fs));
+		p_sd->uthk = MAX(p_sd->uthk, fd->get_underline_thickness(fs));
 	}
 }
 
@@ -2180,8 +2205,8 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
 										sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y);
 									} break;
 									case VALIGN_CENTER: {
-										sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y / 2);
-										sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y / 2);
+										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);
@@ -2195,8 +2220,8 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
 										sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x);
 									} break;
 									case VALIGN_CENTER: {
-										sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x / 2);
-										sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x / 2);
+										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);
@@ -2507,9 +2532,11 @@ void TextServerAdvanced::register_server() {
 }
 
 TextServerAdvanced::TextServerAdvanced() {
+	hb_bmp_create_font_funcs();
 }
 
 TextServerAdvanced::~TextServerAdvanced() {
+	hb_bmp_free_font_funcs();
 	u_cleanup();
 #ifndef ICU_STATIC_DATA
 	if (icu_data != nullptr) {

+ 60 - 46
modules/text_server_fb/text_server_fb.cpp

@@ -625,7 +625,11 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
 		sd->width = 0;
 		sd->upos = 0;
 		sd->uthk = 0;
-		for (int i = 0; i < sd->glyphs.size(); i++) {
+		int sd_size = sd->glyphs.size();
+		const FontDataFallback *fd = nullptr;
+		RID prev_rid = RID();
+
+		for (int i = 0; i < sd_size; i++) {
 			Glyph gl = sd->glyphs[i];
 			Variant key;
 			if (gl.count == 1) {
@@ -645,8 +649,8 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
 							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y);
 						} break;
 						case VALIGN_CENTER: {
-							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y / 2);
-							sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y / 2);
+							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);
@@ -661,8 +665,8 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
 							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x);
 						} break;
 						case VALIGN_CENTER: {
-							sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x / 2);
-							sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x / 2);
+							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);
@@ -671,25 +675,28 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
 					sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
 				}
 			} else {
-				const FontDataFallback *fd = font_owner.getornull(gl.font_rid);
+				if (prev_rid != gl.font_rid) {
+					fd = font_owner.getornull(gl.font_rid);
+					prev_rid = gl.font_rid;
+				}
 				if (fd != nullptr) {
 					if (sd->orientation == ORIENTATION_HORIZONTAL) {
 						sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size));
 						sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size));
 					} else {
-						sd->ascent = MAX(sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
-						sd->descent = MAX(sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
+						sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+						sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
 					}
 					sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
 					sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
 				} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
 					// Glyph not found, replace with hex code box.
 					if (sd->orientation == ORIENTATION_HORIZONTAL) {
-						sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f);
-						sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f);
+						sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+						sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
 					} else {
-						sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
-						sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
+						sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+						sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
 					}
 				}
 				sd->width += gl.advance * gl.repeat;
@@ -760,21 +767,25 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 
 	if (p_length > 0) {
 		new_sd->text = sd->text.substr(p_start, p_length);
+		int sd_size = sd->glyphs.size();
+		const Glyph *sd_glyphs = sd->glyphs.ptr();
 
-		for (int i = 0; i < sd->glyphs.size(); i++) {
-			if ((sd->glyphs[i].start >= new_sd->start) && (sd->glyphs[i].end <= new_sd->end)) {
-				Glyph gl = sd->glyphs[i];
+		for (int i = 0; i < sd_size; i++) {
+			if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) {
+				Glyph gl = sd_glyphs[i];
 				Variant key;
+				bool find_embedded = false;
 				if (gl.count == 1) {
 					for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
 						if (E->get().pos == gl.start) {
+							find_embedded = true;
 							key = E->key();
 							new_sd->objects[key] = E->get();
 							break;
 						}
 					}
 				}
-				if (key != Variant()) {
+				if (find_embedded) {
 					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;
@@ -783,8 +794,8 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 								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, new_sd->objects[key].rect.size.y / 2);
-								new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y / 2);
+								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);
@@ -798,8 +809,8 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 								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, new_sd->objects[key].rect.size.x / 2);
-								new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x / 2);
+								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);
@@ -813,17 +824,17 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
 							new_sd->ascent = MAX(new_sd->ascent, fd->get_ascent(gl.font_size));
 							new_sd->descent = MAX(new_sd->descent, fd->get_descent(gl.font_size));
 						} else {
-							new_sd->ascent = MAX(new_sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
-							new_sd->descent = MAX(new_sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
+							new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+							new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
 						}
 					} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
 						// Glyph not found, replace with hex code box.
 						if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
-							new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f);
-							new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f);
+							new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+							new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
 						} else {
-							new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
-							new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
+							new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+							new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
 						}
 					}
 					new_sd->width += gl.advance * gl.repeat;
@@ -943,7 +954,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
 			if (gl.count > 0) {
 				if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
 					float old_adv = gl.advance;
-					gl.advance = MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size);
+					gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size));
 					sd->width += (gl.advance - old_adv);
 				}
 			}
@@ -978,8 +989,10 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float
 		delta = -1;
 	}
 
+	Glyph *gl = sd->glyphs.ptrw();
+
 	for (int i = start; i != end; i += delta) {
-		if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+		if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
 			float tab_off = 0.f;
 			while (tab_off <= off) {
 				tab_off += p_tab_stops[tab_index];
@@ -988,13 +1001,13 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float
 					tab_index = 0;
 				}
 			}
-			float old_adv = sd->glyphs.write[i].advance;
-			sd->glyphs.write[i].advance = (tab_off - off);
-			sd->width += sd->glyphs.write[i].advance - old_adv;
+			float old_adv = gl[i].advance;
+			gl[i].advance = tab_off - off;
+			sd->width += gl[i].advance - old_adv;
 			off = 0;
 			continue;
 		}
-		off += sd->glyphs[i].advance * sd->glyphs[i].repeat;
+		off += gl[i].advance * gl[i].repeat;
 	}
 
 	return 0.f;
@@ -1012,7 +1025,8 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
 		return true; // Noting to do.
 	}
 
-	for (int i = 0; i < sd->glyphs.size(); i++) {
+	int sd_size = sd->glyphs.size();
+	for (int i = 0; i < sd_size; i++) {
 		if (sd->glyphs[i].count > 0) {
 			char32_t c = sd->text[sd->glyphs[i].start];
 			if (is_whitespace(c) && !is_linebreak(c)) {
@@ -1086,8 +1100,8 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
 						sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y);
 					} break;
 					case VALIGN_CENTER: {
-						sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y / 2);
-						sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y / 2);
+						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);
@@ -1101,8 +1115,8 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
 						sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x);
 					} break;
 					case VALIGN_CENTER: {
-						sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x / 2);
-						sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x / 2);
+						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);
@@ -1157,14 +1171,14 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
 							sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size));
 						} else {
 							gl.advance = fd->get_advance(gl.index, gl.font_size).y;
-							gl.x_off = -fd->get_advance(gl.index, gl.font_size).x * 0.5;
+							gl.x_off = -Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5);
 							gl.y_off = fd->get_ascent(gl.font_size);
-							sd->ascent = MAX(sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
-							sd->descent = MAX(sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5);
+							sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
+							sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5));
 						}
 					}
-					sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
-					sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
+					sd->upos = MAX(sd->upos, fd->get_underline_position(gl.font_size));
+					sd->uthk = MAX(sd->uthk, fd->get_underline_thickness(gl.font_size));
 
 					// Add kerning to previous glyph.
 					if (sd->glyphs.size() > 0) {
@@ -1181,12 +1195,12 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
 					// Glyph not found, replace with hex code box.
 					if (sd->orientation == ORIENTATION_HORIZONTAL) {
 						gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x;
-						sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f);
-						sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f);
+						sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
+						sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
 					} else {
 						gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y;
-						sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
-						sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f);
+						sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+						sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
 					}
 				}
 				sd->width += gl.advance;

+ 1 - 0
scene/gui/item_list.cpp

@@ -78,6 +78,7 @@ void ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
 	item.icon_region = Rect2i();
 	item.icon_modulate = Color(1, 1, 1, 1);
 	//item.text=p_item;
+	item.text_buf.instance();
 	item.selectable = p_selectable;
 	item.selected = false;
 	item.disabled = false;

+ 21 - 14
scene/gui/label.cpp

@@ -64,16 +64,17 @@ bool Label::is_uppercase() const {
 }
 
 int Label::get_line_height(int p_line) const {
+	Ref<Font> font = get_theme_font("font");
 	if (p_line >= 0 && p_line < lines_rid.size()) {
-		return TS->shaped_text_get_size(lines_rid[p_line]).y;
+		return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
 	} else if (lines_rid.size() > 0) {
 		int h = 0;
 		for (int i = 0; i < lines_rid.size(); i++) {
-			h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y);
+			h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
 		}
 		return h;
 	} else {
-		return get_theme_font("font")->get_height(get_theme_font_size("font_size"));
+		return font->get_height(get_theme_font_size("font_size"));
 	}
 }
 
@@ -138,6 +139,7 @@ void Label::_shape() {
 void Label::_update_visible() {
 	int line_spacing = get_theme_constant("line_spacing", "Label");
 	Ref<StyleBox> style = get_theme_stylebox("normal", "Label");
+	Ref<Font> font = get_theme_font("font");
 	int lines_visible = lines_rid.size();
 
 	if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
@@ -147,7 +149,7 @@ void Label::_update_visible() {
 	minsize.height = 0;
 	int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
 	for (int64_t i = lines_skipped; i < last_line; i++) {
-		minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+		minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 		if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 			break;
 		}
@@ -197,7 +199,7 @@ void Label::_notification(int p_what) {
 
 		// Get number of lines to fit to the height.
 		for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
-			total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+			total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 			if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 				break;
 			}
@@ -213,7 +215,7 @@ void Label::_notification(int p_what) {
 		// Get real total height.
 		total_h = 0;
 		for (int64_t i = lines_skipped; i < last_line; i++) {
-			total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+			total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 		}
 
 		int vbegin = 0, vsep = 0;
@@ -249,8 +251,10 @@ void Label::_notification(int p_what) {
 		if (percent_visible < 1) {
 			int total_glyphs = 0;
 			for (int i = lines_skipped; i < last_line; i++) {
-				const Vector<TextServer::Glyph> glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
-				for (int j = 0; j < glyphs.size(); j++) {
+				const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]);
+				const TextServer::Glyph *glyphs = visual.ptr();
+				int gl_size = visual.size();
+				for (int j = 0; j < gl_size; j++) {
 					if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
 						total_glyphs++;
 					}
@@ -263,7 +267,7 @@ void Label::_notification(int p_what) {
 		ofs.y = style->get_offset().y + vbegin;
 		for (int i = lines_skipped; i < last_line; i++) {
 			ofs.x = 0;
-			ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
+			ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(Font::SPACING_TOP);
 			switch (align) {
 				case ALIGN_FILL:
 				case ALIGN_LEFT: {
@@ -285,11 +289,13 @@ void Label::_notification(int p_what) {
 				} break;
 			}
 
-			const Vector<TextServer::Glyph> glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
+			const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]);
+			const TextServer::Glyph *glyphs = visual.ptr();
+			int gl_size = visual.size();
 
 			float x = ofs.x;
 			int outlines_drawn = glyhps_drawn;
-			for (int j = 0; j < glyphs.size(); j++) {
+			for (int j = 0; j < gl_size; j++) {
 				for (int k = 0; k < glyphs[j].repeat; k++) {
 					if (glyphs[j].font_rid != RID()) {
 						if (font_color_shadow.a > 0) {
@@ -318,7 +324,7 @@ void Label::_notification(int p_what) {
 			}
 			ofs.x = x;
 
-			for (int j = 0; j < glyphs.size(); j++) {
+			for (int j = 0; j < gl_size; j++) {
 				for (int k = 0; k < glyphs[j].repeat; k++) {
 					if (glyphs[j].font_rid != RID()) {
 						TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_color);
@@ -337,7 +343,7 @@ void Label::_notification(int p_what) {
 				}
 			}
 
-			ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing;
+			ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(Font::SPACING_BOTTOM);
 		}
 	}
 
@@ -381,12 +387,13 @@ int Label::get_line_count() const {
 }
 
 int Label::get_visible_line_count() const {
+	Ref<Font> font = get_theme_font("font");
 	Ref<StyleBox> style = get_theme_stylebox("normal");
 	int line_spacing = get_theme_constant("line_spacing");
 	int lines_visible = 0;
 	float total_h = 0;
 	for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
-		total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+		total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
 		if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
 			break;
 		}

+ 8 - 5
scene/gui/line_edit.cpp

@@ -732,6 +732,7 @@ void LineEdit::_notification(int p_what) {
 				style = get_theme_stylebox("read_only");
 				draw_caret = false;
 			}
+			Ref<Font> font = get_theme_font("font");
 
 			style->draw(ci, Rect2(Point2(), size));
 
@@ -742,7 +743,7 @@ void LineEdit::_notification(int p_what) {
 			int x_ofs = 0;
 			bool using_placeholder = text.empty() && ime_text.empty();
 			float text_width = TS->shaped_text_get_size(text_rid).x;
-			float text_height = TS->shaped_text_get_size(text_rid).y;
+			float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM);
 
 			switch (align) {
 				case ALIGN_FILL:
@@ -833,14 +834,16 @@ void LineEdit::_notification(int p_what) {
 					RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color);
 				}
 			}
-			const Vector<TextServer::Glyph> glyphs = TS->shaped_text_get_glyphs(text_rid);
+			const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(text_rid);
+			const TextServer::Glyph *glyphs = visual.ptr();
+			int gl_size = visual.size();
 
 			// Draw text.
 			ofs.y += TS->shaped_text_get_ascent(text_rid);
-			for (int i = 0; i < glyphs.size(); i++) {
+			for (int i = 0; i < gl_size; i++) {
 				bool selected = selection.enabled && glyphs[i].start >= selection.begin && glyphs[i].end <= selection.end;
 				for (int j = 0; j < glyphs[i].repeat; j++) {
-					if (ceil(ofs.x) >= x_ofs && floor(ofs.x + glyphs[i].advance) <= ofs_max) {
+					if (ceil(ofs.x) >= x_ofs && (ofs.x + glyphs[i].advance) <= ofs_max) {
 						if (glyphs[i].font_rid != RID()) {
 							TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_color_selected : font_color);
 						} else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
@@ -1570,7 +1573,7 @@ Size2 LineEdit::get_minimum_size() const {
 		min_size.width = MAX(min_size.width, full_width + space_size);
 	}
 
-	min_size.height = MAX(TS->shaped_text_get_size(text_rid).y, font->get_height(font_size));
+	min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM), font->get_height(font_size));
 
 	// Take icons into account.
 	bool using_placeholder = text.empty() && ime_text.empty();

+ 6 - 3
scene/gui/text_edit.cpp

@@ -1141,7 +1141,7 @@ void TextEdit::_notification(int p_what) {
 
 					// Draw line.
 					RID rid = ldata->get_line_rid(line_wrap_index);
-					float text_height = TS->shaped_text_get_size(rid).y;
+					float text_height = TS->shaped_text_get_size(rid).y + cache.font->get_spacing(Font::SPACING_TOP) + cache.font->get_spacing(Font::SPACING_BOTTOM);
 
 					if (rtl) {
 						char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x;
@@ -1240,10 +1240,13 @@ void TextEdit::_notification(int p_what) {
 
 					ofs_y += (row_height - text_height) / 2;
 
-					const Vector<TextServer::Glyph> glyphs = TS->shaped_text_get_glyphs(rid);
+					const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
+					const TextServer::Glyph *glyphs = visual.ptr();
+					int gl_size = visual.size();
+
 					ofs_y += ldata->get_line_ascent(line_wrap_index);
 					float char_ofs = 0.f;
-					for (int j = 0; j < glyphs.size(); j++) {
+					for (int j = 0; j < gl_size; j++) {
 						if (color_map.has(glyphs[j].start)) {
 							current_color = color_map[glyphs[j].start].get("color");
 							if (readonly && current_color.a > cache.font_color_readonly.a) {

+ 10 - 10
scene/resources/font.cpp

@@ -595,41 +595,41 @@ Dictionary Font::get_feature_list() const {
 float Font::get_height(int p_size) const {
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_height(p_size);
+		ret = MAX(ret, data[i]->get_height(p_size));
 	}
-	return (ret / data.size()) + spacing_top + spacing_bottom;
+	return ret + spacing_top + spacing_bottom;
 }
 
 float Font::get_ascent(int p_size) const {
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_ascent(p_size);
+		ret = MAX(ret, data[i]->get_ascent(p_size));
 	}
-	return (ret / data.size()) + spacing_top;
+	return ret + spacing_top;
 }
 
 float Font::get_descent(int p_size) const {
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_descent(p_size);
+		ret = MAX(ret, data[i]->get_descent(p_size));
 	}
-	return (ret / data.size()) + spacing_bottom;
+	return ret + spacing_bottom;
 }
 
 float Font::get_underline_position(int p_size) const {
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_underline_position(p_size);
+		ret = MAX(ret, data[i]->get_underline_position(p_size));
 	}
-	return (ret / data.size());
+	return ret;
 }
 
 float Font::get_underline_thickness(int p_size) const {
 	float ret = 0.f;
 	for (int i = 0; i < data.size(); i++) {
-		ret += data[i]->get_underline_thickness(p_size);
+		ret = MAX(ret, data[i]->get_underline_thickness(p_size));
 	}
-	return (ret / data.size());
+	return ret;
 }
 
 int Font::get_spacing(int p_type) const {

+ 17 - 7
scene/resources/text_line.cpp

@@ -113,6 +113,8 @@ RID TextLine::get_rid() const {
 
 void TextLine::clear() {
 	TS->shaped_text_clear(rid);
+	spacing_top = 0;
+	spacing_bottom = 0;
 }
 
 void TextLine::set_preserve_invalid(bool p_enabled) {
@@ -166,6 +168,8 @@ void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) {
 
 bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
 	bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	dirty = true;
 	return res;
 }
@@ -233,17 +237,21 @@ float TextLine::get_width() const {
 
 Size2 TextLine::get_size() const {
 	const_cast<TextLine *>(this)->_shape();
-	return TS->shaped_text_get_size(rid);
+	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+		return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
+	} else {
+		return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y);
+	}
 }
 
 float TextLine::get_line_ascent() const {
 	const_cast<TextLine *>(this)->_shape();
-	return TS->shaped_text_get_ascent(rid);
+	return TS->shaped_text_get_ascent(rid) + spacing_top;
 }
 
 float TextLine::get_line_descent() const {
 	const_cast<TextLine *>(this)->_shape();
-	return TS->shaped_text_get_descent(rid);
+	return TS->shaped_text_get_descent(rid) + spacing_bottom;
 }
 
 float TextLine::get_line_width() const {
@@ -291,10 +299,10 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) co
 
 	float clip_l;
 	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(rid);
+		ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.x - ofs.x);
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(rid);
+		ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.y - ofs.y);
 	}
 	return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color);
@@ -330,10 +338,10 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si
 
 	float clip_l;
 	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(rid);
+		ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.x - ofs.x);
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(rid);
+		ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top;
 		clip_l = MAX(0, p_pos.y - ofs.y);
 	}
 	return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color);
@@ -347,6 +355,8 @@ int TextLine::hit_test(float p_coords) const {
 
 TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 	rid = TS->create_shaped_text(p_direction, p_orientation);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
 }
 

+ 2 - 0
scene/resources/text_line.h

@@ -40,6 +40,8 @@ class TextLine : public Reference {
 	GDCLASS(TextLine, Reference);
 
 	RID rid;
+	int spacing_top = 0;
+	int spacing_bottom = 0;
 
 	bool dirty = true;
 

+ 36 - 21
scene/resources/text_paragraph.cpp

@@ -140,6 +140,8 @@ RID TextParagraph::get_line_rid(int p_line) const {
 }
 
 void TextParagraph::clear() {
+	spacing_top = 0;
+	spacing_bottom = 0;
 	for (int i = 0; i < lines.size(); i++) {
 		TS->free(lines[i]);
 	}
@@ -187,6 +189,8 @@ TextServer::Orientation TextParagraph::get_orientation() const {
 
 bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
 	bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	dirty_lines = true;
 	return res;
 }
@@ -260,7 +264,11 @@ float TextParagraph::get_width() const {
 
 Size2 TextParagraph::get_non_wraped_size() const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
-	return TS->shaped_text_get_size(rid);
+	if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+		return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
+	} else {
+		return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y);
+	}
 }
 
 Size2 TextParagraph::get_size() const {
@@ -270,9 +278,9 @@ Size2 TextParagraph::get_size() const {
 		Size2 lsize = TS->shaped_text_get_size(lines[i]);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			size.x = MAX(size.x, lsize.x);
-			size.y += lsize.y;
+			size.y += lsize.y + spacing_top + spacing_bottom;
 		} else {
-			size.x += lsize.x;
+			size.x += lsize.x + spacing_top + spacing_bottom;
 			size.y = MAX(size.y, lsize.y);
 		}
 	}
@@ -297,9 +305,9 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
 	for (int i = 0; i < p_line; i++) {
 		Size2 lsize = TS->shaped_text_get_size(lines[i]);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
-			xrect.position.y += lsize.y;
+			xrect.position.y += lsize.y + spacing_top + spacing_bottom;
 		} else {
-			xrect.position.x += lsize.x;
+			xrect.position.x += lsize.x + spacing_top + spacing_bottom;
 		}
 	}
 	return xrect;
@@ -308,7 +316,11 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
 Size2 TextParagraph::get_line_size(int p_line) const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Size2());
-	return TS->shaped_text_get_size(lines[p_line]);
+	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+		return Size2(TS->shaped_text_get_size(lines[p_line]).x, TS->shaped_text_get_size(lines[p_line]).y + spacing_top + spacing_bottom);
+	} else {
+		return Size2(TS->shaped_text_get_size(lines[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines[p_line]).y);
+	}
 }
 
 Vector2i TextParagraph::get_line_range(int p_line) const {
@@ -320,13 +332,13 @@ Vector2i TextParagraph::get_line_range(int p_line) const {
 float TextParagraph::get_line_ascent(int p_line) const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
-	return TS->shaped_text_get_ascent(lines[p_line]);
+	return TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 }
 
 float TextParagraph::get_line_descent(int p_line) const {
 	const_cast<TextParagraph *>(this)->_shape_lines();
 	ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
-	return TS->shaped_text_get_descent(lines[p_line]);
+	return TS->shaped_text_get_descent(lines[p_line]) + spacing_bottom;
 }
 
 float TextParagraph::get_line_width(int p_line) const {
@@ -353,10 +365,10 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
 	for (int i = 0; i < lines.size(); i++) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_ascent(lines[i]);
+			ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		} else {
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_ascent(lines[i]);
+			ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		}
 		float length = TS->shaped_text_get_width(lines[i]);
 		if (width > 0) {
@@ -389,10 +401,10 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
 		TS->shaped_text_draw(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_color);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_descent(lines[i]);
+			ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		} else {
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_descent(lines[i]);
+			ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		}
 	}
 }
@@ -403,10 +415,10 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
 	for (int i = 0; i < lines.size(); i++) {
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_ascent(lines[i]);
+			ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		} else {
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_ascent(lines[i]);
+			ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
 		}
 		float length = TS->shaped_text_get_width(lines[i]);
 		if (width > 0) {
@@ -439,10 +451,10 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
 		TS->shaped_text_draw_outline(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color);
 		if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
-			ofs.y += TS->shaped_text_get_descent(lines[i]);
+			ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		} else {
 			ofs.y = p_pos.y;
-			ofs.x += TS->shaped_text_get_descent(lines[i]);
+			ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
 		}
 	}
 }
@@ -462,10 +474,12 @@ int TextParagraph::hit_test(const Point2 &p_coords) const {
 			if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines[i]).y)) {
 				return TS->shaped_text_hit_test_position(lines[i], p_coords.x);
 			}
+			ofs.y += TS->shaped_text_get_size(lines[i]).y + spacing_bottom + spacing_top;
 		} else {
 			if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines[i]).x)) {
 				return TS->shaped_text_hit_test_position(lines[i], p_coords.y);
 			}
+			ofs.y += TS->shaped_text_get_size(lines[i]).x + spacing_bottom + spacing_top;
 		}
 	}
 	return TS->shaped_text_get_range(rid).y;
@@ -477,9 +491,9 @@ void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, co
 
 	Vector2 ofs = p_pos;
 	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	}
 	return TS->shaped_text_draw(lines[p_line], p_canvas, ofs, -1, -1, p_color);
 }
@@ -490,9 +504,9 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_
 
 	Vector2 ofs = p_pos;
 	if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
-		ofs.y += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	} else {
-		ofs.x += TS->shaped_text_get_ascent(lines[p_line]);
+		ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
 	}
 	return TS->shaped_text_draw_outline(lines[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color);
 }
@@ -500,8 +514,9 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_
 TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 	rid = TS->create_shaped_text(p_direction, p_orientation);
 	TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+	spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
+	spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
 	width = p_width;
-	dirty_lines = true;
 }
 
 TextParagraph::TextParagraph() {

+ 2 - 0
scene/resources/text_paragraph.h

@@ -41,6 +41,8 @@ class TextParagraph : public Reference {
 
 	RID rid;
 	Vector<RID> lines;
+	int spacing_top = 0;
+	int spacing_bottom = 0;
 
 	bool dirty_lines = true;
 

+ 91 - 53
servers/text_server.cpp

@@ -554,14 +554,18 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const
 	int line_start = MAX(p_start, range.x);
 	int last_safe_break = -1;
 	int chunk = 0;
-	for (int i = 0; i < logical.size(); i++) {
-		if (logical[i].start < p_start) {
+
+	int l_size = logical.size();
+	const Glyph *l_gl = logical.ptr();
+
+	for (int i = 0; i < l_size; i++) {
+		if (l_gl[i].start < p_start) {
 			continue;
 		}
-		if (logical[i].count > 0) {
-			if ((p_width[chunk] > 0) && (width + logical[i].advance > p_width[chunk]) && (last_safe_break >= 0)) {
-				lines.push_back(Vector2i(line_start, logical[last_safe_break].end));
-				line_start = logical[last_safe_break].end;
+		if (l_gl[i].count > 0) {
+			if ((p_width[chunk] > 0) && (width + l_gl[i].advance > p_width[chunk]) && (last_safe_break >= 0)) {
+				lines.push_back(Vector2i(line_start, l_gl[last_safe_break].end));
+				line_start = l_gl[last_safe_break].end;
 				i = last_safe_break;
 				last_safe_break = -1;
 				width = 0;
@@ -575,9 +579,9 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const
 				continue;
 			}
 			if ((p_break_flags & BREAK_MANDATORY) == BREAK_MANDATORY) {
-				if ((logical[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
-					lines.push_back(Vector2i(line_start, logical[i].end));
-					line_start = logical[i].end;
+				if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
+					lines.push_back(Vector2i(line_start, l_gl[i].end));
+					line_start = l_gl[i].end;
 					last_safe_break = -1;
 					width = 0;
 					chunk = 0;
@@ -588,7 +592,7 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const
 				}
 			}
 			if ((p_break_flags & BREAK_WORD_BOUND) == BREAK_WORD_BOUND) {
-				if ((logical[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
+				if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
 					last_safe_break = i;
 				}
 			}
@@ -596,10 +600,10 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const
 				last_safe_break = i;
 			}
 		}
-		width += logical[i].advance;
+		width += l_gl[i].advance;
 	}
 
-	if (logical.size() > 0) {
+	if (l_size > 0) {
 		lines.push_back(Vector2i(line_start, range.y));
 	} else {
 		lines.push_back(Vector2i(0, 0));
@@ -618,30 +622,34 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, float p_w
 	float width = 0.f;
 	int line_start = MAX(p_start, range.x);
 	int last_safe_break = -1;
-	for (int i = 0; i < logical.size(); i++) {
-		if (logical[i].start < p_start) {
+
+	int l_size = logical.size();
+	const Glyph *l_gl = logical.ptr();
+
+	for (int i = 0; i < l_size; i++) {
+		if (l_gl[i].start < p_start) {
 			continue;
 		}
-		if (logical[i].count > 0) {
-			if ((p_width > 0) && (width + logical[i].advance > p_width) && (last_safe_break >= 0)) {
-				lines.push_back(Vector2i(line_start, logical[last_safe_break].end));
-				line_start = logical[last_safe_break].end;
+		if (l_gl[i].count > 0) {
+			if ((p_width > 0) && (width + l_gl[i].advance > p_width) && (last_safe_break >= 0)) {
+				lines.push_back(Vector2i(line_start, l_gl[last_safe_break].end));
+				line_start = l_gl[last_safe_break].end;
 				i = last_safe_break;
 				last_safe_break = -1;
 				width = 0;
 				continue;
 			}
 			if ((p_break_flags & BREAK_MANDATORY) == BREAK_MANDATORY) {
-				if ((logical[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
-					lines.push_back(Vector2i(line_start, logical[i].end));
-					line_start = logical[i].end;
+				if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
+					lines.push_back(Vector2i(line_start, l_gl[i].end));
+					line_start = l_gl[i].end;
 					last_safe_break = -1;
 					width = 0;
 					continue;
 				}
 			}
 			if ((p_break_flags & BREAK_WORD_BOUND) == BREAK_WORD_BOUND) {
-				if ((logical[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
+				if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
 					last_safe_break = i;
 				}
 			}
@@ -649,10 +657,10 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, float p_w
 				last_safe_break = i;
 			}
 		}
-		width += logical[i].advance;
+		width += l_gl[i].advance;
 	}
 
-	if (logical.size() > 0) {
+	if (l_size > 0) {
 		if (lines.size() == 0 || lines[lines.size() - 1].y < range.y) {
 			lines.push_back(Vector2i(line_start, range.y));
 		}
@@ -671,15 +679,19 @@ Vector<Vector2i> TextServer::shaped_text_get_word_breaks(RID p_shaped) const {
 	const Vector2i &range = shaped_text_get_range(p_shaped);
 
 	int word_start = range.x;
-	for (int i = 0; i < logical.size(); i++) {
-		if (logical[i].count > 0) {
-			if ((logical[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
-				words.push_back(Vector2i(word_start, logical[i].end - 1));
-				word_start = logical[i].end;
+
+	int l_size = logical.size();
+	const Glyph *l_gl = logical.ptr();
+
+	for (int i = 0; i < l_size; i++) {
+		if (l_gl[i].count > 0) {
+			if ((l_gl[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+				words.push_back(Vector2i(word_start, l_gl[i].end - 1));
+				word_start = l_gl[i].end;
 			}
 		}
 	}
-	if (logical.size() > 0) {
+	if (l_size > 0) {
 		words.push_back(Vector2i(word_start, range.y));
 	}
 
@@ -688,7 +700,7 @@ Vector<Vector2i> TextServer::shaped_text_get_word_breaks(RID p_shaped) const {
 
 void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_leading_caret, Direction &p_leading_dir, Rect2 &p_trailing_caret, Direction &p_trailing_dir) const {
 	Vector<Rect2> carets;
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
 	TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
 	const Vector2 &range = shaped_text_get_range(p_shaped);
 	float ascent = shaped_text_get_ascent(p_shaped);
@@ -698,7 +710,11 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l
 	float off = 0.0f;
 	p_leading_dir = DIRECTION_AUTO;
 	p_trailing_dir = DIRECTION_AUTO;
-	for (int i = 0; i < glyphs.size(); i++) {
+
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
+
+	for (int i = 0; i < v_size; i++) {
 		if (glyphs[i].count > 0) {
 			// Caret before grapheme (top / left).
 			if (p_position == glyphs[i].start && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) {
@@ -831,7 +847,7 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l
 }
 
 TextServer::Direction TextServer::shaped_text_get_dominant_direciton_in_range(RID p_shaped, int p_start, int p_end) const {
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
 
 	if (p_start == p_end) {
 		return DIRECTION_AUTO;
@@ -843,7 +859,10 @@ TextServer::Direction TextServer::shaped_text_get_dominant_direciton_in_range(RI
 	int rtl = 0;
 	int ltr = 0;
 
-	for (int i = 0; i < glyphs.size(); i++) {
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
+
+	for (int i = 0; i < v_size; i++) {
 		if ((glyphs[i].end > start) && (glyphs[i].start < end)) {
 			if (glyphs[i].count > 0) {
 				if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
@@ -865,7 +884,7 @@ TextServer::Direction TextServer::shaped_text_get_dominant_direciton_in_range(RI
 
 Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const {
 	Vector<Vector2> ranges;
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
 
 	if (p_start == p_end) {
 		return ranges;
@@ -874,8 +893,11 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
 	int start = MIN(p_start, p_end);
 	int end = MAX(p_start, p_end);
 
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
+
 	float off = 0.0f;
-	for (int i = 0; i < glyphs.size(); i++) {
+	for (int i = 0; i < v_size; i++) {
 		for (int k = 0; k < glyphs[i].repeat; k++) {
 			if (glyphs[i].count > 0 && glyphs[i].index != 0) {
 				if (glyphs[i].start < end && glyphs[i].end > start) {
@@ -951,11 +973,15 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
 }
 
 int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) const {
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
 
 	// Exact grapheme hit test, return -1 if missed.
 	float off = 0.0f;
-	for (int i = 0; i < glyphs.size(); i++) {
+
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
+
+	for (int i = 0; i < v_size; i++) {
 		for (int j = 0; j < glyphs[i].repeat; j++) {
 			if (p_coords >= off && p_coords < off + glyphs[i].advance) {
 				return i;
@@ -967,13 +993,16 @@ int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, float p_coords) cons
 }
 
 int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) const {
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
+
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
 
 	// Cursor placement hit test.
 
 	// Place caret to the left of the leftmost grapheme, or to position 0 if string is empty.
 	if (p_coords <= 0) {
-		if (glyphs.size() > 0) {
+		if (v_size > 0) {
 			if ((glyphs[0].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
 				return glyphs[0].end;
 			} else {
@@ -986,11 +1015,11 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) cons
 
 	// Place caret to the right of the rightmost grapheme, or to position 0 if string is empty.
 	if (p_coords >= shaped_text_get_width(p_shaped)) {
-		if (glyphs.size() > 0) {
-			if ((glyphs[glyphs.size() - 1].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
-				return glyphs[glyphs.size() - 1].start;
+		if (v_size > 0) {
+			if ((glyphs[v_size - 1].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
+				return glyphs[v_size - 1].start;
 			} else {
-				return glyphs[glyphs.size() - 1].end;
+				return glyphs[v_size - 1].end;
 			}
 		} else {
 			return 0;
@@ -998,7 +1027,7 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) cons
 	}
 
 	float off = 0.0f;
-	for (int i = 0; i < glyphs.size(); i++) {
+	for (int i = 0; i < v_size; i++) {
 		for (int k = 0; k < glyphs[i].repeat; k++) {
 			if (glyphs[i].count > 0) {
 				float advance = 0.f;
@@ -1029,8 +1058,10 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) cons
 }
 
 int TextServer::shaped_text_next_grapheme_pos(RID p_shaped, int p_pos) {
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
-	for (int i = 0; i < glyphs.size(); i++) {
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
+	for (int i = 0; i < v_size; i++) {
 		if (p_pos >= glyphs[i].start && p_pos < glyphs[i].end) {
 			return glyphs[i].end;
 		}
@@ -1039,8 +1070,10 @@ int TextServer::shaped_text_next_grapheme_pos(RID p_shaped, int p_pos) {
 }
 
 int TextServer::shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos) {
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
-	for (int i = 0; i < glyphs.size(); i++) {
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
+	for (int i = 0; i < v_size; i++) {
 		if (p_pos > glyphs[i].start && p_pos <= glyphs[i].end) {
 			return glyphs[i].start;
 		}
@@ -1050,13 +1083,16 @@ int TextServer::shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos) {
 }
 
 void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, const Color &p_color) const {
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
 	TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
 	bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped);
 
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
+
 	Vector2 ofs = p_pos;
 	// Draw at the baseline.
-	for (int i = 0; i < glyphs.size(); i++) {
+	for (int i = 0; i < v_size; i++) {
 		for (int j = 0; j < glyphs[i].repeat; j++) {
 			if (p_clip_r > 0) {
 				// Clip right / bottom.
@@ -1099,12 +1135,14 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p
 }
 
 void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, int p_outline_size, const Color &p_color) const {
-	const Vector<TextServer::Glyph> glyphs = shaped_text_get_glyphs(p_shaped);
+	const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped);
 	TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
 
+	int v_size = visual.size();
+	const Glyph *glyphs = visual.ptr();
 	Vector2 ofs = p_pos;
 	// Draw at the baseline.
-	for (int i = 0; i < glyphs.size(); i++) {
+	for (int i = 0; i < v_size; i++) {
 		for (int j = 0; j < glyphs[i].repeat; j++) {
 			if (p_clip_r > 0) {
 				// Clip right / bottom.