From 805b668260c47e6d3d854361fcc53f12bd2a57e1 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 17 Dec 2007 01:14:27 -0500 Subject: [PATCH] [cairo-xlib] Support scale fonts with glyphs of multiple formats (#13479) We maintain three Xrender glyphsets per scaled font, one for each of A1, A8, and ARGB32. This is required to correctly support fonts with bitmaps for some glyphs but not all. --- src/cairo-xlib-surface.c | 179 +++++++++++++++++++++++++++++---------- 1 file changed, 136 insertions(+), 43 deletions(-) diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index f5267e1cd..39d662b4c 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -1011,6 +1011,7 @@ _cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, break; case CAIRO_EXTEND_REFLECT: case CAIRO_EXTEND_PAD: + default: status = CAIRO_INT_STATUS_UNSUPPORTED; } if (status) @@ -2423,11 +2424,22 @@ cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) return surface->height; } -typedef struct _cairo_xlib_surface_font_private { - Display *dpy; +enum { + GLYPHSET_INDEX_ARGB32, + GLYPHSET_INDEX_A8, + GLYPHSET_INDEX_A1, + NUM_GLYPHSETS +}; + +typedef struct _cairo_xlib_font_glyphset_info { GlyphSet glyphset; cairo_format_t format; XRenderPictFormat *xrender_format; +} cairo_xlib_font_glyphset_info_t; + +typedef struct _cairo_xlib_surface_font_private { + Display *dpy; + cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; } cairo_xlib_surface_font_private_t; static void @@ -2445,17 +2457,23 @@ _cairo_xlib_surface_remove_scaled_font (Display *dpy, CAIRO_MUTEX_UNLOCK (scaled_font->mutex); if (font_private != NULL) { - XRenderFreeGlyphSet (font_private->dpy, font_private->glyphset); + int i; + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; + if (glyphset_info->glyphset) { + XRenderFreeGlyphSet (font_private->dpy, glyphset_info->glyphset); + } + } free (font_private); } } static cairo_status_t _cairo_xlib_surface_font_init (Display *dpy, - cairo_scaled_font_t *scaled_font, - cairo_format_t format) + cairo_scaled_font_t *scaled_font) { cairo_xlib_surface_font_private_t *font_private; + int i; font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); if (!font_private) @@ -2470,9 +2488,18 @@ _cairo_xlib_surface_font_init (Display *dpy, font_private->dpy = dpy; - font_private->format = format; - font_private->xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT(dpy, format); - font_private->glyphset = XRenderCreateGlyphSet (dpy, font_private->xrender_format); + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; + switch (i) { + case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break; + case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break; + case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break; + default: ASSERT_NOT_REACHED; break; + } + glyphset_info->xrender_format = NULL; + glyphset_info->glyphset = None; + } + scaled_font->surface_private = font_private; scaled_font->surface_backend = &cairo_xlib_surface_backend; @@ -2491,10 +2518,19 @@ _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) display = _cairo_xlib_display_get (font_private->dpy); if (display != NULL) { - cairo_status_t status = _cairo_xlib_display_queue_resource (display, - XRenderFreeGlyphSet, - font_private->glyphset); - (void) status; /* XXX cannot propagate failure */ + int i; + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; + if (glyphset_info->glyphset) { + cairo_status_t status; + status = _cairo_xlib_display_queue_resource (display, + XRenderFreeGlyphSet, + glyphset_info->glyphset); + (void) status; /* XXX cannot propagate failure */ + } + } + _cairo_xlib_display_destroy (display); } @@ -2513,6 +2549,19 @@ static void _cairo_xlib_render_free_glyphs (Display *dpy, struct _cairo_xlib_ren &arg->glyph_index, 1); } +static cairo_xlib_font_glyphset_info_t * +_cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) +{ + return scaled_glyph->surface_private; +} + +static void +_cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, + cairo_xlib_font_glyphset_info_t *glyphset_info) +{ + scaled_glyph->surface_private = glyphset_info; +} + static void _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font) @@ -2525,7 +2574,7 @@ _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, struct _cairo_xlib_render_free_glyphs *arg = malloc (sizeof (*arg)); if (arg != NULL) { cairo_status_t status; - arg->glyphset = font_private->glyphset; + arg->glyphset = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph)->glyphset; arg->glyph_index = _cairo_scaled_glyph_index (scaled_glyph); status = _cairo_xlib_display_queue_work (display, (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs, @@ -2551,6 +2600,32 @@ _native_byte_order_lsb (void) return *((char *) &x) == 1; } +static cairo_xlib_font_glyphset_info_t * +_cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font, + cairo_format_t format) +{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + cairo_xlib_font_glyphset_info_t *glyphset_info; + int glyphset_index; + + switch (format) { + default: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: glyphset_index = GLYPHSET_INDEX_ARGB32; break; + case CAIRO_FORMAT_A8: glyphset_index = GLYPHSET_INDEX_A8; break; + case CAIRO_FORMAT_A1: glyphset_index = GLYPHSET_INDEX_A1; break; + } + + glyphset_info = &font_private->glyphset_info[glyphset_index]; + + if (glyphset_info->glyphset == None) { + glyphset_info->xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT(font_private->dpy, glyphset_info->format); + glyphset_info->glyphset = XRenderCreateGlyphSet (font_private->dpy, glyphset_info->xrender_format); + } + + return glyphset_info; +} + static cairo_status_t _cairo_xlib_surface_add_glyph (Display *dpy, cairo_scaled_font_t *scaled_font, @@ -2564,6 +2639,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, cairo_scaled_glyph_t *scaled_glyph = *pscaled_glyph; cairo_image_surface_t *glyph_surface = scaled_glyph->surface; cairo_bool_t already_had_glyph_surface; + cairo_xlib_font_glyphset_info_t *glyphset_info; if (!glyph_surface) { @@ -2583,13 +2659,15 @@ _cairo_xlib_surface_add_glyph (Display *dpy, } if (scaled_font->surface_private == NULL) { - status = _cairo_xlib_surface_font_init (dpy, scaled_font, - glyph_surface->format); + status = _cairo_xlib_surface_font_init (dpy, scaled_font); if (status) return status; } font_private = scaled_font->surface_private; + glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font, + glyph_surface->format); + /* If the glyph surface has zero height or width, we create * a clear 1x1 surface, to avoid various X server bugs. */ @@ -2597,7 +2675,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, cairo_t *cr; cairo_surface_t *tmp_surface; - tmp_surface = cairo_image_surface_create (font_private->format, 1, 1); + tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); cr = cairo_create (tmp_surface); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); @@ -2619,11 +2697,11 @@ _cairo_xlib_surface_add_glyph (Display *dpy, * create a temporary surface for the glyph image with the font's * format. */ - if (glyph_surface->format != font_private->format) { + if (glyph_surface->format != glyphset_info->format) { cairo_t *cr; cairo_surface_t *tmp_surface; - tmp_surface = cairo_image_surface_create (font_private->format, + tmp_surface = cairo_image_surface_create (glyphset_info->format, glyph_surface->width, glyph_surface->height); tmp_surface->device_transform = glyph_surface->base.device_transform; @@ -2719,11 +2797,13 @@ _cairo_xlib_surface_add_glyph (Display *dpy, glyph_index = _cairo_scaled_glyph_index (scaled_glyph); - XRenderAddGlyphs (dpy, font_private->glyphset, + XRenderAddGlyphs (dpy, glyphset_info->glyphset, &glyph_index, &(glyph_info), 1, (char *) data, glyph_surface->stride * glyph_surface->height); + _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); + if (data != glyph_surface->data) free (data); @@ -2773,6 +2853,9 @@ typedef struct { } p; } cairo_xlib_glyph_t; +/* compile-time assert that cairo_xlib_glyph_t is the same size as cairo_glyph_t */ +typedef int cairo_xlib_glyph_t_size_assertion [sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t) ? 1 : -1]; + #define GLYPH_INDEX_SKIP ((unsigned long) -1) #define STACK_ELTS_LEN ((int) (CAIRO_STACK_BUFFER_SIZE / sizeof (XGlyphElt8))) @@ -2780,12 +2863,14 @@ static cairo_status_t _cairo_xlib_surface_emit_glyphs_chunk (cairo_xlib_surface_t *dst, cairo_xlib_glyph_t *glyphs, int num_glyphs, - int width, - int num_elts, cairo_scaled_font_t *scaled_font, cairo_operator_t op, cairo_xlib_surface_t *src, - cairo_surface_attributes_t *attributes) + cairo_surface_attributes_t *attributes, + /* info for this chunk */ + int num_elts, + int width, + cairo_xlib_font_glyphset_info_t *glyphset_info) { /* Which XRenderCompositeText function to use */ cairo_xrender_composite_text_func_t composite_text_func; @@ -2805,8 +2890,6 @@ _cairo_xlib_surface_emit_glyphs_chunk (cairo_xlib_surface_t *dst, int n; /* Num output glyphs in current element */ int j; /* Num output glyphs so far */ - cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; - switch (width) { case 1: /* don't cast the 8-variant, to catch possible mismatches */ @@ -2851,7 +2934,7 @@ _cairo_xlib_surface_emit_glyphs_chunk (cairo_xlib_surface_t *dst, n = 0; } elts[nelt].chars = char8 + size * j; - elts[nelt].glyphset = font_private->glyphset; + elts[nelt].glyphset = glyphset_info->glyphset; elts[nelt].xOff = glyphs[i].p.i.x; elts[nelt].yOff = glyphs[i].p.i.y; } @@ -2877,7 +2960,7 @@ _cairo_xlib_surface_emit_glyphs_chunk (cairo_xlib_surface_t *dst, _render_operator (op), src->src_picture, dst->dst_picture, - font_private->xrender_format, + glyphset_info->xrender_format, attributes->x_offset + elts[0].xOff, attributes->y_offset + elts[0].yOff, elts[0].xOff, elts[0].yOff, @@ -2904,6 +2987,7 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph; cairo_fixed_t x = 0, y = 0; + cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; unsigned long max_index = 0; int width = 1; @@ -2954,6 +3038,19 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, continue; } + /* Send unsent glyphs to the server */ + if (scaled_glyph->surface_private == NULL) { + status = _cairo_xlib_surface_add_glyph (dst->dpy, + scaled_font, + &scaled_glyph); + if (status) + return status; + } + + this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); + if (!glyphset_info) + glyphset_info = this_glyphset_info; + old_width = width; /* Update max glyph index */ @@ -2969,11 +3066,17 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, /* If we will pass the max request size by adding this glyph, * flush current glyphs. Note that we account for a - * possible element being added below. */ - if (request_size + width > max_request_size - sz_xGlyphElt) { + * possible element being added below. + * + * Also flush if changing glyphsets, as Xrender limits one mask + * format per request, so we can either break up, or use a + * wide-enough mask format. We do the former. + */ + if (request_size + width > max_request_size - sz_xGlyphElt || + (this_glyphset_info != glyphset_info)) { status = _cairo_xlib_surface_emit_glyphs_chunk (dst, glyphs, i, - old_width, num_elts, - scaled_font, op, src, attributes); + scaled_font, op, src, attributes, + num_elts, old_width, glyphset_info); if (status != CAIRO_STATUS_SUCCESS) return status; @@ -2986,7 +3089,7 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, num_elts = 0; num_out_glyphs = 0; x = y = 0; - + glyphset_info = this_glyphset_info; } /* Convert absolute glyph position to relative-to-current-point @@ -3001,16 +3104,6 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, request_size += sz_xGlyphElt; } - /* Send unsent glyphs to the server */ - if (scaled_glyph->surface_private == NULL) { - status = _cairo_xlib_surface_add_glyph (dst->dpy, - scaled_font, - &scaled_glyph); - if (status) - return status; - scaled_glyph->surface_private = (void *) 1; - } - /* adjust current-position */ x = this_x + scaled_glyph->x_advance; y = this_y + scaled_glyph->y_advance; @@ -3021,8 +3114,8 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, if (num_elts) status = _cairo_xlib_surface_emit_glyphs_chunk (dst, glyphs, num_glyphs, - width, num_elts, - scaled_font, op, src, attributes); + scaled_font, op, src, attributes, + num_elts, width, glyphset_info); return status; }