render: cache glyph masks

Cache rendered glyph masks for each font combo. When rendering text,
use these when possible (standard ASCII).

For now, use the cached glyph as a mask. This allows colors to just
work. It would be faster to cache a completely pre-rendered image, but
then we would need one image for each background/foreground combo.
This commit is contained in:
Daniel Eklöf 2019-07-02 12:22:05 +02:00
parent e6d9eb7c9e
commit 3d8e140a28
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F

140
main.c
View file

@ -45,10 +45,16 @@ struct wayland {
struct xdg_toplevel *xdg_toplevel; struct xdg_toplevel *xdg_toplevel;
}; };
struct glyph_cache {
cairo_scaled_font_t *font;
cairo_surface_t *surf[256];
cairo_pattern_t *cache[256];
};
struct context { struct context {
bool quit; bool quit;
cairo_scaled_font_t *fonts[8]; struct glyph_cache fonts[4];
cairo_font_extents_t fextents; cairo_font_extents_t fextents;
int width; int width;
@ -61,7 +67,6 @@ struct context {
bool frame_is_scheduled; bool frame_is_scheduled;
}; };
static void frame_callback( static void frame_callback(
void *data, struct wl_callback *wl_callback, uint32_t callback_data); void *data, struct wl_callback *wl_callback, uint32_t callback_data);
@ -69,11 +74,11 @@ static const struct wl_callback_listener frame_listener = {
.done = &frame_callback, .done = &frame_callback,
}; };
static cairo_scaled_font_t * static struct glyph_cache *
attrs_to_font(struct context *c, const struct attributes *attrs) attrs_to_font(struct context *c, const struct attributes *attrs)
{ {
int idx = attrs->italic << 1 | attrs->bold; int idx = attrs->italic << 1 | attrs->bold;
return c->fonts[idx]; return &c->fonts[idx];
} }
static void static void
@ -138,15 +143,9 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d
background = swap; background = swap;
} }
//LOG_DBG("cell %dx%d dirty: c=0x%02x (%c)", /* Background */
// row, col, cell->c[0], cell->c[0]);
cairo_scaled_font_t *font = attrs_to_font(c, &cell->attrs);
cairo_set_scaled_font(buf->cairo, font);
cairo_set_source_rgba( cairo_set_source_rgba(
buf->cairo, background.r, background.g, background.b, background.a); buf->cairo, background.r, background.g, background.b, background.a);
/* Background */
cairo_rectangle(buf->cairo, x, y, width, height); cairo_rectangle(buf->cairo, x, y, width, height);
cairo_fill(buf->cairo); cairo_fill(buf->cairo);
@ -156,24 +155,45 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d
if (cell->attrs.conceal) if (cell->attrs.conceal)
continue; continue;
cairo_glyph_t *glyphs = NULL; struct glyph_cache *cache = attrs_to_font(c, &cell->attrs);
int num_glyphs = 0;
cairo_status_t status = cairo_scaled_font_text_to_glyphs(
font, x, y + c->fextents.ascent,
cell->c, strlen(cell->c), &glyphs, &num_glyphs,
NULL, NULL, NULL);
if (status != CAIRO_STATUS_SUCCESS) {
if (glyphs != NULL)
cairo_glyph_free(glyphs);
continue;
}
cairo_set_source_rgba( cairo_set_source_rgba(
buf->cairo, foreground.r, foreground.g, foreground.b, foreground.a); buf->cairo, foreground.r, foreground.g, foreground.b, foreground.a);
cairo_show_glyphs(buf->cairo, glyphs, num_glyphs);
cairo_glyph_free(glyphs); if (strlen(cell->c) == 1 && cache->cache[(int)cell->c[0]] != NULL) {
cairo_matrix_t matrix;
cairo_matrix_init_translate(&matrix, -x, -y);
cairo_pattern_t *pat = cache->cache[(int)cell->c[0]];
cairo_pattern_set_matrix(pat, &matrix);
#if 1
cairo_pattern_set_matrix(pat, &matrix);
cairo_mask(buf->cairo, pat);
#else /* TODO: blit image instead - but doesn't (yet) handle colors */
cairo_set_source(buf->cairo, pat);
cairo_rectangle(buf->cairo, x, y, width, height);
cairo_fill(buf->cairo);
#endif
} else {
cairo_glyph_t *glyphs = NULL;
int num_glyphs = 0;
cairo_status_t status = cairo_scaled_font_text_to_glyphs(
cache->font, x, y + c->fextents.ascent,
cell->c, strlen(cell->c), &glyphs, &num_glyphs,
NULL, NULL, NULL);
if (status != CAIRO_STATUS_SUCCESS) {
if (glyphs != NULL)
cairo_glyph_free(glyphs);
continue;
}
cairo_set_scaled_font(buf->cairo, cache->font);
cairo_show_glyphs(buf->cairo, glyphs, num_glyphs);
cairo_glyph_free(glyphs);
}
} }
wl_surface_damage_buffer( wl_surface_damage_buffer(
@ -453,8 +473,6 @@ resize(struct context *c, int width, int height)
const size_t normal_old_size = c->term.normal.size; const size_t normal_old_size = c->term.normal.size;
const size_t alt_old_size = c->term.alt.size; const size_t alt_old_size = c->term.alt.size;
c->term.cell_width = (int)ceil(c->fextents.max_x_advance);
c->term.cell_height = (int)ceil(c->fextents.height);
c->term.cols = c->width / c->term.cell_width; c->term.cols = c->width / c->term.cell_width;
c->term.rows = c->height / c->term.cell_height; c->term.rows = c->height / c->term.cell_height;
@ -802,30 +820,70 @@ main(int argc, const char *const *argv)
thrd_create(&keyboard_repeater_id, &keyboard_repeater, &c.term); thrd_create(&keyboard_repeater_id, &keyboard_repeater, &c.term);
const char *font_name = "Dina:pixelsize=12"; const char *font_name = "Dina:pixelsize=12";
c.fonts[0] = font_from_name(font_name); c.fonts[0].font = font_from_name(font_name);
if (c.fonts[0] == NULL) if (c.fonts[0].font == NULL)
goto out; goto out;
{ {
char fname[1024]; char fname[1024];
snprintf(fname, sizeof(fname), "%s:style=bold", font_name); snprintf(fname, sizeof(fname), "%s:style=bold", font_name);
c.fonts[1] = font_from_name(fname); c.fonts[1].font = font_from_name(fname);
snprintf(fname, sizeof(fname), "%s:style=italic", font_name); snprintf(fname, sizeof(fname), "%s:style=italic", font_name);
c.fonts[2] = font_from_name(fname); c.fonts[2].font = font_from_name(fname);
snprintf(fname, sizeof(fname), "%s:style=bold italic", font_name); snprintf(fname, sizeof(fname), "%s:style=bold italic", font_name);
c.fonts[3] = font_from_name(fname); c.fonts[3].font = font_from_name(fname);
/* TODO; underline */
} }
cairo_scaled_font_extents(c.fonts[0], &c.fextents); cairo_scaled_font_extents(c.fonts[0].font, &c.fextents);
c.term.cell_width = (int)ceil(c.fextents.max_x_advance);
c.term.cell_height = (int)ceil(c.fextents.height);
LOG_DBG("font: height: %.2f, x-advance: %.2f", LOG_DBG("font: height: %.2f, x-advance: %.2f",
c.fextents.height, c.fextents.max_x_advance); c.fextents.height, c.fextents.max_x_advance);
assert(c.fextents.max_y_advance == 0); assert(c.fextents.max_y_advance == 0);
for (size_t i; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) {
cairo_scaled_font_t *font = c.fonts[i].font;
for (size_t j = 0; j < 256; j++) {
const char text[2] = {(char)j, '\0'};
cairo_glyph_t *glyphs = NULL;
int num_glyphs = 0;
cairo_status_t status = cairo_scaled_font_text_to_glyphs(
font, 0, 0 + c.fextents.ascent,
text, 1, &glyphs, &num_glyphs, NULL, NULL, NULL);
if (status != CAIRO_STATUS_SUCCESS || num_glyphs != 1) {
if (glyphs != NULL)
cairo_glyph_free(glyphs);
continue;
}
cairo_surface_t *surf = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, c.term.cell_width, c.term.cell_height);
cairo_t *ca = cairo_create(surf);
cairo_set_scaled_font(ca, font);
cairo_set_source_rgba(ca, 1.0, 1.0, 1.0, 1.0);
cairo_show_glyphs(ca, glyphs, num_glyphs);
cairo_glyph_free(glyphs);
cairo_destroy(ca);
assert(surf != NULL);
assert(cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS);
c.fonts[i].surf[j] = surf;
cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf);
assert(cairo_pattern_status(pat) == CAIRO_STATUS_SUCCESS);
c.fonts[i].cache[j] = pat;
}
}
if (c.term.ptmx == -1) { if (c.term.ptmx == -1) {
LOG_ERRNO("failed to open pseudo terminal"); LOG_ERRNO("failed to open pseudo terminal");
goto out; goto out;
@ -988,8 +1046,14 @@ out:
free(c.term.alt.cells); free(c.term.alt.cells);
for (size_t i = 0; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) { for (size_t i = 0; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) {
if (c.fonts[i] != NULL) struct glyph_cache *fcache = &c.fonts[i];
cairo_scaled_font_destroy(c.fonts[i]); if (fcache->font != NULL)
cairo_scaled_font_destroy(fcache->font);
for (size_t i = 0; i < sizeof(fcache->cache) / sizeof(fcache->cache[0]); i++) {
if (fcache->cache[i] != NULL)
cairo_pattern_destroy(fcache->cache[i]);
}
} }
if (c.term.ptmx != -1) if (c.term.ptmx != -1)