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;
|
UScriptCode script_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct EmojiSubrunEntry {
|
||||||
|
int start;
|
||||||
|
int end;
|
||||||
|
};
|
||||||
|
|
||||||
if (p_start >= p_length) {
|
if (p_start >= p_length) {
|
||||||
p_start = p_length - 1;
|
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;
|
int paren_size = PAREN_STACK_DEPTH;
|
||||||
ParenStackEntry starter_stack[PAREN_STACK_DEPTH];
|
ParenStackEntry starter_paren_stack[PAREN_STACK_DEPTH];
|
||||||
ParenStackEntry *paren_stack = starter_stack;
|
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_start;
|
||||||
int script_end = p_start;
|
int script_end = p_start;
|
||||||
@@ -78,19 +87,38 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
script_code = USCRIPT_COMMON;
|
script_code = USCRIPT_COMMON;
|
||||||
|
int emoji_sp = -1;
|
||||||
|
bool emoji_run = false;
|
||||||
for (script_start = script_end; script_end < p_length; script_end++) {
|
for (script_start = script_end; script_end < p_length; script_end++) {
|
||||||
UChar32 ch = str[script_end];
|
UChar32 ch = str[script_end];
|
||||||
UChar32 n = (script_end + 1 < p_length) ? str[script_end + 1] : 0;
|
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);
|
UScriptCode sc = uscript_getScript(ch, &err);
|
||||||
if (U_FAILURE(err)) {
|
if (U_FAILURE(err)) {
|
||||||
if (paren_stack != starter_stack) {
|
if (paren_stack != starter_paren_stack) {
|
||||||
memfree(paren_stack);
|
memfree(paren_stack);
|
||||||
}
|
}
|
||||||
ERR_FAIL_MSG(u_errorName(err));
|
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_NONE) {
|
||||||
if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) {
|
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 (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.
|
// 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_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)));
|
paren_stack = static_cast<ParenStackEntry *>(memalloc(paren_size * sizeof(ParenStackEntry)));
|
||||||
} else {
|
} else {
|
||||||
paren_stack = static_cast<ParenStackEntry *>(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry)));
|
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 (same_script(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 (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
|
if (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
|
||||||
script_code = sc;
|
script_code = sc;
|
||||||
// Now that we have a final script code, fix any open characters we pushed before we knew the script code.
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (emoji_run) {
|
||||||
|
emoji_stack[emoji_sp].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_start = emoji_stack[sub].end;
|
||||||
|
}
|
||||||
|
if (script_start != script_end) {
|
||||||
ScriptRange rng;
|
ScriptRange rng;
|
||||||
rng.script = hb_icu_script_to_script(script_code);
|
rng.script = hb_icu_script_to_script(script_code);
|
||||||
rng.start = script_start;
|
rng.start = script_start;
|
||||||
rng.end = script_end;
|
rng.end = script_end;
|
||||||
|
|
||||||
script_ranges.push_back(rng);
|
script_ranges.push_back(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emoji_stack != starter_emoji_stack) {
|
||||||
|
memfree(emoji_stack);
|
||||||
|
}
|
||||||
} while (script_end < p_length);
|
} while (script_end < p_length);
|
||||||
|
|
||||||
if (paren_stack != starter_stack) {
|
if (paren_stack != starter_paren_stack) {
|
||||||
memfree(paren_stack);
|
memfree(paren_stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ using namespace godot;
|
|||||||
|
|
||||||
class ScriptIterator {
|
class ScriptIterator {
|
||||||
static const int PAREN_STACK_DEPTH = 128;
|
static const int PAREN_STACK_DEPTH = 128;
|
||||||
|
static const int EMOJI_STACK_DEPTH = 32;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct ScriptRange {
|
struct ScriptRange {
|
||||||
|
|||||||
Reference in New Issue
Block a user