From 1b8978fede5dc0160714eb399e9672816f865705 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 1 Nov 2021 10:16:20 +0530 Subject: [PATCH] Implement rendering of ASCII char with FreeType --- kitty/freetype_render_ui_text.c | 45 +++++++++++++++++++++++---------- kitty/freetype_render_ui_text.h | 1 + kitty/glfw.c | 8 ++++++ kitty/shaders.c | 9 +++---- kitty/state.h | 1 + 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/kitty/freetype_render_ui_text.c b/kitty/freetype_render_ui_text.c index 143b796e3..044f9e09d 100644 --- a/kitty/freetype_render_ui_text.c +++ b/kitty/freetype_render_ui_text.c @@ -476,47 +476,64 @@ end: } static uint8_t* -render_single_char_bitmap(const ProcessedBitmap *pbm, size_t *result_width, size_t *result_height) { - *result_width = pbm->width; *result_height = pbm->rows; +render_single_char_bitmap(const FT_Bitmap *bm, size_t *result_width, size_t *result_height) { + *result_width = bm->width; *result_height = bm->rows; uint8_t *rendered = malloc(*result_width * *result_height); if (!rendered) { PyErr_NoMemory(); return NULL; } - for (size_t r = 0; r < pbm->rows; r++) { - uint8_t *src_row = pbm->buf + pbm->stride * r; + for (size_t r = 0; r < bm->rows; r++) { + uint8_t *src_row = bm->buffer + bm->pitch * r; uint8_t *dest_row = rendered + *result_width * r; - memcpy(dest_row, src_row, MIN(*result_width, pbm->stride)); + memcpy(dest_row, src_row, *result_width); } return rendered; } +typedef struct TempFontData { + Face *face; + FT_UInt orig_sz; +} TempFontData; + +static void cleanup_resize(void *p) { + TempFontData *f = p; + if (f->face && f->face->freetype) { + f->face->pixel_size = f->orig_sz; + FT_Set_Pixel_Sizes(f->face->freetype, f->orig_sz, f->orig_sz); + } +} +#define RESIZE_AFTER_FUNCTION __attribute__((cleanup(cleanup_resize))) + uint8_t* -render_single_ascii_char_as_mask(FreeTypeRenderCtx ctx_, const char ch, size_t avail_height, size_t *result_width, size_t *result_height) { +render_single_ascii_char_as_mask(FreeTypeRenderCtx ctx_, const char ch, size_t *result_width, size_t *result_height) { RenderCtx *ctx = (RenderCtx*)ctx_; if (!ctx->created) { PyErr_SetString(PyExc_RuntimeError, "freetype render ctx not created"); return NULL; } + RESIZE_AFTER_FUNCTION TempFontData temp = {0}; Face *face = &main_face; int glyph_index = FT_Get_Char_Index(face->freetype, ch); if (!glyph_index) { PyErr_Format(PyExc_KeyError, "character %c not found in font", ch); return NULL; } unsigned int height = font_units_to_pixels_y(face->freetype, face->freetype->height); + size_t avail_height = *result_height; + if (avail_height < 4) { PyErr_Format(PyExc_ValueError, "Invalid available height: %zu", avail_height); return NULL; } float ratio = ((float)height) / avail_height; - FT_UInt orig_sz = face->pixel_size; + temp.face = face; temp.orig_sz = face->pixel_size; face->pixel_size = (FT_UInt)(face->pixel_size / ratio); - if (face->pixel_size != orig_sz) FT_Set_Pixel_Sizes(face->freetype, avail_height, avail_height); + if (face->pixel_size != temp.orig_sz) FT_Set_Pixel_Sizes(face->freetype, avail_height, avail_height); int error = FT_Load_Glyph(face->freetype, glyph_index, get_load_flags(face->hinting, face->hintstyle, FT_LOAD_DEFAULT)); - FT_Set_Pixel_Sizes(face->freetype, orig_sz, orig_sz); face->pixel_size = orig_sz; if (error) { PyErr_Format(PyExc_Exception, "failed to load glyph for character: %c", ch); return NULL;} - ProcessedBitmap pbm = {0}; + if (face->freetype->glyph->format != FT_GLYPH_FORMAT_BITMAP) { + error = FT_Render_Glyph(face->freetype->glyph, FT_RENDER_MODE_NORMAL); + if (error) { PyErr_Format(PyExc_Exception, "failed to render glyph for character: %c", ch); return NULL;} + } uint8_t *rendered = NULL; switch(face->freetype->glyph->bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: { FT_Bitmap bitmap; if (!freetype_convert_mono_bitmap(&face->freetype->glyph->bitmap, &bitmap)) return NULL; - populate_processed_bitmap(face->freetype->glyph, &bitmap, &pbm); - rendered = render_single_char_bitmap(&pbm, result_width, result_height); + rendered = render_single_char_bitmap(&bitmap, result_width, result_height); FT_Bitmap_Done(freetype_library(), &bitmap); } break; case FT_PIXEL_MODE_GRAY: - populate_processed_bitmap(face->freetype->glyph, &face->freetype->glyph->bitmap, &pbm); - rendered = render_single_char_bitmap(&pbm, result_width, result_height); + rendered = render_single_char_bitmap(&face->freetype->glyph->bitmap, result_width, result_height); break; default: PyErr_Format(PyExc_TypeError, "Unknown FreeType bitmap type: 0x%x", face->freetype->glyph->bitmap.pixel_mode); diff --git a/kitty/freetype_render_ui_text.h b/kitty/freetype_render_ui_text.h index a6773620d..f62efd839 100644 --- a/kitty/freetype_render_ui_text.h +++ b/kitty/freetype_render_ui_text.h @@ -14,6 +14,7 @@ typedef struct {bool created;} *FreeTypeRenderCtx; FreeTypeRenderCtx create_freetype_render_context(const char *family, bool bold, bool italic); void set_main_face_family(FreeTypeRenderCtx ctx, const char *family, bool bold, bool italic); bool render_single_line(FreeTypeRenderCtx ctx, const char *text, unsigned sz_px, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin); +uint8_t* render_single_ascii_char_as_mask(FreeTypeRenderCtx ctx_, const char ch, size_t *result_width, size_t *result_height); void release_freetype_render_context(FreeTypeRenderCtx ctx); typedef struct FontConfigFace { diff --git a/kitty/glfw.c b/kitty/glfw.c index 9ee30e6f0..97023efff 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -460,6 +460,14 @@ draw_window_title(OSWindow *window, const char *text, color_type fg, color_type if (!ok && PyErr_Occurred()) PyErr_Print(); return ok; } + +uint8_t* +draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height) { + if (!ensure_csd_title_render_ctx()) return NULL; + uint8_t *ans = render_single_ascii_char_as_mask(csd_title_render_ctx, ch, result_width, result_height); + if (PyErr_Occurred()) PyErr_Print(); + return ans; +} #endif // }}} diff --git a/kitty/shaders.c b/kitty/shaders.c index da4bc268c..7a5f17011 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -593,13 +593,10 @@ draw_window_number(OSWindow *os_window, Screen *screen, GLfloat xstart, GLfloat ystart -= dy / 2.f; height -= dy; // top and bottom margins xstart += dx / 2.f; width -= dx; // left and right margins GLfloat height_gl = MIN(MIN(12 * dy, height), width); - unsigned height_px = (unsigned)(os_window->viewport_height * height_gl / 2.f); + size_t height_px = (unsigned)(os_window->viewport_height * height_gl / 2.f), width_px = 0; if (height_px < 4) return; - unsigned width_px = height_px; - if (height_px < 4 || width_px < 4) return; - FREE_AFTER_FUNCTION uint8_t *canvas = malloc(height_px * width_px); - if (!canvas) return; - memset(canvas, 255, height_px * width_px); + FREE_AFTER_FUNCTION uint8_t *canvas = draw_single_ascii_char('a', &width_px, &height_px); + if (height_px < 4 || width_px < 4 || !canvas) return; GLfloat width_gl = 2.f * ((float)width_px) / os_window->viewport_width; left = xstart + (width - width_gl) / 2.f; right = left + width_gl; diff --git a/kitty/state.h b/kitty/state.h index 9780bb214..794c2ccf6 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -320,3 +320,4 @@ void send_pending_click_to_window_id(id_type, void*); void send_pending_click_to_window(Window*, void*); void get_platform_dependent_config_values(void *glfw_window); bool draw_window_title(OSWindow *window, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height); +uint8_t* draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height);