mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-07-11 02:03:22 +00:00
More work on multicell font rendering
This commit is contained in:
parent
8993386399
commit
b96da25380
4 changed files with 206 additions and 70 deletions
182
kitty/fonts.c
182
kitty/fonts.c
|
|
@ -34,6 +34,10 @@ typedef struct {
|
|||
unsigned int x, y, z, xnum, ynum;
|
||||
} GPUSpriteTracker;
|
||||
|
||||
typedef struct RunFont {
|
||||
unsigned scale, subscale_n, subscale_d, vertical_align, multicell_y;
|
||||
ssize_t font_idx;
|
||||
} RunFont;
|
||||
|
||||
static hb_buffer_t *harfbuzz_buffer = NULL;
|
||||
static hb_feature_t hb_features[3] = {{0}};
|
||||
|
|
@ -75,6 +79,18 @@ static void free_const(const void* x) { free((void*)x); }
|
|||
#define KEY_DTOR_FN free_const
|
||||
#include "kitty-verstable.h"
|
||||
|
||||
typedef struct ScaledFontData {
|
||||
FontCellMetrics fcm;
|
||||
double font_sz_in_pts;
|
||||
} ScaledFontData;
|
||||
|
||||
#define NAME scaled_font_map_t
|
||||
#define KEY_TY float
|
||||
#define VAL_TY ScaledFontData
|
||||
#define HASH_FN vt_hash_float
|
||||
#define CMPR_FN vt_cmpr_float
|
||||
#include "kitty-verstable.h"
|
||||
|
||||
typedef struct {
|
||||
FONTS_DATA_HEAD
|
||||
id_type id;
|
||||
|
|
@ -84,6 +100,7 @@ typedef struct {
|
|||
Canvas canvas;
|
||||
GPUSpriteTracker sprite_tracker;
|
||||
fallback_font_map_t fallback_font_map;
|
||||
scaled_font_map_t scaled_font_map;
|
||||
} FontGroup;
|
||||
|
||||
static FontGroup* font_groups = NULL;
|
||||
|
|
@ -161,6 +178,7 @@ del_font_group(FontGroup *fg) {
|
|||
free(fg->canvas.buf); fg->canvas.buf = NULL; fg->canvas = (Canvas){0};
|
||||
fg->sprite_map = free_sprite_map(fg->sprite_map);
|
||||
vt_cleanup(&fg->fallback_font_map);
|
||||
vt_cleanup(&fg->scaled_font_map);
|
||||
for (size_t i = 0; i < fg->fonts_count; i++) del_font(fg->fonts + i);
|
||||
free(fg->fonts); fg->fonts = NULL; fg->fonts_count = 0;
|
||||
}
|
||||
|
|
@ -419,8 +437,8 @@ adjust_ypos(unsigned int pos, unsigned int cell_height, int adjustment) {
|
|||
}
|
||||
|
||||
static void
|
||||
calc_cell_metrics(FontGroup *fg) {
|
||||
fg->fcm = cell_metrics(fg->fonts[fg->medium_font_idx].face);
|
||||
calc_cell_metrics(FontGroup *fg, PyObject *face) {
|
||||
fg->fcm = cell_metrics(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;
|
||||
|
|
@ -463,8 +481,6 @@ calc_cell_metrics(FontGroup *fg) {
|
|||
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, fg->fcm.cell_width, fg->fcm.cell_height);
|
||||
ensure_canvas_can_fit(fg, 8, 1);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -718,15 +734,58 @@ ensure_glyph_render_scratch_space(size_t sz) {
|
|||
#undef a
|
||||
}
|
||||
|
||||
static void
|
||||
scaled_cell_dimensions(RunFont rf, unsigned *width, unsigned *height) {
|
||||
*width *= rf.scale;
|
||||
*height *= rf.scale;
|
||||
static float
|
||||
effective_scale(RunFont rf) {
|
||||
float ans = MAX(1u, rf.scale);
|
||||
if (rf.subscale_n && rf.subscale_d && rf.subscale_n < rf.subscale_d) {
|
||||
double frac = ((double)rf.subscale_n) / rf.subscale_d;
|
||||
*width = (unsigned)ceil(frac * *width);
|
||||
*height = (unsigned)ceil(frac * *height);
|
||||
ans *= ((float)rf.subscale_n) / rf.subscale_d;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static float
|
||||
scaled_cell_dimensions(RunFont rf, unsigned *width, unsigned *height) {
|
||||
float frac = effective_scale(rf);
|
||||
*width = (unsigned)ceil(frac * *width);
|
||||
*height = (unsigned)ceil(frac * *height);
|
||||
return frac;
|
||||
}
|
||||
|
||||
static float
|
||||
apply_scale_to_font_group(FontGroup *fg, RunFont *rf) {
|
||||
unsigned int scaled_cell_width = fg->fcm.cell_width, scaled_cell_height = fg->fcm.cell_height;
|
||||
float scale = rf ? scaled_cell_dimensions(*rf, &scaled_cell_width, &scaled_cell_height) : 1.f;
|
||||
scaled_font_map_t_itr i = vt_get(&fg->scaled_font_map, scale);
|
||||
ScaledFontData sfd;
|
||||
if (vt_is_end(i)) {
|
||||
Font *medium_font = &fg->fonts[fg->medium_font_idx];
|
||||
FontGroup copy = {.fcm=fg->fcm, .logical_dpi_x=fg->logical_dpi_x, .logical_dpi_y=fg->logical_dpi_y};
|
||||
copy.fcm.cell_width = scaled_cell_width; copy.fcm.cell_height = scaled_cell_height;
|
||||
copy.font_sz_in_pts = scale * fg->font_sz_in_pts;
|
||||
if (!face_apply_scaling(medium_font->face, (FONTS_DATA_HANDLE)©)) {
|
||||
if (PyErr_Occurred()) PyErr_Print();
|
||||
fatal("Could not apply scale of %f to font group at size: %f", scale, copy.font_sz_in_pts);
|
||||
}
|
||||
calc_cell_metrics(©, medium_font->face);
|
||||
if (copy.fcm.cell_width > scaled_cell_width || copy.fcm.cell_height > scaled_cell_height) {
|
||||
float wfrac = (float)copy.fcm.cell_width / scaled_cell_width, hfrac = (float)copy.fcm.cell_height / scaled_cell_height;
|
||||
float frac = MIN(wfrac, hfrac);
|
||||
copy.font_sz_in_pts *= frac;
|
||||
while(true) {
|
||||
if (!face_apply_scaling(medium_font->face, (FONTS_DATA_HANDLE)©)) {
|
||||
if (PyErr_Occurred()) PyErr_Print();
|
||||
fatal("Could not apply scale of %f to font group at size: %f", scale, copy.font_sz_in_pts);
|
||||
}
|
||||
calc_cell_metrics(©, medium_font->face);
|
||||
if (copy.fcm.cell_width <= scaled_cell_width && copy.fcm.cell_height <= scaled_cell_height) break;
|
||||
if (copy.font_sz_in_pts < 1) fatal("Could not apply scale of %f to font group as font size (%f) is less than minimum threshold", scale, copy.font_sz_in_pts);
|
||||
copy.font_sz_in_pts -= 0.1;
|
||||
}
|
||||
}
|
||||
} else sfd = i.data->val;
|
||||
fg->font_sz_in_pts = sfd.font_sz_in_pts;
|
||||
fg->fcm = sfd.fcm;
|
||||
return scale;
|
||||
}
|
||||
|
||||
static pixel*
|
||||
|
|
@ -738,6 +797,28 @@ extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells)
|
|||
return ans;
|
||||
}
|
||||
|
||||
static pixel*
|
||||
extract_scaled_cell_from_canvas(
|
||||
Canvas *canvas, unsigned i, unsigned num_cells, Region src, Region dest,
|
||||
unsigned unscaled_cell_width, unsigned unscaled_cell_height, unsigned scaled_cell_width, unsigned scaled_cell_height
|
||||
) {
|
||||
unsigned unscaled_cell_area = unscaled_cell_width * unscaled_cell_height;
|
||||
pixel *ans = canvas->buf + (canvas->size_in_bytes / sizeof(canvas->buf[0]) - unscaled_cell_area);
|
||||
memset(ans, 0, sizeof(ans[0]) * unscaled_cell_area);
|
||||
unsigned xoff = i * unscaled_cell_width;
|
||||
unsigned src_row_size = scaled_cell_width * num_cells;
|
||||
if (xoff >= src_row_size) return ans; // nothing to copy
|
||||
unsigned width = MIN(src_row_size - xoff, unscaled_cell_width);
|
||||
/*printf("\n");*/
|
||||
for (unsigned src_y=src.top, dest_y=dest.top; src_y < src.bottom && src_y < scaled_cell_height && dest_y < dest.bottom && dest_y < unscaled_cell_height; src_y++, dest_y++) {
|
||||
pixel *srcp = canvas->buf + src_row_size * src_y;
|
||||
pixel *destp = ans + unscaled_cell_width * dest_y;
|
||||
/*for (unsigned x = xoff; x < xoff + width; x++) { printf("%d ", srcp[x] != 0); } printf("\n");*/
|
||||
memcpy(destp, srcp + xoff, width * sizeof(destp[0]));
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_regions_for_line(RunFont rf, unsigned cell_height, Region *src, Region *dest) {
|
||||
unsigned src_height = src->bottom;
|
||||
|
|
@ -794,14 +875,15 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell,
|
|||
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->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);
|
||||
render_alpha_mask(alpha_mask, fg->canvas.buf, &src, &dest, width, width, 0xffffff);
|
||||
/*for (unsigned y = 0; y < height; y++) { for (unsigned x = 0; x < width; x++) { printf("%d ", (fg->canvas.buf + width * y)[0] != 0); } printf("\n"); }*/
|
||||
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);
|
||||
} else {
|
||||
calculate_regions_for_line(rf, fg->fcm.cell_height, &src, &dest);
|
||||
/*printf("width: %u height: %u src.top: %u src.bottom: %u\n", width, height, src.top, src.bottom);*/
|
||||
for (unsigned i = 0; i < rf.scale; i++) {
|
||||
pixel *b = extract_cell_from_canvas(fg, i, rf.scale);
|
||||
pixel *b = extract_scaled_cell_from_canvas(&fg->canvas, i, 1, src, dest, fg->fcm.cell_width, fg->fcm.cell_height, width, height);
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b);
|
||||
}
|
||||
}
|
||||
|
|
@ -835,7 +917,11 @@ set_cell_sprite(GPUCell *cell, const SpritePosition *sp) {
|
|||
}
|
||||
|
||||
static void
|
||||
render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, RunFont rf, glyph_index *glyphs, unsigned glyph_count, bool center_glyph, const TextCache *tc) {
|
||||
render_group(
|
||||
FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells,
|
||||
hb_glyph_info_t *info, hb_glyph_position_t *positions, RunFont rf, glyph_index *glyphs, unsigned glyph_count,
|
||||
bool center_glyph, const TextCache *tc, float scale, FontCellMetrics unscaled_metrics
|
||||
) {
|
||||
#define sp global_glyph_render_scratch.sprite_positions
|
||||
int error = 0;
|
||||
bool all_rendered = true;
|
||||
|
|
@ -862,14 +948,26 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPU
|
|||
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++) {
|
||||
if (!sp[i]->rendered) {
|
||||
sp[i]->rendered = true;
|
||||
sp[i]->colored = was_colored;
|
||||
pixel *buf = num_cells == 1 ? fg->canvas.buf : extract_cell_from_canvas(fg, i, num_cells);
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, buf);
|
||||
if (scale == 1.f) {
|
||||
for (unsigned i = 0; i < num_cells; i++) {
|
||||
if (!sp[i]->rendered) {
|
||||
sp[i]->rendered = true;
|
||||
sp[i]->colored = was_colored;
|
||||
pixel *buf = num_cells == 1 ? fg->canvas.buf : extract_cell_from_canvas(fg, i, num_cells);
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, buf);
|
||||
}
|
||||
set_cell_sprite(gpu_cells + i, sp[i]);
|
||||
}
|
||||
} else {
|
||||
Region src={.bottom=fg->fcm.cell_height, .right=fg->fcm.cell_width * num_cells}, dest={0};
|
||||
calculate_regions_for_line(rf, unscaled_metrics.cell_height, &src, &dest);
|
||||
for (unsigned i = 0; i < num_cells; i++) {
|
||||
if (!sp[i]->rendered) {
|
||||
pixel *b = extract_scaled_cell_from_canvas(&fg->canvas, i, num_cells, src, dest, unscaled_metrics.cell_width, unscaled_metrics.cell_height, fg->fcm.cell_width, fg->fcm.cell_height);
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b);
|
||||
}
|
||||
set_cell_sprite(gpu_cells + i, sp[i]);
|
||||
}
|
||||
set_cell_sprite(gpu_cells + i, sp[i]);
|
||||
}
|
||||
#undef sp
|
||||
}
|
||||
|
|
@ -1269,8 +1367,9 @@ 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, RunFont rf, bool disable_ligature, const TextCache *tc) {
|
||||
face_apply_scaling(font->face, rf);
|
||||
shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font, RunFont rf, FontGroup *fg, bool disable_ligature, const TextCache *tc) {
|
||||
float scale = apply_scale_to_font_group(fg, &rf);
|
||||
if (scale != 1.f) if (!face_apply_scaling(font->face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();
|
||||
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);
|
||||
|
|
@ -1282,6 +1381,10 @@ shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells
|
|||
hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, group_state.num_glyphs, dbuf, sizeof(dbuf), NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS);
|
||||
printf("\n%s\n", dbuf);
|
||||
#endif
|
||||
if (scale != 1.f) {
|
||||
apply_scale_to_font_group(fg, NULL);
|
||||
if (!face_apply_scaling(font->face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1321,6 +1424,9 @@ split_run_at_offset(index_type cursor_offset, index_type *left, index_type *righ
|
|||
static void
|
||||
render_groups(FontGroup *fg, RunFont rf, bool center_glyph, const TextCache *tc) {
|
||||
unsigned idx = 0;
|
||||
FontCellMetrics unscaled_metrics = fg->fcm;
|
||||
float scale = apply_scale_to_font_group(fg, &rf);
|
||||
if (scale != 1.f) if (!face_apply_scaling(fg->fonts[rf.font_idx].face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();
|
||||
while (idx <= G(group_idx)) {
|
||||
Group *group = G(groups) + idx;
|
||||
if (!group->num_cells) break;
|
||||
|
|
@ -1329,10 +1435,15 @@ render_groups(FontGroup *fg, RunFont rf, bool center_glyph, const TextCache *tc)
|
|||
if (group->num_glyphs) {
|
||||
ensure_glyph_render_scratch_space(MAX(group->num_glyphs, group->num_cells));
|
||||
for (unsigned i = 0; i < group->num_glyphs; i++) global_glyph_render_scratch.glyphs[i] = G(info)[group->first_glyph_idx + i].codepoint;
|
||||
render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, rf, global_glyph_render_scratch.glyphs, group->num_glyphs, center_glyph, tc);
|
||||
render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, rf, global_glyph_render_scratch.glyphs, group->num_glyphs, center_glyph, tc, scale, unscaled_metrics);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (scale != 1.f) {
|
||||
apply_scale_to_font_group(fg, NULL);
|
||||
if (!face_apply_scaling(fg->fonts[rf.font_idx].face, (FONTS_DATA_HANDLE)fg) && PyErr_Occurred()) PyErr_Print();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
|
@ -1353,6 +1464,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||
PyObject *face = NULL;
|
||||
Font *font;
|
||||
if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create at least one font group first"); return NULL; }
|
||||
FontGroup *fg = font_groups;
|
||||
if (path) {
|
||||
face = face_from_path(path, index, (FONTS_DATA_HANDLE)font_groups);
|
||||
if (face == NULL) return NULL;
|
||||
|
|
@ -1360,11 +1472,10 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||
font->face = face;
|
||||
if (!init_hash_tables(font)) return NULL;
|
||||
} else {
|
||||
FontGroup *fg = font_groups;
|
||||
font = fg->fonts + fg->medium_font_idx;
|
||||
}
|
||||
RunFont rf = {0};
|
||||
shape_run(line->cpu_cells, line->gpu_cells, num, font, rf, false, line->text_cache);
|
||||
shape_run(line->cpu_cells, line->gpu_cells, num, font, rf, fg, false, line->text_cache);
|
||||
|
||||
PyObject *ans = PyList_New(0);
|
||||
unsigned int idx = 0;
|
||||
|
|
@ -1388,20 +1499,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], rf, disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS, tc);
|
||||
shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[rf.font_idx], rf, fg, 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], rf, false, tc);
|
||||
shape_run(first_cpu_cell, first_gpu_cell, left, &fg->fonts[rf.font_idx], rf, fg, 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], rf, true, tc);
|
||||
shape_run(first_cpu_cell + left, first_gpu_cell + left, right - left, &fg->fonts[rf.font_idx], rf, fg, 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], rf, false, tc);
|
||||
shape_run(first_cpu_cell + right, first_gpu_cell + right, num_cells - right, &fg->fonts[rf.font_idx], rf, fg, false, tc);
|
||||
render_groups(fg, rf, center_glyph, tc);
|
||||
}
|
||||
break;
|
||||
|
|
@ -1636,6 +1747,7 @@ initialize_font_group(FontGroup *fg) {
|
|||
fg->fonts_count = 1; // the 0 index font is the box font
|
||||
if (!init_hash_tables(fg->fonts)) fatal("Out of memory");
|
||||
vt_init(&fg->fallback_font_map);
|
||||
vt_init(&fg->scaled_font_map);
|
||||
#define I(attr) if (descriptor_indices.attr) fg->attr##_font_idx = initialize_font(fg, descriptor_indices.attr, #attr); else fg->attr##_font_idx = -1;
|
||||
fg->medium_font_idx = initialize_font(fg, 0, "medium");
|
||||
I(bold); I(italic); I(bi);
|
||||
|
|
@ -1647,12 +1759,16 @@ initialize_font_group(FontGroup *fg) {
|
|||
fg->first_fallback_font_idx++;
|
||||
}
|
||||
#undef I
|
||||
calc_cell_metrics(fg);
|
||||
calc_cell_metrics(fg, fg->fonts[fg->medium_font_idx].face);
|
||||
ensure_canvas_can_fit(fg, 8, 1);
|
||||
sprite_tracker_set_layout(&fg->sprite_tracker, fg->fcm.cell_width, fg->fcm.cell_height);
|
||||
// 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->fcm.cell_height, true, (FONTS_DATA_HANDLE)fg);
|
||||
}
|
||||
ScaledFontData sfd = {.fcm=fg->fcm, .font_sz_in_pts=fg->font_sz_in_pts};
|
||||
vt_insert(&fg->scaled_font_map, 1.f, sfd);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,6 @@ 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
|
||||
|
|
@ -62,7 +56,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 face_apply_scaling(PyObject*face, const FONTS_DATA_HANDLE fg);
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -30,15 +30,20 @@ typedef union FaceIndex {
|
|||
FT_Long val;
|
||||
} FaceIndex;
|
||||
|
||||
typedef struct FaceMetrics {
|
||||
float size_in_pts;
|
||||
unsigned int units_per_EM;
|
||||
// The following are in font units use font_units_to_pixels_x/y to convert to pixels
|
||||
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
|
||||
} FaceMetrics;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
FT_Face face;
|
||||
unsigned int units_per_EM;
|
||||
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
|
||||
FaceMetrics metrics;
|
||||
int hinting, hintstyle;
|
||||
bool is_scalable, has_color, is_variable, has_svg;
|
||||
float size_in_pts;
|
||||
FT_F26Dot6 char_width, char_height;
|
||||
FT_UInt xdpi, ydpi;
|
||||
PyObject *path;
|
||||
|
|
@ -46,7 +51,6 @@ typedef struct {
|
|||
hb_codepoint_t space_glyph_id;
|
||||
void *extra_data;
|
||||
free_extra_data_func free_extra_data;
|
||||
float apple_leading;
|
||||
PyObject *name_lookup_table;
|
||||
FontFeatures font_features;
|
||||
} Face;
|
||||
|
|
@ -128,7 +132,7 @@ get_height_for_char(Face *self, char ch) {
|
|||
unsigned int ans = 0;
|
||||
int glyph_index = FT_Get_Char_Index(self->face, ch);
|
||||
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
|
||||
unsigned int baseline = font_units_to_pixels_y(self, self->ascender);
|
||||
unsigned int baseline = font_units_to_pixels_y(self, self->metrics.ascender);
|
||||
FT_GlyphSlotRec *glyph = self->face->glyph;
|
||||
FT_Bitmap *bm = &glyph->bitmap;
|
||||
if (glyph->bitmap_top <= 0 || (glyph->bitmap_top > 0 && (unsigned int)glyph->bitmap_top < baseline)) {
|
||||
|
|
@ -140,7 +144,7 @@ get_height_for_char(Face *self, char ch) {
|
|||
|
||||
static unsigned int
|
||||
calc_cell_height(Face *self, bool for_metrics) {
|
||||
unsigned int ans = font_units_to_pixels_y(self, self->height);
|
||||
unsigned int ans = font_units_to_pixels_y(self, self->metrics.height);
|
||||
if (for_metrics) {
|
||||
unsigned int underscore_height = get_height_for_char(self, '_');
|
||||
if (underscore_height > ans) {
|
||||
|
|
@ -193,7 +197,7 @@ set_size_for_face(PyObject *s, unsigned int desired_height, bool force, FONTS_DA
|
|||
FT_F26Dot6 w = (FT_F26Dot6)(ceil(fg->font_sz_in_pts * 64.0));
|
||||
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;
|
||||
self->metrics.size_in_pts = (float)fg->font_sz_in_pts;
|
||||
return set_font_size(self, w, w, xdpi, ydpi, desired_height, fg->fcm.cell_height);
|
||||
}
|
||||
|
||||
|
|
@ -204,16 +208,32 @@ set_size(Face *self, PyObject *args) {
|
|||
FT_F26Dot6 w = (FT_F26Dot6)(ceil(font_sz_in_pts * 64.0));
|
||||
FT_UInt xdpi = (FT_UInt)dpi_x, ydpi = (FT_UInt)dpi_y;
|
||||
if (self->char_width == w && self->char_height == w && self->xdpi == xdpi && self->ydpi == ydpi) { Py_RETURN_NONE; }
|
||||
self->size_in_pts = (float)font_sz_in_pts;
|
||||
self->metrics.size_in_pts = (float)font_sz_in_pts;
|
||||
if (!set_font_size(self, w, w, xdpi, ydpi, 0, 0)) return NULL;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static bool
|
||||
init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_HANDLE fg) {
|
||||
#define CPY(n) self->n = self->face->n;
|
||||
static void
|
||||
copy_face_metrics(Face *self) {
|
||||
#define CPY(n) self->metrics.n = self->face->n;
|
||||
CPY(units_per_EM); CPY(ascender); CPY(descender); CPY(height); CPY(max_advance_width); CPY(max_advance_height); CPY(underline_position); CPY(underline_thickness);
|
||||
#undef CPY
|
||||
}
|
||||
|
||||
bool
|
||||
face_apply_scaling(PyObject *f, const FONTS_DATA_HANDLE fg) {
|
||||
Face *self = (Face*)f;
|
||||
if (set_size_for_face(f, 0, false, fg)) {
|
||||
if (self->harfbuzz_font) hb_ft_font_changed(self->harfbuzz_font);
|
||||
copy_face_metrics(self);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_HANDLE fg) {
|
||||
copy_face_metrics(self);
|
||||
self->is_scalable = FT_IS_SCALABLE(self->face);
|
||||
self->has_color = FT_HAS_COLOR(self->face);
|
||||
self->is_variable = FT_HAS_MULTIPLE_MASTERS(self->face);
|
||||
|
|
@ -230,8 +250,8 @@ init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_
|
|||
|
||||
TT_OS2 *os2 = (TT_OS2*)FT_Get_Sfnt_Table(self->face, FT_SFNT_OS2);
|
||||
if (os2 != NULL) {
|
||||
self->strikethrough_position = os2->yStrikeoutPosition;
|
||||
self->strikethrough_thickness = os2->yStrikeoutSize;
|
||||
self->metrics.strikethrough_position = os2->yStrikeoutPosition;
|
||||
self->metrics.strikethrough_thickness = os2->yStrikeoutSize;
|
||||
}
|
||||
|
||||
self->path = path; Py_INCREF(self->path);
|
||||
|
|
@ -397,17 +417,17 @@ cell_metrics(PyObject *s) {
|
|||
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));
|
||||
ans.baseline = font_units_to_pixels_y(self, self->metrics.ascender);
|
||||
ans.underline_position = MIN(ans.cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->metrics.ascender - self->metrics.underline_position)));
|
||||
ans.underline_thickness = MAX(1, font_units_to_pixels_y(self, self->metrics.underline_thickness));
|
||||
|
||||
if (self->strikethrough_position != 0) {
|
||||
ans.strikethrough_position = MIN(ans.cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->strikethrough_position)));
|
||||
if (self->metrics.strikethrough_position != 0) {
|
||||
ans.strikethrough_position = MIN(ans.cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->metrics.ascender - self->metrics.strikethrough_position)));
|
||||
} else {
|
||||
ans.strikethrough_position = (unsigned int)floor(ans.baseline * 0.65);
|
||||
}
|
||||
if (self->strikethrough_thickness > 0) {
|
||||
ans.strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->strikethrough_thickness));
|
||||
if (self->metrics.strikethrough_thickness > 0) {
|
||||
ans.strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->metrics.strikethrough_thickness));
|
||||
} else {
|
||||
ans.strikethrough_thickness = ans.underline_thickness;
|
||||
}
|
||||
|
|
@ -1037,22 +1057,26 @@ end:
|
|||
|
||||
static PyMemberDef members[] = {
|
||||
#define MEM(name, type) {#name, type, offsetof(Face, name), READONLY, #name}
|
||||
MEM(units_per_EM, T_UINT),
|
||||
MEM(ascender, T_INT),
|
||||
MEM(descender, T_INT),
|
||||
MEM(height, T_INT),
|
||||
MEM(max_advance_width, T_INT),
|
||||
MEM(max_advance_height, T_INT),
|
||||
MEM(underline_position, T_INT),
|
||||
MEM(underline_thickness, T_INT),
|
||||
MEM(strikethrough_position, T_INT),
|
||||
MEM(strikethrough_thickness, T_INT),
|
||||
#define MMEM(name, type) {#name, type, offsetof(Face, metrics) + offsetof(FaceMetrics, name), READONLY, #name}
|
||||
MMEM(units_per_EM, T_UINT),
|
||||
MMEM(size_in_pts, T_FLOAT),
|
||||
MMEM(ascender, T_INT),
|
||||
MMEM(descender, T_INT),
|
||||
MMEM(height, T_INT),
|
||||
MMEM(max_advance_width, T_INT),
|
||||
MMEM(max_advance_height, T_INT),
|
||||
MMEM(underline_position, T_INT),
|
||||
MMEM(underline_thickness, T_INT),
|
||||
MMEM(strikethrough_position, T_INT),
|
||||
MMEM(strikethrough_thickness, T_INT),
|
||||
MEM(is_scalable, T_BOOL),
|
||||
MEM(is_variable, T_BOOL),
|
||||
MEM(has_svg, T_BOOL),
|
||||
MEM(has_color, T_BOOL),
|
||||
MEM(path, T_OBJECT_EX),
|
||||
{NULL} /* Sentinel */
|
||||
#undef MEM
|
||||
#undef MMEM
|
||||
};
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ vt_hash_bytes(const void *data, const size_t size) {
|
|||
return hash;
|
||||
}
|
||||
|
||||
static inline uint64_t vt_hash_float(float x) { return vt_hash_integer((uint64_t)x); }
|
||||
static inline bool vt_cmpr_float(float a, float b) { return a == b; }
|
||||
#define vt_create_for_loop(itr_type, itr, table) for (itr_type itr = vt_first(table); !vt_is_end(itr); itr = vt_next(itr))
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue