2 * kmscon - Freetype2 font backend
4 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5 * Copyright (c) 2011 University of Tuebingen
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files
9 * (the "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * SECTION:font_freetype2.c
29 * @short_description: Freetype2 font backend
32 * The freetype2 backend uses freetype2 to render glyphs into memory
33 * buffers. It uses a hashmap to cache all rendered glyphs of a single
34 * font-face. Therefore, rendering should be very fast. Also, when loading a
35 * glyph it pre-renders all common (mostly ASCII) characters, so it can measure
36 * the font and return a valid font width.
40 #include <fontconfig/fontconfig.h>
42 #include FT_FREETYPE_H
48 #include "shl_dlist.h"
49 #include "shl_hashtable.h"
51 #include "tsm_unicode.h"
52 #include "uterm_video.h"
54 #define LOG_SUBSYSTEM "font_freetype2"
63 struct shl_dlist list;
66 struct kmscon_font_attr attr;
67 struct kmscon_font_attr real_attr;
68 unsigned int baseline;
70 pthread_mutex_t glyph_lock;
71 struct shl_hashtable *glyphs;
73 struct kmscon_glyph empty;
74 struct kmscon_glyph inval;
77 static pthread_mutex_t manager_mutex = PTHREAD_MUTEX_INITIALIZER;
78 static unsigned long manager__refcnt;
79 static FT_Library manager__lib;
80 static struct shl_dlist manager__list = SHL_DLIST_INIT(manager__list);
82 static void manager_lock()
84 pthread_mutex_lock(&manager_mutex);
87 static void manager_unlock()
89 pthread_mutex_unlock(&manager_mutex);
92 /* TODO: We currently load the default font-config configuration on start-up but
93 * should probably provide a way to refresh it on SIGHUP or similar. Font-config
94 * provides the FcInitBringUptoDate() or FcInitReinitialize() functions. */
95 static int manager__ref()
99 if (!manager__refcnt++) {
100 err = FT_Init_FreeType(&manager__lib);
102 log_warn("cannot initialize freetype2");
108 log_warn("cannot initialize fontconfig library");
109 err = FT_Done_FreeType(manager__lib);
111 log_warn("cannot deinitialize freetype2");
120 static void manager__unref()
124 if (!--manager__refcnt) {
125 /* FcFini() uses assert() to check whether all resources were
126 * correctly freed before FcFini() is called. As an emergency
127 * console, we cannot risk being killed because we have a small
128 * memory leak. Therefore, we rather skip deinitializing
129 * fontconfig and blame their authors here.
130 * Never ever use assert()/abort()/etc. in critical code paths!
132 * TODO: Fix upstream fontconfig to drop all those ugly
135 err = FT_Done_FreeType(manager__lib);
137 log_warn("cannot deinitialize freetype2");
141 static int get_glyph(struct face *face, struct kmscon_glyph **out,
142 uint32_t id, const uint32_t *ch, size_t len)
144 struct kmscon_glyph *glyph;
151 unsigned int i, j, wmax, hmax, idx1, idx2, cwidth;
152 int ret, hoff1, hoff2, woff1, woff2;
156 cwidth = tsm_ucs4_get_width(*ch);
160 pthread_mutex_lock(&face->glyph_lock);
161 res = shl_hashtable_find(face->glyphs, (void**)&glyph,
163 pthread_mutex_unlock(&face->glyph_lock);
171 glyph = malloc(sizeof(*glyph) + sizeof(struct glyph));
173 log_error("cannot allocate memory for new glyph");
177 memset(glyph, 0, sizeof(*glyph) + sizeof(struct glyph));
178 glyph->data = (void*)(((uint8_t*)glyph) + sizeof(*glyph));
180 glyph->width = cwidth;
182 /* We currently ignore composed-symbols. That is, we only use the first
183 * UCS-4 code and draw this character. This works great for most simple
184 * ASCII characters but composed CJK characters often consist of
185 * multiple UCS-4 codes.
186 * TODO: Fix this by drawing all related characters into a single glyph
187 * and saving it or simply refer to the pango backend which already does
194 idx = FT_Get_Char_Index(face->face, *ch);
195 err = FT_Load_Glyph(face->face, idx, FT_LOAD_DEFAULT);
201 err = FT_Render_Glyph(face->face->glyph, FT_RENDER_MODE_NORMAL);
207 slot = face->face->glyph;
208 bmap = &slot->bitmap;
209 if (slot->format != FT_GLYPH_FORMAT_BITMAP ||
210 bmap->pixel_mode != FT_PIXEL_MODE_GRAY ||
211 bmap->num_grays != 256 ||
218 data->width = bmap->width;
219 glyph->buf.width = face->real_attr.width * cwidth;
220 glyph->buf.height = face->real_attr.height;
221 glyph->buf.stride = glyph->buf.width;
222 glyph->buf.format = UTERM_FORMAT_GREY;
223 glyph->buf.data = malloc(glyph->buf.stride * glyph->buf.height);
224 if (!glyph->buf.data) {
229 memset(glyph->buf.data, 0, glyph->buf.stride * glyph->buf.height);
231 /* compute width-offsets and relative width-differences */
232 if (slot->bitmap_left >= glyph->buf.width) {
236 } else if (slot->bitmap_left < 0) {
237 if (glyph->buf.width > bmap->width)
240 wmax = glyph->buf.width;
244 wmax = glyph->buf.width - slot->bitmap_left;
245 if (wmax > bmap->width)
247 woff1 = slot->bitmap_left;
251 /* compute height-offsets and relative height-differences */
252 hoff1 = (int)glyph->buf.height - face->baseline;
253 if (hoff1 > slot->bitmap_top) {
254 hoff1 -= slot->bitmap_top;
257 hoff2 = slot->bitmap_top - hoff1;
261 if (bmap->rows - hoff2 > glyph->buf.height - hoff1)
262 hmax = glyph->buf.height - hoff1;
264 hmax = bmap->rows - hoff2;
266 /* copy bitmap into glyph buffer */
267 for (i = 0; i < hmax; ++i) {
268 for (j = 0; j < wmax; ++j) {
269 idx1 = (i + hoff1) * glyph->buf.stride + (j + woff1);
270 idx2 = (i + hoff2) * bmap->pitch + (j + woff2);
271 glyph->buf.data[idx1] = bmap->buffer[idx2];
275 pthread_mutex_lock(&face->glyph_lock);
276 ret = shl_hashtable_insert(face->glyphs, (void*)(long)id, glyph);
277 pthread_mutex_unlock(&face->glyph_lock);
279 log_error("cannot add glyph to hashtable");
287 free(glyph->buf.data);
295 static void free_glyph(void *data)
297 struct kmscon_glyph *glyph = data;
299 free(glyph->buf.data);
303 static int manager_get_face(struct face **out, struct kmscon_font_attr *attr)
305 struct shl_dlist *iter;
306 struct face *face, *f;
307 FcPattern *pat, *mat;
311 int ret, tmp, idx, weight, slant;
312 double s, em, xsc, ysc;
322 attr->width = attr->height;
324 shl_dlist_for_each(iter, &manager__list) {
325 face = shl_dlist_entry(iter, struct face, list);
326 if (kmscon_font_attr_match(&face->attr, attr)) {
334 ret = manager__ref();
338 face = malloc(sizeof(*face));
340 log_error("cannot allocate memory for new face");
344 memset(face, 0, sizeof(*face));
346 memcpy(&face->attr, attr, sizeof(*attr));
347 memcpy(&face->real_attr, attr, sizeof(*attr));
349 ret = pthread_mutex_init(&face->glyph_lock, NULL);
351 log_error("cannot initialize glyph lock");
355 ret = shl_hashtable_new(&face->glyphs, shl_direct_hash,
356 shl_direct_equal, NULL, free_glyph);
358 log_error("cannot allocate hashtable");
362 s = face->attr.height;
363 weight = face->attr.bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
364 slant = face->attr.italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
365 pat = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, face->attr.name,
366 FC_PIXEL_SIZE, FcTypeDouble, s,
367 FC_WEIGHT, FcTypeInteger, weight,
368 FC_SLANT, FcTypeInteger, slant,
371 log_error("cannot create font-config pattern");
376 if (!FcConfigSubstitute(NULL, pat, FcMatchPattern)) {
377 FcPatternDestroy(pat);
378 log_error("cannot perform font-config substitutions");
384 mat = FcFontMatch(NULL, pat, &res);
385 if (res != FcResultMatch) {
387 FcPatternDestroy(mat);
388 FcPatternDestroy(pat);
389 log_error("font-config cannot find font: %d", res);
394 res = FcPatternGetString(mat, FC_FILE, 0, &fname);
395 if (res != FcResultMatch) {
396 FcPatternDestroy(mat);
397 FcPatternDestroy(pat);
398 log_error("font-config cannot find font (name)");
402 res = FcPatternGetInteger(mat, FC_INDEX, 0, &idx);
403 if (res != FcResultMatch) {
404 FcPatternDestroy(mat);
405 FcPatternDestroy(pat);
406 log_error("font-config cannot find font (index)");
411 log_debug("loading font %s:%d", (const char*)fname, idx);
412 err = FT_New_Face(manager__lib, (const char*)fname, idx, &face->face);
413 FcPatternDestroy(mat);
414 FcPatternDestroy(pat);
417 if (err == FT_Err_Unknown_File_Format)
418 log_error("unknown font file format");
420 log_error("cannot load font");
426 if (!face->face->charmap) {
427 log_warn("cannot load charmap of new font");
432 if (!FT_IS_SCALABLE(face->face)) {
433 log_warn("non-scalable font");
438 err = FT_Set_Pixel_Sizes(face->face, face->attr.width,
441 log_warn("cannot set pixel size of font");
446 /* Every font provides an ascender/descender value which we use to
447 * compute glyph-height and the baseline offset. We need monospace fonts
448 * as we have the same fixed bounding-box for every glyph. However, if
449 * the font is not a monospace font, then the most straight-forward
450 * approach would be using the biggest bounding box. This, however, will
451 * not work as some characters are extremely wide and the text will look
452 * horrible. Therefore, we use the ascender/descender values provided
453 * with each font. This guarantees that special characters like
454 * line-segments are properly aligned without spacing. If the font does
455 * not provide proper asc/desc values, then freetype2 will return proper
458 em = face->face->units_per_EM;
459 xsc = face->face->size->metrics.x_ppem / em;
460 ysc = face->face->size->metrics.y_ppem / em;
462 tmp = face->face->descender * ysc;
465 face->baseline = -tmp;
467 tmp = face->face->ascender * ysc + face->baseline;
468 if (tmp < 0 || tmp < face->baseline) {
469 log_warn("invalid ascender/descender values for font");
473 face->real_attr.height = tmp;
475 /* For font-width we use the biggest bounding-box-width. After the font
476 * has been loaded, this is cut down by pre-rendering some characters
477 * and computing a better average. */
479 tmp = 1 + (int)(xsc * (face->face->bbox.xMax - face->face->bbox.xMin));
482 face->real_attr.width = tmp;
484 kmscon_font_attr_normalize(&face->real_attr);
485 if (!face->real_attr.height || !face->real_attr.width) {
486 log_warn("invalid scaled font sizes");
491 /* The real metrics probably differ from the requested metrics so try
492 * again to find a suitable cached font. */
493 shl_dlist_for_each(iter, &manager__list) {
494 f = shl_dlist_entry(iter, struct face, list);
495 if (kmscon_font_attr_match(&f->real_attr, &face->real_attr)) {
503 shl_dlist_link(&manager__list, &face->list);
509 FT_Done_Face(face->face);
511 shl_hashtable_free(face->glyphs);
513 pthread_mutex_destroy(&face->glyph_lock);
523 static void manager_put_face(struct face *face)
528 shl_dlist_unlink(&face->list);
529 shl_hashtable_free(face->glyphs);
530 pthread_mutex_destroy(&face->glyph_lock);
531 FT_Done_Face(face->face);
539 static int generate_specials(struct face *face)
542 struct kmscon_glyph *g;
544 static const uint32_t question_mark = '?';
546 face->empty.width = 1;
547 face->empty.data = NULL;
548 face->empty.buf.width = face->real_attr.width;
549 face->empty.buf.height = face->real_attr.height;
550 face->empty.buf.stride = face->empty.buf.width;
551 face->empty.buf.format = UTERM_FORMAT_GREY;
552 s = face->empty.buf.stride * face->empty.buf.height;
553 face->empty.buf.data = malloc(s);
554 if (!face->empty.buf.data)
557 memset(face->empty.buf.data, 0, s);
559 ret = get_glyph(face, &g, question_mark, &question_mark, 1);
561 memcpy(&face->inval, &face->empty, sizeof(face->inval));
563 memcpy(&face->inval, g, sizeof(face->inval));
569 static int kmscon_font_freetype2_init(struct kmscon_font *out,
570 const struct kmscon_font_attr *attr)
572 struct face *face = NULL;
576 struct kmscon_glyph *glyph;
579 memcpy(&out->attr, attr, sizeof(*attr));
580 kmscon_font_attr_normalize(&out->attr);
582 log_debug("loading freetype2 font %s", out->attr.name);
584 ret = manager_get_face(&face, &out->attr);
587 memcpy(&out->attr, &face->real_attr, sizeof(out->attr));
588 out->baseline = face->baseline;
590 /* Shrinking is done to get a better width-value for fonts. As not all
591 * fonts provide monospace-glyphs, we need to calculate a proper width
592 * by pre-rendering all ASCII characters and using the widest value.
593 * TODO: We should extend this with a better algorithm as there are
594 * common non-ASCII glyphs which are much wider.
595 * We enable shrinking by default as most fonts have a maximum-width
596 * which is about 3 times the size of 'M'.*/
601 for (i = 0x20; i < 0x7f; ++i) {
602 ret = get_glyph(face, &glyph, i, &i, 1);
607 if (data->width > width)
612 log_warning("cannot measure font");
613 face->shrink = false;
614 } else if (width < face->real_attr.width) {
615 face->real_attr.width = width;
616 kmscon_font_attr_normalize(&face->real_attr);
617 memcpy(&out->attr, &face->real_attr, sizeof(out->attr));
621 /* generate inval/empty glyphs after shrinking */
622 ret = generate_specials(face);
630 manager_put_face(face);
634 static void kmscon_font_freetype2_destroy(struct kmscon_font *font)
639 log_debug("unloading freetype2 font %s", face->real_attr.name);
640 free(face->empty.buf.data);
641 manager_put_face(face);
644 static int kmscon_font_freetype2_render(struct kmscon_font *font, uint32_t id,
645 const uint32_t *ch, size_t len,
646 const struct kmscon_glyph **out)
648 struct kmscon_glyph *glyph;
653 ret = get_glyph(font->data, &glyph, id, ch, len);
659 if (face->shrink && !data->shrinked) {
660 data->shrinked = true;
661 glyph->buf.width = face->real_attr.width * glyph->width;
668 static int kmscon_font_freetype2_render_empty(struct kmscon_font *font,
669 const struct kmscon_glyph **out)
671 struct face *face = font->data;
677 static int kmscon_font_freetype2_render_inval(struct kmscon_font *font,
678 const struct kmscon_glyph **out)
680 struct face *face = font->data;
686 struct kmscon_font_ops kmscon_font_freetype2_ops = {
689 .init = kmscon_font_freetype2_init,
690 .destroy = kmscon_font_freetype2_destroy,
691 .render = kmscon_font_freetype2_render,
692 .render_empty = kmscon_font_freetype2_render_empty,
693 .render_inval = kmscon_font_freetype2_render_inval,