1 #include "fcft/fcft.h"
2 
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <stdbool.h>
6 #include <wchar.h>
7 #include <math.h>
8 #include <assert.h>
9 #include <threads.h>
10 #include <locale.h>
11 
12 #include <pthread.h>
13 
14 #include <ft2build.h>
15 #include FT_FREETYPE_H
16 #include FT_MODULE_H
17 #include FT_LCD_FILTER_H
18 #include FT_TRUETYPE_TABLES_H
19 #include FT_SYNTHESIS_H
20 #include <fontconfig/fontconfig.h>
21 
22 #if defined(FCFT_HAVE_HARFBUZZ)
23  #include <harfbuzz/hb.h>
24  #include <harfbuzz/hb-ft.h>
25 #endif
26 #if defined(FCFT_HAVE_UTF8PROC)
27  #include <utf8proc.h>
28 #endif
29 
30 #include <tllist.h>
31 
32 #define LOG_MODULE "fcft"
33 #define LOG_ENABLE_DBG 0
34 #include "log.h"
35 #include "fcft/stride.h"
36 
37 #include "emoji-data.h"
38 #include "unicode-compose-table.h"
39 #include "version.h"
40 
41 static_assert(sizeof(wchar_t) >= 4, "wchar_t is not wide enough for Unicode");
42 
43 #define min(x, y) ((x) < (y) ? (x) : (y))
44 #define max(x, y) ((x) > (y) ? (x) : (y))
45 #define ALEN(v) (sizeof(v) / sizeof((v)[0]))
46 
47 static FT_Error ft_lib_err;
48 static FT_Library ft_lib;
49 static mtx_t ft_lock;
50 static bool can_set_lcd_filter = false;
51 static enum fcft_scaling_filter scaling_filter = FCFT_SCALING_FILTER_CUBIC;
52 
53 static const size_t glyph_cache_initial_size = 256;
54 #if defined(FCFT_HAVE_HARFBUZZ)
55 static const size_t grapheme_cache_initial_size = 256;
56 #endif
57 
58 #if defined(_DEBUG)
59 static size_t glyph_cache_lookups = 0;
60 static size_t glyph_cache_collisions = 0;
61 
62 #if defined(FCFT_HAVE_HARFBUZZ)
63 static size_t grapheme_cache_lookups = 0;
64 static size_t grapheme_cache_collisions = 0;
65 #endif
66 #endif
67 
68 struct glyph_priv {
69     struct fcft_glyph public;
70     enum fcft_subpixel subpixel;
71     bool valid;
72 };
73 
74 struct grapheme_priv {
75     struct fcft_grapheme public;
76 
77     size_t len;
78     wchar_t *cluster;  /* ‘len’ characters */
79 
80     enum fcft_subpixel subpixel;
81     bool valid;
82 };
83 
84 struct instance {
85     char *path;
86     FT_Face face;
87     int load_flags;
88 
89 #if defined(FCFT_HAVE_HARFBUZZ)
90     hb_font_t *hb_font;
91     hb_buffer_t *hb_buf;
92     hb_feature_t hb_feats[32];
93     size_t hb_feats_count;
94 #endif
95 
96     bool antialias;
97     bool embolden;
98     bool is_color;
99     int render_flags_normal;
100     int render_flags_subpixel;
101 
102     FT_LcdFilter lcd_filter;
103 
104     double pixel_size_fixup; /* Scale factor - should only be used with ARGB32 glyphs */
105     bool pixel_fixup_estimated;
106     bool bgr;  /* True for FC_RGBA_BGR and FC_RGBA_VBGR */
107 
108     struct fcft_font metrics;
109 };
110 
111 struct fallback {
112     FcPattern *pattern;
113     FcCharSet *charset;
114     FcLangSet *langset;
115     struct instance *font;
116 
117     /* User-requested size(s) - i.e. sizes from *base* pattern */
118     double req_pt_size;
119     double req_px_size;
120 };
121 
122 struct font_priv {
123     /* Must be first */
124     struct fcft_font public;
125 
126     mtx_t lock;
127     pthread_rwlock_t glyph_cache_lock;
128     struct {
129         struct glyph_priv **table;
130         size_t size;
131         size_t count;
132     } glyph_cache;
133 
134 #if defined(FCFT_HAVE_HARFBUZZ)
135     pthread_rwlock_t grapheme_cache_lock;
136     struct {
137         struct grapheme_priv **table;
138         size_t size;
139         size_t count;
140     } grapheme_cache;
141 #endif
142 
143     tll(struct fallback) fallbacks;
144     enum fcft_emoji_presentation emoji_presentation;
145     size_t ref_counter;
146 };
147 
148 /* Global font cache */
149 struct fcft_font_cache_entry {
150     uint64_t hash;
151     struct font_priv *font;
152 
153     int waiters;
154     cnd_t cond;
155 };
156 static tll(struct fcft_font_cache_entry) font_cache = tll_init();
157 static mtx_t font_cache_lock;
158 
159 FCFT_EXPORT enum fcft_capabilities
fcft_capabilities(void)160 fcft_capabilities(void)
161 {
162     enum fcft_capabilities ret = 0;
163 
164 #if defined(FCFT_HAVE_HARFBUZZ)
165     ret |= FCFT_CAPABILITY_GRAPHEME_SHAPING;
166 #endif
167 #if defined(FCFT_HAVE_HARFBUZZ) && defined(FCFT_HAVE_UTF8PROC)
168     ret |= FCFT_CAPABILITY_TEXT_RUN_SHAPING;
169 #endif
170 
171     return ret;
172 }
173 
174 static const char *
ft_error_string(FT_Error err)175 ft_error_string(FT_Error err)
176 {
177     #undef FTERRORS_H_
178     #undef __FTERRORS_H__
179     #define FT_ERRORDEF( e, v, s )  case e: return s;
180     #define FT_ERROR_START_LIST     switch (err) {
181     #define FT_ERROR_END_LIST       }
182     #include FT_ERRORS_H
183     return "unknown error";
184 }
185 
186 static void __attribute__((constructor))
init(void)187 init(void)
188 {
189     FcInit();
190     ft_lib_err = FT_Init_FreeType(&ft_lib);
191 
192     /*
193      * Some FreeType builds use the older ClearType-style subpixel
194      * rendering. This mode requires an LCD filter to be set, or it
195      * will produce *severe* color fringes.
196      *
197      * Which subpixel rendering mode to use depends on a FreeType
198      * build-time #define, FT_CONFIG_OPTION_SUBPIXEL_RENDERING. By
199      * default, it is *unset*, meaning FreeType will use the newer
200      * 'Harmony' subpixel rendering mode (i.e. disabling
201      * FT_CONFIG_OPTION_SUBPIXEL_RENDERING does *not* disable subpixel
202      * rendering).
203      *
204      * If defined, ClearType-style subpixel rendering is instead enabled.
205      *
206      * The FT_Library_SetLcdFilter() configures a library instance
207      * global LCD filter. This call will *fail* if
208      * FT_CONFIG_OPTION_SUBPIXEL_RENDERING has not been set.
209      *
210      * In theory, different fonts can have different LCD filters, and
211      * for this reason we need to set the filter *each* time we need
212      * to render a glyph. Since FT_Library_SetLcdFilter() is per
213      * library instance, this means taking the library global lock.
214      *
215      * For performance reasons, we want to skip this altogether if we
216      * know that the call will fail (i.e. when FreeType is using
217      * Harmony). So, detect here in init(), whether we can set an LCD
218      * filter or not.
219      *
220      * When rendering a glyph, we will only configure the LCD filter
221      * (and thus take the library global lock) if we know we *can* set
222      * the filter, and if we need to (the rendering mode includes
223      * FT_RENDER_MODE_LCD{,_V}).
224      */
225 
226     FT_Error err = FT_Library_SetLcdFilter(ft_lib, FT_LCD_FILTER_DEFAULT);
227     can_set_lcd_filter = err == 0;
228     LOG_DBG("can set LCD filter: %s (%d)", ft_error_string(err), err);
229 
230     if (can_set_lcd_filter)
231         FT_Library_SetLcdFilter(ft_lib, FT_LCD_FILTER_NONE);
232 
233 #if defined(FCFT_HAVE_HARFBUZZ)
234     /* Not thread safe first time called */
235     hb_language_get_default();
236 #endif
237 
238     mtx_init(&ft_lock, mtx_plain);
239     mtx_init(&font_cache_lock, mtx_plain);
240 }
241 
242 static void __attribute__((destructor))
fini(void)243 fini(void)
244 {
245     while (tll_length(font_cache) > 0) {
246         if (tll_front(font_cache).font == NULL)
247             tll_pop_front(font_cache);
248         else
249             fcft_destroy(&tll_front(font_cache).font->public);
250     }
251 
252     assert(tll_length(font_cache) == 0);
253 
254     mtx_destroy(&font_cache_lock);
255     mtx_destroy(&ft_lock);
256 
257     if (ft_lib_err == FT_Err_Ok)
258         FT_Done_FreeType(ft_lib);
259 
260     FcFini();
261 
262     LOG_DBG("glyph cache: lookups=%zu, collisions=%zu",
263             glyph_cache_lookups, glyph_cache_collisions);
264 
265 #if defined(FCFT_HAVE_HARFBUZZ)
266     LOG_DBG("grapheme cache: lookups=%zu, collisions=%zu",
267             grapheme_cache_lookups, grapheme_cache_collisions);
268 #endif
269 }
270 
271 static bool
is_assertions_enabled(void)272 is_assertions_enabled(void)
273 {
274 #if defined(NDEBUG)
275     return false;
276 #else
277     return true;
278 #endif
279 }
280 
281 static void
log_version_information(void)282 log_version_information(void)
283 {
284     static bool has_already_logged = false;
285 
286     mtx_lock(&font_cache_lock);
287     if (has_already_logged) {
288         mtx_unlock(&font_cache_lock);
289         return;
290     }
291     has_already_logged = true;
292     mtx_unlock(&font_cache_lock);
293 
294     enum fcft_capabilities caps = fcft_capabilities();
295 
296     static char caps_str[256];
297     snprintf(
298         caps_str, sizeof(caps_str),
299         "%cgraphemes %cruns %cassertions",
300         caps & FCFT_CAPABILITY_GRAPHEME_SHAPING ? '+' : '-',
301         caps & FCFT_CAPABILITY_TEXT_RUN_SHAPING ? '+' : '-',
302         is_assertions_enabled() ? '+' : '-');
303 
304     LOG_INFO("fcft: %s %s", FCFT_VERSION, caps_str);
305 
306     {
307         int raw_version = FcGetVersion();
308 
309         /* See FC_VERSION in <fontconfig/fontconfig.h> */
310         const int major = raw_version / 10000; raw_version %= 10000;
311         const int minor = raw_version / 100; raw_version %= 100;
312         const int patch = raw_version;
313 
314         LOG_INFO("fontconfig: %d.%d.%d", major, minor, patch);
315     }
316 
317     {
318         int major, minor, patch;
319         FT_Library_Version(ft_lib, &major, &minor, &patch);
320         LOG_INFO("freetype: %d.%d.%d", major, minor, patch);
321     }
322 
323 }
324 
325 FCFT_EXPORT bool
fcft_set_scaling_filter(enum fcft_scaling_filter filter)326 fcft_set_scaling_filter(enum fcft_scaling_filter filter)
327 {
328     switch (filter) {
329     case FCFT_SCALING_FILTER_NONE:
330     case FCFT_SCALING_FILTER_NEAREST:
331     case FCFT_SCALING_FILTER_BILINEAR:
332     case FCFT_SCALING_FILTER_CUBIC:
333     case FCFT_SCALING_FILTER_LANCZOS3:
334         scaling_filter = filter;
335         return true;
336     }
337 
338     return false;
339 }
340 
341 static void
glyph_destroy_private(struct glyph_priv * glyph)342 glyph_destroy_private(struct glyph_priv *glyph)
343 {
344     if (glyph->valid) {
345         void *image = pixman_image_get_data(glyph->public.pix);
346         pixman_image_unref(glyph->public.pix);
347         free(image);
348     }
349 
350     free(glyph);
351 }
352 
353 static void
glyph_destroy(const struct fcft_glyph * glyph)354 glyph_destroy(const struct fcft_glyph *glyph)
355 {
356     glyph_destroy_private((struct glyph_priv *)glyph);
357 }
358 
359 static void
instance_destroy(struct instance * inst)360 instance_destroy(struct instance *inst)
361 {
362     if (inst == NULL)
363         return;
364 
365 #if defined(FCFT_HAVE_HARFBUZZ)
366     hb_font_destroy(inst->hb_font);
367     hb_buffer_destroy(inst->hb_buf);
368 #endif
369 
370     mtx_lock(&ft_lock);
371     FT_Done_Face(inst->face);
372     mtx_unlock(&ft_lock);
373 
374     free(inst->path);
375     free(inst);
376 }
377 
378 static void
fallback_destroy(struct fallback * fallback)379 fallback_destroy(struct fallback *fallback)
380 {
381     FcPatternDestroy(fallback->pattern);
382     FcCharSetDestroy(fallback->charset);
383     if (fallback->langset != NULL)
384         FcLangSetDestroy(fallback->langset);
385     instance_destroy(fallback->font);
386 }
387 
388 static void
underline_strikeout_metrics(FT_Face ft_face,struct fcft_font * font)389 underline_strikeout_metrics(FT_Face ft_face, struct fcft_font *font)
390 {
391     double y_scale = ft_face->size->metrics.y_scale / 65536.;
392     double ascent = ft_face->size->metrics.ascender / 64.;
393     double descent = ft_face->size->metrics.descender / 64.;
394 
395     double underline_position = ft_face->underline_position * y_scale / 64.;
396     double underline_thickness = ft_face->underline_thickness * y_scale / 64.;
397 
398     if (underline_position == 0.) {
399         LOG_DBG("estimating underline position and thickness");
400         underline_thickness = fabs(descent / 5.);
401 
402         //underline_position = descent / 2.;           /* Alacritty's algorithm */
403         underline_position = -2 * underline_thickness; /* My own */
404     }
405 
406     /*
407      * Position refers to the line's center, thus we need to take the
408      * thickness into account to determine the line top.
409      *
410      * Since a *negative* number means the position is *under* the
411      * baseline, we need to *add* half the width to adjust the
412      * position "upwards".
413      *
414      * When rounding the thickness, take care not go below 1.0 as that
415      * would make it invisible.
416      */
417     font->underline.position = trunc(underline_position + underline_thickness / 2.);
418     font->underline.thickness = round(max(1., underline_thickness));
419 
420     LOG_DBG("underline: pos=%f, thick=%f -> pos=%f, pos=%d, thick=%d",
421             underline_position, underline_thickness,
422             underline_position + underline_thickness / 2.,
423             font->underline.position, font->underline.thickness);
424 
425     double strikeout_position = 0., strikeout_thickness = 0.;
426     TT_OS2 *os2 = FT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
427     if (os2 != NULL) {
428         strikeout_position = os2->yStrikeoutPosition * y_scale / 64.;
429         strikeout_thickness = os2->yStrikeoutSize * y_scale / 64.;
430     }
431 
432     if (strikeout_position == 0.) {
433         LOG_DBG("estimating strikeout position and thickness");
434         strikeout_thickness = underline_thickness;
435 
436         //strikeout_position = height / 2. + descent;                     /* Alacritty's algorithm */
437         strikeout_position = 3. * ascent / 8. - underline_thickness / 2.; /* xterm's algorithm */
438     }
439 
440     font->strikeout.position = trunc(strikeout_position + strikeout_thickness / 2.);
441     font->strikeout.thickness = round(max(1., strikeout_thickness));
442 
443     LOG_DBG("strikeout: pos=%f, thick=%f -> pos=%f, pos=%d, thick=%d",
444             strikeout_position, strikeout_thickness,
445             strikeout_position + strikeout_thickness / 2.,
446             font->strikeout.position, font->strikeout.thickness);
447 }
448 
449 static FcPattern *
base_pattern_from_name(const char * name,FcFontSet ** set)450 base_pattern_from_name(const char *name, FcFontSet **set)
451 {
452     /* Fontconfig fails to parse floating point values unless locale
453      * is e.g C, or en_US.UTF-8 */
454     assert(strcmp(setlocale(LC_NUMERIC, NULL), "C") == 0);
455     FcPattern *pattern = FcNameParse((const unsigned char *)name);
456 
457     if (pattern == NULL) {
458         LOG_ERR("%s: failed to lookup font", name);
459         return NULL;
460     }
461 
462     if (!FcConfigSubstitute(NULL, pattern, FcMatchPattern)) {
463         LOG_ERR("%s: failed to do config substitution", name);
464         FcPatternDestroy(pattern);
465         return NULL;
466     }
467 
468     FcDefaultSubstitute(pattern);
469 
470     FcResult result;
471     *set = FcFontSort(NULL, pattern, FcTrue, NULL, &result);
472     if (result != FcResultMatch) {
473         LOG_ERR("%s: failed to match font", name);
474         FcPatternDestroy(pattern);
475         return NULL;
476     }
477 
478     return pattern;
479 }
480 
481 static FcPattern *
pattern_from_font_set(FcPattern * base_pattern,FcFontSet * set,size_t idx)482 pattern_from_font_set(FcPattern *base_pattern, FcFontSet *set, size_t idx)
483 {
484     FcPattern *pattern = FcFontRenderPrepare(NULL, base_pattern, set->fonts[idx]);
485     if (pattern == NULL) {
486         LOG_ERR("failed to prepare 'final' pattern");
487         return NULL;
488     }
489 
490     return pattern;
491 }
492 
493 static bool
instantiate_pattern(FcPattern * pattern,double req_pt_size,double req_px_size,struct instance * font)494 instantiate_pattern(FcPattern *pattern, double req_pt_size, double req_px_size,
495                     struct instance *font)
496 {
497     FcChar8 *face_file = NULL;
498     if (FcPatternGetString(pattern, FC_FT_FACE, 0, &face_file) != FcResultMatch &&
499         FcPatternGetString(pattern, FC_FILE, 0, &face_file) != FcResultMatch)
500     {
501         LOG_ERR("no face file path in pattern");
502         return false;
503     }
504 
505     double dpi;
506     if (FcPatternGetDouble(pattern, FC_DPI, 0, &dpi) != FcResultMatch)
507         dpi = 75;
508 
509     double size;
510     if (FcPatternGetDouble(pattern, FC_SIZE, 0, &size) != FcResultMatch)
511         LOG_WARN("%s: failed to get size", face_file);
512 
513     double pixel_size;
514     if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &pixel_size) != FcResultMatch) {
515         LOG_ERR("%s: failed to get pixel size", face_file);
516         return false;
517     }
518 
519     int face_index;
520     if (FcPatternGetInteger(pattern, FC_INDEX, 0, &face_index) != FcResultMatch) {
521         LOG_WARN("%s: failed to get face index", face_file);
522         face_index = 0;
523     }
524 
525     mtx_lock(&ft_lock);
526     FT_Face ft_face;
527     FT_Error ft_err = FT_New_Face(ft_lib, (const char *)face_file, face_index, &ft_face);
528     mtx_unlock(&ft_lock);
529     if (ft_err != 0) {
530         LOG_ERR("%s: failed to create FreeType face; %s",
531                 face_file, ft_error_string(ft_err));
532         return false;
533     }
534 
535     if ((ft_err = FT_Set_Pixel_Sizes(ft_face, 0, round(pixel_size))) != 0) {
536         LOG_ERR("%s: failed to set character size: %s",
537                 face_file, ft_error_string(ft_err));
538         goto err_done_face;
539     }
540 
541     FcBool scalable;
542     if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &scalable) != FcResultMatch)
543         scalable = FcTrue;
544 
545     FcBool outline;
546     if (FcPatternGetBool(pattern, FC_OUTLINE, 0, &outline) != FcResultMatch)
547         outline = FcTrue;
548 
549     FcBool is_color;
550     if (FcPatternGetBool(pattern, FC_COLOR, 0, &is_color) != FcResultMatch)
551         is_color = FcFalse;
552 
553     double pixel_fixup = 1.;
554     bool fixup_estimated = false;
555     if (FcPatternGetDouble(pattern, "pixelsizefixupfactor", 0, &pixel_fixup) != FcResultMatch) {
556         /*
557          * Force a fixup factor on scalable bitmap fonts (typically
558          * emoji fonts). The fixup factor is
559          *   requested-pixel-size / actual-pixels-size
560          */
561         if (scalable && !outline) {
562             if (req_px_size < 0.)
563                 req_px_size = req_pt_size * dpi / 72.;
564 
565             pixel_fixup = req_px_size / ft_face->size->metrics.y_ppem;
566             fixup_estimated = true;
567             LOG_DBG("estimated pixel fixup factor to %f (from pixel size: %f)",
568                     pixel_fixup, req_px_size);
569         } else
570             pixel_fixup = 1.;
571     }
572 
573 #if 0
574     LOG_DBG("FIXED SIZES: %d", ft_face->num_fixed_sizes);
575     for (int i = 0; i < ft_face->num_fixed_sizes; i++)
576         LOG_DBG("  #%d: height=%d, y_ppem=%f", i, ft_face->available_sizes[i].height, ft_face->available_sizes[i].y_ppem / 64.);
577 #endif
578 
579     FcBool fc_hinting;
580     if (FcPatternGetBool(pattern, FC_HINTING,0,  &fc_hinting) != FcResultMatch)
581         fc_hinting = FcTrue;
582 
583     FcBool fc_antialias;
584     if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch)
585         fc_antialias = FcTrue;
586 
587     int fc_hintstyle;
588     if (FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &fc_hintstyle) != FcResultMatch)
589         fc_hintstyle = FC_HINT_SLIGHT;
590 
591     int fc_rgba;
592     if (FcPatternGetInteger(pattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch)
593         fc_rgba = FC_RGBA_UNKNOWN;
594 
595     int load_flags = FT_LOAD_DEFAULT;
596     int load_target = FT_LOAD_TARGET_NORMAL;
597 
598     if (!fc_antialias) {
599         if (!fc_hinting || fc_hintstyle == FC_HINT_NONE)
600             load_flags |= FT_LOAD_NO_HINTING;
601         else
602             load_target = FT_LOAD_TARGET_MONO;
603 
604         load_flags |= FT_LOAD_MONOCHROME;
605     }
606 
607     else {
608         if (!fc_hinting || fc_hintstyle == FC_HINT_NONE)
609             load_flags |= FT_LOAD_NO_HINTING;
610 
611         else if (fc_hintstyle == FC_HINT_SLIGHT)
612             load_target = FT_LOAD_TARGET_LIGHT;
613 
614         else if (fc_hintstyle == FC_HINT_MEDIUM)
615             ;
616 
617         else if (fc_rgba == FC_RGBA_RGB || fc_rgba == FC_RGBA_BGR)
618             load_target = FT_LOAD_TARGET_LCD;
619 
620         else if (fc_rgba == FC_RGBA_VRGB || fc_rgba == FC_RGBA_VBGR)
621             load_target = FT_LOAD_TARGET_LCD_V;
622     }
623 
624     FcBool fc_embeddedbitmap;
625     if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &fc_embeddedbitmap) != FcResultMatch)
626         fc_embeddedbitmap = FcTrue;
627 
628     if (!fc_embeddedbitmap && outline)
629         load_flags |= FT_LOAD_NO_BITMAP;
630 
631     FcBool fc_autohint;
632     if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &fc_autohint) != FcResultMatch)
633         fc_autohint = FcFalse;
634 
635     if (fc_autohint)
636         load_flags |= FT_LOAD_FORCE_AUTOHINT;
637 
638     int render_flags_normal, render_flags_subpixel;
639     if (!fc_antialias)
640         render_flags_normal = render_flags_subpixel = FT_RENDER_MODE_MONO;
641 
642     else {
643         if (fc_rgba == FC_RGBA_RGB || fc_rgba == FC_RGBA_BGR) {
644             render_flags_subpixel = FT_RENDER_MODE_LCD;
645             render_flags_normal = FT_RENDER_MODE_NORMAL;
646         }
647 
648         else if (fc_rgba == FC_RGBA_VRGB || fc_rgba == FC_RGBA_VBGR) {
649             render_flags_subpixel = FT_RENDER_MODE_LCD_V;
650             render_flags_normal = FT_RENDER_MODE_NORMAL;
651         }
652 
653         else
654             render_flags_normal = render_flags_subpixel = FT_RENDER_MODE_NORMAL;
655     }
656 
657     int fc_lcdfilter;
658     if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &fc_lcdfilter) != FcResultMatch)
659         fc_lcdfilter = FC_LCD_DEFAULT;
660 
661     switch (fc_lcdfilter) {
662     case FC_LCD_NONE:    font->lcd_filter = FT_LCD_FILTER_NONE; break;
663     case FC_LCD_DEFAULT: font->lcd_filter = FT_LCD_FILTER_DEFAULT; break;
664     case FC_LCD_LIGHT:   font->lcd_filter = FT_LCD_FILTER_LIGHT; break;
665     case FC_LCD_LEGACY:  font->lcd_filter = FT_LCD_FILTER_LEGACY; break;
666     }
667 
668     FcBool fc_embolden;
669     if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fc_embolden) != FcResultMatch)
670         fc_embolden = FcFalse;
671 
672     FcMatrix *fc_matrix;
673     if (FcPatternGetMatrix(pattern, FC_MATRIX, 0, &fc_matrix) == FcResultMatch) {
674         FT_Matrix m = {
675             .xx = fc_matrix->xx * 0x10000,
676             .xy = fc_matrix->xy * 0x10000,
677             .yx = fc_matrix->yx * 0x10000,
678             .yy = fc_matrix->yy * 0x10000,
679         };
680         FT_Set_Transform(ft_face, &m, NULL);
681     }
682 
683     font->path = strdup((char *)face_file);
684     if (font->path == NULL)
685         goto err_done_face;
686 
687     font->face = ft_face;
688     font->load_flags = load_target | load_flags | FT_LOAD_COLOR;
689     font->antialias = fc_antialias;
690     font->embolden = fc_embolden;
691     font->is_color = is_color;
692     font->render_flags_normal = render_flags_normal;
693     font->render_flags_subpixel = render_flags_subpixel;
694     font->pixel_size_fixup = pixel_fixup;
695     font->pixel_fixup_estimated = fixup_estimated;
696     font->bgr = fc_rgba == FC_RGBA_BGR || fc_rgba == FC_RGBA_VBGR;
697 
698     /* For logging: e.g. "+ss01 -dlig" */
699     char features[256] = {0};
700 
701 #if defined(FCFT_HAVE_HARFBUZZ)
702     font->hb_font = hb_ft_font_create_referenced(ft_face);
703     if (font->hb_font == NULL) {
704         LOG_ERR("%s: failed to instantiate harfbuzz font", face_file);
705         goto err_free_path;
706     }
707 
708     font->hb_buf = hb_buffer_create();
709     if (font->hb_buf == NULL) {
710         LOG_ERR("%s: failed to instantiate harfbuzz buffer", face_file);
711         goto err_hb_font_destroy;
712     }
713 
714     for (font->hb_feats_count = 0; font->hb_feats_count < ALEN(font->hb_feats); ) {
715         FcChar8 *fc_feat;
716         if (FcPatternGetString(
717                 pattern, FC_FONT_FEATURES,
718                 font->hb_feats_count, &fc_feat) != FcResultMatch)
719         {
720             break;
721         }
722 
723         hb_feature_t *feat = &font->hb_feats[font->hb_feats_count];
724 
725         if (hb_feature_from_string((const char *)fc_feat, -1, feat)) {
726             const char tag[4] = {
727                 (feat->tag >> 24) & 0xff,
728                 (feat->tag >> 16) & 0xff,
729                 (feat->tag >> 8) & 0xff,
730                 (feat->tag >> 0) & 0xff,
731             };
732 
733             size_t ofs = font->hb_feats_count * 6;
734             snprintf(&features[ofs], sizeof(features) - ofs,
735                      " %c%.4s", feat->value ? '+' : '-', tag);
736 
737             LOG_DBG("feature: %.4s=%s", tag, feat->value ? "on" : "off");
738             font->hb_feats_count++;
739         }
740     }
741 #endif
742 
743     double max_x_advance = ft_face->size->metrics.max_advance / 64.;
744     double max_y_advance = ft_face->size->metrics.height / 64.;
745     double height = ft_face->size->metrics.height / 64.;
746     double descent = ft_face->size->metrics.descender / 64.;
747     double ascent = ft_face->size->metrics.ascender / 64.;
748 
749     font->metrics.height = ceil(height * font->pixel_size_fixup);
750     font->metrics.descent = ceil(-descent * font->pixel_size_fixup);
751     font->metrics.ascent = ceil(ascent * font->pixel_size_fixup);
752     font->metrics.max_advance.x = ceil(max_x_advance * font->pixel_size_fixup);
753     font->metrics.max_advance.y = ceil(max_y_advance * font->pixel_size_fixup);
754     font->metrics.antialias = fc_antialias;
755     font->metrics.subpixel = !fc_antialias
756         ? FCFT_SUBPIXEL_NONE
757         : (fc_rgba == FC_RGBA_RGB ? FCFT_SUBPIXEL_HORIZONTAL_RGB :
758            fc_rgba == FC_RGBA_BGR ? FCFT_SUBPIXEL_HORIZONTAL_BGR :
759            fc_rgba == FC_RGBA_VRGB ? FCFT_SUBPIXEL_VERTICAL_RGB :
760            fc_rgba == FC_RGBA_VBGR ? FCFT_SUBPIXEL_VERTICAL_BGR :
761            FCFT_SUBPIXEL_NONE);
762 
763     /*
764      * Some fonts (Noto Sans Mono, for example) provides bad
765      * max_advance values for grid-based applications, like terminal
766      * emulators.
767      *
768      * For this reason we also provide the width of a regular space
769      * character, to help these applications determine the cell size.
770      */
771     FT_UInt idx = FT_Get_Char_Index(font->face, L' ');
772     if (idx != 0 &&
773         (ft_err = FT_Load_Glyph(font->face, idx, font->load_flags | FT_LOAD_BITMAP_METRICS_ONLY)) == 0)
774     {
775         if (fc_embolden && font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
776             FT_GlyphSlot_Embolden(font->face->glyph);
777 
778         font->metrics.space_advance.x = ceil(
779             font->face->glyph->advance.x / 64. *
780             (font->pixel_fixup_estimated ? font->pixel_size_fixup : 1.));
781         font->metrics.space_advance.y = ceil(
782             font->face->glyph->advance.y / 64. *
783             (font->pixel_fixup_estimated ? font->pixel_size_fixup : 1.));
784     } else {
785         font->metrics.space_advance.x = -1;
786         font->metrics.space_advance.y = -1;
787     }
788 
789     underline_strikeout_metrics(ft_face, &font->metrics);
790 
791 #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
792     LOG_DBG("%s: size=%.2fpt/%.2fpx, dpi=%.2f, fixup-factor: %.4f, "
793             "line-height: %dpx, ascent: %dpx, descent: %dpx, "
794             "x-advance (max/space): %d/%dpx, features:%s",
795             font->path, size, pixel_size, dpi, font->pixel_size_fixup,
796             font->metrics.height, font->metrics.ascent, font->metrics.descent,
797             font->metrics.max_advance.x, font->metrics.space_advance.x,
798             features);
799 #else
800     LOG_INFO("%s: size=%.2fpt/%dpx, dpi=%.2f%s",
801              font->path, size, (int)round(pixel_size), dpi, features);
802 #endif
803 
804     return true;
805 
806 #if defined(FCFT_HAVE_HARFBUZZ)
807 err_hb_font_destroy:
808     hb_font_destroy(font->hb_font);
809 err_free_path:
810     free(font->path);
811 #endif
812 
813 err_done_face:
814     mtx_lock(&ft_lock);
815     FT_Done_Face(ft_face);
816     mtx_unlock(&ft_lock);
817     return false;
818 }
819 
820 static uint64_t
sdbm_hash(const char * s)821 sdbm_hash(const char *s)
822 {
823     uint64_t hash = 0;
824 
825     for (; *s != '\0'; s++) {
826         int c = *s;
827         hash = c + (hash << 6) + (hash << 16) - hash;
828     }
829 
830     return hash;
831 }
832 
833 static uint64_t
font_hash(size_t count,const char * names[static count],const char * attributes)834 font_hash(size_t count, const char *names[static count], const char *attributes)
835 {
836     uint64_t hash = 0;
837     for (size_t i = 0; i < count; i++)
838         hash ^= sdbm_hash(names[i]);
839 
840     if (attributes != NULL)
841         hash ^= sdbm_hash(attributes);
842 
843     return hash;
844 }
845 
846 FCFT_EXPORT struct fcft_font *
fcft_from_name(size_t count,const char * names[static count],const char * attributes)847 fcft_from_name(size_t count, const char *names[static count],
848                const char *attributes)
849 {
850     log_version_information();
851     if (ft_lib_err != FT_Err_Ok) {
852         LOG_ERR("failed to initialize FreeType: %s", ft_error_string(ft_lib_err));
853         return NULL;
854     }
855 
856     if (count == 0)
857         return NULL;
858 
859     uint64_t hash = font_hash(count, names, attributes);
860     struct fcft_font_cache_entry *cache_entry = NULL;
861 
862     mtx_lock(&font_cache_lock);
863     tll_foreach(font_cache, it) {
864         if (it->item.hash != hash)
865             continue;
866 
867         struct fcft_font_cache_entry *e = &it->item;
868 
869         if (e->font != (void *)(uintptr_t)-1) {
870             /* Font has already been fully initialized */
871 
872             if (e->font != NULL) {
873                 mtx_lock(&e->font->lock);
874                 e->font->ref_counter++;
875                 mtx_unlock(&e->font->lock);
876             }
877 
878             mtx_unlock(&font_cache_lock);
879             return e->font != NULL ? &e->font->public : NULL;
880         }
881 
882         /*
883          * Cache entry is only a reservation - font hasn't yet
884          * been initialized.
885          */
886 
887         /* Let instantiating thread know (yet) another thread
888          * wants a reference to it */
889         e->waiters++;
890 
891         while (e->font == (void *)(uintptr_t)-1)
892             cnd_wait(&e->cond, &font_cache_lock);
893         mtx_unlock(&font_cache_lock);
894         return e->font == NULL ? NULL : &e->font->public;
895     }
896 
897     /* Pre-allocate entry */
898     tll_push_back(
899         font_cache,
900         ((struct fcft_font_cache_entry){.hash = hash, .font = (void *)(uintptr_t)-1}));
901 
902     cache_entry = &tll_back(font_cache);
903 
904     if (cnd_init(&cache_entry->cond) != thrd_success) {
905         LOG_ERR("%s: failed to instantiate font cache condition variable", names[0]);
906         tll_pop_back(font_cache);
907         mtx_unlock(&font_cache_lock);
908         return NULL;
909     }
910     mtx_unlock(&font_cache_lock);
911 
912     struct font_priv *font = NULL;
913 
914     bool have_attrs = attributes != NULL && strlen(attributes) > 0;
915     size_t attr_len = have_attrs ? strlen(attributes) + 1 : 0;
916 
917     tll(struct fallback) fc_fallbacks = tll_init();
918 
919     bool first = true;
920     for (size_t i = 0; i < count; i++) {
921         const char *base_name = names[i];
922 
923         char name[strlen(base_name) + attr_len + 1];
924         strcpy(name, base_name);
925         if (have_attrs) {
926             strcat(name, ":");
927             strcat(name, attributes);
928         }
929 
930         FcFontSet *set;
931         FcPattern *base_pattern = base_pattern_from_name(name, &set);
932         if (base_pattern == NULL)
933             break;
934 
935         FcPattern *pattern = pattern_from_font_set(base_pattern, set, 0);
936         if (pattern == NULL)
937             break;
938 
939         FcCharSet *charset;
940         if (FcPatternGetCharSet(base_pattern, FC_CHARSET, 0, &charset) != FcResultMatch &&
941             FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charset) != FcResultMatch)
942         {
943             LOG_ERR("%s: failed to get charset", name);
944             FcPatternDestroy(pattern);
945             FcPatternDestroy(base_pattern);
946             FcFontSetDestroy(set);
947             break;
948         }
949 
950         FcLangSet *langset;
951         if (FcPatternGetLangSet(pattern, FC_LANG, 0, &langset) != FcResultMatch)
952             langset = NULL;
953 
954         double req_px_size = -1., req_pt_size = -1.;
955         FcPatternGetDouble(base_pattern, FC_PIXEL_SIZE, 0, &req_px_size);
956         FcPatternGetDouble(base_pattern, FC_SIZE, 0, &req_pt_size);
957 
958         if (first) {
959             first = false;
960 
961             bool lock_failed = true;
962             bool glyph_cache_lock_failed = true;
963             bool grapheme_cache_lock_failed = true;
964             bool pattern_failed = true;
965 
966             mtx_t lock;
967             if (mtx_init(&lock, mtx_plain) != thrd_success)
968                 LOG_WARN("%s: failed to instantiate mutex", name);
969             else
970                 lock_failed = false;
971 
972             pthread_rwlock_t glyph_cache_lock;
973             if (pthread_rwlock_init(&glyph_cache_lock, NULL) != 0)
974                 LOG_WARN("%s: failed to instantiate glyph cache rwlock", name);
975             else
976                 glyph_cache_lock_failed = false;
977 
978             struct instance *primary = malloc(sizeof(*primary));
979             if (primary == NULL ||
980                 !instantiate_pattern(pattern, req_pt_size, req_px_size, primary))
981             {
982                 ;
983             } else
984                 pattern_failed = false;
985 
986             font = calloc(1, sizeof(*font));
987             struct glyph_priv **glyph_cache_table = calloc(
988                 glyph_cache_initial_size, sizeof(glyph_cache_table[0]));
989 
990 #if defined(FCFT_HAVE_HARFBUZZ)
991             pthread_rwlock_t grapheme_cache_lock;
992             if (pthread_rwlock_init(&grapheme_cache_lock, NULL) != 0)
993                 LOG_WARN("%s: failed to instantiate grapheme cache rwlock", name);
994             else
995                 grapheme_cache_lock_failed = false;
996 
997             struct grapheme_priv **grapheme_cache_table = calloc(
998                 grapheme_cache_initial_size, sizeof(grapheme_cache_table[0]));
999 #else
1000             struct grapheme_priv **grapheme_cache_table = NULL;
1001             grapheme_cache_lock_failed = false;
1002 #endif
1003 
1004             /* Handle failure(s) */
1005             if (lock_failed || glyph_cache_lock_failed ||
1006                 grapheme_cache_lock_failed || pattern_failed ||
1007                 font == NULL || glyph_cache_table == NULL
1008 #if defined(FCFT_HAVE_HARFBUZZ)
1009                 || grapheme_cache_table == NULL
1010 #endif
1011                 )
1012             {
1013                 if (!lock_failed)
1014                     mtx_destroy(&lock);
1015                 if (!glyph_cache_lock_failed)
1016                     pthread_rwlock_destroy(&glyph_cache_lock);
1017 #if defined(FCFT_HAVE_HARFBUZZ)
1018                 if (!grapheme_cache_lock_failed)
1019                     pthread_rwlock_destroy(&grapheme_cache_lock);
1020 #endif
1021                 if (!pattern_failed)
1022                     free(primary);
1023                 free(font);
1024                 free(glyph_cache_table);
1025                 free(grapheme_cache_table);
1026                 if (langset != NULL)
1027                     FcLangSetDestroy(langset);
1028                 FcCharSetDestroy(charset);
1029                 FcPatternDestroy(pattern);
1030                 FcPatternDestroy(base_pattern);
1031                 FcFontSetDestroy(set);
1032                 break;
1033             }
1034 
1035             font->ref_counter = 1;
1036             font->lock = lock;
1037             font->glyph_cache_lock = glyph_cache_lock;
1038             font->glyph_cache.size = glyph_cache_initial_size;
1039             font->glyph_cache.count = 0;
1040             font->glyph_cache.table = glyph_cache_table;
1041             font->emoji_presentation = FCFT_EMOJI_PRESENTATION_DEFAULT;
1042             font->public = primary->metrics;
1043 
1044 #if defined(FCFT_HAVE_HARFBUZZ)
1045             font->grapheme_cache_lock = grapheme_cache_lock;
1046             font->grapheme_cache.size = grapheme_cache_initial_size;
1047             font->grapheme_cache.count = 0;
1048             font->grapheme_cache.table = grapheme_cache_table;
1049 #endif
1050 
1051             tll_push_back(font->fallbacks, ((struct fallback){
1052                         .pattern = pattern,
1053                         .charset = FcCharSetCopy(charset),
1054                         .langset = langset != NULL ? FcLangSetCopy(langset) : NULL,
1055                         .font = primary,
1056                         .req_px_size = req_px_size,
1057                         .req_pt_size = req_pt_size}));
1058 
1059             for (size_t i = 1; i < set->nfont; i++) {
1060                 FcPattern *fallback_pattern = pattern_from_font_set(base_pattern, set, i);
1061                 if (fallback_pattern == NULL)
1062                     continue;
1063 
1064                 FcCharSet *fallback_charset;
1065                 if (FcPatternGetCharSet(base_pattern, FC_CHARSET, 0, &fallback_charset) != FcResultMatch &&
1066                     FcPatternGetCharSet(fallback_pattern, FC_CHARSET, 0, &fallback_charset) != FcResultMatch)
1067                 {
1068                     LOG_ERR("%s: failed to get charset", name);
1069                     FcPatternDestroy(fallback_pattern);
1070                     continue;
1071                 }
1072 
1073                 FcLangSet *fallback_langset;
1074                 if (FcPatternGetLangSet(fallback_pattern, FC_LANG, 0, &fallback_langset) != FcResultMatch)
1075                     fallback_langset = NULL;
1076 
1077                 FcPatternGetDouble(base_pattern, FC_PIXEL_SIZE, 0, &req_px_size);
1078                 FcPatternGetDouble(base_pattern, FC_SIZE, 0, &req_pt_size);
1079 
1080                 tll_push_back(fc_fallbacks, ((struct fallback){
1081                             .pattern = fallback_pattern,
1082                             .charset = FcCharSetCopy(fallback_charset),
1083                             .langset = fallback_langset != NULL ? FcLangSetCopy(fallback_langset) : NULL,
1084                             .req_px_size = req_px_size,
1085                             .req_pt_size = req_pt_size}));
1086             }
1087 
1088         } else {
1089             assert(font != NULL);
1090             tll_push_back(font->fallbacks, ((struct fallback){
1091                         .pattern = pattern,
1092                         .charset = FcCharSetCopy(charset),
1093                         .langset = langset != NULL ? FcLangSetCopy(langset) : NULL,
1094                         .req_px_size = req_px_size,
1095                         .req_pt_size = req_pt_size}));
1096         }
1097 
1098         FcFontSetDestroy(set);
1099         FcPatternDestroy(base_pattern);
1100     }
1101 
1102     /* Append fontconfig-based fallbacks at the end of our fallback list */
1103     if (font != NULL) {
1104         tll_foreach(fc_fallbacks, it)
1105             tll_push_back(font->fallbacks, it->item);
1106         tll_free(fc_fallbacks);
1107     }
1108 
1109     mtx_lock(&font_cache_lock);
1110     cache_entry->font = font;
1111     if (cache_entry->font != NULL)
1112         cache_entry->font->ref_counter += cache_entry->waiters;
1113     cnd_broadcast(&cache_entry->cond);
1114     mtx_unlock(&font_cache_lock);
1115 
1116     assert(font == NULL || (void *)&font->public == (void *)font);
1117     return font != NULL ? &font->public : NULL;
1118 }
1119 
1120 FCFT_EXPORT struct fcft_font *
fcft_clone(const struct fcft_font * _font)1121 fcft_clone(const struct fcft_font *_font)
1122 {
1123     if (_font == NULL)
1124         return NULL;
1125 
1126     struct font_priv *font = (struct font_priv *)_font;
1127 
1128     mtx_lock(&font->lock);
1129     {
1130         assert(font->ref_counter >= 1);
1131         font->ref_counter++;
1132     }
1133     mtx_unlock(&font->lock);
1134 
1135     return &font->public;
1136 }
1137 
1138 FCFT_EXPORT struct fcft_font *
fcft_size_adjust(const struct fcft_font * _font,double amount)1139 fcft_size_adjust(const struct fcft_font *_font, double amount)
1140 {
1141     struct font_priv *new = calloc(1, sizeof(*new));
1142     if (new == NULL)
1143         return NULL;
1144 
1145     struct glyph_priv **cache_table = calloc(glyph_cache_initial_size, sizeof(cache_table[0]));
1146     if (cache_table == NULL) {
1147         free(new);
1148         return NULL;
1149     }
1150 
1151     mtx_init(&new->lock, mtx_plain);
1152     pthread_rwlock_init(&new->glyph_cache_lock, NULL);
1153 
1154     new->ref_counter = 1;
1155     new->glyph_cache.size = glyph_cache_initial_size;
1156     new->glyph_cache.count = 0;
1157     new->glyph_cache.table = cache_table;
1158 
1159     struct font_priv *font = (struct font_priv *)_font;
1160     tll_foreach(font->fallbacks, it) {
1161         FcPattern *pat = it->item.pattern;
1162         double size = it->item.req_pt_size;
1163 
1164         if (size < 0. &&
1165             FcPatternGetDouble(pat, FC_SIZE, 0, &size) != FcResultMatch)
1166         {
1167             LOG_WARN("failed to get size");
1168             goto err;
1169         }
1170 
1171         size += amount;
1172         if (size < 1.)
1173             goto err;
1174 
1175         FcPattern *new_base = FcPatternDuplicate(pat);
1176         FcPatternRemove(new_base, FC_SIZE, 0);
1177         FcPatternRemove(new_base, FC_PIXEL_SIZE, 0);
1178         FcPatternRemove(new_base, FC_WEIGHT, 0);
1179         FcPatternRemove(new_base, FC_WIDTH, 0);
1180         FcPatternRemove(new_base, FC_FILE, 0);
1181         FcPatternRemove(new_base, FC_FT_FACE, 0);
1182 
1183         FcPatternAddDouble(new_base, FC_SIZE, size);
1184 
1185         if (!FcConfigSubstitute(NULL, new_base, FcMatchPattern)) {
1186             FcPatternDestroy(new_base);
1187             goto err;
1188         }
1189         FcDefaultSubstitute(new_base);
1190 
1191         FcResult res;
1192         FcPattern *new_pat = FcFontMatch(NULL, new_base, &res);
1193 
1194         tll_push_back(new->fallbacks, ((struct fallback){
1195                     .pattern = new_pat,
1196                     .charset = FcCharSetCopy(it->item.charset),
1197                     .langset = it->item.langset != NULL ? FcLangSetCopy(it->item.langset) : NULL,
1198                     .req_px_size = -1.,
1199                     .req_pt_size = size}));
1200 
1201         FcPatternDestroy(new_base);
1202     }
1203 
1204     assert(tll_length(new->fallbacks) > 0);
1205     struct fallback *primary = &tll_front(new->fallbacks);
1206 
1207     struct instance *inst = malloc(sizeof(*inst));
1208     if (inst == NULL ||
1209         !instantiate_pattern(
1210             primary->pattern, primary->req_pt_size, primary->req_px_size, inst))
1211     {
1212         free(inst);
1213         goto err;
1214     }
1215     primary->font = inst;
1216 
1217     new->public = inst->metrics;
1218     return &new->public;
1219 
1220 err:
1221     fcft_destroy(&new->public);
1222     return NULL;
1223 
1224 }
1225 
1226 static bool
glyph_for_index(const struct instance * inst,uint32_t index,enum fcft_subpixel subpixel,struct glyph_priv * glyph)1227 glyph_for_index(const struct instance *inst, uint32_t index,
1228                 enum fcft_subpixel subpixel, struct glyph_priv *glyph)
1229 {
1230     glyph->valid = false;
1231     glyph->subpixel = subpixel;
1232 
1233     pixman_image_t *pix = NULL;
1234     uint8_t *data = NULL;
1235 
1236     FT_Error err;
1237     if ((err = FT_Load_Glyph(inst->face, index, inst->load_flags)) != 0) {
1238         LOG_ERR("%s: failed to load glyph #%d: %s",
1239                 inst->path, index, ft_error_string(err));
1240         goto err;
1241     }
1242 
1243     if (inst->embolden && inst->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
1244         FT_GlyphSlot_Embolden(inst->face->glyph);
1245 
1246     int render_flags;
1247     bool bgr;
1248 
1249     if (inst->antialias) {
1250         switch (subpixel) {
1251         case FCFT_SUBPIXEL_NONE:
1252             render_flags = inst->render_flags_normal;
1253             bgr = false;
1254             break;
1255 
1256         case FCFT_SUBPIXEL_HORIZONTAL_RGB:
1257         case FCFT_SUBPIXEL_HORIZONTAL_BGR:
1258             render_flags = FT_RENDER_MODE_LCD;
1259             bgr = subpixel == FCFT_SUBPIXEL_HORIZONTAL_BGR;
1260             break;
1261 
1262         case FCFT_SUBPIXEL_VERTICAL_RGB:
1263         case FCFT_SUBPIXEL_VERTICAL_BGR:
1264             render_flags = FT_RENDER_MODE_LCD_V;
1265             bgr = subpixel == FCFT_SUBPIXEL_VERTICAL_BGR;
1266             break;
1267 
1268         case FCFT_SUBPIXEL_DEFAULT:
1269         default:
1270             render_flags = inst->render_flags_subpixel;
1271             bgr = inst->bgr;
1272             break;
1273         }
1274     } else {
1275         render_flags = inst->render_flags_normal;
1276         bgr = false;
1277     }
1278 
1279     /*
1280      * LCD filter is per library instance, hence we need to re-set it
1281      * every time. But only if we need it, and only if we _can_, to
1282      * avoid locking a global mutex. See init().
1283      */
1284     bool unlock_ft_lock = false;
1285     if (can_set_lcd_filter &&
1286         ((render_flags & FT_RENDER_MODE_LCD) ||
1287          (render_flags & FT_RENDER_MODE_LCD_V)))
1288     {
1289         mtx_lock(&ft_lock);
1290         unlock_ft_lock = true;
1291 
1292         FT_Error err = FT_Library_SetLcdFilter(ft_lib, inst->lcd_filter);
1293 
1294         if (err != 0) {
1295             LOG_ERR("failed to set LCD filter: %s", ft_error_string(err));
1296             mtx_unlock(&ft_lock);
1297             goto err;
1298         }
1299     }
1300 
1301     if (inst->face->glyph->format != FT_GLYPH_FORMAT_BITMAP) {
1302         if ((err = FT_Render_Glyph(inst->face->glyph, render_flags)) != 0) {
1303             LOG_ERR("%s: failed to render glyph: %s",
1304                     inst->path, ft_error_string(err));
1305             if (unlock_ft_lock)
1306                 mtx_unlock(&ft_lock);
1307             goto err;
1308         }
1309     }
1310 
1311     if (unlock_ft_lock)
1312         mtx_unlock(&ft_lock);
1313 
1314     if (inst->face->glyph->format != FT_GLYPH_FORMAT_BITMAP) {
1315         LOG_ERR("%s: rasterized glyph is not a bitmap", inst->path);
1316         goto err;
1317     }
1318 
1319     const FT_Bitmap *bitmap = &inst->face->glyph->bitmap;
1320     pixman_format_code_t pix_format;
1321     int width;
1322     int rows;
1323 
1324     switch (bitmap->pixel_mode) {
1325     case FT_PIXEL_MODE_MONO:
1326         pix_format = PIXMAN_a1;
1327         width = bitmap->width;
1328         rows = bitmap->rows;
1329         break;
1330 
1331     case FT_PIXEL_MODE_GRAY:
1332         pix_format = PIXMAN_a8;
1333         width = bitmap->width;
1334         rows = bitmap->rows;
1335         break;
1336 
1337     case FT_PIXEL_MODE_LCD:
1338         pix_format = PIXMAN_x8r8g8b8;
1339         width = bitmap->width / 3;
1340         rows = bitmap->rows;
1341         break;
1342 
1343     case FT_PIXEL_MODE_LCD_V:
1344         pix_format = PIXMAN_x8r8g8b8;
1345         width = bitmap->width;
1346         rows = bitmap->rows / 3;
1347         break;
1348 
1349     case FT_PIXEL_MODE_BGRA:
1350         pix_format = PIXMAN_a8r8g8b8;
1351         width = bitmap->width;
1352         rows = bitmap->rows;
1353         break;
1354 
1355     default:
1356         LOG_ERR("unimplemented: FT pixel mode: %d", bitmap->pixel_mode);
1357         goto err;
1358         break;
1359     }
1360 
1361     int stride = stride_for_format_and_width(pix_format, width);
1362     assert(stride >= bitmap->pitch);
1363 
1364     assert(bitmap->buffer != NULL || rows * stride == 0);
1365     data = malloc(rows * stride);
1366     if (data == NULL)
1367         goto err;
1368 
1369     /* Convert FT bitmap to pixman image */
1370     switch (bitmap->pixel_mode) {
1371     case FT_PIXEL_MODE_MONO:  /* PIXMAN_a1 */
1372         /*
1373          * FreeType: left-most pixel is stored in MSB  ABCDEFGH IJKLMNOP
1374          * Pixman: LE: left-most pixel in LSB          HGFEDCBA PONMLKJI
1375          *         BE: left-most pixel in MSB          ABCDEFGH IJKLMNOP
1376          *
1377          * Thus, we need to reverse each byte on little-endian systems.
1378          */
1379         for (size_t r = 0; r < bitmap->rows; r++) {
1380             for (size_t c = 0; c < (bitmap->width + 7) / 8; c++) {
1381                 uint8_t v = bitmap->buffer[r * bitmap->pitch + c];
1382 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1383                 uint8_t reversed = 0;
1384                 for (size_t i = 0; i < min(8, bitmap->width - c * 8); i++)
1385                     reversed |= ((v >> (7 - i)) & 1) << i;
1386 
1387                 data[r * stride + c] = reversed;
1388 #else
1389                 data[r * stride + c] = v;
1390 #endif
1391             }
1392         }
1393         break;
1394 
1395     case FT_PIXEL_MODE_GRAY: /* PIXMAN_a8 */
1396         /*
1397          * One pixel, one byte. No endianness to worry about
1398          */
1399         if (stride == bitmap->pitch) {
1400             if (bitmap->buffer != NULL)
1401                 memcpy(data, bitmap->buffer, rows * stride);
1402         } else {
1403             for (size_t r = 0; r < bitmap->rows; r++) {
1404                 for (size_t c = 0; c < bitmap->width; c++)
1405                     data[r * stride + c] = bitmap->buffer[r * bitmap->pitch + c];
1406             }
1407         }
1408         break;
1409 
1410     case FT_PIXEL_MODE_BGRA: /* PIXMAN_a8r8g8b8 */
1411         /*
1412          * FreeType: blue comes *first* in memory
1413          * Pixman: LE: blue comes *first* in memory
1414          *         BE: alpha comes *first* in memory
1415          *
1416          * Pixman is ARGB *when loaded into a register*, assuming
1417          * machine native 32-bit loads. Thus, it’s in-memory layout
1418          * depends on the host’s endianness.
1419          */
1420         assert(stride == bitmap->pitch);
1421         for (size_t r = 0; r < bitmap->rows; r++) {
1422             for (size_t c = 0; c < bitmap->width * 4; c += 4) {
1423                 unsigned char _b = bitmap->buffer[r * bitmap->pitch + c + 0];
1424                 unsigned char _g = bitmap->buffer[r * bitmap->pitch + c + 1];
1425                 unsigned char _r = bitmap->buffer[r * bitmap->pitch + c + 2];
1426                 unsigned char _a = bitmap->buffer[r * bitmap->pitch + c + 3];
1427 
1428                 uint32_t *p = (uint32_t *)&data[r * stride + c];
1429                 *p = (uint32_t)_a << 24 | _r << 16 | _g << 8 | _b;
1430             }
1431         }
1432         break;
1433 
1434     case FT_PIXEL_MODE_LCD: /* PIXMAN_x8r8g8b8 */
1435         /*
1436          * FreeType: red comes *first* in memory
1437          * Pixman: LE: blue comes *first* in memory
1438          *         BE: x comes *first* in memory
1439          *
1440          * Same as above, except that the FreeType data is now RGBx
1441          * instead of BGRA.
1442          */
1443         for (size_t r = 0; r < bitmap->rows; r++) {
1444             for (size_t c = 0; c < bitmap->width; c += 3) {
1445                 unsigned char _r = bitmap->buffer[r * bitmap->pitch + c + (bgr ? 2 : 0)];
1446                 unsigned char _g = bitmap->buffer[r * bitmap->pitch + c + 1];
1447                 unsigned char _b = bitmap->buffer[r * bitmap->pitch + c + (bgr ? 0 : 2)];
1448 
1449                 uint32_t *p = (uint32_t *)&data[r * stride + 4 * (c / 3)];
1450                 *p = _r << 16 | _g << 8 | _b;
1451             }
1452         }
1453         break;
1454 
1455     case FT_PIXEL_MODE_LCD_V: /* PIXMAN_x8r8g8b8 */
1456         /*
1457          * FreeType: red comes *first* in memory
1458          * Pixman: LE: blue comes *first* in memory
1459          *         BE: x comes *first* in memory
1460          *
1461          * Same as above, except that the FreeType data is now RGBx
1462          * instead of BGRA.
1463          */
1464         for (size_t r = 0; r < bitmap->rows; r += 3) {
1465             for (size_t c = 0; c < bitmap->width; c++) {
1466                 unsigned char _r = bitmap->buffer[(r + (bgr ? 2 : 0)) * bitmap->pitch + c];
1467                 unsigned char _g = bitmap->buffer[(r + 1) * bitmap->pitch + c];
1468                 unsigned char _b = bitmap->buffer[(r + (bgr ? 0 : 2)) * bitmap->pitch + c];
1469 
1470                 uint32_t *p = (uint32_t *)&data[r / 3 * stride + 4 * c];
1471                 *p =  _r << 16 | _g << 8 | _b;
1472             }
1473         }
1474         break;
1475 
1476     default:
1477         abort();
1478         break;
1479     }
1480 
1481     if ((pix = pixman_image_create_bits_no_clear(
1482              pix_format, width, rows, (uint32_t *)data, stride)) == NULL)
1483         goto err;
1484 
1485     pixman_image_set_component_alpha(
1486         pix,
1487         bitmap->pixel_mode == FT_PIXEL_MODE_LCD ||
1488         bitmap->pixel_mode == FT_PIXEL_MODE_LCD_V);
1489 
1490     int x = inst->face->glyph->bitmap_left;
1491     int y = inst->face->glyph->bitmap_top;
1492 
1493     if (inst->pixel_size_fixup == 0.)
1494         x = y = width = rows = 0;
1495 
1496     else if (inst->pixel_size_fixup != 1.) {
1497         struct pixman_f_transform scale;
1498         pixman_f_transform_init_scale(
1499             &scale,
1500             1. / inst->pixel_size_fixup,
1501             1. / inst->pixel_size_fixup);
1502 
1503         struct pixman_transform _scale;
1504         pixman_transform_from_pixman_f_transform(&_scale, &scale);
1505         pixman_image_set_transform(pix, &_scale);
1506 
1507         const enum fcft_scaling_filter filter_to_use = inst->is_color
1508             ? scaling_filter
1509             : FCFT_SCALING_FILTER_BILINEAR;
1510 
1511         switch (filter_to_use) {
1512         case FCFT_SCALING_FILTER_NONE:
1513             break;
1514 
1515         case FCFT_SCALING_FILTER_NEAREST:
1516             pixman_image_set_filter(pix, PIXMAN_FILTER_NEAREST, NULL, 0);
1517             break;
1518 
1519         case FCFT_SCALING_FILTER_BILINEAR:
1520             pixman_image_set_filter(pix, PIXMAN_FILTER_BILINEAR, NULL, 0);
1521             break;
1522 
1523         case FCFT_SCALING_FILTER_CUBIC:
1524         case FCFT_SCALING_FILTER_LANCZOS3: {
1525             /*
1526              * TODO:
1527              *   - find out how the subsample_bit_{x,y} parameters should be set
1528              */
1529             int param_count = 0;
1530             pixman_kernel_t kernel = filter_to_use == FCFT_SCALING_FILTER_CUBIC
1531                 ? PIXMAN_KERNEL_CUBIC
1532                 : PIXMAN_KERNEL_LANCZOS3;
1533 
1534             pixman_fixed_t *params = pixman_filter_create_separable_convolution(
1535                 &param_count,
1536                 pixman_double_to_fixed(1. / inst->pixel_size_fixup),
1537                 pixman_double_to_fixed(1. / inst->pixel_size_fixup),
1538                 kernel, kernel,
1539                 kernel, kernel,
1540                 pixman_int_to_fixed(1),
1541                 pixman_int_to_fixed(1));
1542 
1543             pixman_image_set_filter(
1544                 pix, PIXMAN_FILTER_SEPARABLE_CONVOLUTION,
1545                 params, param_count);
1546             free(params);
1547             break;
1548         }
1549         }
1550 
1551         int scaled_width = width / (1. / inst->pixel_size_fixup);
1552         int scaled_rows = rows / (1. / inst->pixel_size_fixup);
1553         int scaled_stride = stride_for_format_and_width(pix_format, scaled_width);
1554 
1555         if (pix_format == PIXMAN_a8r8g8b8) {
1556             uint8_t *scaled_data = malloc(scaled_rows * scaled_stride);
1557             if (scaled_data == NULL)
1558                 goto err;
1559 
1560             pixman_image_t *scaled_pix = pixman_image_create_bits_no_clear(
1561                 pix_format, scaled_width, scaled_rows,
1562                 (uint32_t *)scaled_data, scaled_stride);
1563 
1564             if (scaled_pix == NULL) {
1565                 free(scaled_data);
1566                 goto err;
1567             }
1568 
1569             pixman_image_composite32(
1570                 PIXMAN_OP_SRC, pix, NULL, scaled_pix, 0, 0, 0, 0,
1571                 0, 0, scaled_width, scaled_rows);
1572 
1573             pixman_image_unref(pix);
1574             free(data);
1575 
1576             data = scaled_data;
1577             pix = scaled_pix;
1578         }
1579 
1580         rows = scaled_rows;
1581         width = scaled_width;
1582         stride = scaled_stride;
1583 
1584         x *= inst->pixel_size_fixup;
1585         y *= inst->pixel_size_fixup;
1586     }
1587 
1588     *glyph = (struct glyph_priv){
1589         .public = {
1590             .pix = pix,
1591             .x = x,
1592             .y = y,
1593             .advance = {
1594                 .x = (inst->face->glyph->advance.x / 64. *
1595                       (inst->pixel_fixup_estimated ? inst->pixel_size_fixup : 1.)),
1596                 .y = (inst->face->glyph->advance.y / 64. *
1597                       (inst->pixel_fixup_estimated ? inst->pixel_size_fixup : 1.)),
1598             },
1599             .width = width,
1600             .height = rows,
1601         },
1602         .subpixel = subpixel,
1603         .valid = true,
1604     };
1605 
1606     return true;
1607 
1608 err:
1609     if (pix != NULL)
1610         pixman_image_unref(pix);
1611     free(data);
1612     assert(!glyph->valid);
1613     return false;
1614 }
1615 
1616 static bool
glyph_for_wchar(const struct instance * inst,wchar_t wc,enum fcft_subpixel subpixel,struct glyph_priv * glyph)1617 glyph_for_wchar(const struct instance *inst, wchar_t wc,
1618                 enum fcft_subpixel subpixel, struct glyph_priv *glyph)
1619 {
1620     FT_UInt idx = -1;
1621 
1622 #if defined(FCFT_HAVE_HARFBUZZ)
1623     /*
1624      * Use HarfBuzz if we have any font features. If we don’t, there’s
1625      * no point in using HarfBuzz since it only slows things down, and
1626      * we can just lookup the glyph index directly using FreeType’s
1627      * API.x
1628      */
1629     if (inst->hb_feats_count > 0) {
1630         hb_buffer_add_utf32(inst->hb_buf, (const uint32_t *)&wc, 1, 0, 1);
1631         hb_buffer_guess_segment_properties(inst->hb_buf);
1632         hb_shape(inst->hb_font, inst->hb_buf, inst->hb_feats, inst->hb_feats_count);
1633 
1634         unsigned count = hb_buffer_get_length(inst->hb_buf);
1635         if (count == 1) {
1636             const hb_glyph_info_t *info = hb_buffer_get_glyph_infos(inst->hb_buf, NULL);
1637             idx = info[0].codepoint;
1638         }
1639 
1640         hb_buffer_clear_contents(inst->hb_buf);
1641     }
1642 #endif
1643 
1644     if (idx == (FT_UInt)-1)
1645         idx = FT_Get_Char_Index(inst->face, wc);
1646 
1647     bool ret = glyph_for_index(inst, idx, subpixel, glyph);
1648     glyph->public.wc = wc;
1649     glyph->public.cols = wcwidth(wc);
1650     return ret;
1651 }
1652 
1653 static size_t
hash_index_for_size(size_t size,size_t v)1654 hash_index_for_size(size_t size, size_t v)
1655 {
1656     return (v * 2654435761) & (size - 1);
1657 }
1658 
1659 static size_t
glyph_hash_index(const struct font_priv * font,size_t v)1660 glyph_hash_index(const struct font_priv *font, size_t v)
1661 {
1662     return hash_index_for_size(font->glyph_cache.size, v);
1663 }
1664 
1665 static uint32_t
hash_value_for_wc(wchar_t wc,enum fcft_subpixel subpixel)1666 hash_value_for_wc(wchar_t wc, enum fcft_subpixel subpixel)
1667 {
1668     return subpixel << 29 | wc;
1669 }
1670 
1671 static struct glyph_priv **
glyph_cache_lookup(struct font_priv * font,wchar_t wc,enum fcft_subpixel subpixel)1672 glyph_cache_lookup(struct font_priv *font, wchar_t wc,
1673                    enum fcft_subpixel subpixel)
1674 {
1675     size_t idx = glyph_hash_index(font, hash_value_for_wc(wc, subpixel));
1676     struct glyph_priv **glyph = &font->glyph_cache.table[idx];
1677 
1678     while (*glyph != NULL && !((*glyph)->public.wc == wc &&
1679                                (*glyph)->subpixel == subpixel))
1680     {
1681         idx = (idx + 1) & (font->glyph_cache.size - 1);
1682         glyph = &font->glyph_cache.table[idx];
1683 
1684 #if defined(_DEBUG)
1685         glyph_cache_collisions++;
1686 #endif
1687     }
1688 
1689 #if defined(_DEBUG)
1690     glyph_cache_lookups++;
1691 #endif
1692     return glyph;
1693 }
1694 
1695 static bool
glyph_cache_resize(struct font_priv * font)1696 glyph_cache_resize(struct font_priv *font)
1697 {
1698     if (font->glyph_cache.count * 100 / font->glyph_cache.size < 75)
1699         return false;
1700 
1701     size_t size = 2 * font->glyph_cache.size;
1702     assert(__builtin_popcount(size) == 1);
1703 
1704     struct glyph_priv **table = calloc(size, sizeof(table[0]));
1705     if (table == NULL)
1706         return false;
1707 
1708     for (size_t i = 0; i < font->glyph_cache.size; i++) {
1709         struct glyph_priv *entry = font->glyph_cache.table[i];
1710 
1711         if (entry == NULL)
1712             continue;
1713 
1714         size_t idx = hash_index_for_size(
1715             size, hash_value_for_wc(entry->public.wc, entry->subpixel));
1716 
1717         while (table[idx] != NULL) {
1718             assert(!(table[idx]->public.wc == entry->public.wc &&
1719                      table[idx]->subpixel == entry->subpixel));
1720             idx = (idx + 1) & (size - 1);
1721         }
1722 
1723         assert(table[idx] == NULL);
1724         table[idx] = entry;
1725     }
1726 
1727     pthread_rwlock_wrlock(&font->glyph_cache_lock);
1728     {
1729         free(font->glyph_cache.table);
1730 
1731         LOG_DBG("resized glyph cache from %zu to %zu", font->glyph_cache.size, size);
1732         font->glyph_cache.table = table;
1733         font->glyph_cache.size = size;
1734     }
1735     pthread_rwlock_unlock(&font->glyph_cache_lock);
1736     return true;
1737 }
1738 
1739 static int
emoji_compare(const void * _key,const void * _emoji)1740 emoji_compare(const void *_key, const void *_emoji)
1741 {
1742     const uint32_t key = *(const uint32_t *)_key;
1743     const struct emoji *emoji = _emoji;
1744 
1745     if (key < emoji->cp)
1746         return -1;
1747     if (key >= emoji->cp + emoji->count)
1748         return 1;
1749     assert(key >= emoji->cp && key < emoji->cp + emoji->count);
1750     return 0;
1751 }
1752 
1753 static const struct emoji *
emoji_lookup(uint32_t cp)1754 emoji_lookup(uint32_t cp)
1755 {
1756     return bsearch(&cp, emojis, ALEN(emojis), sizeof(emojis[0]), &emoji_compare);
1757 }
1758 
1759 #if defined(_DEBUG)
1760 static void __attribute__((constructor))
test_emoji_compare(void)1761 test_emoji_compare(void)
1762 {
1763 #if defined(FCFT_HAVE_HARFBUZZ)
1764     /* WHITE SMILING FACE */
1765     const struct emoji *e = emoji_lookup(0x263a);
1766 
1767     assert(e != NULL);
1768     assert(0x263a >= e->cp);
1769     assert(0x263a < e->cp + e->count);
1770     assert(!e->emoji_presentation);
1771 
1772     e = emoji_lookup(L'a');
1773     assert(e == NULL);
1774 #else
1775     assert(ALEN(emojis) == 0);
1776 #endif
1777 }
1778 #endif
1779 
1780 FCFT_EXPORT const struct fcft_glyph *
fcft_glyph_rasterize(struct fcft_font * _font,wchar_t wc,enum fcft_subpixel subpixel)1781 fcft_glyph_rasterize(struct fcft_font *_font, wchar_t wc,
1782                      enum fcft_subpixel subpixel)
1783 {
1784     struct font_priv *font = (struct font_priv *)_font;
1785 
1786     pthread_rwlock_rdlock(&font->glyph_cache_lock);
1787     struct glyph_priv **entry = glyph_cache_lookup(font, wc, subpixel);
1788 
1789     if (*entry != NULL) {
1790         const struct glyph_priv *glyph = *entry;
1791         pthread_rwlock_unlock(&font->glyph_cache_lock);
1792         return glyph->valid ? &glyph->public : NULL;
1793     }
1794 
1795     pthread_rwlock_unlock(&font->glyph_cache_lock);
1796     mtx_lock(&font->lock);
1797 
1798     /* Check again - another thread may have resized the cache, or
1799      * populated the entry while we acquired the write-lock */
1800     entry = glyph_cache_lookup(font, wc, subpixel);
1801     if (*entry != NULL) {
1802         const struct glyph_priv *glyph = *entry;
1803         mtx_unlock(&font->lock);
1804         return glyph->valid ? &glyph->public : NULL;
1805     }
1806 
1807     if (glyph_cache_resize(font)) {
1808         /* Entry pointer is invalid if the cache was resized */
1809         entry = glyph_cache_lookup(font, wc, subpixel);
1810     }
1811 
1812     struct glyph_priv *glyph = malloc(sizeof(*glyph));
1813     if (glyph == NULL) {
1814         mtx_unlock(&font->lock);
1815         return NULL;
1816     }
1817 
1818     glyph->public.wc = wc;
1819     glyph->valid = false;
1820 
1821     const struct emoji *emoji = emoji_lookup(wc);
1822     assert(emoji == NULL || (wc >= emoji->cp && wc < emoji->cp + emoji->count));
1823 
1824     bool force_text_presentation = false;
1825     bool force_emoji_presentation = false;
1826     bool enforce_presentation_style = emoji != NULL;
1827 
1828     if (emoji != NULL) {
1829         switch (font->emoji_presentation) {
1830         case FCFT_EMOJI_PRESENTATION_TEXT:
1831             force_text_presentation = true;
1832             force_emoji_presentation = false;
1833             break;
1834 
1835         case FCFT_EMOJI_PRESENTATION_EMOJI:
1836             force_text_presentation = false;
1837             force_emoji_presentation = true;
1838             break;
1839 
1840         case FCFT_EMOJI_PRESENTATION_DEFAULT:
1841             force_text_presentation = !emoji->emoji_presentation;
1842             force_emoji_presentation = emoji->emoji_presentation;
1843             break;
1844         }
1845     }
1846 
1847     assert(tll_length(font->fallbacks) > 0);
1848 
1849     bool no_one = true;
1850     bool got_glyph = false;
1851 
1852 search_fonts:
1853 
1854     tll_foreach(font->fallbacks, it) {
1855         if (!FcCharSetHasChar(it->item.charset, wc))
1856             continue;
1857 
1858         static const FcChar8 *const lang_emoji = (const FcChar8 *)"und-zsye";
1859 
1860         if (enforce_presentation_style && it->item.langset != NULL) {
1861             bool has_lang_emoji =
1862                 FcLangSetHasLang(it->item.langset, lang_emoji) == FcLangEqual;
1863 
1864             if (force_text_presentation && has_lang_emoji)
1865                 continue;
1866             if (force_emoji_presentation && !has_lang_emoji)
1867                 continue;
1868         }
1869 
1870         if (it->item.font == NULL) {
1871             struct instance *inst = malloc(sizeof(*inst));
1872             if (inst == NULL)
1873                 continue;
1874 
1875             if (!instantiate_pattern(
1876                     it->item.pattern,
1877                     it->item.req_pt_size, it->item.req_px_size,
1878                     inst))
1879             {
1880                 /* Remove, so that we don't have to keep trying to
1881                  * instantiate it */
1882                 free(inst);
1883                 fallback_destroy(&it->item);
1884                 tll_remove(font->fallbacks, it);
1885                 continue;
1886             }
1887 
1888             it->item.font = inst;
1889         }
1890 
1891         assert(it->item.font != NULL);
1892         got_glyph = glyph_for_wchar(it->item.font, wc, subpixel, glyph);
1893         no_one = false;
1894         break;
1895     }
1896 
1897     if (no_one && enforce_presentation_style) {
1898         enforce_presentation_style = false;
1899         goto search_fonts;
1900     }
1901 
1902     if (no_one) {
1903         /*
1904          * No font claimed this glyph - use the primary font anyway.
1905          */
1906         assert(tll_length(font->fallbacks) > 0);
1907         struct instance *inst = tll_front(font->fallbacks).font;
1908 
1909         assert(inst != NULL);
1910         got_glyph = glyph_for_wchar(inst, wc, subpixel, glyph);
1911     }
1912 
1913     assert(*entry == NULL);
1914     *entry = glyph;
1915     font->glyph_cache.count++;
1916 
1917     mtx_unlock(&font->lock);
1918     return got_glyph ? &glyph->public : NULL;
1919 }
1920 
1921 #if defined(FCFT_HAVE_HARFBUZZ)
1922 
1923 static size_t
grapheme_hash_index(const struct font_priv * font,size_t v)1924 grapheme_hash_index(const struct font_priv *font, size_t v)
1925 {
1926     return hash_index_for_size(font->grapheme_cache.size, v);
1927 }
1928 
1929 static uint64_t
sdbm_hash_wide(const wchar_t * s,size_t len)1930 sdbm_hash_wide(const wchar_t *s, size_t len)
1931 {
1932     uint64_t hash = 0;
1933 
1934     for (size_t i = 0; i < len; i++, s++)
1935         hash = (hash << 4) ^ *s;
1936     return hash;
1937 }
1938 
1939 static uint64_t
hash_value_for_grapheme(size_t len,const wchar_t grapheme[static len],enum fcft_subpixel subpixel)1940 hash_value_for_grapheme(size_t len, const wchar_t grapheme[static len],
1941                         enum fcft_subpixel subpixel)
1942 {
1943     uint64_t hash = sdbm_hash_wide(grapheme, len);
1944     hash &= (1ull << 29) - 1;
1945     return subpixel << 29 | hash;
1946 }
1947 
1948 static struct grapheme_priv **
grapheme_cache_lookup(struct font_priv * font,size_t len,const wchar_t cluster[static len],enum fcft_subpixel subpixel)1949 grapheme_cache_lookup(struct font_priv *font,
1950                       size_t len, const wchar_t cluster[static len],
1951                       enum fcft_subpixel subpixel)
1952 {
1953     size_t idx = grapheme_hash_index(
1954         font, hash_value_for_grapheme(len, cluster, subpixel));
1955     struct grapheme_priv **entry = &font->grapheme_cache.table[idx];
1956 
1957     while (*entry != NULL && !(
1958                (*entry)->len == len &&
1959                wcsncmp((*entry)->cluster, cluster, len) == 0 &&
1960                (*entry)->subpixel == subpixel))
1961     {
1962         idx = (idx + 1) & (font->grapheme_cache.size - 1);
1963         entry = &font->grapheme_cache.table[idx];
1964 
1965 #if defined(_DEBUG)
1966         grapheme_cache_collisions++;
1967 #endif
1968     }
1969 
1970 #if defined(_DEBUG)
1971     grapheme_cache_lookups++;
1972 #endif
1973     return entry;
1974 }
1975 
1976 static bool
grapheme_cache_resize(struct font_priv * font)1977 grapheme_cache_resize(struct font_priv *font)
1978 {
1979     if (font->grapheme_cache.count * 100 / font->grapheme_cache.size < 75)
1980         return false;
1981 
1982     size_t size = 2 * font->grapheme_cache.size;
1983     assert(__builtin_popcount(size) == 1);
1984 
1985     struct grapheme_priv **table = calloc(size, sizeof(table[0]));
1986     if (table == NULL)
1987         return false;
1988 
1989     for (size_t i = 0; i < font->grapheme_cache.size; i++) {
1990         struct grapheme_priv *entry = font->grapheme_cache.table[i];
1991 
1992         if (entry == NULL)
1993             continue;
1994 
1995         size_t idx = hash_index_for_size(
1996             size, hash_value_for_grapheme(
1997                 entry->len, entry->cluster, entry->subpixel));
1998 
1999         while (table[idx] != NULL) {
2000             assert(
2001                 !(table[idx]->len == entry->len &&
2002                   wcsncmp(table[idx]->cluster, entry->cluster, entry->len) == 0 &&
2003                   table[idx]->subpixel == entry->subpixel));
2004             idx = (idx + 1) & (size - 1);
2005         }
2006 
2007         assert(table[idx] == NULL);
2008         table[idx] = entry;
2009     }
2010 
2011     pthread_rwlock_wrlock(&font->grapheme_cache_lock);
2012     {
2013         free(font->grapheme_cache.table);
2014 
2015         LOG_DBG("resized grapheme cache from %zu to %zu (count: %zu)", font->grapheme_cache.size, size, font->grapheme_cache.count);
2016         font->grapheme_cache.table = table;
2017         font->grapheme_cache.size = size;
2018     }
2019     pthread_rwlock_unlock(&font->grapheme_cache_lock);
2020     return true;
2021 }
2022 
2023 /* Must only be called while font->lock is held */
2024 static bool
font_for_grapheme(struct font_priv * font,size_t len,const wchar_t cluster[static len],struct instance ** inst,bool enforce_presentation_style)2025 font_for_grapheme(struct font_priv *font,
2026                   size_t len, const wchar_t cluster[static len],
2027                   struct instance **inst, bool enforce_presentation_style)
2028 {
2029     static const FcChar8 *const lang_emoji = (const FcChar8 *)"und-zsye";
2030 
2031     tll_foreach(font->fallbacks, it) {
2032         const bool has_lang_emoji = it->item.langset != NULL &&
2033             FcLangSetHasLang(it->item.langset, lang_emoji) == FcLangEqual;
2034 
2035         bool has_all_code_points = true;
2036         for (size_t i = 0; i < len && has_all_code_points; i++) {
2037 
2038             const struct emoji *emoji = emoji_lookup(cluster[i]);
2039             assert(emoji == NULL || (cluster[i] >= emoji->cp &&
2040                                      cluster[i] < emoji->cp + emoji->count));
2041 
2042             if (enforce_presentation_style &&
2043                 emoji != NULL &&
2044                 (i + 1 >= len || (cluster[i + 1] != 0xfe0e &&
2045                                   cluster[i + 1] != 0xfe0f))) {
2046                 /*
2047                  * We have an emoji, that is either the last codepoint
2048                  * in the grapheme, *or* is followed by a codepoint
2049                  * that is *not* a presentation selector.
2050                  */
2051                 bool force_text_presentation = false;
2052                 bool force_emoji_presentation = false;
2053 
2054                 switch (font->emoji_presentation) {
2055                 case FCFT_EMOJI_PRESENTATION_TEXT:
2056                     force_text_presentation = true;
2057                     force_emoji_presentation = false;
2058                     break;
2059 
2060                 case FCFT_EMOJI_PRESENTATION_EMOJI:
2061                     force_text_presentation = false;
2062                     force_emoji_presentation = true;
2063                     break;
2064 
2065                 case FCFT_EMOJI_PRESENTATION_DEFAULT:
2066                     force_text_presentation = !emoji->emoji_presentation;
2067                     force_emoji_presentation = emoji->emoji_presentation;
2068                     break;
2069                 }
2070 
2071                 if (force_text_presentation && has_lang_emoji) {
2072                     has_all_code_points = false;
2073                     continue;
2074                 }
2075 
2076                 if (force_emoji_presentation && !has_lang_emoji) {
2077                     has_all_code_points = false;
2078                     continue;
2079                 }
2080             }
2081 
2082             if (cluster[i] == 0x200d) {
2083                 /* ZWJ */
2084                 continue;
2085             }
2086 
2087             if (cluster[i] == 0xfe0f) {
2088                 /* Explicit emoji selector */
2089 
2090 #if 0
2091                 /* Require colored emoji? */
2092                 if (!it->item.is_color) {
2093                     /* Skip font if it is not a colored font */
2094                     has_all_code_points = false;
2095                 }
2096 #endif
2097                 if (!has_lang_emoji) {
2098                     /* Skip font if it isn't an emoji font */
2099                     has_all_code_points = false;
2100                 }
2101 
2102                 continue;
2103             }
2104 
2105             else if (cluster[i] == 0xfe0e) {
2106                 /* Explicit text selector */
2107 
2108                 if (has_lang_emoji) {
2109                     /* Skip font if it is an emoji font */
2110                     has_all_code_points = false;
2111                 }
2112 
2113                 continue;
2114             }
2115 
2116             if (!FcCharSetHasChar(it->item.charset, cluster[i])) {
2117                 has_all_code_points = false;
2118                 break;
2119             }
2120         }
2121 
2122         if (has_all_code_points) {
2123             if (it->item.font == NULL) {
2124                 *inst = malloc(sizeof(**inst));
2125                 if (*inst == NULL)
2126                     return false;
2127 
2128                 if (!instantiate_pattern(
2129                         it->item.pattern,
2130                         it->item.req_pt_size, it->item.req_px_size,
2131                         *inst))
2132                 {
2133                     /* Remove, so that we don't have to keep trying to
2134                      * instantiate it */
2135                     free(*inst);
2136                     fallback_destroy(&it->item);
2137                     tll_remove(font->fallbacks, it);
2138                     continue;
2139                 }
2140 
2141                 it->item.font = *inst;
2142             } else
2143                 *inst = it->item.font;
2144 
2145             return true;
2146         }
2147     }
2148 
2149     if (enforce_presentation_style)
2150         return font_for_grapheme(font, len, cluster, inst, false);
2151 
2152     /* No font found, use primary font anyway */
2153     *inst = tll_front(font->fallbacks).font;
2154     return *inst != NULL;
2155 }
2156 
2157 FCFT_EXPORT const struct fcft_grapheme *
fcft_grapheme_rasterize(struct fcft_font * _font,size_t len,const wchar_t cluster[static len],size_t tag_count,const struct fcft_layout_tag * tags,enum fcft_subpixel subpixel)2158 fcft_grapheme_rasterize(struct fcft_font *_font,
2159                         size_t len, const wchar_t cluster[static len],
2160                         size_t tag_count, const struct fcft_layout_tag *tags,
2161                         enum fcft_subpixel subpixel)
2162 {
2163     struct font_priv *font = (struct font_priv *)_font;
2164     struct instance *inst = NULL;
2165 
2166     pthread_rwlock_rdlock(&font->grapheme_cache_lock);
2167     struct grapheme_priv **entry = grapheme_cache_lookup(
2168         font, len, cluster, subpixel);
2169 
2170     if (*entry != NULL) {
2171         const struct grapheme_priv *grapheme = *entry;
2172         pthread_rwlock_unlock(&font->grapheme_cache_lock);
2173         return grapheme->valid ? &grapheme->public : NULL;
2174     }
2175 
2176     pthread_rwlock_unlock(&font->grapheme_cache_lock);
2177     mtx_lock(&font->lock);
2178 
2179     /* Check again - another thread may have resized the cache, or
2180      * populated the entry while we acquired the write-lock */
2181     entry = grapheme_cache_lookup(font, len, cluster, subpixel);
2182     if (*entry != NULL) {
2183         const struct grapheme_priv *grapheme = *entry;
2184         mtx_unlock(&font->lock);
2185         return grapheme->valid ? &grapheme->public : NULL;
2186     }
2187 
2188     if (grapheme_cache_resize(font)) {
2189         /* Entry pointer is invalid if the cache was resized */
2190         entry = grapheme_cache_lookup(font, len, cluster, subpixel);
2191     }
2192 
2193     struct grapheme_priv *grapheme = malloc(sizeof(*grapheme));
2194     wchar_t *cluster_copy = malloc(len * sizeof(cluster_copy[0]));
2195     if (grapheme == NULL || cluster_copy == NULL) {
2196         /* Can’t update cache entry since we can’t store the cluster */
2197         free(grapheme);
2198         free(cluster_copy);
2199         mtx_unlock(&font->lock);
2200         return NULL;
2201     }
2202 
2203     size_t glyph_idx = 0;
2204     wcsncpy(cluster_copy, cluster, len);
2205     grapheme->valid = false;
2206     grapheme->len = len;
2207     grapheme->cluster = cluster_copy;
2208     grapheme->subpixel = subpixel;
2209     grapheme->public.glyphs = NULL;
2210     grapheme->public.count = 0;
2211 
2212     /* Find a font that has all codepoints in the grapheme */
2213     if (!font_for_grapheme(font, len, cluster, &inst, true))
2214         goto err;
2215 
2216     assert(inst->hb_font != NULL);
2217 
2218     hb_buffer_add_utf32(inst->hb_buf, (const uint32_t *)cluster, len, 0, len);
2219     hb_buffer_guess_segment_properties(inst->hb_buf);
2220 
2221     hb_shape(inst->hb_font, inst->hb_buf, inst->hb_feats, inst->hb_feats_count);
2222 
2223     unsigned count = hb_buffer_get_length(inst->hb_buf);
2224     const hb_glyph_info_t *info = hb_buffer_get_glyph_infos(inst->hb_buf, NULL);
2225     const hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(inst->hb_buf, NULL);
2226 
2227     int grapheme_width = 0;
2228     int min_grapheme_width = 0;
2229     for (size_t i = 0; i < len; i++) {
2230         if (cluster[i] == 0xfe0f)
2231             min_grapheme_width = 2;
2232         grapheme_width += wcwidth(cluster[i]);
2233     }
2234 
2235     LOG_DBG("length: %u", hb_buffer_get_length(inst->hb_buf));
2236     LOG_DBG("infos: %u", count);
2237 
2238     struct fcft_glyph **glyphs = calloc(count, sizeof(glyphs[0]));
2239     if (glyphs == NULL)
2240         goto err;
2241 
2242     grapheme->public.cols = max(grapheme_width, min_grapheme_width);
2243     grapheme->public.glyphs = (const struct fcft_glyph **)glyphs;
2244 
2245     const unsigned count_from_the_beginning = count;
2246 
2247     for (unsigned i = 0; i < count_from_the_beginning; i++) {
2248         LOG_DBG("code point: %04x, cluster: %u", info[i].codepoint, info[i].cluster);
2249         LOG_DBG("x-advance: %d, x-offset: %d, y-advance: %d, y-offset: %d",
2250                 pos[i].x_advance, pos[i].x_offset,
2251                 pos[i].y_advance, pos[i].y_offset);
2252 
2253         struct glyph_priv *glyph = malloc(sizeof(*glyph));
2254         if (glyph == NULL ||
2255             !glyph_for_index(inst, info[i].codepoint, subpixel, glyph))
2256         {
2257             assert(glyph == NULL || !glyph->valid);
2258             free(glyph);
2259             goto err;
2260         }
2261 
2262         assert(glyph->valid);
2263 
2264         assert(info[i].cluster < len);
2265         glyph->public.wc = cluster[info[i].cluster];
2266         glyph->public.cols = wcwidth(glyph->public.wc);
2267 
2268 #if 0
2269         LOG_DBG("grapheme: x: advance: %d -> %d, offset: %d -> %d",
2270                 glyph->public.advance.x,
2271                 (int)(pos[i].x_advance / 64. * inst->pixel_size_fixup),
2272                 glyph->public.x,
2273                 (int)(pos[i].x_offset / 64. * inst->pixel_size_fixup));
2274         LOG_DBG("grapheme: y: advance: %d -> %d, offset: %d -> %d",
2275                 glyph->public.advance.y,
2276                 (int)(pos[i].y_advance / 64. * inst->pixel_size_fixup),
2277                 glyph->public.y,
2278                 (int)(pos[i].y_offset / 64. * inst->pixel_size_fixup));
2279 #endif
2280 
2281         glyph->public.x += pos[i].x_offset / 64. * inst->pixel_size_fixup;
2282         glyph->public.y += pos[i].y_offset / 64. * inst->pixel_size_fixup;
2283         glyph->public.advance.x = pos[i].x_advance / 64. * inst->pixel_size_fixup;
2284         glyph->public.advance.y = pos[i].y_advance / 64. * inst->pixel_size_fixup;
2285 
2286         grapheme->public.glyphs[glyph_idx++] = &glyph->public;
2287     }
2288 
2289 #if defined(_DEBUG)
2290     assert(glyph_idx == count);
2291     for (size_t i = 0; i < count; i++) {
2292         const struct glyph_priv *g
2293             = (const struct glyph_priv *)grapheme->public.glyphs[i];
2294 
2295         assert(g != NULL);
2296         assert(g->valid);
2297     }
2298 #endif
2299 
2300     hb_buffer_clear_contents(inst->hb_buf);
2301 
2302     assert(*entry == NULL);
2303     grapheme->public.count = glyph_idx;
2304     grapheme->valid = true;
2305     *entry = grapheme;
2306     font->grapheme_cache.count++;
2307 
2308     mtx_unlock(&font->lock);
2309     return &grapheme->public;
2310 
2311 err:
2312     for (size_t i = 0; i < glyph_idx; i++)
2313         glyph_destroy(grapheme->public.glyphs[i]);
2314     free(grapheme->public.glyphs);
2315 
2316     assert(*entry == NULL);
2317     assert(!grapheme->valid);
2318     grapheme->public.count = 0;
2319     grapheme->public.glyphs = NULL;
2320     *entry = grapheme;
2321     font->grapheme_cache.count++;
2322     mtx_unlock(&font->lock);
2323     return NULL;
2324 }
2325 #else /* !FCFT_HAVE_HARFBUZZ */
2326 
2327 FCFT_EXPORT const struct fcft_grapheme *
fcft_grapheme_rasterize(struct fcft_font * _font,size_t len,const wchar_t cluster[static len],size_t tag_count,const struct fcft_layout_tag * tags,enum fcft_subpixel subpixel)2328 fcft_grapheme_rasterize(struct fcft_font *_font,
2329                         size_t len, const wchar_t cluster[static len],
2330                         size_t tag_count, const struct fcft_layout_tag *tags,
2331                         enum fcft_subpixel subpixel)
2332 {
2333     return NULL;
2334 }
2335 
2336 #endif
2337 
2338 #if defined(FCFT_HAVE_HARFBUZZ) && defined(FCFT_HAVE_UTF8PROC)
2339 struct text_run {
2340     struct fcft_text_run *public;
2341     size_t size;
2342 };
2343 
2344 static bool
rasterize_partial_run(struct text_run * run,const struct instance * inst,const uint32_t * text,size_t len,size_t run_start,size_t run_len,enum fcft_subpixel subpixel)2345 rasterize_partial_run(struct text_run *run, const struct instance *inst,
2346                       const uint32_t *text, size_t len,
2347                       size_t run_start, size_t run_len,
2348                       enum fcft_subpixel subpixel)
2349 {
2350     hb_buffer_add_utf32(inst->hb_buf, text, len, run_start, run_len);
2351     hb_buffer_guess_segment_properties(inst->hb_buf);
2352 
2353     hb_segment_properties_t props;
2354     hb_buffer_get_segment_properties(inst->hb_buf, &props);
2355 
2356     assert(props.direction == HB_DIRECTION_LTR ||
2357            props.direction == HB_DIRECTION_RTL);
2358 
2359     if (props.direction != HB_DIRECTION_LTR &&
2360         props.direction != HB_DIRECTION_RTL)
2361     {
2362         LOG_ERR("unimplemented: hb_direction=%d", props.direction);
2363         return false;
2364     }
2365 
2366     hb_shape(inst->hb_font, inst->hb_buf, inst->hb_feats, inst->hb_feats_count);
2367 
2368     unsigned count = hb_buffer_get_length(inst->hb_buf);
2369     const hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(inst->hb_buf, NULL);
2370     const hb_glyph_position_t *poss = hb_buffer_get_glyph_positions(inst->hb_buf, NULL);
2371 
2372     for (int i = 0; i < count; i++) {
2373         const hb_glyph_info_t *info = &infos[i];
2374         const hb_glyph_position_t *pos = &poss[i];
2375 
2376         LOG_DBG("#%u: codepoint=%04x, cluster=%d", i, info->codepoint, info->cluster);
2377 
2378         struct glyph_priv *glyph = malloc(sizeof(*glyph));
2379         if (glyph == NULL)
2380             return false;
2381 
2382         if (!glyph_for_index(inst, info->codepoint, subpixel, glyph)) {
2383             free(glyph);
2384             continue;
2385         }
2386 
2387         assert(info->cluster < len);
2388         glyph->public.wc = text[info->cluster];
2389         glyph->public.cols = wcwidth(glyph->public.wc);
2390 
2391         glyph->public.x += pos->x_offset / 64. * inst->pixel_size_fixup;
2392         glyph->public.y += pos->y_offset / 64. * inst->pixel_size_fixup;
2393         glyph->public.advance.x = pos->x_advance / 64. * inst->pixel_size_fixup;
2394         glyph->public.advance.y = pos->y_advance / 64. * inst->pixel_size_fixup;
2395 
2396         if (run->public->count >= run->size) {
2397             size_t new_glyphs_size = run->size * 2;
2398             const struct fcft_glyph **new_glyphs = realloc(
2399                 run->public->glyphs, new_glyphs_size * sizeof(new_glyphs[0]));
2400             int *new_cluster = realloc(
2401                 run->public->cluster, new_glyphs_size * sizeof(new_cluster[0]));
2402 
2403             if (new_glyphs == NULL || new_cluster == NULL) {
2404                 free(new_glyphs);
2405                 free(new_cluster);
2406                 return false;
2407             }
2408 
2409             run->public->glyphs = new_glyphs;
2410             run->public->cluster = new_cluster;
2411             run->size = new_glyphs_size;
2412         }
2413 
2414         assert(run->public->count < run->size);
2415         run->public->cluster[run->public->count] = info->cluster;
2416         run->public->glyphs[run->public->count] = &glyph->public;
2417         run->public->count++;
2418     }
2419 
2420     return true;
2421 }
2422 
2423 FCFT_EXPORT struct fcft_text_run *
fcft_text_run_rasterize(struct fcft_font * _font,size_t len,const wchar_t text[static len],enum fcft_subpixel subpixel)2424 fcft_text_run_rasterize(
2425     struct fcft_font *_font, size_t len, const wchar_t text[static len],
2426     enum fcft_subpixel subpixel)
2427 {
2428     struct font_priv *font = (struct font_priv *)_font;
2429     mtx_lock(&font->lock);
2430 
2431     LOG_DBG("rasterizing a %zu character text run", len);
2432 
2433     struct partial_run {
2434         size_t start;
2435         size_t len;
2436         struct instance *inst;
2437     };
2438 
2439     tll(struct partial_run) pruns = tll_init();
2440 
2441     struct text_run run = {
2442         .size = len,
2443         .public = malloc(sizeof(*run.public)),
2444     };
2445 
2446     if (run.public == NULL)
2447         goto err;
2448 
2449     run.public->glyphs = malloc(len * sizeof(run.public->glyphs[0]));
2450     run.public->cluster = malloc(len * sizeof(run.public->cluster[0]));
2451     run.public->count = 0;
2452 
2453     if (run.public->glyphs == NULL || run.public->cluster == NULL)
2454         goto err;
2455 
2456 
2457     tll_push_back(pruns, ((struct partial_run){.start = 0}));
2458 
2459     /* Split run into graphemes */
2460     utf8proc_int32_t state;
2461     for (size_t i = 1; i < len; i++) {
2462         if (utf8proc_grapheme_break_stateful(text[i - 1], text[i], &state)) {
2463             state = 0;
2464 
2465             struct partial_run *prun = &tll_back(pruns);
2466 
2467             assert(i > prun->start);
2468             prun->len = i - prun->start;
2469 
2470             if (!font_for_grapheme(
2471                     font, prun->len, &text[prun->start], &prun->inst, true))
2472             {
2473                 goto err;
2474             }
2475 
2476             tll_push_back(pruns, ((struct partial_run){.start = i}));
2477         }
2478     }
2479 
2480     /* “Close” that last run */
2481     {
2482         struct partial_run *prun = &tll_back(pruns);
2483 
2484         assert(prun->len == 0);
2485         prun->len = len - prun->start;
2486 
2487         if (!font_for_grapheme(
2488                 font, prun->len, &text[prun->start], &prun->inst, true))
2489         {
2490             goto err;
2491         }
2492     }
2493 
2494 #if defined(_DEBUG) && LOG_ENABLE_DBG
2495     LOG_DBG("%zu partial runs (before merge):", tll_length(pruns));
2496     tll_foreach(pruns, it) {
2497         const struct partial_run *prun = &it->item;
2498         LOG_DBG("  %.*ls (start=%zu, %zu chars), inst=%p",
2499                 (int)prun->len, &text[prun->start], prun->start,
2500                 prun->len, prun->inst);
2501     }
2502 #endif
2503 
2504     /*
2505      * Merge consecutive graphemes if:
2506      *  - they belong to the same script (“language”)
2507      *  - they have the same font instance
2508      */
2509     {
2510         hb_buffer_t *hb_buf = hb_buffer_create();
2511         if (hb_buf == NULL)
2512             goto err;
2513 
2514         struct partial_run *prev = NULL;
2515         hb_script_t prev_script = HB_SCRIPT_INVALID;
2516 
2517         tll_foreach(pruns, it) {
2518             struct partial_run *prun = &it->item;
2519 
2520             /* Get script (“language”) of grapheme */
2521             hb_buffer_add_utf32(
2522                 hb_buf, (const uint32_t *)text, len, prun->start, prun->len);
2523             hb_buffer_guess_segment_properties(hb_buf);
2524 
2525             hb_script_t script = hb_buffer_get_script(hb_buf);
2526             hb_buffer_clear_contents(hb_buf);
2527 
2528             if (prev == NULL) {
2529                 prev = prun;
2530                 prev_script = script;
2531                 continue;
2532             }
2533 
2534             if (prev->inst == prun->inst && prev_script == script) {
2535                 prev->len += prun->len;
2536                 tll_remove(pruns, it);
2537             } else {
2538                 prev = prun;
2539                 prev_script = script;
2540             }
2541         }
2542 
2543         hb_buffer_destroy(hb_buf);
2544     }
2545 
2546 #if defined(_DEBUG) && LOG_ENABLE_DBG
2547     LOG_DBG("%zu partial runs (after merge):", tll_length(pruns));
2548     tll_foreach(pruns, it) {
2549         const struct partial_run *prun = &it->item;
2550         LOG_DBG("  %.*ls (start=%zu, %zu chars), inst=%p",
2551                 (int)prun->len, &text[prun->start], prun->start,
2552                 prun->len, prun->inst);
2553     }
2554 #endif
2555 
2556     /* Shape each partial run */
2557     tll_foreach(pruns, it) {
2558         const struct partial_run *prun = &it->item;
2559 
2560         bool ret = rasterize_partial_run(
2561             &run, prun->inst, (const uint32_t *)text, len,
2562             prun->start, prun->len, subpixel);
2563 
2564         hb_buffer_clear_contents(prun->inst->hb_buf);
2565         if (!ret)
2566             goto err;
2567     }
2568 
2569     /* Re-alloc glyphs/cluster arrays */
2570     {
2571         const struct fcft_glyph **final_glyphs = realloc(
2572             run.public->glyphs, run.public->count * sizeof(final_glyphs[0]));
2573         int *final_cluster = realloc(
2574             run.public->cluster, run.public->count * sizeof(final_cluster[0]));
2575 
2576         if ((final_glyphs == NULL || final_cluster == NULL) &&
2577             run.public->count > 0)
2578         {
2579             free(final_glyphs);
2580             free(final_cluster);
2581             goto err;
2582         }
2583 
2584         run.public->glyphs = final_glyphs;
2585         run.public->cluster = final_cluster;
2586     }
2587 
2588     LOG_DBG("glyph count: %zu", run.public->count);
2589 
2590     tll_free(pruns);
2591     mtx_unlock(&font->lock);
2592     return run.public;
2593 
2594 err:
2595 
2596     if (run.public != NULL) {
2597         for (size_t i = 0; i < run.public->count; i++) {
2598             assert(run.public->glyphs[i] != NULL);
2599             glyph_destroy(run.public->glyphs[i]);
2600         }
2601 
2602         free(run.public->glyphs);
2603         free(run.public->cluster);
2604         free(run.public);
2605     }
2606 
2607     tll_free(pruns);
2608     mtx_unlock(&font->lock);
2609     return NULL;
2610 }
2611 
2612 #else /* !FCFT_HAVE_HARFBUZZ || !FCFT_HAVE_UTF8PROC */
2613 
2614 FCFT_EXPORT struct fcft_text_run *
fcft_text_run_rasterize(struct fcft_font * font,size_t len,const wchar_t text[static len],enum fcft_subpixel subpixel)2615 fcft_text_run_rasterize(
2616     struct fcft_font *font, size_t len, const wchar_t text[static len],
2617     enum fcft_subpixel subpixel)
2618 {
2619     return NULL;
2620 }
2621 
2622 #endif /* !FCFT_HAVE_HARFBUZZ */
2623 
2624 FCFT_EXPORT void
fcft_text_run_destroy(struct fcft_text_run * run)2625 fcft_text_run_destroy(struct fcft_text_run *run)
2626 {
2627     if (run == NULL)
2628         return;
2629 
2630     for (size_t i = 0; i < run->count; i++) {
2631         assert(run->glyphs[i] != NULL);
2632         glyph_destroy(run->glyphs[i]);
2633     }
2634 
2635     free(run->glyphs);
2636     free(run->cluster);
2637     free(run);
2638 }
2639 
2640 FCFT_EXPORT void
fcft_destroy(struct fcft_font * _font)2641 fcft_destroy(struct fcft_font *_font)
2642 {
2643     if (_font == NULL)
2644         return;
2645 
2646     struct font_priv *font = (struct font_priv *)_font;
2647 
2648     bool in_cache = false;
2649     mtx_lock(&font_cache_lock);
2650     tll_foreach(font_cache, it) {
2651         if (it->item.font == font) {
2652 
2653             in_cache = true;
2654 
2655             mtx_lock(&font->lock);
2656             if (--font->ref_counter > 0) {
2657                 mtx_unlock(&font->lock);
2658                 mtx_unlock(&font_cache_lock);
2659                 return;
2660             }
2661             mtx_unlock(&font->lock);
2662 
2663             cnd_destroy(&it->item.cond);
2664             tll_remove(font_cache, it);
2665             break;
2666         }
2667     };
2668     mtx_unlock(&font_cache_lock);
2669 
2670     if (!in_cache) {
2671         mtx_lock(&font->lock);
2672         if (--font->ref_counter > 0) {
2673             mtx_unlock(&font->lock);
2674             return;
2675         }
2676         mtx_unlock(&font->lock);
2677     }
2678 
2679     tll_foreach(font->fallbacks, it)
2680         fallback_destroy(&it->item);
2681 
2682     tll_free(font->fallbacks);
2683     mtx_destroy(&font->lock);
2684 
2685     for (size_t i = 0;
2686          i < font->glyph_cache.size && font->glyph_cache.table != NULL;
2687          i++)
2688     {
2689         struct glyph_priv *entry = font->glyph_cache.table[i];
2690 
2691         if (entry == NULL)
2692             continue;
2693 
2694         glyph_destroy_private(entry);
2695     }
2696     free(font->glyph_cache.table);
2697     pthread_rwlock_destroy(&font->glyph_cache_lock);
2698 
2699 #if defined(FCFT_HAVE_HARFBUZZ)
2700     for (size_t i = 0;
2701          i < font->grapheme_cache.size && font->grapheme_cache.table != NULL;
2702          i++)
2703     {
2704         struct grapheme_priv *entry = font->grapheme_cache.table[i];
2705 
2706         if (entry == NULL)
2707             continue;
2708 
2709         for (size_t j = 0; j < entry->public.count; j++) {
2710             assert(entry->public.glyphs[j] != NULL);
2711             glyph_destroy(entry->public.glyphs[j]);
2712         }
2713 
2714         free(entry->public.glyphs);
2715         free(entry->cluster);
2716         free(entry);
2717     }
2718     free(font->grapheme_cache.table);
2719     pthread_rwlock_destroy(&font->grapheme_cache_lock);
2720 #endif
2721 
2722     free(font);
2723 }
2724 
2725 FCFT_EXPORT bool
fcft_kerning(struct fcft_font * _font,wchar_t left,wchar_t right,long * restrict x,long * restrict y)2726 fcft_kerning(struct fcft_font *_font, wchar_t left, wchar_t right,
2727              long *restrict x, long *restrict y)
2728 {
2729     struct font_priv *font = (struct font_priv *)_font;
2730 
2731     if (x != NULL)
2732         *x = 0;
2733     if (y != NULL)
2734         *y = 0;
2735 
2736     mtx_lock(&font->lock);
2737 
2738     assert(tll_length(font->fallbacks) > 0);
2739     const struct instance *primary = tll_front(font->fallbacks).font;
2740 
2741     if (!FT_HAS_KERNING(primary->face))
2742         goto err;
2743 
2744     FT_UInt left_idx = FT_Get_Char_Index(primary->face, left);
2745     if (left_idx == 0)
2746         goto err;
2747 
2748     FT_UInt right_idx = FT_Get_Char_Index(primary->face, right);
2749     if (right_idx == 0)
2750         goto err;
2751 
2752     FT_Vector kerning;
2753     FT_Error err = FT_Get_Kerning(
2754         primary->face, left_idx, right_idx, FT_KERNING_DEFAULT, &kerning);
2755 
2756     if (err != 0) {
2757         LOG_WARN("%s: failed to get kerning for %lc -> %lc: %s",
2758                  primary->path, (wint_t)left, (wint_t)right,
2759                  ft_error_string(err));
2760         goto err;
2761     }
2762 
2763     if (x != NULL)
2764         *x = kerning.x / 64. * primary->pixel_size_fixup;
2765     if (y != NULL)
2766         *y = kerning.y / 64. * primary->pixel_size_fixup;
2767 
2768     LOG_DBG("%s: kerning: %lc -> %lc: x=%ld 26.6, y=%ld 26.6",
2769             primary->path, (wint_t)left, (wint_t)right,
2770             kerning.x, kerning.y);
2771 
2772     mtx_unlock(&font->lock);
2773     return true;
2774 
2775 err:
2776     mtx_unlock(&font->lock);
2777     return false;
2778 }
2779 
2780 FCFT_EXPORT wchar_t
fcft_precompose(const struct fcft_font * _font,wchar_t base,wchar_t comb,bool * base_is_from_primary,bool * comb_is_from_primary,bool * composed_is_from_primary)2781 fcft_precompose(const struct fcft_font *_font, wchar_t base, wchar_t comb,
2782                 bool *base_is_from_primary,
2783                 bool *comb_is_from_primary,
2784                 bool *composed_is_from_primary)
2785 {
2786     _Static_assert(2 * sizeof(wchar_t) <= sizeof(uint64_t),
2787                   "two wchars does not fit in an uint64_t");
2788 
2789     const struct font_priv *font = (const struct font_priv *)_font;
2790 
2791     assert(tll_length(font->fallbacks) > 0);
2792     const struct fallback *primary = &tll_front(font->fallbacks);
2793 
2794     if (font != NULL) {
2795         if (base_is_from_primary != NULL)
2796             *base_is_from_primary = FcCharSetHasChar(primary->charset, base);
2797         if (comb_is_from_primary != NULL)
2798             *comb_is_from_primary = FcCharSetHasChar(primary->charset, comb);
2799     }
2800 
2801     const uint64_t match = (uint64_t)base << 32 | comb;
2802 
2803     ssize_t start = 0;
2804     ssize_t end = (sizeof(precompose_table) / sizeof(precompose_table[0])) - 1;
2805 
2806     while (start <= end) {
2807         size_t middle = (start + end) / 2;
2808 
2809         const uint64_t maybe =
2810             (uint64_t)precompose_table[middle].base << 32 | precompose_table[middle].comb;
2811 
2812         if (maybe < match)
2813             start = middle + 1;
2814         else if (maybe > match)
2815             end = middle - 1;
2816         else {
2817             wchar_t composed = precompose_table[middle].replacement;
2818             if (font != NULL && composed_is_from_primary != NULL) {
2819                 *composed_is_from_primary = FcCharSetHasChar(
2820                     primary->charset, composed);
2821             }
2822             return composed;
2823         }
2824     }
2825 
2826     if (composed_is_from_primary != NULL)
2827         *composed_is_from_primary = false;
2828     return (wchar_t)-1;
2829 }
2830 
2831 FCFT_EXPORT void
fcft_set_emoji_presentation(struct fcft_font * _font,enum fcft_emoji_presentation presentation)2832 fcft_set_emoji_presentation(struct fcft_font *_font,
2833                             enum fcft_emoji_presentation presentation)
2834 {
2835     struct font_priv *font = (struct font_priv *)_font;
2836     font->emoji_presentation = presentation;
2837 }
2838