[cairo] [PATCH] _cairo_win32_surface_show_glyphs

Jonathan Watt jwatt at jwatt.org
Tue Dec 12 15:21:14 PST 2006


Hi all,

Here's a patch to fix a couple of bugs in _cairo_win32_surface_show_glyphs while 
at the same time simplifying it to use a single ExtTextOut call using ETO_PDY 
(as suggested by Stuart) instead of (potentially) multiple calls.

The first bug that this patch fixes is the spacing between glyphs when the 
X-axis scale isn't unity. The spacing is broken in these cases because the 
values of glyphs[i].x (and .y) are scaled, meaning that the dx values from which 
they are calculated are also scaled. However, ExtTextOutW expects to have to 
scale the dx values that are passed to it itself, and hence the dx values are 
effectively multiplied by scaleX^2 instead of just scaleX causing the spacing to 
be too great/little.

The second bug the patch fixes is incorrect spacing due to calculating the dx 
values from user coordinates (see the inline comment in the patch).

Regards,
Jonathan
-------------- next part --------------
Index: mozilla/gfx/cairo/cairo/src/cairo-win32-surface.c
===================================================================
RCS file: /cvsroot/mozilla/gfx/cairo/cairo/src/cairo-win32-surface.c,v
retrieving revision 1.32
diff -u -8 -p -r1.32 cairo-win32-surface.c
--- mozilla/gfx/cairo/cairo/src/cairo-win32-surface.c	8 Nov 2006 01:31:17 -0000	1.32
+++ mozilla/gfx/cairo/cairo/src/cairo-win32-surface.c	11 Dec 2006 14:48:29 -0000
@@ -1476,29 +1476,31 @@ _cairo_win32_surface_show_glyphs (void		
 				  const cairo_glyph_t	*glyphs,
 				  int			 num_glyphs,
 				  cairo_scaled_font_t	*scaled_font)
 {
     cairo_win32_surface_t *dst = surface;
 
     WORD glyph_buf_stack[STACK_GLYPH_SIZE];
     WORD *glyph_buf = glyph_buf_stack;
-    int dx_buf_stack[STACK_GLYPH_SIZE];
-    int *dx_buf = dx_buf_stack;
+    int dxy_buf_stack[2 * STACK_GLYPH_SIZE];
+    int *dxy_buf = dxy_buf_stack;
 
     BOOL win_result = 0;
-    int i;
-    double last_y = glyphs[0].y;
+    int i, j;
 
     cairo_solid_pattern_t *solid_pattern;
     COLORREF color;
-    int output_count = 0;
 
     cairo_matrix_t device_to_logical;
 
+    int start_x, start_y;
+    double user_x, user_y;
+    int logical_x, logical_y;
+
     /* We can only handle win32 fonts */
     if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     /* We can only handle opaque solid color sources */
     if (!_cairo_pattern_is_opaque_solid(source))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
@@ -1526,68 +1528,78 @@ _cairo_win32_surface_show_glyphs (void		
 
     cairo_win32_scaled_font_select_font(scaled_font, dst->dc);
     SetTextColor(dst->dc, color);
     SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT);
     SetBkMode(dst->dc, TRANSPARENT);
 
     if (num_glyphs > STACK_GLYPH_SIZE) {
 	glyph_buf = (WORD *)malloc(num_glyphs * sizeof(WORD));
-	dx_buf = (int *)malloc(num_glyphs * sizeof(int));
+	dxy_buf = (int *)malloc(num_glyphs * 2 * sizeof(int));
     }
 
-    for (i = 0; i < num_glyphs; ++i) {
-	output_count++;
+    /* It is vital that dx values for dxy_buf are calculated from the delta of
+     * _logical_ x coordinates (not user x coordinates) or else the sum of all
+     * previous dx values may start to diverge from the current glyph's x
+     * coordinate due to accumulated rounding error. As a result strings could
+     * be painted shorter or longer than expected. */
+
+    user_x = glyphs[0].x;
+    user_y = glyphs[0].y;
+
+    cairo_matrix_transform_point(&device_to_logical,
+                                 &user_x, &user_y);
+
+    logical_x = (int) floor(user_x + 0.5);
+    logical_y = (int) floor(user_y + 0.5);
+
+    start_x = logical_x;
+    start_y = logical_y;
+
+    for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) {
+        glyph_buf[i] = (WORD) glyphs[i].index;
+        if (i == num_glyphs - 1) {
+            dxy_buf[j] = 0;
+            dxy_buf[j+1] = 0;
+        } else {
+            double next_user_x = glyphs[i+1].x;
+            double next_user_y = glyphs[i+1].y;
+            int next_logical_x, next_logical_y;
+
+            cairo_matrix_transform_point(&device_to_logical,
+                                         &next_user_x, &next_user_y);
+
+            next_logical_x = (int) floor(next_user_x + 0.5);
+            next_logical_y = (int) floor(next_user_y + 0.5);
+
+            dxy_buf[j] = (int) floor(((next_logical_x - logical_x) * WIN32_FONT_LOGICAL_SCALE) + 0.5);
+            dxy_buf[j+1] = (int) logical_y - start_y;
+
+            logical_x = next_logical_x;
+            logical_y = next_logical_y;
+        }
+    }
 
-	glyph_buf[i] = (WORD) glyphs[i].index;
-	if (i == num_glyphs - 1)
-	    dx_buf[i] = 0;
-	else
-	    dx_buf[i] = (int) floor(((glyphs[i+1].x - glyphs[i].x) * WIN32_FONT_LOGICAL_SCALE) + 0.5);
-
-	if (i == num_glyphs - 1 || glyphs[i].y != glyphs[i+1].y) {
-	    const int offset = (i - output_count) + 1;
-	    double user_x = glyphs[offset].x;
-	    double user_y = last_y;
-	    int logical_x, logical_y;
-
-	    cairo_matrix_transform_point(&device_to_logical,
-					 &user_x, &user_y);
-
-	    logical_x = (int) floor(user_x + 0.5);
-	    logical_y = (int) floor(user_y + 0.5);
-
-	    win_result = ExtTextOutW(dst->dc,
-				     logical_x,
-				     logical_y,
-				     ETO_GLYPH_INDEX,
-				     NULL,
-				     glyph_buf + offset,
-				     output_count,
-				     dx_buf + offset);
-	    if (!win_result) {
-		_cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)");
-		goto FAIL;
-	    }
-
-	    output_count = 0;
-
-	    if (i < num_glyphs - 1)
-		last_y = glyphs[i+1].y;
-	} else {
-	    last_y = glyphs[i].y;
-	}
+    win_result = ExtTextOutW(dst->dc,
+                             start_x,
+                             start_y,
+                             ETO_GLYPH_INDEX | ETO_PDY,
+                             NULL,
+                             glyph_buf,
+                             num_glyphs,
+                             dxy_buf);
+    if (!win_result) {
+        _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)");
     }
 
-FAIL:
     RestoreDC(dst->dc, -1);
 
     if (glyph_buf != glyph_buf_stack) {
 	free(glyph_buf);
-	free(dx_buf);
+        free(dxy_buf);
     }
     return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED;
 }
 
 #undef STACK_GLYPH_SIZE
 
 /**
  * cairo_win32_surface_create:


More information about the cairo mailing list