diff --git a/font.c b/font.c index 6842a439..9c4a9b50 100644 --- a/font.c +++ b/font.c @@ -106,6 +106,14 @@ from_name(const char *base_name, const font_list_t *fallbacks, const char *attri return false; } + FcBool scalable; + if (FcPatternGetBool(final_pattern, FC_SCALABLE, 0, &scalable) != FcResultMatch) + scalable = FcTrue; + + double pixel_fixup; + if (FcPatternGetDouble(final_pattern, "pixelsizefixupfactor", 0, &pixel_fixup) != FcResultMatch) + pixel_fixup = 1.; + LOG_DBG("loading: %s", face_file); mtx_lock(&ft_lock); @@ -199,9 +207,10 @@ from_name(const char *base_name, const font_list_t *fallbacks, const char *attri mtx_init(&font->lock, mtx_plain); font->face = ft_face; - font->load_flags = load_flags; + font->load_flags = load_flags | FT_LOAD_COLOR; font->render_flags = render_flags; font->is_fallback = is_fallback; + font->pixel_size_fixup = scalable ? pixel_fixup : 1.; if (fallbacks != NULL) { tll_foreach(*fallbacks, it) { @@ -222,7 +231,6 @@ from_name(const char *base_name, const font_list_t *fallbacks, const char *attri if (is_fallback) return true; - //font_populate_glyph_cache(font); font->cache = calloc(cache_size, sizeof(font->cache[0])); return true; } @@ -272,15 +280,16 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) FT_UInt idx = FT_Get_Char_Index(font->face, wc); if (idx == 0) { - /* LOG_DBG("no glyph found for %02x %02x %02x %02x", */ - /* (unsigned char)utf8[0], (unsigned char)utf8[1], */ - /* (unsigned char)utf8[2], (unsigned char)utf8[3]); */ + LOG_DBG("no glyph found for %02x %02x %02x %02x", + (unsigned char)utf8[0], (unsigned char)utf8[1], + (unsigned char)utf8[2], (unsigned char)utf8[3]); /* Try fallback fonts */ tll_foreach(font->fallbacks, it) { struct font fallback; if (from_name(it->item, NULL, "", &fallback, true)) { if (glyph_for_wchar(&fallback, wc, glyph)) { + LOG_DBG("used fallback %s (fixup = %f)", it->item, fallback.pixel_size_fixup); font_destroy(&fallback); return true; } @@ -306,11 +315,15 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) assert(font->face->glyph->format == FT_GLYPH_FORMAT_BITMAP); FT_Bitmap *bitmap = &font->face->glyph->bitmap; - assert(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY || - bitmap->pixel_mode == FT_PIXEL_MODE_MONO); + assert(bitmap->pixel_mode == FT_PIXEL_MODE_MONO || + bitmap->pixel_mode == FT_PIXEL_MODE_GRAY || + bitmap->pixel_mode == FT_PIXEL_MODE_BGRA); - cairo_format_t cr_format = bitmap->pixel_mode == FT_PIXEL_MODE_GRAY - ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_A1; + /* 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; int stride = cairo_format_stride_for_width(cr_format, bitmap->width); assert(stride >= bitmap->pitch); @@ -318,6 +331,7 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) uint8_t *data = malloc(bitmap->rows * stride); assert(bitmap->pitch >= 0); + /* Convert FT bitmap to cairo surface (well, the backing image) */ switch (bitmap->pixel_mode) { case FT_PIXEL_MODE_MONO: for (size_t r = 0; r < bitmap->rows; r++) { @@ -339,6 +353,11 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) } break; + case FT_PIXEL_MODE_BGRA: + assert(stride == bitmap->pitch); + memcpy(data, bitmap->buffer, bitmap->rows * bitmap->pitch); + break; + default: LOG_ERR("unimplemented FreeType bitmap pixel mode: %d", bitmap->pixel_mode); @@ -357,10 +376,11 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) *glyph = (struct glyph){ .wc = wc, - .data = data, + .width = wcwidth(wc), .surf = surf, .left = font->face->glyph->bitmap_left, .top = font->face->glyph->bitmap_top, + .pixel_size_fixup = font->pixel_size_fixup, }; return true; @@ -428,30 +448,25 @@ font_destroy(struct font *font) mtx_unlock(&ft_lock); } - if (font->cache != NULL) { - for (size_t i = 0; i < cache_size; i++) { - if (font->cache[i] == NULL) - continue; - - tll_foreach(*font->cache[i], it) { - cairo_surface_destroy(it->item.surf); - free(it->item.data); - } - - tll_free(*font->cache[i]); - free(font->cache[i]); - } - free(font->cache); - } - -#if 0 - for (size_t i = 0; i < 256; i++) { - if (font->cache[i].surf != NULL) - cairo_surface_destroy(font->cache[i].surf); - if (font->cache[i].data != NULL) - free(font->cache[i].data); - } -#endif - mtx_destroy(&font->lock); + + if (font->cache == NULL) + return; + + for (size_t i = 0; i < cache_size; i++) { + if (font->cache[i] == NULL) + continue; + + tll_foreach(*font->cache[i], it) { + cairo_surface_flush(it->item.surf); + void *image = cairo_image_surface_get_data(it->item.surf); + + cairo_surface_destroy(it->item.surf); + free(image); + } + + tll_free(*font->cache[i]); + free(font->cache[i]); + } + free(font->cache); } diff --git a/font.h b/font.h index 0f2c9833..9746eaac 100644 --- a/font.h +++ b/font.h @@ -15,18 +15,13 @@ typedef tll(const char *) font_list_t; struct glyph { wchar_t wc; + int width; - void *data; cairo_surface_t *surf; int left; int top; -#if 0 - int format; - int width; - int height; - int stride; -#endif + double pixel_size_fixup; }; typedef tll(struct glyph) hash_entry_t; @@ -36,10 +31,13 @@ struct font { int load_flags; int render_flags; FT_LcdFilter lcd_filter; + double pixel_size_fixup; /* Scale factor - should only be used with ARGB32 glyphs */ + struct { double position; double thickness; } underline; + struct { double position; double thickness; @@ -48,7 +46,6 @@ struct font { bool is_fallback; tll(char *) fallbacks; - //struct glyph cache[256]; hash_entry_t **cache; mtx_t lock; }; diff --git a/main.c b/main.c index d8b94c12..55639c65 100644 --- a/main.c +++ b/main.c @@ -493,10 +493,10 @@ main(int argc, char *const *argv) int descent = ft_face->size->metrics.descender / 64; int ascent = ft_face->size->metrics.ascender / 64; - term.fextents.height = height; - term.fextents.descent = -descent; - term.fextents.ascent = ascent; - term.fextents.max_x_advance = max_x_advance; + term.fextents.height = height * term.fonts[0].pixel_size_fixup; + term.fextents.descent = -descent * term.fonts[0].pixel_size_fixup; + term.fextents.ascent = ascent * term.fonts[0].pixel_size_fixup; + term.fextents.max_x_advance = max_x_advance * term.fonts[0].pixel_size_fixup; LOG_DBG("metrics: height: %d, descent: %d, ascent: %d, x-advance: %d", height, descent, ascent, max_x_advance); diff --git a/render.c b/render.c index 0a04a59b..bd96149b 100644 --- a/render.c +++ b/render.c @@ -139,6 +139,7 @@ static void render_cell(struct terminal *term, cairo_t *cr, struct cell *cell, int col, int row, bool has_cursor) { + if (cell->attrs.clean) return; @@ -215,12 +216,31 @@ render_cell(struct terminal *term, cairo_t *cr, struct font *font = attrs_to_font(term, &cell->attrs); const struct glyph *glyph = font_glyph_for_utf8(font, cell->c); - if (glyph != NULL) { - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + if (glyph == NULL) + return; + + 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) { + /* Glyph surface is a pre-rendered image (typically a color emoji...) */ + cairo_set_source_surface(cr, glyph->surf, 0, 0); + cairo_paint(cr); + } else { + /* Glyph surface is an alpha mask */ + //assert(glyph->pixel_size_fixup == 1.); cairo_set_source_rgb(cr, fg.r, fg.g, fg.b); - cairo_mask_surface( - cr, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top); + cairo_mask_surface(cr, glyph->surf, 0, 0); + //cr, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top); } + cairo_restore(cr); } static void @@ -284,7 +304,7 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, static void render_row(struct terminal *term, cairo_t *cr, struct row *row, int row_no) { - for (int col = 0; col < term->cols; col++) + for (int col = term->cols - 1; col >= 0; col--) render_cell(term, cr, &row->cells[col], col, row_no, false); } diff --git a/vt.c b/vt.c index 7fbe47b3..1d62c30a 100644 --- a/vt.c +++ b/vt.c @@ -691,6 +691,7 @@ pre_print(struct terminal *term) } } +#include static inline void post_print(struct terminal *term) { @@ -721,12 +722,9 @@ action_print_utf8(struct terminal *term) struct row *row = term->grid->cur_row; struct cell *cell = &row->cells[term->cursor.col]; -#if 0 - term_damage_update(term, term->cursor.linear, 1); -#else + row->dirty = true; cell->attrs.clean = 0; -#endif print_insert(term); @@ -736,6 +734,29 @@ action_print_utf8(struct terminal *term) term->vt.utf8.idx = 0; cell->attrs = term->vt.attrs; + + /* Hack: zero- and double-width characters */ + mbstate_t ps = {0}; + wchar_t wc; + if (mbrtowc(&wc, cell->c, 4, &ps) >= 0) { + int width = wcwidth(wc); + if (width <= 0) { + /* Skip post_print() below - i.e. don't advance cursor */ + return; + } + + /* Advance cursor the 'additional' columns (last step is done + * by post_print()) */ + for (int i = 1; i < width && term->cursor.col < term->cols - 1; i++) { + term_cursor_right(term, 1); + + assert(term->cursor.col < term->cols); + struct cell *cell = &row->cells[term->cursor.col]; + cell->c[0] = '\0'; + cell->attrs.clean = 0; + } + } + post_print(term); } @@ -746,12 +767,9 @@ action_print(struct terminal *term, uint8_t c) struct row *row = term->grid->cur_row; struct cell *cell = &row->cells[term->cursor.col]; -#if 0 - term_damage_update(term, term->cursor.linear, 1); -#else + row->dirty = true; cell->attrs.clean = 0; -#endif print_insert(term);