From bed7b34c2857ebac8028e8b7a00b0bef0d831df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 16 Aug 2019 20:40:32 +0200 Subject: [PATCH] wip: render background and glyphs using pixman --- font.c | 52 ++++++++++++++++--------- font.h | 13 ++++--- render.c | 115 ++++++++++++++++++++++++++++++------------------------- shm.c | 28 +++++++++++++- shm.h | 4 ++ 5 files changed, 134 insertions(+), 78 deletions(-) diff --git a/font.c b/font.c index ca9d4464..85c54bbd 100644 --- a/font.c +++ b/font.c @@ -367,13 +367,20 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) if (bitmap->width == 0) goto err; - /* Map FT pixel format to cairo surface format */ - cairo_format_t cr_format = - bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? CAIRO_FORMAT_A1 : - bitmap->pixel_mode == FT_PIXEL_MODE_GRAY ? CAIRO_FORMAT_A8 : - bitmap->pixel_mode == FT_PIXEL_MODE_BGRA ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_INVALID; + /* Map FT pixel format to pixman format */ + pixman_format_code_t pix_format = + bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? PIXMAN_a1 : + bitmap->pixel_mode == FT_PIXEL_MODE_GRAY ? PIXMAN_a8 : + bitmap->pixel_mode == FT_PIXEL_MODE_BGRA ? PIXMAN_a8r8g8b8 : -1; - int stride = cairo_format_stride_for_width(cr_format, bitmap->width); + /* Calculate stride. Copied from cairoint.h:CAIRO_STRIDE_FOR_WIDTH_BPP */ + int bpp = + pix_format == PIXMAN_a1 ? 1 : + pix_format == PIXMAN_a8 ? 8 : + pix_format == PIXMAN_a8r8g8b8 ? 32 : -1; + assert(bpp >= 0); + + int stride = (((bpp * bitmap->width + 7) / 8 + 4 - 1) & -4); assert(stride >= bitmap->pitch); uint8_t *data = malloc(bitmap->rows * stride); @@ -413,22 +420,31 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) goto err; } - cairo_surface_t *surf = cairo_image_surface_create_for_data( - data, cr_format, bitmap->width, bitmap->rows, stride); + pixman_image_t *pix = pixman_image_create_bits_no_clear( + pix_format, bitmap->width, bitmap->rows, (uint32_t *)data, stride); - if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { + if (pix == NULL) { free(data); - cairo_surface_destroy(surf); goto err; } + if (font->pixel_size_fixup != 1.) { + struct pixman_transform scale; + pixman_transform_init_scale( + &scale, + pixman_double_to_fixed(1. / font->pixel_size_fixup), + pixman_double_to_fixed(1. / font->pixel_size_fixup)); + pixman_image_set_transform(pix, &scale); + } + *glyph = (struct glyph){ .wc = wc, - .width = wcwidth(wc), - .surf = surf, - .left = font->face->glyph->bitmap_left, - .top = font->face->glyph->bitmap_top, - .pixel_size_fixup = font->pixel_size_fixup, + .cols = wcwidth(wc), + .pix = pix, + .x = font->face->glyph->bitmap_left / font->pixel_size_fixup, + .y = font->face->glyph->bitmap_top * font->pixel_size_fixup, + .width = bitmap->width, + .height = bitmap->rows, .valid = true, }; @@ -506,10 +522,8 @@ font_destroy(struct font *font) if (!it->item.valid) continue; - cairo_surface_flush(it->item.surf); - void *image = cairo_image_surface_get_data(it->item.surf); - - cairo_surface_destroy(it->item.surf); + void *image = pixman_image_get_data(it->item.pix); + pixman_image_unref(it->item.pix); free(image); } diff --git a/font.h b/font.h index 1450f5f9..d0ebb5bb 100644 --- a/font.h +++ b/font.h @@ -7,7 +7,7 @@ #include FT_FREETYPE_H #include FT_LCD_FILTER_H #include -#include +#include #include "tllist.h" //#include "terminal.h" @@ -16,13 +16,14 @@ typedef tll(const char *) font_list_t; struct glyph { wchar_t wc; + int cols; + + pixman_image_t *pix; + int x; + int y; int width; + int height; - cairo_surface_t *surf; - int left; - int top; - - double pixel_size_fixup; bool valid; }; diff --git a/render.c b/render.c index 5ed69d6e..147583d4 100644 --- a/render.c +++ b/render.c @@ -37,6 +37,17 @@ color_hex_to_rgb(uint32_t color) }; } +static inline pixman_color_t +color_hex_to_pixman(uint32_t color) +{ + return (pixman_color_t){ + .red = (((color >> 16) & 0xff) + 1) * 256 - 1, + .green = (((color >> 8) & 0xff) + 1) * 256 - 1, + .blue = (((color >> 0) & 0xff) + 1) * 256 - 1, + .alpha = -1, + }; +} + static inline void color_dim(struct rgb *rgb) { @@ -45,6 +56,14 @@ color_dim(struct rgb *rgb) rgb->b /= 2.; } +static inline void +pixman_color_dim(pixman_color_t *color) +{ + color->red /= 2; + color->green /= 2; + color->blue /= 2; +} + static void draw_underline(const struct terminal *term, cairo_t *cr, const struct font *font, struct rgb color, double x, double y, int cols) @@ -136,7 +155,7 @@ arm_blink_timer(struct terminal *term) } static int -render_cell(struct terminal *term, cairo_t *cr, +render_cell(struct terminal *term, cairo_t *cr, pixman_image_t *pix, struct cell *cell, int col, int row, bool has_cursor) { if (cell->attrs.clean) @@ -169,35 +188,36 @@ render_cell(struct terminal *term, cairo_t *cr, if (cell->attrs.blink && term->blink.state == BLINK_OFF) _fg = _bg; - struct rgb fg = color_hex_to_rgb(_fg); - struct rgb bg = color_hex_to_rgb(_bg); + pixman_color_t fg = color_hex_to_pixman(_fg); + pixman_color_t bg = color_hex_to_pixman(_bg); if (cell->attrs.dim) - color_dim(&fg); + pixman_color_dim(&fg); if (block_cursor && term->cursor_color.text >> 31) { /* User configured cursor color overrides all attributes */ assert(term->cursor_color.cursor >> 31); - fg = color_hex_to_rgb(term->cursor_color.text); - bg = color_hex_to_rgb(term->cursor_color.cursor); + fg = color_hex_to_pixman(term->cursor_color.text); + bg = color_hex_to_pixman(term->cursor_color.cursor); } struct font *font = attrs_to_font(term, &cell->attrs); const struct glyph *glyph = font_glyph_for_wc(font, cell->wc); - int cell_cols = glyph != NULL ? max(1, glyph->width) : 1; + int cell_cols = glyph != NULL ? max(1, glyph->cols) : 1; /* Background */ - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, bg.r, bg.g, bg.b, block_cursor ? 1.0 : term->colors.alpha); - cairo_rectangle(cr, x, y, cell_cols * width, height); - cairo_fill(cr); + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, pix, &bg, 1, + &(pixman_rectangle16_t){x, y, cell_cols * width, height}); /* Non-block cursors */ if (has_cursor) { +#if 1 struct rgb cursor_color = term->cursor_color.text >> 31 ? color_hex_to_rgb(term->cursor_color.cursor) - : fg; + : color_hex_to_rgb(_fg); +#endif if (term->cursor_style == CURSOR_BAR) draw_bar(term, cr, cursor_color, x, y); @@ -215,36 +235,32 @@ render_cell(struct terminal *term, cairo_t *cr, return cell_cols; if (glyph != NULL) { - cairo_save(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - - double fixup = glyph->pixel_size_fixup; - cairo_translate( - cr, - x + glyph->left / fixup, - y + term->fextents.ascent - glyph->top * fixup); - cairo_scale(cr, fixup, fixup); - - if (cairo_image_surface_get_format(glyph->surf) == CAIRO_FORMAT_ARGB32) { + if (pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) { /* Glyph surface is a pre-rendered image (typically a color emoji...) */ if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) { - cairo_set_source_surface(cr, glyph->surf, 0, 0); - cairo_paint(cr); + pixman_image_composite( + PIXMAN_OP_OVER, glyph->pix, NULL, pix, 0, 0, 0, 0, + x + glyph->x, y + term->fextents.ascent - glyph->y, + glyph->width, glyph->height); } } else { /* Glyph surface is an alpha mask */ - cairo_set_source_rgb(cr, fg.r, fg.g, fg.b); - cairo_mask_surface(cr, glyph->surf, 0, 0); + /* TODO: cache */ + pixman_image_t *src = pixman_image_create_solid_fill(&fg); + pixman_image_composite( + PIXMAN_OP_OVER, src, glyph->pix, pix, 0, 0, 0, 0, + x + glyph->x, y + term->fextents.ascent - glyph->y, + glyph->width, glyph->height); + pixman_image_unref(src); } - cairo_restore(cr); } /* Underline */ if (cell->attrs.underline) - draw_underline(term, cr, attrs_to_font(term, &cell->attrs), fg, x, y, cell_cols); + draw_underline(term, cr, attrs_to_font(term, &cell->attrs), color_hex_to_rgb(_fg), x, y, cell_cols); if (cell->attrs.strikethrough) - draw_strikeout(term, cr, attrs_to_font(term, &cell->attrs), fg, x, y, cell_cols); + draw_strikeout(term, cr, attrs_to_font(term, &cell->attrs), color_hex_to_rgb(_fg), x, y, cell_cols); return cell_cols; } @@ -258,21 +274,18 @@ grid_render_scroll(struct terminal *term, struct buffer *buf, int width = buf->width; int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * term->cell_height; - const uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - LOG_DBG("damage: SCROLL: %d-%d by %d lines (dst-y: %d, src-y: %d, " "height: %d, stride: %d, mmap-size: %zu)", dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.lines, - dst_y, src_y, height, stride, + dst_y, src_y, height, buf->stride, buf->size); if (height > 0) { - cairo_surface_flush(buf->cairo_surface[0]); - uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]); - - memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); - cairo_surface_mark_dirty(buf->cairo_surface[0]); + uint8_t *raw = buf->mmapped; + memmove(raw + dst_y * buf->stride, + raw + src_y * buf->stride, + height * buf->stride); wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height); } @@ -287,31 +300,28 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, int width = buf->width; int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * term->cell_height; - const uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - LOG_DBG("damage: SCROLL REVERSE: %d-%d by %d lines (dst-y: %d, src-y: %d, " "height: %d, stride: %d, mmap-size: %zu)", dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.lines, - dst_y, src_y, height, stride, + dst_y, src_y, height, buf->stride, buf->size); if (height > 0) { - cairo_surface_flush(buf->cairo_surface[0]); - uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]); - - memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); - cairo_surface_mark_dirty(buf->cairo_surface[0]); + uint8_t *raw = buf->mmapped; + memmove(raw + dst_y * buf->stride, + raw + src_y * buf->stride, + height * buf->stride); wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height); } } static void -render_row(struct terminal *term, cairo_t *cr, struct row *row, int row_no) +render_row(struct terminal *term, cairo_t *cr, pixman_image_t *pix, struct row *row, int row_no) { for (int col = term->cols - 1; col >= 0; col--) - render_cell(term, cr, &row->cells[col], col, row_no, false); + render_cell(term, cr, pix, &row->cells[col], col, row_no, false); } int @@ -349,7 +359,7 @@ render_worker_thread(void *_ctx) switch (row_no) { default: assert(buf != NULL); - render_row(term, buf->cairo[my_id], grid_row_in_view(term->grid, row_no), row_no); + render_row(term, buf->cairo[my_id], buf->pix[my_id], grid_row_in_view(term->grid, row_no), row_no); break; case -1: @@ -389,6 +399,7 @@ grid_render(struct terminal *term) struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height, 1 + term->render.workers.count); cairo_t *cr = buf->cairo[0]; cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + pixman_image_t *pix = buf->pix[0]; bool all_clean = tll_length(term->grid->scroll_damage) == 0; @@ -399,7 +410,7 @@ grid_render(struct terminal *term) if (cell->attrs.clean) { cell->attrs.clean = 0; - render_cell(term, cr, cell, at.col, at.row, false); + render_cell(term, cr, pix, cell, at.col, at.row, false); wl_surface_damage_buffer( term->wl.surface, @@ -504,7 +515,7 @@ grid_render(struct terminal *term) if (!row->dirty) continue; - render_row(term, cr, row, r); + render_row(term, cr, pix, row, r); row->dirty = false; all_clean = false; @@ -590,7 +601,7 @@ grid_render(struct terminal *term) cell->attrs.clean = 0; term->render.last_cursor.cell = cell; int cols_updated = render_cell( - term, cr, cell, term->cursor.col, view_aligned_row, true); + term, cr, pix, cell, term->cursor.col, view_aligned_row, true); wl_surface_damage_buffer( term->wl.surface, diff --git a/shm.c b/shm.c index ca078544..19ec66db 100644 --- a/shm.c +++ b/shm.c @@ -7,6 +7,8 @@ #include #include +#include + #define LOG_MODULE "shm" #include "log.h" #include "tllist.h" @@ -61,6 +63,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies) cairo_surface_t **cairo_surface = NULL; cairo_t **cairo = NULL; + pixman_image_t **pix = NULL; /* Backing memory for SHM */ pool_fd = memfd_create("f00sel-wayland-shm-buffer-pool", MFD_CLOEXEC); @@ -103,6 +106,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies) /* Create a cairo surface around the mmapped memory */ cairo_surface = calloc(copies, sizeof(cairo_surface[0])); cairo = calloc(copies, sizeof(cairo[0])); + pix = calloc(copies, sizeof(pix[0])); for (size_t i = 0; i < copies; i++) { cairo_surface[i] = cairo_image_surface_create_for_data( @@ -120,6 +124,14 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies) cairo_status_to_string(cairo_status(cairo[i]))); goto err; } + + pix[i] = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, width, height, (uint32_t *)mmapped, stride); + + if (pix[i] == NULL) { + LOG_ERR("failed to create pixman image"); + goto err; + } } /* Push to list of available buffers, but marked as 'busy' */ @@ -128,13 +140,15 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies) ((struct buffer){ .width = width, .height = height, + .stride = stride, .busy = true, .size = size, .mmapped = mmapped, .wl_buf = buf, .copies = copies, .cairo_surface = cairo_surface, - .cairo = cairo} + .cairo = cairo, + .pix = pix} ) ); @@ -155,6 +169,12 @@ err: cairo_surface_destroy(cairo_surface[i]); free(cairo_surface); } + if (pix != NULL) { + for (size_t i = 0; i < copies; i++) + if (pix[i] != NULL) + pixman_image_unref(pix[i]); + free(pix); + } if (buf != NULL) wl_buffer_destroy(buf); if (pool != NULL) @@ -185,6 +205,12 @@ shm_fini(void) cairo_surface_destroy(buf->cairo_surface[i]); free(buf->cairo_surface); } + if (buf->pix != NULL) { + for (size_t i = 0; i < buf->copies; i++) + if (buf->pix[i] != NULL) + pixman_image_unref(buf->pix[i]); + free(buf->pix); + } wl_buffer_destroy(buf->wl_buf); munmap(buf->mmapped, buf->size); diff --git a/shm.h b/shm.h index 9982cb8d..668b26b8 100644 --- a/shm.h +++ b/shm.h @@ -4,11 +4,13 @@ #include #include +#include #include struct buffer { int width; int height; + int stride; bool busy; size_t size; @@ -19,6 +21,8 @@ struct buffer { size_t copies; cairo_surface_t **cairo_surface; cairo_t **cairo; + + pixman_image_t **pix; }; struct buffer *shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies);