summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBehdad Esfahbod <behdad@behdad.org>2018-07-07 16:42:03 +0200
committerBehdad Esfahbod <behdad@behdad.org>2018-07-07 17:26:17 +0200
commit9f65356b7b473b607bea0510709a4d7330be9c9a (patch)
treee31da9a93b76ba50fcce6e711a756e767158c466
parentcdb88930da73bdbeb0e6167825121435d7e5e99f (diff)
downloadpango-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.c69
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;