Pango-based font handling: combine display of adjacent characters
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 11 May 2009 08:46:17 +0000 (08:46 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 11 May 2009 08:46:17 +0000 (08:46 +0000)
into a single gdk_draw_layout() where conveniently feasible, after
some work with xtrace revealed this as a major source of pterm's
slow display updates when using client-side fonts.

Ideally we ought to be able to do better. I know exactly what
sequence of X protocol operations I want to see on the wire, but I
don't know how to persuade Pango to generate them.

git-svn-id: svn://svn.tartarus.org/sgt/putty@8558 cda61777-01e9-0310-a592-d414129be87e

unix/gtkfont.c

index fb75630..0db5ba2 100644 (file)
@@ -892,28 +892,83 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
     }
 
     while (len > 0) {
-       int clen;
+       int clen, n;
 
        /*
-        * Extract a single UTF-8 character from the string.
+        * We want to display every character from this string in
+        * the centre of its own character cell. In the worst case,
+        * this requires a separate text-drawing call for each
+        * character; but in the common case where the font is
+        * properly fixed-width, we can draw many characters in one
+        * go which is much faster.
+        *
+        * This still isn't really ideal. If you look at what
+        * happens in the X protocol as a result of all of this, you
+        * find - naturally enough - that each call to
+        * gdk_draw_layout() generates a separate set of X RENDER
+        * operations involving creating a picture, setting a clip
+        * rectangle, doing some drawing and undoing the whole lot.
+        * In an ideal world, we should _always_ be able to turn the
+        * contents of this loop into a single RenderCompositeGlyphs
+        * operation which internally specifies inter-character
+        * deltas to get the spacing right, which would give us full
+        * speed _even_ in the worst case of a non-fixed-width font.
+        * However, Pango's architecture and documentation are so
+        * unhelpful that I have no idea how if at all to persuade
+        * them to do that.
+        */
+
+       /*
+        * Start by extracting a single UTF-8 character from the
+        * string.
         */
        clen = 1;
        while (clen < len &&
               (unsigned char)string[clen] >= 0x80 &&
               (unsigned char)string[clen] < 0xC0)
            clen++;
+       n = 1;
+
+       /*
+        * See if that character has the width we expect.
+        */
+       pango_layout_set_text(layout, string, clen);
+       pango_layout_get_pixel_extents(layout, NULL, &rect);
+
+       if (rect.width == cellwidth) {
+           /*
+            * Try extracting more characters, for as long as they
+            * stay well-behaved.
+            */
+           while (clen < len) {
+               int oldclen = clen;
+               clen++;                /* skip UTF-8 introducer byte */
+               while (clen < len &&
+                      (unsigned char)string[clen] >= 0x80 &&
+                      (unsigned char)string[clen] < 0xC0)
+                   clen++;
+               n++;
+               pango_layout_set_text(layout, string, clen);
+               pango_layout_get_pixel_extents(layout, NULL, &rect);
+               if (rect.width != n * cellwidth) {
+                   clen = oldclen;
+                   n--;
+                   break;
+               }
+           }
+       }
 
        pango_layout_set_text(layout, string, clen);
        pango_layout_get_pixel_extents(layout, NULL, &rect);
-       gdk_draw_layout(target, gc, x + (cellwidth - rect.width)/2,
+       gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2,
                        y + (pfont->u.height - rect.height)/2, layout);
        if (shadowbold)
-           gdk_draw_layout(target, gc, x + (cellwidth - rect.width)/2 + pfont->shadowoffset,
+           gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset,
                            y + (pfont->u.height - rect.height)/2, layout);
 
        len -= clen;
        string += clen;
-       x += cellwidth;
+       x += n * cellwidth;
     }
 
     g_object_unref(layout);