diff --git a/kitty/fonts.c b/kitty/fonts.c index 325cee6fd..eb0f08845 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -33,11 +33,7 @@ typedef struct { } GPUSpriteTracker; -static GPUSpriteTracker sprite_tracker = { - .max_array_len = 1000, - .max_texture_size = 1000, - .max_y = 100, -}; +static GPUSpriteTracker sprite_tracker = {0}; typedef struct { @@ -177,9 +173,10 @@ clear_font(Font *f) { } -static Font medium_font = {0}, bold_font = {0}, italic_font = {0}, bi_font = {0}, box_font = {0}, missing_font = {0}, blank_font = {0}; +static Font medium_font = {0}, bold_font = {0}, italic_font = {0}, bi_font = {0}, box_font = {0}; static Font fallback_fonts[256] = {{0}}; static PyObject *get_fallback_font = NULL; +typedef enum { FONT, BLANK_FONT, BOX_FONT, MISSING_FONT } FontType; typedef struct { char_type left, right; @@ -196,7 +193,7 @@ clear_canvas(void) { memset(canvas, 0, cell_width * cell_height); } static void python_send_to_gpu(unsigned int x, unsigned int y, unsigned int z, uint8_t* buf) { - if (python_send_to_gpu_impl != NULL) { + if (python_send_to_gpu_impl != NULL && python_send_to_gpu_impl != Py_None) { PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIN", x, y, z, PyBytes_FromStringAndSize((const char*)buf, cell_width * cell_height)); if (ret == NULL) PyErr_Print(); else Py_DECREF(ret); @@ -207,7 +204,7 @@ python_send_to_gpu(unsigned int x, unsigned int y, unsigned int z, uint8_t* buf) static inline PyObject* update_cell_metrics(float pt_sz, float xdpi, float ydpi) { #define CALL(f) { if ((f)->face) { if(!set_size_for_face((f)->face, pt_sz, xdpi, ydpi)) return NULL; clear_sprite_map(f); } } - CALL(&medium_font); CALL(&bold_font); CALL(&italic_font); CALL(&bi_font); + CALL(&medium_font); CALL(&bold_font); CALL(&italic_font); CALL(&bi_font); CALL(&box_font); for (size_t i = 0; fallback_fonts[i].face != NULL; i++) { CALL(fallback_fonts + i); } @@ -258,12 +255,12 @@ fallback_font(Cell *cell) { return fallback_fonts + i; } } - if (get_fallback_font == NULL || i == (sizeof(fallback_fonts)/sizeof(fallback_fonts[0])-1)) return &missing_font; + if (get_fallback_font == NULL || i == (sizeof(fallback_fonts)/sizeof(fallback_fonts[0])-1)) return NULL; Py_UCS4 buf[10]; size_t n = cell_as_unicode(cell, true, buf, ' '); PyObject *face = PyObject_CallFunction(get_fallback_font, "NOO", PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n), bold ? Py_True : Py_False, italic ? Py_True : Py_False); - if (face == NULL) { PyErr_Print(); return &missing_font; } - if (face == Py_None) { Py_DECREF(face); return &missing_font; } + if (face == NULL) { PyErr_Print(); return NULL; } + if (face == Py_None) { Py_DECREF(face); return NULL; } if (!alloc_font(fallback_fonts + i, face, bold, italic)) { fatal("Out of memory"); } return fallback_fonts + i; } @@ -276,39 +273,37 @@ in_symbol_maps(char_type ch) { return NULL; } - -static Font* -font_for_cell(Cell *cell) { - Font *ans; +static FontType +font_for_cell(Cell *cell, Font** font) { START_ALLOW_CASE_RANGE switch(cell->ch) { case 0: - return &blank_font; + return BLANK_FONT; case 0x2500 ... 0x2570: case 0x2574 ... 0x257f: case 0xe0b0: case 0xe0b2: - return &box_font; + return BOX_FONT; default: - ans = in_symbol_maps(cell->ch); - if (ans != NULL) return ans; + *font = in_symbol_maps(cell->ch); + if (*font != NULL) return FONT; switch(BI_VAL(cell->attrs)) { case 0: - ans = &medium_font; + *font = &medium_font; break; case 1: - ans = bold_font.face ? &bold_font : &medium_font; + *font = bold_font.face ? &bold_font : &medium_font; break; case 2: - ans = italic_font.face ? &italic_font : &medium_font; + *font = italic_font.face ? &italic_font : &medium_font; break; case 4: - ans = bi_font.face ? &bi_font : &medium_font; + *font = bi_font.face ? &bi_font : &medium_font; break; } - if (has_cell_text(ans, cell)) return ans; - return fallback_font(cell); - + if (has_cell_text(*font, cell)) return FONT; + *font = fallback_font(cell); + return *font ? FONT : MISSING_FONT; } END_ALLOW_CASE_RANGE } @@ -356,36 +351,40 @@ render_box_cell(Cell *cell) { } static void -render_run(Cell *first_cell, index_type num_cells, Font *font) { - if (font->face) { - } else { - // special font - if (font == &blank_font) { +render_run(Cell *first_cell, index_type num_cells, Font UNUSED *font, FontType ft) { + switch(ft) { + case FONT: + break; + case BLANK_FONT: while(num_cells--) set_sprite(first_cell++, 0, 0, 0); - } else if (font == &missing_font) { - while(num_cells--) set_sprite(first_cell++, MISSING_GLYPH, 0, 0); - } else { + break; + case BOX_FONT: while(num_cells--) render_box_cell(first_cell++); - } + break; + case MISSING_FONT: + while(num_cells--) set_sprite(first_cell++, MISSING_GLYPH, 0, 0); + break; } } void render_line(Line *line) { Font *run_font = NULL; - index_type first_cell_in_run = 0, i = 0; + FontType run_font_type = MISSING_FONT; + index_type first_cell_in_run, i; attrs_type prev_width = 0; - for (; i < line->xnum; i++) { + for (i=0, first_cell_in_run=0; i < line->xnum; i++) { if (prev_width == 2) continue; Cell *cell = line->cells + i; - Font *cell_font = font_for_cell(cell); + Font *cell_font = NULL; + FontType cell_font_type = font_for_cell(cell, &cell_font); prev_width = cell->attrs & WIDTH_MASK; - if (cell_font == run_font) continue; - if (run_font != NULL && i > first_cell_in_run) render_run(cell, i - first_cell_in_run, run_font); - run_font = cell_font; + if (cell_font_type == run_font_type && cell_font == run_font) continue; + if ((run_font != NULL || run_font_type != FONT) && i > first_cell_in_run) render_run(cell, i - first_cell_in_run, run_font, run_font_type); + run_font = cell_font; run_font_type = cell_font_type; first_cell_in_run = i; } - if (run_font != NULL && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font); + 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); } static PyObject* @@ -395,7 +394,7 @@ set_font(PyObject UNUSED *m, PyObject *args) { Py_CLEAR(get_fallback_font); Py_CLEAR(box_drawing_function); if (!PyArg_ParseTuple(args, "OOO!O!fffO|OOO", &get_fallback_font, &box_drawing_function, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &pt_sz, &xdpi, &ydpi, &medium, &bold, &italic, &bi)) return NULL; Py_INCREF(get_fallback_font); Py_INCREF(box_drawing_function); - clear_font(&medium_font); clear_font(&bold_font); clear_font(&italic_font); clear_font(&bi_font); + clear_font(&medium_font); clear_font(&bold_font); clear_font(&italic_font); clear_font(&bi_font); clear_sprite_map(&box_font); if (!alloc_font(&medium_font, medium, false, false)) return PyErr_NoMemory(); if (bold && !alloc_font(&bold_font, bold, false, false)) return PyErr_NoMemory(); if (italic && !alloc_font(&italic_font, italic, false, false)) return PyErr_NoMemory(); @@ -464,7 +463,7 @@ test_sprite_position_for(PyObject UNUSED *self, PyObject *args) { uint64_t extra_glyphs = 0; if (!PyArg_ParseTuple(args, "H|I", &glyph, &extra_glyphs)) return NULL; int error; - SpritePosition *pos = sprite_position_for(&box_font, glyph, extra_glyphs, false, &error); + SpritePosition *pos = sprite_position_for(&medium_font, glyph, extra_glyphs, false, &error); if (pos == NULL) { sprite_map_set_error(error); return NULL; } return Py_BuildValue("HHH", pos->x, pos->y, pos->z); } diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index 2632c01b8..64ffa027d 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -7,8 +7,7 @@ from unittest import skipIf from kitty.config import build_ansi_color_table, defaults from kitty.fast_data_types import ( - REVERSE, ColorProfile, Cursor as C, HistoryBuf, LineBuf, - sprite_map_set_layout, sprite_map_set_limits, test_sprite_position_for + REVERSE, ColorProfile, Cursor as C, HistoryBuf, LineBuf ) from kitty.utils import sanitize_title, wcwidth @@ -326,20 +325,6 @@ class TestDataTypes(BaseTest): self.assertEqual(c.as_color(i << 8 | 1), (col[0], col[1], col[2])) self.ae(c.as_color(255 << 8 | 1), (0xee, 0xee, 0xee)) - def test_sprite_map(self): - sprite_map_set_limits(10, 2) - sprite_map_set_layout(5, 5) - self.ae(test_sprite_position_for(0), (0, 0, 0)) - self.ae(test_sprite_position_for(1), (1, 0, 0)) - self.ae(test_sprite_position_for(2), (0, 1, 0)) - self.ae(test_sprite_position_for(3), (1, 1, 0)) - self.ae(test_sprite_position_for(4), (0, 0, 1)) - self.ae(test_sprite_position_for(5), (1, 0, 1)) - self.ae(test_sprite_position_for(0, 1), (0, 1, 1)) - self.ae(test_sprite_position_for(0, 2), (1, 1, 1)) - self.ae(test_sprite_position_for(0, 2), (1, 1, 1)) - sprite_map_set_limits(1000, 1000) - def test_historybuf(self): lb = filled_line_buf() hb = HistoryBuf(5, 5) diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index 4fcdabeb7..3340fcb06 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -4,9 +4,12 @@ from collections import OrderedDict -from kitty.fast_data_types import set_send_sprite_to_gpu, test_render_line, sprite_map_set_limits -from kitty.fonts.render import set_font_family +from kitty.fast_data_types import ( + set_send_sprite_to_gpu, sprite_map_set_layout, sprite_map_set_limits, + test_render_line, test_sprite_position_for +) from kitty.fonts.box_drawing import box_chars +from kitty.fonts.render import set_font_family from . import BaseTest @@ -14,13 +17,13 @@ from . import BaseTest class Rendering(BaseTest): def setUp(self): + sprite_map_set_limits(100000, 100) self.sprites = OrderedDict() def send_to_gpu(x, y, z, data): self.sprites[(x, y, z)] = data set_send_sprite_to_gpu(send_to_gpu) - sprite_map_set_limits(100000, 100) self.cell_width, self.cell_height = set_font_family(override_dpi=(96.0, 96.0)) self.assertEqual([k[0] for k in self.sprites], [0, 1, 2, 3, 4]) @@ -28,11 +31,23 @@ class Rendering(BaseTest): set_send_sprite_to_gpu(None) del self.sprites + def test_sprite_map(self): + sprite_map_set_limits(10, 2) + sprite_map_set_layout(5, 5) + self.ae(test_sprite_position_for(0), (0, 0, 0)) + self.ae(test_sprite_position_for(1), (1, 0, 0)) + self.ae(test_sprite_position_for(2), (0, 1, 0)) + self.ae(test_sprite_position_for(3), (1, 1, 0)) + self.ae(test_sprite_position_for(4), (0, 0, 1)) + self.ae(test_sprite_position_for(5), (1, 0, 1)) + self.ae(test_sprite_position_for(0, 1), (0, 1, 1)) + self.ae(test_sprite_position_for(0, 2), (1, 1, 1)) + self.ae(test_sprite_position_for(0, 2), (1, 1, 1)) + def test_box_drawing(self): prerendered = len(self.sprites) s = self.create_screen(cols=len(box_chars), lines=1, scrollback=0) s.draw(''.join(box_chars)) line = s.line(0) test_render_line(line) - print(self.sprites.keys()) self.assertEqual(len(self.sprites), prerendered + len(box_chars))