1 /*
2  * freetype.c
3  * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>
4  *
5  * Distributed under terms of the GPL3 license.
6  */
7 
8 #include "fonts.h"
9 #include "cleanup.h"
10 #include "state.h"
11 #include <math.h>
12 #include <structmember.h>
13 #include <ft2build.h>
14 #include <hb-ft.h>
15 
16 #if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 7
17 #define FT_Bitmap_Init FT_Bitmap_New
18 #endif
19 
20 #include FT_BITMAP_H
21 #include FT_TRUETYPE_TABLES_H
22 typedef struct {
23     PyObject_HEAD
24 
25     FT_Face face;
26     unsigned int units_per_EM;
27     int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
28     int hinting, hintstyle, index;
29     bool is_scalable, has_color;
30     float size_in_pts;
31     FT_F26Dot6 char_width, char_height;
32     FT_UInt xdpi, ydpi;
33     PyObject *path;
34     hb_font_t *harfbuzz_font;
35     hb_codepoint_t space_glyph_id;
36     void *extra_data;
37     free_extra_data_func free_extra_data;
38     float apple_leading;
39 } Face;
40 PyTypeObject Face_Type;
41 
42 static PyObject* FreeType_Exception = NULL;
43 
44 void
set_freetype_error(const char * prefix,int err_code)45 set_freetype_error(const char* prefix, int err_code) {
46     int i = 0;
47 #undef FTERRORS_H_
48 #undef __FTERRORS_H__
49 #define FT_ERRORDEF( e, v, s )  { e, s },
50 #define FT_ERROR_START_LIST     {
51 #define FT_ERROR_END_LIST       { 0, NULL } };
52 
53     static const struct {
54         int          err_code;
55         const char*  err_msg;
56     } ft_errors[] =
57 
58 #ifdef FT_ERRORS_H
59 #include FT_ERRORS_H
60 #else
61     FT_ERROR_START_LIST FT_ERROR_END_LIST
62 #endif
63 
64     while(ft_errors[i].err_msg != NULL) {
65         if (ft_errors[i].err_code == err_code) {
66             PyErr_Format(FreeType_Exception, "%s %s", prefix, ft_errors[i].err_msg);
67             return;
68         }
69         i++;
70     }
71     PyErr_Format(FreeType_Exception, "%s (error code: %d)", prefix, err_code);
72 }
73 
74 static FT_Library  library;
75 
76 FT_Library
freetype_library(void)77 freetype_library(void) { return library; }
78 
79 static int
font_units_to_pixels_y(Face * self,int x)80 font_units_to_pixels_y(Face *self, int x) {
81     return (int)ceil((double)FT_MulFix(x, self->face->size->metrics.y_scale) / 64.0);
82 }
83 
84 static int
font_units_to_pixels_x(Face * self,int x)85 font_units_to_pixels_x(Face *self, int x) {
86     return (int)ceil((double)FT_MulFix(x, self->face->size->metrics.x_scale) / 64.0);
87 }
88 
89 
90 static int
get_load_flags(int hinting,int hintstyle,int base)91 get_load_flags(int hinting, int hintstyle, int base) {
92     int flags = base;
93     if (hinting) {
94         if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL;
95         else if (0 < hintstyle  && hintstyle < 3) flags |= FT_LOAD_TARGET_LIGHT;
96     } else flags |= FT_LOAD_NO_HINTING;
97     return flags;
98 }
99 
100 
101 static bool
load_glyph(Face * self,int glyph_index,int load_type)102 load_glyph(Face *self, int glyph_index, int load_type) {
103     int flags = get_load_flags(self->hinting, self->hintstyle, load_type);
104     int error = FT_Load_Glyph(self->face, glyph_index, flags);
105     if (error) {
106         char buf[256];
107         snprintf(buf, sizeof(buf) - 1, "Failed to load glyph_index=%d load_type=%d, with error:", glyph_index, load_type);
108         set_freetype_error(buf, error); return false;
109     }
110     return true;
111 }
112 
113 static unsigned int
get_height_for_char(Face * self,char ch)114 get_height_for_char(Face *self, char ch) {
115     unsigned int ans = 0;
116     int glyph_index = FT_Get_Char_Index(self->face, ch);
117     if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
118         unsigned int baseline = font_units_to_pixels_y(self, self->ascender);
119         FT_GlyphSlotRec *glyph = self->face->glyph;
120         FT_Bitmap *bm = &glyph->bitmap;
121         if (glyph->bitmap_top <= 0 || (glyph->bitmap_top > 0 && (unsigned int)glyph->bitmap_top < baseline)) {
122             ans = baseline - glyph->bitmap_top + bm->rows;
123         }
124     }
125     return ans;
126 }
127 
128 static unsigned int
calc_cell_height(Face * self,bool for_metrics)129 calc_cell_height(Face *self, bool for_metrics) {
130     unsigned int ans = font_units_to_pixels_y(self, self->height);
131     if (for_metrics) {
132         unsigned int underscore_height = get_height_for_char(self, '_');
133         if (underscore_height > ans) {
134             if (global_state.debug_font_fallback) printf(
135                 "Increasing cell height by %u pixels to work around buggy font that renders underscore outside the bounding box\n", underscore_height - ans);
136             return underscore_height;
137         }
138     }
139     return ans;
140 }
141 
142 static bool
set_font_size(Face * self,FT_F26Dot6 char_width,FT_F26Dot6 char_height,FT_UInt xdpi,FT_UInt ydpi,unsigned int desired_height,unsigned int cell_height)143 set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt xdpi, FT_UInt ydpi, unsigned int desired_height, unsigned int cell_height) {
144     int error = FT_Set_Char_Size(self->face, 0, char_height, xdpi, ydpi);
145     if (!error) {
146         unsigned int ch = calc_cell_height(self, false);
147         if (desired_height && ch != desired_height) {
148             FT_F26Dot6 h = (FT_F26Dot6)floor((double)char_height * (double)desired_height / (double) ch);
149             return set_font_size(self, 0, h, xdpi, ydpi, 0, cell_height);
150         }
151         self->char_width = char_width; self->char_height = char_height; self->xdpi = xdpi; self->ydpi = ydpi;
152         if (self->harfbuzz_font != NULL) hb_ft_font_changed(self->harfbuzz_font);
153     } else {
154         if (!self->is_scalable && self->face->num_fixed_sizes > 0) {
155             int32_t min_diff = INT32_MAX;
156             if (desired_height == 0) desired_height = cell_height;
157             if (desired_height == 0) {
158                 desired_height = (unsigned int)ceil(((double)char_height / 64.) * (double)ydpi / 72.);
159                 desired_height += (unsigned int)ceil(0.2 * desired_height);
160             }
161             FT_Int strike_index = -1;
162             for (FT_Int i = 0; i < self->face->num_fixed_sizes; i++) {
163                 int h = self->face->available_sizes[i].height;
164                 int32_t diff = h < (int32_t)desired_height ? (int32_t)desired_height - h : h - (int32_t)desired_height;
165                 if (diff < min_diff) {
166                     min_diff = diff;
167                     strike_index = i;
168                 }
169             }
170             if (strike_index > -1) {
171                 error = FT_Select_Size(self->face, strike_index);
172                 if (error) { set_freetype_error("Failed to set char size for non-scalable font, with error:", error); return false; }
173                 return true;
174             }
175         }
176         set_freetype_error("Failed to set char size, with error:", error);
177         return false;
178     }
179     return !error;
180 }
181 
182 bool
set_size_for_face(PyObject * s,unsigned int desired_height,bool force,FONTS_DATA_HANDLE fg)183 set_size_for_face(PyObject *s, unsigned int desired_height, bool force, FONTS_DATA_HANDLE fg) {
184     Face *self = (Face*)s;
185     FT_F26Dot6 w = (FT_F26Dot6)(ceil(fg->font_sz_in_pts * 64.0));
186     FT_UInt xdpi = (FT_UInt)fg->logical_dpi_x, ydpi = (FT_UInt)fg->logical_dpi_y;
187     if (!force && (self->char_width == w && self->char_height == w && self->xdpi == xdpi && self->ydpi == ydpi)) return true;
188     ((Face*)self)->size_in_pts = (float)fg->font_sz_in_pts;
189     return set_font_size(self, w, w, xdpi, ydpi, desired_height, fg->cell_height);
190 }
191 
192 static bool
init_ft_face(Face * self,PyObject * path,int hinting,int hintstyle,FONTS_DATA_HANDLE fg)193 init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_HANDLE fg) {
194 #define CPY(n) self->n = self->face->n;
195     CPY(units_per_EM); CPY(ascender); CPY(descender); CPY(height); CPY(max_advance_width); CPY(max_advance_height); CPY(underline_position); CPY(underline_thickness);
196 #undef CPY
197     self->is_scalable = FT_IS_SCALABLE(self->face);
198     self->has_color = FT_HAS_COLOR(self->face);
199     self->hinting = hinting; self->hintstyle = hintstyle;
200     if (!set_size_for_face((PyObject*)self, 0, false, fg)) return false;
201     self->harfbuzz_font = hb_ft_font_create(self->face, NULL);
202     if (self->harfbuzz_font == NULL) { PyErr_NoMemory(); return false; }
203     hb_ft_font_set_load_flags(self->harfbuzz_font, get_load_flags(self->hinting, self->hintstyle, FT_LOAD_DEFAULT));
204 
205     TT_OS2 *os2 = (TT_OS2*)FT_Get_Sfnt_Table(self->face, FT_SFNT_OS2);
206     if (os2 != NULL) {
207       self->strikethrough_position = os2->yStrikeoutPosition;
208       self->strikethrough_thickness = os2->yStrikeoutSize;
209     }
210 
211     self->path = path;
212     Py_INCREF(self->path);
213     self->index = self->face->face_index & 0xFFFF;
214     self->space_glyph_id = glyph_id_for_codepoint((PyObject*)self, ' ');
215     return true;
216 }
217 
218 PyObject*
face_from_descriptor(PyObject * descriptor,FONTS_DATA_HANDLE fg)219 face_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) {
220 #define D(key, conv, missing_ok) { \
221     PyObject *t = PyDict_GetItemString(descriptor, #key); \
222     if (t == NULL) { \
223         if (!missing_ok) { PyErr_SetString(PyExc_KeyError, "font descriptor is missing the key: " #key); return NULL; } \
224     } else key = conv(t); \
225 }
226     const char *path = NULL;
227     long index = 0;
228     bool hinting = false;
229     long hint_style = 0;
230     D(path, PyUnicode_AsUTF8, false);
231     D(index, PyLong_AsLong, true);
232     D(hinting, PyObject_IsTrue, true);
233     D(hint_style, PyLong_AsLong, true);
234 #undef D
235     Face *self = (Face *)Face_Type.tp_alloc(&Face_Type, 0);
236     if (self != NULL) {
237         int error = FT_New_Face(library, path, index, &(self->face));
238         if(error) { set_freetype_error("Failed to load face, with error:", error); Py_CLEAR(self); return NULL; }
239         if (!init_ft_face(self, PyDict_GetItemString(descriptor, "path"), hinting, hint_style, fg)) { Py_CLEAR(self); return NULL; }
240     }
241     return (PyObject*)self;
242 }
243 
244 FT_Face
native_face_from_path(const char * path,int index)245 native_face_from_path(const char *path, int index) {
246     int error;
247     FT_Face ans;
248     error = FT_New_Face(library, path, index, &ans);
249     if (error) { set_freetype_error("Failed to load face, with error:", error); return NULL; }
250     return ans;
251 }
252 
253 PyObject*
face_from_path(const char * path,int index,FONTS_DATA_HANDLE fg)254 face_from_path(const char *path, int index, FONTS_DATA_HANDLE fg) {
255     Face *ans = (Face*)Face_Type.tp_alloc(&Face_Type, 0);
256     if (ans == NULL) return NULL;
257     int error;
258     error = FT_New_Face(library, path, index, &ans->face);
259     if (error) { set_freetype_error("Failed to load face, with error:", error); ans->face = NULL; return NULL; }
260     if (!init_ft_face(ans, Py_None, true, 3, fg)) { Py_CLEAR(ans); return NULL; }
261     return (PyObject*)ans;
262 }
263 
264 static void
dealloc(Face * self)265 dealloc(Face* self) {
266     if (self->harfbuzz_font) hb_font_destroy(self->harfbuzz_font);
267     if (self->face) FT_Done_Face(self->face);
268     if (self->extra_data && self->free_extra_data) self->free_extra_data(self->extra_data);
269     Py_CLEAR(self->path);
270     Py_TYPE(self)->tp_free((PyObject*)self);
271 }
272 
273 static PyObject *
repr(Face * self)274 repr(Face *self) {
275     const char *ps_name = FT_Get_Postscript_Name(self->face);
276     return PyUnicode_FromFormat(
277         "Face(family=%s, style=%s, ps_name=%s, path=%S, index=%d, is_scalable=%S, has_color=%S, ascender=%i, descender=%i, height=%i, underline_position=%i, underline_thickness=%i, strikethrough_position=%i, strikethrough_thickness=%i)",
278         self->face->family_name ? self->face->family_name : "", self->face->style_name ? self->face->style_name : "",
279         ps_name ? ps_name: "",
280         self->path, self->index, self->is_scalable ? Py_True : Py_False, self->has_color ? Py_True : Py_False,
281         self->ascender, self->descender, self->height, self->underline_position, self->underline_thickness, self->strikethrough_position, self->strikethrough_thickness
282     );
283 }
284 
285 
286 const char*
postscript_name_for_face(const PyObject * face_)287 postscript_name_for_face(const PyObject *face_) {
288     const Face *self = (const Face*)face_;
289     const char *ps_name = FT_Get_Postscript_Name(self->face);
290     return ps_name ? ps_name : "";
291 }
292 
293 static unsigned int
calc_cell_width(Face * self)294 calc_cell_width(Face *self) {
295     unsigned int ans = 0;
296     for (char_type i = 32; i < 128; i++) {
297         int glyph_index = FT_Get_Char_Index(self->face, i);
298         if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
299             ans = MAX(ans, (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
300         }
301     }
302     return ans;
303 }
304 
305 
306 static unsigned int
adjust_ypos(unsigned int pos,unsigned int cell_height,int adjustment)307 adjust_ypos(unsigned int pos, unsigned int cell_height, int adjustment) {
308     if (adjustment >= 0) adjustment = MIN(adjustment, (int)pos - 1);
309     else adjustment = MAX(adjustment, (int)pos - (int)cell_height + 1);
310     return pos - adjustment;
311 }
312 
313 void
cell_metrics(PyObject * s,unsigned int * cell_width,unsigned int * cell_height,unsigned int * baseline,unsigned int * underline_position,unsigned int * underline_thickness,unsigned int * strikethrough_position,unsigned int * strikethrough_thickness)314 cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) {
315     Face *self = (Face*)s;
316     *cell_width = calc_cell_width(self);
317     *cell_height = calc_cell_height(self, true);
318     int baseline_offset = 0;
319     if (OPT(adjust_baseline_px) != 0) baseline_offset = OPT(adjust_baseline_px);
320     else if (OPT(adjust_baseline_frac) != 0) baseline_offset = (int)(*cell_height * OPT(adjust_baseline_frac));
321     *baseline = font_units_to_pixels_y(self, self->ascender);
322     *underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position)));
323     *underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness));
324 
325     if (self->strikethrough_position != 0) {
326       *strikethrough_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->strikethrough_position)));
327     } else {
328       *strikethrough_position = (unsigned int)floor(*baseline * 0.65);
329     }
330     if (self->strikethrough_thickness > 0) {
331       *strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->strikethrough_thickness));
332     } else {
333       *strikethrough_thickness = *underline_thickness;
334     }
335     if (baseline_offset) {
336         *baseline = adjust_ypos(*baseline, *cell_height, baseline_offset);
337         *underline_position = adjust_ypos(*underline_position, *cell_height, baseline_offset);
338         *strikethrough_position = adjust_ypos(*strikethrough_position, *cell_height, baseline_offset);
339     }
340 }
341 
342 unsigned int
glyph_id_for_codepoint(PyObject * s,char_type cp)343 glyph_id_for_codepoint(PyObject *s, char_type cp) {
344     return FT_Get_Char_Index(((Face*)s)->face, cp);
345 }
346 
347 
348 bool
is_glyph_empty(PyObject * s,glyph_index g)349 is_glyph_empty(PyObject *s, glyph_index g) {
350     Face *self = (Face*)s;
351     if (!load_glyph(self, g, FT_LOAD_DEFAULT)) { PyErr_Print(); return false; }
352 #define M self->face->glyph->metrics
353     /* printf("glyph: %u horiBearingX: %ld horiBearingY: %ld width: %ld height: %ld\n", g, M.horiBearingX, M.horiBearingY, M.width, M.height); */
354     return M.width == 0;
355 #undef M
356 }
357 
358 int
get_glyph_width(PyObject * s,glyph_index g)359 get_glyph_width(PyObject *s, glyph_index g) {
360     Face *self = (Face*)s;
361     if (!load_glyph(self, g, FT_LOAD_DEFAULT)) { PyErr_Print(); return 0; }
362 #define M self->face->glyph->metrics
363 #define B self->face->glyph->bitmap
364     /* printf("glyph: %u bitmap.width: %d bitmap.rows: %d horiAdvance: %ld horiBearingX: %ld horiBearingY: %ld vertBearingX: %ld vertBearingY: %ld vertAdvance: %ld width: %ld height: %ld\n", */
365     /*         g, B.width, B.rows, M.horiAdvance, M.horiBearingX, M.horiBearingY, M.vertBearingX, M.vertBearingY, M.vertAdvance, M.width, M.height); */
366     return B.width ? (int)B.width : (int)(M.width / 64);
367 #undef M
368 #undef B
369 }
370 
371 hb_font_t*
harfbuzz_font_for_face(PyObject * self)372 harfbuzz_font_for_face(PyObject *self) { return ((Face*)self)->harfbuzz_font; }
373 
374 
375 typedef struct {
376     unsigned char* buf;
377     size_t start_x, width, stride;
378     size_t rows;
379     FT_Pixel_Mode pixel_mode;
380     bool needs_free;
381     unsigned int factor, right_edge;
382     int bitmap_left, bitmap_top;
383 } ProcessedBitmap;
384 
385 static void
free_processed_bitmap(ProcessedBitmap * bm)386 free_processed_bitmap(ProcessedBitmap *bm) {
387     if (bm->needs_free) {
388         bm->needs_free = false;
389         free(bm->buf); bm->buf = NULL;
390     }
391 }
392 
393 static void
trim_borders(ProcessedBitmap * ans,size_t extra)394 trim_borders(ProcessedBitmap *ans, size_t extra) {
395     bool column_has_text = false;
396 
397     // Trim empty columns from the right side of the bitmap
398     for (ssize_t x = ans->width - 1; !column_has_text && x > -1 && extra > 0; x--) {
399         for (size_t y = 0; y < ans->rows && !column_has_text; y++) {
400             if (ans->buf[x + y * ans->stride] > 200) column_has_text = true;
401         }
402         if (!column_has_text) { ans->width--; extra--; }
403     }
404 
405     // Remove any remaining extra columns from the left edge of the bitmap
406     ans->start_x = extra;
407     ans->width -= extra;
408 }
409 
410 static void
populate_processed_bitmap(FT_GlyphSlotRec * slot,FT_Bitmap * bitmap,ProcessedBitmap * ans,bool copy_buf)411 populate_processed_bitmap(FT_GlyphSlotRec *slot, FT_Bitmap *bitmap, ProcessedBitmap *ans, bool copy_buf) {
412     ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;
413     ans->rows = bitmap->rows;
414     if (copy_buf) {
415         ans->buf = calloc(ans->rows, ans->stride);
416         if (!ans->buf) fatal("Out of memory");
417         ans->needs_free = true;
418         memcpy(ans->buf, bitmap->buffer, ans->rows * ans->stride);
419     } else ans->buf = bitmap->buffer;
420     ans->start_x = 0; ans->width = bitmap->width;
421     ans->pixel_mode = bitmap->pixel_mode;
422     ans->bitmap_top = slot->bitmap_top; ans->bitmap_left = slot->bitmap_left;
423 }
424 
425 bool
freetype_convert_mono_bitmap(FT_Bitmap * src,FT_Bitmap * dest)426 freetype_convert_mono_bitmap(FT_Bitmap *src, FT_Bitmap *dest) {
427     FT_Bitmap_Init(dest);
428     // This also sets pixel_mode to FT_PIXEL_MODE_GRAY so we don't have to
429     int error = FT_Bitmap_Convert(library, src, dest, 1);
430     if (error) { set_freetype_error("Failed to convert bitmap, with error:", error); return false; }
431     // Normalize gray levels to the range [0..255]
432     dest->num_grays = 256;
433     unsigned int stride = dest->pitch < 0 ? -dest->pitch : dest->pitch;
434     for (unsigned i = 0; i < (unsigned)dest->rows; ++i) {
435         // We only have 2 levels
436         for (unsigned j = 0; j < (unsigned)dest->width; ++j) dest->buffer[i * stride + j] *= 255;
437     }
438     return true;
439 }
440 
441 static bool
render_bitmap(Face * self,int glyph_id,ProcessedBitmap * ans,unsigned int cell_width,unsigned int cell_height,unsigned int num_cells,bool bold,bool italic,bool rescale,FONTS_DATA_HANDLE fg)442 render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, bool bold, bool italic, bool rescale, FONTS_DATA_HANDLE fg) {
443     if (!load_glyph(self, glyph_id, FT_LOAD_RENDER)) return false;
444     unsigned int max_width = cell_width * num_cells;
445 
446     // Embedded bitmap glyph?
447     if (self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
448         FT_Bitmap bitmap;
449         freetype_convert_mono_bitmap(&self->face->glyph->bitmap, &bitmap);
450         populate_processed_bitmap(self->face->glyph, &bitmap, ans, true);
451         FT_Bitmap_Done(library, &bitmap);
452     } else {
453         populate_processed_bitmap(self->face->glyph, &self->face->glyph->bitmap, ans, false);
454     }
455 
456     if (ans->width > max_width) {
457         size_t extra = ans->width - max_width;
458         if (italic && extra < cell_width / 2) {
459             trim_borders(ans, extra);
460         } else if (extra == 2 && num_cells == 1) {
461             // there exist fonts that have bitmaps just a couple of pixels
462             // wider than their advances, rather than rescale, which looks
463             // bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352
464         } else if (rescale && self->is_scalable && extra > 1) {
465             FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
466             float ar = (float)max_width / (float)ans->width;
467             if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi, 0, fg->cell_height)) {
468                 free_processed_bitmap(ans);
469                 if (!render_bitmap(self, glyph_id, ans, cell_width, cell_height, num_cells, bold, italic, false, fg)) return false;
470                 if (!set_font_size(self, char_width, char_height, self->xdpi, self->ydpi, 0, fg->cell_height)) return false;
471             } else return false;
472         }
473     }
474     return true;
475 }
476 
477 int
downsample_32bit_image(uint8_t * src,unsigned src_width,unsigned src_height,unsigned src_stride,uint8_t * dest,unsigned dest_width,unsigned dest_height)478 downsample_32bit_image(uint8_t *src, unsigned src_width, unsigned src_height, unsigned src_stride, uint8_t *dest, unsigned dest_width, unsigned dest_height) {
479     // Downsample using a simple area averaging algorithm. Could probably do
480     // better with bi-cubic or lanczos, but at these small sizes I don't think
481     // it matters
482     float ratio = MAX((float)src_width / dest_width, (float)src_height / dest_height);
483     int factor = (int)ceilf(ratio);
484     uint8_t *d = dest;
485     for (unsigned int i = 0, sr = 0; i < dest_height; i++, sr += factor) {
486         for (unsigned int j = 0, sc = 0; j < dest_width; j++, sc += factor, d += 4) {
487             // calculate area average
488             unsigned int r=0, g=0, b=0, a=0, count=0;
489             for (unsigned int y=sr; y < MIN(sr + factor, src_height); y++) {
490                 uint8_t *p = src + (y * src_stride) + sc * 4;
491                 for (unsigned int x=sc; x < MIN(sc + factor, src_width); x++, count++) {
492                     b += *(p++); g += *(p++); r += *(p++); a += *(p++);
493                 }
494             }
495             if (count) {
496                 d[0] = b / count; d[1] = g / count; d[2] = r / count; d[3] = a / count;
497             }
498         }
499     }
500     return factor;
501 }
502 
503 static void
downsample_bitmap(ProcessedBitmap * bm,unsigned int width,unsigned int cell_height)504 downsample_bitmap(ProcessedBitmap *bm, unsigned int width, unsigned int cell_height) {
505     uint8_t *dest = calloc(4, (size_t)width * cell_height);
506     if (dest == NULL) fatal("Out of memory");
507     bm->factor = downsample_32bit_image(bm->buf, bm->width, bm->rows, bm->stride, dest, width, cell_height);
508     bm->buf = dest; bm->needs_free = true; bm->stride = 4 * width; bm->width = width; bm->rows = cell_height;
509 }
510 
511 static void
detect_right_edge(ProcessedBitmap * ans)512 detect_right_edge(ProcessedBitmap *ans) {
513     ans->right_edge = 0;
514     for (ssize_t x = ans->width - 1; !ans->right_edge && x > -1; x--) {
515         for (size_t y = 0; y < ans->rows && !ans->right_edge; y++) {
516             uint8_t *p = ans->buf + x * 4 + y * ans->stride;
517             if (p[3] > 20) ans->right_edge = x;
518         }
519     }
520 }
521 
522 static bool
render_color_bitmap(Face * self,int glyph_id,ProcessedBitmap * ans,unsigned int cell_width,unsigned int cell_height,unsigned int num_cells,unsigned int baseline UNUSED)523 render_color_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline UNUSED) {
524     unsigned short best = 0, diff = USHRT_MAX;
525     const short limit = self->face->num_fixed_sizes;
526     for (short i = 0; i < limit; i++) {
527         unsigned short w = self->face->available_sizes[i].width;
528         unsigned short d = w > (unsigned short)cell_width ? w - (unsigned short)cell_width : (unsigned short)cell_width - w;
529         if (d < diff) {
530             diff = d;
531             best = i;
532         }
533     }
534     FT_Error error = FT_Select_Size(self->face, best);
535     if (error) { set_freetype_error("Failed to set char size for non-scalable font, with error:", error); return false; }
536     if (!load_glyph(self, glyph_id, FT_LOAD_COLOR)) return false;
537     FT_Set_Char_Size(self->face, 0, self->char_height, self->xdpi, self->ydpi);
538     FT_Bitmap *bitmap = &self->face->glyph->bitmap;
539     if (bitmap->pixel_mode != FT_PIXEL_MODE_BGRA) return false;
540     ans->buf = bitmap->buffer;
541     ans->start_x = 0; ans->width = bitmap->width;
542     ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;
543     ans->rows = bitmap->rows;
544     ans->pixel_mode = bitmap->pixel_mode;
545     if (ans->width > num_cells * cell_width + 2) downsample_bitmap(ans, num_cells * cell_width, cell_height);
546     ans->bitmap_top = (int)((float)self->face->glyph->bitmap_top / ans->factor);
547     ans->bitmap_left = (int)((float)self->face->glyph->bitmap_left / ans->factor);
548     detect_right_edge(ans);
549     return true;
550 }
551 
552 
553 static void
copy_color_bitmap(uint8_t * src,pixel * dest,Region * src_rect,Region * dest_rect,size_t src_stride,size_t dest_stride)554 copy_color_bitmap(uint8_t *src, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {
555     for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {
556         pixel *d = dest + dest_stride * dr;
557         uint8_t *s = src + src_stride * sr;
558         for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {
559             uint8_t *bgra = s + 4 * sc;
560             if (bgra[3]) {
561 #define C(idx, shift) ( (uint8_t)(((float)bgra[idx] / (float)bgra[3]) * 255) << shift)
562                 d[dc] = C(2, 24) | C(1, 16) | C(0, 8) | bgra[3];
563 #undef C
564         } else d[dc] = 0;
565         }
566     }
567 }
568 
569 static const bool debug_placement = false;
570 
571 static void
place_bitmap_in_canvas(pixel * cell,ProcessedBitmap * bm,size_t cell_width,size_t cell_height,float x_offset,float y_offset,size_t baseline,unsigned int glyph_num)572 place_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size_t cell_height, float x_offset, float y_offset, size_t baseline, unsigned int glyph_num) {
573     // We want the glyph to be positioned inside the cell based on the bearingX
574     // and bearingY values, making sure that it does not overflow the cell.
575 
576     Region src = { .left = bm->start_x, .bottom = bm->rows, .right = bm->width + bm->start_x }, dest = { .bottom = cell_height, .right = cell_width };
577 
578     // Calculate column bounds
579     int32_t xoff = (int32_t)(x_offset + bm->bitmap_left);
580     if (debug_placement) printf(" bitmap_left: %d xoff: %d", bm->bitmap_left, xoff);
581     if (xoff < 0) src.left += -xoff;
582     else dest.left = xoff;
583     // Move the dest start column back if the width overflows because of it, but only if we are not in a very long/infinite ligature
584     if (glyph_num < 4 && dest.left > 0 && dest.left + bm->width > cell_width) {
585         uint32_t extra = dest.left + bm->width - cell_width;
586         dest.left = extra > dest.left ? 0 : dest.left - extra;
587     }
588 
589     // Calculate row bounds
590     int32_t yoff = (ssize_t)(y_offset + bm->bitmap_top);
591     if ((yoff > 0 && (size_t)yoff > baseline)) {
592         dest.top = 0;
593     } else {
594         dest.top = baseline - yoff;
595     }
596 
597     /* printf("x_offset: %d y_offset: %d src_start_row: %u src_start_column: %u dest_start_row: %u dest_start_column: %u bm_width: %lu bitmap_rows: %lu\n", xoff, yoff, src.top, src.left, dest.top, dest.left, bm->width, bm->rows); */
598 
599     if (bm->pixel_mode == FT_PIXEL_MODE_BGRA) {
600         copy_color_bitmap(bm->buf, cell, &src, &dest, bm->stride, cell_width);
601     } else render_alpha_mask(bm->buf, cell, &src, &dest, bm->stride, cell_width);
602 }
603 
604 static const ProcessedBitmap EMPTY_PBM = {.factor = 1};
605 
606 bool
render_glyphs_in_cells(PyObject * f,bool bold,bool italic,hb_glyph_info_t * info,hb_glyph_position_t * positions,unsigned int num_glyphs,pixel * canvas,unsigned int cell_width,unsigned int cell_height,unsigned int num_cells,unsigned int baseline,bool * was_colored,FONTS_DATA_HANDLE fg,bool center_glyph)607 render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE fg, bool center_glyph) {
608     Face *self = (Face*)f;
609     bool is_emoji = *was_colored; *was_colored = is_emoji && self->has_color;
610     float x = 0.f, y = 0.f, x_offset = 0.f;
611     ProcessedBitmap bm;
612     unsigned int canvas_width = cell_width * num_cells;
613     for (unsigned int i = 0; i < num_glyphs; i++) {
614         bm = EMPTY_PBM;
615         // dont load the space glyph since loading it fails for some fonts/sizes and it is anyway to be rendered as a blank
616         if (info[i].codepoint != self->space_glyph_id) {
617             if (*was_colored) {
618                 if (!render_color_bitmap(self, info[i].codepoint, &bm, cell_width, cell_height, num_cells, baseline)) {
619                     if (PyErr_Occurred()) PyErr_Print();
620                     if (!render_bitmap(self, info[i].codepoint, &bm, cell_width, cell_height, num_cells, bold, italic, true, fg)) {
621                         free_processed_bitmap(&bm);
622                         return false;
623                     }
624                     *was_colored = false;
625                 }
626             } else {
627                 if (!render_bitmap(self, info[i].codepoint, &bm, cell_width, cell_height, num_cells, bold, italic, true, fg)) {
628                     free_processed_bitmap(&bm);
629                     return false;
630                 }
631             }
632         }
633         x_offset = x + (float)positions[i].x_offset / 64.0f;
634         y = (float)positions[i].y_offset / 64.0f;
635         if (debug_placement) printf("%d: x=%f canvas: %u", i, x_offset, canvas_width);
636         if ((*was_colored || self->face->glyph->metrics.width > 0) && bm.width > 0) {
637             place_bitmap_in_canvas(canvas, &bm, canvas_width, cell_height, x_offset, y, baseline, i);
638         }
639         if (debug_placement) printf(" adv: %f\n", (float)positions[i].x_advance / 64.0f);
640         // the roundf() below is needed for infinite length ligatures, for a test case
641         // use: kitty --config None -o 'font_family Fira Code' -o 'font_size 4.5' sh -c
642         // "echo '|---|--------|-------|-------------|-------------|HH'; read"
643         // if this causes issues with non-infinite ligatures, we could choose this behavior
644         // based on num_glyphs and/or num_cells
645         x += roundf((float)positions[i].x_advance / 64.0f);
646         free_processed_bitmap(&bm);
647     }
648 
649     if (center_glyph && num_glyphs) {
650         unsigned int right_edge = (unsigned int)x, delta;
651         // x_advance is wrong for colored bitmaps that have been downsampled
652         if (*was_colored) right_edge = num_glyphs == 1 ? bm.right_edge : canvas_width;
653         if (num_cells > 1 && right_edge < canvas_width && (delta = (canvas_width - right_edge) / 2) && delta > 1) {
654             right_shift_canvas(canvas, canvas_width, cell_height, delta);
655         }
656     }
657     return true;
658 }
659 
660 static PyObject*
display_name(PyObject * s,PyObject * a UNUSED)661 display_name(PyObject *s, PyObject *a UNUSED) {
662     Face *self = (Face*)s;
663     const char *psname = FT_Get_Postscript_Name(self->face);
664     if (psname) return Py_BuildValue("s", psname);
665     Py_INCREF(self->path);
666     return self->path;
667 }
668 
669 static PyObject*
extra_data(PyObject * self,PyObject * a UNUSED)670 extra_data(PyObject *self, PyObject *a UNUSED) {
671     return PyLong_FromVoidPtr(((Face*)self)->extra_data);
672 }
673 
674 
675 StringCanvas
render_simple_text_impl(PyObject * s,const char * text,unsigned int baseline)676 render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {
677     Face *self = (Face*)s;
678     StringCanvas ans = {0};
679     size_t num_chars = strnlen(text, 32);
680     int max_char_width = font_units_to_pixels_x(self, self->face->max_advance_width);
681     size_t canvas_width = max_char_width * (num_chars*2);
682     size_t canvas_height = font_units_to_pixels_y(self, self->face->height) + 8;
683     pixel *canvas = calloc(canvas_width * canvas_height, sizeof(pixel));
684     if (!canvas) return ans;
685     size_t pen_x = 0;
686     ProcessedBitmap pbm;
687     for (size_t n = 0; n < num_chars; n++) {
688         FT_UInt glyph_index = FT_Get_Char_Index(self->face, text[n]);
689         int error = FT_Load_Glyph(self->face, glyph_index, FT_LOAD_DEFAULT);
690         if (error) continue;
691         error = FT_Render_Glyph(self->face->glyph, FT_RENDER_MODE_NORMAL);
692         if (error) continue;
693         FT_Bitmap *bitmap = &self->face->glyph->bitmap;
694         pbm = EMPTY_PBM;
695         populate_processed_bitmap(self->face->glyph, bitmap, &pbm, false);
696         place_bitmap_in_canvas(canvas, &pbm, canvas_width, canvas_height, pen_x, 0, baseline, n);
697         pen_x += self->face->glyph->advance.x >> 6;
698     }
699     ans.width = pen_x; ans.height = canvas_height;
700     ans.canvas = malloc(ans.width * ans.height);
701     if (ans.canvas) {
702         for (size_t row = 0; row < ans.height; row++) {
703             unsigned char *destp = ans.canvas + (ans.width * row);
704             pixel *srcp = canvas + (canvas_width * row);
705             for (size_t i = 0; i < ans.width; i++) destp[i] = srcp[i] & 0xff;
706         }
707     }
708     free(canvas);
709     return ans;
710 }
711 
712 // Boilerplate {{{
713 
714 static PyMemberDef members[] = {
715 #define MEM(name, type) {#name, type, offsetof(Face, name), READONLY, #name}
716     MEM(units_per_EM, T_UINT),
717     MEM(ascender, T_INT),
718     MEM(descender, T_INT),
719     MEM(height, T_INT),
720     MEM(max_advance_width, T_INT),
721     MEM(max_advance_height, T_INT),
722     MEM(underline_position, T_INT),
723     MEM(underline_thickness, T_INT),
724     MEM(strikethrough_position, T_INT),
725     MEM(strikethrough_thickness, T_INT),
726     MEM(is_scalable, T_BOOL),
727     MEM(path, T_OBJECT_EX),
728     {NULL}  /* Sentinel */
729 };
730 
731 static PyMethodDef methods[] = {
732     METHODB(display_name, METH_NOARGS),
733     METHODB(extra_data, METH_NOARGS),
734     {NULL}  /* Sentinel */
735 };
736 
737 
738 PyTypeObject Face_Type = {
739     PyVarObject_HEAD_INIT(NULL, 0)
740     .tp_name = "fast_data_types.Face",
741     .tp_basicsize = sizeof(Face),
742     .tp_dealloc = (destructor)dealloc,
743     .tp_flags = Py_TPFLAGS_DEFAULT,
744     .tp_doc = "FreeType Font face",
745     .tp_methods = methods,
746     .tp_members = members,
747     .tp_repr = (reprfunc)repr,
748 };
749 
750 static void
free_freetype(void)751 free_freetype(void) {
752     FT_Done_FreeType(library);
753 }
754 
755 bool
init_freetype_library(PyObject * m)756 init_freetype_library(PyObject *m) {
757     if (PyType_Ready(&Face_Type) < 0) return 0;
758     if (PyModule_AddObject(m, "Face", (PyObject *)&Face_Type) != 0) return 0;
759     Py_INCREF(&Face_Type);
760     FreeType_Exception = PyErr_NewException("fast_data_types.FreeTypeError", NULL, NULL);
761     if (FreeType_Exception == NULL) return false;
762     if (PyModule_AddObject(m, "FreeTypeError", FreeType_Exception) != 0) return false;
763     int error = FT_Init_FreeType(&library);
764     if (error) {
765         set_freetype_error("Failed to initialize FreeType library, with error:", error);
766         return false;
767     }
768     register_at_exit_cleanup_func(FREETYPE_CLEANUP_FUNC, free_freetype);
769     return true;
770 }
771 
772 // }}}
773