wip: render background and glyphs using pixman

This commit is contained in:
Daniel Eklöf 2019-08-16 20:40:32 +02:00
parent 9259696b18
commit bed7b34c28
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
5 changed files with 134 additions and 78 deletions

52
font.c
View file

@ -367,13 +367,20 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph)
if (bitmap->width == 0) if (bitmap->width == 0)
goto err; goto err;
/* Map FT pixel format to cairo surface format */ /* Map FT pixel format to pixman format */
cairo_format_t cr_format = pixman_format_code_t pix_format =
bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? CAIRO_FORMAT_A1 : bitmap->pixel_mode == FT_PIXEL_MODE_MONO ? PIXMAN_a1 :
bitmap->pixel_mode == FT_PIXEL_MODE_GRAY ? CAIRO_FORMAT_A8 : bitmap->pixel_mode == FT_PIXEL_MODE_GRAY ? PIXMAN_a8 :
bitmap->pixel_mode == FT_PIXEL_MODE_BGRA ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_INVALID; 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); assert(stride >= bitmap->pitch);
uint8_t *data = malloc(bitmap->rows * stride); 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; goto err;
} }
cairo_surface_t *surf = cairo_image_surface_create_for_data( pixman_image_t *pix = pixman_image_create_bits_no_clear(
data, cr_format, bitmap->width, bitmap->rows, stride); pix_format, bitmap->width, bitmap->rows, (uint32_t *)data, stride);
if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { if (pix == NULL) {
free(data); free(data);
cairo_surface_destroy(surf);
goto err; 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){ *glyph = (struct glyph){
.wc = wc, .wc = wc,
.width = wcwidth(wc), .cols = wcwidth(wc),
.surf = surf, .pix = pix,
.left = font->face->glyph->bitmap_left, .x = font->face->glyph->bitmap_left / font->pixel_size_fixup,
.top = font->face->glyph->bitmap_top, .y = font->face->glyph->bitmap_top * font->pixel_size_fixup,
.pixel_size_fixup = font->pixel_size_fixup, .width = bitmap->width,
.height = bitmap->rows,
.valid = true, .valid = true,
}; };
@ -506,10 +522,8 @@ font_destroy(struct font *font)
if (!it->item.valid) if (!it->item.valid)
continue; continue;
cairo_surface_flush(it->item.surf); void *image = pixman_image_get_data(it->item.pix);
void *image = cairo_image_surface_get_data(it->item.surf); pixman_image_unref(it->item.pix);
cairo_surface_destroy(it->item.surf);
free(image); free(image);
} }

13
font.h
View file

@ -7,7 +7,7 @@
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include FT_LCD_FILTER_H #include FT_LCD_FILTER_H
#include <fontconfig/fontconfig.h> #include <fontconfig/fontconfig.h>
#include <cairo.h> #include <pixman.h>
#include "tllist.h" #include "tllist.h"
//#include "terminal.h" //#include "terminal.h"
@ -16,13 +16,14 @@ typedef tll(const char *) font_list_t;
struct glyph { struct glyph {
wchar_t wc; wchar_t wc;
int cols;
pixman_image_t *pix;
int x;
int y;
int width; int width;
int height;
cairo_surface_t *surf;
int left;
int top;
double pixel_size_fixup;
bool valid; bool valid;
}; };

115
render.c
View file

