[TextServer] Improve embedded objects handling performance.

This commit is contained in:
bruvzg 2024-09-26 09:37:47 +03:00
parent b2b13d46c2
commit df69cd6759
No known key found for this signature in database
GPG Key ID: 7960FCF39844EC38
15 changed files with 242 additions and 89 deletions

View File

@ -1181,6 +1181,14 @@
Returns number of text spans added using [method shaped_text_add_string] or [method shaped_text_add_object].
</description>
</method>
<method name="shaped_get_span_embedded_object" qualifiers="const">
<return type="Variant" />
<param index="0" name="shaped" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns text embedded object key.
</description>
</method>
<method name="shaped_get_span_meta" qualifiers="const">
<return type="Variant" />
<param index="0" name="shaped" type="RID" />

View File

@ -1290,6 +1290,15 @@
Returns number of text spans added using [method _shaped_text_add_string] or [method _shaped_text_add_object].
</description>
</method>
<method name="_shaped_get_span_embedded_object" qualifiers="virtual const">
<return type="Variant" />
<param index="0" name="shaped" type="RID" />
<param index="1" name="index" type="int" />
<description>
[b]Required.[/b]
Returns text embedded object key.
</description>
</method>
<method name="_shaped_get_span_meta" qualifiers="virtual const">
<return type="Variant" />
<param index="0" name="shaped" type="RID" />

View File

@ -369,7 +369,7 @@ void EditorSpinSlider::_draw_spin_slider() {
}
if (glyphs[i].font_rid != RID()) {
TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
} else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
} else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
TS->draw_hex_code_box(ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color);
}
}

View File

