diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp index f8d2450bb39..b03231b2395 100644 --- a/modules/text_server_adv/script_iterator.cpp +++ b/modules/text_server_adv/script_iterator.cpp @@ -61,7 +61,8 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length } int paren_size = PAREN_STACK_DEPTH; - ParenStackEntry *paren_stack = static_cast(memalloc(paren_size * sizeof(ParenStackEntry))); + ParenStackEntry starter_stack[PAREN_STACK_DEPTH]; + ParenStackEntry *paren_stack = starter_stack; int script_start; int script_end = p_start; @@ -78,7 +79,9 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length UChar32 n = (script_end + 1 < p_length) ? str[script_end + 1] : 0; UScriptCode sc = uscript_getScript(ch, &err); if (U_FAILURE(err)) { - memfree(paren_stack); + if (paren_stack != starter_stack) { + memfree(paren_stack); + } ERR_FAIL_MSG(u_errorName(err)); } if (is_emoji(ch, n)) { @@ -92,7 +95,11 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length if (unlikely(paren_sp >= paren_size)) { // If the stack is full, allocate more space to handle deeply nested parentheses. This is unlikely to happen with any real text. paren_size += PAREN_STACK_DEPTH; - paren_stack = static_cast(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry))); + if (paren_stack == starter_stack) { + paren_stack = static_cast(memalloc(paren_size * sizeof(ParenStackEntry))); + } else { + paren_stack = static_cast(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry))); + } } paren_stack[paren_sp].pair_index = ch; paren_stack[paren_sp].script_code = script_code; @@ -144,5 +151,7 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length script_ranges.push_back(rng); } while (script_end < p_length); - memfree(paren_stack); + if (paren_stack != starter_stack) { + memfree(paren_stack); + } } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 7d6ea54e332..19901725f6c 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -4275,7 +4275,13 @@ bool TextServerAdvanced::_font_is_script_supported(const RID &p_font_rid, const Vector2i size = _get_size(fd, 16); FontForSizeAdvanced *ffsd = nullptr; ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), false); - return fd->supported_scripts.has(hb_tag_from_string(p_script.ascii().get_data(), -1)); + char ascii_script[] = { ' ', ' ', ' ', ' ' }; + for (int i = 0; i < MIN(4, p_script.size()); i++) { + if (p_script[i] <= 0x7f) { + ascii_script[i] = p_script[i]; + } + } + return fd->supported_scripts.has(hb_tag_from_string(ascii_script, -1)); } } @@ -5294,6 +5300,14 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S int32_t bidi_run_start = _convert_pos(p_sd, ov_start + start + _bidi_run_start); int32_t bidi_run_end = _convert_pos(p_sd, ov_start + start + _bidi_run_start + _bidi_run_length); + bool cache_valid = false; + int cached_font_size = -1; + RID cached_font_rid = RID(); + double cached_font_ascent = 0; + double cached_font_descent = 0; + double cached_font_top_spacing = 0; + double cached_font_bottom_spacing = 0; + p_new_sd->glyphs.reserve(p_new_sd->glyphs.size() + MIN(sd_size, bidi_run_end - bidi_run_start)); for (int j = 0; j < sd_size; j++) { int col_key_off = (sd_glyphs[j].start == sd_glyphs[j].end) ? 1 : 0; if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end - col_key_off)) { @@ -5346,20 +5360,32 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S } else { if (gl.font_rid.is_valid()) { if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) { - p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP), -gl.y_off)); - p_new_sd->descent = MAX(p_new_sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM), gl.y_off)); + if (!cache_valid || cached_font_rid != gl.font_rid || cached_font_size != gl.font_size) { + cache_valid = true; + cached_font_rid = gl.font_rid; + cached_font_size = gl.font_size; + cached_font_ascent = _font_get_ascent(gl.font_rid, gl.font_size); + cached_font_descent = _font_get_descent(gl.font_rid, gl.font_size); + cached_font_top_spacing = _font_get_spacing(gl.font_rid, SPACING_TOP); + cached_font_bottom_spacing = _font_get_spacing(gl.font_rid, SPACING_BOTTOM); + } + p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(cached_font_ascent + cached_font_top_spacing, -gl.y_off)); + p_new_sd->descent = MAX(p_new_sd->descent, MAX(cached_font_descent + cached_font_bottom_spacing, gl.y_off)); } else { - p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); - p_new_sd->descent = MAX(p_new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + double glyph_advance = Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5); + p_new_sd->ascent = MAX(p_new_sd->ascent, glyph_advance); + p_new_sd->descent = MAX(p_new_sd->descent, glyph_advance); } } else if (p_new_sd->preserve_invalid || (p_new_sd->preserve_control && is_control(ch[gl.start - p_sd->start]))) { // Glyph not found, replace with hex code box. if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) { - p_new_sd->ascent = MAX(p_new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85); - p_new_sd->descent = MAX(p_new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15); + double box_size = get_hex_code_box_size(gl.font_size, gl.index).y; + p_new_sd->ascent = MAX(p_new_sd->ascent, box_size * 0.85); + p_new_sd->descent = MAX(p_new_sd->descent, box_size * 0.15); } else { - p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5)); - p_new_sd->descent = MAX(p_new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5)); + double box_size = Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5); + p_new_sd->ascent = MAX(p_new_sd->ascent, box_size); + p_new_sd->descent = MAX(p_new_sd->descent, box_size); } } p_new_sd->width += gl.advance * gl.repeat; @@ -6649,10 +6675,9 @@ UBreakIterator *TextServerAdvanced::_create_line_break_iterator_for_locale(const return ubrk_clone(bi, r_err); } -void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font) { +void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font) { RID f; int fs = p_sd->spans[p_span].font_size; - if (p_fb_index >= 0 && p_fb_index < p_fonts.size()) { // Try font from list. f = p_fonts[p_fb_index]; @@ -6697,7 +6722,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star } bool found = false; - for (int j = 0; j <= p_fonts.size(); j++) { + for (uint32_t j = 0; j <= p_fonts.size(); j++) { RID f_rid; if (j == p_fonts.size()) { f_rid = p_prev_font; @@ -6848,6 +6873,11 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star } } + bool cache_valid = false; + RID cached_font_rid = RID(); + int cached_font_size = 0; + float cached_offset = 0; + double adv_rem = 0.0; for (unsigned int i = 0; i < glyph_count; i++) { if ((i > 0) && (last_cluster_id != glyph_info[i].cluster)) { @@ -6933,10 +6963,16 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star adv_rem = full_adv + gl.advance; } } + if (!cache_valid || cached_font_rid != gl.font_rid || cached_font_size != gl.font_size) { + cache_valid = true; + cached_font_size = gl.font_size; + cached_font_rid = gl.font_rid; + cached_offset = _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size)); + } if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.y_off += _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size)); + gl.y_off += cached_offset; } else { - gl.x_off += _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size)); + gl.x_off += cached_offset; } } if ((!last_run || i < last_non_zero_w) && !Math::is_zero_approx(gl.advance)) { @@ -6978,6 +7014,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star failed_subrun_start = p_end + 1; failed_subrun_end = p_start; } + p_sd->glyphs.reserve(p_sd->glyphs.size() + w[i].count); for (int j = 0; j < w[i].count; j++) { if (p_sd->orientation == ORIENTATION_HORIZONTAL) { p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off); @@ -7222,27 +7259,8 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { } sd->glyphs.push_back(gl); } else { - Array fonts; - Array fonts_scr_only; - Array fonts_no_match; - int font_count = span.fonts.size(); - if (font_count > 0) { - fonts.push_back(sd->spans[k].fonts[0]); - } - for (int l = 1; l < font_count; l++) { - if (_font_is_script_supported(span.fonts[l], script_code)) { - if (_font_is_language_supported(span.fonts[l], span.language)) { - fonts.push_back(sd->spans[k].fonts[l]); - } else { - fonts_scr_only.push_back(sd->spans[k].fonts[l]); - } - } else { - fonts_no_match.push_back(sd->spans[k].fonts[l]); - } - } - fonts.append_array(fonts_scr_only); - fonts.append_array(fonts_no_match); - _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0, RID()); + FontPriorityList fonts(this, span.fonts, span.language, script_code); + _shape_run(sd, MAX(span.start - sd->start, script_run_start), MIN(span.end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0, RID()); } } } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 2f09a7cfb2a..54aa1849280 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -705,7 +705,73 @@ class TextServerAdvanced : public TextServerExtension { int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const; int64_t _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const; bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_length) const; - void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font); + + struct FontPriorityList { + friend class TextServerAdvanced; + + const int MAX_PRIORITY = 2; + int current_priority = 0; + uint32_t current_index = 0; + uint32_t font_count = 0; + const String *language; + const String *script_code; + LocalVector> unprocessed_fonts; + LocalVector fonts; + const TextServerAdvanced *text_server; + + FontPriorityList(const TextServerAdvanced *p_text_server, const Array &p_fonts, const String &p_language, const String &p_script_code) { + text_server = p_text_server; + language = &p_language; + script_code = &p_script_code; + font_count = p_fonts.size(); + + unprocessed_fonts.reserve(font_count); + for (uint32_t i = 0; i < font_count; i++) { + unprocessed_fonts.push_back(Pair(p_fonts[i], -1)); + } + + fonts.reserve(font_count); + if (font_count > 0) { + fonts.push_back(p_fonts[0]); + unprocessed_fonts[0].second = 0; + current_index++; + } + } + + _FORCE_INLINE_ uint32_t size() const { + return font_count; + } + + _FORCE_INLINE_ int _get_priority(const RID &font) { + return text_server->_font_is_script_supported(font, *script_code) ? (text_server->_font_is_language_supported(font, *language) ? 0 : 1) : 2; + } + + RID operator[](uint32_t index) { + if (index < fonts.size()) { + return fonts[index]; + } + while (current_priority < MAX_PRIORITY || current_index < font_count) { + if (current_index >= font_count) { + current_priority++; + current_index = 0; + } + const RID &font = unprocessed_fonts[current_index].first; + int &priority = unprocessed_fonts[current_index].second; + if (priority < 0) { + priority = _get_priority(font); + } + if (priority == current_priority) { + fonts.push_back(font); + if (index < fonts.size()) { + return fonts[index]; + } + } + current_index++; + } + return RID(); + } + }; + void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font); Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size); _FORCE_INLINE_ RID _find_sys_font_for_text(const RID &p_fdef, const String &p_script_code, const String &p_language, const String &p_text);