diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp index fbe63735384..f8d2450bb39 100644 --- a/modules/text_server_adv/script_iterator.cpp +++ b/modules/text_server_adv/script_iterator.cpp @@ -32,10 +32,20 @@ // This implementation is derived from ICU: icu4c/source/extra/scrptrun/scrptrun.cpp -bool ScriptIterator::same_script(int32_t p_script_one, int32_t p_script_two) { +inline bool ScriptIterator::same_script(int32_t p_script_one, int32_t p_script_two) { return p_script_one <= USCRIPT_INHERITED || p_script_two <= USCRIPT_INHERITED || p_script_one == p_script_two; } +inline bool ScriptIterator::is_emoji(UChar32 p_c, UChar32 p_next) { + if (p_next == 0xFE0E) { // Variation Selector-15 + return false; + } else if (p_next == 0xFE0F) { // Variation Selector-16 + return true; + } else { + return u_hasBinaryProperty(p_c, UCHAR_EMOJI) || u_hasBinaryProperty(p_c, UCHAR_EMOJI_PRESENTATION) || u_hasBinaryProperty(p_c, UCHAR_EMOJI_MODIFIER) || u_hasBinaryProperty(p_c, UCHAR_REGIONAL_INDICATOR) || u_hasBinaryProperty(p_c, UCHAR_EXTENDED_PICTOGRAPHIC); + } +} + ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length) { struct ParenStackEntry { int pair_index; @@ -65,11 +75,16 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length script_code = USCRIPT_COMMON; 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; UScriptCode sc = uscript_getScript(ch, &err); if (U_FAILURE(err)) { 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) { // If it's an open character, push it onto the stack. @@ -96,7 +111,12 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length } } - if (same_script(script_code, sc)) { + if (script_code == USCRIPT_SYMBOLS_EMOJI && script_code != sc) { + UCharCategory cat = (UCharCategory)u_charType(ch); + if ((cat >= U_SPACE_SEPARATOR && cat <= U_CONTROL_CHAR) || (cat >= U_DASH_PUNCTUATION && cat <= U_OTHER_PUNCTUATION) || (cat >= U_INITIAL_PUNCTUATION && cat <= U_FINAL_PUNCTUATION)) { + break; + } + } else 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. diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h index ee865fbf6dd..8a1de6f30e0 100644 --- a/modules/text_server_adv/script_iterator.h +++ b/modules/text_server_adv/script_iterator.h @@ -68,7 +68,8 @@ public: Vector script_ranges; private: - static bool same_script(int32_t p_script_one, int32_t p_script_two); + inline static bool same_script(int32_t p_script_one, int32_t p_script_two); + inline static bool is_emoji(UChar32 p_c, UChar32 p_next); public: ScriptIterator(const String &p_string, int p_start, int p_length); diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 567bd73d3f7..c6bc44a5deb 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -2006,7 +2006,7 @@ _FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontAdvanced *p_font_d p_font_data->supported_scripts.clear(); } -hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_t p_size) const { +hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_t p_size, bool &r_is_color) const { FontAdvanced *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL_V(fd, nullptr); @@ -2015,6 +2015,11 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_ FontForSizeAdvanced *ffsd = nullptr; ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), nullptr); +#ifdef MODULE_FREETYPE_ENABLED + r_is_color = FT_HAS_COLOR(ffsd->face); +#else + r_is_color = false; +#endif return ffsd->hb_handle; } @@ -6527,7 +6532,8 @@ bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shap } Glyph TextServerAdvanced::_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) { - hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size); + bool color = false; + hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size, color); double scale = _font_get_scale(p_font, p_font_size); bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_AUTO && p_font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE); ERR_FAIL_NULL_V(hb_font, Glyph()); @@ -6535,7 +6541,7 @@ Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char hb_buffer_clear_contents(p_sd->hb_buffer); hb_buffer_set_direction(p_sd->hb_buffer, p_direction); hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_DEFAULT)); - hb_buffer_set_script(p_sd->hb_buffer, p_script); + hb_buffer_set_script(p_sd->hb_buffer, (p_script == HB_TAG('Z', 's', 'y', 'e')) ? HB_SCRIPT_COMMON : p_script); hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)&p_char, 1, 0, 1); hb_shape(hb_font, p_sd->hb_buffer, nullptr, 0); @@ -6740,9 +6746,17 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star FontAdvanced *fd = _get_font_data(f); ERR_FAIL_NULL(fd); MutexLock lock(fd->mutex); + bool color = false; Vector2i fss = _get_size(fd, fs); - hb_font_t *hb_font = _font_get_hb_handle(f, fs); + hb_font_t *hb_font = _font_get_hb_handle(f, fs, color); + + if (p_script == HB_TAG('Z', 's', 'y', 'e') && !color) { + // Color emoji is requested, skip non-color font. + _shape_run(p_sd, p_start, p_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, f); + return; + } + double scale = _font_get_scale(f, fs); double sp_sp = p_sd->extra_spacing[SPACING_SPACE] + _font_get_spacing(f, SPACING_SPACE); double sp_gl = p_sd->extra_spacing[SPACING_GLYPH] + _font_get_spacing(f, SPACING_GLYPH); @@ -6763,7 +6777,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star flags |= HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL; #endif hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)flags); - hb_buffer_set_script(p_sd->hb_buffer, p_script); + hb_buffer_set_script(p_sd->hb_buffer, (p_script == HB_TAG('Z', 's', 'y', 'e')) ? HB_SCRIPT_COMMON : p_script); if (p_sd->spans[p_span].language.is_empty()) { hb_language_t lang = hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 254430037fb..7e17128c58e 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -726,7 +726,7 @@ class TextServerAdvanced : public TextServerExtension { static void _bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref); static hb_font_t *_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, hb_destroy_func_t p_destroy); - hb_font_t *_font_get_hb_handle(const RID &p_font, int64_t p_font_size) const; + hb_font_t *_font_get_hb_handle(const RID &p_font, int64_t p_font_size, bool &r_is_color) const; struct GlyphCompare { // For line breaking reordering. _FORCE_INLINE_ bool operator()(const Glyph &l, const Glyph &r) const {