@ -4165,15 +4165,14 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
}
}
for (int i = 0; i < parent->spans.size(); i++) {
for (int i = p_shaped->first_span; i <= p_shaped->last_span; i++) {
ShapedTextDataAdvanced::Span span = parent->spans[i];
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
continue;
}
span.start = MAX(p_shaped->start, span.start);
span.end = MIN(p_shaped->end, span.end);
p_shaped->spans.push_back(span);
}
p_shaped->first_span = 0;
p_shaped->last_span = 0;
p_shaped->parent = RID();
}
@ -4199,6 +4198,8 @@ void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) {
sd->end = 0;
sd->text = String();
sd->spans.clear();
sd->first_span = 0;
sd->last_span = 0;
sd->objects.clear();
sd->bidi_override.clear();
invalidate(sd, true);
@ -4383,19 +4384,48 @@ TextServer::Orientation TextServerAdvanced::_shaped_text_get_orientation(const R
int64_t TextServerAdvanced::_shaped_get_span_count(const RID &p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, 0);
if (sd->parent != RID()) {
return sd->last_span - sd->first_span + 1;
} else {
return sd->spans.size();
}
}
Variant TextServerAdvanced::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, Variant());
if (sd->parent != RID()) {
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());
ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());
return parent_sd->spans[p_index + sd->first_span].meta;
} else {
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
return sd->spans[p_index].meta;
}
}
Variant TextServerAdvanced::_shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, Variant());
if (sd->parent != RID()) {
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());
ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());
return parent_sd->spans[p_index + sd->first_span].embedded_key;
} else {
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
return sd->spans[p_index].embedded_key;
}
}
void TextServerAdvanced::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL(sd);
if (sd->parent != RID()) {
full_copy(sd);
}
ERR_FAIL_INDEX(p_index, sd->spans.size());
ShapedTextDataAdvanced::Span &span = sd->spans.ptrw()[p_index];
@ -4489,18 +4519,22 @@ bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const V
sd->width = 0;
sd->upos = 0;
sd->uthk = 0;
Vector<ShapedTextDataAdvanced::Span> &spans = sd->spans;
if (sd->parent != RID()) {
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND_V(!parent_sd->valid.is_set(), false);
spans = parent_sd->spans;
}
int sd_size = sd->glyphs.size();
int span_size = spans.size();
for (int i = 0; i < sd_size; i++) {
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
if (E.value.start == gl.start) {
key = E.key;
break;
}
}
if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index + sd->first_span >= 0 && gl.span_index + sd->first_span < span_size) {
key = spans[gl.span_index + sd->first_span].embedded_key;
}
if (key != Variant()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@ -4671,6 +4705,20 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
p_new_sd->utf16 = p_new_sd->text.utf16();
p_new_sd->script_iter = memnew(ScriptIterator(p_new_sd->text, 0, p_new_sd->text.length()));
int span_size = p_sd->spans.size();
p_new_sd->first_span = 0;
p_new_sd->last_span = span_size - 1;
for (int i = 0; i < span_size; i++) {
const ShapedTextDataAdvanced::Span &span = p_sd->spans[i];
if (span.end <= p_start) {
p_new_sd->first_span = i + 1;
} else if (span.start >= p_start + p_length) {
p_new_sd->last_span = i - 1;
break;
}
}
int sd_size = p_sd->glyphs.size();
const Glyph *sd_glyphs = p_sd->glyphs.ptr();
for (int ov = 0; ov < p_sd->bidi_override.size(); ov++) {
@ -4749,31 +4797,27 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) {
// Copy glyphs.
Glyph gl = sd_glyphs[j];
if (gl.span_index >= 0) {
gl.span_index -= p_new_sd->first_span;
}
if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) {
uint32_t index = font_get_glyph_index(gl.font_rid, gl.font_size, 0x00ad, 0);
float w = font_get_glyph_advance(gl.font_rid, gl.font_size, index)[(p_new_sd->orientation == ORIENTATION_HORIZONTAL) ? 0 : 1];
gl.index = index;
gl.advance = w;
}
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
if (E.value.start == gl.start) {
find_embedded = true;
key = E.key;
p_new_sd->objects[key] = E.value;
break;
}
}
}
if (find_embedded) {
if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index >= 0 && gl.span_index < span_size) {
Variant key = p_sd->spans[gl.span_index].embedded_key;
if (key != Variant()) {
ShapedTextDataAdvanced::EmbeddedObject obj = p_sd->objects[key];
if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
p_new_sd->objects[key].rect.position.x = p_new_sd->width;
p_new_sd->width += p_new_sd->objects[key].rect.size.x;
obj.rect.position.x = p_new_sd->width;
p_new_sd->width += obj.rect.size.x;
} else {
p_new_sd->objects[key].rect.position.y = p_new_sd->width;
p_new_sd->width += p_new_sd->objects[key].rect.size.y;
obj.rect.position.y = p_new_sd->width;
p_new_sd->width += obj.rect.size.y;
}
p_new_sd->objects[key] = obj;
}
} else {
if (gl.font_rid.is_valid()) {
@ -5226,7 +5270,8 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
spans = parent_sd->spans;
}
if (spans.size() == 0) {
int span_size = spans.size();
if (span_size == 0) {
return;
}
@ -5237,7 +5282,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
const Array &fonts = spans[spans.size() - 1].fonts;
const Array &fonts = spans[span_size - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
if (_font_has_char(fonts[i], sd->el_char)) {
dot_gl_font_rid = fonts[i];
@ -5247,7 +5292,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
const char32_t u32str[] = { sd->el_char, 0 };
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, u32str);
if (rid.is_valid()) {
dot_gl_font_rid = rid;
found_el_char = true;
@ -5260,7 +5305,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
bool found_dot_char = false;
dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
if (!_font_has_char(dot_gl_font_rid, '.')) {
const Array &fonts = spans[spans.size() - 1].fonts;
const Array &fonts = spans[span_size - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
if (_font_has_char(fonts[i], '.')) {
dot_gl_font_rid = fonts[i];
@ -5269,7 +5314,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
}
if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, ".");
if (rid.is_valid()) {
dot_gl_font_rid = rid;
}
@ -5278,7 +5323,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
if (!_font_has_char(whitespace_gl_font_rid, ' ')) {
const Array &fonts = spans[spans.size() - 1].fonts;
const Array &fonts = spans[span_size - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
if (_font_has_char(fonts[i], ' ')) {
whitespace_gl_font_rid = fonts[i];
@ -5437,7 +5482,8 @@ void TextServerAdvanced::_update_chars(ShapedTextDataAdvanced *p_sd) const {
spans = parent_sd->spans;
}
while (i < spans.size()) {
int span_size = spans.size();
while (i < span_size) {
if (spans[i].start > p_sd->end) {
break;
}
@ -5448,7 +5494,7 @@ void TextServerAdvanced::_update_chars(ShapedTextDataAdvanced *p_sd) const {
int r_start = MAX(0, spans[i].start - p_sd->start);
String language = spans[i].language;
while (i + 1 < spans.size() && language == spans[i + 1].language) {
while (i + 1 < span_size && language == spans[i + 1].language) {
i++;
}
int r_end = MIN(spans[i].end - p_sd->start, p_sd->text.length());
@ -5510,10 +5556,11 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
sd->break_inserts = 0;
UErrorCode err = U_ZERO_ERROR;
int i = 0;
while (i < sd->spans.size()) {
int span_size = sd->spans.size();
while (i < span_size) {
String language = sd->spans[i].language;
int r_start = sd->spans[i].start;
while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) {
while (i + 1 < span_size && language == sd->spans[i + 1].language) {
i++;
}
int r_end = sd->spans[i].end;
@ -5637,6 +5684,7 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
}
}
Glyph gl;
gl.span_index = sd_glyphs[i].span_index;
gl.start = sd_glyphs[i].start;
gl.end = sd_glyphs[i].end;
gl.count = 1;
@ -5885,6 +5933,7 @@ bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shap
}
// Inject virtual space for alignment.
Glyph gl;
gl.span_index = sd_glyphs[i].span_index;
gl.start = sd_glyphs[i].start;
gl.end = sd_glyphs[i].end;
gl.count = 1;
@ -6031,6 +6080,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
for (int i = fb_from; i != fb_to; i += fb_delta) {
if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
Glyph gl;
gl.span_index = p_span;
gl.start = i + p_sd->start;
gl.end = i + 1 + p_sd->start;
gl.count = 1;
@ -6187,6 +6237,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
Glyph &gl = w[i];
gl = Glyph();
gl.span_index = p_span;
gl.start = glyph_info[i].cluster;
gl.end = end;
gl.count = 0;
@ -6489,6 +6540,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
gl.start = span.start;
gl.end = span.end;
gl.count = 1;
gl.span_index = k;
gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_EMBEDDED_OBJECT;
if (sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = sd->objects[span.embedded_key].rect.size.x;

View File

@ -479,6 +479,8 @@ class TextServerAdvanced : public TextServerExtension {
Variant meta;
};
Vector<Span> spans;
int first_span = 0; // First span in the parent ShapedTextData.
int last_span = 0;
struct EmbeddedObject {
int start = -1;
@ -941,6 +943,7 @@ public:
MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t);
MODBIND2RC(Variant, shaped_get_span_embedded_object, const RID &, int64_t);
MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t);

View File

@ -3045,15 +3045,14 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
}
}
for (int k = 0; k < parent->spans.size(); k++) {
ShapedTextDataFallback::Span span = parent->spans[k];
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
continue;
}
for (int i = p_shaped->first_span; i <= p_shaped->last_span; i++) {
ShapedTextDataFallback::Span span = parent->spans[i];
span.start = MAX(p_shaped->start, span.start);
span.end = MIN(p_shaped->end, span.end);
p_shaped->spans.push_back(span);
}
p_shaped->first_span = 0;
p_shaped->last_span = 0;
p_shaped->parent = RID();
}
@ -3079,6 +3078,8 @@ void TextServerFallback::_shaped_text_clear(const RID &p_shaped) {
sd->end = 0;
sd->text = String();
sd->spans.clear();
sd->first_span = 0;
sd->last_span = 0;
sd->objects.clear();
invalidate(sd);
}
@ -3231,19 +3232,48 @@ int64_t TextServerFallback::_shaped_text_get_spacing(const RID &p_shaped, Spacin
int64_t TextServerFallback::_shaped_get_span_count(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, 0);
if (sd->parent != RID()) {
return sd->last_span - sd->first_span + 1;
} else {
return sd->spans.size();
}
}
Variant TextServerFallback::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, Variant());
if (sd->parent != RID()) {
ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());
ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());
return parent_sd->spans[p_index + sd->first_span].meta;
} else {
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
return sd->spans[p_index].meta;
}
}
Variant TextServerFallback::_shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, Variant());
if (sd->parent != RID()) {
ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant());
ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant());
return parent_sd->spans[p_index + sd->first_span].embedded_key;
} else {
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
return sd->spans[p_index].embedded_key;
}
}
void TextServerFallback::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL(sd);
if (sd->parent != RID()) {
full_copy(sd);
}
ERR_FAIL_INDEX(p_index, sd->spans.size());
ShapedTextDataFallback::Span &span = sd->spans.ptrw()[p_index];
@ -3365,18 +3395,22 @@ bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const V
sd->width = 0;
sd->upos = 0;
sd->uthk = 0;
Vector<ShapedTextDataFallback::Span> &spans = sd->spans;
if (sd->parent != RID()) {
ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND_V(!parent_sd->valid.is_set(), false);
spans = parent_sd->spans;
}
int sd_size = sd->glyphs.size();
int span_size = spans.size();
for (int i = 0; i < sd_size; i++) {
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (E.value.start == gl.start) {
key = E.key;
break;
}
}
if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index + sd->first_span >= 0 && gl.span_index + sd->first_span < span_size) {
key = spans[gl.span_index + sd->first_span].embedded_key;
}
if (key != Variant()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@ -3528,35 +3562,46 @@ RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start
if (p_length > 0) {
new_sd->text = sd->text.substr(p_start - sd->start, p_length);
int span_size = sd->spans.size();
new_sd->first_span = 0;
new_sd->last_span = span_size - 1;
for (int i = 0; i < span_size; i++) {
const ShapedTextDataFallback::Span &span = sd->spans[i];
if (span.end <= p_start) {
new_sd->first_span = i + 1;
} else if (span.start >= p_start + p_length) {
new_sd->last_span = i - 1;
break;
}
}
int sd_size = sd->glyphs.size();
const Glyph *sd_glyphs = sd->glyphs.ptr();
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];
if (gl.span_index >= 0) {
gl.span_index -= new_sd->first_span;
}
if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) {
gl.index = 0x00ad;
gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, 0x00ad).x;
}
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (E.value.start == gl.start) {
find_embedded = true;
key = E.key;
new_sd->objects[key] = E.value;
break;
}
}
}
if (find_embedded) {
if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index >= 0 && gl.span_index < span_size) {
Variant key = sd->spans[gl.span_index].embedded_key;
if (key != Variant()) {
ShapedTextDataFallback::EmbeddedObject obj = sd->objects[key];
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;
obj.rect.position.x = new_sd->width;
new_sd->width += obj.rect.size.x;
} else {
new_sd->objects[key].rect.position.y = new_sd->width;
new_sd->width += new_sd->objects[key].rect.size.y;
obj.rect.position.y = new_sd->width;
new_sd->width += obj.rect.size.y;
}
new_sd->objects[key] = obj;
}
} else {
if (gl.font_rid.is_valid()) {
@ -4041,7 +4086,8 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
spans = parent_sd->spans;
}
if (spans.size() == 0) {
int span_size = spans.size();
if (span_size == 0) {
return;
}
@ -4052,7 +4098,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
const Array &fonts = spans[spans.size() - 1].fonts;
const Array &fonts = spans[span_size - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
if (_font_has_char(fonts[i], sd->el_char)) {
dot_gl_font_rid = fonts[i];
@ -4062,7 +4108,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
const char32_t u32str[] = { sd->el_char, 0 };
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, u32str);
if (rid.is_valid()) {
dot_gl_font_rid = rid;
found_el_char = true;
@ -4075,7 +4121,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
bool found_dot_char = false;
dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
if (!_font_has_char(dot_gl_font_rid, '.')) {
const Array &fonts = spans[spans.size() - 1].fonts;
const Array &fonts = spans[span_size - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
if (_font_has_char(fonts[i], '.')) {
dot_gl_font_rid = fonts[i];
@ -4084,7 +4130,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
}
if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, ".");
if (rid.is_valid()) {
dot_gl_font_rid = rid;
}
@ -4093,7 +4139,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
if (!_font_has_char(whitespace_gl_font_rid, ' ')) {
const Array &fonts = spans[spans.size() - 1].fonts;
const Array &fonts = spans[span_size - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
if (_font_has_char(fonts[i], ' ')) {
whitespace_gl_font_rid = fonts[i];
@ -4265,6 +4311,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
sd->width += sd->objects[span.embedded_key].rect.size.y;
}
Glyph gl;
gl.span_index = i;
gl.start = span.start;
gl.end = span.end;
gl.count = 1;
@ -4281,6 +4328,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
RID prev_font;
for (int j = span.start; j < span.end; j++) {
Glyph gl;
gl.span_index = i;
gl.start = j;
gl.end = j + 1;
gl.count = 1;

View File

@ -425,6 +425,8 @@ class TextServerFallback : public TextServerExtension {
Variant meta;
};
Vector<Span> spans;
int first_span = 0; // First span in the parent ShapedTextData.
int last_span = 0;
struct EmbeddedObject {
int start = -1;
@ -809,6 +811,7 @@ public:
MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t);
MODBIND2RC(Variant, shaped_get_span_embedded_object, const RID &, int64_t);
MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t);

View File

@ -344,7 +344,7 @@ void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset,
gl_uv = TS->font_get_glyph_uv_rect(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index);
texs = TS->font_get_glyph_texture_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index);
}
} else {
} else if (((p_glyph.flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((p_glyph.flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
gl_sz = TS->get_hex_code_box_size(p_glyph.font_size, p_glyph.index) * pixel_size;
gl_of = Vector2(0, -gl_sz.y);
}

View File

@ -311,7 +311,7 @@ void Label::_update_visible() {
inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) {
if (p_gl.font_rid != RID()) {
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color);
} else {
} else if (((p_gl.flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((p_gl.flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
TS->draw_hex_code_box(p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color);
}
}

View File

@ -549,7 +549,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (font_size_it && font_size_it->font_size > 0) {
font_size = font_size_it->font_size;
}
l.text_buf->add_string(String::chr(0x200B), font, font_size);
l.text_buf->add_string(String::chr(0x200B), font, font_size, String(), it->rid);
txt += "\n";
l.char_count++;
remaining_characters--;
@ -791,7 +791,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
MutexLock lock(l.text_buf->get_mutex());
Item *it_from = l.from;
Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
if (it_from == nullptr) {
return 0;
@ -1030,9 +1029,26 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
float box_start = 0.0;
Color last_color = Color(0, 0, 0, 0);
Item *it = it_from;
int span = -1;
for (int i = 0; i < gl_size; i++) {
bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
if (glyphs[i].span_index != span) {
span = glyphs[i].span_index;
if (span >= 0) {
if ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) == TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) {
Item *new_it = items.get_or_null(TS->shaped_get_span_embedded_object(rid, span));
if (new_it) {
it = new_it;
}
} else {
Item *new_it = items.get_or_null(TS->shaped_get_span_meta(rid, span));
if (new_it) {
it = new_it;
}
}
}
}
Color font_color = (step == DRAW_STEP_SHADOW || step == DRAW_STEP_OUTLINE || step == DRAW_STEP_TEXT) ? _find_color(it, p_base_color) : Color();
int outline_size = (step == DRAW_STEP_OUTLINE) ? _find_outline_size(it, p_outline_size) : 0;

View File

@ -99,6 +99,7 @@ public:
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) override { return false; }
virtual int64_t shaped_get_span_count(const RID &p_shaped) const override { return 0; }
virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override { return Variant(); }
virtual Variant shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const override { return Variant(); }
virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) override {}
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override { return RID(); }
virtual RID shaped_text_get_parent(const RID &p_shaped) const override { return RID(); }

View File

@ -264,6 +264,7 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_shaped_get_span_count, "shaped");
GDVIRTUAL_BIND(_shaped_get_span_meta, "shaped", "index");
GDVIRTUAL_BIND(_shaped_get_span_embedded_object, "shaped", "index");
GDVIRTUAL_BIND(_shaped_set_span_update_font, "shaped", "index", "fonts", "size", "opentype_features");
GDVIRTUAL_BIND(_shaped_text_substr, "shaped", "start", "length");
@ -1167,11 +1168,17 @@ int64_t TextServerExtension::shaped_get_span_count(const RID &p_shaped) const {
}
Variant TextServerExtension::shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
Variant ret = false;
Variant ret;
GDVIRTUAL_CALL(_shaped_get_span_meta, p_shaped, p_index, ret);
return ret;
}
Variant TextServerExtension::shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const {
Variant ret;
GDVIRTUAL_CALL(_shaped_get_span_embedded_object, p_shaped, p_index, ret);
return ret;
}
void TextServerExtension::shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
GDVIRTUAL_CALL(_shaped_set_span_update_font, p_shaped, p_index, p_fonts, p_size, p_opentype_features);
}

View File

@ -437,9 +437,11 @@ public:
virtual int64_t shaped_get_span_count(const RID &p_shaped) const override;
virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override;
virtual Variant shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const override;
virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) override;
GDVIRTUAL1RC_REQUIRED(int64_t, _shaped_get_span_count, RID);
GDVIRTUAL2RC_REQUIRED(Variant, _shaped_get_span_meta, RID, int64_t);
GDVIRTUAL2RC_REQUIRED(Variant, _shaped_get_span_embedded_object, RID, int64_t);
GDVIRTUAL5_REQUIRED(_shaped_set_span_update_font, RID, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override;

View File

@ -420,6 +420,7 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count);
ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta);
ClassDB::bind_method(D_METHOD("shaped_get_span_embedded_object", "shaped", "index"), &TextServer::shaped_get_span_embedded_object);
ClassDB::bind_method(D_METHOD("shaped_set_span_update_font", "shaped", "index", "fonts", "size", "opentype_features"), &TextServer::shaped_set_span_update_font, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr);

View File

@ -464,6 +464,7 @@ public:
virtual int64_t shaped_get_span_count(const RID &p_shaped) const = 0;
virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const = 0;
virtual Variant shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const = 0;
virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) = 0;
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range.
@ -578,9 +579,11 @@ struct Glyph {
float advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical).
RID font_rid; // Font resource.
int font_size = 0; // Font size;
int font_size = 0; // Font size.
int32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs).
int span_index = -1;
bool operator==(const Glyph &p_a) const;
bool operator!=(const Glyph &p_a) const;