Re: News on the text rendering front



Hi Lars,
sorry for the late answer. There where constantly other things dragging away my time, prohibting to look closely enough into Dia's weakest area ;)

On 05.09.2006 20:34, Lars Clausen wrote:
The last couple of weeks, I have been working on secret text rendering
stuff, trying out various things I had hoped would work to improve the
quality and speed.  To that end, I have introduced a new object, called
TextLine, which comprises a single line of text with its font and height
and possibly some cached information.

This is an interesting idea but at the moment the TextLine objects seem to
be too short-lived to have much positive impact. In fact they only seem to
be created within the draw_text methods, so any performance boost through
caching seems moot, when looking at the code. [In fact there is a huge
speed improvement, but I don't completely understand why that is.]

Also your implementation heavily depends on direct Freetype usage which is
a problem for portability. [Although Freetype is available for win32, Dia
does not depend on it, but instead transparently uses the win32 native font
backend through Gtk+ and Pango (or Pango/cairo since gtk+-2.8)]

So instead of poking into the internals of the various backends I've asked
google for help about the root problem, switching off font hinting:
http://www.google.com/search?q=win32+font+hinting

The first match is a post [1] from Owen Taylor of Gtk+, Pango and cairo fame.

Owens answer to specific rendering needs for Pango is PangoRenderer [2,3],
introduced with Pango 1.8, which is a requirement of Gtk+-2.6, so
acceptable as Dia's minimal version. PangoRender allows to set a matrix to
adapt the rendering, which also seems to be the only way to switch of
hinting in win32 [1].

Just completely turning of dia_font_scaled_build_layout() and instead using
an appropriate pango_matrix_scale() gave very encouraging results, Dia's
strings were perfectly matching the width of the box - regardless of the
zoom - for the first time in Dia's history.

The remaining issues were:
1) if you use a large scale factor the missing hinting is very visible,
  to the extend, that single glyphs are not not drawn at all, see:
  http://hans.breuer.org/dia/text-box-1282-1.png
  But with a combination of scaled font and font matrix it got much better.

2) the alignment adjustment is somewhat wrong, probably a combination
  from still using dia_font_get_scaled_string_width() to offset but
  already somewhat adjusting the offset for Pango with the matrix
  http://hans.breuer.org/dia/text-box-1282-1.png

3) the cursor position is wrong when zoomed. This is not new with the
  new approach, but should be fixed anyway.

For the unscaled version see: http://hans.breuer.org/dia/text-box-100-1.png

This allowed me to fiddle enough with rendering to finally find what
appears to be the root cause of the font rendering problems:  Pango
seems to round the width of glyphs in its layout to pixels.  Thus
rendering the same text at different font sizes is almost guaranteed to
give different relative widths. Our current kludge of trying to find a
proper height was not only very slow, potentially creating many layouts
per rendering, but also could not possibly work in all cases.

But that kludge is still used in CVS, isn't it?

In the TextLine rendering function for the GDK FT2 rendering, I make use
of cached values for the sizes of the glyphs at 100% to "manually"
adjust the sizes at other zoom levels to match.  This has allowed me to
sidestep the whole ugly kludge and render directly with the desired
size.

Could you explain me in simple words why we need another high level Text
object or better how the new TextLine is conceptually different from the
long time existing Text object, which already used to cache its size. Even
better it clearly belongs to the object side of rendering so many object
implementations alreay benefit from it.

The outcome:

    Text rendering is now up to 30 times (not just 30%, 30 times!)
    faster than before, text width is actually accurate, and we can
    toss out our ugly kludge.  Three very annoying birds with one
    stone.  I am very happy.

There's still some amount of work for this to be used throughout the
renderers, and the objects should use TextLine instead of ever calling
draw_string.
Getting rid of draw_string, where font and text are completely separated
may be a worthwhile goal, but both our approaches don't really need this.

I expect to be working on this in the upcoming weeks, and
any help would be appreciated, especially from those who know the ins
and outs of the various renderers.
The patch I've done just calculates the the width of the string a second time and returns the deviation of the desired as scale factor. It does not need the new TexLine object to be almost as fast and I would prefer to not have another object between DiaObjects and DiaRenderers at least if we can do as well without.

