2019-06-13 16:24:35 +02:00
|
|
|
#include "font.h"
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2019-07-30 18:04:28 +02:00
|
|
|
#include <stdint.h>
|
2019-06-13 16:24:35 +02:00
|
|
|
#include <stdbool.h>
|
2019-07-29 20:10:55 +02:00
|
|
|
#include <wchar.h>
|
2019-09-29 13:03:48 +02:00
|
|
|
#include <math.h>
|
2019-07-17 22:50:26 +02:00
|
|
|
#include <assert.h>
|
2019-07-30 18:04:28 +02:00
|
|
|
#include <threads.h>
|
2019-06-13 16:24:35 +02:00
|
|
|
|
2019-09-29 13:03:48 +02:00
|
|
|
#include <freetype/tttables.h>
|
|
|
|
|
|
2019-06-13 16:24:35 +02:00
|
|
|
#define LOG_MODULE "font"
|
2019-07-30 18:04:28 +02:00
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-06-13 16:24:35 +02:00
|
|
|
#include "log.h"
|
2019-08-18 17:59:43 +02:00
|
|
|
#include "stride.h"
|
2019-06-13 16:24:35 +02:00
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
|
|
2019-07-28 12:09:22 +02:00
|
|
|
static FT_Library ft_lib;
|
2019-07-30 18:04:28 +02:00
|
|
|
static mtx_t ft_lock;
|
|
|
|
|
|
|
|
|
|
static const size_t cache_size = 512;
|
2019-07-28 12:09:22 +02:00
|
|
|
|
2019-11-01 20:40:42 +01:00
|
|
|
struct font_cache_entry {
|
|
|
|
|
uint64_t hash;
|
|
|
|
|
struct font *font;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static tll(struct font_cache_entry) font_cache = tll_init();
|
|
|
|
|
|
2019-06-13 16:24:35 +02:00
|
|
|
static void __attribute__((constructor))
|
|
|
|
|
init(void)
|
|
|
|
|
{
|
|
|
|
|
FcInit();
|
2019-07-28 12:09:22 +02:00
|
|
|
FT_Init_FreeType(&ft_lib);
|
2019-07-30 18:04:28 +02:00
|
|
|
mtx_init(&ft_lock, mtx_plain);
|
2019-06-13 16:24:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __attribute__((destructor))
|
|
|
|
|
fini(void)
|
|
|
|
|
{
|
2019-11-01 20:40:42 +01:00
|
|
|
while (tll_length(font_cache) > 0)
|
|
|
|
|
font_destroy(tll_front(font_cache).font);
|
|
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
mtx_destroy(&ft_lock);
|
2019-07-28 12:09:22 +02:00
|
|
|
FT_Done_FreeType(ft_lib);
|
2019-07-30 18:04:28 +02:00
|
|
|
FcFini();
|
2019-06-13 16:24:35 +02:00
|
|
|
}
|
2019-07-28 12:09:22 +02:00
|
|
|
|
2019-09-29 13:03:48 +02:00
|
|
|
static void
|
|
|
|
|
underline_strikeout_metrics(struct font *font)
|
|
|
|
|
{
|
|
|
|
|
FT_Face ft_face = font->face;
|
|
|
|
|
double x_scale = ft_face->size->metrics.x_scale / 65526.;
|
|
|
|
|
double height = ft_face->size->metrics.height / 64;
|
|
|
|
|
double descent = ft_face->size->metrics.descender / 64;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("ft: x-scale: %f, height: %f, descent: %f",
|
|
|
|
|
x_scale, height, descent);
|
|
|
|
|
|
|
|
|
|
font->underline.position = round(ft_face->underline_position * x_scale / 64.);
|
|
|
|
|
font->underline.thickness = ceil(ft_face->underline_thickness * x_scale / 64.);
|
|
|
|
|
|
|
|
|
|
if (font->underline.position == 0.) {
|
|
|
|
|
font->underline.position = round(descent / 2.);
|
|
|
|
|
font->underline.thickness = fabs(round(descent / 5.));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("underline: pos=%d, thick=%d",
|
|
|
|
|
font->underline.position, font->underline.thickness);
|
|
|
|
|
|
|
|
|
|
TT_OS2 *os2 = FT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
|
|
|
|
|
if (os2 != NULL) {
|
|
|
|
|
font->strikeout.position = round(os2->yStrikeoutPosition * x_scale / 64.);
|
|
|
|
|
font->strikeout.thickness = ceil(os2->yStrikeoutSize * x_scale / 64.);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (font->strikeout.position == 0.) {
|
|
|
|
|
font->strikeout.position = round(height / 2. + descent);
|
|
|
|
|
font->strikeout.thickness = font->underline.thickness;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("strikeout: pos=%d, thick=%d",
|
|
|
|
|
font->strikeout.position, font->strikeout.thickness);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
static bool
|
2019-10-17 17:43:40 +02:00
|
|
|
from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx,
|
|
|
|
|
struct font *font, bool is_fallback)
|
2019-06-13 16:24:35 +02:00
|
|
|
{
|
2019-07-28 12:09:22 +02:00
|
|
|
memset(font, 0, sizeof(*font));
|
|
|
|
|
|
2019-08-10 20:34:22 +02:00
|
|
|
FcChar8 *face_file = NULL;
|
|
|
|
|
FcPattern *final_pattern = NULL;
|
|
|
|
|
int font_idx = -1;
|
2019-07-30 18:04:28 +02:00
|
|
|
|
2019-08-10 20:34:22 +02:00
|
|
|
for (int i = start_idx; i < fonts->nfont; i++) {
|
|
|
|
|
FcPattern *pat = FcFontRenderPrepare(NULL, pattern, fonts->fonts[i]);
|
|
|
|
|
assert(pat != NULL);
|
2019-07-30 18:04:28 +02:00
|
|
|
|
2019-08-10 20:34:22 +02:00
|
|
|
if (FcPatternGetString(pat, FC_FT_FACE, 0, &face_file) != FcResultMatch) {
|
|
|
|
|
if (FcPatternGetString(pat, FC_FILE, 0, &face_file) != FcResultMatch) {
|
|
|
|
|
FcPatternDestroy(pat);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-13 16:24:35 +02:00
|
|
|
|
2019-08-10 20:34:22 +02:00
|
|
|
final_pattern = pat;
|
|
|
|
|
font_idx = i;
|
|
|
|
|
break;
|
2019-06-13 16:24:35 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-10 20:34:22 +02:00
|
|
|
assert(font_idx != -1);
|
|
|
|
|
assert(final_pattern != NULL);
|
2019-06-13 16:24:35 +02:00
|
|
|
|
2019-07-28 12:09:22 +02:00
|
|
|
double dpi;
|
|
|
|
|
if (FcPatternGetDouble(final_pattern, FC_DPI, 0, &dpi) != FcResultMatch)
|
|
|
|
|
dpi = 96;
|
|
|
|
|
|
|
|
|
|
double size;
|
|
|
|
|
if (FcPatternGetDouble(final_pattern, FC_PIXEL_SIZE, 0, &size)) {
|
2019-08-10 20:34:22 +02:00
|
|
|
LOG_ERR("%s: failed to get size", face_file);
|
2019-07-28 12:09:22 +02:00
|
|
|
FcPatternDestroy(final_pattern);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
font: initial support for double-width *and* color emoji glyphs
Fonts are now loaded with FT_LOAD_COLOR and we recognize and support
the FT_PIXEL_MODE_BGRA pixel mode.
This is mapped to a CAIRO_FORMAT_ARGB32 surface, that is blitted
as-is (instead of used as a mask like we do for gray and mono glyphs).
Furthermore, since many emojis are double-width, we add initial
support for double-width glyphs.
These are assumed to always be utf8. When PRINT:ing an utf8 character,
we check its width, and add empty "spacer" cells after the cell with
the multi-column glyph.
When rendering, we render the columns in each row backwards. This
ensures the spacer cells get cleared *before* we render the glyph (so
that we don't end up erasing part of the glyph).
Finally, emoji fonts are usually bitmap fonts with *large*
glyphs. These aren't automatically scaled down. I.e. even if we
request a glyph of 13 pixels, we might end up getting a 100px glyph.
To handle this, fontconfig must be configured to scale bitmap
fonts. When it is, we can look at the 'scalable' and 'pixelsizefixup'
properties, and use these to scale the rendered glyph.
2019-07-31 18:03:35 +02:00
|
|
|
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.;
|
|
|
|
|
|
2019-07-28 12:09:22 +02:00
|
|
|
LOG_DBG("loading: %s", face_file);
|
|
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
mtx_lock(&ft_lock);
|
2019-07-28 12:09:22 +02:00
|
|
|
FT_Face ft_face;
|
|
|
|
|
FT_Error ft_err = FT_New_Face(ft_lib, (const char *)face_file, 0, &ft_face);
|
2019-07-30 18:04:28 +02:00
|
|
|
mtx_unlock(&ft_lock);
|
2019-07-28 12:09:22 +02:00
|
|
|
if (ft_err != 0)
|
|
|
|
|
LOG_ERR("%s: failed to create FreeType face", face_file);
|
|
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
if ((ft_err = FT_Set_Char_Size(ft_face, size * 64, 0, 0, 0)) != 0)
|
|
|
|
|
LOG_WARN("failed to set character size");
|
2019-07-28 12:09:22 +02:00
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
FcBool fc_hinting;
|
2019-07-18 10:03:08 +02:00
|
|
|
if (FcPatternGetBool(final_pattern, FC_HINTING,0, &fc_hinting) != FcResultMatch)
|
2019-07-17 22:50:26 +02:00
|
|
|
fc_hinting = FcTrue;
|
|
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
FcBool fc_antialias;
|
2019-07-18 10:03:08 +02:00
|
|
|
if (FcPatternGetBool(final_pattern, FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch)
|
2019-07-17 22:50:26 +02:00
|
|
|
fc_antialias = FcTrue;
|
|
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
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;
|
2019-08-19 17:45:21 +02:00
|
|
|
else if (fc_rgba == FC_RGBA_RGB || fc_rgba == FC_RGBA_BGR)
|
2019-08-18 17:40:57 +02:00
|
|
|
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD;
|
2019-08-19 17:45:21 +02:00
|
|
|
else if (fc_rgba == FC_RGBA_VRGB || fc_rgba == FC_RGBA_VBGR)
|
2019-08-18 17:40:57 +02:00
|
|
|
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD_V;
|
|
|
|
|
else
|
2019-07-29 20:10:55 +02:00
|
|
|
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 {
|
2019-08-19 17:45:21 +02:00
|
|
|
if (fc_rgba == FC_RGBA_RGB || fc_rgba == FC_RGBA_BGR)
|
2019-08-18 17:40:57 +02:00
|
|
|
render_flags |= FT_RENDER_MODE_LCD;
|
2019-08-19 17:45:21 +02:00
|
|
|
else if (fc_rgba == FC_RGBA_VRGB || fc_rgba == FC_RGBA_VBGR)
|
2019-08-18 17:40:57 +02:00
|
|
|
render_flags |= FT_RENDER_MODE_LCD_V;
|
|
|
|
|
else
|
2019-07-29 20:10:55 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 17:43:40 +02:00
|
|
|
font->name = strdup((char *)face_file);
|
2019-06-13 16:24:35 +02:00
|
|
|
FcPatternDestroy(final_pattern);
|
|
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
mtx_init(&font->lock, mtx_plain);
|
2019-07-28 12:09:22 +02:00
|
|
|
font->face = ft_face;
|
font: initial support for double-width *and* color emoji glyphs
Fonts are now loaded with FT_LOAD_COLOR and we recognize and support
the FT_PIXEL_MODE_BGRA pixel mode.
This is mapped to a CAIRO_FORMAT_ARGB32 surface, that is blitted
as-is (instead of used as a mask like we do for gray and mono glyphs).
Furthermore, since many emojis are double-width, we add initial
support for double-width glyphs.
These are assumed to always be utf8. When PRINT:ing an utf8 character,
we check its width, and add empty "spacer" cells after the cell with
the multi-column glyph.
When rendering, we render the columns in each row backwards. This
ensures the spacer cells get cleared *before* we render the glyph (so
that we don't end up erasing part of the glyph).
Finally, emoji fonts are usually bitmap fonts with *large*
glyphs. These aren't automatically scaled down. I.e. even if we
request a glyph of 13 pixels, we might end up getting a 100px glyph.
To handle this, fontconfig must be configured to scale bitmap
fonts. When it is, we can look at the 'scalable' and 'pixelsizefixup'
properties, and use these to scale the rendered glyph.
2019-07-31 18:03:35 +02:00
|
|
|
font->load_flags = load_flags | FT_LOAD_COLOR;
|
2019-07-29 20:10:55 +02:00
|
|
|
font->render_flags = render_flags;
|
2019-07-30 18:04:28 +02:00
|
|
|
font->is_fallback = is_fallback;
|
font: initial support for double-width *and* color emoji glyphs
Fonts are now loaded with FT_LOAD_COLOR and we recognize and support
the FT_PIXEL_MODE_BGRA pixel mode.
This is mapped to a CAIRO_FORMAT_ARGB32 surface, that is blitted
as-is (instead of used as a mask like we do for gray and mono glyphs).
Furthermore, since many emojis are double-width, we add initial
support for double-width glyphs.
These are assumed to always be utf8. When PRINT:ing an utf8 character,
we check its width, and add empty "spacer" cells after the cell with
the multi-column glyph.
When rendering, we render the columns in each row backwards. This
ensures the spacer cells get cleared *before* we render the glyph (so
that we don't end up erasing part of the glyph).
Finally, emoji fonts are usually bitmap fonts with *large*
glyphs. These aren't automatically scaled down. I.e. even if we
request a glyph of 13 pixels, we might end up getting a 100px glyph.
To handle this, fontconfig must be configured to scale bitmap
fonts. When it is, we can look at the 'scalable' and 'pixelsizefixup'
properties, and use these to scale the rendered glyph.
2019-07-31 18:03:35 +02:00
|
|
|
font->pixel_size_fixup = scalable ? pixel_fixup : 1.;
|
2019-08-19 17:45:21 +02:00
|
|
|
font->bgr = fc_rgba == FC_RGBA_BGR || fc_rgba == FC_RGBA_VBGR;
|
2019-10-16 22:34:23 +02:00
|
|
|
font->ref_counter = 1;
|
2019-10-17 17:07:37 +02:00
|
|
|
font->fc_idx = font_idx;
|
2019-07-30 18:04:28 +02:00
|
|
|
|
2019-10-16 22:18:57 +02:00
|
|
|
if (is_fallback) {
|
|
|
|
|
font->fc_pattern = NULL;
|
|
|
|
|
font->fc_fonts = NULL;
|
2019-10-17 17:06:42 +02:00
|
|
|
font->fc_loaded_fallbacks = NULL;
|
2019-10-17 17:12:04 +02:00
|
|
|
font->glyph_cache = NULL;
|
2019-10-16 22:18:57 +02:00
|
|
|
} else {
|
|
|
|
|
font->fc_pattern = !is_fallback ? pattern : NULL;
|
|
|
|
|
font->fc_fonts = !is_fallback ? fonts : NULL;
|
2019-10-17 17:06:42 +02:00
|
|
|
font->fc_loaded_fallbacks = calloc(
|
|
|
|
|
fonts->nfont, sizeof(font->fc_loaded_fallbacks[0]));
|
2019-10-17 17:12:04 +02:00
|
|
|
font->glyph_cache = calloc(cache_size, sizeof(font->glyph_cache[0]));
|
2019-08-10 21:10:56 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-29 13:03:48 +02:00
|
|
|
underline_strikeout_metrics(font);
|
2019-08-10 20:34:22 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-16 22:22:26 +02:00
|
|
|
static struct font *
|
2019-10-17 17:43:40 +02:00
|
|
|
from_name(const char *name, bool is_fallback)
|
2019-08-10 20:34:22 +02:00
|
|
|
{
|
|
|
|
|
LOG_DBG("instantiating %s", name);
|
|
|
|
|
|
|
|
|
|
FcPattern *pattern = FcNameParse((const unsigned char *)name);
|
|
|
|
|
if (pattern == NULL) {
|
|
|
|
|
LOG_ERR("%s: failed to lookup font", name);
|
2019-10-16 22:22:26 +02:00
|
|
|
return NULL;
|
2019-08-10 20:34:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!FcConfigSubstitute(NULL, pattern, FcMatchPattern)) {
|
|
|
|
|
LOG_ERR("%s: failed to do config substitution", name);
|
|
|
|
|
FcPatternDestroy(pattern);
|
2019-10-16 22:22:26 +02:00
|
|
|
return NULL;
|
2019-08-10 20:34:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FcDefaultSubstitute(pattern);
|
|
|
|
|
|
|
|
|
|
FcResult result;
|
|
|
|
|
FcFontSet *fonts = FcFontSort(NULL, pattern, FcTrue, NULL, &result);
|
|
|
|
|
if (result != FcResultMatch) {
|
|
|
|
|
LOG_ERR("%s: failed to match font", name);
|
|
|
|
|
FcPatternDestroy(pattern);
|
2019-10-16 22:22:26 +02:00
|
|
|
return NULL;
|
2019-08-10 20:34:22 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-16 22:22:26 +02:00
|
|
|
struct font *font = malloc(sizeof(*font));
|
|
|
|
|
|
2019-10-17 17:43:40 +02:00
|
|
|
if (!from_font_set(pattern, fonts, 0, font, is_fallback)) {
|
2019-10-16 22:22:26 +02:00
|
|
|
free(font);
|
2019-08-10 20:34:22 +02:00
|
|
|
FcFontSetDestroy(fonts);
|
|
|
|
|
FcPatternDestroy(pattern);
|
2019-10-16 22:22:26 +02:00
|
|
|
return NULL;
|
2019-08-10 20:34:22 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-10 21:10:56 +02:00
|
|
|
if (is_fallback) {
|
2019-08-10 20:34:22 +02:00
|
|
|
FcFontSetDestroy(fonts);
|
|
|
|
|
FcPatternDestroy(pattern);
|
|
|
|
|
}
|
2019-07-30 18:04:28 +02:00
|
|
|
|
2019-10-17 17:43:40 +02:00
|
|
|
LOG_DBG("instantiated: %s", font->name);
|
2019-10-16 22:22:26 +02:00
|
|
|
return font;
|
2019-06-13 16:24:35 +02:00
|
|
|
}
|
2019-07-28 20:37:59 +02:00
|
|
|
|
2019-11-01 20:40:42 +01:00
|
|
|
static uint64_t
|
|
|
|
|
sdbm_hash(const char *s)
|
|
|
|
|
{
|
|
|
|
|
uint64_t hash = 0;
|
|
|
|
|
|
|
|
|
|
for (; *s != '\0'; s++) {
|
|
|
|
|
int c = *s;
|
|
|
|
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
|
font_hash(font_list_t names, const char *attributes)
|
|
|
|
|
{
|
|
|
|
|
uint64_t hash = 0;
|
|
|
|
|
tll_foreach(names, it)
|
|
|
|
|
hash ^= sdbm_hash(it->item);
|
|
|
|
|
|
|
|
|
|
if (attributes != NULL)
|
|
|
|
|
hash ^= sdbm_hash(attributes);
|
|
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-16 21:52:12 +02:00
|
|
|
struct font *
|
|
|
|
|
font_from_name(font_list_t names, const char *attributes)
|
2019-07-28 20:37:59 +02:00
|
|
|
{
|
2019-07-30 18:04:28 +02:00
|
|
|
if (tll_length(names) == 0)
|
2019-07-28 20:37:59 +02:00
|
|
|
return false;
|
2019-08-10 20:34:22 +02:00
|
|
|
|
2019-11-01 20:40:42 +01:00
|
|
|
uint64_t hash = font_hash(names, attributes);
|
|
|
|
|
tll_foreach(font_cache, it) {
|
|
|
|
|
if (it->item.hash == hash) {
|
|
|
|
|
it->item.font->ref_counter++;
|
|
|
|
|
return it->item.font;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 17:53:03 +02:00
|
|
|
struct font *font = NULL;
|
2019-10-17 17:43:40 +02:00
|
|
|
|
|
|
|
|
bool have_attrs = attributes != NULL && strlen(attributes) > 0;
|
|
|
|
|
size_t attr_len = have_attrs ? strlen(attributes) + 1 : 0;
|
|
|
|
|
|
|
|
|
|
bool first = true;
|
2019-07-30 18:04:28 +02:00
|
|
|
tll_foreach(names, it) {
|
2019-10-17 17:43:40 +02:00
|
|
|
const char *base_name = it->item;
|
|
|
|
|
|
2019-10-17 17:53:03 +02:00
|
|
|
char name[strlen(base_name) + attr_len + 1];
|
|
|
|
|
strcpy(name, base_name);
|
2019-10-17 17:43:40 +02:00
|
|
|
if (have_attrs) {
|
2019-10-17 17:53:03 +02:00
|
|
|
strcat(name, ":");
|
|
|
|
|
strcat(name, attributes);
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-17 17:43:40 +02:00
|
|
|
if (first) {
|
|
|
|
|
first = false;
|
2019-10-17 17:53:03 +02:00
|
|
|
|
|
|
|
|
font = from_name(name, false);
|
|
|
|
|
if (font == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2019-10-17 17:43:40 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2019-07-29 20:10:55 +02:00
|
|
|
|
2019-10-17 17:53:03 +02:00
|
|
|
assert(font != NULL);
|
|
|
|
|
tll_push_back(
|
|
|
|
|
font->fallbacks, ((struct font_fallback){.pattern = strdup(name)}));
|
2019-10-17 17:43:40 +02:00
|
|
|
}
|
2019-10-16 21:52:12 +02:00
|
|
|
|
2019-11-01 20:40:42 +01:00
|
|
|
tll_push_back(font_cache, ((struct font_cache_entry){.hash = hash, .font = font}));
|
2019-10-17 17:53:03 +02:00
|
|
|
return font;
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
hash_index(wchar_t wc)
|
|
|
|
|
{
|
|
|
|
|
return wc % cache_size;
|
|
|
|
|
}
|
2019-07-29 20:10:55 +02:00
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
static bool
|
2019-08-19 17:45:21 +02:00
|
|
|
glyph_for_wchar(const struct font *font, wchar_t wc, struct glyph *glyph)
|
2019-07-30 18:04:28 +02:00
|
|
|
{
|
2019-10-18 19:50:48 +02:00
|
|
|
*glyph = (struct glyph){
|
|
|
|
|
.wc = wc,
|
|
|
|
|
.valid = false,
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
/*
|
|
|
|
|
* LCD filter is per library instance. Thus we need to re-set it
|
|
|
|
|
* every time...
|
2019-08-18 17:40:57 +02:00
|
|
|
*
|
2019-07-29 20:10:55 +02:00
|
|
|
* 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;
|
2019-07-28 20:37:59 +02:00
|
|
|
|
|
|
|
|
FT_UInt idx = FT_Get_Char_Index(font->face, wc);
|
2019-07-30 18:04:28 +02:00
|
|
|
if (idx == 0) {
|
2019-08-08 17:57:58 +02:00
|
|
|
/* No glyph in this font, try fallback fonts */
|
2019-08-10 20:34:22 +02:00
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
tll_foreach(font->fallbacks, it) {
|
2019-10-17 17:53:03 +02:00
|
|
|
if (it->item.font == NULL) {
|
|
|
|
|
it->item.font = from_name(it->item.pattern, true);
|
|
|
|
|
if (it->item.font == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (glyph_for_wchar(it->item.font, wc, glyph)) {
|
2019-10-17 17:43:40 +02:00
|
|
|
LOG_DBG("%C: used fallback: %s (fixup = %f)",
|
2019-10-17 17:53:03 +02:00
|
|
|
wc, it->item.font->name,
|
|
|
|
|
it->item.font->pixel_size_fixup);
|
2019-10-16 22:22:26 +02:00
|
|
|
return true;
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (font->is_fallback)
|
|
|
|
|
return false;
|
2019-08-08 17:57:58 +02:00
|
|
|
|
2019-08-10 20:34:22 +02:00
|
|
|
/* Try fontconfig fallback fonts */
|
|
|
|
|
|
|
|
|
|
assert(font->fc_pattern != NULL);
|
|
|
|
|
assert(font->fc_fonts != NULL);
|
2019-10-17 17:06:42 +02:00
|
|
|
assert(font->fc_loaded_fallbacks != NULL);
|
2019-08-10 20:34:22 +02:00
|
|
|
assert(font->fc_idx != -1);
|
|
|
|
|
|
|
|
|
|
for (int i = font->fc_idx + 1; i < font->fc_fonts->nfont; i++) {
|
2019-10-17 17:06:42 +02:00
|
|
|
if (font->fc_loaded_fallbacks[i] == NULL) {
|
2019-10-16 22:18:57 +02:00
|
|
|
/* Load font */
|
|
|
|
|
struct font *fallback = malloc(sizeof(*fallback));
|
2019-10-17 17:43:40 +02:00
|
|
|
if (!from_font_set(font->fc_pattern, font->fc_fonts, i, fallback, true))
|
2019-10-16 22:18:57 +02:00
|
|
|
{
|
|
|
|
|
LOG_WARN("failed to load fontconfig fallback font");
|
|
|
|
|
free(fallback);
|
|
|
|
|
continue;
|
2019-08-10 20:34:22 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-16 22:18:57 +02:00
|
|
|
LOG_DBG("loaded new fontconfig fallback font");
|
2019-10-17 17:07:37 +02:00
|
|
|
assert(fallback->fc_idx >= i);
|
|
|
|
|
|
|
|
|
|
i = fallback->fc_idx;
|
2019-10-17 17:06:42 +02:00
|
|
|
font->fc_loaded_fallbacks[i] = fallback;
|
2019-10-16 22:18:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-17 17:06:42 +02:00
|
|
|
assert(font->fc_loaded_fallbacks[i] != NULL);
|
2019-10-16 22:18:57 +02:00
|
|
|
|
2019-10-17 17:06:42 +02:00
|
|
|
if (glyph_for_wchar(font->fc_loaded_fallbacks[i], wc, glyph)) {
|
2019-10-17 17:43:40 +02:00
|
|
|
LOG_DBG("%C: used fontconfig fallback: %s",
|
|
|
|
|
wc, font->fc_loaded_fallbacks[i]->name);
|
2019-10-16 22:18:57 +02:00
|
|
|
return true;
|
2019-08-10 20:34:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-08 17:57:58 +02:00
|
|
|
LOG_WARN("%C: no glyph found (in neither the main font, "
|
|
|
|
|
"nor any fallback fonts)", wc);
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
err = FT_Load_Glyph(font->face, idx, font->load_flags);
|
|
|
|
|
if (err != 0) {
|
|
|
|
|
LOG_ERR("load failed");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
2019-07-28 20:37:59 +02:00
|
|
|
|
2019-07-29 20:10:55 +02:00
|
|
|
err = FT_Render_Glyph(font->face->glyph, font->render_flags);
|
2019-07-28 20:37:59 +02:00
|
|
|
if (err != 0)
|
2019-07-29 20:10:55 +02:00
|
|
|
goto err;
|
2019-07-28 20:37:59 +02:00
|
|
|
|
2019-07-28 21:03:38 +02:00
|
|
|
assert(font->face->glyph->format == FT_GLYPH_FORMAT_BITMAP);
|
|
|
|
|
|
2019-07-28 20:37:59 +02:00
|
|
|
FT_Bitmap *bitmap = &font->face->glyph->bitmap;
|
2019-08-13 20:44:30 +02:00
|
|
|
if (bitmap->width == 0)
|
|
|
|
|
goto err;
|
|
|
|
|
|
2019-08-18 17:40:57 +02:00
|
|
|
pixman_format_code_t pix_format;
|
|
|
|
|
int width;
|
|
|
|
|
int rows;
|
2019-08-16 20:40:32 +02:00
|
|
|
|
2019-08-18 17:40:57 +02:00
|
|
|
switch (bitmap->pixel_mode) {
|
|
|
|
|
case FT_PIXEL_MODE_MONO:
|
|
|
|
|
pix_format = PIXMAN_a1;
|
|
|
|
|
width = bitmap->width;
|
|
|
|
|
rows = bitmap->rows;
|
|
|
|
|
break;
|
2019-08-16 20:40:32 +02:00
|
|
|
|
2019-08-18 17:40:57 +02:00
|
|
|
case FT_PIXEL_MODE_GRAY:
|
|
|
|
|
pix_format = PIXMAN_a8;
|
|
|
|
|
width = bitmap->width;
|
|
|
|
|
rows = bitmap->rows;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FT_PIXEL_MODE_LCD:
|
|
|
|
|
pix_format = PIXMAN_x8r8g8b8;
|
|
|
|
|
width = bitmap->width / 3;
|
|
|
|
|
rows = bitmap->rows;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FT_PIXEL_MODE_LCD_V:
|
|
|
|
|
pix_format = PIXMAN_x8r8g8b8;
|
|
|
|
|
width = bitmap->width;
|
|
|
|
|
rows = bitmap->rows / 3;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FT_PIXEL_MODE_BGRA:
|
|
|
|
|
pix_format = PIXMAN_a8r8g8b8;
|
|
|
|
|
width = bitmap->width;
|
|
|
|
|
rows = bitmap->rows;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
LOG_ERR("unimplemented: FT pixel mode: %d", bitmap->pixel_mode);
|
|
|
|
|
goto err;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-18 17:59:43 +02:00
|
|
|
int stride = stride_for_format_and_width(pix_format, width);
|
2019-07-28 20:37:59 +02:00
|
|
|
assert(stride >= bitmap->pitch);
|
|
|
|
|
|
2019-08-18 17:40:57 +02:00
|
|
|
uint8_t *data = malloc(rows * stride);
|
2019-07-28 20:37:59 +02:00
|
|
|
|
2019-08-16 22:11:22 +02:00
|
|
|
/* Convert FT bitmap to pixman image */
|
2019-07-28 20:37:59 +02:00
|
|
|
switch (bitmap->pixel_mode) {
|
|
|
|
|
case FT_PIXEL_MODE_MONO:
|
|
|
|
|
for (size_t r = 0; r < bitmap->rows; r++) {
|
2019-07-29 20:10:55 +02:00
|
|
|
for (size_t c = 0; c < (bitmap->width + 7) / 8; c++) {
|
2019-07-28 20:37:59 +02:00
|
|
|
uint8_t v = bitmap->buffer[r * bitmap->pitch + c];
|
|
|
|
|
uint8_t reversed = 0;
|
2019-07-29 20:10:55 +02:00
|
|
|
for (size_t i = 0; i < min(8, bitmap->width - c * 8); i++)
|
|
|
|
|
reversed |= ((v >> (7 - i)) & 1) << i;
|
|
|
|
|
|
2019-07-28 20:37:59 +02:00
|
|
|
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;
|
|
|
|
|
|
font: initial support for double-width *and* color emoji glyphs
Fonts are now loaded with FT_LOAD_COLOR and we recognize and support
the FT_PIXEL_MODE_BGRA pixel mode.
This is mapped to a CAIRO_FORMAT_ARGB32 surface, that is blitted
as-is (instead of used as a mask like we do for gray and mono glyphs).
Furthermore, since many emojis are double-width, we add initial
support for double-width glyphs.
These are assumed to always be utf8. When PRINT:ing an utf8 character,
we check its width, and add empty "spacer" cells after the cell with
the multi-column glyph.
When rendering, we render the columns in each row backwards. This
ensures the spacer cells get cleared *before* we render the glyph (so
that we don't end up erasing part of the glyph).
Finally, emoji fonts are usually bitmap fonts with *large*
glyphs. These aren't automatically scaled down. I.e. even if we
request a glyph of 13 pixels, we might end up getting a 100px glyph.
To handle this, fontconfig must be configured to scale bitmap
fonts. When it is, we can look at the 'scalable' and 'pixelsizefixup'
properties, and use these to scale the rendered glyph.
2019-07-31 18:03:35 +02:00
|
|
|
case FT_PIXEL_MODE_BGRA:
|
|
|
|
|
assert(stride == bitmap->pitch);
|
|
|
|
|
memcpy(data, bitmap->buffer, bitmap->rows * bitmap->pitch);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-08-18 17:40:57 +02:00
|
|
|
case FT_PIXEL_MODE_LCD:
|
|
|
|
|
for (size_t r = 0; r < bitmap->rows; r++) {
|
|
|
|
|
for (size_t c = 0; c < bitmap->width; c += 3) {
|
2019-08-19 17:45:21 +02:00
|
|
|
unsigned char _r = bitmap->buffer[r * bitmap->pitch + c + (font->bgr ? 2 : 0)];
|
2019-08-18 17:40:57 +02:00
|
|
|
unsigned char _g = bitmap->buffer[r * bitmap->pitch + c + 1];
|
2019-08-19 17:45:21 +02:00
|
|
|
unsigned char _b = bitmap->buffer[r * bitmap->pitch + c + (font->bgr ? 0 : 2)];
|
2019-08-18 17:40:57 +02:00
|
|
|
|
|
|
|
|
uint32_t *p = (uint32_t *)&data[r * stride + 4 * (c / 3)];
|
|
|
|
|
*p = _r << 16 | _g << 8 | _b;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2019-08-19 17:45:21 +02:00
|
|
|
case FT_PIXEL_MODE_LCD_V:
|
|
|
|
|
/* Unverified */
|
|
|
|
|
for (size_t r = 0; r < bitmap->rows; r += 3) {
|
|
|
|
|
for (size_t c = 0; c < bitmap->width; c++) {
|
|
|
|
|
unsigned char _r = bitmap->buffer[(r + (font->bgr ? 2 : 0)) * bitmap->pitch + c];
|
|
|
|
|
unsigned char _g = bitmap->buffer[(r + 1) * bitmap->pitch + c];
|
|
|
|
|
unsigned char _b = bitmap->buffer[(r + (font->bgr ? 0 : 2)) * bitmap->pitch + c];
|
|
|
|
|
|
|
|
|
|
uint32_t *p = (uint32_t *)&data[r / 3 * stride + 4 * c];
|
|
|
|
|
*p = _r << 16 | _g << 8 | _b;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2019-07-28 20:37:59 +02:00
|
|
|
default:
|
2019-08-18 17:40:57 +02:00
|
|
|
abort();
|
|
|
|
|
break;
|
2019-07-28 20:37:59 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-16 20:40:32 +02:00
|
|
|
pixman_image_t *pix = pixman_image_create_bits_no_clear(
|
2019-08-18 17:40:57 +02:00
|
|
|
pix_format, width, rows, (uint32_t *)data, stride);
|
2019-07-28 20:37:59 +02:00
|
|
|
|
2019-08-16 20:40:32 +02:00
|
|
|
if (pix == NULL) {
|
2019-07-28 20:37:59 +02:00
|
|
|
free(data);
|
2019-07-29 20:10:55 +02:00
|
|
|
goto err;
|
2019-07-28 20:37:59 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-18 17:40:57 +02:00
|
|
|
pixman_image_set_component_alpha(
|
|
|
|
|
pix,
|
|
|
|
|
bitmap->pixel_mode == FT_PIXEL_MODE_LCD ||
|
|
|
|
|
bitmap->pixel_mode == FT_PIXEL_MODE_LCD_V);
|
|
|
|
|
|
2019-08-16 20:40:32 +02:00
|
|
|
if (font->pixel_size_fixup != 1.) {
|
|
|
|
|
struct pixman_transform scale;
|
|
|
|
|
pixman_transform_init_scale(
|
|
|
|
|
&scale,
|
|
|
|
|
pixman_double_to_fixed(1. / font->pixel_size_fixup),
|
|
|
|
|
pixman_double_to_fixed(1. / font->pixel_size_fixup));
|
|
|
|
|
pixman_image_set_transform(pix, &scale);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 20:37:59 +02:00
|
|
|
*glyph = (struct glyph){
|
2019-07-30 18:04:28 +02:00
|
|
|
.wc = wc,
|
2019-08-16 20:40:32 +02:00
|
|
|
.cols = wcwidth(wc),
|
|
|
|
|
.pix = pix,
|
|
|
|
|
.x = font->face->glyph->bitmap_left / font->pixel_size_fixup,
|
|
|
|
|
.y = font->face->glyph->bitmap_top * font->pixel_size_fixup,
|
2019-08-18 17:40:57 +02:00
|
|
|
.width = width,
|
|
|
|
|
.height = rows,
|
2019-08-02 18:19:07 +02:00
|
|
|
.valid = true,
|
2019-07-28 20:37:59 +02:00
|
|
|
};
|
2019-07-30 18:04:28 +02:00
|
|
|
|
2019-07-28 20:37:59 +02:00
|
|
|
return true;
|
2019-07-29 20:10:55 +02:00
|
|
|
|
|
|
|
|
err:
|
2019-10-18 19:50:48 +02:00
|
|
|
assert(!glyph->valid);
|
2019-07-29 20:10:55 +02:00
|
|
|
return false;
|
2019-07-28 20:37:59 +02:00
|
|
|
}
|
2019-07-28 21:03:38 +02:00
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
const struct glyph *
|
2019-08-02 18:19:07 +02:00
|
|
|
font_glyph_for_wc(struct font *font, wchar_t wc)
|
2019-07-30 18:04:28 +02:00
|
|
|
{
|
|
|
|
|
mtx_lock(&font->lock);
|
|
|
|
|
|
2019-10-17 17:12:04 +02:00
|
|
|
assert(font->glyph_cache != NULL);
|
2019-07-30 18:04:28 +02:00
|
|
|
size_t hash_idx = hash_index(wc);
|
2019-10-17 17:12:04 +02:00
|
|
|
hash_entry_t *hash_entry = font->glyph_cache[hash_idx];
|
2019-07-30 18:04:28 +02:00
|
|
|
|
|
|
|
|
if (hash_entry != NULL) {
|
|
|
|
|
tll_foreach(*hash_entry, it) {
|
|
|
|
|
if (it->item.wc == wc) {
|
|
|
|
|
mtx_unlock(&font->lock);
|
2019-08-13 20:41:21 +02:00
|
|
|
return it->item.valid ? &it->item : NULL;
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct glyph glyph;
|
2019-08-02 18:19:07 +02:00
|
|
|
bool got_glyph = glyph_for_wchar(font, wc, &glyph);
|
2019-07-30 18:04:28 +02:00
|
|
|
|
|
|
|
|
if (hash_entry == NULL) {
|
|
|
|
|
hash_entry = calloc(1, sizeof(*hash_entry));
|
|
|
|
|
|
2019-10-17 17:12:04 +02:00
|
|
|
assert(font->glyph_cache[hash_idx] == NULL);
|
|
|
|
|
font->glyph_cache[hash_idx] = hash_entry;
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
2019-08-13 20:44:30 +02:00
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
assert(hash_entry != NULL);
|
|
|
|
|
tll_push_back(*hash_entry, glyph);
|
|
|
|
|
|
|
|
|
|
mtx_unlock(&font->lock);
|
2019-08-02 18:19:07 +02:00
|
|
|
return got_glyph ? &tll_back(*hash_entry) : NULL;
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-28 21:03:38 +02:00
|
|
|
void
|
|
|
|
|
font_destroy(struct font *font)
|
|
|
|
|
{
|
2019-10-16 22:18:57 +02:00
|
|
|
if (font == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-16 22:34:23 +02:00
|
|
|
if (--font->ref_counter > 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-11-01 20:40:42 +01:00
|
|
|
tll_foreach(font_cache, it) {
|
|
|
|
|
if (it->item.font == font) {
|
|
|
|
|
tll_remove(font_cache, it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 17:43:40 +02:00
|
|
|
free(font->name);
|
2019-10-17 17:53:03 +02:00
|
|
|
|
|
|
|
|
tll_foreach(font->fallbacks, it) {
|
|
|
|
|
font_destroy(it->item.font);
|
|
|
|
|
free(it->item.pattern);
|
|
|
|
|
}
|
|
|
|
|
tll_free(font->fallbacks);
|
2019-07-30 18:04:28 +02:00
|
|
|
|
|
|
|
|
if (font->face != NULL) {
|
|
|
|
|
mtx_lock(&ft_lock);
|
2019-07-28 21:03:38 +02:00
|
|
|
FT_Done_Face(font->face);
|
2019-07-30 18:04:28 +02:00
|
|
|
mtx_unlock(&ft_lock);
|
|
|
|
|
}
|
2019-07-28 21:03:38 +02:00
|
|
|
|
font: initial support for double-width *and* color emoji glyphs
Fonts are now loaded with FT_LOAD_COLOR and we recognize and support
the FT_PIXEL_MODE_BGRA pixel mode.
This is mapped to a CAIRO_FORMAT_ARGB32 surface, that is blitted
as-is (instead of used as a mask like we do for gray and mono glyphs).
Furthermore, since many emojis are double-width, we add initial
support for double-width glyphs.
These are assumed to always be utf8. When PRINT:ing an utf8 character,
we check its width, and add empty "spacer" cells after the cell with
the multi-column glyph.
When rendering, we render the columns in each row backwards. This
ensures the spacer cells get cleared *before* we render the glyph (so
that we don't end up erasing part of the glyph).
Finally, emoji fonts are usually bitmap fonts with *large*
glyphs. These aren't automatically scaled down. I.e. even if we
request a glyph of 13 pixels, we might end up getting a 100px glyph.
To handle this, fontconfig must be configured to scale bitmap
fonts. When it is, we can look at the 'scalable' and 'pixelsizefixup'
properties, and use these to scale the rendered glyph.
2019-07-31 18:03:35 +02:00
|
|
|
mtx_destroy(&font->lock);
|
2019-07-30 18:04:28 +02:00
|
|
|
|
2019-10-16 22:18:57 +02:00
|
|
|
if (font->fc_fonts != NULL) {
|
2019-10-17 17:06:42 +02:00
|
|
|
assert(font->fc_loaded_fallbacks != NULL);
|
2019-10-16 22:18:57 +02:00
|
|
|
|
|
|
|
|
for (size_t i = 0; i < font->fc_fonts->nfont; i++)
|
2019-10-17 17:06:42 +02:00
|
|
|
font_destroy(font->fc_loaded_fallbacks[i]);
|
2019-10-16 22:18:57 +02:00
|
|
|
|
2019-10-17 17:06:42 +02:00
|
|
|
free(font->fc_loaded_fallbacks);
|
2019-10-16 22:18:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-10 20:34:22 +02:00
|
|
|
if (font->fc_pattern != NULL)
|
|
|
|
|
FcPatternDestroy(font->fc_pattern);
|
|
|
|
|
if (font->fc_fonts != NULL)
|
|
|
|
|
FcFontSetDestroy(font->fc_fonts);
|
|
|
|
|
|
2019-07-30 18:04:28 +02:00
|
|
|
|
2019-10-17 17:12:04 +02:00
|
|
|
for (size_t i = 0; i < cache_size && font->glyph_cache != NULL; i++) {
|
|
|
|
|
if (font->glyph_cache[i] == NULL)
|
font: initial support for double-width *and* color emoji glyphs
Fonts are now loaded with FT_LOAD_COLOR and we recognize and support
the FT_PIXEL_MODE_BGRA pixel mode.
This is mapped to a CAIRO_FORMAT_ARGB32 surface, that is blitted
as-is (instead of used as a mask like we do for gray and mono glyphs).
Furthermore, since many emojis are double-width, we add initial
support for double-width glyphs.
These are assumed to always be utf8. When PRINT:ing an utf8 character,
we check its width, and add empty "spacer" cells after the cell with
the multi-column glyph.
When rendering, we render the columns in each row backwards. This
ensures the spacer cells get cleared *before* we render the glyph (so
that we don't end up erasing part of the glyph).
Finally, emoji fonts are usually bitmap fonts with *large*
glyphs. These aren't automatically scaled down. I.e. even if we
request a glyph of 13 pixels, we might end up getting a 100px glyph.
To handle this, fontconfig must be configured to scale bitmap
fonts. When it is, we can look at the 'scalable' and 'pixelsizefixup'
properties, and use these to scale the rendered glyph.
2019-07-31 18:03:35 +02:00
|
|
|
continue;
|
|
|
|
|
|
2019-10-17 17:12:04 +02:00
|
|
|
tll_foreach(*font->glyph_cache[i], it) {
|
2019-08-13 20:41:21 +02:00
|
|
|
if (!it->item.valid)
|
|
|
|
|
continue;
|
|
|
|
|
|
2019-08-16 20:40:32 +02:00
|
|
|
void *image = pixman_image_get_data(it->item.pix);
|
|
|
|
|
pixman_image_unref(it->item.pix);
|
font: initial support for double-width *and* color emoji glyphs
Fonts are now loaded with FT_LOAD_COLOR and we recognize and support
the FT_PIXEL_MODE_BGRA pixel mode.
This is mapped to a CAIRO_FORMAT_ARGB32 surface, that is blitted
as-is (instead of used as a mask like we do for gray and mono glyphs).
Furthermore, since many emojis are double-width, we add initial
support for double-width glyphs.
These are assumed to always be utf8. When PRINT:ing an utf8 character,
we check its width, and add empty "spacer" cells after the cell with
the multi-column glyph.
When rendering, we render the columns in each row backwards. This
ensures the spacer cells get cleared *before* we render the glyph (so
that we don't end up erasing part of the glyph).
Finally, emoji fonts are usually bitmap fonts with *large*
glyphs. These aren't automatically scaled down. I.e. even if we
request a glyph of 13 pixels, we might end up getting a 100px glyph.
To handle this, fontconfig must be configured to scale bitmap
fonts. When it is, we can look at the 'scalable' and 'pixelsizefixup'
properties, and use these to scale the rendered glyph.
2019-07-31 18:03:35 +02:00
|
|
|
free(image);
|
2019-07-30 18:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-17 17:12:04 +02:00
|
|
|
tll_free(*font->glyph_cache[i]);
|
|
|
|
|
free(font->glyph_cache[i]);
|
2019-07-28 21:03:38 +02:00
|
|
|
}
|
2019-10-17 17:12:04 +02:00
|
|
|
free(font->glyph_cache);
|
2019-10-16 22:18:57 +02:00
|
|
|
free(font);
|
2019-07-28 21:03:38 +02:00
|
|
|
}
|