Merge branch 'use-freetype-to-render-glyphs'

This commit is contained in:
Daniel Eklöf 2019-07-29 20:12:33 +02:00
commit d1b88f67e4
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
7 changed files with 327 additions and 140 deletions

View file

@ -1,5 +1,5 @@
pkgname=foot pkgname=foot
pkgver=0.0.r2.g7379198 pkgver=0.0.r136.g90d357b
pkgrel=1 pkgrel=1
pkgdesc="A wayland native terminal emulator" pkgdesc="A wayland native terminal emulator"
arch=('x86_64') arch=('x86_64')

295
font.c
View file

@ -2,46 +2,57 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <wchar.h>
#include <assert.h> #include <assert.h>
#include <fontconfig/fontconfig.h> #include <fontconfig/fontconfig.h>
#include <cairo-ft.h>
#define LOG_MODULE "font" #define LOG_MODULE "font"
#include "log.h" #include "log.h"
#define min(x, y) ((x) < (y) ? (x) : (y))
static FT_Library ft_lib;
static void __attribute__((constructor)) static void __attribute__((constructor))
init(void) init(void)
{ {
FcInit(); FcInit();
FT_Init_FreeType(&ft_lib);
} }
static void __attribute__((destructor)) static void __attribute__((destructor))
fini(void) fini(void)
{ {
FcFini(); FcFini();
FT_Done_FreeType(ft_lib);
} }
cairo_scaled_font_t *
font_from_name(const char *name) static void
font_populate_glyph_cache(struct font *font)
{ {
memset(font->cache, 0, sizeof(font->cache));
for (size_t i = 0; i < 256; i++)
font_glyph_for_utf8(font, &(char){i}, &font->cache[i]);
}
bool
font_from_name(const char *name, struct font *font)
{
memset(font, 0, sizeof(*font));
FcPattern *pattern = FcNameParse((const unsigned char *)name); FcPattern *pattern = FcNameParse((const unsigned char *)name);
if (pattern == NULL) { if (pattern == NULL) {
LOG_ERR("%s: failed to lookup font", name); LOG_ERR("%s: failed to lookup font", name);
return NULL; return false;
} }
if (!FcConfigSubstitute(NULL, pattern, FcMatchPattern)) { if (!FcConfigSubstitute(NULL, pattern, FcMatchPattern)) {
LOG_ERR("%s: failed to do config substitution", name); LOG_ERR("%s: failed to do config substitution", name);
FcPatternDestroy(pattern); FcPatternDestroy(pattern);
return NULL; return false;
} }
cairo_font_options_t *options = cairo_font_options_create();
cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_DEFAULT);
cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_DEFAULT);
cairo_font_options_set_subpixel_order(options, CAIRO_SUBPIXEL_ORDER_DEFAULT);
cairo_ft_font_options_substitute(options, pattern);
FcDefaultSubstitute(pattern); FcDefaultSubstitute(pattern);
FcResult result; FcResult result;
@ -50,54 +61,242 @@ font_from_name(const char *name)
if (final_pattern == NULL) { if (final_pattern == NULL) {
LOG_ERR("%s: failed to match font", name); LOG_ERR("%s: failed to match font", name);
return NULL; return false;
} }
FcBool fc_hinting, fc_antialias; FcChar8 *face_file = NULL;
if (FcPatternGetBool(final_pattern, FC_HINTING,0, &fc_hinting) != FcResultMatch) if (FcPatternGetString(final_pattern, FC_FT_FACE, 0, &face_file) != FcResultMatch) {
fc_hinting = FcTrue; if (FcPatternGetString(final_pattern, FC_FILE, 0, &face_file) != FcResultMatch) {
LOG_ERR("no font file name available");
FcPatternDestroy(final_pattern);
return false;
}
}
if (FcPatternGetBool(final_pattern, FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) double dpi;
fc_antialias = FcTrue; if (FcPatternGetDouble(final_pattern, FC_DPI, 0, &dpi) != FcResultMatch)
dpi = 96;
cairo_font_options_set_hint_style(
options, fc_hinting ? CAIRO_HINT_STYLE_DEFAULT : CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_antialias(
options, fc_antialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
double size; double size;
if (FcPatternGetDouble(final_pattern, FC_PIXEL_SIZE, 0, &size)) { if (FcPatternGetDouble(final_pattern, FC_PIXEL_SIZE, 0, &size)) {
LOG_ERR("%s: failed to get size", name); LOG_ERR("%s: failed to get size", name);
FcPatternDestroy(final_pattern); FcPatternDestroy(final_pattern);
return NULL; return false;
} }
cairo_font_face_t *face = cairo_ft_font_face_create_for_pattern( LOG_DBG("loading: %s", face_file);
final_pattern);
FT_Face ft_face;
FT_Error ft_err = FT_New_Face(ft_lib, (const char *)face_file, 0, &ft_face);
if (ft_err != 0)
LOG_ERR("%s: failed to create FreeType face", face_file);
if ((ft_err = FT_Set_Char_Size(ft_face, size * 64, 0, 0, 0)) != 0)
LOG_WARN("failed to set character size");
FcBool fc_hinting;
if (FcPatternGetBool(final_pattern, FC_HINTING,0, &fc_hinting) != FcResultMatch)
fc_hinting = FcTrue;
FcBool fc_antialias;
if (FcPatternGetBool(final_pattern, FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch)
fc_antialias = FcTrue;
int fc_hintstyle;
if (FcPatternGetInteger(final_pattern, FC_HINT_STYLE, 0, &fc_hintstyle) != FcResultMatch)
fc_hintstyle = FC_HINT_SLIGHT;
int fc_rgba;
if (FcPatternGetInteger(final_pattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch)
fc_rgba = FC_RGBA_UNKNOWN;
int load_flags = 0;
if (!fc_antialias) {
if (!fc_hinting || fc_hintstyle == FC_HINT_NONE)
load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_NO_HINTING | FT_LOAD_TARGET_NORMAL;
else
load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO;
} else {
if (!fc_hinting || fc_hintstyle == FC_HINT_NONE)
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING | FT_LOAD_TARGET_NORMAL;
else if (fc_hinting && fc_hintstyle == FC_HINT_SLIGHT)
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LIGHT;
else if (fc_rgba == FC_RGBA_RGB) {
LOG_WARN("unimplemented: subpixel antialiasing");
// load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD;
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
} else if (fc_rgba == FC_RGBA_VRGB) {
LOG_WARN("unimplemented: subpixel antialiasing");
//load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD_V;
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
} else
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
}
FcBool fc_embeddedbitmap;
if (FcPatternGetBool(final_pattern, FC_EMBEDDED_BITMAP, 0, &fc_embeddedbitmap) != FcResultMatch)
fc_embeddedbitmap = FcTrue;
if (!fc_embeddedbitmap)
load_flags |= FT_LOAD_NO_BITMAP;
int render_flags = 0;
if (!fc_antialias)
render_flags |= FT_RENDER_MODE_MONO;
else {
if (false)
;
#if 0
if (fc_rgba == FC_RGBA_RGB)
render_flags |= FT_RENDER_MODE_LCD;
else if (fc_rgba == FC_RGBA_VRGB)
render_flags |= FT_RENDER_MODE_LCD_V;
#endif
else
render_flags |= FT_RENDER_MODE_NORMAL;
}
int fc_lcdfilter;
if (FcPatternGetInteger(final_pattern, FC_LCD_FILTER, 0, &fc_lcdfilter) != FcResultMatch)
fc_lcdfilter = FC_LCD_DEFAULT;
switch (fc_lcdfilter) {
case FC_LCD_NONE: font->lcd_filter = FT_LCD_FILTER_NONE; break;
case FC_LCD_DEFAULT: font->lcd_filter = FT_LCD_FILTER_DEFAULT; break;
case FC_LCD_LIGHT: font->lcd_filter = FT_LCD_FILTER_LIGHT; break;
case FC_LCD_LEGACY: font->lcd_filter = FT_LCD_FILTER_LEGACY; break;
}
FcPatternDestroy(final_pattern); FcPatternDestroy(final_pattern);
if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS) { mtx_init(&font->lock, mtx_plain);
LOG_ERR("%s: failed to create cairo font face", name); font->face = ft_face;
cairo_font_face_destroy(face); font->load_flags = load_flags;
return NULL; font->render_flags = render_flags;
} font_populate_glyph_cache(font);
return true;
cairo_matrix_t matrix, ctm; }
cairo_matrix_init_identity(&ctm);
cairo_matrix_init_scale(&matrix, size, size); bool
font_glyph_for_utf8(struct font *font, const char *utf8,
cairo_scaled_font_t *scaled_font = cairo_scaled_font_create( struct glyph *glyph)
face, &matrix, &ctm, options); {
mbstate_t ps = {0};
cairo_font_options_destroy(options); wchar_t wc;
cairo_font_face_destroy(face); if (mbrtowc(&wc, utf8, 4, &ps) < 0) {
LOG_ERR("FAILED: %.4s", utf8);
if (cairo_scaled_font_status(scaled_font) != CAIRO_STATUS_SUCCESS) { return false;
LOG_ERR("%s: failed to create scaled font", name); }
cairo_scaled_font_destroy(scaled_font);
return NULL; wprintf(L"CONVERTED: %.1s\n", &wc);
}
mtx_lock(&font->lock);
return scaled_font;
/*
* LCD filter is per library instance. Thus we need to re-set it
* every time...
*
* Also note that many freetype builds lack this feature
* (FT_CONFIG_OPTION_SUBPIXEL_RENDERING must be defined, and isn't
* by default) */
FT_Error err = FT_Library_SetLcdFilter(ft_lib, font->lcd_filter);
if (err != 0 && err != FT_Err_Unimplemented_Feature)
goto err;
FT_UInt idx = FT_Get_Char_Index(font->face, wc);
err = FT_Load_Glyph(font->face, idx, font->load_flags);
if (err != 0) {
LOG_ERR("load failed");
goto err;
}
err = FT_Render_Glyph(font->face->glyph, font->render_flags);
if (err != 0)
goto err;
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);
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);
assert(bitmap->pitch >= 0);
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 + 7) / 8; c++) {
uint8_t v = bitmap->buffer[r * bitmap->pitch + c];
uint8_t reversed = 0;
for (size_t i = 0; i < min(8, bitmap->width - c * 8); i++)
reversed |= ((v >> (7 - i)) & 1) << i;
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);
goto err;
}
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);
goto err;
}
*glyph = (struct glyph){
.data = data,
.surf = surf,
.left = font->face->glyph->bitmap_left,
.top = font->face->glyph->bitmap_top,
.format = cr_format,
.width = bitmap->width,
.height = bitmap->rows,
.stride = stride,
};
mtx_unlock(&font->lock);
return true;
err:
mtx_unlock(&font->lock);
return false;
}
void
font_destroy(struct font *font)
{
if (font->face != NULL)
FT_Done_Face(font->face);
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);
}
mtx_destroy(&font->lock);
} }

