Refactor the font management code
Use a hash table for special glyph status caching Manage (almost) all global state with a single object Allocate all fonts on the heap Fix the use-after-free for fallback fonts
This commit is contained in:
parent
26e6716f67
commit
94c11af7c8
316
kitty/fonts.c
316
kitty/fonts.c
@ -17,6 +17,9 @@ static PyObject *python_send_to_gpu_impl = NULL;
|
|||||||
extern PyTypeObject Line_Type;
|
extern PyTypeObject Line_Type;
|
||||||
|
|
||||||
typedef struct SpritePosition SpritePosition;
|
typedef struct SpritePosition SpritePosition;
|
||||||
|
typedef struct SpecialGlyphCache SpecialGlyphCache;
|
||||||
|
enum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};
|
||||||
|
|
||||||
|
|
||||||
struct SpritePosition {
|
struct SpritePosition {
|
||||||
SpritePosition *next;
|
SpritePosition *next;
|
||||||
@ -27,6 +30,11 @@ struct SpritePosition {
|
|||||||
uint64_t extra_glyphs;
|
uint64_t extra_glyphs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SpecialGlyphCache {
|
||||||
|
SpecialGlyphCache *next;
|
||||||
|
glyph_index glyph;
|
||||||
|
bool is_special, filled;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t max_array_len, max_texture_size, max_y;
|
size_t max_array_len, max_texture_size, max_y;
|
||||||
@ -44,10 +52,25 @@ typedef struct {
|
|||||||
hb_font_t *hb_font;
|
hb_font_t *hb_font;
|
||||||
// Map glyphs to sprite map co-ords
|
// Map glyphs to sprite map co-ords
|
||||||
SpritePosition sprite_map[1024];
|
SpritePosition sprite_map[1024];
|
||||||
uint8_t special_glyph_cache[1 << (8 * sizeof(glyph_index))];
|
SpecialGlyphCache special_glyph_cache[1024];
|
||||||
bool bold, italic;
|
bool bold, italic;
|
||||||
} Font;
|
} Font;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_type left, right;
|
||||||
|
size_t font_idx;
|
||||||
|
} SymbolMap;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Font *fonts;
|
||||||
|
SymbolMap* symbol_maps;
|
||||||
|
size_t fonts_capacity, fonts_count, symbol_maps_capacity, symbol_maps_count, symbol_map_fonts_count, fallback_fonts_count;
|
||||||
|
ssize_t box_font_idx, medium_font_idx, bold_font_idx, italic_font_idx, bi_font_idx, first_symbol_font_idx, first_fallback_font_idx;
|
||||||
|
} Fonts;
|
||||||
|
|
||||||
|
static Fonts fonts = {0};
|
||||||
|
|
||||||
|
// Sprites {{{
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
sprite_map_set_error(int error) {
|
sprite_map_set_error(int error) {
|
||||||
@ -109,23 +132,47 @@ sprite_position_for(Font *font, glyph_index glyph, uint64_t extra_glyphs, uint8_
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SpecialGlyphCache*
|
||||||
|
special_glyph_cache_for(Font *font, glyph_index glyph) {
|
||||||
|
SpecialGlyphCache *s = font->special_glyph_cache + (glyph & 0x3ff);
|
||||||
|
// Optimize for the common case of glyph under 1024 already in the cache
|
||||||
|
if (LIKELY(s->glyph == glyph && s->filled)) return s; // Cache hit
|
||||||
|
while(true) {
|
||||||
|
if (s->filled) {
|
||||||
|
if (s->glyph == glyph) return s; // Cache hit
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!s->next) {
|
||||||
|
s->next = calloc(1, sizeof(SpecialGlyphCache));
|
||||||
|
if (s->next == NULL) return NULL;
|
||||||
|
}
|
||||||
|
s = s->next;
|
||||||
|
}
|
||||||
|
s->glyph = glyph;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z) {
|
sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z) {
|
||||||
*x = sprite_tracker.xnum; *y = sprite_tracker.ynum; *z = sprite_tracker.z;
|
*x = sprite_tracker.xnum; *y = sprite_tracker.ynum; *z = sprite_tracker.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sprite_map_free(Font *font) {
|
free_maps(Font *font) {
|
||||||
SpritePosition *s, *t;
|
#define free_a_map(type, attr) {\
|
||||||
for (size_t i = 0; i < sizeof(font->sprite_map)/sizeof(font->sprite_map[0]); i++) {
|
type *s, *t; \
|
||||||
s = font->sprite_map + i;
|
for (size_t i = 0; i < sizeof(font->attr)/sizeof(font->attr[0]); i++) { \
|
||||||
s = s->next;
|
s = font->attr[i].next; \
|
||||||
while (s) {
|
while (s) { \
|
||||||
t = s;
|
t = s; \
|
||||||
s = s->next;
|
s = s->next; \
|
||||||
free(t);
|
free(t); \
|
||||||
}
|
} \
|
||||||
}
|
}}
|
||||||
|
free_a_map(SpritePosition, sprite_map);
|
||||||
|
free_a_map(SpecialGlyphCache, special_glyph_cache);
|
||||||
|
#undef free_a_map
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -142,6 +189,20 @@ clear_sprite_map(Font *font) {
|
|||||||
#undef CLEAR
|
#undef CLEAR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clear_special_glyph_cache(Font *font) {
|
||||||
|
#define CLEAR(s) s->filled = false; s->glyph = 0;
|
||||||
|
SpecialGlyphCache *s;
|
||||||
|
for (size_t i = 0; i < sizeof(font->special_glyph_cache)/sizeof(font->special_glyph_cache[0]); i++) {
|
||||||
|
s = font->special_glyph_cache + i;
|
||||||
|
CLEAR(s);
|
||||||
|
while ((s = s->next)) {
|
||||||
|
CLEAR(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef CLEAR
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sprite_tracker_set_layout(unsigned int cell_width, unsigned int cell_height) {
|
sprite_tracker_set_layout(unsigned int cell_width, unsigned int cell_height) {
|
||||||
sprite_tracker.xnum = MIN(MAX(1, sprite_tracker.max_texture_size / cell_width), UINT16_MAX);
|
sprite_tracker.xnum = MIN(MAX(1, sprite_tracker.max_texture_size / cell_width), UINT16_MAX);
|
||||||
@ -149,6 +210,7 @@ sprite_tracker_set_layout(unsigned int cell_width, unsigned int cell_height) {
|
|||||||
sprite_tracker.ynum = 1;
|
sprite_tracker.ynum = 1;
|
||||||
sprite_tracker.x = 0; sprite_tracker.y = 0; sprite_tracker.z = 0;
|
sprite_tracker.x = 0; sprite_tracker.y = 0; sprite_tracker.z = 0;
|
||||||
}
|
}
|
||||||
|
// }}}
|
||||||
|
|
||||||
static inline PyObject*
|
static inline PyObject*
|
||||||
desc_to_face(PyObject *desc) {
|
desc_to_face(PyObject *desc) {
|
||||||
@ -161,7 +223,7 @@ desc_to_face(PyObject *desc) {
|
|||||||
|
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
alloc_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face) {
|
init_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face) {
|
||||||
PyObject *face;
|
PyObject *face;
|
||||||
if (is_face) { face = descriptor; Py_INCREF(face); }
|
if (is_face) { face = descriptor; Py_INCREF(face); }
|
||||||
else { face = desc_to_face(descriptor); if (face == NULL) return false; }
|
else { face = desc_to_face(descriptor); if (face == NULL) return false; }
|
||||||
@ -173,10 +235,10 @@ alloc_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
free_font(Font *f) {
|
del_font(Font *f) {
|
||||||
f->hb_font = NULL;
|
f->hb_font = NULL;
|
||||||
Py_CLEAR(f->face);
|
Py_CLEAR(f->face);
|
||||||
sprite_map_free(f);
|
free_maps(f);
|
||||||
f->bold = false; f->italic = false;
|
f->bold = false; f->italic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,25 +246,11 @@ static inline void
|
|||||||
clear_font(Font *f) {
|
clear_font(Font *f) {
|
||||||
f->hb_font = NULL;
|
f->hb_font = NULL;
|
||||||
Py_CLEAR(f->face);
|
Py_CLEAR(f->face);
|
||||||
clear_sprite_map(f);
|
clear_sprite_map(f); clear_special_glyph_cache(f);
|
||||||
memset(f->special_glyph_cache, 0, sizeof(f->special_glyph_cache));
|
|
||||||
f->bold = false; f->italic = false;
|
f->bold = false; f->italic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Font medium_font = {0}, bold_font = {0}, italic_font = {0}, bi_font = {0}, box_font = {0};
|
|
||||||
static Font *fallback_fonts = NULL;
|
|
||||||
static size_t fallback_fonts_count = 0;
|
|
||||||
typedef enum { FONT, BLANK_FONT, BOX_FONT, MISSING_FONT } FontType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char_type left, right;
|
|
||||||
size_t font_idx;
|
|
||||||
} SymbolMap;
|
|
||||||
static SymbolMap* symbol_maps = NULL;
|
|
||||||
static Font *symbol_map_fonts = NULL;
|
|
||||||
static size_t symbol_maps_count = 0, symbol_map_fonts_count = 0;
|
|
||||||
|
|
||||||
static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_position = 0, underline_thickness = 0;
|
static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_position = 0, underline_thickness = 0;
|
||||||
static uint8_t *canvas = NULL;
|
static uint8_t *canvas = NULL;
|
||||||
#define CELLS_IN_CANVAS 16
|
#define CELLS_IN_CANVAS 16
|
||||||
@ -221,9 +269,10 @@ python_send_to_gpu(unsigned int x, unsigned int y, unsigned int z, uint8_t* buf)
|
|||||||
|
|
||||||
static inline PyObject*
|
static inline PyObject*
|
||||||
update_cell_metrics() {
|
update_cell_metrics() {
|
||||||
#define CALL(f, desired_height, force) { if ((f)->face) { if(!set_size_for_face((f)->face, desired_height, force)) return NULL; (f)->hb_font = harfbuzz_font_for_face((f)->face); } clear_sprite_map((f)); }
|
#define CALL(idx, desired_height, force) { if (idx >= 0) { Font *f = fonts.fonts + idx; if ((f)->face) { if(!set_size_for_face((f)->face, desired_height, force)) return NULL; (f)->hb_font = harfbuzz_font_for_face((f)->face); } clear_sprite_map((f)); }}
|
||||||
CALL(&medium_font, 0, false); CALL(&bold_font, 0, false); CALL(&italic_font, 0, false); CALL(&bi_font, 0, false); CALL(&box_font, 0, false);
|
CALL(BOX_FONT, 0, false); CALL(fonts.medium_font_idx, 0, false);
|
||||||
cell_metrics(medium_font.face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
|
CALL(fonts.bold_font_idx, 0, false); CALL(fonts.italic_font_idx, 0, false); CALL(fonts.bi_font_idx, 0, false);
|
||||||
|
cell_metrics(fonts.fonts[fonts.medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
|
||||||
if (!cell_width) { PyErr_SetString(PyExc_ValueError, "Failed to calculate cell width for the specified font."); return NULL; }
|
if (!cell_width) { PyErr_SetString(PyExc_ValueError, "Failed to calculate cell width for the specified font."); return NULL; }
|
||||||
if (OPT(adjust_line_height_px) != 0) cell_height += OPT(adjust_line_height_px);
|
if (OPT(adjust_line_height_px) != 0) cell_height += OPT(adjust_line_height_px);
|
||||||
if (OPT(adjust_line_height_frac) != 0.f) cell_height *= OPT(adjust_line_height_frac);
|
if (OPT(adjust_line_height_frac) != 0.f) cell_height *= OPT(adjust_line_height_frac);
|
||||||
@ -234,11 +283,11 @@ update_cell_metrics() {
|
|||||||
global_state.cell_width = cell_width; global_state.cell_height = cell_height;
|
global_state.cell_width = cell_width; global_state.cell_height = cell_height;
|
||||||
free(canvas); canvas = malloc(CELLS_IN_CANVAS * cell_width * cell_height);
|
free(canvas); canvas = malloc(CELLS_IN_CANVAS * cell_width * cell_height);
|
||||||
if (canvas == NULL) return PyErr_NoMemory();
|
if (canvas == NULL) return PyErr_NoMemory();
|
||||||
for (size_t i = 0; i < fallback_fonts_count; i++) {
|
for (ssize_t i = 0, j = fonts.first_symbol_font_idx; i < (ssize_t)fonts.symbol_map_fonts_count; i++, j++) {
|
||||||
CALL(fallback_fonts + i, cell_height, true);
|
CALL(j, cell_height, true);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < symbol_map_fonts_count; i++) {
|
for (ssize_t i = 0, j = fonts.first_fallback_font_idx; i < (ssize_t)fonts.fallback_fonts_count; i++, j++) {
|
||||||
CALL(symbol_map_fonts + i, cell_height, true);
|
CALL(j, cell_height, true);
|
||||||
}
|
}
|
||||||
return Py_BuildValue("IIIII", cell_width, cell_height, baseline, underline_position, underline_thickness);
|
return Py_BuildValue("IIIII", cell_width, cell_height, baseline, underline_position, underline_thickness);
|
||||||
#undef CALL
|
#undef CALL
|
||||||
@ -266,52 +315,53 @@ has_cell_text(Font *self, Cell *cell) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline ssize_t
|
||||||
static inline Font*
|
|
||||||
fallback_font(Cell *cell) {
|
fallback_font(Cell *cell) {
|
||||||
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
||||||
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
||||||
Font *base_font, *ans;
|
ssize_t f;
|
||||||
|
|
||||||
for (size_t i = 0; i < fallback_fonts_count; i++) {
|
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
|
||||||
if (fallback_fonts[i].bold == bold && fallback_fonts[i].italic == italic && has_cell_text(fallback_fonts + i, cell)) {
|
Font *ff = fonts.fonts +j;
|
||||||
return fallback_fonts + i;
|
if (ff->bold == bold && ff->italic == italic && has_cell_text(ff, cell)) {
|
||||||
|
return j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return NULL; }
|
if (fonts.fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return MISSING_FONT; }
|
||||||
|
|
||||||
if (bold) base_font = italic ? &bi_font : &bold_font;
|
if (bold) f = fonts.italic_font_idx > 0 ? fonts.bi_font_idx : fonts.bold_font_idx;
|
||||||
else base_font = italic ? &italic_font : &medium_font;
|
else f = italic ? fonts.italic_font_idx : fonts.medium_font_idx;
|
||||||
if (!base_font->face) base_font = &medium_font;
|
if (f < 0) f = fonts.medium_font_idx;
|
||||||
|
|
||||||
PyObject *face = create_fallback_face(base_font->face, cell, bold, italic);
|
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic);
|
||||||
if (face == NULL) { PyErr_Print(); return NULL; }
|
if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
|
||||||
if (face == Py_None) { Py_DECREF(face); return NULL; }
|
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
|
||||||
set_size_for_face(face, cell_height, true);
|
set_size_for_face(face, cell_height, true);
|
||||||
|
|
||||||
fallback_fonts = realloc(fallback_fonts, sizeof(Font) * (fallback_fonts_count + 1));
|
ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true);
|
||||||
if (fallback_fonts == NULL) fatal("Out of memory");
|
ssize_t ans = fonts.first_fallback_font_idx + fonts.fallback_fonts_count;
|
||||||
ans = fallback_fonts + fallback_fonts_count;
|
Font *af = &fonts.fonts[ans];
|
||||||
memset(ans, 0, sizeof(Font));
|
if (!init_font(af, face, bold, italic, true)) fatal("Out of memory");
|
||||||
|
|
||||||
if (!alloc_font(ans, face, bold, italic, true)) fatal("Out of memory");
|
|
||||||
Py_DECREF(face);
|
Py_DECREF(face);
|
||||||
fallback_fonts_count++;
|
fonts.fallback_fonts_count++;
|
||||||
|
fonts.fonts_count++;
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Font*
|
static inline ssize_t
|
||||||
in_symbol_maps(char_type ch) {
|
in_symbol_maps(char_type ch) {
|
||||||
for (size_t i = 0; i < symbol_maps_count; i++) {
|
for (size_t i = 0; i < fonts.symbol_maps_count; i++) {
|
||||||
if (symbol_maps[i].left <= ch && ch <= symbol_maps[i].right) return symbol_map_fonts + symbol_maps[i].font_idx;
|
if (fonts.symbol_maps[i].left <= ch && ch <= fonts.symbol_maps[i].right) return fonts.first_symbol_font_idx + fonts.symbol_maps[i].font_idx;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NO_FONT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FontType
|
|
||||||
font_for_cell(Cell *cell, Font** font) {
|
static ssize_t
|
||||||
|
font_for_cell(Cell *cell) {
|
||||||
START_ALLOW_CASE_RANGE
|
START_ALLOW_CASE_RANGE
|
||||||
|
ssize_t ans;
|
||||||
switch(cell->ch) {
|
switch(cell->ch) {
|
||||||
case 0:
|
case 0:
|
||||||
case ' ':
|
case ' ':
|
||||||
@ -322,25 +372,21 @@ START_ALLOW_CASE_RANGE
|
|||||||
case 0xe0b2:
|
case 0xe0b2:
|
||||||
return BOX_FONT;
|
return BOX_FONT;
|
||||||
default:
|
default:
|
||||||
*font = in_symbol_maps(cell->ch);
|
ans = in_symbol_maps(cell->ch);
|
||||||
if (*font != NULL) return FONT;
|
if (ans > -1) return ans;
|
||||||
switch(BI_VAL(cell->attrs)) {
|
switch(BI_VAL(cell->attrs)) {
|
||||||
case 0:
|
case 0:
|
||||||
*font = &medium_font;
|
ans = fonts.medium_font_idx; break;
|
||||||
break;
|
|
||||||
case 1:
|
case 1:
|
||||||
*font = bold_font.face ? &bold_font : &medium_font;
|
ans = fonts.bold_font_idx ; break;
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
*font = italic_font.face ? &italic_font : &medium_font;
|
ans = fonts.italic_font_idx; break;
|
||||||
break;
|
|
||||||
case 3:
|
case 3:
|
||||||
*font = bi_font.face ? &bi_font : &medium_font;
|
ans = fonts.bi_font_idx; break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (has_cell_text(*font, cell)) return FONT;
|
if (ans < 0) ans = fonts.medium_font_idx;
|
||||||
*font = fallback_font(cell);
|
if (has_cell_text(fonts.fonts + ans, cell)) return ans;
|
||||||
return *font ? FONT : MISSING_FONT;
|
return fallback_font(cell);
|
||||||
}
|
}
|
||||||
END_ALLOW_CASE_RANGE
|
END_ALLOW_CASE_RANGE
|
||||||
}
|
}
|
||||||
@ -372,7 +418,7 @@ static void
|
|||||||
render_box_cell(Cell *cell) {
|
render_box_cell(Cell *cell) {
|
||||||
int error = 0;
|
int error = 0;
|
||||||
glyph_index glyph = box_glyph_id(cell->ch);
|
glyph_index glyph = box_glyph_id(cell->ch);
|
||||||
SpritePosition *sp = sprite_position_for(&box_font, glyph, 0, false, &error);
|
SpritePosition *sp = sprite_position_for(&fonts.fonts[BOX_FONT], glyph, 0, false, &error);
|
||||||
if (sp == NULL) {
|
if (sp == NULL) {
|
||||||
sprite_map_set_error(error); PyErr_Print();
|
sprite_map_set_error(error); PyErr_Print();
|
||||||
set_sprite(cell, 0, 0, 0);
|
set_sprite(cell, 0, 0, 0);
|
||||||
@ -460,13 +506,16 @@ static inline bool
|
|||||||
is_special_glyph(glyph_index glyph_id, Font *font, CellData* cell_data) {
|
is_special_glyph(glyph_index glyph_id, Font *font, CellData* cell_data) {
|
||||||
// A glyph is special if the codepoint it corresponds to matches a
|
// A glyph is special if the codepoint it corresponds to matches a
|
||||||
// different glyph in the font
|
// different glyph in the font
|
||||||
if (font->special_glyph_cache[glyph_id] == 0) {
|
SpecialGlyphCache *s = special_glyph_cache_for(font, glyph_id);
|
||||||
font->special_glyph_cache[glyph_id] = cell_data->current_codepoint ? (
|
if (s == NULL) return false;
|
||||||
glyph_id != glyph_id_for_codepoint(font->face, cell_data->current_codepoint) ? 1 : 2)
|
if (!s->filled) {
|
||||||
|
s->is_special = cell_data->current_codepoint ? (
|
||||||
|
glyph_id != glyph_id_for_codepoint(font->face, cell_data->current_codepoint) ? true : false)
|
||||||
:
|
:
|
||||||
2;
|
false;
|
||||||
|
s->filled = true;
|
||||||
}
|
}
|
||||||
return font->special_glyph_cache[glyph_id] & 1;
|
return s->is_special;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int
|
static inline unsigned int
|
||||||
@ -608,9 +657,8 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL;
|
if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL;
|
||||||
index_type num = 0;
|
index_type num = 0;
|
||||||
while(num < line->xnum && line->cells[num].ch) num += line->cells[num].attrs & WIDTH_MASK;
|
while(num < line->xnum && line->cells[num].ch) num += line->cells[num].attrs & WIDTH_MASK;
|
||||||
load_hb_buffer(line->cells, num);
|
|
||||||
PyObject *face = NULL;
|
PyObject *face = NULL;
|
||||||
Font *font = &medium_font;
|
Font *font = fonts.fonts + fonts.medium_font_idx;
|
||||||
if (path) {
|
if (path) {
|
||||||
face = face_from_path(path, index);
|
face = face_from_path(path, index);
|
||||||
if (face == NULL) return NULL;
|
if (face == NULL) return NULL;
|
||||||
@ -618,7 +666,6 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||||||
font->hb_font = harfbuzz_font_for_face(face);
|
font->hb_font = harfbuzz_font_for_face(face);
|
||||||
font->face = face;
|
font->face = face;
|
||||||
}
|
}
|
||||||
hb_shape(font->hb_font, harfbuzz_buffer, NULL, 0);
|
|
||||||
hb_glyph_info_t *info;
|
hb_glyph_info_t *info;
|
||||||
hb_glyph_position_t *positions;
|
hb_glyph_position_t *positions;
|
||||||
unsigned int num_glyphs = shape(line->cells, num, font->hb_font, &info, &positions);
|
unsigned int num_glyphs = shape(line->cells, num, font->hb_font, &info, &positions);
|
||||||
@ -636,10 +683,10 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_run(Cell *first_cell, index_type num_cells, Font *font, FontType ft) {
|
render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) {
|
||||||
switch(ft) {
|
switch(font_idx) {
|
||||||
case FONT:
|
default:
|
||||||
shape_run(first_cell, num_cells, font);
|
shape_run(first_cell, num_cells, &fonts.fonts[font_idx]);
|
||||||
break;
|
break;
|
||||||
case BLANK_FONT:
|
case BLANK_FONT:
|
||||||
while(num_cells--) set_sprite(first_cell++, 0, 0, 0);
|
while(num_cells--) set_sprite(first_cell++, 0, 0, 0);
|
||||||
@ -655,20 +702,19 @@ render_run(Cell *first_cell, index_type num_cells, Font *font, FontType ft) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
render_line(Line *line) {
|
render_line(Line *line) {
|
||||||
#define RENDER if ((run_font != NULL || run_font_type != FONT) && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font, run_font_type);
|
#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx);
|
||||||
Font *run_font = NULL;
|
ssize_t run_font_idx = NO_FONT;
|
||||||
FontType run_font_type = MISSING_FONT;
|
|
||||||
index_type first_cell_in_run, i;
|
index_type first_cell_in_run, i;
|
||||||
attrs_type prev_width = 0;
|
attrs_type prev_width = 0;
|
||||||
for (i=0, first_cell_in_run=0; i < line->xnum; i++) {
|
for (i=0, first_cell_in_run=0; i < line->xnum; i++) {
|
||||||
if (prev_width == 2) { prev_width = 0; continue; }
|
if (prev_width == 2) { prev_width = 0; continue; }
|
||||||
Cell *cell = line->cells + i;
|
Cell *cell = line->cells + i;
|
||||||
Font *cell_font = NULL;
|
ssize_t cell_font_idx = font_for_cell(cell);
|
||||||
FontType cell_font_type = font_for_cell(cell, &cell_font);
|
|
||||||
prev_width = cell->attrs & WIDTH_MASK;
|
prev_width = cell->attrs & WIDTH_MASK;
|
||||||
if (cell_font_type == run_font_type && cell_font == run_font) continue;
|
if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx;
|
||||||
|
if (run_font_idx == cell_font_idx) continue;
|
||||||
RENDER;
|
RENDER;
|
||||||
run_font = cell_font; run_font_type = cell_font_type;
|
run_font_idx = cell_font_idx;
|
||||||
first_cell_in_run = i;
|
first_cell_in_run = i;
|
||||||
}
|
}
|
||||||
RENDER;
|
RENDER;
|
||||||
@ -681,36 +727,32 @@ set_font(PyObject UNUSED *m, PyObject *args) {
|
|||||||
Py_CLEAR(box_drawing_function);
|
Py_CLEAR(box_drawing_function);
|
||||||
if (!PyArg_ParseTuple(args, "OO!O!fO|OOO", &box_drawing_function, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &global_state.font_sz_in_pts, &medium, &bold, &italic, &bi)) return NULL;
|
if (!PyArg_ParseTuple(args, "OO!O!fO|OOO", &box_drawing_function, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &global_state.font_sz_in_pts, &medium, &bold, &italic, &bi)) return NULL;
|
||||||
Py_INCREF(box_drawing_function);
|
Py_INCREF(box_drawing_function);
|
||||||
clear_font(&medium_font); clear_font(&bold_font); clear_font(&italic_font); clear_font(&bi_font); clear_sprite_map(&box_font);
|
fonts.symbol_map_fonts_count = PyTuple_GET_SIZE(smf);
|
||||||
if (!alloc_font(&medium_font, medium, false, false, false)) return NULL;
|
size_t num_fonts = 5 + fonts.symbol_map_fonts_count;
|
||||||
if (bold && !alloc_font(&bold_font, bold, true, false, false)) return NULL;
|
for (size_t i = 0; i < fonts.fonts_count; i++) del_font(fonts.fonts + i);
|
||||||
if (italic && !alloc_font(&italic_font, italic, false, true, false)) return NULL;
|
ensure_space_for(&fonts, fonts, Font, num_fonts, fonts_capacity, 5, true);
|
||||||
if (bi && !alloc_font(&bi_font, bi, true, true, false)) return NULL;
|
fonts.fonts_count = 1;
|
||||||
for (size_t i = 0; i < fallback_fonts_count; i++) free_font(fallback_fonts + i);
|
#define A(attr, bold, italic) { if(attr) { if (!init_font(&fonts.fonts[fonts.fonts_count], attr, bold, italic, false)) return NULL; fonts.attr##_font_idx = fonts.fonts_count++; } else fonts.attr##_font_idx = -1; }
|
||||||
for (size_t i = 0; symbol_map_fonts_count; i++) free_font(symbol_map_fonts + i);
|
A(medium, false, false);
|
||||||
free(fallback_fonts); fallback_fonts = NULL; fallback_fonts_count = 0;
|
A(bold, true, false); A(italic, false, true); A(bi, true, true);
|
||||||
free(symbol_maps); free(symbol_map_fonts); symbol_maps = NULL; symbol_map_fonts = NULL;
|
#undef A
|
||||||
symbol_maps_count = 0; symbol_map_fonts_count = 0;
|
|
||||||
|
|
||||||
symbol_maps_count = PyTuple_GET_SIZE(sm);
|
fonts.first_symbol_font_idx = fonts.fonts_count;
|
||||||
if (symbol_maps_count > 0) {
|
fonts.symbol_maps_count = PyTuple_GET_SIZE(sm);
|
||||||
symbol_maps = malloc(symbol_maps_count * sizeof(SymbolMap));
|
ensure_space_for(&fonts, symbol_maps, SymbolMap, fonts.symbol_maps_count, symbol_maps_capacity, 5, true);
|
||||||
symbol_map_fonts_count = PyTuple_GET_SIZE(smf);
|
for (size_t i = 0; i < fonts.symbol_map_fonts_count; i++) {
|
||||||
symbol_map_fonts = calloc(symbol_map_fonts_count, sizeof(Font));
|
|
||||||
if (symbol_maps == NULL || symbol_map_fonts == NULL) return PyErr_NoMemory();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < symbol_map_fonts_count; i++) {
|
|
||||||
PyObject *face;
|
PyObject *face;
|
||||||
int bold, italic;
|
int bold, italic;
|
||||||
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, i), "Opp", &face, &bold, &italic)) return NULL;
|
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, i), "Opp", &face, &bold, &italic)) return NULL;
|
||||||
if (!alloc_font(symbol_map_fonts + i, face, bold != 0, italic != 0, false)) return NULL;
|
if (!init_font(fonts.fonts + fonts.fonts_count++, face, bold != 0, italic != 0, false)) return NULL;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < symbol_maps_count; i++) {
|
for (size_t i = 0; i < fonts.symbol_maps_count; i++) {
|
||||||
unsigned int left, right, font_idx;
|
unsigned int left, right, font_idx;
|
||||||
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(sm, i), "III", &left, &right, &font_idx)) return NULL;
|
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(sm, i), "III", &left, &right, &font_idx)) return NULL;
|
||||||
symbol_maps[i].left = left; symbol_maps[i].right = right; symbol_maps[i].font_idx = font_idx;
|
fonts.symbol_maps[i].left = left; fonts.symbol_maps[i].right = right; fonts.symbol_maps[i].font_idx = font_idx;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
fonts.first_fallback_font_idx = fonts.fonts_count;
|
||||||
|
fonts.fallback_fonts_count = 0;
|
||||||
return update_cell_metrics();
|
return update_cell_metrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,10 +761,8 @@ finalize(void) {
|
|||||||
Py_CLEAR(python_send_to_gpu_impl);
|
Py_CLEAR(python_send_to_gpu_impl);
|
||||||
free(canvas);
|
free(canvas);
|
||||||
Py_CLEAR(box_drawing_function);
|
Py_CLEAR(box_drawing_function);
|
||||||
free_font(&medium_font); free_font(&bold_font); free_font(&italic_font); free_font(&bi_font); free_font(&box_font);
|
for (size_t i = 0; i < fonts.fonts_count; i++) del_font(fonts.fonts + i);
|
||||||
for (size_t i = 0; i < fallback_fonts_count; i++) free_font(fallback_fonts + i);
|
free(fonts.symbol_maps); free(fonts.fonts);
|
||||||
for (size_t i = 0; i < symbol_map_fonts_count; i++) free_font(symbol_map_fonts + i);
|
|
||||||
free(symbol_maps); free(symbol_map_fonts); free(fallback_fonts);
|
|
||||||
if (harfbuzz_buffer) hb_buffer_destroy(harfbuzz_buffer);
|
if (harfbuzz_buffer) hb_buffer_destroy(harfbuzz_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,7 +788,7 @@ test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
|||||||
uint64_t extra_glyphs = 0;
|
uint64_t extra_glyphs = 0;
|
||||||
if (!PyArg_ParseTuple(args, "H|I", &glyph, &extra_glyphs)) return NULL;
|
if (!PyArg_ParseTuple(args, "H|I", &glyph, &extra_glyphs)) return NULL;
|
||||||
int error;
|
int error;
|
||||||
SpritePosition *pos = sprite_position_for(&medium_font, glyph, extra_glyphs, 0, &error);
|
SpritePosition *pos = sprite_position_for(&fonts.fonts[fonts.medium_font_idx], glyph, extra_glyphs, 0, &error);
|
||||||
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
|
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
|
||||||
return Py_BuildValue("HHH", pos->x, pos->y, pos->z);
|
return Py_BuildValue("HHH", pos->x, pos->y, pos->z);
|
||||||
}
|
}
|
||||||
@ -813,22 +853,18 @@ static PyObject*
|
|||||||
current_fonts(PyObject UNUSED *self) {
|
current_fonts(PyObject UNUSED *self) {
|
||||||
PyObject *ans = PyDict_New();
|
PyObject *ans = PyDict_New();
|
||||||
if (!ans) return NULL;
|
if (!ans) return NULL;
|
||||||
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, val) != 0) { goto error; }}
|
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fonts.fonts[val].face) != 0) { goto error; }}
|
||||||
SET(medium, medium_font.face);
|
SET(medium, fonts.medium_font_idx);
|
||||||
if (bold_font.face) SET(bold, bold_font.face);
|
if (fonts.bold_font_idx) SET(bold, fonts.bold_font_idx);
|
||||||
if (italic_font.face) SET(italic, italic_font.face);
|
if (fonts.italic_font_idx) SET(italic, fonts.italic_font_idx);
|
||||||
if (bi_font.face) SET(bi, bi_font.face);
|
if (fonts.bi_font_idx) SET(bi, fonts.bi_font_idx);
|
||||||
int num = 0;
|
PyObject *ff = PyTuple_New(fonts.fallback_fonts_count);
|
||||||
while (fallback_fonts[num].face) num++;
|
|
||||||
PyObject *ff = PyTuple_New(num);
|
|
||||||
if (!ff) goto error;
|
if (!ff) goto error;
|
||||||
num = 0;
|
for (size_t i = 0; i < fonts.fallback_fonts_count; i++) {
|
||||||
while (fallback_fonts[num].face) {
|
Py_INCREF(fonts.fonts[fonts.first_fallback_font_idx + i].face);
|
||||||
Py_INCREF(fallback_fonts[num].face);
|
PyTuple_SET_ITEM(ff, i, fonts.fonts[fonts.first_fallback_font_idx + i].face);
|
||||||
PyTuple_SET_ITEM(ff, num, fallback_fonts[num].face);
|
|
||||||
num++;
|
|
||||||
}
|
}
|
||||||
SET(fallback, ff);
|
PyDict_SetItemString(ans, "fallback", ff);
|
||||||
Py_CLEAR(ff);
|
Py_CLEAR(ff);
|
||||||
return ans;
|
return ans;
|
||||||
error:
|
error:
|
||||||
@ -849,9 +885,9 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (PyUnicode_GetLength(text) > 2) cell.cc |= (char_buf[2] & CC_MASK) << 16;
|
if (PyUnicode_GetLength(text) > 2) cell.cc |= (char_buf[2] & CC_MASK) << 16;
|
||||||
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
||||||
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
||||||
Font *ans = fallback_font(&cell);
|
ssize_t ans = fallback_font(&cell);
|
||||||
if (ans == NULL) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
|
if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
|
||||||
return ans->face;
|
return fonts.fonts[ans].face;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user