1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-05-10 06:02:10 -05:00

GTK: purely server-side X bitmap font rendering with Cairo

This is a fairly radical change of how X bitmap fonts are handled when
using Cairo for rendering.  Before, we would download each glyph to the
client on first use and then composite those glyphs into the terminal's
backing surface. This worked pretty well when we were keeping an image
of the whole screen on the client anyway, but once I'd pushed all the
other Cairo rendering onto the X server, it meant that the character
bitmaps had to be repeatedly pushed to the X server.

The new arrangement just renders each string into a temporary Pixmap
using the usual X text-drawing calls and then asks Cairo to paste it
into the main backing Pixmap.  It's tempting to draw the text straight
into the backing Pixmap, but that would require dealing directly with
X colour management.  This way, we get to leave colours in the hands
of Cairo (and hence the Render extension).

There are still fragments of the old system around.  Those should go
in the next commit.
This commit is contained in:
Ben Harris 2025-04-25 20:12:12 +01:00
parent 08bc670304
commit 9bd1b234a0

View File

@ -685,70 +685,41 @@ static void x11font_cairo_setup(
} }
} }
static void x11font_cairo_cache_glyph(
Display *disp, x11font_individual *xfi, int glyphindex)
{
if (xfi->nglyphs <= glyphindex) {
/* Round up to the next multiple of 256 on the general
* principle that Unicode characters come in contiguous blocks
* often used together */
int old_nglyphs = xfi->nglyphs;
xfi->nglyphs = (glyphindex + 0x100) & ~0xFF;
xfi->glyphcache = sresize(xfi->glyphcache, xfi->nglyphs,
struct cairo_cached_glyph);
while (old_nglyphs < xfi->nglyphs) {
xfi->glyphcache[old_nglyphs].surface = NULL;
old_nglyphs++;
}
}
cairo_surface_mark_dirty(xfi->pixmap_surface);
xfi->glyphcache[glyphindex].surface = cairo_image_surface_create(
CAIRO_FORMAT_A1, xfi->pixwidth, xfi->pixheight);
cairo_t *cr = cairo_create(xfi->glyphcache[glyphindex].surface);
cairo_set_source_surface(cr, xfi->pixmap_surface, 0, 0);
cairo_paint(cr);
cairo_destroy(cr);
}
static void x11font_cairo_draw_glyph(unifont_drawctx *ctx,
x11font_individual *xfi, int x, int y,
int glyphindex)
{
if (xfi->glyphcache[glyphindex].surface) {
cairo_pattern_t *glyph_pattern = cairo_pattern_create_for_surface(
xfi->glyphcache[glyphindex].surface);
cairo_matrix_t xfrm;
/* We really don't want bilinear interpolation of bitmap fonts. */
cairo_pattern_set_filter(glyph_pattern, CAIRO_FILTER_NEAREST);
cairo_matrix_init_translate(&xfrm, -(x - xfi->pixoriginx),
-(y - xfi->pixoriginy));
cairo_pattern_set_matrix(glyph_pattern, &xfrm);
cairo_mask(ctx->u.cairo.cr, glyph_pattern);
cairo_pattern_destroy(glyph_pattern);
}
}
static void x11font_cairo_draw_16( static void x11font_cairo_draw_16(
unifont_drawctx *ctx, x11font_individual *xfi, Display *disp, unifont_drawctx *ctx, x11font_individual *xfi, Display *disp,
int x, int y, const void *vstring, int start, int length) int x, int y, const void *vstring, int start, int length)
{ {
const XChar2b *string = (const XChar2b *)vstring + start; const XChar2b *string = (const XChar2b *)vstring + start;
int i; GdkWindow *widgetwin = gtk_widget_get_window(ctx->u.cairo.widget);
for (i = 0; i < length; i++) { int widgetscr = GDK_SCREEN_XNUMBER(gdk_window_get_screen(widgetwin));
if (x11_font_has_glyph(xfi->xfs, string[i].byte1, string[i].byte2)) { XCharStruct bounds;
int glyphindex = (256 * (unsigned char)string[i].byte1 + int pixwidth, pixheight, direction, font_ascent, font_descent;
(unsigned char)string[i].byte2); Pixmap pixmap;
if (glyphindex >= xfi->nglyphs ||
!xfi->glyphcache[glyphindex].surface) { XTextExtents16(xfi->xfs, string + start, length,
XDrawImageString16(disp, xfi->pixmap, xfi->gc, &direction, &font_ascent, &font_descent, &bounds);
xfi->pixoriginx, xfi->pixoriginy, pixwidth = bounds.rbearing - bounds.lbearing;
string+i, 1); pixheight = bounds.ascent + bounds.descent;
x11font_cairo_cache_glyph(disp, xfi, glyphindex); if (pixwidth > 0 && pixheight > 0) {
} pixmap = XCreatePixmap(disp, GDK_DRAWABLE_XID(widgetwin),
x11font_cairo_draw_glyph(ctx, xfi, x, y, glyphindex); pixwidth, pixheight, 1);
x += XTextWidth16(xfi->xfs, string+i, 1); XDrawImageString16(disp, pixmap, xfi->gc,
} -bounds.lbearing, bounds.ascent,
string+start, length);
cairo_surface_t *surface = cairo_xlib_surface_create_for_bitmap(
disp, pixmap, ScreenOfDisplay(disp, widgetscr),
pixwidth, pixheight);
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(surface);
/* We really don't want bilinear interpolation of bitmap fonts. */
cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
cairo_matrix_t xfrm;
cairo_matrix_init_translate(&xfrm,
-x - bounds.lbearing, -y + bounds.ascent);
cairo_pattern_set_matrix(pattern, &xfrm);
cairo_mask(ctx->u.cairo.cr, pattern);
cairo_pattern_destroy(pattern);
cairo_surface_destroy(surface);
XFreePixmap(disp, pixmap);
} }
} }
@ -757,20 +728,36 @@ static void x11font_cairo_draw_8(
int x, int y, const void *vstring, int start, int length) int x, int y, const void *vstring, int start, int length)
{ {
const char *string = (const char *)vstring + start; const char *string = (const char *)vstring + start;
int i; GdkWindow *widgetwin = gtk_widget_get_window(ctx->u.cairo.widget);
for (i = 0; i < length; i++) { int widgetscr = GDK_SCREEN_XNUMBER(gdk_window_get_screen(widgetwin));
if (x11_font_has_glyph(xfi->xfs, 0, string[i])) { XCharStruct bounds;
int glyphindex = (unsigned char)string[i]; int pixwidth, pixheight, direction, font_ascent, font_descent;
if (glyphindex >= xfi->nglyphs || Pixmap pixmap;
!xfi->glyphcache[glyphindex].surface) {
XDrawImageString(disp, xfi->pixmap, xfi->gc, XTextExtents(xfi->xfs, string + start, length,
xfi->pixoriginx, xfi->pixoriginy, &direction, &font_ascent, &font_descent, &bounds);
string+i, 1); pixwidth = bounds.rbearing - bounds.lbearing;
x11font_cairo_cache_glyph(disp, xfi, glyphindex); pixheight = bounds.ascent + bounds.descent;
} if (pixwidth > 0 && pixheight > 0) {
x11font_cairo_draw_glyph(ctx, xfi, x, y, glyphindex); pixmap = XCreatePixmap(disp, GDK_DRAWABLE_XID(widgetwin),
x += XTextWidth(xfi->xfs, string+i, 1); pixwidth, pixheight, 1);
} XDrawImageString(disp, pixmap, xfi->gc,
-bounds.lbearing, bounds.ascent,
string+start, length);
cairo_surface_t *surface = cairo_xlib_surface_create_for_bitmap(
disp, pixmap, ScreenOfDisplay(disp, widgetscr),
pixwidth, pixheight);
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(surface);
/* We really don't want bilinear interpolation of bitmap fonts. */
cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
cairo_matrix_t xfrm;
cairo_matrix_init_translate(&xfrm,
-x - bounds.lbearing, -y + bounds.ascent);
cairo_pattern_set_matrix(pattern, &xfrm);
cairo_mask(ctx->u.cairo.cr, pattern);
cairo_pattern_destroy(pattern);
cairo_surface_destroy(surface);
XFreePixmap(disp, pixmap);
} }
} }
#endif /* DRAW_TEXT_CAIRO */ #endif /* DRAW_TEXT_CAIRO */