10
font.h
View file

@ -1,5 +1,11 @@
#pragma once #pragma once
#include <cairo.h> #include <stdbool.h>
#include <threads.h>
cairo_scaled_font_t *font_from_name(const char *name); #include "terminal.h"
bool font_from_name(const char *name, struct font *result);
bool font_glyph_for_utf8(
struct font *font, const char *utf8, struct glyph *glyph);
void font_destroy(struct font *font);

76
main.c
View file

@ -409,30 +409,29 @@ main(int argc, char *const *argv)
thrd_t keyboard_repeater_id; thrd_t keyboard_repeater_id;
thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term); thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term);
term.fonts[0].font = font_from_name(conf.font); if (!font_from_name(conf.font, &term.fonts[0]))
if (term.fonts[0].font == NULL)
goto out; goto out;
{ {
char fname[1024]; char fname[1024];
snprintf(fname, sizeof(fname), "%s:style=bold", conf.font); snprintf(fname, sizeof(fname), "%s:style=bold", conf.font);
term.fonts[1].font = font_from_name(fname); font_from_name(fname, &term.fonts[1]);
snprintf(fname, sizeof(fname), "%s:style=italic", conf.font); snprintf(fname, sizeof(fname), "%s:style=italic", conf.font);
term.fonts[2].font = font_from_name(fname); font_from_name(fname, &term.fonts[2]);
snprintf(fname, sizeof(fname), "%s:style=bold italic", conf.font); snprintf(fname, sizeof(fname), "%s:style=bold italic", conf.font);
term.fonts[3].font = font_from_name(fname); font_from_name(fname, &term.fonts[3]);
} }
/* Underline position and size */ /* Underline position and size */
for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) { for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) {
struct font *f = &term.fonts[i]; struct font *f = &term.fonts[i];
if (f->font == NULL) if (f->face == NULL)
continue; continue;
FT_Face ft_face = cairo_ft_scaled_font_lock_face(f->font); FT_Face ft_face = f->face;
double x_scale = ft_face->size->metrics.x_scale / 65526.; double x_scale = ft_face->size->metrics.x_scale / 65526.;
double height = ft_face->size->metrics.height / 64; double height = ft_face->size->metrics.height / 64;
@ -465,46 +464,27 @@ main(int argc, char *const *argv)
LOG_DBG("strikeout: pos=%f, thick=%f", LOG_DBG("strikeout: pos=%f, thick=%f",
f->strikeout.position, f->strikeout.thickness); f->strikeout.position, f->strikeout.thickness);
cairo_ft_scaled_font_unlock_face(f->font);
} }
cairo_scaled_font_extents(term.fonts[0].font, &term.fextents); {
FT_Face ft_face = term.fonts[0].face;
int max_x_advance = ft_face->size->metrics.max_advance / 64;
int height = ft_face->size->metrics.height / 64;
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;
LOG_WARN("metrics: height: %d, descent: %d, ascent: %d, x-advance: %d",
height, descent, ascent, max_x_advance);
}
term.cell_width = (int)ceil(term.fextents.max_x_advance); term.cell_width = (int)ceil(term.fextents.max_x_advance);
term.cell_height = (int)ceil(term.fextents.height); term.cell_height = (int)ceil(term.fextents.height);
LOG_DBG("font: height: %.2f, x-advance: %.2f",
term.fextents.height, term.fextents.max_x_advance);
assert(term.fextents.max_y_advance == 0);
/* Glyph cache */
for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) {
struct font *f = &term.fonts[i];
for (int j = 0; j < 256; j++) {
cairo_glyph_t *glyphs = NULL;
int count = 0;
char c = j;
cairo_status_t status = cairo_scaled_font_text_to_glyphs(
f->font, 0, 0 + term.fextents.ascent,
&c, 1, &glyphs, &count,
NULL, NULL, NULL);
if (status != CAIRO_STATUS_SUCCESS)
continue;
if (count == 0)
continue;
assert(glyphs != NULL);
assert(count == 1);
f->glyph_cache[j].glyphs = glyphs;
f->glyph_cache[j].count = count;
}
}
term.wl.display = wl_display_connect(NULL); term.wl.display = wl_display_connect(NULL);
if (term.wl.display == NULL) { if (term.wl.display == NULL) {
LOG_ERR("failed to connect to wayland; no compositor running?"); LOG_ERR("failed to connect to wayland; no compositor running?");
@ -916,15 +896,8 @@ out:
free(term.window_title); free(term.window_title);
tll_free_and_free(term.window_title_stack, free); tll_free_and_free(term.window_title_stack, free);
for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) { for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++)
struct font *f = &term.fonts[i]; font_destroy(&term.fonts[i]);
if (f->font != NULL)
cairo_scaled_font_destroy(f->font);
for (size_t j = 0; j < 256; j++)
cairo_glyph_free(f->glyph_cache[j].glyphs);
}
if (term.flash.fd != -1) if (term.flash.fd != -1)
close(term.flash.fd); close(term.flash.fd);
@ -944,4 +917,5 @@ out:
cairo_debug_reset_static_data(); cairo_debug_reset_static_data();
return ret; return ret;
} }

