From 0518cabef6701df80e62e9ead8db29552cb9e735 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 1 Nov 2017 05:36:29 +0530 Subject: [PATCH] Infrastructure for rendering cells in the test suite --- kitty/fonts.c | 35 +++++++++++++++++++++++++++++++++++ kitty/fonts/render.py | 14 ++++++-------- kitty/utils.py | 4 +++- kitty_tests/fonts.py | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 kitty_tests/fonts.py diff --git a/kitty/fonts.c b/kitty/fonts.c index b988865b7..c09462f75 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -13,6 +13,7 @@ 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; +static PyObject *python_send_to_gpu_impl = NULL; typedef struct SpritePosition SpritePosition; @@ -166,6 +167,15 @@ free_font(Font *f) { f->bold = false; f->italic = false; } +static inline void +clear_font(Font *f) { + f->hb_font = NULL; + Py_CLEAR(f->face); + clear_sprite_map(f); + f->bold = false; f->italic = false; +} + + 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 fallback_fonts[256] = {{0}}; static PyObject *get_fallback_font = NULL; @@ -182,6 +192,16 @@ static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_pos static uint8_t *canvas = NULL; static inline void 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) { + PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIy#", x, y, z, buf, cell_width * cell_height); + if (ret == NULL) PyErr_Print(); + else Py_DECREF(ret); + } +} + + 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); } } @@ -373,10 +393,15 @@ 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); 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(); if (bi && !alloc_font(&bi_font, bi, false, false)) return PyErr_NoMemory(); + for (size_t i = 0; fallback_fonts[i].face != NULL; i++) clear_font(fallback_fonts + i); + for (size_t i = 0; symbol_map_fonts_count; i++) free_font(symbol_map_fonts + i); + free(symbol_maps); free(symbol_map_fonts); symbol_maps = NULL; symbol_map_fonts = NULL; + symbol_maps_count = 0; symbol_map_fonts_count = 0; symbol_maps_count = PyTuple_GET_SIZE(sm); if (symbol_maps_count > 0) { @@ -404,6 +429,7 @@ static hb_buffer_t *harfbuzz_buffer = NULL; static void finalize(void) { + Py_CLEAR(python_send_to_gpu_impl); free(canvas); Py_CLEAR(get_fallback_font); Py_CLEAR(box_drawing_function); @@ -459,6 +485,14 @@ send_prerendered_sprites(PyObject UNUSED *s, PyObject *args) { return Py_BuildValue("H", x); } +static PyObject* +set_send_sprite_to_gpu(PyObject UNUSED *self, PyObject *func) { + Py_CLEAR(python_send_to_gpu_impl); + python_send_to_gpu_impl = func; + Py_INCREF(func); + current_send_sprite_to_gpu = func == Py_None ? send_sprite_to_gpu : python_send_to_gpu; + Py_RETURN_NONE; +} static PyMethodDef module_methods[] = { METHODB(set_font_size, METH_VARARGS), @@ -467,6 +501,7 @@ static PyMethodDef module_methods[] = { METHODB(sprite_map_set_layout, METH_VARARGS), METHODB(send_prerendered_sprites, METH_VARARGS), METHODB(test_sprite_position_for, METH_VARARGS), + METHODB(set_send_sprite_to_gpu, METH_O), {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py index aedfafc2d..580a90b8d 100644 --- a/kitty/fonts/render.py +++ b/kitty/fonts/render.py @@ -7,6 +7,7 @@ from collections import namedtuple from math import ceil, floor, pi, sin, sqrt from kitty.constants import isosx +from kitty.config import defaults from kitty.fast_data_types import ( send_prerendered_sprites, set_font, set_font_size ) @@ -56,13 +57,10 @@ def get_fallback_font(text, 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' - ) +def set_font_family(opts=None, override_font_size=None, override_dpi=None): + opts = opts or defaults sz = override_font_size or opts.font_size - xdpi, ydpi = get_logical_dpi() + xdpi, ydpi = get_logical_dpi(override_dpi) set_font_family.state = FontState('', sz, xdpi, ydpi, 0, 0, 0, 0, 0) font_map = get_font_files(opts) faces = [create_face(font_map['medium'])] @@ -136,7 +134,7 @@ 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_position, underline_thickness = s.underline_position, s.underline_thickness - CharTexture = ctypes.c_ubyte * cell_width * cell_height + CharTexture = ctypes.c_ubyte * (cell_width * cell_height) ans = CharTexture if missing else CharTexture() def dl(f, *a): @@ -171,7 +169,7 @@ def prerender(): def render_box_drawing(codepoint): s = set_font_family.state cell_width, cell_height = s.cell_width, s.cell_height - CharTexture = ctypes.c_ubyte * cell_width * cell_height + CharTexture = ctypes.c_ubyte * (cell_width * cell_height) buf = render_box_char( chr(codepoint), CharTexture(), cell_width, cell_height ) diff --git a/kitty/utils.py b/kitty/utils.py index bba4b5a4a..f88877164 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -107,9 +107,11 @@ def x11_dpi(): pass -def get_logical_dpi(): +def get_logical_dpi(override_dpi=None): # See https://github.com/glfw/glfw/issues/1019 for why we cant use # glfw_get_physical_dpi() + if override_dpi is not None: + get_logical_dpi.ans = override_dpi if not hasattr(get_logical_dpi, 'ans'): if isosx: # TODO: Investigate if this needs a different implementation on OS X diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py new file mode 100644 index 000000000..892f1753e --- /dev/null +++ b/kitty_tests/fonts.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2017, Kovid Goyal + +from collections import OrderedDict + +from kitty.fast_data_types import set_send_sprite_to_gpu +from kitty.fonts.render import set_font_family + +from . import BaseTest + + +class Rendering(BaseTest): + + sprites = OrderedDict() + + @classmethod + def setUpClass(cls): + def send_to_gpu(x, y, z, data): + cls.sprites[(x, y, z)] = data + + set_send_sprite_to_gpu(send_to_gpu) + cls.cell_width, cls.cell_height = set_font_family(override_dpi=(96.0, 96.0)) + + @classmethod + def tearDownClass(cls): + set_send_sprite_to_gpu(None) + cls.sprites.clear() + + def setUp(self): + self.sprites.clear() + + def tearDown(self): + self.sprites.clear() + + def test_box_drawing(self): + pass