From 7d7d82a68d155c4bc776c55638d391c16ddad418 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 31 Oct 2017 20:56:41 +0530 Subject: [PATCH] Start working on actually rendering lines of text --- kitty/boss.py | 6 +- kitty/fonts.c | 124 ++++++++++++++++++++++++++++++---- kitty/fonts.h | 1 - kitty/fonts/render.py | 150 +++++++++++++++++------------------------- kitty/shaders.c | 47 ++----------- kitty/state.h | 1 + 6 files changed, 182 insertions(+), 147 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 7cfc64884..722e59ca8 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -11,7 +11,7 @@ from .fast_data_types import ( GLFW_KEY_DOWN, GLFW_KEY_UP, ChildMonitor, destroy_global_data, destroy_sprite_map, glfw_post_empty_event, layout_sprite_map ) -from .fonts.render import render_cell_wrapper, set_font_family, resize_fonts +from .fonts.render import set_font_family, resize_fonts from .keys import get_key_map, get_sent_data, get_shortcut from .session import create_session from .tabs import SpecialWindow, TabManager @@ -23,7 +23,7 @@ from .window import load_shader_programs def initialize_renderer(): load_shader_programs() - layout_sprite_map(cell_size.width, cell_size.height, render_cell_wrapper) + layout_sprite_map(cell_size.width, cell_size.height) class DumpCommands: # {{{ @@ -143,7 +143,7 @@ class Boss: w, h = cell_size.width, cell_size.height windows = tuple(filter(None, self.window_id_map.values())) cell_size.width, cell_size.height = resize_fonts(self.current_font_size) - layout_sprite_map(cell_size.width, cell_size.height, render_cell_wrapper) + layout_sprite_map(cell_size.width, cell_size.height) for window in windows: window.screen.rescale_images(w, h) self.resize_windows_after_font_size_change() diff --git a/kitty/fonts.c b/kitty/fonts.c index df4fbd6db..b988865b7 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -8,7 +8,11 @@ #include "fonts.h" #include "state.h" +#define MISSING_GLYPH 4 + typedef uint16_t glyph_index; +typedef void (*send_sprite_to_gpu_func)(unsigned int, unsigned int, unsigned int, uint8_t*); +send_sprite_to_gpu_func current_send_sprite_to_gpu = NULL; typedef struct SpritePosition SpritePosition; @@ -75,7 +79,7 @@ do_increment(int *error) { } -SpritePosition* +static SpritePosition* sprite_position_for(Font *font, glyph_index glyph, uint64_t extra_glyphs, bool is_second, int *error) { glyph_index idx = glyph & 0x3ff; SpritePosition *s = font->sprite_map + idx; @@ -108,14 +112,6 @@ 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; } -int -sprite_tracker_increment(sprite_index *x, sprite_index *y, sprite_index *z) { - int error = 0; - *x = sprite_tracker.x; *y = sprite_tracker.y; *z = sprite_tracker.z; - do_increment(&error); - return error; -} - void sprite_map_free(Font *font) { SpritePosition *s, *t; @@ -183,6 +179,8 @@ 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 uint8_t *canvas = NULL; +static inline void clear_canvas(void) { memset(canvas, 0, cell_width * cell_height); } static inline PyObject* update_cell_metrics(float pt_sz, float xdpi, float ydpi) { @@ -203,6 +201,8 @@ update_cell_metrics(float pt_sz, float xdpi, float ydpi) { if (cell_height > 1000) { PyErr_SetString(PyExc_ValueError, "line height too large after adjustment"); return NULL; } underline_position = MIN(cell_height - 1, underline_position); sprite_tracker_set_layout(cell_width, cell_height); + free(canvas); canvas = malloc(cell_width * cell_height); + if (canvas == NULL) return PyErr_NoMemory(); return Py_BuildValue("IIIII", cell_width, cell_height, baseline, underline_position, underline_thickness); } @@ -255,7 +255,7 @@ in_symbol_maps(char_type ch) { } -Font* +static Font* font_for_cell(Cell *cell) { Font *ans; START_ALLOW_CASE_RANGE @@ -263,7 +263,7 @@ START_ALLOW_CASE_RANGE case 0: return &blank_font; case 0x2500 ... 0x2570: - case 0x2574 ... 0x2577: + case 0x2574 ... 0x257f: case 0xe0b0: case 0xe0b2: return &box_font; @@ -291,11 +291,88 @@ START_ALLOW_CASE_RANGE END_ALLOW_CASE_RANGE } +static inline void +set_sprite(Cell *cell, sprite_index x, sprite_index y, sprite_index z) { + cell->sprite_x = x; cell->sprite_y = y; cell->sprite_z = z; +} + +static inline glyph_index +box_glyph_id(char_type ch) { +START_ALLOW_CASE_RANGE + switch(ch) { + case 0x2500 ... 0x257f: + return ch - 0x2500; + case 0xe0b0: + return 0x80; + case 0xe0b2: + return 0x81; + default: + return 0x82; + } +END_ALLOW_CASE_RANGE +} + +static PyObject* box_drawing_function = NULL; + +static void +render_box_cell(Cell *cell) { + int error = 0; + glyph_index glyph = box_glyph_id(cell->ch); + SpritePosition *sp = sprite_position_for(&box_font, glyph, 0, false, &error); + if (sp == NULL) { + sprite_map_set_error(error); PyErr_Print(); + set_sprite(cell, 0, 0, 0); + return; + } + set_sprite(cell, sp->x, sp->y, sp->z); + if (sp->rendered) return; + sp->rendered = true; + PyObject *ret = PyObject_CallFunction(box_drawing_function, "I", cell->ch); + if (ret == NULL) { PyErr_Print(); return; } + current_send_sprite_to_gpu(sp->x, sp->y, sp->z, PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0))); + Py_DECREF(ret); +} + +static void +render_run(Cell *first_cell, index_type num_cells, Font *font) { + if (font->face) { + } else { + // special font + if (font == &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 { + while(num_cells--) render_box_cell(first_cell++); + } + } +} + +void +render_line(Line *line) { + Font *run_font = NULL; + index_type first_cell_in_run = 0, i = 0; + attrs_type prev_width = 0; + for (; i < line->xnum; i++) { + if (prev_width == 2) continue; + Cell *cell = line->cells + i; + Font *cell_font = font_for_cell(cell); + 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; + 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); +} + static PyObject* set_font(PyObject UNUSED *m, PyObject *args) { PyObject *sm, *smf, *medium, *bold = NULL, *italic = NULL, *bi = NULL; float xdpi, ydpi, pt_sz; - if (!PyArg_ParseTuple(args, "OO!O!fffO|OOO", &get_fallback_font, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &pt_sz, &xdpi, &ydpi, &medium, &bold, &italic, &bi)) return NULL; + 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); 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(); @@ -327,7 +404,9 @@ static hb_buffer_t *harfbuzz_buffer = NULL; static void finalize(void) { + free(canvas); Py_CLEAR(get_fallback_font); + Py_CLEAR(box_drawing_function); free_font(&medium_font); free_font(&bold_font); free_font(&italic_font); free_font(&bi_font); for (size_t i = 0; fallback_fonts[i].face != NULL; i++) free_font(fallback_fonts + i); for (size_t i = 0; symbol_map_fonts_count; i++) free_font(symbol_map_fonts + i); @@ -362,11 +441,31 @@ test_sprite_position_for(PyObject UNUSED *self, PyObject *args) { return Py_BuildValue("HHH", pos->x, pos->y, pos->z); } +static PyObject* +send_prerendered_sprites(PyObject UNUSED *s, PyObject *args) { + int error = 0; + sprite_index x = 0, y = 0, z = 0; + // blank cell + clear_canvas(); + current_send_sprite_to_gpu(x, y, z, canvas); + do_increment(&error); + if (error != 0) { sprite_map_set_error(error); return NULL; } + for (ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) { + x = sprite_tracker.x; y = sprite_tracker.y; z = sprite_tracker.z; + do_increment(&error); + if (error != 0) { sprite_map_set_error(error); return NULL; } + current_send_sprite_to_gpu(x, y, z, PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, i))); + } + return Py_BuildValue("H", x); +} + + static PyMethodDef module_methods[] = { METHODB(set_font_size, METH_VARARGS), METHODB(set_font, METH_VARARGS), METHODB(sprite_map_set_limits, METH_VARARGS), METHODB(sprite_map_set_layout, METH_VARARGS), + METHODB(send_prerendered_sprites, METH_VARARGS), METHODB(test_sprite_position_for, METH_VARARGS), {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -380,5 +479,6 @@ init_fonts(PyObject *module) { harfbuzz_buffer = hb_buffer_create(); if (harfbuzz_buffer == NULL || !hb_buffer_allocation_successful(harfbuzz_buffer) || !hb_buffer_pre_allocate(harfbuzz_buffer, 2000)) { PyErr_NoMemory(); return false; } if (PyModule_AddFunctions(module, module_methods) != 0) return false; + current_send_sprite_to_gpu = send_sprite_to_gpu; return true; } diff --git a/kitty/fonts.h b/kitty/fonts.h index 0ea2d0152..5987ee8c3 100644 --- a/kitty/fonts.h +++ b/kitty/fonts.h @@ -19,4 +19,3 @@ hb_font_t* harfbuzz_font_for_face(PyObject*); bool set_size_for_face(PyObject*, float, float, float); void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*); void sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z); -int sprite_tracker_increment(sprite_index *x, sprite_index *y, sprite_index *z); diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py index b59e0a381..aedfafc2d 100644 --- a/kitty/fonts/render.py +++ b/kitty/fonts/render.py @@ -4,12 +4,15 @@ import ctypes from collections import namedtuple -from math import sin, pi, ceil, floor, sqrt +from math import ceil, floor, pi, sin, sqrt from kitty.constants import isosx +from kitty.fast_data_types import ( + send_prerendered_sprites, set_font, set_font_size +) +from kitty.fonts.box_drawing import render_box_char, render_missing_glyph from kitty.utils import get_logical_dpi -from kitty.fast_data_types import set_font, set_font_size -from .box_drawing import render_box_char, is_renderable_box_char + if isosx: from .core_text import get_font_files, font_for_text, face_from_font, font_for_family, save_medium_face else: @@ -37,17 +40,27 @@ def create_symbol_map(opts): return sm, tuple(faces) -FontState = namedtuple('FontState', 'family pt_sz xdpi ydpi cell_width cell_height baseline underline_position underline_thickness') +FontState = namedtuple( + 'FontState', + 'family pt_sz xdpi ydpi cell_width cell_height baseline underline_position underline_thickness' +) def get_fallback_font(text, bold, italic): state = set_font_family.state - return create_face(font_for_text(text, state.family, state.pt_sz, state.xdpi, state.ydpi, bold, italic)) + return create_face( + font_for_text( + text, state.family, state.pt_sz, state.xdpi, state.ydpi, bold, + italic + ) + ) def set_font_family(opts, override_font_size=None): if hasattr(set_font_family, 'state'): - raise ValueError('Cannot set font family more than once, use resize_fonts() to change size') + raise ValueError( + 'Cannot set font family more than once, use resize_fonts() to change size' + ) sz = override_font_size or opts.font_size xdpi, ydpi = get_logical_dpi() set_font_family.state = FontState('', sz, xdpi, ydpi, 0, 0, 0, 0, 0) @@ -58,8 +71,14 @@ def set_font_family(opts, override_font_size=None): if k in font_map: faces.append(create_face(font_map[k])) sm, sfaces = create_symbol_map(opts) - cell_width, cell_height, baseline, underline_position, underline_thickness = set_font(get_fallback_font, sm, sfaces, sz, xdpi, ydpi, *faces) - set_font_family.state = FontState(opts.font_family, sz, xdpi, ydpi, cell_width, cell_height, baseline, underline_position, underline_thickness) + cell_width, cell_height, baseline, underline_position, underline_thickness = set_font( + get_fallback_font, render_box_drawing, sm, sfaces, sz, xdpi, ydpi, *faces + ) + set_font_family.state = FontState( + opts.font_family, sz, xdpi, ydpi, cell_width, cell_height, baseline, + underline_position, underline_thickness + ) + prerender() return cell_width, cell_height @@ -67,9 +86,14 @@ def resize_fonts(new_sz, xdpi=None, ydpi=None): s = set_font_family.state xdpi = xdpi or s.xdpi ydpi = ydpi or s.ydpi - cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(new_sz, xdpi, ydpi) + cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size( + new_sz, xdpi, ydpi + ) set_font_family.state = FontState( - s.family, new_sz, xdpi, ydpi, cell_width, cell_height, baseline, underline_position, underline_thickness) + s.family, new_sz, xdpi, ydpi, cell_width, cell_height, baseline, + underline_position, underline_thickness + ) + prerender() def add_line(buf, cell_width, position, thickness, cell_height): @@ -93,7 +117,9 @@ def add_curl(buf, cell_width, position, thickness, cell_height): return max(0, min(int(x), cell_width - 1)) def add_intensity(x, y, distance): - buf[cell_width * y + x] = min(255, buf[cell_width * y + x] + int(255 * (1 - distance))) + buf[cell_width * y + x] = min( + 255, buf[cell_width * y + x] + int(255 * (1 - distance)) + ) for x_exact in range(cell_width): y_exact = yfactor * sin(x_exact * xfactor) + position @@ -106,103 +132,47 @@ def add_curl(buf, cell_width, position, thickness, cell_height): add_intensity(x, y, dist) -def render_cell(text=' ', bold=False, italic=False, underline=0, strikethrough=False): +def render_special(underline=0, strikethrough=False, missing=False): s = set_font_family.state cell_width, cell_height, baseline = s.cell_width, s.cell_height, s.baseline - underline_thickness, underline_position = s.underline_thickness, s.underline_position + underline_position, underline_thickness = s.underline_position, s.underline_thickness CharTexture = ctypes.c_ubyte * cell_width * cell_height - if is_renderable_box_char(text): - first, second = render_box_char(text, CharTexture(), cell_width, cell_height), None - else: - first = CharTexture() - second = None + ans = CharTexture if missing else CharTexture() def dl(f, *a): - f(first, cell_width, *a) - if second is not None: - f(second, cell_width, *a) + f(ans, cell_width, *a) if underline: t = underline_thickness if underline == 2: t = max(1, min(cell_height - underline_position - 1, t)) - dl(add_curl if underline == 2 else add_line, underline_position, t, cell_height) + dl( + add_curl + if underline == 2 else add_line, underline_position, t, cell_height + ) if strikethrough: pos = int(0.65 * baseline) dl(add_line, pos, underline_thickness, cell_height) - return first, second - - -class Buf: - - def __init__(self, buf): - self.buf = buf - - def __call__(self): - return ctypes.addressof(self.buf) - - -def render_cell_wrapper(text, bold, italic, underline, strikethrough, is_second): - first, second = render_cell(text, bold, italic, underline, strikethrough) - ans = second if is_second else first - ans = ans or render_cell()[0] - return Buf(ans) - - -def join_cells(cell_width, cell_height, *cells): - dstride = len(cells) * cell_width - ans = (ctypes.c_ubyte * (cell_height * dstride))() - for r in range(cell_height): - soff = r * cell_width - doff = r * dstride - for cnum, cell in enumerate(cells): - doff2 = doff + (cnum * cell_width) - ans[doff2:doff2 + cell_width] = cell[soff:soff + cell_width] + if missing: + buf = bytearray(cell_width * cell_height) + render_missing_glyph(buf, cell_width, cell_height) + ans = CharTexture.from_buffer(buf) return ans -def display_bitmap(data, w, h): - from PIL import Image - img = Image.new('L', (w, h)) - img.putdata(data) - img.show() +def prerender(): + # Pre-render the special blank, underline and strikethrough cells + cells = render_special(1), render_special(2), render_special(0, True), render_special(missing=True) + if send_prerendered_sprites(*map(ctypes.addressof, cells)) != len(cells): + raise RuntimeError('Your GPU has too small a max texture size') -def render_string(text='\'Qing👁a⧽', underline=2, strikethrough=True): - import unicodedata - cells = [] - current_text = '' - - def render_one(c): - f, s = render_cell(c, underline=underline, strikethrough=strikethrough) - cells.append(f) - if s is not None: - cells.append(s) - - for c in text: - if unicodedata.combining(c): - current_text += c - else: - if current_text: - render_one(current_text) - current_text = c - if current_text: - render_one(current_text) +def render_box_drawing(codepoint): s = set_font_family.state cell_width, cell_height = s.cell_width, s.cell_height - char_data = join_cells(cell_width, cell_height, *cells) - return char_data, cell_width * len(cells), cell_height - - -def test_rendering(text='\'Ping👁a⧽', sz=144, family='monospace', underline=2, strikethrough=True): - from kitty.config import defaults - from kitty.fast_data_types import glfw_init, glfw_terminate - if not glfw_init(): - raise SystemExit('Failed to initialize glfw') - try: - opts = defaults._replace(font_family=family, font_size=sz) - set_font_family(opts) - display_bitmap(*render_string(text, underline=underline, strikethrough=strikethrough)) - finally: - glfw_terminate() + CharTexture = ctypes.c_ubyte * cell_width * cell_height + buf = render_box_char( + chr(codepoint), CharTexture(), cell_width, cell_height + ) + return ctypes.addressof(buf), buf diff --git a/kitty/shaders.c b/kitty/shaders.c index 86545e23b..777ef4b20 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -19,7 +19,6 @@ typedef struct { GLuint texture_id; GLenum texture_unit; GLint max_texture_size, max_array_texture_layers; - PyObject *render_cell; } SpriteMap; static SpriteMap sprite_map = { .xnum = 1, .ynum = 1, .last_num_of_layers = 1, .last_ynum = -1, .texture_unit = GL_TEXTURE0 }; @@ -78,15 +77,6 @@ realloc_sprite_texture() { sprite_map.texture_id = tex; } -static inline PyObject* -render_cell(PyObject *text, bool bold, bool italic, unsigned int underline, bool strikethrough, bool is_second) { -#define B(x) (x ? Py_True : Py_False) - PyObject *ret = PyObject_CallFunction(sprite_map.render_cell, "OOOIOO", text, B(bold), B(italic), underline, B(strikethrough), B(is_second)); - if (ret == NULL) { PyErr_Print(); fatal("Rendering of a cell failed, aborting"); } - return ret; -#undef B -} - static inline void ensure_sprite_map() { static GLuint bound_texture_id = 0; @@ -98,36 +88,20 @@ ensure_sprite_map() { } } -static void -sprite_send_to_gpu(unsigned int x, unsigned int y, unsigned int z, PyObject *buf) { +void +send_sprite_to_gpu(unsigned int x, unsigned int y, unsigned int z, uint8_t *buf) { unsigned int xnum, ynum, znum; sprite_tracker_current_layout(&xnum, &ynum, &znum); if ((int)znum >= sprite_map.last_num_of_layers || (znum == 0 && (int)ynum > sprite_map.last_ynum)) realloc_sprite_texture(); glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map.texture_id); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); x *= sprite_map.cell_width; y *= sprite_map.cell_height; - PyObject *ret = PyObject_CallObject(buf, NULL); - if (ret == NULL) { PyErr_Print(); fatal("Failed to get address of rendered cell buffer"); } - void *address = PyLong_AsVoidPtr(ret); - Py_DECREF(ret); - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, sprite_map.cell_width, sprite_map.cell_height, 1, GL_RED, GL_UNSIGNED_BYTE, address); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, sprite_map.cell_width, sprite_map.cell_height, 1, GL_RED, GL_UNSIGNED_BYTE, buf); Py_DECREF(buf); } -static inline sprite_index -send_prerendered(unsigned int underline, bool strikethrough) { - sprite_index x, y, z; - PyObject *blank = PyUnicode_FromString(" "); - if (blank == NULL) { fatal("Out of memory"); } - PyObject *buf = render_cell(blank, false, false, underline, strikethrough, false); - Py_CLEAR(blank); - if (sprite_tracker_increment(&x, &y, &z) != 0) { fatal("Failed to increment sprite map for prerendering"); } - sprite_send_to_gpu(x, y, z, buf); - return x; -} - static void -layout_sprite_map(unsigned int cell_width, unsigned int cell_height, PyObject *render_cell) { +layout_sprite_map(unsigned int cell_width, unsigned int cell_height) { sprite_map.cell_width = MAX(1, cell_width); sprite_map.cell_height = MAX(1, cell_height); global_state.cell_width = sprite_map.cell_width; @@ -138,21 +112,13 @@ layout_sprite_map(unsigned int cell_width, unsigned int cell_height, PyObject *r /* sprite_map_set_limits(sprite_map.max_texture_size, sprite_map.max_array_texture_layers); */ } /* sprite_map_set_layout(sprite_map.cell_width, sprite_map.cell_height); */ - Py_CLEAR(sprite_map.render_cell); - sprite_map.render_cell = render_cell; Py_INCREF(sprite_map.render_cell); if (sprite_map.texture_id) { glDeleteTextures(1, &(sprite_map.texture_id)); sprite_map.texture_id = 0; } realloc_sprite_texture(); - // Pre-render the basic cells to ensure they have known sprite numbers - send_prerendered(0, false); - send_prerendered(1, false); - send_prerendered(2, false); - if (send_prerendered(0, true) != 3) { fatal("Available OpenGL texture size is too small"); } } static void destroy_sprite_map() { /* sprite_map_free(); */ - Py_CLEAR(sprite_map.render_cell); if (sprite_map.texture_id) { glDeleteTextures(1, &(sprite_map.texture_id)); sprite_map.texture_id = 0; @@ -535,9 +501,8 @@ NO_ARG_INT(create_graphics_vao) NO_ARG(destroy_sprite_map) PYWRAP1(layout_sprite_map) { unsigned int cell_width, cell_height; - PyObject *render_cell; - PA("IIO", &cell_width, &cell_height, &render_cell); - layout_sprite_map(cell_width, cell_height, render_cell); + PA("II", &cell_width, &cell_height); + layout_sprite_map(cell_width, cell_height); Py_RETURN_NONE; } diff --git a/kitty/state.h b/kitty/state.h index 42f64e6e0..bff465fe1 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -104,3 +104,4 @@ void draw_cursor(CursorRenderInfo *); void update_viewport_size(int, int); void free_texture(uint32_t*); void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool); +void send_sprite_to_gpu(unsigned int, unsigned int, unsigned int, uint8_t*);