@ -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 static inline void
color_dim(struct rgb *rgb) color_dim(struct rgb *rgb)
{ {
@ -45,6 +56,14 @@ color_dim(struct rgb *rgb)
rgb->b /= 2.; rgb->b /= 2.;
} }
static inline void
pixman_color_dim(pixman_color_t *color)
{
color->red /= 2;
color->green /= 2;
color->blue /= 2;
}
static void static void
draw_underline(const struct terminal *term, cairo_t *cr, const struct font *font, draw_underline(const struct terminal *term, cairo_t *cr, const struct font *font,
struct rgb color, double x, double y, int cols) struct rgb color, double x, double y, int cols)
@ -136,7 +155,7 @@ arm_blink_timer(struct terminal *term)
} }
static int 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) struct cell *cell, int col, int row, bool has_cursor)
{ {
if (cell->attrs.clean) 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) if (cell->attrs.blink && term->blink.state == BLINK_OFF)
_fg = _bg; _fg = _bg;
struct rgb fg = color_hex_to_rgb(_fg); pixman_color_t fg = color_hex_to_pixman(_fg);
struct rgb bg = color_hex_to_rgb(_bg); pixman_color_t bg = color_hex_to_pixman(_bg);
if (cell->attrs.dim) if (cell->attrs.dim)
color_dim(&fg); pixman_color_dim(&fg);
if (block_cursor && term->cursor_color.text >> 31) { if (block_cursor && term->cursor_color.text >> 31) {
/* User configured cursor color overrides all attributes */ /* User configured cursor color overrides all attributes */
assert(term->cursor_color.cursor >> 31); assert(term->cursor_color.cursor >> 31);
fg = color_hex_to_rgb(term->cursor_color.text); fg = color_hex_to_pixman(term->cursor_color.text);
bg = color_hex_to_rgb(term->cursor_color.cursor); bg = color_hex_to_pixman(term->cursor_color.cursor);
} }
struct font *font = attrs_to_font(term, &cell->attrs); struct font *font = attrs_to_font(term, &cell->attrs);
const struct glyph *glyph = font_glyph_for_wc(font, cell->wc); 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 */ /* Background */
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); pixman_image_fill_rectangles(
cairo_set_source_rgba(cr, bg.r, bg.g, bg.b, block_cursor ? 1.0 : term->colors.alpha); PIXMAN_OP_SRC, pix, &bg, 1,
cairo_rectangle(cr, x, y, cell_cols * width, height); &(pixman_rectangle16_t){x, y, cell_cols * width, height});
cairo_fill(cr);
/* Non-block cursors */ /* Non-block cursors */
if (has_cursor) { if (has_cursor) {
#if 1
struct rgb cursor_color = term->cursor_color.text >> 31 struct rgb cursor_color = term->cursor_color.text >> 31
? color_hex_to_rgb(term->cursor_color.cursor) ? color_hex_to_rgb(term->cursor_color.cursor)
: fg; : color_hex_to_rgb(_fg);
#endif
if (term->cursor_style == CURSOR_BAR) if (term->cursor_style == CURSOR_BAR)
draw_bar(term, cr, cursor_color, x, y); draw_bar(term, cr, cursor_color, x, y);
@ -215,36 +235,32 @@ render_cell(struct terminal *term, cairo_t *cr,
return cell_cols; return cell_cols;
if (glyph != NULL) { if (glyph != NULL) {
cairo_save(cr); if (pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) {
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) {
/* Glyph surface is a pre-rendered image (typically a color emoji...) */ /* Glyph surface is a pre-rendered image (typically a color emoji...) */
if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) { if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) {
cairo_set_source_surface(cr, glyph->surf, 0, 0); pixman_image_composite(
cairo_paint(cr); 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 { } else {
/* Glyph surface is an alpha mask */ /* Glyph surface is an alpha mask */
cairo_set_source_rgb(cr, fg.r, fg.g, fg.b); /* TODO: cache */
cairo_mask_surface(cr, glyph->surf, 0, 0); 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 */ /* Underline */
if (cell->attrs.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) 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; return cell_cols;
} }
@ -258,21 +274,18 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
int width = buf->width; int width = buf->width;
int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * term->cell_height; 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, " LOG_DBG("damage: SCROLL: %d-%d by %d lines (dst-y: %d, src-y: %d, "
"height: %d, stride: %d, mmap-size: %zu)", "height: %d, stride: %d, mmap-size: %zu)",
dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.region.start, dmg->scroll.region.end,
dmg->scroll.lines, dmg->scroll.lines,
dst_y, src_y, height, stride, dst_y, src_y, height, buf->stride,
buf->size); buf->size);
if (height > 0) { if (height > 0) {
cairo_surface_flush(buf->cairo_surface[0]); uint8_t *raw = buf->mmapped;
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]); memmove(raw + dst_y * buf->stride,
raw + src_y * buf->stride,
memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); height * buf->stride);
cairo_surface_mark_dirty(buf->cairo_surface[0]);
wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height); 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 width = buf->width;
int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * term->cell_height; 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, " LOG_DBG("damage: SCROLL REVERSE: %d-%d by %d lines (dst-y: %d, src-y: %d, "
"height: %d, stride: %d, mmap-size: %zu)", "height: %d, stride: %d, mmap-size: %zu)",
dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.region.start, dmg->scroll.region.end,
dmg->scroll.lines, dmg->scroll.lines,
dst_y, src_y, height, stride, dst_y, src_y, height, buf->stride,
buf->size); buf->size);
if (height > 0) { if (height > 0) {
cairo_surface_flush(buf->cairo_surface[0]); uint8_t *raw = buf->mmapped;
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]); memmove(raw + dst_y * buf->stride,
raw + src_y * buf->stride,
memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); height * buf->stride);
cairo_surface_mark_dirty(buf->cairo_surface[0]);
wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height); wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height);
} }
} }
static void 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--) 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 int
@ -349,7 +359,7 @@ render_worker_thread(void *_ctx)
switch (row_no) { switch (row_no) {
default: default:
assert(buf != NULL); 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; break;
case -1: 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); 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_t *cr = buf->cairo[0];
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
pixman_image_t *pix = buf->pix[0];
bool all_clean = tll_length(term->grid->scroll_damage) == 0; bool all_clean = tll_length(term->grid->scroll_damage) == 0;
@ -399,7 +410,7 @@ grid_render(struct terminal *term)
if (cell->attrs.clean) { if (cell->attrs.clean) {
cell->attrs.clean = 0; 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( wl_surface_damage_buffer(
term->wl.surface, term->wl.surface,
@ -504,7 +515,7 @@ grid_render(struct terminal *term)
if (!row->dirty) if (!row->dirty)
continue; continue;
render_row(term, cr, row, r); render_row(term, cr, pix, row, r);
row->dirty = false; row->dirty = false;
all_clean = false; all_clean = false;
@ -590,7 +601,7 @@ grid_render(struct terminal *term)
cell->attrs.clean = 0; cell->attrs.clean = 0;
term->render.last_cursor.cell = cell; term->render.last_cursor.cell = cell;
int cols_updated = render_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( wl_surface_damage_buffer(
term->wl.surface, term->wl.surface,

28
shm.c
View file

@ -7,6 +7,8 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <linux/memfd.h> #include <linux/memfd.h>
#include <pixman.h>
#define LOG_MODULE "shm" #define LOG_MODULE "shm"
#include "log.h" #include "log.h"
#include "tllist.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_surface_t **cairo_surface = NULL;
cairo_t **cairo = NULL; cairo_t **cairo = NULL;
pixman_image_t **pix = NULL;
/* Backing memory for SHM */ /* Backing memory for SHM */
pool_fd = memfd_create("f00sel-wayland-shm-buffer-pool", MFD_CLOEXEC); 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 */ /* Create a cairo surface around the mmapped memory */
cairo_surface = calloc(copies, sizeof(cairo_surface[0])); cairo_surface = calloc(copies, sizeof(cairo_surface[0]));
cairo = calloc(copies, sizeof(cairo[0])); cairo = calloc(copies, sizeof(cairo[0]));
pix = calloc(copies, sizeof(pix[0]));
for (size_t i = 0; i < copies; i++) { for (size_t i = 0; i < copies; i++) {
cairo_surface[i] = cairo_image_surface_create_for_data( 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]))); cairo_status_to_string(cairo_status(cairo[i])));
goto err; 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' */ /* 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){ ((struct buffer){
.width = width, .width = width,
.height = height, .height = height,
.stride = stride,
.busy = true, .busy = true,
.size = size, .size = size,
.mmapped = mmapped, .mmapped = mmapped,
.wl_buf = buf, .wl_buf = buf,
.copies = copies, .copies = copies,
.cairo_surface = cairo_surface, .cairo_surface = cairo_surface,
.cairo = cairo} .cairo = cairo,
.pix = pix}
) )
); );
@ -155,6 +169,12 @@ err:
cairo_surface_destroy(cairo_surface[i]); cairo_surface_destroy(cairo_surface[i]);
free(cairo_surface); 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) if (buf != NULL)
wl_buffer_destroy(buf); wl_buffer_destroy(buf);
if (pool != NULL) if (pool != NULL)
@ -185,6 +205,12 @@ shm_fini(void)
cairo_surface_destroy(buf->cairo_surface[i]); cairo_surface_destroy(buf->cairo_surface[i]);
free(buf->cairo_surface); 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); wl_buffer_destroy(buf->wl_buf);
munmap(buf->mmapped, buf->size); munmap(buf->mmapped, buf->size);

4
shm.h
View file

@ -4,11 +4,13 @@
#include <stddef.h> #include <stddef.h>
#include <cairo.h> #include <cairo.h>
#include <pixman.h>
#include <wayland-client.h> #include <wayland-client.h>
struct buffer { struct buffer {
int width; int width;
int height; int height;
int stride;
bool busy; bool busy;
size_t size; size_t size;
@ -19,6 +21,8 @@ struct buffer {
size_t copies; size_t copies;
cairo_surface_t **cairo_surface; cairo_surface_t **cairo_surface;
cairo_t **cairo; cairo_t **cairo;
pixman_image_t **pix;
}; };
struct buffer *shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies); struct buffer *shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies);