From 90d357befb02ca10231cb998c2c3004703e43646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 26 Jul 2019 18:54:14 +0200 Subject: [PATCH] render: poc: use freetype to render glyphs Initial POC that uses freetype to render the glyphs. The bitmap produced by freetype contains the alpha value. We use this bitmap in a mask surface and then draw the final glyph with cairo by applying the source rgb using the OVER operator and the freetype generated alpha mask as surface mask. Note that we only support grayscale antialiasing (and no antialiasing). We are probably not setting the antialias options correctly either. --- render.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/render.c b/render.c index 98babe38..0ffb0d18 100644 --- a/render.c +++ b/render.c @@ -5,6 +5,10 @@ #include #include +#include +#include FT_FREETYPE_H +#include + #include #include @@ -255,11 +259,14 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, gseq.foreground = _fg; } +#if 0 int new_glyphs = sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - gseq.count; +#endif struct font *font = attrs_to_font(term, &cell->attrs); +#if 0 struct glyph_cache *entry = cell->c[1] == '\0' ? &font->glyph_cache[(unsigned char)cell->c[0]] : NULL; @@ -287,6 +294,123 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, gseq.g += new_glyphs; gseq.count += new_glyphs; assert(gseq.count <= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0])); +#else + + struct foo_cache { + uint8_t *data; + cairo_surface_t *surf; + int left; + int top; + }; + static struct foo_cache foo_cache[4][256] = {0}; + + cairo_surface_t *glyph = NULL; + double left, top; + + struct foo_cache *e = strnlen(cell->c, 4) == 1 + ? &foo_cache[cell->attrs.italic << 1 | cell->attrs.bold][(unsigned char)cell->c[0]] + : NULL; + + if (e == NULL || e->data == NULL) { + wchar_t wc; + int res __attribute__((unused)) = mbstowcs(&wc, cell->c, 1); + if (res != 1) + return; + + FT_Face ft_face = cairo_ft_scaled_font_lock_face(font->font); + + FT_UInt glyph_idx = FT_Get_Char_Index(ft_face, wc); + FT_Error ft_err = FT_Load_Glyph(ft_face, glyph_idx, FT_LOAD_DEFAULT); + if (ft_err != 0) { + LOG_ERR("FT_Load_Glyph"); + cairo_ft_scaled_font_unlock_face(font->font); + goto done; + } + + ft_err = FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_NORMAL); + if (ft_err != 0) { + LOG_ERR("FT_Render_Glyph"); + cairo_ft_scaled_font_unlock_face(font->font); + goto done; + } + + FT_Bitmap *bitmap = &ft_face->glyph->bitmap; + assert(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY || + bitmap->pixel_mode == FT_PIXEL_MODE_MONO); + + cairo_format_t cr_format = bitmap->pixel_mode == FT_PIXEL_MODE_GRAY + ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_A1; + + int stride = cairo_format_stride_for_width(cr_format, buf->width); + assert(stride >= bitmap->pitch); + + uint8_t *copy = malloc(bitmap->rows * stride); + + switch (bitmap->pixel_mode) { + case FT_PIXEL_MODE_MONO: { + for (size_t r = 0; r < bitmap->rows; r++) { + for (size_t c = 0; c < bitmap->width; c++) { + uint8_t v = bitmap->buffer[r * bitmap->pitch + c]; + uint8_t reversed = 0; + for (size_t i = 0; i < 8; i++) { + reversed |= (v & 1) << (7 - i); + v >>= 1; + } + copy[r * stride + c] = reversed; + } + } + break; + } + + case FT_PIXEL_MODE_GRAY: + for (size_t r = 0; r < bitmap->rows; r++) { + for (size_t c = 0; c < bitmap->width; c++) + copy[r * stride + c] = bitmap->buffer[r * bitmap->pitch + c]; + } + break; + + default: + LOG_ERR("unimplemented FT bitmap pixel mode: %d", bitmap->pixel_mode); + abort(); + break; + } + + glyph = cairo_image_surface_create_for_data( + copy, cr_format, bitmap->width, bitmap->rows, stride); + + left = ft_face->glyph->bitmap_left; + top = ft_face->glyph->bitmap_top; + + assert(cairo_surface_status(glyph) == CAIRO_STATUS_SUCCESS); + + if (e != NULL) { + e->data = copy; + e->surf = glyph; + e->left = left; + e->top = top; + } + + cairo_ft_scaled_font_unlock_face(font->font); + } else { + glyph = e->surf; + left = e->left; + top = e->top; + } + + assert(glyph != NULL); + cairo_set_source_rgb(buf->cairo, fg.r, fg.g, fg.b); + cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); + cairo_mask_surface(buf->cairo, glyph, x + left, y + term->fextents.ascent - top); + + if (e == NULL) { + void *raw = cairo_image_surface_get_data(glyph); + cairo_surface_destroy(glyph); + free(raw); + } + +done: + return; +#endif } static void