diff --git a/kittens/choose_fonts/backend.py b/kittens/choose_fonts/backend.py index 76437c692..273eb9aff 100644 --- a/kittens/choose_fonts/backend.py +++ b/kittens/choose_fonts/backend.py @@ -128,13 +128,18 @@ def render_face_sample(font: Descriptor, opts: Options, dpi_x: float, dpi_y: flo 'psname': face.postscript_name(), 'features': get_features(face.get_features()), 'applied_features': face.applied_features(), + 'cell_width': 0, 'cell_height': 0, 'canvas_height': 0, 'canvas_width': width, } if is_variable(font): ns = get_named_style(face) if ns: metadata['variable_named_style'] = ns metadata['variable_axis_map'] = get_axis_map(face) - return face.render_sample_text(SAMPLE_TEXT, width, height, opts.foreground.rgb), metadata + bitmap, cell_width, cell_height = face.render_sample_text(SAMPLE_TEXT, width, height, opts.foreground.rgb) + metadata['cell_width'] = cell_width + metadata['cell_height'] = cell_height + metadata['canvas_height'] = len(bitmap) // (4 *width) + return bitmap, metadata def render_family_sample( @@ -230,8 +235,8 @@ def showcase(family: str = 'family="Fira Code"') -> None: ss = screen_size_function()() width = ss.cell_width * ss.cols height = 5 * ss.cell_height - bitmap = render_face_sample(desc, opts, float(q['dpi_x']), float(q['dpi_y']), width, height)[0] - display_bitmap(bitmap, width, height) + bitmap, m = render_face_sample(desc, opts, float(q['dpi_x']), float(q['dpi_y']), width, height) + display_bitmap(bitmap, m['canvas_width'], m['canvas_height']) def test_render(spec: str = 'family="Fira Code"', width: int = 1560, height: int = 116, font_size: float = 12, dpi: float = 288) -> None: @@ -240,5 +245,5 @@ def test_render(spec: str = 'family="Fira Code"', width: int = 1560, height: int opts.font_size = font_size opts.foreground = to_color('white') desc = get_font_files(opts)['medium'] - bitmap = render_face_sample(desc, opts, float(dpi), float(dpi), width, height)[0] - display_bitmap(bitmap, width, height) + bitmap, m = render_face_sample(desc, opts, float(dpi), float(dpi), width, height) + display_bitmap(bitmap, m['canvas_width'], m['canvas_height']) diff --git a/kittens/choose_fonts/face.go b/kittens/choose_fonts/face.go index c9263aa2d..3ee5d1786 100644 --- a/kittens/choose_fonts/face.go +++ b/kittens/choose_fonts/face.go @@ -200,8 +200,7 @@ func (self *face_panel) draw_screen() (err error) { y := self.render_lines(2, lines...) num_lines_per_font := (int(sz.HeightCells) - y - 1) - 2 - num_lines_needed := int(math.Ceil(100. / float64(sz.WidthCells))) - num_lines := max(1, min(num_lines_per_font, num_lines_needed)) + num_lines := max(1, num_lines_per_font) key := faces_preview_key{settings: self.settings, width: int(sz.WidthCells * sz.CellWidth), height: int(sz.CellHeight) * num_lines} self.current_preview_key = key self.preview_cache_mutex.Lock() @@ -232,13 +231,14 @@ func (self *face_panel) draw_screen() (err error) { return err } + num_lines = int(math.Ceil(float64(preview.Canvas_height) / float64(sz.CellHeight))) if int(sz.HeightCells)-y >= num_lines+2 { - y += 1 + y++ lp.MoveCursorTo(1, y+1) self.handler.draw_preview_header(0) y++ lp.MoveCursorTo(1, y+1) - self.handler.graphics_manager.display_image(0, preview.Path, key.width, key.height) + self.handler.graphics_manager.display_image(0, preview.Path, preview.Canvas_width, preview.Canvas_height) } return } diff --git a/kittens/choose_fonts/faces.go b/kittens/choose_fonts/faces.go index 840a96a78..b972582b1 100644 --- a/kittens/choose_fonts/faces.go +++ b/kittens/choose_fonts/faces.go @@ -45,8 +45,7 @@ func (self *faces) draw_screen() (err error) { lp.QueueWriteString(str) num_lines_per_font := ((int(sz.HeightCells) - y - 1) / 4) - 2 - num_lines_needed := int(math.Ceil(100. / float64(sz.WidthCells))) - num_lines := max(1, min(num_lines_per_font, num_lines_needed)) + num_lines := max(1, num_lines_per_font) key := faces_preview_key{settings: self.settings, width: int(sz.WidthCells * sz.CellWidth), height: int(sz.CellHeight) * num_lines} self.preview_cache_mutex.Lock() defer self.preview_cache_mutex.Unlock() @@ -74,6 +73,8 @@ func (self *faces) draw_screen() (err error) { slot := 0 d := func(setting, title string) { + r := previews[setting] + num_lines := int(math.Ceil(float64(r.Canvas_height) / float64(sz.CellHeight))) if int(sz.HeightCells)-y < num_lines+1 { return } @@ -82,7 +83,7 @@ func (self *faces) draw_screen() (err error) { lp.QueueWriteString(str) if y+num_lines < int(sz.HeightCells) { lp.MoveCursorTo(1, y+1) - self.handler.graphics_manager.display_image(slot, previews[setting].Path, key.width, key.height) + self.handler.graphics_manager.display_image(slot, r.Path, r.Canvas_width, r.Canvas_height) slot++ y += num_lines + 1 } diff --git a/kittens/choose_fonts/list.go b/kittens/choose_fonts/list.go index 64642e70a..72627e538 100644 --- a/kittens/choose_fonts/list.go +++ b/kittens/choose_fonts/list.go @@ -2,14 +2,14 @@ package choose_fonts import ( "fmt" + "strings" + "sync" + "kitty/tools/tui/loop" "kitty/tools/tui/readline" "kitty/tools/utils" "kitty/tools/utils/style" "kitty/tools/wcswidth" - "math" - "strings" - "sync" ) var _ = fmt.Print @@ -19,6 +19,11 @@ type preview_cache_key struct { width, height int } +type preview_cache_value struct { + path string + width, height int +} + type FontList struct { rl *readline.Readline family_list FamilyList @@ -27,13 +32,13 @@ type FontList struct { resolved_faces_from_kitty_conf ResolvedFaces handler *handler variable_data_requested_for *utils.Set[string] - preview_cache map[preview_cache_key]string + preview_cache map[preview_cache_key]preview_cache_value preview_cache_mutex sync.Mutex } func (self *FontList) initialize(h *handler) error { self.handler = h - self.preview_cache = make(map[preview_cache_key]string) + self.preview_cache = make(map[preview_cache_key]preview_cache_value) self.rl = readline.New(h.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "Family: "}) self.variable_data_requested_for = utils.NewSet[string](256) return nil @@ -133,7 +138,6 @@ func (self *FontList) draw_preview(x, y int, sz loop.ScreenSize) (err error) { self.handler.draw_preview_header(x) y++ height_cells -= 2 - height_cells = min(height_cells, int(math.Ceil(100./float64(width_cells)))) self.handler.lp.MoveCursorTo(x+1, y+1) key := preview_cache_key{ family: self.family_list.CurrentFamily(), width: int(sz.CellWidth) * width_cells, height: int(sz.CellHeight) * height_cells, @@ -143,10 +147,10 @@ func (self *FontList) draw_preview(x, y int, sz loop.ScreenSize) (err error) { } self.preview_cache_mutex.Lock() defer self.preview_cache_mutex.Unlock() - img_path := self.preview_cache[key] - switch img_path { + cc := self.preview_cache[key] + switch cc.path { case "": - self.preview_cache[key] = "requested" + self.preview_cache[key] = preview_cache_value{path: "requested"} go func() { var r map[string]RenderedSampleTransmit self.handler.set_worker_error(kitty_font_backend.query("render_family_samples", map[string]any{ @@ -155,14 +159,14 @@ func (self *FontList) draw_preview(x, y int, sz loop.ScreenSize) (err error) { }, &r)) self.preview_cache_mutex.Lock() defer self.preview_cache_mutex.Unlock() - self.preview_cache[key] = r["font_family"].Path + self.preview_cache[key] = preview_cache_value{path: r["font_family"].Path, width: r["font_family"].Canvas_width, height: r["font_family"].Canvas_height} self.handler.lp.WakeupMainThread() }() return case "requested": return } - self.handler.graphics_manager.display_image(0, img_path, key.width, key.height) + self.handler.graphics_manager.display_image(0, cc.path, cc.width, cc.height) return } diff --git a/kittens/choose_fonts/types.go b/kittens/choose_fonts/types.go index 4e8d61d25..cb08979d6 100644 --- a/kittens/choose_fonts/types.go +++ b/kittens/choose_fonts/types.go @@ -103,6 +103,10 @@ type RenderedSampleTransmit struct { Applied_features map[string]string `json:"applied_features"` Variable_named_style NamedStyle `json:"variable_named_style"` Variable_axis_map map[string]float64 `json:"variable_axis_map"` + Cell_width int `json:"cell_width"` + Cell_height int `json:"cell_height"` + Canvas_width int `json:"canvas_width"` + Canvas_height int `json:"canvas_height"` } func (self RenderedSampleTransmit) default_axis_values() (ans map[string]float64) { diff --git a/kitty/core_text.m b/kitty/core_text.m index 45a7b3f69..e39f04c27 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -749,11 +749,13 @@ render_sample_text(CTFace *self, PyObject *args) { CTFontRef font = self->ct_font; PyObject *ptext; if (!PyArg_ParseTuple(args, "Ukk|k", &ptext, &canvas_width, &canvas_height, &fg)) return NULL; - RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height)); - if (!pbuf) return NULL; unsigned int cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; cell_metrics((PyObject*)self, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness); size_t num_chars = PyUnicode_GET_LENGTH(ptext); + int num_chars_per_line = canvas_width / cell_width, num_of_lines = (int)ceil((float)num_chars / (float)num_chars_per_line); + canvas_height = MIN(canvas_height, num_of_lines * cell_height); + RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height)); + if (!pbuf) return NULL; RAII_ALLOC(unichar, chars, calloc(sizeof(unichar), num_chars)); if (!chars) return PyErr_NoMemory(); for (size_t i = 0; i < num_chars; i++) chars[i] = PyUnicode_READ_CHAR(ptext, i); @@ -786,8 +788,7 @@ render_sample_text(CTFace *self, PyObject *args) { p[0] = r; p[1] = g; p[2] = b; p[3] = s[0]; } end: - Py_INCREF(pbuf); - return pbuf; + return Py_BuildValue("OII", pbuf, cell_width, cell_height); } diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index f596cdd1a..a8c715f6f 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -439,7 +439,7 @@ class Face: def identify_for_debug(self) -> str: ... def postscript_name(self) -> str: ... def set_size(self, sz_in_pts: float, dpi_x: float, dpi_y: float) -> None: ... - def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ... + def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> Tuple[bytes, int, int]: ... def get_variation(self) -> Optional[Dict[str, float]]: ... def get_features(self) -> Dict[str, Optional[FeatureData]]: ... def applied_features(self) -> Dict[str, str]: ... @@ -476,7 +476,7 @@ class CTFace: def identify_for_debug(self) -> str: ... def postscript_name(self) -> str: ... def set_size(self, sz_in_pts: float, dpi_x: float, dpi_y: float) -> None: ... - def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ... + def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> Tuple[bytes, int, int]: ... def get_variation(self) -> Optional[Dict[str, float]]: ... def get_features(self) -> Dict[str, Optional[FeatureData]]: ... def applied_features(self) -> Dict[str, str]: ... diff --git a/kitty/freetype.c b/kitty/freetype.c index 8f827b28b..fe47e0299 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -964,10 +964,12 @@ render_sample_text(Face *self, PyObject *args) { unsigned long fg = 0xffffff; PyObject *ptext; if (!PyArg_ParseTuple(args, "Ukk|k", &ptext, &canvas_width, &canvas_height, &fg)) return NULL; - RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height)); - if (!pbuf) return NULL; unsigned int cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; cell_metrics((PyObject*)self, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness); + int num_chars_per_line = canvas_width / cell_width, num_of_lines = (int)ceil((float)PyUnicode_GET_LENGTH(ptext) / (float)num_chars_per_line); + canvas_height = MIN(canvas_height, num_of_lines * cell_height); + RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height)); + if (!pbuf) return NULL; pixel *canvas = (pixel*)PyBytes_AS_STRING(pbuf); memset(canvas, 0, PyBytes_GET_SIZE(pbuf)); if (cell_width > canvas_width) goto end; @@ -996,8 +998,7 @@ render_sample_text(Face *self, PyObject *args) { p[0] = r; p[1] = g; p[2] = b; p[3] = a; } end: - Py_INCREF(pbuf); - return pbuf; + return Py_BuildValue("OII", pbuf, cell_width, cell_height); }