1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-21 14:57:09 +00:00

[TextServer] Add function to change font, font size, and OpenType features without invalidating line break points, justification points, or recreating shaped text buffer.

This commit is contained in:
bruvzg
2022-01-20 09:30:42 +02:00
parent 050908626f
commit 215bede6ff
22 changed files with 705 additions and 491 deletions

View File

@@ -2901,7 +2901,7 @@ void TextServerAdvanced::font_set_global_oversampling(float p_oversampling) {
List<RID> text_bufs;
shaped_owner.get_owned_list(&text_bufs);
for (const RID &E : text_bufs) {
invalidate(shaped_owner.get_or_null(E));
invalidate(shaped_owner.get_or_null(E), false);
}
}
}
@@ -2936,7 +2936,7 @@ int TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int
return limit;
}
void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped) {
void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped, bool p_text) {
p_shaped->valid = false;
p_shaped->sort_valid = false;
p_shaped->line_breaks_valid = false;
@@ -2951,27 +2951,32 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *
p_shaped->glyphs_logical.clear();
p_shaped->overrun_trim_data = TrimData();
p_shaped->utf16 = Char16String();
if (p_shaped->script_iter != nullptr) {
memdelete(p_shaped->script_iter);
p_shaped->script_iter = nullptr;
}
for (int i = 0; i < p_shaped->bidi_iter.size(); i++) {
ubidi_close(p_shaped->bidi_iter[i]);
}
p_shaped->bidi_iter.clear();
if (p_text) {
if (p_shaped->script_iter != nullptr) {
memdelete(p_shaped->script_iter);
p_shaped->script_iter = nullptr;
}
p_shaped->break_ops_valid = false;
p_shaped->js_ops_valid = false;
}
}
void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent);
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : parent->objects) {
if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
p_shaped->objects[E.key] = E.value;
}
}
for (int k = 0; k < parent->spans.size(); k++) {
ShapedTextDataAdvanced::Span span = parent->spans[k];
for (int i = 0; i < parent->spans.size(); i++) {
ShapedTextDataAdvanced::Span span = parent->spans[i];
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
continue;
}
@@ -3004,7 +3009,7 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
sd->spans.clear();
sd->objects.clear();
sd->bidi_override.clear();
invalidate(sd);
invalidate(sd, true);
}
void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
@@ -3017,7 +3022,7 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir
full_copy(sd);
}
sd->direction = p_direction;
invalidate(sd);
invalidate(sd, false);
}
}
@@ -3047,7 +3052,7 @@ void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const
full_copy(sd);
}
sd->custom_punct = p_punct;
invalidate(sd);
invalidate(sd, false);
}
}
@@ -3070,7 +3075,7 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array
for (int i = 0; i < p_override.size(); i++) {
sd->bidi_override.push_back(p_override[i]);
}
invalidate(sd);
invalidate(sd, false);
}
void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
@@ -3083,7 +3088,7 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O
full_copy(sd);
}
sd->orientation = p_orientation;
invalidate(sd);
invalidate(sd, false);
}
}
@@ -3095,7 +3100,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
ERR_FAIL_COND(sd->parent != RID());
if (sd->preserve_invalid != p_enabled) {
sd->preserve_invalid = p_enabled;
invalidate(sd);
invalidate(sd, false);
}
}
@@ -3117,7 +3122,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e
full_copy(sd);
}
sd->preserve_control = p_enabled;
invalidate(sd);
invalidate(sd, false);
}
}
@@ -3137,7 +3142,41 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_sh
return sd->orientation;
}
bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
int TextServerAdvanced::shaped_get_span_count(RID p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
return sd->spans.size();
}
Variant TextServerAdvanced::shaped_get_span_meta(RID p_shaped, int p_index) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Variant());
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
return sd->spans[p_index].meta;
}
void TextServerAdvanced::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
ERR_FAIL_INDEX(p_index, sd->spans.size());
ShapedTextDataAdvanced::Span &span = sd->spans.write[p_index];
bool changed = (span.font_size != p_size) || (span.features != p_opentype_features) || (p_fonts.size() != span.fonts.size());
if (!changed) {
for (int i = 0; i < p_fonts.size(); i++) {
changed = changed || (span.fonts[i] != p_fonts[i]);
}
}
if (changed) {
span.fonts = p_fonts;
span.font_size = p_size;
span.features = p_opentype_features;
invalidate(sd, false);
}
}
bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
ERR_FAIL_COND_V(p_size <= 0, false);
@@ -3162,11 +3201,12 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te
span.font_size = p_size;
span.language = p_language;
span.features = p_opentype_features;
span.meta = p_meta;
sd->spans.push_back(span);
sd->text += p_text;
sd->end += p_text.length();
invalidate(sd);
invalidate(sd, true);
return true;
}
@@ -3196,13 +3236,13 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con
sd->text += String::chr(0xfffc).repeat(p_length);
sd->end += p_length;
sd->objects[p_key] = obj;
invalidate(sd);
invalidate(sd, true);
return true;
}
bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3222,7 +3262,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
if (E.value.pos == gl.start) {
key = E.key;
break;
@@ -3262,77 +3302,80 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->width += gl.advance * gl.repeat;
}
}
// Align embedded objects to baseline.
float full_ascent = sd->ascent;
float full_descent = sd->descent;
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.y = -sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.y = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.y = sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.y -= E.value.rect.size.y;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.x = -sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.x = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.x = sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.x -= E.value.rect.size.x;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
}
sd->ascent = full_ascent;
sd->descent = full_descent;
_realign(sd);
}
return true;
}
void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
// Align embedded objects to baseline.
float full_ascent = p_sd->ascent;
float full_descent = p_sd->descent;
for (KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) {
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.y = -p_sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.y = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.y = p_sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.y -= E.value.rect.size.y;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.x = -p_sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.x = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.x = p_sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.x -= E.value.rect.size.x;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
}
p_sd->ascent = full_ascent;
p_sd->descent = full_descent;
}
RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
@@ -3423,7 +3466,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_sd->objects) {
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
if (E.value.pos == gl.start) {
find_embedded = true;
key = E.key;
@@ -3466,72 +3509,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
}
}
// Align embedded objects to baseline.
float full_ascent = p_new_sd->ascent;
float full_descent = p_new_sd->descent;
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_new_sd->objects) {
if ((E.value.pos >= p_new_sd->start) && (E.value.pos < p_new_sd->end)) {
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.y = -p_new_sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.y = (-p_new_sd->ascent + p_new_sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.y = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.y = p_new_sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.y -= E.value.rect.size.y;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.x = -p_new_sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.x = (-p_new_sd->ascent + p_new_sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.x = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.x = p_new_sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.x -= E.value.rect.size.x;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
}
p_new_sd->ascent = full_ascent;
p_new_sd->descent = full_descent;
_realign(p_new_sd);
}
p_new_sd->valid = true;
@@ -3968,40 +3946,43 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
const UChar *data = sd->utf16.ptr();
HashMap<int, bool> breaks;
UErrorCode err = U_ZERO_ERROR;
int i = 0;
while (i < sd->spans.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) {
if (!sd->break_ops_valid) {
sd->breaks.clear();
UErrorCode err = U_ZERO_ERROR;
int i = 0;
while (i < sd->spans.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) {
i++;
}
int r_end = sd->spans[i].end;
UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
if (U_FAILURE(err)) {
// No data loaded - use fallback.
for (int j = r_start; j < r_end; j++) {
char32_t c = sd->text[j - sd->start];
if (is_whitespace(c)) {
sd->breaks[j + 1] = false;
}
if (is_linebreak(c)) {
sd->breaks[j + 1] = true;
}
}
} else {
while (ubrk_next(bi) != UBRK_DONE) {
int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
sd->breaks[pos] = true;
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
sd->breaks[pos] = false;
}
}
}
ubrk_close(bi);
i++;
}
int r_end = sd->spans[i].end;
UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
if (U_FAILURE(err)) {
// No data loaded - use fallback.
for (int j = r_start; j < r_end; j++) {
char32_t c = sd->text[j - sd->start];
if (is_whitespace(c)) {
breaks[j + 1] = false;
}
if (is_linebreak(c)) {
breaks[j + 1] = true;
}
}
} else {
while (ubrk_next(bi) != UBRK_DONE) {
int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
breaks[pos] = true;
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
breaks[pos] = false;
}
}
}
ubrk_close(bi);
i++;
sd->break_ops_valid = true;
}
sd->sort_valid = false;
@@ -4013,7 +3994,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
int c_punct_size = sd->custom_punct.length();
const char32_t *c_punct = sd->custom_punct.ptr();
for (i = 0; i < sd_size; i++) {
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) {
@@ -4040,8 +4021,8 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
if (is_underscore(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
}
if (breaks.has(sd_glyphs[i].end)) {
if (breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
if (sd->breaks.has(sd_glyphs[i].end)) {
if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
} else if (is_whitespace(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
@@ -4186,41 +4167,45 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
const UChar *data = sd->utf16.ptr();
int32_t data_size = sd->utf16.length();
Map<int, bool> jstops;
if (!sd->js_ops_valid) {
sd->jstops.clear();
// Use ICU word iterator and custom kashida detection.
UErrorCode err = U_ZERO_ERROR;
UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
if (U_FAILURE(err)) {
// No data - use fallback.
int limit = 0;
for (int i = 0; i < sd->text.length(); i++) {
if (is_whitespace(data[i])) {
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
if (ks != -1) {
jstops[ks] = true;
// Use ICU word iterator and custom kashida detection.
UErrorCode err = U_ZERO_ERROR;
UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
if (U_FAILURE(err)) {
// No data - use fallback.
int limit = 0;
for (int i = 0; i < sd->text.length(); i++) {
if (is_whitespace(data[i])) {
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
if (ks != -1) {
sd->jstops[ks] = true;
}
limit = i + 1;
}
limit = i + 1;
}
}
int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
if (ks != -1) {
jstops[ks] = true;
}
} else {
int limit = 0;
while (ubrk_next(bi) != UBRK_DONE) {
if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
int i = _convert_pos(sd, ubrk_current(bi));
jstops[i + sd->start] = false;
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
if (ks != -1) {
jstops[ks + sd->start] = true;
int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
if (ks != -1) {
sd->jstops[ks] = true;
}
} else {
int limit = 0;
while (ubrk_next(bi) != UBRK_DONE) {
if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
int i = _convert_pos(sd, ubrk_current(bi));
sd->jstops[i + sd->start] = false;
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
if (ks != -1) {
sd->jstops[ks + sd->start] = true;
}
limit = i;
}
limit = i;
}
ubrk_close(bi);
}
ubrk_close(bi);
sd->js_ops_valid = true;
}
sd->sort_valid = false;
@@ -4228,18 +4213,18 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
Glyph *sd_glyphs = sd->glyphs.ptrw();
int sd_size = sd->glyphs.size();
if (jstops.size() > 0) {
if (sd->jstops.size() > 0) {
for (int i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
if (c == 0x0640) {
sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION;
}
if (jstops.has(sd_glyphs[i].start)) {
if (sd->jstops.has(sd_glyphs[i].start)) {
if (c == 0xfffc) {
continue;
}
if (jstops[sd_glyphs[i].start]) {
if (sd->jstops[sd_glyphs[i].start]) {
if (c != 0x0640) {
if (sd_glyphs[i].font_rid != RID()) {
Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
@@ -4574,7 +4559,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
return true;
}
invalidate(sd);
invalidate(sd, false);
if (sd->parent != RID()) {
shaped_text_shape(sd->parent);
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
@@ -4733,70 +4718,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
}
// Align embedded objects to baseline.
float full_ascent = sd->ascent;
float full_descent = sd->descent;
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.y = -sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.y = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.y = sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.y -= E.value.rect.size.y;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
E.value.rect.position.x = -sd->ascent;
} break;
case INLINE_ALIGNMENT_TO_CENTER: {
E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
} break;
case INLINE_ALIGNMENT_TO_BASELINE: {
E.value.rect.position.x = 0;
} break;
case INLINE_ALIGNMENT_TO_BOTTOM: {
E.value.rect.position.x = sd->descent;
} break;
}
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
case INLINE_ALIGNMENT_BOTTOM_TO: {
E.value.rect.position.x -= E.value.rect.size.x;
} break;
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
}
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
sd->ascent = full_ascent;
sd->descent = full_descent;
_realign(sd);
sd->valid = true;
return sd->valid;
}
@@ -4863,7 +4785,7 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
ERR_FAIL_COND_V(!sd, ret);
MutexLock lock(sd->mutex);
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
ret.push_back(E.key);
}