font: set FT load and render flags from FontConfig properties

This commit is contained in:
Daniel Eklöf 2019-07-29 20:10:55 +02:00
parent fe882bddba
commit 4302d3eb68
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
4 changed files with 129 additions and 21 deletions

139
font.c
View file

@ -2,6 +2,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <wchar.h>
#include <assert.h>
#include <fontconfig/fontconfig.h>
@ -9,6 +10,8 @@
#define LOG_MODULE "font"
#include "log.h"
#define min(x, y) ((x) < (y) ? (x) : (y))
static FT_Library ft_lib;
static void __attribute__((constructor))
@ -88,40 +91,128 @@ font_from_name(const char *name, struct font *font)
if (ft_err != 0)
LOG_ERR("%s: failed to create FreeType face", face_file);
/* TODO: use FT_Set_Char_Size() if FC_PIXEL_SIZE doesn't exist, and use size instead? */
if ((ft_err = FT_Set_Pixel_Sizes(ft_face, 0, size)) != 0)
LOG_WARN("failed to set FreeType pixel sizes");
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, fc_antialias;
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);
mtx_init(&font->lock, mtx_plain);
font->face = ft_face;
font->load_flags = load_flags;
font->render_flags = render_flags;
font_populate_glyph_cache(font);
return true;
}
bool
font_glyph_for_utf8(const struct font *font, const char *utf8,
font_glyph_for_utf8(struct font *font, const char *utf8,
struct glyph *glyph)
{
mbstate_t ps = {0};
wchar_t wc;
if (mbstowcs(&wc, utf8, 1) < 0)
if (mbrtowc(&wc, utf8, 4, &ps) < 0) {
LOG_ERR("FAILED: %.4s", utf8);
return false;
}
wprintf(L"CONVERTED: %.1s\n", &wc);
mtx_lock(&font->lock);
/*
* 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);
FT_Error err = FT_Load_Glyph(font->face, idx, FT_LOAD_DEFAULT);
if (err != 0)
return false;
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, FT_RENDER_MODE_NORMAL);
err = FT_Render_Glyph(font->face->glyph, font->render_flags);
if (err != 0)
return false;
goto err;
assert(font->face->glyph->format == FT_GLYPH_FORMAT_BITMAP);
@ -136,17 +227,17 @@ font_glyph_for_utf8(const struct font *font, const char *utf8,
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; c++) {
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 < 8; i++) {
reversed |= (v & 1) << (7 - i);
v >>= 1;
}
for (size_t i = 0; i < min(8, bitmap->width - c * 8); i++)
reversed |= ((v >> (7 - i)) & 1) << i;
data[r * stride + c] = reversed;
}
}
@ -163,7 +254,7 @@ font_glyph_for_utf8(const struct font *font, const char *utf8,
LOG_ERR("unimplemented FreeType bitmap pixel mode: %d",
bitmap->pixel_mode);
free(data);
return false;
goto err;
}
cairo_surface_t *surf = cairo_image_surface_create_for_data(
@ -172,7 +263,7 @@ font_glyph_for_utf8(const struct font *font, const char *utf8,
if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
free(data);
cairo_surface_destroy(surf);
return false;
goto err;
}
*glyph = (struct glyph){
@ -180,8 +271,18 @@ font_glyph_for_utf8(const struct font *font, const char *utf8,
.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
@ -196,4 +297,6 @@ font_destroy(struct font *font)
if (font->cache[i].data != NULL)
free(font->cache[i].data);
}
mtx_destroy(&font->lock);
}

4
font.h
View file

@ -1,9 +1,11 @@
#pragma once
#include <stdbool.h>
#include <threads.h>
#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);
struct font *font, const char *utf8, struct glyph *glyph);
void font_destroy(struct font *font);

View file

@ -10,7 +10,7 @@ project('foot', 'c',
is_debug_build = get_option('buildtype').startswith('debug')
add_project_arguments(
['-D_GNU_SOURCE',
['-D_GNU_SOURCE=200809L',
#'-DF00SEL_VERSION=@0@'.format(version)] +
] +
(is_debug_build ? ['-D_DEBUG'] : []),

View file

@ -215,6 +215,9 @@ struct glyph {
struct font {
FT_Face face;
int load_flags;
int render_flags;
FT_LcdFilter lcd_filter;
struct {
double position;
double thickness;
@ -225,6 +228,7 @@ struct font {
} strikeout;
struct glyph cache[256];
mtx_t lock;
};
enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR };
@ -326,7 +330,6 @@ struct terminal {
struct grid *grid;
struct font fonts[4];
//cairo_font_extents_t fextents;
struct {
int height;
int descent;