mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-05-10 06:02:10 -05:00
GTK: use a persistent pixmap for rendering X fonts under Cairo
Instead of creating a new pixmap for each text-rendering operation, we now stash one (along with the various Cairo structures associated with it) in the xfont_individual structure. This saves the 20% or so of Xwayland's CPU time that was being spent on CreatePixmap requests.
This commit is contained in:
parent
19353dca85
commit
649c8155e7
146
unix/unifont.c
146
unix/unifont.c
@ -156,7 +156,11 @@ typedef struct x11font_individual {
|
|||||||
bool allocated;
|
bool allocated;
|
||||||
|
|
||||||
#ifdef DRAW_TEXT_CAIRO
|
#ifdef DRAW_TEXT_CAIRO
|
||||||
XRenderPictFormat *pictformat;
|
/* Cached pixmap etc for drawing into. */
|
||||||
|
Pixmap pixmap;
|
||||||
|
int pixwidth, pixheight, pixdepth;
|
||||||
|
cairo_surface_t *surface;
|
||||||
|
cairo_pattern_t *pattern;
|
||||||
GC gc;
|
GC gc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -513,6 +517,11 @@ static unifont *x11font_create(GtkWidget *widget, const char *name,
|
|||||||
xfont->fonts[i].xfs = NULL;
|
xfont->fonts[i].xfs = NULL;
|
||||||
xfont->fonts[i].allocated = false;
|
xfont->fonts[i].allocated = false;
|
||||||
#ifdef DRAW_TEXT_CAIRO
|
#ifdef DRAW_TEXT_CAIRO
|
||||||
|
xfont->fonts[i].pixmap = None;
|
||||||
|
xfont->fonts[i].pixwidth = xfont->fonts[i].pixheight = 0;
|
||||||
|
xfont->fonts[i].pixdepth = 0;
|
||||||
|
xfont->fonts[i].surface = NULL;
|
||||||
|
xfont->fonts[i].pattern = NULL;
|
||||||
xfont->fonts[i].gc = None;
|
xfont->fonts[i].gc = None;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -534,6 +543,12 @@ static void x11font_destroy(unifont *font)
|
|||||||
#ifdef DRAW_TEXT_CAIRO
|
#ifdef DRAW_TEXT_CAIRO
|
||||||
if (xfont->fonts[i].gc != None)
|
if (xfont->fonts[i].gc != None)
|
||||||
XFreeGC(disp, xfont->fonts[i].gc);
|
XFreeGC(disp, xfont->fonts[i].gc);
|
||||||
|
if (xfont->fonts[i].pattern != NULL)
|
||||||
|
cairo_pattern_destroy(xfont->fonts[i].pattern);
|
||||||
|
if (xfont->fonts[i].surface != NULL)
|
||||||
|
cairo_surface_destroy(xfont->fonts[i].surface);
|
||||||
|
if (xfont->fonts[i].pixmap != None)
|
||||||
|
XFreePixmap(disp, xfont->fonts[i].pixmap);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
sfree(xfont);
|
sfree(xfont);
|
||||||
@ -610,96 +625,107 @@ static void x11font_gdk_draw(unifont_drawctx *ctx, x11font_individual *xfi,
|
|||||||
static void x11font_cairo_setup(
|
static void x11font_cairo_setup(
|
||||||
unifont_drawctx *ctx, x11font_individual *xfi, Display *disp)
|
unifont_drawctx *ctx, x11font_individual *xfi, Display *disp)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* To render X fonts under Cairo, we use the usual X font rendering
|
|
||||||
* requests to draw text into a pixmap, make a Cairo surface out
|
|
||||||
* of it, and then use that as a mask to paint the current colour
|
|
||||||
* into the terminal surface. This means that colours are
|
|
||||||
* entirely in the hands of Cairo and we don't have to think about
|
|
||||||
* X colourmaps. But we do need to be able to create that pixmap.
|
|
||||||
*
|
|
||||||
* All X servers are required to support 1bpp pixmaps, and Cairo
|
|
||||||
* can always use one of them as a mask. But on 2025's X servers,
|
|
||||||
* 1bpp font rendering is unaccelerated and hence much slower than
|
|
||||||
* on deeper drawables. So we find out if we can make an 8-bit
|
|
||||||
* alpha-only surface, and only fall back to a 1bpp pixmap if that
|
|
||||||
* fails.
|
|
||||||
*
|
|
||||||
* XRenderFindStandardFormat() will return NULL if the X Rendering
|
|
||||||
* Extension is missing or unusable, so we don't need to check
|
|
||||||
* that in advance.
|
|
||||||
*/
|
|
||||||
xfi->pictformat = XRenderFindStandardFormat(disp, PictStandardA8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void x11font_cairo_ensure_pixmap(
|
||||||
* Creating an X GC requires a Drawable, so this gets deferred until
|
unifont_drawctx *ctx, x11font_individual *xfi, Display *disp,
|
||||||
* we first need to draw something, and hence have something to draw
|
int width, int height)
|
||||||
* on.
|
|
||||||
*/
|
|
||||||
static void x11font_cairo_init_gc(x11font_individual *xfi, Display *disp,
|
|
||||||
Pixmap pixmap)
|
|
||||||
{
|
{
|
||||||
if (xfi->gc == None) {
|
Pixmap newpixmap;
|
||||||
|
XRenderPictFormat *pictformat = NULL;
|
||||||
|
|
||||||
|
if (xfi->pixmap != None
|
||||||
|
&& xfi->pixwidth >= width && xfi->pixheight >= height) return;
|
||||||
|
if (xfi->pixmap == None) {
|
||||||
|
/*
|
||||||
|
* To render X fonts under Cairo, we use the usual X font
|
||||||
|
* rendering requests to draw text into a pixmap that's also a
|
||||||
|
* Cairo surface and then use that as a mask to paint the
|
||||||
|
* current colour into the terminal surface. This means that
|
||||||
|
* colours are entirely in the hands of Cairo and we don't
|
||||||
|
* have to think about X colourmaps. But we do need to be
|
||||||
|
* able to create that pixmap.
|
||||||
|
*
|
||||||
|
* All X servers are required to support 1bpp pixmaps, and
|
||||||
|
* Cairo can always use one of them as a mask. But on 2025's
|
||||||
|
* X servers, 1bpp font rendering is unaccelerated and hence
|
||||||
|
* much slower than on deeper drawables. So we find out if we
|
||||||
|
* can make an 8-bit alpha-only surface, and only fall back to
|
||||||
|
* a 1bpp pixmap if that fails.
|
||||||
|
*
|
||||||
|
* XRenderFindStandardFormat() will return NULL if the X
|
||||||
|
* Rendering Extension is missing or unusable, so we don't
|
||||||
|
* need to check that in advance.
|
||||||
|
*/
|
||||||
|
pictformat = XRenderFindStandardFormat(disp, PictStandardA8);
|
||||||
|
if (pictformat != NULL)
|
||||||
|
xfi->pixdepth = 8;
|
||||||
|
else
|
||||||
|
xfi->pixdepth = 1;
|
||||||
|
}
|
||||||
|
if (width < xfi->pixwidth) width = xfi->pixwidth;
|
||||||
|
if (height < xfi->pixheight) height = xfi->pixheight;
|
||||||
|
newpixmap = XCreatePixmap(
|
||||||
|
disp, GDK_DRAWABLE_XID(gtk_widget_get_window(ctx->u.cairo.widget)),
|
||||||
|
width, height, xfi->pixdepth);
|
||||||
|
if (xfi->pixmap != None) {
|
||||||
|
assert(xfi->surface != NULL);
|
||||||
|
cairo_xlib_surface_set_drawable(xfi->surface, newpixmap,
|
||||||
|
width, height);
|
||||||
|
XFreePixmap(disp, xfi->pixmap);
|
||||||
|
} else {
|
||||||
|
Screen *widgetscr =
|
||||||
|
GDK_SCREEN_XSCREEN(gtk_widget_get_screen(ctx->u.cairo.widget));
|
||||||
|
if (pictformat != NULL)
|
||||||
|
xfi->surface = cairo_xlib_surface_create_with_xrender_format(
|
||||||
|
disp, newpixmap, widgetscr, pictformat, width, height);
|
||||||
|
else
|
||||||
|
xfi->surface = cairo_xlib_surface_create_for_bitmap(
|
||||||
|
disp, newpixmap, widgetscr, width, height);
|
||||||
|
xfi->pattern = cairo_pattern_create_for_surface(xfi->surface);
|
||||||
|
/* We really don't want bilinear interpolation of bitmap fonts. */
|
||||||
|
cairo_pattern_set_filter(xfi->pattern, CAIRO_FILTER_NEAREST);
|
||||||
XGCValues gcvals = {
|
XGCValues gcvals = {
|
||||||
.function = GXclear,
|
.function = GXclear,
|
||||||
.foreground = 0xffffffff,
|
.foreground = 0xffffffff,
|
||||||
.background = 0,
|
.background = 0,
|
||||||
.font = xfi->xfs->fid,
|
.font = xfi->xfs->fid,
|
||||||
};
|
};
|
||||||
|
xfi->gc = XCreateGC(disp, newpixmap,
|
||||||
xfi->gc = XCreateGC(disp, pixmap,
|
|
||||||
GCFunction | GCForeground | GCBackground | GCFont,
|
GCFunction | GCForeground | GCBackground | GCFont,
|
||||||
&gcvals);
|
&gcvals);
|
||||||
}
|
}
|
||||||
|
XFillRectangle(disp, newpixmap, xfi->gc, 0, 0, width, height);
|
||||||
|
xfi->pixmap = newpixmap;
|
||||||
|
xfi->pixwidth = width;
|
||||||
|
xfi->pixheight = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void x11font_cairo_draw(
|
static void x11font_cairo_draw(
|
||||||
unifont_drawctx *ctx, x11font_individual *xfi, Display *disp,
|
unifont_drawctx *ctx, x11font_individual *xfi, Display *disp,
|
||||||
int x, int y, const XChar2b *string, int start, int length)
|
int x, int y, const XChar2b *string, int start, int length)
|
||||||
{
|
{
|
||||||
GdkWindow *widgetwin = gtk_widget_get_window(ctx->u.cairo.widget);
|
|
||||||
int widgetscr = GDK_SCREEN_XNUMBER(gdk_window_get_screen(widgetwin));
|
|
||||||
XCharStruct bounds;
|
XCharStruct bounds;
|
||||||
int pixwidth, pixheight, direction, font_ascent, font_descent;
|
int pixwidth, pixheight, direction, font_ascent, font_descent;
|
||||||
Pixmap pixmap;
|
|
||||||
|
|
||||||
XTextExtents16(xfi->xfs, string + start, length,
|
XTextExtents16(xfi->xfs, string + start, length,
|
||||||
&direction, &font_ascent, &font_descent, &bounds);
|
&direction, &font_ascent, &font_descent, &bounds);
|
||||||
pixwidth = bounds.rbearing - bounds.lbearing;
|
pixwidth = bounds.rbearing - bounds.lbearing;
|
||||||
pixheight = bounds.ascent + bounds.descent;
|
pixheight = bounds.ascent + bounds.descent;
|
||||||
if (pixwidth > 0 && pixheight > 0) {
|
if (pixwidth > 0 && pixheight > 0) {
|
||||||
cairo_surface_t *surface;
|
x11font_cairo_ensure_pixmap(ctx, xfi, disp, pixwidth, pixheight);
|
||||||
if (xfi->pictformat != NULL) {
|
XDrawImageString16(disp, xfi->pixmap, xfi->gc,
|
||||||
pixmap = XCreatePixmap(disp, GDK_DRAWABLE_XID(widgetwin),
|
|
||||||
pixwidth, pixheight, 8);
|
|
||||||
surface = cairo_xlib_surface_create_with_xrender_format(
|
|
||||||
disp, pixmap, ScreenOfDisplay(disp, widgetscr),
|
|
||||||
xfi->pictformat, pixwidth, pixheight);
|
|
||||||
} else {
|
|
||||||
pixmap = XCreatePixmap(disp, GDK_DRAWABLE_XID(widgetwin),
|
|
||||||
pixwidth, pixheight, 1);
|
|
||||||
surface = cairo_xlib_surface_create_for_bitmap(
|
|
||||||
disp, pixmap, ScreenOfDisplay(disp, widgetscr),
|
|
||||||
pixwidth, pixheight);
|
|
||||||
}
|
|
||||||
x11font_cairo_init_gc(xfi, disp, pixmap);
|
|
||||||
XFillRectangle(disp, pixmap, xfi->gc, 0, 0, pixwidth, pixheight);
|
|
||||||
XDrawImageString16(disp, pixmap, xfi->gc,
|
|
||||||
-bounds.lbearing, bounds.ascent,
|
-bounds.lbearing, bounds.ascent,
|
||||||
string+start, length);
|
string+start, length);
|
||||||
cairo_surface_mark_dirty(surface);
|
cairo_surface_mark_dirty(xfi->surface);
|
||||||
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_t xfrm;
|
||||||
cairo_matrix_init_translate(&xfrm,
|
cairo_matrix_init_translate(&xfrm,
|
||||||
-x - bounds.lbearing, -y + bounds.ascent);
|
-x - bounds.lbearing, -y + bounds.ascent);
|
||||||
cairo_pattern_set_matrix(pattern, &xfrm);
|
cairo_pattern_set_matrix(xfi->pattern, &xfrm);
|
||||||
cairo_mask(ctx->u.cairo.cr, pattern);
|
cairo_mask(ctx->u.cairo.cr, xfi->pattern);
|
||||||
cairo_pattern_destroy(pattern);
|
/* Clear the part of the pixmap that we used. */
|
||||||
cairo_surface_destroy(surface);
|
XFillRectangle(disp, xfi->pixmap, xfi->gc, 0, 0, pixwidth, pixheight);
|
||||||
XFreePixmap(disp, pixmap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* DRAW_TEXT_CAIRO */
|
#endif /* DRAW_TEXT_CAIRO */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user