View file

@ -10,7 +10,7 @@ project('foot', 'c',
is_debug_build = get_option('buildtype').startswith('debug') is_debug_build = get_option('buildtype').startswith('debug')
add_project_arguments( add_project_arguments(
['-D_GNU_SOURCE', ['-D_GNU_SOURCE=200809L',
#'-DF00SEL_VERSION=@0@'.format(version)] + #'-DF00SEL_VERSION=@0@'.format(version)] +
] + ] +
(is_debug_build ? ['-D_DEBUG'] : []), (is_debug_build ? ['-D_DEBUG'] : []),
@ -23,7 +23,6 @@ math = cc.find_library('m')
threads = dependency('threads') threads = dependency('threads')
fontconfig = dependency('fontconfig') fontconfig = dependency('fontconfig')
cairo = dependency('cairo') cairo = dependency('cairo')
cairo_ft = dependency('cairo-ft')
wayland_protocols = dependency('wayland-protocols') wayland_protocols = dependency('wayland-protocols')
wayland_client = dependency('wayland-client') wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor') wayland_cursor = dependency('wayland-cursor')
@ -78,7 +77,7 @@ executable(
'tllist.h', 'tllist.h',
'vt.c', 'vt.h', 'vt.c', 'vt.h',
wl_proto_src + wl_proto_headers, wl_proto_src + wl_proto_headers,
dependencies: [threads, math, cairo, cairo_ft, fontconfig, wayland_client, wayland_cursor, xkb], dependencies: [threads, math, cairo, fontconfig, wayland_client, wayland_cursor, xkb],
install: true) install: true)
custom_target( custom_target(

View file

@ -13,6 +13,7 @@
#include "log.h" #include "log.h"
#include "shm.h" #include "shm.h"
#include "grid.h" #include "grid.h"
#include "font.h"
#define min(x, y) ((x) < (y) ? (x) : (y)) #define min(x, y) ((x) < (y) ? (x) : (y))
#define max(x, y) ((x) > (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y))
@ -59,15 +60,17 @@ gseq_flush(struct terminal *term, struct buffer *buf)
if (gseq.count == 0) if (gseq.count == 0)
return; return;
assert(NULL);
struct rgb fg = color_hex_to_rgb(gseq.foreground); struct rgb fg = color_hex_to_rgb(gseq.foreground);
if (gseq.attrs.dim) if (gseq.attrs.dim)
color_dim(&fg); color_dim(&fg);
#if 0
cairo_set_scaled_font(buf->cairo, attrs_to_font(term, &gseq.attrs)->font); cairo_set_scaled_font(buf->cairo, attrs_to_font(term, &gseq.attrs)->font);
cairo_set_source_rgb(buf->cairo, fg.r, fg.g, fg.b); cairo_set_source_rgb(buf->cairo, fg.r, fg.g, fg.b);
cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count); cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count);
#endif
gseq.g = gseq.glyphs; gseq.g = gseq.glyphs;
gseq.count = 0; gseq.count = 0;
} }
@ -255,38 +258,30 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
gseq.foreground = _fg; gseq.foreground = _fg;
} }
int new_glyphs
= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - gseq.count;
struct font *font = attrs_to_font(term, &cell->attrs); struct font *font = attrs_to_font(term, &cell->attrs);
struct glyph_cache *entry = cell->c[1] == '\0' struct glyph *glyph = NULL;
? &font->glyph_cache[(unsigned char)cell->c[0]] if (strnlen(cell->c, 4) == 1) {
: NULL; if (font->cache[(unsigned char)cell->c[0]].surf != NULL)
glyph = &font->cache[(unsigned char)cell->c[0]];
if (likely(entry != NULL && entry->glyphs != NULL)) {
/* Copy cached glyph(s) and upate position */
memcpy(gseq.g, entry->glyphs, entry->count * sizeof(gseq.g[0]));
for (size_t i = 0; i < entry->count; i++) {
gseq.g[i].x += x;
gseq.g[i].y += y;
}
new_glyphs = entry->count;
} else {
/* Must generate new glyph(s) */
cairo_status_t status = cairo_scaled_font_text_to_glyphs(
font->font, x, y + term->fextents.ascent,
cell->c, strnlen(cell->c, 4), &gseq.g, &new_glyphs,
NULL, NULL, NULL);
if (status != CAIRO_STATUS_SUCCESS)
return;
} }
gseq.g += new_glyphs; struct glyph _glyph;
gseq.count += new_glyphs; if (glyph == NULL) {
assert(gseq.count <= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0])); if (!font_glyph_for_utf8(font, cell->c, &_glyph))
return;
glyph = &_glyph;
}
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->surf, x + glyph->left, y + term->fextents.ascent - glyph->top);
if (glyph == &_glyph) {
cairo_surface_destroy(_glyph.surf);
free(_glyph.data);
}
} }
static void static void

View file

@ -6,6 +6,9 @@
#include <threads.h> #include <threads.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <cairo.h> #include <cairo.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <primary-selection-unstable-v1.h> #include <primary-selection-unstable-v1.h>
@ -203,13 +206,18 @@ struct primary {
uint32_t serial; uint32_t serial;
}; };
struct glyph_cache { struct glyph {
cairo_glyph_t *glyphs; void *data;
int count; cairo_surface_t *surf;
int left;
int top;
}; };
struct font { struct font {
cairo_scaled_font_t *font; FT_Face face;
int load_flags;
int render_flags;
FT_LcdFilter lcd_filter;
struct { struct {
double position; double position;
double thickness; double thickness;
@ -219,7 +227,8 @@ struct font {
double thickness; double thickness;
} strikeout; } strikeout;
struct glyph_cache glyph_cache[256]; struct glyph cache[256];
mtx_t lock;
}; };
enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR }; enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR };
@ -321,7 +330,12 @@ struct terminal {
struct grid *grid; struct grid *grid;
struct font fonts[4]; struct font fonts[4];
cairo_font_extents_t fextents; struct {
int height;
int descent;
int ascent;
int max_x_advance;
} fextents;
struct wayland wl; struct wayland wl;
struct { struct {