font: load all fallbacks when instantiating a primary font

The caching of fallback fonts didn't work. It "worked" because we
didn't free the fonts... When we started doing that, the fallback
fonts were no longer cached.

Another solution would have been to keep the cached fallback fonts
around until exit, and free them there. But that didn't seem very
clean.

So, for now, load *all* fallbacks when instantiating a
primary (non-fallback) font.

Note that this slows down initial startup time.
This commit is contained in:
Daniel Eklöf 2019-10-17 17:43:40 +02:00
parent 5a80bcff18
commit e218e19ea3
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 44 additions and 115 deletions

155
font.c
View file

@ -20,12 +20,6 @@
static FT_Library ft_lib; static FT_Library ft_lib;
static mtx_t ft_lock; static mtx_t ft_lock;
struct font_cache_entry {
uint64_t hash;
struct font *font;
};
static tll(struct font_cache_entry) font_cache = tll_init();
static const size_t cache_size = 512; static const size_t cache_size = 512;
static void __attribute__((constructor)) static void __attribute__((constructor))
@ -39,8 +33,6 @@ init(void)
static void __attribute__((destructor)) static void __attribute__((destructor))
fini(void) fini(void)
{ {
assert(tll_length(font_cache) == 0);
mtx_destroy(&ft_lock); mtx_destroy(&ft_lock);
FT_Done_FreeType(ft_lib); FT_Done_FreeType(ft_lib);
FcFini(); FcFini();
@ -84,14 +76,11 @@ underline_strikeout_metrics(struct font *font)
} }
static bool static bool
from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, const font_list_t *fallbacks, from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx,
const char *attributes, struct font *font, bool is_fallback) struct font *font, bool is_fallback)
{ {
memset(font, 0, sizeof(*font)); memset(font, 0, sizeof(*font));
size_t attr_len = attributes == NULL ? 0 : strlen(attributes);
bool have_attrs = attr_len > 0;
FcChar8 *face_file = NULL; FcChar8 *face_file = NULL;
FcPattern *final_pattern = NULL; FcPattern *final_pattern = NULL;
int font_idx = -1; int font_idx = -1;
@ -211,6 +200,7 @@ from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, const font_li
case FC_LCD_LEGACY: font->lcd_filter = FT_LCD_FILTER_LEGACY; break; case FC_LCD_LEGACY: font->lcd_filter = FT_LCD_FILTER_LEGACY; break;
} }
font->name = strdup((char *)face_file);
FcPatternDestroy(final_pattern); FcPatternDestroy(final_pattern);
mtx_init(&font->lock, mtx_plain); mtx_init(&font->lock, mtx_plain);
@ -236,79 +226,13 @@ from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, const font_li
font->glyph_cache = calloc(cache_size, sizeof(font->glyph_cache[0])); font->glyph_cache = calloc(cache_size, sizeof(font->glyph_cache[0]));
} }
if (fallbacks != NULL) {
tll_foreach(*fallbacks, it) {
size_t len = strlen(it->item) + (have_attrs ? 1 : 0) + attr_len + 1;
char *fallback = malloc(len);
strcpy(fallback, it->item);
if (have_attrs) {
strcat(fallback, ":");
strcat(fallback, attributes);
}
LOG_DBG("%s: adding fallback: %s", it->item, fallback);
tll_push_back(font->fallbacks, fallback);
}
}
underline_strikeout_metrics(font); underline_strikeout_metrics(font);
return true; return true;
} }
static uint64_t
hash_font(const char *base_name, const font_list_t *fallbacks,
const char *attributes, bool is_fallback)
{
#define rot(h, n) (((h) << (n)) | ((h) >> (64 - (n))))
/* TODO: better string hash */
uint64_t hash = 0;
for (size_t i = 0; i < strlen(base_name); i++)
hash = rot(hash, 7) ^ base_name[i];
if (fallbacks != NULL) {
tll_foreach(*fallbacks, it) {
for (size_t i = 0; i < strlen(it->item); i++)
hash = rot(hash, 17) ^ it->item[i];
}
}
if (attributes != NULL) {
for (size_t i = 0; i < strlen(attributes); i++)
hash = rot(hash, 11) ^ attributes[i];
}
if (is_fallback)
hash = rot(hash, 27);
return hash;
#undef rot
}
static struct font * static struct font *
from_name(const char *base_name, const font_list_t *fallbacks, from_name(const char *name, bool is_fallback)
const char *attributes, bool is_fallback)
{ {
uint64_t hash = hash_font(base_name, fallbacks, attributes, is_fallback);
tll_foreach(font_cache, it) {
if (it->item.hash == hash) {
it->item.font->ref_counter++;
return it->item.font;
}
}
size_t attr_len = attributes == NULL ? 0 : strlen(attributes);
bool have_attrs = attr_len > 0;
char name[strlen(base_name) + (have_attrs ? 1 : 0) + attr_len + 1];
strcpy(name, base_name);
if (have_attrs){
strcat(name, ":");
strcat(name, attributes);
}
LOG_DBG("instantiating %s", name); LOG_DBG("instantiating %s", name);
FcPattern *pattern = FcNameParse((const unsigned char *)name); FcPattern *pattern = FcNameParse((const unsigned char *)name);
@ -335,7 +259,7 @@ from_name(const char *base_name, const font_list_t *fallbacks,
struct font *font = malloc(sizeof(*font)); struct font *font = malloc(sizeof(*font));
if (!from_font_set(pattern, fonts, 0, fallbacks, attributes, font, is_fallback)) { if (!from_font_set(pattern, fonts, 0, font, is_fallback)) {
free(font); free(font);
FcFontSetDestroy(fonts); FcFontSetDestroy(fonts);
FcPatternDestroy(pattern); FcPatternDestroy(pattern);
@ -347,7 +271,7 @@ from_name(const char *base_name, const font_list_t *fallbacks,
FcPatternDestroy(pattern); FcPatternDestroy(pattern);
} }
tll_push_back(font_cache, ((struct font_cache_entry){.hash = hash, .font = font})); LOG_DBG("instantiated: %s", font->name);
return font; return font;
} }
@ -357,21 +281,39 @@ font_from_name(font_list_t names, const char *attributes)
if (tll_length(names) == 0) if (tll_length(names) == 0)
return false; return false;
font_list_t fallbacks = tll_init(); struct font *res = NULL;
bool skip_first = true;
bool have_attrs = attributes != NULL && strlen(attributes) > 0;
size_t attr_len = have_attrs ? strlen(attributes) + 1 : 0;
bool first = true;
tll_foreach(names, it) { tll_foreach(names, it) {
if (skip_first) { const char *base_name = it->item;
skip_first = false;
char primary_name[strlen(base_name) + attr_len + 1];
strcpy(primary_name, base_name);
if (have_attrs) {
strcat(primary_name, ":");
strcat(primary_name, attributes);
}
struct font *font = from_name(primary_name, !first);
if (font == NULL) {
if (first)
return NULL;
continue; continue;
} }
tll_push_back(fallbacks, it->item); if (first) {
res = font;
first = false;
continue;
}
tll_push_back(res->fallbacks, font);
} }
struct font *font = from_name(tll_front(names), &fallbacks, attributes, false); return res;
tll_free(fallbacks);
return font;
} }
static size_t static size_t
@ -398,16 +340,10 @@ glyph_for_wchar(const struct font *font, wchar_t wc, struct glyph *glyph)
if (idx == 0) { if (idx == 0) {
/* No glyph in this font, try fallback fonts */ /* No glyph in this font, try fallback fonts */
/* Try user configured fallback fonts */
tll_foreach(font->fallbacks, it) { tll_foreach(font->fallbacks, it) {
struct font *fallback = from_name(it->item, NULL, "", true); if (glyph_for_wchar(it->item, wc, glyph)) {
if (fallback == NULL) LOG_DBG("%C: used fallback: %s (fixup = %f)",
continue; wc, it->item->name, it->item->pixel_size_fixup);
if (glyph_for_wchar(fallback, wc, glyph)) {
LOG_DBG("%C: used fallback %s (fixup = %f)",
wc, it->item, fallback->pixel_size_fixup);
font_destroy(fallback);
return true; return true;
} }
} }
@ -426,8 +362,7 @@ glyph_for_wchar(const struct font *font, wchar_t wc, struct glyph *glyph)
if (font->fc_loaded_fallbacks[i] == NULL) { if (font->fc_loaded_fallbacks[i] == NULL) {
/* Load font */ /* Load font */
struct font *fallback = malloc(sizeof(*fallback)); struct font *fallback = malloc(sizeof(*fallback));
if (!from_font_set(font->fc_pattern, font->fc_fonts, i, NULL, if (!from_font_set(font->fc_pattern, font->fc_fonts, i, fallback, true))
"", fallback, true))
{ {
LOG_WARN("failed to load fontconfig fallback font"); LOG_WARN("failed to load fontconfig fallback font");
free(fallback); free(fallback);
@ -444,7 +379,8 @@ glyph_for_wchar(const struct font *font, wchar_t wc, struct glyph *glyph)
assert(font->fc_loaded_fallbacks[i] != NULL); assert(font->fc_loaded_fallbacks[i] != NULL);
if (glyph_for_wchar(font->fc_loaded_fallbacks[i], wc, glyph)) { if (glyph_for_wchar(font->fc_loaded_fallbacks[i], wc, glyph)) {
LOG_DBG("%C: used fontconfig fallback", wc); LOG_DBG("%C: used fontconfig fallback: %s",
wc, font->fc_loaded_fallbacks[i]->name);
return true; return true;
} }
} }
@ -661,7 +597,8 @@ font_destroy(struct font *font)
if (--font->ref_counter > 0) if (--font->ref_counter > 0)
return; return;
tll_free_and_free(font->fallbacks, free); free(font->name);
tll_free_and_free(font->fallbacks, font_destroy);
if (font->face != NULL) { if (font->face != NULL) {
mtx_lock(&ft_lock); mtx_lock(&ft_lock);
@ -703,15 +640,5 @@ font_destroy(struct font *font)
free(font->glyph_cache[i]); free(font->glyph_cache[i]);
} }
free(font->glyph_cache); free(font->glyph_cache);
tll_foreach(font_cache, it) {
if (it->item.font == font) {
tll_remove(font_cache, it);
free(font);
return;
}
}
LOG_ERR("font not found in cache");
free(font); free(font);
} }

4
font.h
View file

@ -30,6 +30,8 @@ struct glyph {
typedef tll(struct glyph) hash_entry_t; typedef tll(struct glyph) hash_entry_t;
struct font { struct font {
char *name;
mtx_t lock; mtx_t lock;
FT_Face face; FT_Face face;
int load_flags; int load_flags;
@ -50,7 +52,7 @@ struct font {
} strikeout; } strikeout;
bool is_fallback; bool is_fallback;
tll(char *) fallbacks; tll(struct font *) fallbacks;
size_t ref_counter; size_t ref_counter;