[pango] Short-circuit FriBidi call if paragraph is unidirectional



commit 9f65356b7b473b607bea0510709a4d7330be9c9a
Author: Behdad Esfahbod <behdad behdad org>
Date:   Sat Jul 7 16:42:03 2018 +0200

    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.

 pango/pango-bidi-type.c | 69 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file 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;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]