From 8a049039ef839dc183ec954375776d533f054aff Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 27 Oct 2017 12:42:12 +0530 Subject: [PATCH] More work on rendering complex glyphs --- kitty/fonts/freetype.py | 9 +++++-- kitty/freetype.c | 53 +++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/kitty/fonts/freetype.py b/kitty/fonts/freetype.py index 77ad75ef8..8771d4489 100644 --- a/kitty/fonts/freetype.py +++ b/kitty/fonts/freetype.py @@ -193,8 +193,13 @@ 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) - import pprint - pprint.pprint(face.shape(text)) + 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 + ) def place_char_in_cell(bitmap_char): diff --git a/kitty/freetype.c b/kitty/freetype.c index e8a557395..f9469d37a 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -6,6 +6,7 @@ */ #include "data-types.h" +#include #include #include #pragma GCC diagnostic push @@ -181,12 +182,11 @@ static PyStructSequence_Field gm_fields[] = { static PyStructSequence_Desc gm_desc = {"GlpyhMetrics", NULL, gm_fields, 8}; static PyTypeObject GlpyhMetricsType = {{{0}}}; -static PyObject* -glyph_metrics(Face *self) { -#define glyph_metrics_doc "" +static inline PyObject* +gm_to_py(FT_Glyph_Metrics *gm) { PyObject *ans = PyStructSequence_New(&GlpyhMetricsType); if (ans != NULL) { -#define SI(num, attr) PyStructSequence_SET_ITEM(ans, num, PyLong_FromLong(self->face->glyph->metrics.attr)); if (PyStructSequence_GET_ITEM(ans, num) == NULL) { Py_CLEAR(ans); return PyErr_NoMemory(); } +#define SI(num, attr) PyStructSequence_SET_ITEM(ans, num, PyLong_FromLong(gm->attr)); if (PyStructSequence_GET_ITEM(ans, num) == NULL) { Py_CLEAR(ans); return PyErr_NoMemory(); } SI(0, width); SI(1, height); SI(2, horiBearingX); SI(3, horiBearingY); SI(4, horiAdvance); SI(5, vertBearingX); SI(6, vertBearingY); SI(7, vertAdvance); @@ -195,6 +195,12 @@ glyph_metrics(Face *self) { return ans; } +static PyObject* +glyph_metrics(Face *self) { +#define glyph_metrics_doc "" + return gm_to_py(&self->face->glyph->metrics); +} + static PyStructSequence_Field bm_fields[] = { {"rows", NULL}, {"width", NULL}, @@ -308,12 +314,30 @@ static inline bool ensure_space(GlyphBuffer *g, unsigned int width, unsigned int height) { if (g->width >= width && g->height >= height) return true; char *newbuf = calloc(width * height, sizeof(char)); - if (newbuf == NULL) return false; + if (newbuf == NULL) { free(g->buf); g->buf = NULL; g->width = 0; g->height = 0; return false; } for (unsigned int r = 0; r < g->height; r++) memcpy(newbuf + r * width, g->buf + r * g->width, g->width); free(g->buf); g->buf = newbuf; g->width = width; g->height = height; return true; } +typedef struct { + size_t x, y; +} BitmapPoint; + + +static inline void +apply_bitmap(GlyphBuffer *dest, FT_Bitmap *bitmap, BitmapPoint src_start, BitmapPoint dest_start) { + char *src = (char*)bitmap->buffer; + size_t src_height = bitmap->rows, src_width = bitmap->pitch; +#define ZERO_SUB(a, b) ((a) <= (b) ? 0 : (a) - (b)) + size_t width = MIN(ZERO_SUB(dest->width, dest_start.x), ZERO_SUB(src_width, src_start.x)); +#undef ZERO_SUB + + for (size_t sy = src_start.y, dy = dest_start.y; sy < src_height && dy < dest->height; sy++, dy++) { + memcpy(dest->buf + dest_start.x + dy * dest->width, src + sy * src_width, width); + } +} + static PyObject* draw_complex_glyph(Face *self, PyObject *args) { @@ -326,6 +350,7 @@ draw_complex_glyph(Face *self, PyObject *args) { ShapeData sd; _shape(self, string, len, &sd); GlyphBuffer g = {0}; + BitmapPoint src, dest; for (unsigned i = 0; i < sd.length; i++) { if (sd.info[i].codepoint == 0) continue; _load_char(self, sd.info[i].codepoint); @@ -335,12 +360,26 @@ draw_complex_glyph(Face *self, PyObject *args) { width = MAX(width, (unsigned int)ceilf(x + self->face->glyph->bitmap.pitch)); height = MAX(height, (unsigned int)ceilf(y + self->face->glyph->bitmap.rows)); if (!ensure_space(&g, width, height)) return PyErr_NoMemory(); - + src.x = (size_t)(x < 0 ? ceilf(-x) : 0); + src.y = (size_t)(y < 0 ? ceilf(-y) : 0); + dest.x = (size_t)(x < 0 ? 0 : roundf(x)); + dest.y = (size_t)(y < 0 ? 0 : roundf(y)); + if (self->face->glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) { + free(g.buf); + PyErr_Format(PyExc_ValueError, "FreeType rendered a complex glyph with an unsupported pixel mode: %d", self->face->glyph->bitmap.pixel_mode); + return NULL; + } + apply_bitmap(&g, &self->face->glyph->bitmap, src, dest); + x += (float)sd.positions[i].x_advance / 64.0; + y = 0; } if (!g.buf) { - PyErr_Format(PyExc_ValueError, "No glpyhs found for string: %s", string); + PyErr_Format(PyExc_ValueError, "No glyphs found for string: %s", string); return NULL; } + PyObject *t = PyByteArray_FromStringAndSize((const char*)g.buf, g.width * g.height); + free(g.buf); + return Py_BuildValue("NNII", t, gm_to_py(&g.metrics), g.width, g.height); }