Skip to content

Commit

Permalink
Store font related cell metrics in a single struct
Browse files Browse the repository at this point in the history
  • Loading branch information
kovidgoyal committed Dec 3, 2024
1 parent 1514f09 commit 4752a3c
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 113 deletions.
2 changes: 1 addition & 1 deletion kitty/child-monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
6 changes: 5 additions & 1 deletion kitty/data-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
109 changes: 51 additions & 58 deletions kitty/fonts.c

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion kitty/fonts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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);
Expand Down
47 changes: 24 additions & 23 deletions kitty/freetype.c
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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; }
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
Expand All @@ -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);
}


Expand Down
16 changes: 8 additions & 8 deletions kitty/glfw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}


Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion kitty/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 6 additions & 6 deletions kitty/mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions kitty/shaders.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 4752a3c

Please sign in to comment.