From 9e57ba21086abb15772ae7efda06e584d618cd19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jul 2019 20:37:59 +0200 Subject: [PATCH] font: add font_glyph_for_utf8() This function fills in a struct glyph with glyph bitmap data for the provided utf-8 character (essentially a FT_Bitmap wrapped in a cairo surface). --- font.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ font.h | 2 ++ render.c | 84 +++++++----------------------------------------------- terminal.h | 8 ++++-- 4 files changed, 94 insertions(+), 76 deletions(-) diff --git a/font.c b/font.c index 5686c12a..b22e4457 100644 --- a/font.c +++ b/font.c @@ -96,3 +96,79 @@ font_from_name(const char *name, struct font *font) font->face = ft_face; return true; } + +bool +font_glyph_for_utf8(const struct font *font, const char *utf8, + struct glyph *glyph) +{ + wchar_t wc; + if (mbstowcs(&wc, utf8, 1) < 0) + return false; + + FT_UInt idx = FT_Get_Char_Index(font->face, wc); + FT_Error err = FT_Load_Glyph(font->face, idx, FT_LOAD_DEFAULT); + if (err != 0) + return false; + + err = FT_Render_Glyph(font->face->glyph, FT_RENDER_MODE_NORMAL); + if (err != 0) + return false; + + FT_Bitmap *bitmap = &font->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, bitmap->width); + assert(stride >= bitmap->pitch); + + uint8_t *data = 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; + } + data[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++) + data[r * stride + c] = bitmap->buffer[r * bitmap->pitch + c]; + } + break; + + default: + LOG_ERR("unimplemented FreeType bitmap pixel mode: %d", + bitmap->pixel_mode); + free(data); + return false; + } + + cairo_surface_t *surf = cairo_image_surface_create_for_data( + data, cr_format, bitmap->width, bitmap->rows, stride); + + if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { + free(data); + cairo_surface_destroy(surf); + return false; + } + + *glyph = (struct glyph){ + .data = data, + .surf = surf, + .left = font->face->glyph->bitmap_left, + .top = font->face->glyph->bitmap_top, + }; + return true; +} diff --git a/font.h b/font.h index 90a1b6a7..d8efd509 100644 --- a/font.h +++ b/font.h @@ -4,3 +4,5 @@ #include "terminal.h" bool font_from_name(const char *name, struct font *result); +bool font_glyph_for_utf8( + const struct font *font, const char *utf8, struct glyph *glyph); diff --git a/render.c b/render.c index e1340a86..7bebbbce 100644 --- a/render.c +++ b/render.c @@ -5,8 +5,6 @@ #include #include -#include - #include #include @@ -15,6 +13,7 @@ #include "log.h" #include "shm.h" #include "grid.h" +#include "font.h" #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -312,81 +311,20 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, : 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 = font->face; - - 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"); + struct glyph g; + if (!font_glyph_for_utf8(font, cell->c, &g)) goto done; - } - - ft_err = FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_NORMAL); - if (ft_err != 0) { - LOG_ERR("FT_Render_Glyph"); - 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; + e->data = g.data; + e->surf = g.surf; + e->left = g.left; + e->top = g.top; } + + glyph = g.surf; + left = g.left; + top = g.top; } else { glyph = e->surf; left = e->left; diff --git a/terminal.h b/terminal.h index b5fea057..83efe69a 100644 --- a/terminal.h +++ b/terminal.h @@ -206,9 +206,11 @@ struct primary { uint32_t serial; }; -struct glyph_cache { - cairo_glyph_t *glyphs; - int count; +struct glyph { + void *data; + cairo_surface_t *surf; + int left; + int top; }; struct font {