1 /*
2  * vim:fileencoding=utf-8
3  * fonts.c
4  * Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
5  *
6  * Distributed under terms of the GPL3 license.
7  */
8 
9 #include "fonts.h"
10 #include "state.h"
11 #include "emoji.h"
12 #include "unicode-data.h"
13 #include "charsets.h"
14 #include "glyph-cache.h"
15 
16 #define MISSING_GLYPH 4
17 #define MAX_NUM_EXTRA_GLYPHS_PUA 4u
18 
19 typedef void (*send_sprite_to_gpu_func)(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*);
20 send_sprite_to_gpu_func current_send_sprite_to_gpu = NULL;
21 static PyObject *python_send_to_gpu_impl = NULL;
22 extern PyTypeObject Line_Type;
23 
24 enum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};
25 typedef enum {
26     LIGATURE_UNKNOWN, INFINITE_LIGATURE_START, INFINITE_LIGATURE_MIDDLE, INFINITE_LIGATURE_END
27 } LigatureType;
28 
29 
30 #define SPECIAL_FILLED_MASK 1
31 #define SPECIAL_VALUE_MASK 2
32 #define EMPTY_FILLED_MASK 4
33 #define EMPTY_VALUE_MASK 8
34 
35 typedef struct {
36     size_t max_y;
37     unsigned int x, y, z, xnum, ynum;
38 } GPUSpriteTracker;
39 
40 
41 static hb_buffer_t *harfbuzz_buffer = NULL;
42 static hb_feature_t hb_features[3] = {{0}};
43 static char_type shape_buffer[4096] = {0};
44 static size_t max_texture_size = 1024, max_array_len = 1024;
45 typedef enum { LIGA_FEATURE, DLIG_FEATURE, CALT_FEATURE } HBFeature;
46 static PyObject* font_feature_settings = NULL;
47 
48 typedef struct {
49     char_type left, right;
50     size_t font_idx;
51 } SymbolMap;
52 
53 static SymbolMap *symbol_maps = NULL;
54 static size_t num_symbol_maps = 0;
55 
56 typedef enum { SPACER_STRATEGY_UNKNOWN, SPACERS_BEFORE, SPACERS_AFTER, SPACERS_IOSEVKA } SpacerStrategy;
57 
58 typedef struct {
59     PyObject *face;
60     // Map glyphs to sprite map co-ords
61     SpritePosition *sprite_position_hash_table;
62     hb_feature_t* ffs_hb_features;
63     size_t num_ffs_hb_features;
64     GlyphProperties *glyph_properties_hash_table;
65     bool bold, italic, emoji_presentation;
66     SpacerStrategy spacer_strategy;
67 } Font;
68 
69 typedef struct Canvas {
70     pixel *buf;
71     unsigned current_cells, alloced_cells;
72 } Canvas;
73 
74 typedef struct {
75     FONTS_DATA_HEAD
76     id_type id;
77     unsigned int baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
78     size_t fonts_capacity, fonts_count, fallback_fonts_count;
79     ssize_t medium_font_idx, bold_font_idx, italic_font_idx, bi_font_idx, first_symbol_font_idx, first_fallback_font_idx;
80     Font *fonts;
81     Canvas canvas;
82     GPUSpriteTracker sprite_tracker;
83 } FontGroup;
84 
85 static FontGroup* font_groups = NULL;
86 static size_t font_groups_capacity = 0;
87 static size_t num_font_groups = 0;
88 static id_type font_group_id_counter = 0;
89 static void initialize_font_group(FontGroup *fg);
90 
91 static void
ensure_canvas_can_fit(FontGroup * fg,unsigned cells)92 ensure_canvas_can_fit(FontGroup *fg, unsigned cells) {
93     if (fg->canvas.alloced_cells < cells) {
94         free(fg->canvas.buf);
95         fg->canvas.alloced_cells = cells + 4;
96         fg->canvas.buf = malloc(sizeof(fg->canvas.buf[0]) * 3u * fg->canvas.alloced_cells * fg->cell_width * fg->cell_height);
97         if (!fg->canvas.buf) fatal("Out of memory allocating canvas");
98     }
99     fg->canvas.current_cells = cells;
100     if (fg->canvas.buf) memset(fg->canvas.buf, 0, sizeof(fg->canvas.buf[0]) * fg->canvas.current_cells * 3u * fg->cell_width * fg->cell_height);
101 }
102 
103 
104 static void
save_window_font_groups(void)105 save_window_font_groups(void) {
106     for (size_t o = 0; o < global_state.num_os_windows; o++) {
107         OSWindow *w = global_state.os_windows + o;
108         w->temp_font_group_id = w->fonts_data ? ((FontGroup*)(w->fonts_data))->id : 0;
109     }
110 }
111 
112 static void
restore_window_font_groups(void)113 restore_window_font_groups(void) {
114     for (size_t o = 0; o < global_state.num_os_windows; o++) {
115         OSWindow *w = global_state.os_windows + o;
116         w->fonts_data = NULL;
117         for (size_t i = 0; i < num_font_groups; i++) {
118             if (font_groups[i].id == w->temp_font_group_id) {
119                 w->fonts_data = (FONTS_DATA_HANDLE)(font_groups + i);
120                 break;
121             }
122         }
123     }
124 }
125 
126 static bool
font_group_is_unused(FontGroup * fg)127 font_group_is_unused(FontGroup *fg) {
128     for (size_t o = 0; o < global_state.num_os_windows; o++) {
129         OSWindow *w = global_state.os_windows + o;
130         if (w->temp_font_group_id == fg->id) return false;
131     }
132     return true;
133 }
134 
135 void
free_maps(Font * font)136 free_maps(Font *font) {
137     free_sprite_position_hash_table(&font->sprite_position_hash_table);
138     font->sprite_position_hash_table = NULL;
139     free_glyph_properties_hash_table(&font->glyph_properties_hash_table);
140     font->glyph_properties_hash_table = NULL;
141 }
142 
143 static void
del_font(Font * f)144 del_font(Font *f) {
145     Py_CLEAR(f->face);
146     free(f->ffs_hb_features); f->ffs_hb_features = NULL;
147     free_maps(f);
148     f->bold = false; f->italic = false;
149 }
150 
151 static void
del_font_group(FontGroup * fg)152 del_font_group(FontGroup *fg) {
153     free(fg->canvas.buf); fg->canvas.buf = NULL; fg->canvas = (Canvas){0};
154     fg->sprite_map = free_sprite_map(fg->sprite_map);
155     for (size_t i = 0; i < fg->fonts_count; i++) del_font(fg->fonts + i);
156     free(fg->fonts); fg->fonts = NULL;
157 }
158 
159 static void
trim_unused_font_groups(void)160 trim_unused_font_groups(void) {
161     save_window_font_groups();
162     size_t i = 0;
163     while (i < num_font_groups) {
164         if (font_group_is_unused(font_groups + i)) {
165             del_font_group(font_groups + i);
166             size_t num_to_right = (--num_font_groups) - i;
167             if (!num_to_right) break;
168             memmove(font_groups + i, font_groups + 1 + i, num_to_right * sizeof(FontGroup));
169         } else i++;
170     }
171     restore_window_font_groups();
172 }
173 
174 static void
add_font_group(void)175 add_font_group(void) {
176     if (num_font_groups) trim_unused_font_groups();
177     if (num_font_groups >= font_groups_capacity) {
178         save_window_font_groups();
179         font_groups_capacity += 5;
180         font_groups = realloc(font_groups, sizeof(FontGroup) * font_groups_capacity);
181         if (font_groups == NULL) fatal("Out of memory creating a new font group");
182         restore_window_font_groups();
183     }
184     num_font_groups++;
185 }
186 
187 static FontGroup*
font_group_for(double font_sz_in_pts,double logical_dpi_x,double logical_dpi_y)188 font_group_for(double font_sz_in_pts, double logical_dpi_x, double logical_dpi_y) {
189     for (size_t i = 0; i < num_font_groups; i++) {
190         FontGroup *fg = font_groups + i;
191         if (fg->font_sz_in_pts == font_sz_in_pts && fg->logical_dpi_x == logical_dpi_x && fg->logical_dpi_y == logical_dpi_y) return fg;
192     }
193     add_font_group();
194     FontGroup *fg = font_groups + num_font_groups - 1;
195     zero_at_ptr(fg);
196     fg->font_sz_in_pts = font_sz_in_pts;
197     fg->logical_dpi_x = logical_dpi_x;
198     fg->logical_dpi_y = logical_dpi_y;
199     fg->id = ++font_group_id_counter;
200     initialize_font_group(fg);
201     return fg;
202 }
203 
204 
205 
206 // Sprites {{{
207 
208 static void
sprite_map_set_error(int error)209 sprite_map_set_error(int error) {
210     switch(error) {
211         case 1:
212             PyErr_NoMemory(); break;
213         case 2:
214             PyErr_SetString(PyExc_RuntimeError, "Out of texture space for sprites"); break;
215         default:
216             PyErr_SetString(PyExc_RuntimeError, "Unknown error occurred while allocating sprites"); break;
217     }
218 }
219 
220 void
sprite_tracker_set_limits(size_t max_texture_size_,size_t max_array_len_)221 sprite_tracker_set_limits(size_t max_texture_size_, size_t max_array_len_) {
222     max_texture_size = max_texture_size_;
223     max_array_len = MIN(0xfffu, max_array_len_);
224 }
225 
226 static void
do_increment(FontGroup * fg,int * error)227 do_increment(FontGroup *fg, int *error) {
228     fg->sprite_tracker.x++;
229     if (fg->sprite_tracker.x >= fg->sprite_tracker.xnum) {
230         fg->sprite_tracker.x = 0; fg->sprite_tracker.y++;
231         fg->sprite_tracker.ynum = MIN(MAX(fg->sprite_tracker.ynum, fg->sprite_tracker.y + 1), fg->sprite_tracker.max_y);
232         if (fg->sprite_tracker.y >= fg->sprite_tracker.max_y) {
233             fg->sprite_tracker.y = 0; fg->sprite_tracker.z++;
234             if (fg->sprite_tracker.z >= MIN((size_t)UINT16_MAX, max_array_len)) *error = 2;
235         }
236     }
237 }
238 
239 
240 static SpritePosition*
sprite_position_for(FontGroup * fg,Font * font,glyph_index * glyphs,unsigned glyph_count,uint8_t ligature_index,unsigned cell_count,int * error)241 sprite_position_for(FontGroup *fg, Font *font, glyph_index *glyphs, unsigned glyph_count, uint8_t ligature_index, unsigned cell_count, int *error) {
242     bool created;
243     SpritePosition *s = find_or_create_sprite_position(&font->sprite_position_hash_table, glyphs, glyph_count, ligature_index, cell_count, &created);
244     if (!s) { *error = 1; return NULL; }
245     if (created) {
246         s->x = fg->sprite_tracker.x; s->y = fg->sprite_tracker.y; s->z = fg->sprite_tracker.z;
247         do_increment(fg, error);
248     }
249     return s;
250 }
251 
252 void
sprite_tracker_current_layout(FONTS_DATA_HANDLE data,unsigned int * x,unsigned int * y,unsigned int * z)253 sprite_tracker_current_layout(FONTS_DATA_HANDLE data, unsigned int *x, unsigned int *y, unsigned int *z) {
254     FontGroup *fg = (FontGroup*)data;
255     *x = fg->sprite_tracker.xnum; *y = fg->sprite_tracker.ynum; *z = fg->sprite_tracker.z;
256 }
257 
258 
259 static void
sprite_tracker_set_layout(GPUSpriteTracker * sprite_tracker,unsigned int cell_width,unsigned int cell_height)260 sprite_tracker_set_layout(GPUSpriteTracker *sprite_tracker, unsigned int cell_width, unsigned int cell_height) {
261     sprite_tracker->xnum = MIN(MAX(1u, max_texture_size / cell_width), (size_t)UINT16_MAX);
262     sprite_tracker->max_y = MIN(MAX(1u, max_texture_size / cell_height), (size_t)UINT16_MAX);
263     sprite_tracker->ynum = 1;
264     sprite_tracker->x = 0; sprite_tracker->y = 0; sprite_tracker->z = 0;
265 }
266 // }}}
267 
268 static PyObject*
desc_to_face(PyObject * desc,FONTS_DATA_HANDLE fg)269 desc_to_face(PyObject *desc, FONTS_DATA_HANDLE fg) {
270     PyObject *d = specialize_font_descriptor(desc, fg);
271     if (d == NULL) return NULL;
272     PyObject *ans = face_from_descriptor(d, fg);
273     Py_DECREF(d);
274     return ans;
275 }
276 
277 static bool
init_font(Font * f,PyObject * face,bool bold,bool italic,bool emoji_presentation)278 init_font(Font *f, PyObject *face, bool bold, bool italic, bool emoji_presentation) {
279     f->face = face; Py_INCREF(f->face);
280     f->bold = bold; f->italic = italic; f->emoji_presentation = emoji_presentation;
281     f->num_ffs_hb_features = 0;
282     const char *psname = postscript_name_for_face(face);
283     if (font_feature_settings != NULL){
284         PyObject* o = PyDict_GetItemString(font_feature_settings, psname);
285         if (o != NULL && PyTuple_Check(o)) {
286             Py_ssize_t len = PyTuple_GET_SIZE(o);
287             if (len > 0) {
288                 f->num_ffs_hb_features = len + 1;
289                 f->ffs_hb_features = calloc(f->num_ffs_hb_features, sizeof(hb_feature_t));
290                 if (!f->ffs_hb_features) return false;
291                 for (Py_ssize_t i = 0; i < len; i++) {
292                     PyObject* parsed = PyObject_GetAttrString(PyTuple_GET_ITEM(o, i), "parsed");
293                     if (parsed) {
294                         memcpy(f->ffs_hb_features + i, PyBytes_AS_STRING(parsed), sizeof(hb_feature_t));
295                         Py_DECREF(parsed);
296                     }
297                 }
298                 memcpy(f->ffs_hb_features + len, &hb_features[CALT_FEATURE], sizeof(hb_feature_t));
299             }
300         }
301     }
302     if (!f->num_ffs_hb_features) {
303         f->ffs_hb_features = calloc(4, sizeof(hb_feature_t));
304         if (!f->ffs_hb_features) return false;
305         if (strstr(psname, "NimbusMonoPS-") == psname) {
306             memcpy(f->ffs_hb_features + f->num_ffs_hb_features++, &hb_features[LIGA_FEATURE], sizeof(hb_feature_t));
307             memcpy(f->ffs_hb_features + f->num_ffs_hb_features++, &hb_features[DLIG_FEATURE], sizeof(hb_feature_t));
308         }
309         memcpy(f->ffs_hb_features + f->num_ffs_hb_features++, &hb_features[CALT_FEATURE], sizeof(hb_feature_t));
310     }
311     return true;
312 }
313 
314 static void
free_font_groups(void)315 free_font_groups(void) {
316     if (font_groups) {
317         for (size_t i = 0; i < num_font_groups; i++) del_font_group(font_groups + i);
318         free(font_groups); font_groups = NULL;
319         font_groups_capacity = 0; num_font_groups = 0;
320     }
321     free_glyph_cache_global_resources();
322 }
323 
324 static void
python_send_to_gpu(FONTS_DATA_HANDLE fg,unsigned int x,unsigned int y,unsigned int z,pixel * buf)325 python_send_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigned int z, pixel* buf) {
326     if (python_send_to_gpu_impl) {
327         if (!num_font_groups) fatal("Cannot call send to gpu with no font groups");
328         PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIN", x, y, z, PyBytes_FromStringAndSize((const char*)buf, sizeof(pixel) * fg->cell_width * fg->cell_height));
329         if (ret == NULL) PyErr_Print();
330         else Py_DECREF(ret);
331     }
332 }
333 
334 
335 static void
calc_cell_metrics(FontGroup * fg)336 calc_cell_metrics(FontGroup *fg) {
337     unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
338     cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness);
339     if (!cell_width) fatal("Failed to calculate cell width for the specified font");
340     unsigned int before_cell_height = cell_height;
341     int cw = cell_width, ch = cell_height;
342     if (OPT(adjust_line_height_px) != 0) ch += OPT(adjust_line_height_px);
343     if (OPT(adjust_line_height_frac) != 0.f) ch = (int)(ch * OPT(adjust_line_height_frac));
344     if (OPT(adjust_column_width_px != 0)) cw += OPT(adjust_column_width_px);
345     if (OPT(adjust_column_width_frac) != 0.f) cw = (int)(cw * OPT(adjust_column_width_frac));
346 #define MAX_DIM 1000
347 #define MIN_WIDTH 2
348 #define MIN_HEIGHT 4
349     if (cw >= MIN_WIDTH && cw <= MAX_DIM) cell_width = cw;
350     else log_error("Cell width invalid after adjustment, ignoring adjust_column_width");
351     if (ch >= MIN_HEIGHT && ch <= MAX_DIM) cell_height = ch;
352     else log_error("Cell height invalid after adjustment, ignoring adjust_line_height");
353     int line_height_adjustment = cell_height - before_cell_height;
354     if (cell_height < MIN_HEIGHT) fatal("Line height too small: %u", cell_height);
355     if (cell_height > MAX_DIM) fatal("Line height too large: %u", cell_height);
356     if (cell_width < MIN_WIDTH) fatal("Cell width too small: %u", cell_width);
357     if (cell_width > MAX_DIM) fatal("Cell width too large: %u", cell_width);
358 #undef MIN_WIDTH
359 #undef MIN_HEIGHT
360 #undef MAX_DIM
361     underline_position = MIN(cell_height - 1, underline_position);
362     // ensure there is at least a couple of pixels available to render styled underlines
363     while (underline_position > baseline + 1 && cell_height - underline_position < 2) underline_position--;
364     if (line_height_adjustment > 1) {
365         baseline += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2);
366         underline_position += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2);
367     }
368     sprite_tracker_set_layout(&fg->sprite_tracker, cell_width, cell_height);
369     fg->cell_width = cell_width; fg->cell_height = cell_height;
370     fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness, fg->strikethrough_position = strikethrough_position, fg->strikethrough_thickness = strikethrough_thickness;
371     ensure_canvas_can_fit(fg, 8);
372 }
373 
374 static bool
face_has_codepoint(PyObject * face,char_type cp)375 face_has_codepoint(PyObject* face, char_type cp) {
376     return glyph_id_for_codepoint(face, cp) > 0;
377 }
378 
379 static bool
has_emoji_presentation(CPUCell * cpu_cell,GPUCell * gpu_cell)380 has_emoji_presentation(CPUCell *cpu_cell, GPUCell *gpu_cell) {
381     return (gpu_cell->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cell->ch) && cpu_cell->cc_idx[0] != VS15;
382 }
383 
384 static bool
has_cell_text(Font * self,CPUCell * cell)385 has_cell_text(Font *self, CPUCell *cell) {
386     if (!face_has_codepoint(self->face, cell->ch)) return false;
387     char_type combining_chars[arraysz(cell->cc_idx)];
388     unsigned num_cc = 0;
389     for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
390         if (cell->cc_idx[i] == VS15 || cell->cc_idx[i] == VS16) continue;
391         combining_chars[num_cc++] = codepoint_for_mark(cell->cc_idx[i]);
392     }
393     if (num_cc == 0) return true;
394     if (num_cc == 1) {
395         if (face_has_codepoint(self->face, combining_chars[0])) return true;
396         char_type ch = 0;
397         if (hb_unicode_compose(hb_unicode_funcs_get_default(), cell->ch, combining_chars[0], &ch) && face_has_codepoint(self->face, ch)) return true;
398         return false;
399     }
400     for (unsigned i = 0; i < num_cc; i++) {
401         if (!face_has_codepoint(self->face, combining_chars[i])) return false;
402     }
403     return true;
404 }
405 
406 static void
output_cell_fallback_data(CPUCell * cell,bool bold,bool italic,bool emoji_presentation,PyObject * face,bool new_face)407 output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) {
408     printf("U+%x ", cell->ch);
409     for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
410         printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
411     }
412     if (bold) printf("bold ");
413     if (italic) printf("italic ");
414     if (emoji_presentation) printf("emoji_presentation ");
415     PyObject_Print(face, stdout, 0);
416     if (new_face) printf(" (new face)");
417     printf("\n");
418 }
419 
420 static ssize_t
load_fallback_font(FontGroup * fg,CPUCell * cell,bool bold,bool italic,bool emoji_presentation)421 load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool emoji_presentation) {
422     if (fg->fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; }
423     ssize_t f;
424 
425     if (bold) f = fg->italic_font_idx > 0 ? fg->bi_font_idx : fg->bold_font_idx;
426     else f = italic ? fg->italic_font_idx : fg->medium_font_idx;
427     if (f < 0) f = fg->medium_font_idx;
428 
429     PyObject *face = create_fallback_face(fg->fonts[f].face, cell, bold, italic, emoji_presentation, (FONTS_DATA_HANDLE)fg);
430     if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
431     if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
432     if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, face, true);
433     set_size_for_face(face, fg->cell_height, true, (FONTS_DATA_HANDLE)fg);
434 
435     ensure_space_for(fg, fonts, Font, fg->fonts_count + 1, fonts_capacity, 5, true);
436     ssize_t ans = fg->first_fallback_font_idx + fg->fallback_fonts_count;
437     Font *af = &fg->fonts[ans];
438     if (!init_font(af, face, bold, italic, emoji_presentation)) fatal("Out of memory");
439     Py_DECREF(face);
440     if (!has_cell_text(af, cell)) {
441         if (global_state.debug_font_fallback) {
442             printf("The font chosen by the OS for the text: ");
443             printf("U+%x ", cell->ch);
444             for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
445                 printf("U+%x ", codepoint_for_mark(cell->cc_idx[i]));
446             }
447             printf("is ");
448             PyObject_Print(af->face, stdout, 0);
449             printf(" but it does not actually contain glyphs for that text\n");
450         }
451         del_font(af);
452         return MISSING_FONT;
453     }
454     fg->fallback_fonts_count++;
455     fg->fonts_count++;
456     return ans;
457 }
458 
459 static ssize_t
fallback_font(FontGroup * fg,CPUCell * cpu_cell,GPUCell * gpu_cell)460 fallback_font(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
461     bool bold = (gpu_cell->attrs >> BOLD_SHIFT) & 1;
462     bool italic = (gpu_cell->attrs >> ITALIC_SHIFT) & 1;
463     bool emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
464 
465     // Check if one of the existing fallback fonts has this text
466     for (size_t i = 0, j = fg->first_fallback_font_idx; i < fg->fallback_fonts_count; i++, j++)  {
467         Font *ff = fg->fonts +j;
468         if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cpu_cell)) {
469             if (global_state.debug_font_fallback) output_cell_fallback_data(cpu_cell, bold, italic, emoji_presentation, ff->face, false);
470             return j;
471         }
472     }
473 
474     return load_fallback_font(fg, cpu_cell, bold, italic, emoji_presentation);
475 }
476 
477 static ssize_t
in_symbol_maps(FontGroup * fg,char_type ch)478 in_symbol_maps(FontGroup *fg, char_type ch) {
479     for (size_t i = 0; i < num_symbol_maps; i++) {
480         if (symbol_maps[i].left <= ch && ch <= symbol_maps[i].right) return fg->first_symbol_font_idx + symbol_maps[i].font_idx;
481     }
482     return NO_FONT;
483 }
484 
485 
486 // Decides which 'font' to use for a given cell.
487 //
488 // Possible results:
489 // - NO_FONT
490 // - MISSING_FONT
491 // - BLANK_FONT
492 // - BOX_FONT
493 // - an index in the fonts list
494 static ssize_t
font_for_cell(FontGroup * fg,CPUCell * cpu_cell,GPUCell * gpu_cell,bool * is_fallback_font,bool * is_emoji_presentation)495 font_for_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell, bool *is_fallback_font, bool *is_emoji_presentation) {
496     *is_fallback_font = false;
497     *is_emoji_presentation = false;
498 START_ALLOW_CASE_RANGE
499     ssize_t ans;
500     switch(cpu_cell->ch) {
501         case 0:
502         case ' ':
503         case '\t':
504             return BLANK_FONT;
505         case 0x2500 ... 0x2573:
506         case 0x2574 ... 0x259f:
507         case 0x2800 ... 0x28ff:
508         case 0xe0b0 ... 0xe0bf:  // powerline box drawing
509         case 0x1fb00 ... 0x1fb8b:  // symbols for legacy computing
510         case 0x1fba0 ... 0x1fbae:
511             return BOX_FONT;
512         default:
513             ans = in_symbol_maps(fg, cpu_cell->ch);
514             if (ans > -1) return ans;
515             switch(BI_VAL(gpu_cell->attrs)) {
516                 case 0:
517                     ans = fg->medium_font_idx; break;
518                 case 1:
519                     ans = fg->bold_font_idx ; break;
520                 case 2:
521                     ans = fg->italic_font_idx; break;
522                 case 3:
523                     ans = fg->bi_font_idx; break;
524             }
525             if (ans < 0) ans = fg->medium_font_idx;
526             *is_emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
527             if (!*is_emoji_presentation && has_cell_text(fg->fonts + ans, cpu_cell)) return ans;
528             *is_fallback_font = true;
529             return fallback_font(fg, cpu_cell, gpu_cell);
530     }
531 END_ALLOW_CASE_RANGE
532 }
533 
534 static void
set_sprite(GPUCell * cell,sprite_index x,sprite_index y,sprite_index z)535 set_sprite(GPUCell *cell, sprite_index x, sprite_index y, sprite_index z) {
536     cell->sprite_x = x; cell->sprite_y = y; cell->sprite_z = z;
537 }
538 
539 // Gives a unique (arbitrary) id to a box glyph
540 static glyph_index
box_glyph_id(char_type ch)541 box_glyph_id(char_type ch) {
542 START_ALLOW_CASE_RANGE
543     switch(ch) {
544         case 0x2500 ... 0x259f:
545             return ch - 0x2500; // IDs from 0x00 to 0x9f
546         case 0xe0b0 ... 0xe0d4:
547             return 0xa0 + ch - 0xe0b0; // IDs from 0xa0 to 0xc4
548         case 0x1fb00 ... 0x1fb8b:
549             return 0xc5 + ch - 0x1fb00; // IDs from 0xc5 to 0x150
550         case 0x1fba0 ... 0x1fbae:  // IDs from 0x151 to 0x15f
551             return 0x151 + ch - 0x1fba0;
552         case 0x2800 ... 0x28ff:
553             return 0x160 + ch - 0x2800;
554         default:
555             return 0xffff;
556     }
557 END_ALLOW_CASE_RANGE
558 }
559 
560 static PyObject* box_drawing_function = NULL, *prerender_function = NULL, *descriptor_for_idx = NULL;
561 
562 void
render_alpha_mask(const uint8_t * alpha_mask,pixel * dest,Region * src_rect,Region * dest_rect,size_t src_stride,size_t dest_stride)563 render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {
564     for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {
565         pixel *d = dest + dest_stride * dr;
566         const uint8_t *s = alpha_mask + src_stride * sr;
567         for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {
568             uint8_t src_alpha = d[dc] & 0xff;
569             uint8_t alpha = s[sc];
570             d[dc] = 0xffffff00 | MAX(alpha, src_alpha);
571         }
572     }
573 }
574 
575 static void
render_box_cell(FontGroup * fg,CPUCell * cpu_cell,GPUCell * gpu_cell)576 render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
577     int error = 0;
578     glyph_index glyph = box_glyph_id(cpu_cell->ch);
579     SpritePosition *sp = sprite_position_for(fg, &fg->fonts[BOX_FONT], &glyph, 1, 0, 1, &error);
580     if (sp == NULL) {
581         sprite_map_set_error(error); PyErr_Print();
582         set_sprite(gpu_cell, 0, 0, 0);
583         return;
584     }
585     set_sprite(gpu_cell, sp->x, sp->y, sp->z);
586     if (sp->rendered) return;
587     sp->rendered = true;
588     sp->colored = false;
589     PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cpu_cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0);
590     if (ret == NULL) { PyErr_Print(); return; }
591     uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0));
592     ensure_canvas_can_fit(fg, 1);
593     Region r = { .right = fg->cell_width, .bottom = fg->cell_height };
594     render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->cell_width, fg->cell_width);
595     current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp->x, sp->y, sp->z, fg->canvas.buf);
596     Py_DECREF(ret);
597 }
598 
599 static void
load_hb_buffer(CPUCell * first_cpu_cell,GPUCell * first_gpu_cell,index_type num_cells)600 load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells) {
601     index_type num;
602     hb_buffer_clear_contents(harfbuzz_buffer);
603     while (num_cells) {
604         attrs_type prev_width = 0;
605         for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cpu_cell->cc_idx); first_cpu_cell++, first_gpu_cell++, num_cells--) {
606             if (prev_width == 2) { prev_width = 0; continue; }
607             shape_buffer[num++] = first_cpu_cell->ch;
608             prev_width = first_gpu_cell->attrs & WIDTH_MASK;
609             for (unsigned i = 0; i < arraysz(first_cpu_cell->cc_idx) && first_cpu_cell->cc_idx[i]; i++) {
610                 shape_buffer[num++] = codepoint_for_mark(first_cpu_cell->cc_idx[i]);
611             }
612         }
613         hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num);
614     }
615     hb_buffer_guess_segment_properties(harfbuzz_buffer);
616     if (OPT(force_ltr)) hb_buffer_set_direction(harfbuzz_buffer, HB_DIRECTION_LTR);
617 }
618 
619 
620 static void
set_cell_sprite(GPUCell * cell,const SpritePosition * sp)621 set_cell_sprite(GPUCell *cell, const SpritePosition *sp) {
622     cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z;
623     if (sp->colored) cell->sprite_z |= 0x4000;
624 }
625 
626 static pixel*
extract_cell_from_canvas(FontGroup * fg,unsigned int i,unsigned int num_cells)627 extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells) {
628     pixel *ans = fg->canvas.buf + (fg->cell_width * fg->cell_height * (fg->canvas.current_cells - 1)), *dest = ans, *src = fg->canvas.buf + (i * fg->cell_width);
629     unsigned int stride = fg->cell_width * num_cells;
630     for (unsigned int r = 0; r < fg->cell_height; r++, dest += fg->cell_width, src += stride) memcpy(dest, src, fg->cell_width * sizeof(fg->canvas.buf[0]));
631     return ans;
632 }
633 
634 typedef struct GlyphRenderScratch {
635     SpritePosition* *sprite_positions;
636     glyph_index *glyphs;
637     size_t sz;
638 } GlyphRenderScratch;
639 static GlyphRenderScratch global_glyph_render_scratch = {0};
640 
641 static void
render_group(FontGroup * fg,unsigned int num_cells,unsigned int num_glyphs,CPUCell * cpu_cells,GPUCell * gpu_cells,hb_glyph_info_t * info,hb_glyph_position_t * positions,Font * font,glyph_index * glyphs,unsigned glyph_count,bool center_glyph)642 render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index *glyphs, unsigned glyph_count, bool center_glyph) {
643 #define sp global_glyph_render_scratch.sprite_positions
644     int error = 0;
645     bool all_rendered = true;
646     bool is_infinite_ligature = num_cells > 9 && num_glyphs == num_cells;
647     for (unsigned i = 0, ligature_index = 0; i < num_cells; i++) {
648         bool is_repeat_glyph = is_infinite_ligature && i > 1 && i + 1 < num_cells && glyphs[i] == glyphs[i-1] && glyphs[i] == glyphs[i-2] && glyphs[i] == glyphs[i+1];
649         if (is_repeat_glyph) {
650             sp[i] = sp[i-1];
651         } else {
652             sp[i] = sprite_position_for(fg, font, glyphs, glyph_count, ligature_index++, num_cells, &error);
653         }
654         if (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; }
655         if (!sp[i]->rendered) all_rendered = false;
656     }
657     if (all_rendered) {
658         for (unsigned i = 0; i < num_cells; i++) { set_cell_sprite(gpu_cells + i, sp[i]); }
659         return;
660     }
661 
662     ensure_canvas_can_fit(fg, num_cells + 1);
663     bool was_colored = (gpu_cells->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cells->ch);
664     render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, fg->canvas.buf, fg->cell_width, fg->cell_height, num_cells, fg->baseline, &was_colored, (FONTS_DATA_HANDLE)fg, center_glyph);
665     if (PyErr_Occurred()) PyErr_Print();
666 
667     for (unsigned i = 0; i < num_cells; i++) {
668         if (!sp[i]->rendered) {
669             sp[i]->rendered = true;
670             sp[i]->colored = was_colored;
671             pixel *buf = num_cells == 1 ? fg->canvas.buf : extract_cell_from_canvas(fg, i, num_cells);
672             current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, buf);
673         }
674         set_cell_sprite(gpu_cells + i, sp[i]);
675     }
676 #undef sp
677 }
678 
679 typedef struct {
680     CPUCell *cpu_cell;
681     GPUCell *gpu_cell;
682     unsigned int num_codepoints;
683     unsigned int codepoints_consumed;
684     char_type current_codepoint;
685 } CellData;
686 
687 typedef struct {
688     unsigned int first_glyph_idx, first_cell_idx, num_glyphs, num_cells;
689     bool has_special_glyph, is_space_ligature, started_with_infinite_ligature;
690 } Group;
691 
692 typedef struct {
693     uint32_t previous_cluster;
694     bool prev_was_special, prev_was_empty;
695     CellData current_cell_data;
696     Group *groups;
697     size_t groups_capacity, group_idx, glyph_idx, cell_idx, num_cells, num_glyphs;
698     CPUCell *first_cpu_cell, *last_cpu_cell;
699     GPUCell *first_gpu_cell, *last_gpu_cell;
700     hb_glyph_info_t *info;
701     hb_glyph_position_t *positions;
702 } GroupState;
703 
704 static GroupState group_state = {0};
705 
706 static unsigned int
num_codepoints_in_cell(CPUCell * cell)707 num_codepoints_in_cell(CPUCell *cell) {
708     unsigned int ans = 1;
709     for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++;
710     return ans;
711 }
712 
713 static void
shape(CPUCell * first_cpu_cell,GPUCell * first_gpu_cell,index_type num_cells,hb_font_t * font,Font * fobj,bool disable_ligature)714 shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb_font_t *font, Font *fobj, bool disable_ligature) {
715     if (group_state.groups_capacity <= 2 * num_cells) {
716         group_state.groups_capacity = MAX(128u, 2 * num_cells);  // avoid unnecessary reallocs
717         group_state.groups = realloc(group_state.groups, sizeof(Group) * group_state.groups_capacity);
718         if (!group_state.groups) fatal("Out of memory");
719     }
720     group_state.previous_cluster = UINT32_MAX;
721     group_state.prev_was_special = false;
722     group_state.prev_was_empty = false;
723     group_state.current_cell_data.cpu_cell = first_cpu_cell;
724     group_state.current_cell_data.gpu_cell = first_gpu_cell;
725     group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cpu_cell);
726     group_state.current_cell_data.codepoints_consumed = 0;
727     group_state.current_cell_data.current_codepoint = first_cpu_cell->ch;
728     zero_at_ptr_count(group_state.groups, group_state.groups_capacity);
729     group_state.group_idx = 0;
730     group_state.glyph_idx = 0;
731     group_state.cell_idx = 0;
732     group_state.num_cells = num_cells;
733     group_state.first_cpu_cell = first_cpu_cell;
734     group_state.first_gpu_cell = first_gpu_cell;
735     group_state.last_cpu_cell = first_cpu_cell + (num_cells ? num_cells - 1 : 0);
736     group_state.last_gpu_cell = first_gpu_cell + (num_cells ? num_cells - 1 : 0);
737     load_hb_buffer(first_cpu_cell, first_gpu_cell, num_cells);
738 
739     size_t num_features = fobj->num_ffs_hb_features;
740     if (num_features && !disable_ligature) num_features--;  // the last feature is always -calt
741     hb_shape(font, harfbuzz_buffer, fobj->ffs_hb_features, num_features);
742 
743     unsigned int info_length, positions_length;
744     group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length);
745     group_state.positions = hb_buffer_get_glyph_positions(harfbuzz_buffer, &positions_length);
746     if (!group_state.info || !group_state.positions) group_state.num_glyphs = 0;
747     else group_state.num_glyphs = MIN(info_length, positions_length);
748 }
749 
750 static bool
is_special_glyph(glyph_index glyph_id,Font * font,CellData * cell_data)751 is_special_glyph(glyph_index glyph_id, Font *font, CellData* cell_data) {
752     // A glyph is special if the codepoint it corresponds to matches a
753     // different glyph in the font
754     GlyphProperties *s = find_or_create_glyph_properties(&font->glyph_properties_hash_table, glyph_id);
755     if (s == NULL) return false;
756     if (!(s->data & SPECIAL_FILLED_MASK)) {
757         bool is_special = cell_data->current_codepoint ? (
758             glyph_id != glyph_id_for_codepoint(font->face, cell_data->current_codepoint) ? true : false)
759             :
760             false;
761         uint8_t val = is_special ? SPECIAL_VALUE_MASK : 0;
762         s->data |= val | SPECIAL_FILLED_MASK;
763     }
764     return s->data & SPECIAL_VALUE_MASK;
765 }
766 
767 static bool
is_empty_glyph(glyph_index glyph_id,Font * font)768 is_empty_glyph(glyph_index glyph_id, Font *font) {
769     // A glyph is empty if its metrics have a width of zero
770     GlyphProperties *s = find_or_create_glyph_properties(&font->glyph_properties_hash_table, glyph_id);
771     if (s == NULL) return false;
772     if (!(s->data & EMPTY_FILLED_MASK)) {
773         uint8_t val = is_glyph_empty(font->face, glyph_id) ? EMPTY_VALUE_MASK : 0;
774         s->data |= val | EMPTY_FILLED_MASK;
775     }
776     return s->data & EMPTY_VALUE_MASK;
777 }
778 
779 static unsigned int
check_cell_consumed(CellData * cell_data,CPUCell * last_cpu_cell)780 check_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell) {
781     cell_data->codepoints_consumed++;
782     if (cell_data->codepoints_consumed >= cell_data->num_codepoints) {
783         attrs_type width = cell_data->gpu_cell->attrs & WIDTH_MASK;
784         cell_data->cpu_cell += MAX(1, width);
785         cell_data->gpu_cell += MAX(1, width);
786         cell_data->codepoints_consumed = 0;
787         if (cell_data->cpu_cell <= last_cpu_cell) {
788             cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cpu_cell);
789             cell_data->current_codepoint = cell_data->cpu_cell->ch;
790         } else cell_data->current_codepoint = 0;
791         return width;
792     } else {
793         switch(cell_data->codepoints_consumed) {
794             case 0:
795                 cell_data->current_codepoint = cell_data->cpu_cell->ch;
796                 break;
797             default: {
798                 index_type mark = cell_data->cpu_cell->cc_idx[cell_data->codepoints_consumed - 1];
799                 // VS15/16 cause rendering to break, as they get marked as
800                 // special glyphs, so map to 0, to avoid that
801                 cell_data->current_codepoint = (mark == VS15 || mark == VS16) ? 0 : codepoint_for_mark(mark);
802                 break;
803             }
804         }
805     }
806     return 0;
807 }
808 
809 static LigatureType
ligature_type_from_glyph_name(const char * glyph_name,SpacerStrategy strategy)810 ligature_type_from_glyph_name(const char *glyph_name, SpacerStrategy strategy) {
811     const char *p, *m, *s, *e;
812     if (strategy == SPACERS_IOSEVKA) {
813         p = strrchr(glyph_name, '.');
814         m = ".join-m"; s = ".join-l"; e = ".join-r";
815     } else {
816         p = strrchr(glyph_name, '_');
817         m = "_middle.seq"; s = "_start.seq"; e = "_end.seq";
818     }
819     if (p) {
820         if (strcmp(p, m) == 0) return INFINITE_LIGATURE_MIDDLE;
821         if (strcmp(p, s) == 0) return INFINITE_LIGATURE_START;
822         if (strcmp(p, e) == 0) return INFINITE_LIGATURE_END;
823     }
824     return LIGATURE_UNKNOWN;
825 }
826 
827 #define G(x) (group_state.x)
828 
829 static void
detect_spacer_strategy(hb_font_t * hbf,Font * font)830 detect_spacer_strategy(hb_font_t *hbf, Font *font) {
831     CPUCell cpu_cells[3] = {{.ch = '='}, {.ch = '='}, {.ch = '='}};
832     GPUCell gpu_cells[3] = {{.attrs = 1}, {.attrs = 1}, {.attrs = 1}};
833     shape(cpu_cells, gpu_cells, arraysz(cpu_cells), hbf, font, false);
834     font->spacer_strategy = SPACERS_BEFORE;
835     if (G(num_glyphs) > 1) {
836         glyph_index glyph_id = G(info)[G(num_glyphs) - 1].codepoint;
837         bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));
838         bool is_empty = is_special && is_empty_glyph(glyph_id, font);
839         if (is_empty) font->spacer_strategy = SPACERS_AFTER;
840     }
841     shape(cpu_cells, gpu_cells, 2, hbf, font, false);
842     if (G(num_glyphs)) {
843         char glyph_name[128]; glyph_name[arraysz(glyph_name)-1] = 0;
844         for (unsigned i = 0; i < G(num_glyphs); i++) {
845             glyph_index glyph_id = G(info)[i].codepoint;
846             hb_font_glyph_to_string(hbf, glyph_id, glyph_name, arraysz(glyph_name)-1);
847             char *dot = strrchr(glyph_name, '.');
848             if (dot && (!strcmp(dot, ".join-l") || !strcmp(dot, ".join-r") || !strcmp(dot, ".join-m"))) {
849                 font->spacer_strategy = SPACERS_IOSEVKA;
850                 break;
851             }
852         }
853     }
854 }
855 
856 static LigatureType
ligature_type_for_glyph(hb_font_t * hbf,glyph_index glyph_id,SpacerStrategy strategy)857 ligature_type_for_glyph(hb_font_t *hbf, glyph_index glyph_id, SpacerStrategy strategy) {
858     static char glyph_name[128]; glyph_name[arraysz(glyph_name)-1] = 0;
859     hb_font_glyph_to_string(hbf, glyph_id, glyph_name, arraysz(glyph_name)-1);
860     return ligature_type_from_glyph_name(glyph_name, strategy);
861 }
862 
863 #define L INFINITE_LIGATURE_START
864 #define M INFINITE_LIGATURE_MIDDLE
865 #define R INFINITE_LIGATURE_END
866 #define I LIGATURE_UNKNOWN
867 static bool
is_iosevka_lig_starter(LigatureType before,LigatureType current,LigatureType after)868 is_iosevka_lig_starter(LigatureType before, LigatureType current, LigatureType after) {
869     return (current == R || (current == I && (after == L || after == M))) \
870                      && \
871            !(before == R || before == M);
872 }
873 
874 static bool
is_iosevka_lig_ender(LigatureType before,LigatureType current,LigatureType after)875 is_iosevka_lig_ender(LigatureType before, LigatureType current, LigatureType after) {
876     return (current == L || (current == I && (before == R || before == M))) \
877                      && \
878             !(after == L || after == M);
879 }
880 #undef L
881 #undef M
882 #undef R
883 #undef I
884 
885 static LigatureType *ligature_types = NULL;
886 static size_t ligature_types_sz = 0;
887 
888 static void
group_iosevka(Font * font,hb_font_t * hbf)889 group_iosevka(Font *font, hb_font_t *hbf) {
890     // Group as per algorithm discussed in: https://github.com/be5invis/Iosevka/issues/1007
891     if (ligature_types_sz <= G(num_glyphs)) {
892         ligature_types_sz = G(num_glyphs) + 16;
893         ligature_types = realloc(ligature_types, ligature_types_sz * sizeof(ligature_types[0]));
894         if (!ligature_types) fatal("Out of memory allocating ligature types array");
895     }
896     for (size_t i = G(glyph_idx); i < G(num_glyphs); i++) {
897         ligature_types[i] = ligature_type_for_glyph(hbf, G(info)[i].codepoint, font->spacer_strategy);
898     }
899 
900     uint32_t cluster, next_cluster;
901     while (G(glyph_idx) < G(num_glyphs) && G(cell_idx) < G(num_cells)) {
902         cluster = G(info)[G(glyph_idx)].cluster;
903         uint32_t num_codepoints_used_by_glyph = 0;
904         bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1;
905         Group *current_group = G(groups) + G(group_idx);
906         if (is_last_glyph) {
907             num_codepoints_used_by_glyph = UINT32_MAX;
908             next_cluster = 0;
909         } else {
910             next_cluster = G(info)[G(glyph_idx) + 1].cluster;
911             // RTL languages like Arabic have decreasing cluster numbers
912             if (next_cluster != cluster) num_codepoints_used_by_glyph = cluster > next_cluster ? cluster - next_cluster : next_cluster - cluster;
913         }
914         const LigatureType before = G(glyph_idx) ? ligature_types[G(glyph_idx - 1)] : LIGATURE_UNKNOWN;
915         const LigatureType current = ligature_types[G(glyph_idx)];
916         const LigatureType after = is_last_glyph ? LIGATURE_UNKNOWN : ligature_types[G(glyph_idx + 1)];
917         bool end_current_group = false;
918         if (current_group->num_glyphs && is_iosevka_lig_ender(before, current, after)) {
919             end_current_group = true;
920         }
921         if (!current_group->num_glyphs++) {
922             if (is_iosevka_lig_starter(before, current, after)) current_group->has_special_glyph = true;
923             else end_current_group = true;
924             current_group->first_glyph_idx = G(glyph_idx);
925             current_group->first_cell_idx = G(cell_idx);
926         }
927         if (is_last_glyph) {
928             // soak up all remaining cells
929             if (G(cell_idx) < G(num_cells)) {
930                 unsigned int num_left = G(num_cells) - G(cell_idx);
931                 current_group->num_cells += num_left;
932                 G(cell_idx) += num_left;
933             }
934         } else {
935             unsigned int num_cells_consumed = 0;
936             while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {
937                 unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell));
938                 G(cell_idx) += w;
939                 num_cells_consumed += w;
940                 num_codepoints_used_by_glyph--;
941             }
942             current_group->num_cells += num_cells_consumed;
943         }
944         if (end_current_group) G(group_idx)++;
945         G(glyph_idx)++;
946     }
947 }
948 
949 static void
group_normal(Font * font,hb_font_t * hbf)950 group_normal(Font *font, hb_font_t *hbf) {
951     /* Now distribute the glyphs into groups of cells
952      * Considerations to keep in mind:
953      * Group sizes should be as small as possible for best performance
954      * Combining chars can result in multiple glyphs rendered into a single cell
955      * Emoji and East Asian wide chars can cause a single glyph to be rendered over multiple cells
956      * Ligature fonts, take two common approaches:
957      * 1. ABC becomes EMPTY, EMPTY, WIDE GLYPH this means we have to render N glyphs in N cells (example Fira Code)
958      * 2. ABC becomes WIDE GLYPH this means we have to render one glyph in N cells (example Operator Mono Lig)
959      * 3. ABC becomes WIDE GLYPH, EMPTY, EMPTY this means we have to render N glyphs in N cells (example Cascadia Code)
960      * 4. Variable length ligatures are identified by a glyph naming convention of _start.seq, _middle.seq and _end.seq
961      *    with EMPTY glyphs in the middle or after (both Fira Code and Cascadia Code)
962      *
963      * We rely on the cluster numbers from harfbuzz to tell us how many unicode codepoints a glyph corresponds to.
964      * Then we check if the glyph is a ligature glyph (is_special_glyph) and if it is an empty glyph.
965      * We detect if the font uses EMPTY glyphs before or after ligature glyphs (1. or 3. above) by checking what it does for ===.
966      * Finally we look at the glyph name. These five datapoints give us enough information to satisfy the constraint above,
967      * for a wide variety of fonts.
968      */
969     uint32_t cluster, next_cluster;
970     bool add_to_current_group;
971     bool prev_glyph_was_inifinte_ligature_end = false;
972     while (G(glyph_idx) < G(num_glyphs) && G(cell_idx) < G(num_cells)) {
973         glyph_index glyph_id = G(info)[G(glyph_idx)].codepoint;
974         LigatureType ligature_type = ligature_type_for_glyph(hbf, glyph_id, font->spacer_strategy);
975         cluster = G(info)[G(glyph_idx)].cluster;
976         bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data));
977         bool is_empty = is_special && is_empty_glyph(glyph_id, font);
978         uint32_t num_codepoints_used_by_glyph = 0;
979         bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1;
980         Group *current_group = G(groups) + G(group_idx);
981 
982         if (is_last_glyph) {
983             num_codepoints_used_by_glyph = UINT32_MAX;
984             next_cluster = 0;
985         } else {
986             next_cluster = G(info)[G(glyph_idx) + 1].cluster;
987             // RTL languages like Arabic have decreasing cluster numbers
988             if (next_cluster != cluster) num_codepoints_used_by_glyph = cluster > next_cluster ? cluster - next_cluster : next_cluster - cluster;
989         }
990         if (!current_group->num_glyphs) {
991             add_to_current_group = true;
992         } else if (current_group->started_with_infinite_ligature) {
993             if (prev_glyph_was_inifinte_ligature_end) add_to_current_group = is_empty && font->spacer_strategy == SPACERS_AFTER;
994             else add_to_current_group = ligature_type == INFINITE_LIGATURE_MIDDLE || ligature_type == INFINITE_LIGATURE_END || is_empty;
995         } else {
996             if (is_special) {
997                 if (!current_group->num_cells) add_to_current_group = true;
998                 else if (font->spacer_strategy == SPACERS_BEFORE) add_to_current_group = G(prev_was_empty);
999                 else add_to_current_group = is_empty;
1000             } else {
1001                 add_to_current_group = !G(prev_was_special) || !current_group->num_cells;
1002             }
1003         }
1004 #if 0
1005         char ch[8] = {0};
1006         encode_utf8(G(current_cell_data).current_codepoint, ch);
1007         printf("\x1b[32m→ %s\x1b[m glyph_idx: %zu glyph_id: %u (%s) group_idx: %zu cluster: %u -> %u is_special: %d\n"
1008                 "  num_codepoints_used_by_glyph: %u current_group: (%u cells, %u glyphs) add_to_current_group: %d\n",
1009                 ch, G(glyph_idx), glyph_id, glyph_name, G(group_idx), cluster, next_cluster, is_special,
1010                 num_codepoints_used_by_glyph, current_group->num_cells, current_group->num_glyphs, add_to_current_group);
1011 #endif
1012 
1013         if (!add_to_current_group) { current_group = G(groups) + ++G(group_idx); }
1014         if (!current_group->num_glyphs++) {
1015             if (ligature_type == INFINITE_LIGATURE_START || ligature_type == INFINITE_LIGATURE_MIDDLE) current_group->started_with_infinite_ligature = true;
1016             current_group->first_glyph_idx = G(glyph_idx);
1017             current_group->first_cell_idx = G(cell_idx);
1018         }
1019 #define MOVE_GLYPH_TO_NEXT_GROUP(start_cell_idx) { \
1020     current_group->num_glyphs--; \
1021     G(group_idx)++; current_group = G(groups) + G(group_idx); \
1022     current_group->first_cell_idx = start_cell_idx; \
1023     current_group->num_glyphs = 1; \
1024     current_group->first_glyph_idx = G(glyph_idx); \
1025 }
1026         if (is_special) current_group->has_special_glyph = true;
1027         if (is_last_glyph) {
1028             // soak up all remaining cells
1029             if (G(cell_idx) < G(num_cells)) {
1030                 unsigned int num_left = G(num_cells) - G(cell_idx);
1031                 current_group->num_cells += num_left;
1032                 G(cell_idx) += num_left;
1033             }
1034         } else {
1035             unsigned int num_cells_consumed = 0;
1036             while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) {
1037                 unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell));
1038                 G(cell_idx) += w;
1039                 num_cells_consumed += w;
1040                 num_codepoints_used_by_glyph--;
1041             }
1042             if (num_cells_consumed) {
1043                 current_group->num_cells += num_cells_consumed;
1044                 if (!is_special) {  // not a ligature, end the group
1045                     G(group_idx)++; current_group = G(groups) + G(group_idx);
1046                 }
1047             }
1048         }
1049 
1050         G(prev_was_special) = is_special;
1051         G(prev_was_empty) = is_empty;
1052         G(previous_cluster) = cluster;
1053         prev_glyph_was_inifinte_ligature_end = ligature_type == INFINITE_LIGATURE_END;
1054         G(glyph_idx)++;
1055     }
1056 }
1057 
1058 
1059 static void
shape_run(CPUCell * first_cpu_cell,GPUCell * first_gpu_cell,index_type num_cells,Font * font,bool disable_ligature)1060 shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font, bool disable_ligature) {
1061     hb_font_t *hbf = harfbuzz_font_for_face(font->face);
1062     if (font->spacer_strategy == SPACER_STRATEGY_UNKNOWN) detect_spacer_strategy(hbf, font);
1063     shape(first_cpu_cell, first_gpu_cell, num_cells, hbf, font, disable_ligature);
1064     if (font->spacer_strategy == SPACERS_IOSEVKA) group_iosevka(font, hbf);
1065     else group_normal(font, hbf);
1066 #if 0
1067         static char dbuf[1024];
1068         // You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text
1069         hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, group_state.num_glyphs, dbuf, sizeof(dbuf), NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS);
1070         printf("\n%s\n", dbuf);
1071 #endif
1072 }
1073 
1074 static void
merge_groups_for_pua_space_ligature(void)1075 merge_groups_for_pua_space_ligature(void) {
1076     while (G(group_idx) > 0) {
1077         Group *g = G(groups), *g1 = G(groups) + 1;
1078         g->num_cells += g1->num_cells;
1079         g->num_glyphs += g1->num_glyphs;
1080         G(group_idx)--;
1081     }
1082     G(groups)->is_space_ligature = true;
1083 }
1084 
1085 #undef MOVE_GLYPH_TO_NEXT_GROUP
1086 
1087 static bool
is_group_calt_ligature(const Group * group)1088 is_group_calt_ligature(const Group *group) {
1089     GPUCell *first_cell = G(first_gpu_cell) + group->first_cell_idx;
1090     return group->num_cells > 1 && group->has_special_glyph && (first_cell->attrs & WIDTH_MASK) == 1;
1091 }
1092 
1093 
1094 static void
split_run_at_offset(index_type cursor_offset,index_type * left,index_type * right)1095 split_run_at_offset(index_type cursor_offset, index_type *left, index_type *right) {
1096     *left = 0; *right = 0;
1097     for (unsigned idx = 0; idx < G(group_idx) + 1; idx++) {
1098         Group *group = G(groups) + idx;
1099         if (group->first_cell_idx <= cursor_offset && cursor_offset < group->first_cell_idx + group->num_cells) {
1100             if (is_group_calt_ligature(group)) {
1101                 // likely a calt ligature
1102                 *left = group->first_cell_idx; *right = group->first_cell_idx + group->num_cells;
1103             }
1104             break;
1105         }
1106     }
1107 }
1108 
1109 
1110 static void
render_groups(FontGroup * fg,Font * font,bool center_glyph)1111 render_groups(FontGroup *fg, Font *font, bool center_glyph) {
1112     unsigned idx = 0;
1113     while (idx <= G(group_idx)) {
1114         Group *group = G(groups) + idx;
1115         if (!group->num_cells) break;
1116         /* printf("Group: idx: %u num_cells: %u num_glyphs: %u first_glyph_idx: %u first_cell_idx: %u total_num_glyphs: %zu\n", */
1117         /*         idx, group->num_cells, group->num_glyphs, group->first_glyph_idx, group->first_cell_idx, group_state.num_glyphs); */
1118         if (group->num_glyphs) {
1119             size_t sz = MAX(group->num_glyphs, group->num_cells) + 16;
1120             if (global_glyph_render_scratch.sz < sz) {
1121 #define a(what) free(global_glyph_render_scratch.what); global_glyph_render_scratch.what = malloc(sz * sizeof(global_glyph_render_scratch.what[0])); if (!global_glyph_render_scratch.what) fatal("Out of memory");
1122                 a(glyphs); a(sprite_positions);
1123 #undef a
1124                 global_glyph_render_scratch.sz = sz;
1125             }
1126             for (unsigned i = 0; i < group->num_glyphs; i++) global_glyph_render_scratch.glyphs[i] = G(info)[group->first_glyph_idx + i].codepoint;
1127             // We dont want to render the spaces in a space ligature because
1128             // there exist stupid fonts like Powerline that have no space glyph,
1129             // so special case it: https://github.com/kovidgoyal/kitty/issues/1225
1130             unsigned int num_glyphs = group->is_space_ligature ? 1 : group->num_glyphs;
1131             render_group(fg, group->num_cells, num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, global_glyph_render_scratch.glyphs, num_glyphs, center_glyph);
1132         }
1133         idx++;
1134     }
1135 }
1136 
1137 static PyObject*
test_shape(PyObject UNUSED * self,PyObject * args)1138 test_shape(PyObject UNUSED *self, PyObject *args) {
1139     Line *line;
1140     char *path = NULL;
1141     int index = 0;
1142     if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL;
1143     index_type num = 0;
1144     while(num < line->xnum && line->cpu_cells[num].ch) num += line->gpu_cells[num].attrs & WIDTH_MASK;
1145     PyObject *face = NULL;
1146     Font *font;
1147     if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create at least one font group first"); return NULL; }
1148     if (path) {
1149         face = face_from_path(path, index, (FONTS_DATA_HANDLE)font_groups);
1150         if (face == NULL) return NULL;
1151         font = calloc(1, sizeof(Font));
1152         font->face = face;
1153     } else {
1154         FontGroup *fg = font_groups;
1155         font = fg->fonts + fg->medium_font_idx;
1156     }
1157     shape_run(line->cpu_cells, line->gpu_cells, num, font, false);
1158 
1159     PyObject *ans = PyList_New(0);
1160     unsigned int idx = 0;
1161     glyph_index first_glyph;
1162     while (idx <= G(group_idx)) {
1163         Group *group = G(groups) + idx;
1164         if (!group->num_cells) break;
1165         first_glyph = group->num_glyphs ? G(info)[group->first_glyph_idx].codepoint : 0;
1166 
1167         PyObject *eg = PyTuple_New(group->num_glyphs);
1168         for (size_t g = 0; g < group->num_glyphs; g++) PyTuple_SET_ITEM(eg, g, Py_BuildValue("H", G(info)[group->first_glyph_idx + g].codepoint));
1169         PyList_Append(ans, Py_BuildValue("IIHN", group->num_cells, group->num_glyphs, first_glyph, eg));
1170         idx++;
1171     }
1172     if (face) { Py_CLEAR(face); free_maps(font); free(font); }
1173     return ans;
1174 }
1175 #undef G
1176 
1177 static void
render_run(FontGroup * fg,CPUCell * first_cpu_cell,GPUCell * first_gpu_cell,index_type num_cells,ssize_t font_idx,bool pua_space_ligature,bool center_glyph,int cursor_offset,DisableLigature disable_ligature_strategy)1178 render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature, bool center_glyph, int cursor_offset, DisableLigature disable_ligature_strategy) {
1179     switch(font_idx) {
1180         default:
1181             shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[font_idx], disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS);
1182             if (pua_space_ligature) merge_groups_for_pua_space_ligature();
1183             else if (cursor_offset > -1) { // false if DISABLE_LIGATURES_NEVER
1184                 index_type left, right;
1185                 split_run_at_offset(cursor_offset, &left, &right);
1186                 if (right > left) {
1187                     if (left) {
1188                         shape_run(first_cpu_cell, first_gpu_cell, left, &fg->fonts[font_idx], false);
1189                         render_groups(fg, &fg->fonts[font_idx], center_glyph);
1190                     }
1191                         shape_run(first_cpu_cell + left, first_gpu_cell + left, right - left, &fg->fonts[font_idx], true);
1192                         render_groups(fg, &fg->fonts[font_idx], center_glyph);
1193                     if (right < num_cells) {
1194                         shape_run(first_cpu_cell + right, first_gpu_cell + right, num_cells - right, &fg->fonts[font_idx], false);
1195                         render_groups(fg, &fg->fonts[font_idx], center_glyph);
1196                     }
1197                     break;
1198                 }
1199             }
1200             render_groups(fg, &fg->fonts[font_idx], center_glyph);
1201             break;
1202         case BLANK_FONT:
1203             while(num_cells--) { set_sprite(first_gpu_cell, 0, 0, 0); first_cpu_cell++; first_gpu_cell++; }
1204             break;
1205         case BOX_FONT:
1206             while(num_cells--) { render_box_cell(fg, first_cpu_cell, first_gpu_cell); first_cpu_cell++; first_gpu_cell++; }
1207             break;
1208         case MISSING_FONT:
1209             while(num_cells--) { set_sprite(first_gpu_cell, MISSING_GLYPH, 0, 0); first_cpu_cell++; first_gpu_cell++; }
1210             break;
1211     }
1212 }
1213 
1214 static bool
is_non_emoji_dingbat(char_type ch)1215 is_non_emoji_dingbat(char_type ch) {
1216     return 0x2700 <= ch && ch <= 0x27bf && !is_emoji(ch);
1217 }
1218 
1219 void
render_line(FONTS_DATA_HANDLE fg_,Line * line,index_type lnum,Cursor * cursor,DisableLigature disable_ligature_strategy)1220 render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor, DisableLigature disable_ligature_strategy) {
1221 #define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) { \
1222     int cursor_offset = -1; \
1223     if (disable_ligature_at_cursor && first_cell_in_run <= cursor->x && cursor->x <= i) cursor_offset = cursor->x - first_cell_in_run; \
1224     render_run(fg, line->cpu_cells + first_cell_in_run, line->gpu_cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false, center_glyph, cursor_offset, disable_ligature_strategy); \
1225 }
1226     FontGroup *fg = (FontGroup*)fg_;
1227     ssize_t run_font_idx = NO_FONT;
1228     bool center_glyph = false;
1229     bool disable_ligature_at_cursor = cursor != NULL && disable_ligature_strategy == DISABLE_LIGATURES_CURSOR && lnum == cursor->y;
1230     index_type first_cell_in_run, i;
1231     attrs_type prev_width = 0;
1232     for (i=0, first_cell_in_run=0; i < line->xnum; i++) {
1233         if (prev_width == 2) { prev_width = 0; continue; }
1234         CPUCell *cpu_cell = line->cpu_cells + i;
1235         GPUCell *gpu_cell = line->gpu_cells + i;
1236         bool is_fallback_font, is_emoji_presentation;
1237         ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell, &is_fallback_font, &is_emoji_presentation);
1238 
1239         if (
1240                 cell_font_idx != MISSING_FONT &&
1241                 ((is_fallback_font && !is_emoji_presentation && is_symbol(cpu_cell->ch)) || (cell_font_idx != BOX_FONT && (is_private_use(cpu_cell->ch))) || is_non_emoji_dingbat(cpu_cell->ch))
1242         ) {
1243             unsigned int desired_cells = 1;
1244             if (cell_font_idx > 0) {
1245                 Font *font = (fg->fonts + cell_font_idx);
1246                 glyph_index glyph_id = glyph_id_for_codepoint(font->face, cpu_cell->ch);
1247 
1248                 int width = get_glyph_width(font->face, glyph_id);
1249                 desired_cells = (unsigned int)ceilf((float)width / fg->cell_width);
1250             }
1251 
1252             unsigned int num_spaces = 0;
1253             while ((line->cpu_cells[i+num_spaces+1].ch == ' ')
1254                     && num_spaces < MAX_NUM_EXTRA_GLYPHS_PUA
1255                     && num_spaces + 1 < desired_cells
1256                     && i + num_spaces + 1 < line->xnum) {
1257                 num_spaces++;
1258                 // We have a private use char followed by space(s), render it as a multi-cell ligature.
1259                 GPUCell *space_cell = line->gpu_cells + i + num_spaces;
1260                 // Ensure the space cell uses the foreground color from the PUA cell.
1261                 // This is needed because there are applications like
1262                 // Powerline that use PUA+space with different foreground colors
1263                 // for the space and the PUA. See for example: https://github.com/kovidgoyal/kitty/issues/467
1264                 space_cell->fg = gpu_cell->fg;
1265                 space_cell->decoration_fg = gpu_cell->decoration_fg;
1266             }
1267             if (num_spaces) {
1268                 center_glyph = true;
1269                 RENDER
1270                 center_glyph = false;
1271                 render_run(fg, line->cpu_cells + i, line->gpu_cells + i, num_spaces + 1, cell_font_idx, true, center_glyph, -1, disable_ligature_strategy);
1272                 run_font_idx = NO_FONT;
1273                 first_cell_in_run = i + num_spaces + 1;
1274                 prev_width = line->gpu_cells[i+num_spaces].attrs & WIDTH_MASK;
1275                 i += num_spaces;
1276                 continue;
1277             }
1278         }
1279         prev_width = gpu_cell->attrs & WIDTH_MASK;
1280         if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
1281         if (run_font_idx == cell_font_idx) continue;
1282         RENDER
1283         run_font_idx = cell_font_idx;
1284         first_cell_in_run = i;
1285     }
1286     RENDER
1287 #undef RENDER
1288 }
1289 
1290 StringCanvas
render_simple_text(FONTS_DATA_HANDLE fg_,const char * text)1291 render_simple_text(FONTS_DATA_HANDLE fg_, const char *text) {
1292     FontGroup *fg = (FontGroup*)fg_;
1293     if (fg->fonts_count && fg->medium_font_idx) return render_simple_text_impl(fg->fonts[fg->medium_font_idx].face, text, fg->baseline);
1294     StringCanvas ans = {0};
1295     return ans;
1296 }
1297 
1298 static void
clear_symbol_maps(void)1299 clear_symbol_maps(void) {
1300     if (symbol_maps) { free(symbol_maps); symbol_maps = NULL; num_symbol_maps = 0; }
1301 }
1302 
1303 typedef struct {
1304     unsigned int main, bold, italic, bi, num_symbol_fonts;
1305 } DescriptorIndices;
1306 
1307 DescriptorIndices descriptor_indices = {0};
1308 
1309 static PyObject*
set_font_data(PyObject UNUSED * m,PyObject * args)1310 set_font_data(PyObject UNUSED *m, PyObject *args) {
1311     PyObject *sm;
1312     Py_CLEAR(box_drawing_function); Py_CLEAR(prerender_function); Py_CLEAR(descriptor_for_idx); Py_CLEAR(font_feature_settings);
1313     if (!PyArg_ParseTuple(args, "OOOIIIIO!dO",
1314                 &box_drawing_function, &prerender_function, &descriptor_for_idx,
1315                 &descriptor_indices.bold, &descriptor_indices.italic, &descriptor_indices.bi, &descriptor_indices.num_symbol_fonts,
1316                 &PyTuple_Type, &sm, &OPT(font_size), &font_feature_settings)) return NULL;
1317     Py_INCREF(box_drawing_function); Py_INCREF(prerender_function); Py_INCREF(descriptor_for_idx); Py_INCREF(font_feature_settings);
1318     free_font_groups();
1319     clear_symbol_maps();
1320     num_symbol_maps = PyTuple_GET_SIZE(sm);
1321     symbol_maps = calloc(num_symbol_maps, sizeof(SymbolMap));
1322     if (symbol_maps == NULL) return PyErr_NoMemory();
1323     for (size_t s = 0; s < num_symbol_maps; s++) {
1324         unsigned int left, right, font_idx;
1325         SymbolMap *x = symbol_maps + s;
1326         if (!PyArg_ParseTuple(PyTuple_GET_ITEM(sm, s), "III", &left, &right, &font_idx)) return NULL;
1327         x->left = left; x->right = right; x->font_idx = font_idx;
1328     }
1329     Py_RETURN_NONE;
1330 }
1331 
1332 static void
send_prerendered_sprites(FontGroup * fg)1333 send_prerendered_sprites(FontGroup *fg) {
1334     int error = 0;
1335     sprite_index x = 0, y = 0, z = 0;
1336     // blank cell
1337     ensure_canvas_can_fit(fg, 1);
1338     current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf);
1339     do_increment(fg, &error);
1340     if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
1341     PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, fg->strikethrough_position, fg->strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y);
1342     if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); }
1343     for (ssize_t i = 0; i < PyTuple_GET_SIZE(args) - 1; i++) {
1344         x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z;
1345         if (y > 0) { fatal("Too many pre-rendered sprites for your GPU or the font size is too large"); }
1346         do_increment(fg, &error);
1347         if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
1348         uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, i));
1349         ensure_canvas_can_fit(fg, 1);  // clear canvas
1350         Region r = { .right = fg->cell_width, .bottom = fg->cell_height };
1351         render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->cell_width, fg->cell_width);
1352         current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf);
1353     }
1354     Py_CLEAR(args);
1355 }
1356 
1357 static size_t
initialize_font(FontGroup * fg,unsigned int desc_idx,const char * ftype)1358 initialize_font(FontGroup *fg, unsigned int desc_idx, const char *ftype) {
1359     PyObject *d = PyObject_CallFunction(descriptor_for_idx, "I", desc_idx);
1360     if (d == NULL) { PyErr_Print(); fatal("Failed for %s font", ftype); }
1361     bool bold = PyObject_IsTrue(PyTuple_GET_ITEM(d, 1));
1362     bool italic = PyObject_IsTrue(PyTuple_GET_ITEM(d, 2));
1363     PyObject *face = desc_to_face(PyTuple_GET_ITEM(d, 0), (FONTS_DATA_HANDLE)fg);
1364     Py_CLEAR(d);
1365     if (face == NULL) { PyErr_Print(); fatal("Failed to convert descriptor to face for %s font", ftype); }
1366     size_t idx = fg->fonts_count++;
1367     bool ok = init_font(fg->fonts + idx, face, bold, italic, false);
1368     Py_CLEAR(face);
1369     if (!ok) {
1370         if (PyErr_Occurred()) { PyErr_Print(); }
1371         fatal("Failed to initialize %s font: %zu", ftype, idx);
1372     }
1373     return idx;
1374 }
1375 
1376 static void
initialize_font_group(FontGroup * fg)1377 initialize_font_group(FontGroup *fg) {
1378     fg->fonts_capacity = 10 + descriptor_indices.num_symbol_fonts;
1379     fg->fonts = calloc(fg->fonts_capacity, sizeof(Font));
1380     if (fg->fonts == NULL) fatal("Out of memory allocating fonts array");
1381     fg->fonts_count = 1;  // the 0 index font is the box font
1382 #define I(attr)  if (descriptor_indices.attr) fg->attr##_font_idx = initialize_font(fg, descriptor_indices.attr, #attr); else fg->attr##_font_idx = -1;
1383     fg->medium_font_idx = initialize_font(fg, 0, "medium");
1384     I(bold); I(italic); I(bi);
1385 #undef I
1386     fg->first_symbol_font_idx = fg->fonts_count; fg->first_fallback_font_idx = fg->fonts_count;
1387     fg->fallback_fonts_count = 0;
1388     for (size_t i = 0; i < descriptor_indices.num_symbol_fonts; i++) {
1389         initialize_font(fg, descriptor_indices.bi + 1 + i, "symbol_map");
1390         fg->first_fallback_font_idx++;
1391     }
1392 #undef I
1393     calc_cell_metrics(fg);
1394 }
1395 
1396 
1397 void
send_prerendered_sprites_for_window(OSWindow * w)1398 send_prerendered_sprites_for_window(OSWindow *w) {
1399     FontGroup *fg = (FontGroup*)w->fonts_data;
1400     if (!fg->sprite_map) {
1401         fg->sprite_map = alloc_sprite_map(fg->cell_width, fg->cell_height);
1402         send_prerendered_sprites(fg);
1403     }
1404 }
1405 
1406 FONTS_DATA_HANDLE
load_fonts_data(double font_sz_in_pts,double dpi_x,double dpi_y)1407 load_fonts_data(double font_sz_in_pts, double dpi_x, double dpi_y) {
1408     FontGroup *fg = font_group_for(font_sz_in_pts, dpi_x, dpi_y);
1409     return (FONTS_DATA_HANDLE)fg;
1410 }
1411 
1412 static void
finalize(void)1413 finalize(void) {
1414     Py_CLEAR(python_send_to_gpu_impl);
1415     clear_symbol_maps();
1416     Py_CLEAR(box_drawing_function);
1417     Py_CLEAR(prerender_function);
1418     Py_CLEAR(descriptor_for_idx);
1419     Py_CLEAR(font_feature_settings);
1420     free_font_groups();
1421     free(ligature_types);
1422     if (harfbuzz_buffer) { hb_buffer_destroy(harfbuzz_buffer); harfbuzz_buffer = NULL; }
1423     free(group_state.groups); group_state.groups = NULL; group_state.groups_capacity = 0;
1424     free(global_glyph_render_scratch.glyphs);
1425     free(global_glyph_render_scratch.sprite_positions);
1426     global_glyph_render_scratch = (GlyphRenderScratch){0};
1427 }
1428 
1429 static PyObject*
sprite_map_set_layout(PyObject UNUSED * self,PyObject * args)1430 sprite_map_set_layout(PyObject UNUSED *self, PyObject *args) {
1431     unsigned int w, h;
1432     if(!PyArg_ParseTuple(args, "II", &w, &h)) return NULL;
1433     if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create font group first"); return NULL; }
1434     sprite_tracker_set_layout(&font_groups->sprite_tracker, w, h);
1435     Py_RETURN_NONE;
1436 }
1437 
1438 static PyObject*
test_sprite_position_for(PyObject UNUSED * self,PyObject * args)1439 test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
1440     int error;
1441     FREE_AFTER_FUNCTION glyph_index *glyphs = calloc(PyTuple_GET_SIZE(args), sizeof(glyph_index));
1442     for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
1443         if (!PyLong_Check(PyTuple_GET_ITEM(args, i))) {
1444             PyErr_SetString(PyExc_TypeError, "glyph indices must be integers");
1445             return NULL;
1446         }
1447         glyphs[i] = (glyph_index)PyLong_AsUnsignedLong(PyTuple_GET_ITEM(args, i));
1448         if (PyErr_Occurred()) return NULL;
1449     }
1450     FontGroup *fg = font_groups;
1451     if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create font group first"); return NULL; }
1452     SpritePosition *pos = sprite_position_for(fg, &fg->fonts[fg->medium_font_idx], glyphs, PyTuple_GET_SIZE(args), 0, 1, &error);
1453     if (pos == NULL) { sprite_map_set_error(error); return NULL; }
1454     return Py_BuildValue("HHH", pos->x, pos->y, pos->z);
1455 }
1456 
1457 static PyObject*
set_send_sprite_to_gpu(PyObject UNUSED * self,PyObject * func)1458 set_send_sprite_to_gpu(PyObject UNUSED *self, PyObject *func) {
1459     Py_CLEAR(python_send_to_gpu_impl);
1460     if (func != Py_None) {
1461         python_send_to_gpu_impl = func;
1462         Py_INCREF(python_send_to_gpu_impl);
1463     }
1464     current_send_sprite_to_gpu = python_send_to_gpu_impl ? python_send_to_gpu : send_sprite_to_gpu;
1465     Py_RETURN_NONE;
1466 }
1467 
1468 static PyObject*
test_render_line(PyObject UNUSED * self,PyObject * args)1469 test_render_line(PyObject UNUSED *self, PyObject *args) {
1470     PyObject *line;
1471     if (!PyArg_ParseTuple(args, "O!", &Line_Type, &line)) return NULL;
1472     if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create font group first"); return NULL; }
1473     render_line((FONTS_DATA_HANDLE)font_groups, (Line*)line, 0, NULL, DISABLE_LIGATURES_NEVER);
1474     Py_RETURN_NONE;
1475 }
1476 
1477 static PyObject*
concat_cells(PyObject UNUSED * self,PyObject * args)1478 concat_cells(PyObject UNUSED *self, PyObject *args) {
1479     // Concatenate cells returning RGBA data
1480     unsigned int cell_width, cell_height;
1481     int is_32_bit;
1482     PyObject *cells;
1483     if (!PyArg_ParseTuple(args, "IIpO!", &cell_width, &cell_height, &is_32_bit, &PyTuple_Type, &cells)) return NULL;
1484     size_t num_cells = PyTuple_GET_SIZE(cells), r, c, i;
1485     PyObject *ans = PyBytes_FromStringAndSize(NULL, (size_t)4 * cell_width * cell_height * num_cells);
1486     if (ans == NULL) return PyErr_NoMemory();
1487     pixel *dest = (pixel*)PyBytes_AS_STRING(ans);
1488     for (r = 0; r < cell_height; r++) {
1489         for (c = 0; c < num_cells; c++) {
1490             void *s = ((uint8_t*)PyBytes_AS_STRING(PyTuple_GET_ITEM(cells, c)));
1491             if (is_32_bit) {
1492                 pixel *src = (pixel*)s + cell_width * r;
1493                 for (i = 0; i < cell_width; i++, dest++) {
1494                     uint8_t *rgba = (uint8_t*)dest;
1495                     rgba[0] = (src[i] >> 24) & 0xff;
1496                     rgba[1] = (src[i] >> 16) & 0xff;
1497                     rgba[2] = (src[i] >> 8) & 0xff;
1498                     rgba[3] = src[i] & 0xff;
1499                 }
1500             } else {
1501                 uint8_t *src = (uint8_t*)s + cell_width * r;
1502                 for (i = 0; i < cell_width; i++, dest++) {
1503                     uint8_t *rgba = (uint8_t*)dest;
1504                     if (src[i]) { memset(rgba, 0xff, 3); rgba[3] = src[i]; }
1505                     else *dest = 0;
1506                 }
1507             }
1508 
1509         }
1510     }
1511     return ans;
1512 }
1513 
1514 static PyObject*
current_fonts(PYNOARG)1515 current_fonts(PYNOARG) {
1516     if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create font group first"); return NULL; }
1517     PyObject *ans = PyDict_New();
1518     if (!ans) return NULL;
1519     FontGroup *fg = font_groups;
1520 #define SET(key, val) {if (PyDict_SetItemString(ans, #key, fg->fonts[val].face) != 0) { goto error; }}
1521     SET(medium, fg->medium_font_idx);
1522     if (fg->bold_font_idx > 0) SET(bold, fg->bold_font_idx);
1523     if (fg->italic_font_idx > 0) SET(italic, fg->italic_font_idx);
1524     if (fg->bi_font_idx > 0) SET(bi, fg->bi_font_idx);
1525     PyObject *ff = PyTuple_New(fg->fallback_fonts_count);
1526     if (!ff) goto error;
1527     for (size_t i = 0; i < fg->fallback_fonts_count; i++) {
1528         Py_INCREF(fg->fonts[fg->first_fallback_font_idx + i].face);
1529         PyTuple_SET_ITEM(ff, i, fg->fonts[fg->first_fallback_font_idx + i].face);
1530     }
1531     PyDict_SetItemString(ans, "fallback", ff);
1532     Py_CLEAR(ff);
1533     return ans;
1534 error:
1535     Py_CLEAR(ans); return NULL;
1536 #undef SET
1537 }
1538 
1539 static PyObject*
get_fallback_font(PyObject UNUSED * self,PyObject * args)1540 get_fallback_font(PyObject UNUSED *self, PyObject *args) {
1541     if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create font group first"); return NULL; }
1542     PyObject *text;
1543     int bold, italic;
1544     if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL;
1545     CPUCell cpu_cell = {0};
1546     GPUCell gpu_cell = {0};
1547     static Py_UCS4 char_buf[2 + arraysz(cpu_cell.cc_idx)];
1548     if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
1549     cpu_cell.ch = char_buf[0];
1550     for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cpu_cell.cc_idx); i++) cpu_cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]);
1551     if (bold) gpu_cell.attrs |= 1 << BOLD_SHIFT;
1552     if (italic) gpu_cell.attrs |= 1 << ITALIC_SHIFT;
1553     FontGroup *fg = font_groups;
1554     ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell);
1555     if (ans == MISSING_FONT) { PyErr_SetString(PyExc_ValueError, "No fallback font found"); return NULL; }
1556     if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
1557     return fg->fonts[ans].face;
1558 }
1559 
1560 static PyObject*
create_test_font_group(PyObject * self UNUSED,PyObject * args)1561 create_test_font_group(PyObject *self UNUSED, PyObject *args) {
1562     double sz, dpix, dpiy;
1563     if (!PyArg_ParseTuple(args, "ddd", &sz, &dpix, &dpiy)) return NULL;
1564     FontGroup *fg = font_group_for(sz, dpix, dpiy);
1565     if (!fg->sprite_map) send_prerendered_sprites(fg);
1566     return Py_BuildValue("II", fg->cell_width, fg->cell_height);
1567 }
1568 
1569 static PyObject*
free_font_data(PyObject * self UNUSED,PyObject * args UNUSED)1570 free_font_data(PyObject *self UNUSED, PyObject *args UNUSED) {
1571     finalize();
1572     Py_RETURN_NONE;
1573 }
1574 
1575 static PyObject*
parse_font_feature(PyObject * self UNUSED,PyObject * feature)1576 parse_font_feature(PyObject *self UNUSED, PyObject *feature) {
1577     if (!PyUnicode_Check(feature)) {
1578         PyErr_SetString(PyExc_TypeError, "feature must be a unicode object");
1579         return NULL;
1580     }
1581     PyObject *ans = PyBytes_FromStringAndSize(NULL, sizeof(hb_feature_t));
1582     if (!ans) return NULL;
1583     if (!hb_feature_from_string(PyUnicode_AsUTF8(feature), -1, (hb_feature_t*)PyBytes_AS_STRING(ans))) {
1584         Py_CLEAR(ans);
1585         PyErr_Format(PyExc_ValueError, "%U is not a valid font feature", feature);
1586         return NULL;
1587     }
1588     return ans;
1589 }
1590 
1591 static PyMethodDef module_methods[] = {
1592     METHODB(set_font_data, METH_VARARGS),
1593     METHODB(free_font_data, METH_NOARGS),
1594     METHODB(parse_font_feature, METH_O),
1595     METHODB(create_test_font_group, METH_VARARGS),
1596     METHODB(sprite_map_set_layout, METH_VARARGS),
1597     METHODB(test_sprite_position_for, METH_VARARGS),
1598     METHODB(concat_cells, METH_VARARGS),
1599     METHODB(set_send_sprite_to_gpu, METH_O),
1600     METHODB(test_shape, METH_VARARGS),
1601     METHODB(current_fonts, METH_NOARGS),
1602     METHODB(test_render_line, METH_VARARGS),
1603     METHODB(get_fallback_font, METH_VARARGS),
1604     {NULL, NULL, 0, NULL}        /* Sentinel */
1605 };
1606 
1607 bool
init_fonts(PyObject * module)1608 init_fonts(PyObject *module) {
1609     harfbuzz_buffer = hb_buffer_create();
1610     if (harfbuzz_buffer == NULL || !hb_buffer_allocation_successful(harfbuzz_buffer) || !hb_buffer_pre_allocate(harfbuzz_buffer, 2048)) { PyErr_NoMemory(); return false; }
1611     hb_buffer_set_cluster_level(harfbuzz_buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
1612 #define create_feature(feature, where) {\
1613     if (!hb_feature_from_string(feature, sizeof(feature) - 1, &hb_features[where])) { \
1614         PyErr_SetString(PyExc_RuntimeError, "Failed to create " feature " harfbuzz feature"); \
1615         return false; \
1616     }}
1617     create_feature("-liga", LIGA_FEATURE);
1618     create_feature("-dlig", DLIG_FEATURE);
1619     create_feature("-calt", CALT_FEATURE);
1620 #undef create_feature
1621     if (PyModule_AddFunctions(module, module_methods) != 0) return false;
1622     current_send_sprite_to_gpu = send_sprite_to_gpu;
1623     return true;
1624 }
1625