diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 189139be4..732e1e4d7 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1683,7 +1683,7 @@ typedef void (* GLFWjoystickfun)(int,int); typedef void (* GLFWuserdatafun)(unsigned long long, void*); typedef void (* GLFWtickcallback)(void*); -typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset); +typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, 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); /*! @brief Video mode type. * diff --git a/glfw/wl_client_side_decorations.c b/glfw/wl_client_side_decorations.c index 6bae27b60..ad7e5637f 100644 --- a/glfw/wl_client_side_decorations.c +++ b/glfw/wl_client_side_decorations.c @@ -55,7 +55,7 @@ render_title_bar(_GLFWwindow *window, bool to_front_buffer) { uint8_t *output = to_front_buffer ? decs.top.buffer.data.front : decs.top.buffer.data.back; if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) { uint32_t fg_color = is_focused ? 0xff444444 : 0xff888888; - if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height, 0, 0)) return; + if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height, 0, 0, 0)) return; } for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.top.buffer.size_in_bytes); px < end; px++) { *px = bg_color; diff --git a/kitty/freetype_render_ui_text.c b/kitty/freetype_render_ui_text.c index 938ed6104..94c05acfd 100644 --- a/kitty/freetype_render_ui_text.c +++ b/kitty/freetype_render_ui_text.c @@ -98,19 +98,6 @@ get_load_flags(int hinting, int hintstyle, int base) { return flags; } -static inline unsigned int -width_for_char(Face *face, char_type ch) { - unsigned int ans = 0; - int glyph_index = FT_Get_Char_Index(face->freetype, ch); - if (glyph_index) { - int error = FT_Load_Glyph(face->freetype, glyph_index, get_load_flags(face->hinting, face->hintstyle, FT_LOAD_DEFAULT)); - if (!error) { - ans = (unsigned int)ceilf((float)face->freetype->glyph->metrics.horiAdvance / 64.f); - } - } - return ans; -} - static bool load_font(FontConfigFace *info, Face *ans) { ans->freetype = native_face_from_path(info->path, info->index); @@ -159,7 +146,7 @@ set_pixel_size(RenderCtx *ctx, Face *face, FT_UInt sz, bool get_metrics UNUSED) typedef struct RenderState { uint32_t pending_in_buffer, fg, bg; pixel *output; - size_t output_width, output_height; + size_t output_width, output_height, stride; Face *current_face; float x, y; int y_offset; @@ -207,7 +194,7 @@ alpha_blend_premult(pixel over, pixel under) { static void render_color_bitmap(ProcessedBitmap *src, RenderState *rs) { for (size_t sr = rs->src.top, dr = rs->dest.top; sr < rs->src.bottom && dr < rs->dest.bottom; sr++, dr++) { - pixel *dest_row = rs->output + rs->output_width * dr; + pixel *dest_row = rs->output + rs->stride * dr; uint8_t *src_px = src->buf + src->stride * sr + 4 * rs->src.left; for (size_t sc = rs->src.left, dc = rs->dest.left; sc < rs->src.right && dc < rs->dest.right; sc++, dc++, src_px += 4) { pixel fg = premult_pixel(ARGB(src_px[3], src_px[2], src_px[1], src_px[0]), src_px[3]); @@ -219,7 +206,7 @@ render_color_bitmap(ProcessedBitmap *src, RenderState *rs) { static void render_gray_bitmap(ProcessedBitmap *src, RenderState *rs) { for (size_t sr = rs->src.top, dr = rs->dest.top; sr < rs->src.bottom && dr < rs->dest.bottom; sr++, dr++) { - pixel *dest_row = rs->output + rs->output_width * dr; + pixel *dest_row = rs->output + rs->stride * dr; uint8_t *src_row = src->buf + src->stride * sr; for (size_t sc = rs->src.left, dc = rs->dest.left; sc < rs->src.right && dc < rs->dest.right; sc++, dc++) { pixel fg = premult_pixel(rs->fg, src_row[sc]); @@ -439,12 +426,16 @@ process_codepoint(RenderCtx *ctx, RenderState *rs, char_type codep, char_type ne } bool -render_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px, pixel fg, pixel bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset) { +render_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px, pixel fg, pixel bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin) { RenderCtx *ctx = (RenderCtx*)ctx_; if (!ctx->created) return false; + size_t output_width = right_margin <= width ? width - right_margin : 0; bool has_text = text && text[0]; pixel pbg = premult_pixel(bg, ((bg >> 24) & 0xff)); - for (pixel *px = (pixel*)output_buf, *end = ((pixel*)output_buf) + width * height; px < end; px++) *px = pbg; + for (size_t y = 0; y < height; y++) { + pixel *px = (pixel*)(output_buf + 4 * y * width); + for (size_t x = (size_t)x_offset; x < output_width; x++) px[x] = pbg; + } if (!has_text) return true; hb_buffer_clear_contents(hb_buffer); if (!hb_buffer_pre_allocate(hb_buffer, 512)) { PyErr_NoMemory(); return false; } @@ -457,7 +448,8 @@ render_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px, pix set_pixel_size(ctx, &main_face, sz_px, true); unsigned text_height = font_units_to_pixels_y(main_face.freetype, main_face.freetype->height); RenderState rs = { - .current_face = &main_face, .fg = fg, .bg = bg, .output_width = width, .output_height = height, + .current_face = &main_face, .fg = fg, .bg = bg, + .output_width = output_width, .output_height = height, .stride = width, .output = (pixel*)output_buf, .x = x_offset, .y = y_offset, .sz_px = sz_px }; if (text_height < height) rs.y_offset = (height - text_height) / 2; @@ -504,18 +496,18 @@ render_line(PyObject *self UNUSED, PyObject *args, PyObject *kw) { // use for testing as below // kitty +runpy "from kitty.fast_data_types import *; open('/tmp/test.rgba', 'wb').write(freetype_render_line())" && convert -size 800x60 -depth 8 /tmp/test.rgba /tmp/test.png && icat /tmp/test.png const char *text = "Test 猫 H🐱🚀b rendering with ellipsis for cut off text", *family = NULL; - unsigned int width = 800, height = 60; + unsigned int width = 800, height = 60, right_margin = 0; int bold = 0, italic = 0; unsigned long fg = 0, bg = 0xfffefefe; float x_offset = 0, y_offset = 0; - static const char* kwlist[] = {"text", "width", "height", "font_family", "bold", "italic", "fg", "bg", "x_offset", "y_offset", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|sIIzppkkff", (char**)kwlist, &text, &width, &height, &family, &bold, &italic, &fg, &bg, &x_offset, &y_offset)) return NULL; + static const char* kwlist[] = {"text", "width", "height", "font_family", "bold", "italic", "fg", "bg", "x_offset", "y_offset", "right_margin", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|sIIzppkkffI", (char**)kwlist, &text, &width, &height, &family, &bold, &italic, &fg, &bg, &x_offset, &y_offset, &right_margin)) return NULL; PyObject *ans = PyBytes_FromStringAndSize(NULL, width * height * 4); if (!ans) return NULL; uint8_t *buffer = (u_int8_t*) PyBytes_AS_STRING(ans); RenderCtx *ctx = (RenderCtx*)create_freetype_render_context(family, bold, italic); if (!ctx) return NULL; - if (!render_single_line((FreeTypeRenderCtx)ctx, text, 3 * height / 4, 0, 0xffffffff, buffer, width, height, x_offset, y_offset)) { + if (!render_single_line((FreeTypeRenderCtx)ctx, text, 3 * height / 4, 0, 0xffffffff, buffer, width, height, x_offset, y_offset, right_margin)) { Py_CLEAR(ans); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, "Unknown error while rendering text"); ans = NULL; diff --git a/kitty/freetype_render_ui_text.h b/kitty/freetype_render_ui_text.h index 78691dc4c..a6773620d 100644 --- a/kitty/freetype_render_ui_text.h +++ b/kitty/freetype_render_ui_text.h @@ -13,7 +13,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); +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); void release_freetype_render_context(FreeTypeRenderCtx ctx); typedef struct FontConfigFace { diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index d7bc5b586..508cd2e49 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1421,7 +1421,7 @@ typedef void (* GLFWjoystickfun)(int,int); typedef void (* GLFWuserdatafun)(unsigned long long, void*); typedef void (* GLFWtickcallback)(void*); -typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset); +typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, 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); /*! @brief Video mode type. * diff --git a/kitty/glfw.c b/kitty/glfw.c index 09b3dcb3c..e4dc3c705 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -398,7 +398,7 @@ apple_file_open_callback(const char* filepath) { static FreeTypeRenderCtx csd_title_render_ctx = NULL; static bool -draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset) { +draw_text_callback(GLFWwindow *window, const char *text, 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) { if (!set_callback_window(window)) return false; if (!csd_title_render_ctx) { csd_title_render_ctx = create_freetype_render_context(NULL, true, false); @@ -413,7 +413,7 @@ draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t b px_sz = MIN(px_sz, 3 * height / 4); static char title[2048]; snprintf(title, sizeof(title), "🐱 %s", text); - bool ok = render_single_line(csd_title_render_ctx, title, px_sz, fg, bg, output_buf, width, height, x_offset, y_offset); + bool ok = render_single_line(csd_title_render_ctx, title, px_sz, fg, bg, output_buf, width, height, x_offset, y_offset, right_margin); if (!ok && PyErr_Occurred()) PyErr_Print(); return ok; }