diff --git a/kitty/fonts/freetype.py b/kitty/fonts/freetype.py index 8771d4489..8b0750ea3 100644 --- a/kitty/fonts/freetype.py +++ b/kitty/fonts/freetype.py @@ -193,13 +193,16 @@ def render_char(text, bold=False, italic=False, width=1): def render_complex_char(text, bold=False, italic=False, width=1): font, face = face_for_text(text, bold, italic) - buf, m, width, height = face.draw_complex_glyph(text) - return CharBitmap( - buf, - ceil_int(abs(m.horiBearingX) / 64), - ceil_int(abs(m.horiBearingY) / 64), - ceil_int(m.horiAdvance / 64), height, width - ) + if width == 1: + buf = CharTexture() + ans = (buf,) + else: + buf = (ctypes.c_ubyte * (cell_width * width * cell_height))() + ans = tuple(CharTexture() for i in range(width)) + face.draw_complex_glyph(text, cell_width, cell_height, ctypes.addressof(buf), width, bold, italic, baseline) + if width > 1: + face.split_cells(cell_width, cell_height, ctypes.addressof(buf), *(ctypes.addressof(x) for x in ans)) + return ans def place_char_in_cell(bitmap_char): @@ -273,26 +276,35 @@ def missing_glyph(width): def render_cell(text=' ', bold=False, italic=False): width = wcwidth(text[0]) - try: - if len(text) > 1: - bitmap_char = render_complex_char(text, bold, italic, width) - else: - bitmap_char = render_char(text, bold, italic, width) - except FontNotFound as err: - safe_print('ERROR:', err, file=sys.stderr) - return missing_glyph(width) - except FreeTypeError as err: - safe_print('Failed to render text:', repr(text), 'with error:', err, file=sys.stderr) - return missing_glyph(width) - second = None - if width == 2: - if bitmap_char.columns > cell_width: - bitmap_char, second = split_char_bitmap(bitmap_char) - second = place_char_in_cell(second) - else: - second = render_cell()[0] + def safe_freetype(func): + try: + return func(text, bold, italic, width) + except FontNotFound as err: + safe_print('ERROR:', err, file=sys.stderr) + except FreeTypeError as err: + safe_print('Failed to render text:', repr(text), 'with error:', err, file=sys.stderr) - first = place_char_in_cell(bitmap_char) + if len(text) > 1: + ret = safe_freetype(render_complex_char) + if ret is None: + return missing_glyph(width) + if width == 1: + first, second = ret[0], None + else: + first, second = ret + else: + bitmap_char = safe_freetype(render_char) + if bitmap_char is None: + return missing_glyph(width) + second = None + if width == 2: + if bitmap_char.columns > cell_width: + bitmap_char, second = split_char_bitmap(bitmap_char) + second = place_char_in_cell(second) + else: + second = render_cell()[0] + + first = place_char_in_cell(bitmap_char) return first, second diff --git a/kitty/freetype.c b/kitty/freetype.c index 229b7ac67..8563ebdab 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -380,7 +380,7 @@ place_bitmap_in_cell(unsigned char *cell, ProcessedBitmap *bm, size_t cell_width ssize_t dy = (ssize_t)((float)metrics->horiBearingY / 64.f + y_offset); size_t src_start_row, dest_start_row; if (dy > 0 && (size_t)dy > baseline) { - src_start_row = dy - baseline; + src_start_row = 0; dest_start_row = 0; } else { src_start_row = 0; @@ -405,7 +405,7 @@ draw_complex_glyph(Face *self, PyObject *args) { unsigned int cell_width, cell_height, num_cells, baseline; PyObject *addr; float x = 0.f, y = 0.f; - if (!PyArg_ParseTuple(args, "s#IIO!Ipp", &text, &text_len, &cell_width, &cell_height, &PyLong_Type, &addr, &num_cells, &bold, &italic, &baseline)) return NULL; + if (!PyArg_ParseTuple(args, "s#IIO!IppI", &text, &text_len, &cell_width, &cell_height, &PyLong_Type, &addr, &num_cells, &bold, &italic, &baseline)) return NULL; unsigned char *cell = PyLong_AsVoidPtr(addr); ShapeData sd; _shape(self, text, text_len, &sd); @@ -423,6 +423,30 @@ draw_complex_glyph(Face *self, PyObject *args) { Py_RETURN_NONE; } +static PyObject* +split_cells(Face UNUSED *self, PyObject *args) { +#define split_cells_doc "split_cells(cell_width, cell_height, src, *cells)" + unsigned int cell_width, cell_height; + unsigned char *cells[10], *src; + size_t num_cells = PyTuple_GET_SIZE(args) - 3; + if (num_cells > sizeof(cells)/sizeof(cells[0])) { PyErr_SetString(PyExc_ValueError, "Too many cells being split"); return NULL; } + cell_width = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(args, 0)); + cell_height = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(args, 1)); + src = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, 2)); + for (size_t i = 3; i < num_cells + 3; i++) cells[i - 3] = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, i)); + + size_t stride = num_cells * cell_width; + for (size_t y = 0; y < cell_height; y++) { + for (size_t i = 0; i < num_cells; i++) { + unsigned char *dest = cells[i] + y * cell_width; + for (size_t x = 0; x < cell_width; x++) { + dest[x] = src[y * stride + i * cell_width + x]; + } + } + } + Py_RETURN_NONE; +} + static PyObject* trim_to_width(Face UNUSED *self, PyObject *args) { @@ -486,6 +510,7 @@ static PyMethodDef methods[] = { METHOD(load_char, METH_O) METHOD(shape, METH_VARARGS) METHOD(draw_complex_glyph, METH_VARARGS) + METHOD(split_cells, METH_VARARGS) METHOD(get_char_index, METH_VARARGS) METHOD(glyph_metrics, METH_NOARGS) METHOD(bitmap, METH_NOARGS)