diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2013-10-22 14:53:58 +0200 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2013-10-22 14:53:58 +0200 |
commit | 9e901f72a6f05e7c98dc2cf46432017ed2d31d4a (patch) | |
tree | 3a6b8e4c4832b6ebb1c5694fd98f3c9c09a4e42e | |
parent | 325b4ca59b63ed42ca3875f788c83d69e8f0ecd1 (diff) |
Add proper software renderer
cairo-rendering is horribly slow. Add a proper software renderer via
shadow buffers so we get proper rendering speed.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r-- | src/wlt_render.c | 287 | ||||
-rw-r--r-- | src/wlterm.c | 23 | ||||
-rw-r--r-- | src/wlterm.h | 9 |
3 files changed, 271 insertions, 48 deletions
diff --git a/src/wlt_render.c b/src/wlt_render.c index 3306c18..40ccd68 100644 --- a/src/wlt_render.c +++ b/src/wlt_render.c @@ -31,65 +31,268 @@ #include <errno.h> #include <libtsm.h> #include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "wlterm.h" -static int wlt_render_cell(struct tsm_screen *screen, uint32_t id, - const uint32_t *ch, size_t len, unsigned int cwidth, - unsigned int posx, unsigned int posy, - const struct tsm_screen_attr *attr, void *data) +struct wlt_renderer { + unsigned int width; + unsigned int height; + int stride; + uint8_t *data; + cairo_surface_t *surface; +}; + +static int wlt_renderer_realloc(struct wlt_renderer *rend, unsigned int width, + unsigned int height) { - struct wlt_draw_ctx *ctx = data; - cairo_t *cr = ctx->cr; - struct wlt_glyph *glyph; - int r; + int stride; + uint8_t *data; + cairo_surface_t *surface; + + stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + data = malloc(abs(stride * height)); + if (!data) + return -ENOMEM; - cairo_rectangle(cr, posx * ctx->cell_width, posy * ctx->cell_height, - ctx->cell_width, ctx->cell_height); - - /* draw bg */ - cairo_set_source_rgb(cr, - attr->br / (double)256, - attr->bg / (double)256, - attr->bb / (double)256); - cairo_fill(cr); - - /* draw fg */ - if (len) { - r = wlt_face_render(ctx->face, &glyph, id, ch, len, cwidth); - if (r < 0) - return r; - - cairo_set_source_rgb(cr, - attr->fr / (double)256, - attr->fg / (double)256, - attr->fb / (double)256); - cairo_mask_surface(cr, glyph->cr_surface, - posx * glyph->width, - posy * glyph->height); + surface = cairo_image_surface_create_for_data(data, + CAIRO_FORMAT_ARGB32, + width, + height, + stride); + if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(surface); + free(data); + return -ENOMEM; } + if (rend->data) { + cairo_surface_destroy(rend->surface); + free(rend->data); + } + + rend->width = width; + rend->height = height; + rend->stride = stride; + rend->data = data; + rend->surface = surface; return 0; } -static void wlt_render_debug(struct wlt_draw_ctx *ctx) +int wlt_renderer_new(struct wlt_renderer **out, unsigned int width, + unsigned int height) +{ + struct wlt_renderer *rend; + int r; + + rend = calloc(1, sizeof(*rend)); + if (!rend) + return -ENOMEM; + + r = wlt_renderer_realloc(rend, width, height); + if (r < 0) + goto err_rend; + + *out = rend; + return 0; + +err_rend: + free(rend); + return r; +} + +void wlt_renderer_free(struct wlt_renderer *rend) { - cairo_t *cr = ctx->cr; - double x1, x2, y1, y2; + if (!rend) + return; + + cairo_surface_destroy(rend->surface); + free(rend->data); + free(rend); +} + +int wlt_renderer_resize(struct wlt_renderer *rend, unsigned int width, + unsigned int height) +{ + return wlt_renderer_realloc(rend, width, height); +} + +static void wlt_renderer_fill(struct wlt_renderer *rend, + unsigned int x, unsigned int y, + unsigned int width, unsigned int height, + uint8_t br, uint8_t bg, uint8_t bb) +{ + unsigned int i, tmp; + uint8_t *dst; + uint32_t out; - if (1) + /* clip width */ + tmp = x + width; + if (tmp <= x || x >= rend->width) return; + if (tmp > rend->width) + width = rend->width - x; - cairo_clip_extents(cr, &x1, &y1, &x2, &y2); - cairo_rectangle(cr, x1, y1, x2, y2); - cairo_set_source_rgb(cr, 1, 0, 0); - cairo_stroke(cr); + /* clip height */ + tmp = y + height; + if (tmp <= y || y >= rend->height) + return; + if (tmp > rend->height) + height = rend->height - y; + + /* prepare */ + dst = rend->data; + dst = &dst[y * rend->stride + x * 4]; + out = (0xff << 24) | (br << 16) | (bg << 8) | bb; + + /* fill buffer */ + while (height--) { + for (i = 0; i < width; ++i) + ((uint32_t*)dst)[i] = out; + + dst += rend->stride; + } } -void wlt_render(struct wlt_draw_ctx *ctx) +static void wlt_renderer_blend(struct wlt_renderer *rend, + const struct wlt_glyph *glyph, + unsigned int x, unsigned int y, + uint8_t fr, uint8_t fg, uint8_t fb, + uint8_t br, uint8_t bg, uint8_t bb) { - tsm_screen_draw(ctx->screen, NULL, wlt_render_cell, NULL, ctx); - wlt_render_debug(ctx); + unsigned int i, tmp, width, height; + const uint8_t *src; + uint8_t *dst; + uint32_t out; + uint_fast32_t r, g, b; + + /* clip width */ + tmp = x + glyph->width; + if (tmp <= x || x >= rend->width) + return; + if (tmp > rend->width) + width = rend->width - x; + else + width = glyph->width; + + /* clip height */ + tmp = y + glyph->height; + if (tmp <= y || y >= rend->height) + return; + if (tmp > rend->height) + height = rend->height - y; + else + height = glyph->height; + + /* prepare */ + dst = rend->data; + dst = &dst[y * rend->stride + x * 4]; + src = glyph->buffer; + + /* blend buffer */ + while (height--) { + for (i = 0; i < width; ++i) { + if (src[i] == 0) { + r = br; + g = bg; + b = bb; + } else if (src[i] == 255) { + r = fr; + g = fg; + b = fb; + } else { + /* Division by 255 (t /= 255) is done with: + * t += 0x80 + * t = (t + (t >> 8)) >> 8 + * This speeds up the computation by ~20% as + * the division is skipped. */ + r = fr * src[i] + br * (255 - src[i]); + r += 0x80; + r = (r + (r >> 8)) >> 8; + + g = fg * src[i] + bg * (255 - src[i]); + g += 0x80; + g = (g + (g >> 8)) >> 8; + + b = fb * src[i] + bb * (255 - src[i]); + b += 0x80; + b = (b + (b >> 8)) >> 8; + } + + out = (0xff << 24) | (r << 16) | (g << 8) | b; + ((uint32_t*)dst)[i] = out; + } + + dst += rend->stride; + src += glyph->stride; + } +} + +static int wlt_renderer_draw_cell(struct tsm_screen *screen, uint32_t id, + const uint32_t *ch, size_t len, + unsigned int cwidth, unsigned int posx, + unsigned int posy, + const struct tsm_screen_attr *attr, + void *data) +{ + const struct wlt_draw_ctx *ctx = data; + uint8_t fr, fg, fb, br, bg, bb; + unsigned int x, y; + struct wlt_glyph *glyph; + int r; + + /* invert colors if requested */ + if (attr->inverse) { + fr = attr->br; + fg = attr->bg; + fb = attr->bb; + br = attr->fr; + bg = attr->fg; + bb = attr->fb; + } else { + fr = attr->fr; + fg = attr->fg; + fb = attr->fb; + br = attr->br; + bg = attr->bg; + bb = attr->bb; + } + + x = posx * ctx->cell_width; + y = posy * ctx->cell_height; + + /* !len means background-only */ + if (!len) { + wlt_renderer_fill(ctx->rend, x, y, ctx->cell_width, + ctx->cell_height, br, bg, bb); + return 0; + } + + r = wlt_face_render(ctx->face, &glyph, id, ch, len, cwidth); + if (r < 0) + return r; + + wlt_renderer_blend(ctx->rend, glyph, x, y, + fr, fg, fb, br, bg, bb); + + return 0; +} + +void wlt_renderer_draw(const struct wlt_draw_ctx *ctx) +{ + struct wlt_renderer *rend = ctx->rend; + + /* cairo is *way* too slow to render all masks efficiently. Therefore, + * we render all glyphs into a shadow buffer on the CPU and then tell + * cairo to blit it into the gtk buffer. This way we get two mem-writes + * but at least it's fast enough to render a whole screen. */ + + tsm_screen_draw(ctx->screen, NULL, wlt_renderer_draw_cell, NULL, + (void*)ctx); + + /* TODO: mark surface as dirty so cairo notices that */ + cairo_set_source_surface(ctx->cr, rend->surface, 0, 0); + cairo_paint(ctx->cr); } diff --git a/src/wlterm.c b/src/wlterm.c index 88ec7c7..48b4703 100644 --- a/src/wlterm.c +++ b/src/wlterm.c @@ -57,6 +57,7 @@ struct term { guint pty_idle_src; guint child_src; + struct wlt_renderer *rend; struct wlt_face *face; unsigned int cell_width; unsigned int cell_height; @@ -224,8 +225,18 @@ static gboolean term_configure_cb(GtkWidget *widget, GdkEvent *ev, bool new_adjust_size = term->adjust_size; int r, pid; + term->width = cev->width; + term->height = cev->height; + /* Initial configure-event, setup fonts, pty, etc. */ if (!term->initialized) { + r = wlt_renderer_new(&term->rend, term->width, term->height); + if (r < 0) { + err("cannot initialize renderer (%d)", r); + gtk_main_quit(); + return TRUE; + } + r = term_change_font(term); if (r < 0) { err("cannot load font (%d)", r); @@ -234,8 +245,6 @@ static gboolean term_configure_cb(GtkWidget *widget, GdkEvent *ev, } /* compute cell size */ - term->width = cev->width; - term->height = cev->height; term_recalc_cells(term); term_set_geometry(term); @@ -272,10 +281,12 @@ static gboolean term_configure_cb(GtkWidget *widget, GdkEvent *ev, term_notify_resize(term); } else { /* compute cell size */ - term->width = cev->width; - term->height = cev->height; term_recalc_cells(term); term_notify_resize(term); + + r = wlt_renderer_resize(term->rend, term->width, term->height); + if (r < 0) + err("cannot resize renderer (%d)", r); } /* adjust geometry */ @@ -310,12 +321,13 @@ static gboolean term_redraw_cb(GtkWidget *widget, cairo_t *cr, gpointer data) start = g_get_monotonic_time(); + ctx.rend = term->rend; ctx.cr = cr; ctx.face = term->face; ctx.cell_width = term->cell_width; ctx.cell_height = term->cell_height; ctx.screen = term->screen; - wlt_render(&ctx); + wlt_renderer_draw(&ctx); end = g_get_monotonic_time(); if (0) @@ -416,6 +428,7 @@ static void term_free(struct term *term) shl_pty_bridge_free(term->pty_bridge); tsm_vte_unref(term->vte); tsm_screen_unref(term->screen); + wlt_renderer_free(term->rend); wlt_face_unref(term->face); wlt_font_unref(term->font); if (term->window) diff --git a/src/wlterm.h b/src/wlterm.h index f4124e7..c5d9ace 100644 --- a/src/wlterm.h +++ b/src/wlterm.h @@ -38,6 +38,7 @@ struct wlt_font; struct wlt_face; +struct wlt_renderer; /* fonts */ @@ -81,6 +82,7 @@ int wlt_face_render(struct wlt_face *face, struct wlt_glyph **out, /* rendering */ struct wlt_draw_ctx { + struct wlt_renderer *rend; cairo_t *cr; struct wlt_face *face; unsigned int cell_width; @@ -88,6 +90,11 @@ struct wlt_draw_ctx { struct tsm_screen *screen; }; -void wlt_render(struct wlt_draw_ctx *ctx); +int wlt_renderer_new(struct wlt_renderer **out, unsigned int width, + unsigned int height); +void wlt_renderer_free(struct wlt_renderer *rend); +int wlt_renderer_resize(struct wlt_renderer *rend, unsigned int width, + unsigned int height); +void wlt_renderer_draw(const struct wlt_draw_ctx *ctx); #endif /* WLT_WLTERM_H */ |