summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2013-10-22 14:53:58 +0200
committerDavid Herrmann <dh.herrmann@gmail.com>2013-10-22 14:53:58 +0200
commit9e901f72a6f05e7c98dc2cf46432017ed2d31d4a (patch)
tree3a6b8e4c4832b6ebb1c5694fd98f3c9c09a4e42e
parent325b4ca59b63ed42ca3875f788c83d69e8f0ecd1 (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.c287
-rw-r--r--src/wlterm.c23
-rw-r--r--src/wlterm.h9
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 */