Thanks,
        Hans


[1] http://lists.freedesktop.org/archives/cairo/2005-January/002901.html
[2] http://bugzilla.gnome.org/show_bug.cgi?id=107668
[3] http://developer.gnome.org/doc/API/2.0/pango/pango-PangoRenderer.html#PangoRenderer


-------- Hans "at" Breuer "dot" Org -----------
Tell me what you need, and I'll tell you how to
get along without it.                -- Dilbert

Only in my-gtk/dia/lib: dia-lib-icons.h
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/dia/lib/diagdkrenderer.c 
my-gtk/dia/lib/diagdkrenderer.c
--- from-cvs/dia/lib/diagdkrenderer.c   Sat Sep 23 13:20:59 2006
+++ my-gtk/dia/lib/diagdkrenderer.c     Sun Oct 01 17:18:24 2006
@@ -659,7 +659,7 @@
              const gchar *text, Point *pos, Alignment alignment,
              Color *color)
 {
-#define DRAW_STRING_WITH_TEXT_LINE
+//#define DRAW_STRING_WITH_TEXT_LINE
 #ifdef DRAW_STRING_WITH_TEXT_LINE
   TextLine *text_line = text_line_new(text, object->font, object->font_height);
   real width = text_line_get_width(text_line);
@@ -782,6 +782,10 @@
   }
 #else
   {
+    PangoMatrix *pango_matrix = NULL;
+       PangoMatrix matrix = PANGO_MATRIX_INIT;
+       double scale;
+
     GdkGC *gc = renderer->gc;
     gdk_gc_set_foreground(gc, &gdkcolor);
     dia_transform_coords(renderer->transform, start_pos.x, start_pos.y, &x, &y);
@@ -789,13 +793,20 @@
     layout = dia_font_scaled_build_layout(
                 text, object->font,
                 object->font_height,
-                dia_transform_length (renderer->transform, 10.0) / 10.0);
-    y -= get_layout_first_baseline(layout);  
+               dia_transform_length (renderer->transform, 10.0) / 10.0, &scale);
+    y -= (get_layout_first_baseline(layout) * scale);
+       pango_matrix = pango_context_get_matrix (pango_layout_get_context (layout));
+       if (pango_matrix)
+         matrix = *pango_matrix;
+       pango_matrix_scale (&matrix, scale, scale);
+       pango_context_set_matrix (pango_layout_get_context (layout), &matrix);
+
     if (renderer->highlight_color != NULL) {
       draw_highlighted_string(renderer, layout, x, y, &gdkcolor);
     } else {
       gdk_draw_layout(renderer->pixmap,gc,x,y,layout);
     }
+       pango_context_set_matrix (pango_layout_get_context (layout), pango_matrix);
   }
 #endif
   
