37800ddbe53bf75c820d5c0506ee0a7ebb1f3832
[platform/upstream/kmscon.git] / src / font_freetype2.c
1 /*
2  * kmscon - Freetype2 font backend
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5  * Copyright (c) 2011 University of Tuebingen
6  *
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:
14  *
15  * The above copyright notice and this permission notice shall be included
16  * in all copies or substantial portions of the Software.
17  *
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.
25  */
26
27 /**
28  * SECTION:font_freetype2.c
29  * @short_description: Freetype2 font backend
30  * @include: font.h
31  *
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.
37  */
38
39 #include <errno.h>
40 #include <fontconfig/fontconfig.h>
41 #include <ft2build.h>
42 #include FT_FREETYPE_H
43 #include <pthread.h>
44 #include <stdbool.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "font.h"
48 #include "shl_dlist.h"
49 #include "shl_hashtable.h"
50 #include "shl_log.h"
51 #include "tsm_unicode.h"
52 #include "uterm_video.h"
53
54 #define LOG_SUBSYSTEM "font_freetype2"
55
56 struct glyph {
57         bool shrinked;
58         unsigned int width;
59 };
60
61 struct face {
62         unsigned long ref;
63         struct shl_dlist list;
64
65         bool shrink;
66         struct kmscon_font_attr attr;
67         struct kmscon_font_attr real_attr;
68         unsigned int baseline;
69         FT_Face face;
70         pthread_mutex_t glyph_lock;
71         struct shl_hashtable *glyphs;
72
73         struct kmscon_glyph empty;
74         struct kmscon_glyph inval;
75 };
76
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);
81
82 static void manager_lock()
83 {
84         pthread_mutex_lock(&manager_mutex);
85 }
86
87 static void manager_unlock()
88 {
89         pthread_mutex_unlock(&manager_mutex);
90 }
91
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()
96 {
97         FT_Error err;
98
99         if (!manager__refcnt++) {
100                 err = FT_Init_FreeType(&manager__lib);
101                 if (err) {
102                         log_warn("cannot initialize freetype2");
103                         --manager__refcnt;
104                         return -EFAULT;
105                 }
106
107                 if (!FcInit()) {
108                         log_warn("cannot initialize fontconfig library");
109                         err = FT_Done_FreeType(manager__lib);
110                         if (err)
111                                 log_warn("cannot deinitialize freetype2");
112                         --manager__refcnt;
113                         return -EFAULT;
114                 }
115         }
116
117         return 0;
118 }
119
120 static void manager__unref()
121 {
122         FT_Error err;
123
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!
131                  * Bullshit...
132                  * TODO: Fix upstream fontconfig to drop all those ugly
133                  * assertions. */
134                 // FcFini();
135                 err = FT_Done_FreeType(manager__lib);
136                 if (err)
137                         log_warn("cannot deinitialize freetype2");
138         }
139 }
140
141 static int get_glyph(struct face *face, struct kmscon_glyph **out,
142                      uint32_t id, const uint32_t *ch, size_t len)
143 {
144         struct kmscon_glyph *glyph;
145         struct glyph *data;
146         FT_Error err;
147         FT_UInt idx;
148         FT_Bitmap *bmap;
149         FT_GlyphSlot slot;
150         bool res;
151         unsigned int i, j, wmax, hmax, idx1, idx2, cwidth;
152         int ret, hoff1, hoff2, woff1, woff2;
153
154         if (!len)
155                 return -ERANGE;
156         cwidth = tsm_ucs4_get_width(*ch);
157         if (!cwidth)
158                 return -ERANGE;
159
160         pthread_mutex_lock(&face->glyph_lock);
161         res = shl_hashtable_find(face->glyphs, (void**)&glyph,
162                                  (void*)(long)id);
163         pthread_mutex_unlock(&face->glyph_lock);
164         if (res) {
165                 *out = glyph;
166                 return 0;
167         }
168
169         manager_lock();
170
171         glyph = malloc(sizeof(*glyph) + sizeof(struct glyph));
172         if (!glyph) {
173                 log_error("cannot allocate memory for new glyph");
174                 ret = -ENOMEM;
175                 goto out_unlock;
176         }
177         memset(glyph, 0, sizeof(*glyph) + sizeof(struct glyph));
178         glyph->data = (void*)(((uint8_t*)glyph) + sizeof(*glyph));
179         data = glyph->data;
180         glyph->width = cwidth;
181
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
188          * that. */
189         if (!*ch) {
190                 ret = -ERANGE;
191                 goto out_glyph;
192         }
193
194         idx = FT_Get_Char_Index(face->face, *ch);
195         err = FT_Load_Glyph(face->face, idx, FT_LOAD_DEFAULT);
196         if (err) {
197                 ret = -ERANGE;
198                 goto out_glyph;
199         }
200
201         err = FT_Render_Glyph(face->face->glyph, FT_RENDER_MODE_NORMAL);
202         if (err) {
203                 ret = -ERANGE;
204                 goto out_glyph;
205         }
206
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 ||
212             !bmap->rows ||
213             !bmap->width) {
214                 ret = -ERANGE;
215                 goto out_glyph;
216         }
217
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) {
225                 ret = -ENOMEM;
226                 goto out_glyph;
227         }
228
229         memset(glyph->buf.data, 0, glyph->buf.stride * glyph->buf.height);
230
231         /* compute width-offsets and relative width-differences */
232         if (slot->bitmap_left >= glyph->buf.width) {
233                 wmax = 0;
234                 woff1 = 0;
235                 woff2 = 0;
236         } else if (slot->bitmap_left < 0) {
237                 if (glyph->buf.width > bmap->width)
238                         wmax = bmap->width;
239                 else
240                         wmax = glyph->buf.width;
241                 woff1 = 0;
242                 woff2 = 0;
243         } else {
244                 wmax = glyph->buf.width - slot->bitmap_left;
245                 if (wmax > bmap->width)
246                         wmax = bmap->width;
247                 woff1 = slot->bitmap_left;
248                 woff2 = 0;
249         }
250
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;
255                 hoff2 = 0;
256         } else {
257                 hoff2 = slot->bitmap_top - hoff1;
258                 hoff1 = 0;
259         }
260
261         if (bmap->rows - hoff2 > glyph->buf.height - hoff1)
262                 hmax = glyph->buf.height - hoff1;
263         else
264                 hmax = bmap->rows - hoff2;
265
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];
272                 }
273         }
274
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);
278         if (ret) {
279                 log_error("cannot add glyph to hashtable");
280                 goto out_buffer;
281         }
282
283         *out = glyph;
284         goto out_unlock;
285
286 out_buffer:
287         free(glyph->buf.data);
288 out_glyph:
289         free(glyph);
290 out_unlock:
291         manager_unlock();
292         return ret;
293 }
294
295 static void free_glyph(void *data)
296 {
297         struct kmscon_glyph *glyph = data;
298
299         free(glyph->buf.data);
300         free(glyph);
301 }
302
303 static int manager_get_face(struct face **out, struct kmscon_font_attr *attr)
304 {
305         struct shl_dlist *iter;
306         struct face *face, *f;
307         FcPattern *pat, *mat;
308         FcResult res;
309         FcChar8 *fname;
310         FT_Error err;
311         int ret, tmp, idx, weight, slant;
312         double s, em, xsc, ysc;
313
314         manager_lock();
315
316         if (!attr->height) {
317                 ret = -EINVAL;
318                 goto out_unlock;
319         }
320
321         if (!attr->width)
322                 attr->width = attr->height;
323
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)) {
327                         ++face->ref;
328                         *out = face;
329                         ret = 0;
330                         goto out_unlock;
331                 }
332         }
333
334         ret = manager__ref();
335         if (ret)
336                 goto out_unlock;
337
338         face = malloc(sizeof(*face));
339         if (!face) {
340                 log_error("cannot allocate memory for new face");
341                 ret = -ENOMEM;
342                 goto err_manager;
343         }
344         memset(face, 0, sizeof(*face));
345         face->ref = 1;
346         memcpy(&face->attr, attr, sizeof(*attr));
347         memcpy(&face->real_attr, attr, sizeof(*attr));
348
349         ret = pthread_mutex_init(&face->glyph_lock, NULL);
350         if (ret) {
351                 log_error("cannot initialize glyph lock");
352                 goto err_free;
353         }
354
355         ret = shl_hashtable_new(&face->glyphs, shl_direct_hash,
356                                 shl_direct_equal, NULL, free_glyph);
357         if (ret) {
358                 log_error("cannot allocate hashtable");
359                 goto err_lock;
360         }
361
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,
369                                    NULL);
370         if (!pat) {
371                 log_error("cannot create font-config pattern");
372                 ret = -EFAULT;
373                 goto err_htable;
374         }
375
376         if (!FcConfigSubstitute(NULL, pat, FcMatchPattern)) {
377                 FcPatternDestroy(pat);
378                 log_error("cannot perform font-config substitutions");
379                 ret = -ENOMEM;
380                 goto err_htable;
381         }
382
383         res = FcResultMatch;
384         mat = FcFontMatch(NULL, pat, &res);
385         if (res != FcResultMatch) {
386                 if (mat)
387                         FcPatternDestroy(mat);
388                 FcPatternDestroy(pat);
389                 log_error("font-config cannot find font: %d", res);
390                 ret = -EFAULT;
391                 goto err_htable;
392         }
393
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)");
399                 ret = -EFAULT;
400                 goto err_htable;
401         }
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)");
407                 ret = -EFAULT;
408                 goto err_htable;
409         }
410
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);
415
416         if (err) {
417                 if (err == FT_Err_Unknown_File_Format)
418                         log_error("unknown font file format");
419                 else
420                         log_error("cannot load font");
421
422                 ret = -EFAULT;
423                 goto err_htable;
424         }
425
426         if (!face->face->charmap) {
427                 log_warn("cannot load charmap of new font");
428                 ret = -EFAULT;
429                 goto err_face;
430         }
431
432         if (!FT_IS_SCALABLE(face->face)) {
433                 log_warn("non-scalable font");
434                 ret = -EFAULT;
435                 goto err_face;
436         }
437
438         err = FT_Set_Pixel_Sizes(face->face, face->attr.width,
439                                  face->attr.height);
440         if (err) {
441                 log_warn("cannot set pixel size of font");
442                 ret = -EFAULT;
443                 goto err_face;
444         }
445
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
456          * substitutions. */
457
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;
461
462         tmp = face->face->descender * ysc;
463         if (tmp > 0)
464                 tmp = 0;
465         face->baseline = -tmp;
466
467         tmp = face->face->ascender * ysc + face->baseline;
468         if (tmp < 0 || tmp < face->baseline) {
469                 log_warn("invalid ascender/descender values for font");
470                 ret = -EFAULT;
471                 goto err_face;
472         }
473         face->real_attr.height = tmp;
474
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. */
478
479         tmp = 1 + (int)(xsc * (face->face->bbox.xMax - face->face->bbox.xMin));
480         if (tmp < 0)
481                 tmp = 0;
482         face->real_attr.width = tmp;
483
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");
487                 ret = -EFAULT;
488                 goto err_face;
489         }
490
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)) {
496                         ++f->ref;
497                         *out = f;
498                         ret = 0;
499                         goto err_face;
500                 }
501         }
502
503         shl_dlist_link(&manager__list, &face->list);
504         *out = face;
505         ret = 0;
506         goto out_unlock;
507
508 err_face:
509         FT_Done_Face(face->face);
510 err_htable:
511         shl_hashtable_free(face->glyphs);
512 err_lock:
513         pthread_mutex_destroy(&face->glyph_lock);
514 err_free:
515         free(face);
516 err_manager:
517         manager__unref();
518 out_unlock:
519         manager_unlock();
520         return ret;
521 }
522
523 static void manager_put_face(struct face *face)
524 {
525         manager_lock();
526
527         if (!--face->ref) {
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);
532                 free(face);
533                 manager__unref();
534         }
535
536         manager_unlock();
537 }
538
539 static int generate_specials(struct face *face)
540 {
541         size_t s;
542         struct kmscon_glyph *g;
543         int ret;
544         static const uint32_t question_mark = '?';
545
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)
555                 return -ENOMEM;
556
557         memset(face->empty.buf.data, 0, s);
558
559         ret = get_glyph(face, &g, question_mark, &question_mark, 1);
560         if (ret) {
561                 memcpy(&face->inval, &face->empty, sizeof(face->inval));
562         } else {
563                 memcpy(&face->inval, g, sizeof(face->inval));
564         }
565
566         return 0;
567 }
568
569 static int kmscon_font_freetype2_init(struct kmscon_font *out,
570                                       const struct kmscon_font_attr *attr)
571 {
572         struct face *face = NULL;
573         int ret;
574         unsigned int width;
575         uint32_t i;
576         struct kmscon_glyph *glyph;
577         struct glyph *data;
578
579         memcpy(&out->attr, attr, sizeof(*attr));
580         kmscon_font_attr_normalize(&out->attr);
581
582         log_debug("loading freetype2 font %s", out->attr.name);
583
584         ret = manager_get_face(&face, &out->attr);
585         if (ret)
586                 return ret;
587         memcpy(&out->attr, &face->real_attr, sizeof(out->attr));
588         out->baseline = face->baseline;
589
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'.*/
597         face->shrink = true;
598
599         if (face->shrink) {
600                 width = 0;
601                 for (i = 0x20; i < 0x7f; ++i) {
602                         ret = get_glyph(face, &glyph, i, &i, 1);
603                         if (ret)
604                                 continue;
605                         data = glyph->data;
606
607                         if (data->width > width)
608                                 width = data->width;
609                 }
610
611                 if (!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));
618                 }
619         }
620
621         /* generate inval/empty glyphs after shrinking */
622         ret = generate_specials(face);
623         if (ret)
624                 goto err_face;
625
626         out->data = face;
627         return 0;
628
629 err_face:
630         manager_put_face(face);
631         return ret;
632 }
633
634 static void kmscon_font_freetype2_destroy(struct kmscon_font *font)
635 {
636         struct face *face;
637
638         face = font->data;
639         log_debug("unloading freetype2 font %s", face->real_attr.name);
640         free(face->empty.buf.data);
641         manager_put_face(face);
642 }
643
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)
647 {
648         struct kmscon_glyph *glyph;
649         struct glyph *data;
650         struct face *face;
651         int ret;
652
653         ret = get_glyph(font->data, &glyph, id, ch, len);
654         if (ret)
655                 return ret;
656
657         face = font->data;
658         data = glyph->data;
659         if (face->shrink && !data->shrinked) {
660                 data->shrinked = true;
661                 glyph->buf.width = face->real_attr.width * glyph->width;
662         }
663
664         *out = glyph;
665         return 0;
666 }
667
668 static int kmscon_font_freetype2_render_empty(struct kmscon_font *font,
669                                               const struct kmscon_glyph **out)
670 {
671         struct face *face = font->data;
672
673         *out = &face->empty;
674         return 0;
675 }
676
677 static int kmscon_font_freetype2_render_inval(struct kmscon_font *font,
678                                               const struct kmscon_glyph **out)
679 {
680         struct face *face = font->data;
681
682         *out = &face->inval;
683         return 0;
684 }
685
686 struct kmscon_font_ops kmscon_font_freetype2_ops = {
687         .name = "freetype2",
688         .owner = NULL,
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,
694 };