diff options
author | Behdad Esfahbod <behdad@behdad.org> | 2018-07-07 16:42:03 +0200 |
---|---|---|
committer | Behdad Esfahbod <behdad@behdad.org> | 2018-07-07 17:26:17 +0200 |
commit | 9f65356b7b473b607bea0510709a4d7330be9c9a (patch) | |
tree | e31da9a93b76ba50fcce6e711a756e767158c466 | |
parent | cdb88930da73bdbeb0e6167825121435d7e5e99f (diff) | |
download | pango-9f65356b7b473b607bea0510709a4d7330be9c9a.tar.gz |
Short-circuit FriBidi call if paragraph is unidirectional
This was included in Pango's mini-fribidi but removed when we moved to
external FriBidi.
Most apps create PangoLayout twice to show text, one to measure, one to
render. Each PangoLayout shaping apparently calls FriBidi twice (TODO:
figure out why and fix); FriBidi creates two runs per work. So that's
eight malloc calls per word to show text. That's a lot. With this
optimization that completely disappears for most text.
We should make an API in FriBidi out of this.
Reported by Christian Hergert.
-rw-r--r-- | pango/pango-bidi-type.c | 69 |
1 files changed, 64 insertions, 5 deletions
diff --git a/pango/pango-bidi-type.c b/pango/pango-bidi-type.c index 55d44e98..9e2d4a9f 100644 --- a/pango/pango-bidi-type.c +++ b/pango/pango-bidi-type.c @@ -137,6 +137,8 @@ pango_log2vis_get_embedding_levels (const gchar *text, FriBidiBracketType *bracket_types; #endif FriBidiLevel max_level; + FriBidiCharType ored_types = 0; + FriBidiCharType anded_strongs = FRIBIDI_TYPE_RLE; G_STATIC_ASSERT (sizeof (FriBidiLevel) == sizeof (guint8)); G_STATIC_ASSERT (sizeof (FriBidiChar) == sizeof (gunichar)); @@ -175,7 +177,12 @@ pango_log2vis_get_embedding_levels (const gchar *text, for (i = 0, p = text; p < text + length; p = g_utf8_next_char(p), i++) { gunichar ch = g_utf8_get_char (p); - bidi_types[i] = fribidi_get_bidi_type (ch); + FriBidiCharType char_type; + char_type = fribidi_get_bidi_type (ch); + bidi_types[i] = char_type; + ored_types |= char_type; + if (FRIBIDI_IS_STRONG (char_type)) + anded_strongs &= char_type; #ifdef USE_FRIBIDI_EX_API if (G_UNLIKELY(bidi_types[i] == FRIBIDI_TYPE_ON)) bracket_types[i] = fribidi_get_bracket (ch); @@ -184,6 +191,58 @@ pango_log2vis_get_embedding_levels (const gchar *text, #endif } + /* Short-circuit (malloc-expensive) FriBidi call for unidirectional + * text. + * + * For details see: + * https://bugzilla.gnome.org/show_bug.cgi?id=590183 + */ + +#ifndef FRIBIDI_IS_ISOLATE +#define FRIBIDI_IS_ISOLATE(x) 0 +#endif + /* The case that all resolved levels will be ltr. + * No isolates, all strongs be LTR, there should be no Arabic numbers + * (or letters for that matter), and one of the following: + * + * o base_dir doesn't have an RTL taste. + * o there are letters, and base_dir is weak. + */ + if (!FRIBIDI_IS_ISOLATE (ored_types) && + !FRIBIDI_IS_RTL (ored_types) && + !FRIBIDI_IS_ARABIC (ored_types) && + (!FRIBIDI_IS_RTL (fribidi_base_dir) || + (FRIBIDI_IS_WEAK (fribidi_base_dir) && + FRIBIDI_IS_LETTER (ored_types)) + )) + { + /* all LTR */ + fribidi_base_dir = FRIBIDI_PAR_LTR; + memset (embedding_levels_list, 0, n_chars); + goto resolved; + } + /* The case that all resolved levels will be RTL is much more complex. + * No isolates, no numbers, all strongs are RTL, and one of + * the following: + * + * o base_dir has an RTL taste (may be weak). + * o there are letters, and base_dir is weak. + */ + else if (!FRIBIDI_IS_ISOLATE (ored_types) && + !FRIBIDI_IS_NUMBER (ored_types) && + FRIBIDI_IS_RTL (anded_strongs) && + (FRIBIDI_IS_RTL (fribidi_base_dir) || + (FRIBIDI_IS_WEAK (fribidi_base_dir) && + FRIBIDI_IS_LETTER (ored_types)) + )) + { + /* all RTL */ + fribidi_base_dir = FRIBIDI_PAR_RTL; + memset (embedding_levels_list, 1, n_chars); + goto resolved; + } + + #ifdef USE_FRIBIDI_EX_API max_level = fribidi_get_par_embedding_levels_ex (bidi_types, bracket_types, n_chars, &fribidi_base_dir, @@ -195,15 +254,15 @@ pango_log2vis_get_embedding_levels (const gchar *text, (FriBidiLevel*)embedding_levels_list); #endif - g_free (bidi_types); - if (G_UNLIKELY(max_level == 0)) { - /* fribidi_get_par_embedding_levels() failed, - * is this the best thing to do? */ + /* fribidi_get_par_embedding_levels() failed. */ memset (embedding_levels_list, 0, length); } +resolved: + g_free (bidi_types); + *pbase_dir = (fribidi_base_dir == FRIBIDI_PAR_LTR) ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL; return embedding_levels_list; |