Re: writing a text widget with pango



Peter Zelezny <pzel dodo com au> writes: 
1) There are two ways I can render a PangoLayout:

   gdk_draw_layout (window, gc, layout, x, y);

   -or-

   ln = 0;
   while (1)
      {
              line = pango_layout_get_line (layout, ln);
              if (!line)
                      break;
              gdk_draw_layout_line (window, gc, x, y, line);
              y += this_lines_height;
              ln++;
      }

I thought these two ways would produce the same result, but they
don't. Rendering it line-by-line ignores the alignment setting
(PANGO_ALIGN_CENTER/PANGO_ALIGN_RIGHT). Is this intentional?

Yes. The layout lays out the lines. Note that a layout is intended to
be a paragraph, and a line in this context is a line created by line
wrapping. (Though you can put \n in PangoLayout to allow multiple
paragraphs in a layout, and that works fine, it doesn't scale to very
many paragraphs. \n is a paragraph delimiter rather than a line
delimiter in this context.)

2) When your window is resized you have to do alot of work. How
   does GtkTextView handle this? I basically have to walk my entire
   buffer (list of PangoLayouts) and call pango_layout_set_width and
   pango_layout_get_line_count on them all (to set my Adjustment). This
   is pretty CPU intensive when using opaque resize or a large buffer
   (anything beyond a few 100 lines).

TextView is doing incremental layout in an idle handler. It's way
fun. ;-)

TextView used to do the "brute force" method, when it was first ported
from Tk. Tk used the brute force method. However, there is no way
to say "processing 10,000 lines will just be fast enough" when you're
doing correct i18n text processing; the algorithms are just inherently
slow, and so you just have to redesign things on a high level to avoid
the need for speed in calculating paragraph layouts.

So when we moved to Pango we had to do incremental layout.

3) Mapping the adjustment->value to a position in the text buffer.
   Again, how does GtkTextView do this? It's not as easy as it seems,
   since each entry in the buffer (a PangoLayout) could take up any
   number of lines on the screen (1 or more). Currently I'm doing
   something like:

   l = 0;
   while (buffer)
      {
              l += pango_layout_get_line_count (buffer->layout);
              if (l >= adjustment->value)
                      break;
              buffer = buffer->next;
      }

   This is CPU intensitve aswell. Any suggestions on how to improve
   this algorithm would be appriciated.

TextView scrolling is pixel-based rather than line-based (a change
from the Tk widget it's based on, which was line-based). But the way
it works is that there's an enormously complicated BTree data
structure, and one of the things it does is that it maps from pixel
positions to paragraphs. I'm pretty sure you want to do something
simpler than what TextView does.

You could probably just get away with a list of one node per
paragraph, where each node caches the pixel height of the paragraph,
or number of lines in it. You most likely _don't_ want to keep a
PangoLayout around for each paragraph, just calculate them as-needed
for the paragraphs that are actually on the screen.

Then you could move to incremental layout fairly easily, essentially
incremental means you always compute the onscreen sizes immediately,
assuming all other sizes are 0, in an idle handler you replace some 0
sizes with the actual sizes and update the scrolling accordingly.

4) Is pango_layout_line_get_pixel_extents cheap or should I cache
it?

It's medium. I'd only cache it if you get it to show up in a profile
once you have things working well. You probably don't want to keep
PangoLayout objects around, so creating those will overshadow the cost
of the pixel extents computation.

5) In this new GTK2.0/double-buffer world, is it quicker to set your
   GdkWindow background color, or use a 'None' backing pixmap?

Neither is faster I don't think, you'd normally want to avoid None
just because it creates weird visual effects.


One thing we're considering for GTK 2.2 is to put back a simple
draw-string API like gdk_draw_string and gdk_text_width(), for use by
terminal widgets and that sort of thing. Would probably make porting
XChat a lot easier. But of course using this API means you don't have
i18n quite working.

Havoc



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