You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-12-02 16:48:55 +00:00
[TextServer] Track emoji subruns separately from parentheses stack.
This commit is contained in:
@@ -56,6 +56,11 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
|
||||
UScriptCode script_code;
|
||||
};
|
||||
|
||||
struct EmojiSubrunEntry {
|
||||
int start;
|
||||
int end;
|
||||
};
|
||||
|
||||
if (p_start >= p_length) {
|
||||
p_start = p_length - 1;
|
||||
}
|
||||
@@ -65,8 +70,12 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
|
||||
}
|
||||
|
||||
int paren_size = PAREN_STACK_DEPTH;
|
||||
ParenStackEntry starter_stack[PAREN_STACK_DEPTH];
|
||||
ParenStackEntry *paren_stack = starter_stack;
|
||||
ParenStackEntry starter_paren_stack[PAREN_STACK_DEPTH];
|
||||
ParenStackEntry *paren_stack = starter_paren_stack;
|
||||
|
||||
int emoji_size = EMOJI_STACK_DEPTH;
|
||||
EmojiSubrunEntry starter_emoji_stack[EMOJI_STACK_DEPTH];
|
||||
EmojiSubrunEntry *emoji_stack = starter_emoji_stack;
|
||||
|
||||
int script_start;
|
||||
int script_end = p_start;
|
||||
@@ -78,19 +87,38 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
|
||||
|
||||
do {
|
||||
script_code = USCRIPT_COMMON;
|
||||
int emoji_sp = -1;
|
||||
bool emoji_run = false;
|
||||
for (script_start = script_end; script_end < p_length; script_end++) {
|
||||
UChar32 ch = str[script_end];
|
||||
UChar32 n = (script_end + 1 < p_length) ? str[script_end + 1] : 0;
|
||||
if (is_emoji(ch, n)) {
|
||||
if (!emoji_run) {
|
||||
emoji_run = true;
|
||||
emoji_sp++;
|
||||
if (unlikely(emoji_sp >= emoji_size)) {
|
||||
emoji_size += EMOJI_STACK_DEPTH;
|
||||
if (emoji_stack == starter_emoji_stack) {
|
||||
emoji_stack = static_cast<EmojiSubrunEntry *>(memalloc(emoji_size * sizeof(EmojiSubrunEntry)));
|
||||
} else {
|
||||
emoji_stack = static_cast<EmojiSubrunEntry *>(memrealloc(emoji_stack, emoji_size * sizeof(EmojiSubrunEntry)));
|
||||
}
|
||||
}
|
||||
emoji_stack[emoji_sp].start = script_end;
|
||||
emoji_stack[emoji_sp].end = script_end;
|
||||
}
|
||||
} else if (emoji_run && ch != ZERO_WIDTH_JOINER && ch != VARIATION_SELECTOR_16 && !(u_hasBinaryProperty(ch, UCHAR_EXTENDED_PICTOGRAPHIC) && n != VARIATION_SELECTOR_15)) {
|
||||
emoji_run = false;
|
||||
emoji_stack[emoji_sp].end = script_end;
|
||||
}
|
||||
|
||||
UScriptCode sc = uscript_getScript(ch, &err);
|
||||
if (U_FAILURE(err)) {
|
||||
if (paren_stack != starter_stack) {
|
||||
if (paren_stack != starter_paren_stack) {
|
||||
memfree(paren_stack);
|
||||
}
|
||||
ERR_FAIL_MSG(u_errorName(err));
|
||||
}
|
||||
if (is_emoji(ch, n)) {
|
||||
sc = USCRIPT_SYMBOLS_EMOJI;
|
||||
}
|
||||
|
||||
if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) != U_BPT_NONE) {
|
||||
if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) {
|
||||
@@ -99,7 +127,7 @@ 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;
|
||||
if (paren_stack == starter_stack) {
|
||||
if (paren_stack == starter_paren_stack) {
|
||||
paren_stack = static_cast<ParenStackEntry *>(memalloc(paren_size * sizeof(ParenStackEntry)));
|
||||
} else {
|
||||
paren_stack = static_cast<ParenStackEntry *>(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry)));
|
||||
@@ -122,11 +150,7 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
|
||||
}
|
||||
}
|
||||
|
||||
if (script_code == USCRIPT_SYMBOLS_EMOJI && script_code != sc) {
|
||||
if (ch == VARIATION_SELECTOR_15 || n == VARIATION_SELECTOR_15 || !(is_emoji(ch, n) || ch == ZERO_WIDTH_JOINER || ch == VARIATION_SELECTOR_16 || u_hasBinaryProperty(ch, UCHAR_EXTENDED_PICTOGRAPHIC))) {
|
||||
break;
|
||||
}
|
||||
} else if (same_script(script_code, sc)) {
|
||||
if (same_script(script_code, sc)) {
|
||||
if (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
|
||||
script_code = sc;
|
||||
// Now that we have a final script code, fix any open characters we pushed before we knew the script code.
|
||||
@@ -145,16 +169,40 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (emoji_run) {
|
||||
emoji_stack[emoji_sp].end = script_end;
|
||||
}
|
||||
|
||||
ScriptRange rng;
|
||||
rng.script = hb_icu_script_to_script(script_code);
|
||||
rng.start = script_start;
|
||||
rng.end = script_end;
|
||||
for (int sub = 0; sub <= emoji_sp; sub++) {
|
||||
if (emoji_stack[sub].start > script_start) {
|
||||
ScriptRange rng;
|
||||
rng.script = hb_icu_script_to_script(script_code);
|
||||
rng.start = script_start;
|
||||
rng.end = emoji_stack[sub].start;
|
||||
script_ranges.push_back(rng);
|
||||
}
|
||||
ScriptRange rng;
|
||||
rng.script = (hb_script_t)HB_TAG('Z', 's', 'y', 'e');
|
||||
rng.start = emoji_stack[sub].start;
|
||||
rng.end = emoji_stack[sub].end;
|
||||
script_ranges.push_back(rng);
|
||||
|
||||
script_ranges.push_back(rng);
|
||||
script_start = emoji_stack[sub].end;
|
||||
}
|
||||
if (script_start != script_end) {
|
||||
ScriptRange rng;
|
||||
rng.script = hb_icu_script_to_script(script_code);
|
||||
rng.start = script_start;
|
||||
rng.end = script_end;
|
||||
script_ranges.push_back(rng);
|
||||
}
|
||||
|
||||
if (emoji_stack != starter_emoji_stack) {
|
||||
memfree(emoji_stack);
|
||||
}
|
||||
} while (script_end < p_length);
|
||||
|
||||
if (paren_stack != starter_stack) {
|
||||
if (paren_stack != starter_paren_stack) {
|
||||
memfree(paren_stack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ using namespace godot;
|
||||
|
||||
class ScriptIterator {
|
||||
static const int PAREN_STACK_DEPTH = 128;
|
||||
static const int EMOJI_STACK_DEPTH = 32;
|
||||
|
||||
public:
|
||||
struct ScriptRange {
|
||||
|
||||
Reference in New Issue
Block a user