diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 864792602a..2b00d7d7b0 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -785,7 +785,7 @@ draw_resizing_text(OSWindow *w) { if (monotonic() - w->created_at > ms_to_monotonic_t(1000) && w->live_resize.num_of_resize_events > 1) { char text[32] = {0}; unsigned int width = w->live_resize.width, height = w->live_resize.height; - snprintf(text, sizeof(text), "%u x %u cells", width / w->fonts_data->cell_width, height / w->fonts_data->cell_height); + snprintf(text, sizeof(text), "%u x %u cells", width / w->fonts_data->fcm.cell_width, height / w->fonts_data->fcm.cell_height); StringCanvas rendered = render_simple_text(w->fonts_data, text); if (rendered.canvas) { draw_centered_alpha_mask(w, width, height, rendered.width, rendered.height, rendered.canvas, OPT(background_opacity)); diff --git a/kitty/data-types.h b/kitty/data-types.h index 0eb47bdc29..54b3832e1e 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -269,7 +269,11 @@ typedef struct { } CellPixelSize; typedef struct {int x;} *SPRITE_MAP_HANDLE; -#define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; unsigned int cell_width, cell_height; + +typedef struct FontCellMetrics { + unsigned int cell_width, cell_height, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; +} FontCellMetrics; +#define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; FontCellMetrics fcm; typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE; #define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0; diff --git a/kitty/fonts.c b/kitty/fonts.c index 6e1d7d2de9..a9929e14a4 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -62,11 +62,6 @@ typedef struct { SpacerStrategy spacer_strategy; } Font; -typedef struct RunFont { - unsigned scale, subscale_n, subscale_d, vertical_align, multicell_y; - ssize_t font_idx; -} RunFont; - typedef struct Canvas { pixel *buf; unsigned current_cells, alloced_cells, alloced_scale, current_scale; @@ -83,7 +78,6 @@ static void free_const(const void* x) { free((void*)x); } typedef struct { FONTS_DATA_HEAD id_type id; - unsigned int baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; size_t fonts_capacity, fonts_count, fallback_fonts_count; ssize_t medium_font_idx, bold_font_idx, italic_font_idx, bi_font_idx, first_symbol_font_idx, first_fallback_font_idx; Font *fonts; @@ -100,7 +94,7 @@ static void initialize_font_group(FontGroup *fg); static void ensure_canvas_can_fit(FontGroup *fg, unsigned cells, unsigned scale) { -#define cs(cells, scale) (sizeof(fg->canvas.buf[0]) * 3u * cells * fg->cell_width * fg->cell_height * scale * scale) +#define cs(cells, scale) (sizeof(fg->canvas.buf[0]) * 3u * cells * fg->fcm.cell_width * fg->fcm.cell_height * scale * scale) size_t size_in_bytes = cs(cells, scale); if (size_in_bytes > fg->canvas.size_in_bytes) { free(fg->canvas.buf); @@ -396,7 +390,7 @@ static void python_send_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigned int z, pixel* buf) { if (python_send_to_gpu_impl) { if (!num_font_groups) fatal("Cannot call send to gpu with no font groups"); - PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIN", x, y, z, PyBytes_FromStringAndSize((const char*)buf, sizeof(pixel) * fg->cell_width * fg->cell_height)); + PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIN", x, y, z, PyBytes_FromStringAndSize((const char*)buf, sizeof(pixel) * fg->fcm.cell_width * fg->fcm.cell_height)); if (ret == NULL) PyErr_Print(); else Py_DECREF(ret); } @@ -426,53 +420,50 @@ adjust_ypos(unsigned int pos, unsigned int cell_height, int adjustment) { static void calc_cell_metrics(FontGroup *fg) { - unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; - cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness); - if (!cell_width) fatal("Failed to calculate cell width for the specified font"); - unsigned int before_cell_height = cell_height; - unsigned int cw = cell_width, ch = cell_height; + fg->fcm = cell_metrics(fg->fonts[fg->medium_font_idx].face); + if (!fg->fcm.cell_width) fatal("Failed to calculate cell width for the specified font"); + unsigned int before_cell_height = fg->fcm.cell_height; + unsigned int cw = fg->fcm.cell_width, ch = fg->fcm.cell_height; adjust_metric(&cw, OPT(cell_width).val, OPT(cell_width).unit, fg->logical_dpi_x); adjust_metric(&ch, OPT(cell_height).val, OPT(cell_height).unit, fg->logical_dpi_y); #define MAX_DIM 1000 #define MIN_WIDTH 2 #define MIN_HEIGHT 4 - if (cw >= MIN_WIDTH && cw <= MAX_DIM) cell_width = cw; + if (cw >= MIN_WIDTH && cw <= MAX_DIM) fg->fcm.cell_width = cw; else log_error("Cell width invalid after adjustment, ignoring modify_font cell_width"); - if (ch >= MIN_HEIGHT && ch <= MAX_DIM) cell_height = ch; + if (ch >= MIN_HEIGHT && ch <= MAX_DIM) fg->fcm.cell_height = ch; else log_error("Cell height invalid after adjustment, ignoring modify_font cell_height"); - int line_height_adjustment = cell_height - before_cell_height; - if (cell_height < MIN_HEIGHT) fatal("Line height too small: %u", cell_height); - if (cell_height > MAX_DIM) fatal("Line height too large: %u", cell_height); - if (cell_width < MIN_WIDTH) fatal("Cell width too small: %u", cell_width); - if (cell_width > MAX_DIM) fatal("Cell width too large: %u", cell_width); + int line_height_adjustment = fg->fcm.cell_height - before_cell_height; + if (fg->fcm.cell_height < MIN_HEIGHT) fatal("Line height too small: %u", fg->fcm.cell_height); + if (fg->fcm.cell_height > MAX_DIM) fatal("Line height too large: %u", fg->fcm.cell_height); + if (fg->fcm.cell_width < MIN_WIDTH) fatal("Cell width too small: %u", fg->fcm.cell_width); + if (fg->fcm.cell_width > MAX_DIM) fatal("Cell width too large: %u", fg->fcm.cell_width); #undef MIN_WIDTH #undef MIN_HEIGHT #undef MAX_DIM - unsigned int baseline_before = baseline; -#define A(which, dpi) adjust_metric(&which, OPT(which).val, OPT(which).unit, fg->logical_dpi_##dpi); + unsigned int baseline_before = fg->fcm.baseline; +#define A(which, dpi) adjust_metric(&fg->fcm.which, OPT(which).val, OPT(which).unit, fg->logical_dpi_##dpi); A(underline_thickness, y); A(underline_position, y); A(strikethrough_thickness, y); A(strikethrough_position, y); A(baseline, y); #undef A - if (baseline_before != baseline) { - int adjustment = baseline - baseline_before; - baseline = adjust_ypos(baseline_before, cell_height, adjustment); - underline_position = adjust_ypos(underline_position, cell_height, adjustment); - strikethrough_position = adjust_ypos(strikethrough_position, cell_height, adjustment); + if (baseline_before != fg->fcm.baseline) { + int adjustment = fg->fcm.baseline - baseline_before; + fg->fcm.baseline = adjust_ypos(baseline_before, fg->fcm.cell_height, adjustment); + fg->fcm.underline_position = adjust_ypos(fg->fcm.underline_position, fg->fcm.cell_height, adjustment); + fg->fcm.strikethrough_position = adjust_ypos(fg->fcm.strikethrough_position, fg->fcm.cell_height, adjustment); } - underline_position = MIN(cell_height - 1, underline_position); + fg->fcm.underline_position = MIN(fg->fcm.cell_height - 1, fg->fcm.underline_position); // ensure there is at least a couple of pixels available to render styled underlines // there should be at least one pixel on either side of the underline_position - if (underline_position > baseline + 1 && underline_position > cell_height - 1) - underline_position = MAX(baseline + 1, cell_height - 1); + if (fg->fcm.underline_position > fg->fcm.baseline + 1 && fg->fcm.underline_position > fg->fcm.cell_height - 1) + fg->fcm.underline_position = MAX(fg->fcm.baseline + 1, fg->fcm.cell_height - 1); if (line_height_adjustment > 1) { - baseline += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2); - underline_position += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2); + fg->fcm.baseline += MIN(fg->fcm.cell_height - 1, (unsigned)line_height_adjustment / 2); + fg->fcm.underline_position += MIN(fg->fcm.cell_height - 1, (unsigned)line_height_adjustment / 2); } - sprite_tracker_set_layout(&fg->sprite_tracker, cell_width, cell_height); - fg->cell_width = cell_width; fg->cell_height = cell_height; - fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness, fg->strikethrough_position = strikethrough_position, fg->strikethrough_thickness = strikethrough_thickness; + sprite_tracker_set_layout(&fg->sprite_tracker, fg->fcm.cell_width, fg->fcm.cell_height); ensure_canvas_can_fit(fg, 8, 1); } @@ -557,7 +548,7 @@ load_fallback_font(FontGroup *fg, const ListOfChars *lc, bool bold, bool italic, if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; } if (global_state.debug_font_fallback) output_cell_fallback_data(lc, bold, italic, emoji_presentation, face); if (PyLong_Check(face)) { ssize_t ans = fg->first_fallback_font_idx + PyLong_AsSsize_t(face); Py_DECREF(face); return ans; } - set_size_for_face(face, fg->cell_height, true, (FONTS_DATA_HANDLE)fg); + set_size_for_face(face, fg->fcm.cell_height, true, (FONTS_DATA_HANDLE)fg); ensure_space_for(fg, fonts, Font, fg->fonts_count + 1, fonts_capacity, 5, true); ssize_t ans = fg->first_fallback_font_idx + fg->fallback_fonts_count; @@ -740,10 +731,10 @@ scaled_cell_dimensions(RunFont rf, unsigned *width, unsigned *height) { static pixel* extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells) { - pixel *ans = fg->canvas.buf + (fg->canvas.size_in_bytes / sizeof(fg->canvas.buf[0]) - fg->cell_width * fg->cell_height); - pixel *dest = ans, *src = fg->canvas.buf + (i * fg->cell_width); - unsigned int stride = fg->cell_width * num_cells; - for (unsigned int r = 0; r < fg->cell_height; r++, dest += fg->cell_width, src += stride) memcpy(dest, src, fg->cell_width * sizeof(fg->canvas.buf[0])); + pixel *ans = fg->canvas.buf + (fg->canvas.size_in_bytes / sizeof(fg->canvas.buf[0]) - fg->fcm.cell_width * fg->fcm.cell_height); + pixel *dest = ans, *src = fg->canvas.buf + (i * fg->fcm.cell_width); + unsigned int stride = fg->fcm.cell_width * num_cells; + for (unsigned int r = 0; r < fg->fcm.cell_height; r++, dest += fg->fcm.cell_width, src += stride) memcpy(dest, src, fg->fcm.cell_width * sizeof(fg->canvas.buf[0])); return ans; } @@ -796,15 +787,15 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, } } if (all_rendered) return; - unsigned width = fg->cell_width, height = fg->cell_height; + unsigned width = fg->fcm.cell_width, height = fg->fcm.cell_height; scaled_cell_dimensions(rf, &width, &height); RAII_PyObject(ret, PyObject_CallFunction(box_drawing_function, "IIId", (unsigned int)ch, width, height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0)); if (ret == NULL) { PyErr_Print(); return; } uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0)); ensure_canvas_can_fit(fg, 2, rf.scale); Region src = { .right = width, .bottom = height }, dest = src; - unsigned dest_stride = rf.scale * fg->cell_width, src_stride = width; - calculate_regions_for_line(rf, fg->cell_height, &src, &dest); + unsigned dest_stride = rf.scale * fg->fcm.cell_width, src_stride = width; + calculate_regions_for_line(rf, fg->fcm.cell_height, &src, &dest); render_alpha_mask(alpha_mask, fg->canvas.buf, &src, &dest, src_stride, dest_stride, 0xffffff); if (rf.scale == 1) { current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[0]->x, sp[0]->y, sp[0]->z, fg->canvas.buf); @@ -868,7 +859,7 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPU ensure_canvas_can_fit(fg, num_cells + 1, rf.scale); text_in_cell(cpu_cells, tc, global_glyph_render_scratch.lc); bool was_colored = has_emoji_presentation(cpu_cells, global_glyph_render_scratch.lc); - render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, fg->canvas.buf, fg->cell_width, fg->cell_height, num_cells, fg->baseline, &was_colored, (FONTS_DATA_HANDLE)fg, center_glyph); + render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, fg->canvas.buf, fg->fcm.cell_width, fg->fcm.cell_height, num_cells, fg->fcm.baseline, &was_colored, (FONTS_DATA_HANDLE)fg, center_glyph); if (PyErr_Occurred()) PyErr_Print(); for (unsigned i = 0; i < num_cells; i++) { @@ -1278,7 +1269,8 @@ group_normal(Font *font, hb_font_t *hbf, const TextCache *tc) { static void -shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font, bool disable_ligature, const TextCache *tc) { +shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font, RunFont rf, bool disable_ligature, const TextCache *tc) { + face_apply_scaling(font->face, rf); hb_font_t *hbf = harfbuzz_font_for_face(font->face); if (font->spacer_strategy == SPACER_STRATEGY_UNKNOWN) detect_spacer_strategy(hbf, font, tc); shape(first_cpu_cell, first_gpu_cell, num_cells, hbf, font, disable_ligature, tc); @@ -1371,7 +1363,8 @@ test_shape(PyObject UNUSED *self, PyObject *args) { FontGroup *fg = font_groups; font = fg->fonts + fg->medium_font_idx; } - shape_run(line->cpu_cells, line->gpu_cells, num, font, false, line->text_cache); + RunFont rf = {0}; + shape_run(line->cpu_cells, line->gpu_cells, num, font, rf, false, line->text_cache); PyObject *ans = PyList_New(0); unsigned int idx = 0; @@ -1395,20 +1388,20 @@ static void render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, RunFont rf, bool pua_space_ligature, bool center_glyph, int cursor_offset, DisableLigature disable_ligature_strategy, const TextCache *tc) { switch(rf.font_idx) { default: - shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[rf.font_idx], disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS, tc); + shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[rf.font_idx], rf, disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS, tc); if (pua_space_ligature) collapse_pua_space_ligature(num_cells); else if (cursor_offset > -1) { // false if DISABLE_LIGATURES_NEVER index_type left, right; split_run_at_offset(cursor_offset, &left, &right); if (right > left) { if (left) { - shape_run(first_cpu_cell, first_gpu_cell, left, &fg->fonts[rf.font_idx], false, tc); + shape_run(first_cpu_cell, first_gpu_cell, left, &fg->fonts[rf.font_idx], rf, false, tc); render_groups(fg, rf, center_glyph, tc); } - shape_run(first_cpu_cell + left, first_gpu_cell + left, right - left, &fg->fonts[rf.font_idx], true, tc); + shape_run(first_cpu_cell + left, first_gpu_cell + left, right - left, &fg->fonts[rf.font_idx], rf, true, tc); render_groups(fg, rf, center_glyph, tc); if (right < num_cells) { - shape_run(first_cpu_cell + right, first_gpu_cell + right, num_cells - right, &fg->fonts[rf.font_idx], false, tc); + shape_run(first_cpu_cell + right, first_gpu_cell + right, num_cells - right, &fg->fonts[rf.font_idx], rf, false, tc); render_groups(fg, rf, center_glyph, tc); } break; @@ -1498,7 +1491,7 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor, glyph_index glyph_id = glyph_id_for_codepoint(font->face, first_ch); int width = get_glyph_width(font->face, glyph_id); - desired_cells = (unsigned int)ceilf((float)width / fg->cell_width); + desired_cells = (unsigned int)ceilf((float)width / fg->fcm.cell_width); } desired_cells = MIN(desired_cells, cell_cap_for_codepoint(first_ch)); @@ -1543,7 +1536,7 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor, StringCanvas render_simple_text(FONTS_DATA_HANDLE fg_, const char *text) { FontGroup *fg = (FontGroup*)fg_; - if (fg->fonts_count && fg->medium_font_idx) return render_simple_text_impl(fg->fonts[fg->medium_font_idx].face, text, fg->baseline); + if (fg->fonts_count && fg->medium_font_idx) return render_simple_text_impl(fg->fonts[fg->medium_font_idx].face, text, fg->fcm.baseline); StringCanvas ans = {0}; return ans; } @@ -1599,7 +1592,7 @@ send_prerendered_sprites(FontGroup *fg) { current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf); do_increment(fg, &error); if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); } - PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, fg->strikethrough_position, fg->strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y); + PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->fcm.cell_width, fg->fcm.cell_height, fg->fcm.baseline, fg->fcm.underline_position, fg->fcm.underline_thickness, fg->fcm.strikethrough_position, fg->fcm.strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y); if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); } PyObject *cell_addresses = PyTuple_GET_ITEM(args, 0); for (ssize_t i = 0; i < PyTuple_GET_SIZE(cell_addresses); i++) { @@ -1609,8 +1602,8 @@ send_prerendered_sprites(FontGroup *fg) { if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); } uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(cell_addresses, i)); ensure_canvas_can_fit(fg, 1, 1); // clear canvas - Region r = { .right = fg->cell_width, .bottom = fg->cell_height }; - render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->cell_width, fg->cell_width, 0xffffff); + Region r = { .right = fg->fcm.cell_width, .bottom = fg->fcm.cell_height }; + render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->fcm.cell_width, fg->fcm.cell_width, 0xffffff); current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf); } Py_CLEAR(args); @@ -1658,7 +1651,7 @@ initialize_font_group(FontGroup *fg) { // rescale the symbol_map faces for the desired cell height, this is how fallback fonts are sized as well for (size_t i = 0; i < descriptor_indices.num_symbol_fonts; i++) { Font *font = fg->fonts + i + fg->first_symbol_font_idx; - set_size_for_face(font->face, fg->cell_height, true, (FONTS_DATA_HANDLE)fg); + set_size_for_face(font->face, fg->fcm.cell_height, true, (FONTS_DATA_HANDLE)fg); } } @@ -1667,7 +1660,7 @@ void send_prerendered_sprites_for_window(OSWindow *w) { FontGroup *fg = (FontGroup*)w->fonts_data; if (!fg->sprite_map) { - fg->sprite_map = alloc_sprite_map(fg->cell_width, fg->cell_height); + fg->sprite_map = alloc_sprite_map(fg->fcm.cell_width, fg->fcm.cell_height); send_prerendered_sprites(fg); } } @@ -1847,7 +1840,7 @@ create_test_font_group(PyObject *self UNUSED, PyObject *args) { if (!PyArg_ParseTuple(args, "ddd", &sz, &dpix, &dpiy)) return NULL; FontGroup *fg = font_group_for(sz, dpix, dpiy); if (!fg->sprite_map) send_prerendered_sprites(fg); - return Py_BuildValue("II", fg->cell_width, fg->cell_height); + return Py_BuildValue("II", fg->fcm.cell_width, fg->fcm.cell_height); } static PyObject* diff --git a/kitty/fonts.h b/kitty/fonts.h index 78091c6798..8e37acf515 100644 --- a/kitty/fonts.h +++ b/kitty/fonts.h @@ -31,6 +31,12 @@ typedef struct ParsedFontFeature { bool hash_computed; } ParsedFontFeature; +typedef struct RunFont { + unsigned scale, subscale_n, subscale_d, vertical_align, multicell_y; + ssize_t font_idx; +} RunFont; + + ParsedFontFeature* parse_font_feature(const char *spec); // API that font backends need to implement @@ -39,7 +45,7 @@ int get_glyph_width(PyObject *, glyph_index); bool is_glyph_empty(PyObject *, glyph_index); hb_font_t* harfbuzz_font_for_face(PyObject*); bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE); -void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*); +FontCellMetrics cell_metrics(PyObject*); bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE, bool center_glyph); PyObject* create_fallback_face(PyObject *base_face, const ListOfChars *lc, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg); PyObject* specialize_font_descriptor(PyObject *base_descriptor, double, double, double); @@ -56,6 +62,7 @@ void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len); typedef void (*free_extra_data_func)(void*); StringCanvas render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline); StringCanvas render_simple_text(FONTS_DATA_HANDLE fg_, const char *text); +static inline void face_apply_scaling(PyObject*face, RunFont rf) {(void)face; (void)rf;} bool add_font_name_record(PyObject *table, uint16_t platform_id, uint16_t encoding_id, uint16_t language_id, uint16_t name_id, const char *string, uint16_t string_len); diff --git a/kitty/freetype.c b/kitty/freetype.c index ff6ace5468..29b421806e 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -194,7 +194,7 @@ set_size_for_face(PyObject *s, unsigned int desired_height, bool force, FONTS_DA FT_UInt xdpi = (FT_UInt)fg->logical_dpi_x, ydpi = (FT_UInt)fg->logical_dpi_y; if (!force && (self->char_width == w && self->char_height == w && self->xdpi == xdpi && self->ydpi == ydpi)) return true; ((Face*)self)->size_in_pts = (float)fg->font_sz_in_pts; - return set_font_size(self, w, w, xdpi, ydpi, desired_height, fg->cell_height); + return set_font_size(self, w, w, xdpi, ydpi, desired_height, fg->fcm.cell_height); } static PyObject* @@ -391,25 +391,27 @@ calc_cell_width(Face *self) { } -void -cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) { +FontCellMetrics +cell_metrics(PyObject *s) { Face *self = (Face*)s; - *cell_width = calc_cell_width(self); - *cell_height = calc_cell_height(self, true); - *baseline = font_units_to_pixels_y(self, self->ascender); - *underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position))); - *underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness)); + FontCellMetrics ans = {0}; + ans.cell_width = calc_cell_width(self); + ans.cell_height = calc_cell_height(self, true); + ans.baseline = font_units_to_pixels_y(self, self->ascender); + ans.underline_position = MIN(ans.cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position))); + ans.underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness)); if (self->strikethrough_position != 0) { - *strikethrough_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->strikethrough_position))); + ans.strikethrough_position = MIN(ans.cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->strikethrough_position))); } else { - *strikethrough_position = (unsigned int)floor(*baseline * 0.65); + ans.strikethrough_position = (unsigned int)floor(ans.baseline * 0.65); } if (self->strikethrough_thickness > 0) { - *strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->strikethrough_thickness)); + ans.strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->strikethrough_thickness)); } else { - *strikethrough_thickness = *underline_thickness; + ans.strikethrough_thickness = ans.underline_thickness; } + return ans; } unsigned int @@ -537,10 +539,10 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_ } else if (rescale && self->is_scalable && extra > 1) { FT_F26Dot6 char_width = self->char_width, char_height = self->char_height; float ar = (float)max_width / (float)ans->width; - if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi, 0, fg->cell_height)) { + if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi, 0, fg->fcm.cell_height)) { free_processed_bitmap(ans); if (!render_bitmap(self, glyph_id, ans, cell_width, cell_height, num_cells, bold, italic, false, fg)) return false; - if (!set_font_size(self, char_width, char_height, self->xdpi, self->ydpi, 0, fg->cell_height)) return false; + if (!set_font_size(self, char_width, char_height, self->xdpi, self->ydpi, 0, fg->fcm.cell_height)) return false; } else return false; } } @@ -976,14 +978,13 @@ 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; - 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); + FontCellMetrics fcm = cell_metrics((PyObject*)self); RAII_PyObject(pbuf, PyBytes_FromStringAndSize(NULL, sizeof(pixel) * canvas_width * canvas_height)); if (!pbuf) return NULL; memset(PyBytes_AS_STRING(pbuf), 0, PyBytes_GET_SIZE(pbuf)); - if (!cell_width || !cell_height) return Py_BuildValue("OII", pbuf, cell_width, cell_height); - 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); + if (!fcm.cell_width || !fcm.cell_height) return Py_BuildValue("OII", pbuf, fcm.cell_width, fcm.cell_height); + int num_chars_per_line = canvas_width / fcm.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 * fcm.cell_height); __attribute__((cleanup(destroy_hb_buffer))) hb_buffer_t *hb_buffer = hb_buffer_create(); if (!hb_buffer_pre_allocate(hb_buffer, 4*PyUnicode_GET_LENGTH(ptext))) { PyErr_NoMemory(); return NULL; } @@ -998,7 +999,7 @@ render_sample_text(Face *self, PyObject *args) { hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL); hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL); - if (cell_width > canvas_width) goto end; + if (fcm.cell_width > canvas_width) goto end; pixel *canvas = (pixel*)PyBytes_AS_STRING(pbuf); int load_flags = get_load_flags(self->hinting, self->hintstyle, FT_LOAD_RENDER); int error; @@ -1007,7 +1008,7 @@ render_sample_text(Face *self, PyObject *args) { for (unsigned int i = 0; i < len; i++) { float advance = (float)positions[i].x_advance / 64.0f; if (pen_x + advance > canvas_width) { - pen_y += cell_height; + pen_y += fcm.cell_height; pen_x = 0; if (pen_y >= canvas_height) break; } @@ -1019,7 +1020,7 @@ render_sample_text(Face *self, PyObject *args) { FT_Bitmap *bitmap = &self->face->glyph->bitmap; ProcessedBitmap pbm = EMPTY_PBM; populate_processed_bitmap(self->face->glyph, bitmap, &pbm, false); - place_bitmap_in_canvas(canvas, &pbm, canvas_width, canvas_height, x, 0, baseline, 99999, fg, 0, y); + place_bitmap_in_canvas(canvas, &pbm, canvas_width, canvas_height, x, 0, fcm.baseline, 99999, fg, 0, y); } const uint8_t *last_pixel = (uint8_t*)PyBytes_AS_STRING(pbuf) + PyBytes_GET_SIZE(pbuf) - sizeof(pixel); @@ -1028,7 +1029,7 @@ render_sample_text(Face *self, PyObject *args) { p[0] = r; p[1] = g; p[2] = b; p[3] = a; } end: - return Py_BuildValue("OII", pbuf, cell_width, cell_height); + return Py_BuildValue("OII", pbuf, fcm.cell_width, fcm.cell_height); } diff --git a/kitty/glfw.c b/kitty/glfw.c index 08b4b0f78c..5f2a4ef6e8 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -116,8 +116,8 @@ request_tick_callback(void) { static void min_size_for_os_window(OSWindow *window, int *min_width, int *min_height) { - *min_width = MAX(8u, window->fonts_data->cell_width + 1); - *min_height = MAX(8u, window->fonts_data->cell_height + 1); + *min_width = MAX(8u, window->fonts_data->fcm.cell_width + 1); + *min_height = MAX(8u, window->fonts_data->fcm.cell_height + 1); } @@ -1067,21 +1067,21 @@ calculate_layer_shell_window_size( if (!*height) *height = monitor_height; double spacing = edge_spacing(GLFW_EDGE_LEFT) + edge_spacing(GLFW_EDGE_RIGHT); spacing *= xdpi / 72.; - spacing += (fonts_data->cell_width * config->x_size_in_cells) / xscale; + spacing += (fonts_data->fcm.cell_width * config->x_size_in_cells) / xscale; *width = (uint32_t)(1. + spacing); } else if (config->edge == GLFW_EDGE_TOP || config->edge == GLFW_EDGE_BOTTOM) { if (!*width) *width = monitor_width; double spacing = edge_spacing(GLFW_EDGE_TOP) + edge_spacing(GLFW_EDGE_BOTTOM); spacing *= ydpi / 72.; - spacing += (fonts_data->cell_height * config->y_size_in_cells) / yscale; + spacing += (fonts_data->fcm.cell_height * config->y_size_in_cells) / yscale; *height = (uint32_t)(1. + spacing); } else { double spacing_x = edge_spacing(GLFW_EDGE_LEFT); spacing_x *= xdpi / 72.; - spacing_x += (fonts_data->cell_width * config->x_size_in_cells) / xscale; + spacing_x += (fonts_data->fcm.cell_width * config->x_size_in_cells) / xscale; double spacing_y = edge_spacing(GLFW_EDGE_TOP); spacing_y *= ydpi / 72.; - spacing_y += (fonts_data->cell_height * config->y_size_in_cells) / yscale; + spacing_y += (fonts_data->fcm.cell_height * config->y_size_in_cells) / yscale; *width = (uint32_t)(1. + spacing_x); *height = (uint32_t)(1. + spacing_y); } @@ -1213,7 +1213,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { get_window_content_scale(temp_window, &xscale, &yscale, &xdpi, &ydpi); } FONTS_DATA_HANDLE fonts_data = load_fonts_data(OPT(font_size), xdpi, ydpi); - PyObject *ret = PyObject_CallFunction(get_window_size, "IIddff", fonts_data->cell_width, fonts_data->cell_height, fonts_data->logical_dpi_x, fonts_data->logical_dpi_y, xscale, yscale); + PyObject *ret = PyObject_CallFunction(get_window_size, "IIddff", fonts_data->fcm.cell_width, fonts_data->fcm.cell_height, fonts_data->logical_dpi_x, fonts_data->logical_dpi_y, xscale, yscale); if (ret == NULL) return NULL; int width = PyLong_AsLong(PyTuple_GET_ITEM(ret, 0)), height = PyLong_AsLong(PyTuple_GET_ITEM(ret, 1)); Py_CLEAR(ret); @@ -1343,7 +1343,7 @@ void os_window_update_size_increments(OSWindow *window) { if (OPT(resize_in_steps)) { if (window->handle && window->fonts_data) glfwSetWindowSizeIncrements( - window->handle, window->fonts_data->cell_width, window->fonts_data->cell_height); + window->handle, window->fonts_data->fcm.cell_width, window->fonts_data->fcm.cell_height); } else { if (window->handle) glfwSetWindowSizeIncrements( window->handle, GLFW_DONT_CARE, GLFW_DONT_CARE); diff --git a/kitty/keys.c b/kitty/keys.c index a0861f2089..a811d73ecc 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -119,7 +119,7 @@ update_ime_focus(OSWindow *osw, bool focused) { void prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev) { - unsigned int cell_width = osw->fonts_data->cell_width, cell_height = osw->fonts_data->cell_height; + unsigned int cell_width = osw->fonts_data->fcm.cell_width, cell_height = osw->fonts_data->fcm.cell_height; unsigned int left = w->geometry.left, top = w->geometry.top; if (screen_is_overlay_active(screen)) { left += screen->overlay_line.cursor_x * cell_width; diff --git a/kitty/mouse.c b/kitty/mouse.c index a93232de30..aaedf46f3b 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -245,13 +245,13 @@ cell_for_pos(Window *w, unsigned int *x, unsigned int *y, bool *in_left_half_of_ qx = screen->columns - 1; in_left_half = false; } else if (mouse_x >= g->left) { - double xval = (double)(mouse_x - g->left) / os_window->fonts_data->cell_width; + double xval = (double)(mouse_x - g->left) / os_window->fonts_data->fcm.cell_width; double fxval = floor(xval); qx = (unsigned int)fxval; in_left_half = (xval - fxval <= 0.5) ? true : false; } if (mouse_y >= g->bottom) qy = screen->lines - 1; - else if (mouse_y >= g->top) qy = (unsigned int)((double)(mouse_y - g->top) / os_window->fonts_data->cell_height); + else if (mouse_y >= g->top) qy = (unsigned int)((double)(mouse_y - g->top) / os_window->fonts_data->fcm.cell_height); if (qx < screen->columns && qy < screen->lines) { *x = qx; *y = qy; *in_left_half_of_cell = in_left_half; @@ -296,7 +296,7 @@ do_drag_scroll(Window *w, bool upwards) { bool drag_scroll(Window *w, OSWindow *frame) { - unsigned int margin = frame->fonts_data->cell_height / 2; + unsigned int margin = frame->fonts_data->fcm.cell_height / 2; double y = frame->mouse_y; bool upwards = y <= (w->geometry.top + margin); if (upwards || y >= w->geometry.bottom - margin) { @@ -410,7 +410,7 @@ clear_click_queue(Window *w, int button) { static double radius_for_multiclick(void) { - return 0.5 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->cell_height : 8); + return 0.5 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->fcm.cell_height : 8); } static bool @@ -955,7 +955,7 @@ scroll_event(double xoffset, double yoffset, int flags, int modifiers) { bool is_high_resolution = flags & 1; if (yoffset != 0.0) { - s = scale_scroll(screen->modes.mouse_tracking_mode, yoffset, is_high_resolution, &screen->pending_scroll_pixels_y, global_state.callback_os_window->fonts_data->cell_height); + s = scale_scroll(screen->modes.mouse_tracking_mode, yoffset, is_high_resolution, &screen->pending_scroll_pixels_y, global_state.callback_os_window->fonts_data->fcm.cell_height); if (s) { bool upwards = s > 0; if (screen->modes.mouse_tracking_mode) { @@ -976,7 +976,7 @@ scroll_event(double xoffset, double yoffset, int flags, int modifiers) { } } if (xoffset != 0.0) { - s = scale_scroll(screen->modes.mouse_tracking_mode, xoffset, is_high_resolution, &screen->pending_scroll_pixels_x, global_state.callback_os_window->fonts_data->cell_width); + s = scale_scroll(screen->modes.mouse_tracking_mode, xoffset, is_high_resolution, &screen->pending_scroll_pixels_x, global_state.callback_os_window->fonts_data->fcm.cell_width); if (s) { if (screen->modes.mouse_tracking_mode) { int sz = encode_mouse_scroll(w, s > 0 ? 6 : 7, modifiers); diff --git a/kitty/shaders.c b/kitty/shaders.c index aefc150aca..473711f192 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -674,7 +674,7 @@ static GLfloat render_a_bar(OSWindow *os_window, Screen *screen, const CellRenderData *crd, WindowBarData *bar, PyObject *title, bool along_bottom) { GLfloat left = os_window->viewport_width * (crd->gl.xstart + 1.f) / 2.f; GLfloat right = left + os_window->viewport_width * crd->gl.width / 2.f; - unsigned bar_height = os_window->fonts_data->cell_height + 2; + unsigned bar_height = os_window->fonts_data->fcm.cell_height + 2; if (!bar_height || right <= left) return 0; unsigned bar_width = (unsigned)ceilf(right - left); if (!bar->buf || bar->width != bar_width || bar->height != bar_height) { @@ -801,7 +801,7 @@ draw_window_number(OSWindow *os_window, Screen *screen, const CellRenderData *cr GLfloat right = left + os_window->viewport_width * crd->gl.width / 2.f; GLfloat title_bar_height = 0; size_t requested_height = (size_t)(os_window->viewport_height * crd->gl.height / 2.f); - if (window->title && PyUnicode_Check(window->title) && (requested_height > (os_window->fonts_data->cell_height + 1) * 2)) { + if (window->title && PyUnicode_Check(window->title) && (requested_height > (os_window->fonts_data->fcm.cell_height + 1) * 2)) { title_bar_height = render_a_bar(os_window, screen, crd, &window->title_bar_data, window->title, false); } GLfloat ystart = crd->gl.ystart, height = crd->gl.height, xstart = crd->gl.xstart, width = crd->gl.width; diff --git a/kitty/state.c b/kitty/state.c index fc5340ab34..398e5366ff 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -417,8 +417,8 @@ detach_window(id_type os_window_id, id_type tab_id, id_type id) { static void resize_screen(OSWindow *os_window, Screen *screen, bool has_graphics) { if (screen) { - screen->cell_size.width = os_window->fonts_data->cell_width; - screen->cell_size.height = os_window->fonts_data->cell_height; + screen->cell_size.width = os_window->fonts_data->fcm.cell_width; + screen->cell_size.height = os_window->fonts_data->fcm.cell_height; screen_dirty_sprite_positions(screen); if (has_graphics) screen_rescale_images(screen); } @@ -437,8 +437,8 @@ attach_window(id_type os_window_id, id_type tab_id, id_type id) { make_os_window_context_current(osw); create_gpu_resources_for_window(w); if ( - w->render_data.screen->cell_size.width != osw->fonts_data->cell_width || - w->render_data.screen->cell_size.height != osw->fonts_data->cell_height + w->render_data.screen->cell_size.width != osw->fonts_data->fcm.cell_width || + w->render_data.screen->cell_size.height != osw->fonts_data->fcm.cell_height ) resize_screen(osw, w->render_data.screen, true); else screen_dirty_sprite_positions(w->render_data.screen); w->render_data.screen->reload_all_gpu_data = true; @@ -566,20 +566,20 @@ os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) { switch(OPT(tab_bar_edge)) { case TOP_EDGE: central->left = 0; central->right = os_window->viewport_width - 1; - central->top = os_window->fonts_data->cell_height + margin_inner + margin_outer; + central->top = os_window->fonts_data->fcm.cell_height + margin_inner + margin_outer; central->bottom = os_window->viewport_height - 1; central->top = MIN(central->top, central->bottom); tab_bar->top = margin_outer; break; default: central->left = 0; central->top = 0; central->right = os_window->viewport_width - 1; - long bottom = os_window->viewport_height - os_window->fonts_data->cell_height - 1 - margin_inner - margin_outer; + long bottom = os_window->viewport_height - os_window->fonts_data->fcm.cell_height - 1 - margin_inner - margin_outer; central->bottom = MAX(0, bottom); tab_bar->top = central->bottom + 1 + margin_inner; break; } tab_bar->left = central->left; tab_bar->right = central->right; - tab_bar->bottom = tab_bar->top + os_window->fonts_data->cell_height - 1; + tab_bar->bottom = tab_bar->top + os_window->fonts_data->fcm.cell_height - 1; } else { zero_at_ptr(tab_bar); central->left = 0; central->top = 0; central->right = os_window->viewport_width - 1; @@ -768,8 +768,8 @@ PYWRAP1(set_ignore_os_keyboard_processing) { static void init_window_render_data(OSWindow *osw, const WindowGeometry *g, WindowRenderData *d) { - d->dx = gl_size(osw->fonts_data->cell_width, osw->viewport_width); - d->dy = gl_size(osw->fonts_data->cell_height, osw->viewport_height); + d->dx = gl_size(osw->fonts_data->fcm.cell_width, osw->viewport_width); + d->dy = gl_size(osw->fonts_data->fcm.cell_height, osw->viewport_height); d->xstart = gl_pos_x(g->left, osw->viewport_width); d->ystart = gl_pos_y(g->top, osw->viewport_height); } @@ -818,7 +818,7 @@ PYWRAP1(viewport_for_window) { WITH_OS_WINDOW(os_window_id) os_window_regions(os_window, ¢ral, &tab_bar); vw = os_window->viewport_width; vh = os_window->viewport_height; - cell_width = os_window->fonts_data->cell_width; cell_height = os_window->fonts_data->cell_height; + cell_width = os_window->fonts_data->fcm.cell_width; cell_height = os_window->fonts_data->fcm.cell_height; goto end; END_WITH_OS_WINDOW end: @@ -830,7 +830,7 @@ PYWRAP1(cell_size_for_window) { unsigned int cell_width = 0, cell_height = 0; PA("K", &os_window_id); WITH_OS_WINDOW(os_window_id) - cell_width = os_window->fonts_data->cell_width; cell_height = os_window->fonts_data->cell_height; + cell_width = os_window->fonts_data->fcm.cell_width; cell_height = os_window->fonts_data->fcm.cell_height; goto end; END_WITH_OS_WINDOW end: @@ -1091,7 +1091,7 @@ PYWRAP1(get_os_window_size) { int width, height, fw, fh; get_os_window_size(os_window, &width, &height, &fw, &fh); get_os_window_content_scale(os_window, &xdpi, &ydpi, &xscale, &yscale); - unsigned int cell_width = os_window->fonts_data->cell_width, cell_height = os_window->fonts_data->cell_height; + unsigned int cell_width = os_window->fonts_data->fcm.cell_width, cell_height = os_window->fonts_data->fcm.cell_height; return Py_BuildValue("{si si si si sf sf sd sd sI sI}", "width", width, "height", height, "framebuffer_width", fw, "framebuffer_height", fh, "xscale", xscale, "yscale", yscale, "xdpi", xdpi, "ydpi", ydpi,