@@ -971,7 +982,7 @@
     dia_transform_coords(renderer->transform, start_pos.x, start_pos.y, &x, &y);
 
     layout = dia_font_scaled_build_layout(text, font, font_height,
-                dia_transform_length (renderer->transform, 10.0) / 10.0);
+                dia_transform_length (renderer->transform, 10.0) / 10.0, NULL);
     y -= get_layout_first_baseline(layout);  
     if (renderer->highlight_color != NULL) {
       draw_highlighted_string(renderer, layout, x, y, &gdkcolor);
Only in my-gtk/dia/lib: diagdkrenderer.c.bak
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/dia/lib/dialibartrenderer.c 
my-gtk/dia/lib/dialibartrenderer.c
--- from-cvs/dia/lib/dialibartrenderer.c        Sat Aug 12 23:32:20 2006
+++ my-gtk/dia/lib/dialibartrenderer.c  Sun Oct 01 16:55:10 2006
@@ -1119,7 +1119,7 @@
 
   layout = dia_font_scaled_build_layout(
               text, self->font, self->font_height,
-              dia_transform_length(renderer->transform, 1.0));
+              dia_transform_length(renderer->transform, 1.0), NULL);
 
   if (renderer->highlight_color != NULL) {
     draw_highlighted_string(renderer, layout, start_pos.x, start_pos.y, rgba);
Only in my-gtk/dia/lib: diamarshal.c
Only in my-gtk/dia/lib: diamarshal.h
Only in my-gtk/dia/lib: font-performance.txt
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/dia/lib/font.c my-gtk/dia/lib/font.c
--- from-cvs/dia/lib/font.c     Tue Sep 05 21:56:57 2006
+++ my-gtk/dia/lib/font.c       Sun Oct 01 20:03:42 2006
@@ -156,7 +156,7 @@
 
     /* dia centimetres to pango device units */
 static gint
-dcm_to_pdu(real dcm) { return dcm * global_zoom_factor * PANGO_SCALE; }
+dcm_to_pdu(real dcm) { return (gint)(dcm * global_zoom_factor * PANGO_SCALE); }
     /* pango device units to dia centimetres */
 static real
 pdu_to_dcm(gint pdu) { return (real)pdu / (global_zoom_factor * PANGO_SCALE); }
@@ -596,19 +596,20 @@
     int lw,lh;
     real result;
     PangoLayout* layout;
+    real match;
 
     if (string == NULL || string[0] == '\0') {
       return 0.0;
     }
 
-    layout = dia_font_scaled_build_layout(string, font, height, zoom_factor);
+    layout = dia_font_scaled_build_layout(string, font, height, zoom_factor, &match);
     pango_layout_get_size(layout,&lw,&lh);
     g_object_unref(G_OBJECT(layout));
     
     result = pdu_to_dcm(lw);
     /* Scale the result back for the zoom factor */
     result /= (zoom_factor/global_zoom_factor);
-    return result;
+    return result * match;
 }
 
 static gboolean
@@ -621,13 +622,14 @@
     PangoLayout* layout;
     PangoLayoutIter* iter;
     guint i;
+    real match;
 
     if (string == NULL || string[0] == '\0') {
       return FALSE;
     }
 
     layout = dia_font_scaled_build_layout(string, font,
-                                          height, zoom_factor);
+                                          height, zoom_factor, &match);
     iter = pango_layout_get_iter(layout);
     for (i = 0; i < line_no; ++i) {
        if (!pango_layout_iter_next_line(iter)) {
@@ -757,7 +759,7 @@
 
 PangoLayout*
 dia_font_scaled_build_layout(const char* string, DiaFont* font,
-                            real height, real zoom_factor)
+                            real height, real zoom_factor, real* match)
 {
     DiaFont* altered_font;
     real scaling;
@@ -769,6 +771,8 @@
 
     scaling = zoom_factor / global_zoom_factor;
     if (fabs(1.0 - scaling) < 1E-7) {
+               if (match)
+           *match = 1.0;
         return dia_font_build_layout(string,font,height);
     }
 
@@ -777,7 +781,10 @@
 
         /* First try: no tweaks. */
     real_width = dia_font_string_width(string,font, height * scaling);
-    if (real_width <= target_zoomed_width) {
+    if (real_width <= target_zoomed_width || match) {
+        /* stop early with deviation, caller is responsible to apply the respective matrix before rendering 
the layout */
+        if (match)
+           *match = target_zoomed_width / real_width;
         return dia_font_build_layout(string,font,height*scaling);
     }
 
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/dia/lib/font.h my-gtk/dia/lib/font.h
--- from-cvs/dia/lib/font.h     Tue Sep 05 21:56:57 2006
+++ my-gtk/dia/lib/font.h       Sun Oct 01 16:04:26 2006
@@ -222,7 +222,7 @@
     ligaturing and other wild beasts usually get in the way of linear
     scaling). */
 PangoLayout* dia_font_scaled_build_layout(const char *string, DiaFont* font,
-                                          real height, real zoom_factor);
+                                          real height, real zoom_factor, real* match);
 
 
 #endif /* FONT_H */
Only in my-gtk/dia/lib/pixmaps: broken-chain.xpm
Only in my-gtk/dia/lib/pixmaps: unbroken-chain.xpm


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