All tests pass
This commit is contained in:
parent
523aadaa3b
commit
ed44515318
@ -178,7 +178,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
|
||||
}
|
||||
|
||||
PyObject*
|
||||
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONT_DATA_HANDLE fg UNUSED) {
|
||||
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) {
|
||||
CTFace *self = (CTFace*)base_face;
|
||||
CTFontRef new_font;
|
||||
if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL);
|
||||
@ -214,12 +214,12 @@ is_glyph_empty(PyObject *s, glyph_index g) {
|
||||
}
|
||||
|
||||
static inline float
|
||||
scaled_point_sz(FONT_DATA_HANDLE fg) {
|
||||
scaled_point_sz(FONTS_DATA_HANDLE fg) {
|
||||
return ((fg->logical_dpi_x + fg->logical_dpi_y) / 144.0) * fg->font_sz_in_pts;
|
||||
}
|
||||
|
||||
bool
|
||||
set_size_for_face(PyObject *s, unsigned int UNUSED desired_height, bool force, FONT_DATA_HANDLE fg) {
|
||||
set_size_for_face(PyObject *s, unsigned int UNUSED desired_height, bool force, FONTS_DATA_HANDLE fg) {
|
||||
CTFace *self = (CTFace*)s;
|
||||
float sz = scaled_point_sz(fg);
|
||||
if (!force && self->scaled_point_sz == sz) return true;
|
||||
@ -282,7 +282,7 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
|
||||
}
|
||||
|
||||
PyObject*
|
||||
face_from_descriptor(PyObject *descriptor, FONT_DATA_HANDLE fg) {
|
||||
face_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) {
|
||||
CTFontDescriptorRef desc = font_descriptor_from_python(descriptor);
|
||||
if (!desc) return NULL;
|
||||
CTFontRef font = CTFontCreateWithFontDescriptor(desc, scaled_point_sz(fg), NULL);
|
||||
@ -292,7 +292,7 @@ face_from_descriptor(PyObject *descriptor, FONT_DATA_HANDLE fg) {
|
||||
}
|
||||
|
||||
PyObject*
|
||||
face_from_path(const char *path, int UNUSED index, FONT_DATA_HANDLE fg UNUSED) {
|
||||
face_from_path(const char *path, int UNUSED index, FONTS_DATA_HANDLE fg UNUSED) {
|
||||
CFStringRef s = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
|
||||
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, s, kCFURLPOSIXPathStyle, false);
|
||||
CGDataProviderRef dp = CGDataProviderCreateWithURL(url);
|
||||
@ -371,7 +371,7 @@ render_glyphs(CTFontRef font, unsigned int width, unsigned int height, unsigned
|
||||
}
|
||||
|
||||
static inline bool
|
||||
do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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, bool allow_resize, FONT_DATA_HANDLE fg) {
|
||||
do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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, bool allow_resize, FONTS_DATA_HANDLE fg) {
|
||||
unsigned int canvas_width = cell_width * num_cells;
|
||||
CGRect br = CTFontGetBoundingRectsForGlyphs(ct_font, kCTFontOrientationHorizontal, glyphs, boxes, num_glyphs);
|
||||
if (allow_resize) {
|
||||
@ -408,7 +408,7 @@ do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_g
|
||||
}
|
||||
|
||||
bool
|
||||
render_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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, FONT_DATA_HANDLE fg) {
|
||||
render_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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 fg) {
|
||||
CTFace *self = (CTFace*)s;
|
||||
for (unsigned i=0; i < num_glyphs; i++) glyphs[i] = info[i].codepoint;
|
||||
return do_render(self->ct_font, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, true, fg);
|
||||
|
||||
147
kitty/fonts.c
147
kitty/fonts.c
@ -60,19 +60,13 @@ static hb_buffer_t *harfbuzz_buffer = NULL;
|
||||
static char_type shape_buffer[4096] = {0};
|
||||
static size_t max_texture_size = 1024, max_array_len = 1024;
|
||||
|
||||
typedef struct {
|
||||
PyObject *face;
|
||||
bool bold, italic;
|
||||
} Descriptor;
|
||||
|
||||
typedef struct {
|
||||
char_type left, right;
|
||||
size_t font_idx;
|
||||
} SymbolMap;
|
||||
|
||||
static Descriptor *descriptors = NULL;
|
||||
static size_t num_symbol_fonts = 0;
|
||||
static SymbolMap *symbol_maps = NULL;
|
||||
static size_t num_symbol_maps = 0;
|
||||
|
||||
|
||||
|
||||
@ -99,6 +93,7 @@ static FontGroup* font_groups = NULL;
|
||||
static size_t font_groups_capacity = 0;
|
||||
static size_t num_font_groups = 0;
|
||||
static id_type font_group_id_counter = 0;
|
||||
static void initialize_font_group(FontGroup *fg);
|
||||
|
||||
static inline void
|
||||
save_window_font_groups() {
|
||||
@ -151,7 +146,7 @@ add_font_group() {
|
||||
if (num_font_groups >= font_groups_capacity) {
|
||||
save_window_font_groups();
|
||||
font_groups_capacity += 5;
|
||||
font_groups = realloc(font_groups, sizeof(FontGroup) * num_font_groups);
|
||||
font_groups = realloc(font_groups, sizeof(FontGroup) * font_groups_capacity);
|
||||
if (font_groups == NULL) fatal("Out of memory creating a new font group");
|
||||
restore_window_font_groups();
|
||||
}
|
||||
@ -171,6 +166,7 @@ font_group_for(double font_sz_in_pts, double logical_dpi_x, double logical_dpi_y
|
||||
fg->logical_dpi_x = logical_dpi_x;
|
||||
fg->logical_dpi_y = logical_dpi_y;
|
||||
fg->id = ++font_group_id_counter;
|
||||
initialize_font_group(fg);
|
||||
return fg;
|
||||
}
|
||||
|
||||
@ -349,11 +345,8 @@ desc_to_face(PyObject *desc, FONTS_DATA_HANDLE fg) {
|
||||
|
||||
|
||||
static inline bool
|
||||
init_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face, bool emoji_presentation, FONTS_DATA_HANDLE fg) {
|
||||
PyObject *face;
|
||||
if (is_face) { face = descriptor; Py_INCREF(face); }
|
||||
else { face = desc_to_face(descriptor, fg); if (face == NULL) return false; }
|
||||
f->face = face;
|
||||
init_font(Font *f, PyObject *face, bool bold, bool italic, bool emoji_presentation) {
|
||||
f->face = face; Py_INCREF(f->face);
|
||||
f->bold = bold; f->italic = italic; f->emoji_presentation = emoji_presentation;
|
||||
return true;
|
||||
}
|
||||
@ -384,7 +377,7 @@ free_font_groups() {
|
||||
|
||||
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 != NULL && python_send_to_gpu_impl != Py_None) {
|
||||
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));
|
||||
if (ret == NULL) PyErr_Print();
|
||||
@ -415,6 +408,7 @@ calc_cell_metrics(FontGroup *fg) {
|
||||
}
|
||||
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;
|
||||
free(fg->canvas);
|
||||
fg->canvas = calloc(CELLS_IN_CANVAS * fg->cell_width * fg->cell_height, sizeof(pixel));
|
||||
if (!fg->canvas) fatal("Out of memory allocating canvas for font group");
|
||||
@ -473,7 +467,7 @@ load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji
|
||||
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;
|
||||
Font *af = &fg->fonts[ans];
|
||||
if (!init_font(af, face, bold, italic, true, emoji_presentation, (FONTS_DATA_HANDLE)fg)) fatal("Out of memory");
|
||||
if (!init_font(af, face, bold, italic, emoji_presentation)) fatal("Out of memory");
|
||||
Py_DECREF(face);
|
||||
fg->fallback_fonts_count++;
|
||||
fg->fonts_count++;
|
||||
@ -500,7 +494,7 @@ fallback_font(FontGroup *fg, Cell *cell) {
|
||||
|
||||
static inline ssize_t
|
||||
in_symbol_maps(FontGroup *fg, char_type ch) {
|
||||
for (size_t i = 0; i < num_symbol_fonts; i++) {
|
||||
for (size_t i = 0; i < num_symbol_maps; i++) {
|
||||
if (symbol_maps[i].left <= ch && ch <= symbol_maps[i].right) return fg->first_symbol_font_idx + symbol_maps[i].font_idx;
|
||||
}
|
||||
return NO_FONT;
|
||||
@ -567,7 +561,7 @@ START_ALLOW_CASE_RANGE
|
||||
END_ALLOW_CASE_RANGE
|
||||
}
|
||||
|
||||
static PyObject* box_drawing_function = NULL, *prerender_function = NULL;
|
||||
static PyObject* box_drawing_function = NULL, *prerender_function = NULL, *descriptor_for_idx = NULL;
|
||||
|
||||
void
|
||||
render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {
|
||||
@ -961,7 +955,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
||||
PyList_Append(ans, Py_BuildValue("IIHN", group->num_cells, group->num_glyphs, first_glyph, eg));
|
||||
idx++;
|
||||
}
|
||||
if (face) { Py_CLEAR(face); free(font); }
|
||||
if (face) { Py_CLEAR(face); free_maps(font); free(font); }
|
||||
return ans;
|
||||
}
|
||||
#undef G
|
||||
@ -1030,48 +1024,31 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
clear_descriptors() {
|
||||
if (descriptors) {
|
||||
Descriptor *d = descriptors;
|
||||
while(d) {
|
||||
Py_CLEAR(d->face);
|
||||
d++;
|
||||
}
|
||||
free(descriptors); descriptors = NULL;
|
||||
}
|
||||
free(symbol_maps); symbol_maps = NULL;
|
||||
clear_symbol_maps() {
|
||||
if (symbol_maps) { free(symbol_maps); symbol_maps = NULL; num_symbol_maps = 0; }
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned int main, bold, italic, bi, num_symbol_fonts;
|
||||
} DescriptorIndices;
|
||||
|
||||
DescriptorIndices descriptor_indices = {0};
|
||||
|
||||
static PyObject*
|
||||
set_font_data(PyObject UNUSED *m, PyObject *args) {
|
||||
PyObject *sm, *smf, *medium, *bold = NULL, *italic = NULL, *bi = NULL;
|
||||
PyObject *sm;
|
||||
Py_CLEAR(box_drawing_function); Py_CLEAR(prerender_function); Py_CLEAR(descriptor_for_idx);
|
||||
if (!PyArg_ParseTuple(args, "OOOIIIIO!d",
|
||||
&box_drawing_function, &prerender_function, &descriptor_for_idx,
|
||||
&descriptor_indices.bold, &descriptor_indices.italic, &descriptor_indices.bi, &descriptor_indices.num_symbol_fonts,
|
||||
&PyTuple_Type, &sm, &global_state.font_sz_in_pts)) return NULL;
|
||||
Py_INCREF(box_drawing_function); Py_INCREF(prerender_function); Py_INCREF(descriptor_for_idx);
|
||||
free_font_groups();
|
||||
Py_CLEAR(box_drawing_function); Py_CLEAR(prerender_function);
|
||||
clear_descriptors();
|
||||
if (!PyArg_ParseTuple(args, "OOO!O!fO|OOO", &box_drawing_function, &prerender_function, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &global_state.font_sz_in_pts, &medium, &bold, &italic, &bi)) return NULL;
|
||||
Py_INCREF(box_drawing_function); Py_INCREF(prerender_function);
|
||||
descriptors = calloc(5 + PyTuple_GET_SIZE(smf), sizeof(Descriptor));
|
||||
if (descriptors == NULL) return PyErr_NoMemory();
|
||||
symbol_maps = calloc(1 + PyTuple_GET_SIZE(sm), sizeof(SymbolMap));
|
||||
clear_symbol_maps();
|
||||
num_symbol_maps = PyTuple_GET_SIZE(sm);
|
||||
symbol_maps = calloc(num_symbol_maps, sizeof(SymbolMap));
|
||||
if (symbol_maps == NULL) return PyErr_NoMemory();
|
||||
size_t desc_idx = 0;
|
||||
#define AF(name) { \
|
||||
if (name) { Py_INCREF(name); descriptors[desc_idx].face = name; descriptors[desc_idx].bold = desc_idx & 1; descriptors[desc_idx].italic = desc_idx & 2; } \
|
||||
desc_idx++; \
|
||||
}
|
||||
AF(medium); AF(bold); AF(italic); AF(bi);
|
||||
#undef AF
|
||||
num_symbol_fonts = 0;
|
||||
for (Py_ssize_t s = 0; s < PyTuple_GET_SIZE(smf); s++, desc_idx++) {
|
||||
Descriptor *d = descriptors + desc_idx;
|
||||
PyObject *face;
|
||||
int bold, italic;
|
||||
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, s), "Opp", &face, &bold, &italic)) return NULL;
|
||||
Py_INCREF(face);
|
||||
d->face = face; d->bold = bold != 0; d->italic = italic != 0;
|
||||
num_symbol_fonts++;
|
||||
}
|
||||
for (Py_ssize_t s = 0; s < PyTuple_GET_SIZE(sm); s++) {
|
||||
for (size_t s = 0; s < num_symbol_maps; s++) {
|
||||
unsigned int left, right, font_idx;
|
||||
SymbolMap *x = symbol_maps + s;
|
||||
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(sm, s), "III", &left, &right, &font_idx)) return NULL;
|
||||
@ -1104,33 +1081,39 @@ send_prerendered_sprites(FontGroup *fg) {
|
||||
Py_CLEAR(args);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
initialize_font(FontGroup *fg, unsigned int desc_idx, const char *ftype) {
|
||||
PyObject *d = PyObject_CallFunction(descriptor_for_idx, "I", desc_idx);
|
||||
if (d == NULL) { PyErr_Print(); fatal("failed for %s font", ftype); }
|
||||
bool bold = PyObject_IsTrue(PyTuple_GET_ITEM(d, 1));
|
||||
bool italic = PyObject_IsTrue(PyTuple_GET_ITEM(d, 2));
|
||||
PyObject *face = desc_to_face(PyTuple_GET_ITEM(d, 0), (FONTS_DATA_HANDLE)fg);
|
||||
Py_CLEAR(d);
|
||||
if (face == NULL) { PyErr_Print(); fatal("failed to convert descriptor to face for %s font", ftype); }
|
||||
size_t idx = fg->fonts_count++;
|
||||
bool ok = init_font(fg->fonts + idx, face, bold, italic, false);
|
||||
Py_CLEAR(face);
|
||||
if (!ok) {
|
||||
if (PyErr_Occurred()) { PyErr_Print(); }
|
||||
fatal("Failed to initialize %s font: %d", ftype, idx);
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
static void
|
||||
initialize_font_group(FontGroup *fg) {
|
||||
if (!descriptors) fatal("Must call set_font_data() before initializing a font group");
|
||||
fg->fonts_capacity = 10 + num_symbol_fonts;
|
||||
fg->fonts_capacity = 10 + descriptor_indices.num_symbol_fonts;
|
||||
fg->fonts = calloc(fg->fonts_capacity, sizeof(Font));
|
||||
if (fg->fonts == NULL) fatal("Out of memory allocating fonts array");
|
||||
fg->fonts_count = 1; // the 0 index font is the box font
|
||||
#define I(ftype) { \
|
||||
size_t idx = fg->fonts_count; \
|
||||
if (!init_font(fg->fonts + fg->fonts_count++, d->face, d->bold, d->italic, false, false, (FONTS_DATA_HANDLE)fg)) { \
|
||||
if (PyErr_Occurred()) PyErr_Print(); \
|
||||
fatal("Failed to initialize %s font: %d", #ftype, idx); \
|
||||
}}
|
||||
#define IF(idx, attr) { \
|
||||
Descriptor *d = descriptors + idx; \
|
||||
if (d->face) { \
|
||||
fg->attr##_font_idx = fg->fonts_count; I(basic); \
|
||||
} else fg->attr##_font_idx = -1; \
|
||||
IF(0, medium); IF(1, bold); IF(2, italic); IF(3, bi); \
|
||||
}
|
||||
#undef IF
|
||||
#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);
|
||||
#undef I
|
||||
fg->first_symbol_font_idx = fg->fonts_count; fg->first_fallback_font_idx = fg->fonts_count;
|
||||
fg->fallback_fonts_count = 0;
|
||||
for (size_t i = 0; i < num_symbol_fonts; i++) {
|
||||
Descriptor *d = descriptors + i + 4;
|
||||
I(symbol_mapped);
|
||||
for (size_t i = 0; i < descriptor_indices.num_symbol_fonts; i++) {
|
||||
initialize_font(fg, descriptor_indices.bi + 1 + i, "symbol_map");
|
||||
fg->first_fallback_font_idx++;
|
||||
}
|
||||
#undef I
|
||||
@ -1144,16 +1127,16 @@ void
|
||||
load_fonts_for_window(OSWindow *w) {
|
||||
w->fonts_data = NULL;
|
||||
FontGroup *fg = font_group_for(w->font_sz_in_pts, w->logical_dpi_x, w->logical_dpi_y);
|
||||
if (!fg->cell_width) initialize_font_group(fg);
|
||||
w->fonts_data = (FONTS_DATA_HANDLE)fg;
|
||||
}
|
||||
|
||||
static void
|
||||
finalize(void) {
|
||||
Py_CLEAR(python_send_to_gpu_impl);
|
||||
clear_descriptors();
|
||||
clear_symbol_maps();
|
||||
Py_CLEAR(box_drawing_function);
|
||||
Py_CLEAR(prerender_function);
|
||||
Py_CLEAR(descriptor_for_idx);
|
||||
free_font_groups();
|
||||
if (harfbuzz_buffer) hb_buffer_destroy(harfbuzz_buffer);
|
||||
free(group_state.groups);
|
||||
@ -1184,9 +1167,11 @@ test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
||||
static PyObject*
|
||||
set_send_sprite_to_gpu(PyObject UNUSED *self, PyObject *func) {
|
||||
Py_CLEAR(python_send_to_gpu_impl);
|
||||
python_send_to_gpu_impl = func;
|
||||
Py_INCREF(func);
|
||||
current_send_sprite_to_gpu = func == Py_None ? send_sprite_to_gpu : python_send_to_gpu;
|
||||
if (func != Py_None) {
|
||||
python_send_to_gpu_impl = func;
|
||||
Py_INCREF(python_send_to_gpu_impl);
|
||||
}
|
||||
current_send_sprite_to_gpu = python_send_to_gpu_impl ? python_send_to_gpu : send_sprite_to_gpu;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -1244,9 +1229,9 @@ current_fonts(PYNOARG) {
|
||||
FontGroup *fg = font_groups;
|
||||
#define SET(key, val) {if (PyDict_SetItemString(ans, #key, fg->fonts[val].face) != 0) { goto error; }}
|
||||
SET(medium, fg->medium_font_idx);
|
||||
if (fg->bold_font_idx) SET(bold, fg->bold_font_idx);
|
||||
if (fg->italic_font_idx) SET(italic, fg->italic_font_idx);
|
||||
if (fg->bi_font_idx) SET(bi, fg->bi_font_idx);
|
||||
if (fg->bold_font_idx > 0) SET(bold, fg->bold_font_idx);
|
||||
if (fg->italic_font_idx > 0) SET(italic, fg->italic_font_idx);
|
||||
if (fg->bi_font_idx > 0) SET(bi, fg->bi_font_idx);
|
||||
PyObject *ff = PyTuple_New(fg->fallback_fonts_count);
|
||||
if (!ff) goto error;
|
||||
for (size_t i = 0; i < fg->fallback_fonts_count; i++) {
|
||||
|
||||
@ -21,31 +21,50 @@ if is_macos:
|
||||
else:
|
||||
from .fontconfig import get_font_files, font_for_family
|
||||
|
||||
current_faces = None
|
||||
|
||||
|
||||
def create_symbol_map(opts):
|
||||
val = opts.symbol_map
|
||||
family_map = {}
|
||||
faces = []
|
||||
count = 0
|
||||
for family in val.values():
|
||||
if family not in family_map:
|
||||
font, bold, italic = font_for_family(family)
|
||||
family_map[family] = len(faces)
|
||||
faces.append((font, bold, italic))
|
||||
family_map[family] = count
|
||||
count += 1
|
||||
current_faces.append((font, bold, italic))
|
||||
sm = tuple((a, b, family_map[f]) for (a, b), f in val.items())
|
||||
return sm, tuple(faces)
|
||||
return sm
|
||||
|
||||
|
||||
def descriptor_for_idx(idx):
|
||||
return current_faces[idx]
|
||||
|
||||
|
||||
def set_font_family(opts=None, override_font_size=None):
|
||||
global current_faces
|
||||
opts = opts or defaults
|
||||
sz = override_font_size or opts.font_size
|
||||
font_map = get_font_files(opts)
|
||||
faces = [font_map['medium']]
|
||||
current_faces = [(font_map['medium'], False, False)]
|
||||
bold_idx = italic_idx = bi_idx = 0
|
||||
for k in 'bold italic bi'.split():
|
||||
if k in font_map:
|
||||
faces.append(font_map[k])
|
||||
sm, sfonts = create_symbol_map(opts)
|
||||
if k == 'bold':
|
||||
bold_idx = len(current_faces)
|
||||
elif k == 'italic':
|
||||
italic_idx = len(current_faces)
|
||||
elif k == 'bi':
|
||||
bi_idx = len(current_faces)
|
||||
current_faces.append((font_map[k], 'b' in k, 'i' in k))
|
||||
before = len(current_faces)
|
||||
sm = create_symbol_map(opts)
|
||||
num_symbol_fonts = len(current_faces) - before
|
||||
set_font_data(
|
||||
render_box_drawing, prerender_function, sm, sfonts, sz, *faces
|
||||
render_box_drawing, prerender_function, descriptor_for_idx,
|
||||
bold_idx, italic_idx, bi_idx, num_symbol_fonts,
|
||||
sm, sz
|
||||
)
|
||||
|
||||
|
||||
@ -141,36 +160,45 @@ def prerender_function(cell_width, cell_height, baseline, underline_position, un
|
||||
def render_box_drawing(codepoint, cell_width, cell_height, dpi):
|
||||
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
|
||||
buf = render_box_char(
|
||||
chr(codepoint), CharTexture(), cell_width, dpi
|
||||
chr(codepoint), CharTexture(), cell_width, cell_height, dpi
|
||||
)
|
||||
return ctypes.addressof(buf), buf
|
||||
|
||||
|
||||
def setup_for_testing(family='monospace', size=11.0, dpi=96.0, send_to_gpu=None):
|
||||
from collections import OrderedDict
|
||||
opts = defaults._replace(font_family=family, font_size=size)
|
||||
set_options(opts)
|
||||
sprites = OrderedDict()
|
||||
class setup_for_testing:
|
||||
|
||||
def _send_to_gpu(x, y, z, data):
|
||||
sprites[(x, y, z)] = data
|
||||
def __init__(self, family='monospace', size=11.0, dpi=96.0):
|
||||
self.family, self.size, self.dpi = family, size, dpi
|
||||
|
||||
sprite_map_set_limits(100000, 100)
|
||||
set_send_sprite_to_gpu(send_to_gpu or _send_to_gpu)
|
||||
set_font_family(opts)
|
||||
cell_width, cell_height = create_test_font_group(size, dpi, dpi)
|
||||
return sprites, cell_width, cell_height
|
||||
def __enter__(self):
|
||||
from collections import OrderedDict
|
||||
opts = defaults._replace(font_family=self.family, font_size=self.size)
|
||||
set_options(opts)
|
||||
sprites = OrderedDict()
|
||||
|
||||
def send_to_gpu(x, y, z, data):
|
||||
sprites[(x, y, z)] = data
|
||||
|
||||
sprite_map_set_limits(100000, 100)
|
||||
set_send_sprite_to_gpu(send_to_gpu)
|
||||
try:
|
||||
set_font_family(opts)
|
||||
cell_width, cell_height = create_test_font_group(self.size, self.dpi, self.dpi)
|
||||
return sprites, cell_width, cell_height
|
||||
except Exception:
|
||||
set_send_sprite_to_gpu(None)
|
||||
raise
|
||||
|
||||
def __exit__(self, *args):
|
||||
set_send_sprite_to_gpu(None)
|
||||
|
||||
|
||||
def render_string(text, family='monospace', size=11.0, dpi=96.0):
|
||||
try:
|
||||
sprites, cell_width, cell_height = setup_for_testing(family, size, dpi)
|
||||
with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):
|
||||
s = Screen(None, 1, len(text)*2)
|
||||
line = s.line(0)
|
||||
s.draw(text)
|
||||
test_render_line(line)
|
||||
finally:
|
||||
set_send_sprite_to_gpu(None)
|
||||
cells = []
|
||||
found_content = False
|
||||
for i in reversed(range(s.columns)):
|
||||
@ -185,14 +213,11 @@ def render_string(text, family='monospace', size=11.0, dpi=96.0):
|
||||
|
||||
|
||||
def shape_string(text="abcd", family='monospace', size=11.0, dpi=96.0, path=None):
|
||||
try:
|
||||
sprites, cell_width, cell_height = setup_for_testing(family, size, dpi)
|
||||
with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):
|
||||
s = Screen(None, 1, len(text)*2)
|
||||
line = s.line(0)
|
||||
s.draw(text)
|
||||
return test_shape(line, path)
|
||||
finally:
|
||||
set_send_sprite_to_gpu(None)
|
||||
|
||||
|
||||
def display_bitmap(rgb_data, width, height):
|
||||
@ -225,14 +250,14 @@ def test_render_string(text='Hello, world!', family='monospace', size=64.0, dpi=
|
||||
|
||||
|
||||
def test_fallback_font(qtext=None, bold=False, italic=False):
|
||||
setup_for_testing()
|
||||
trials = (qtext,) if qtext else ('你', 'He\u0347\u0305', '\U0001F929')
|
||||
for text in trials:
|
||||
f = get_fallback_font(text, bold, italic)
|
||||
try:
|
||||
print(text, f)
|
||||
except UnicodeEncodeError:
|
||||
sys.stdout.buffer.write((text + ' %s\n' % f).encode('utf-8'))
|
||||
with setup_for_testing():
|
||||
trials = (qtext,) if qtext else ('你', 'He\u0347\u0305', '\U0001F929')
|
||||
for text in trials:
|
||||
f = get_fallback_font(text, bold, italic)
|
||||
try:
|
||||
print(text, f)
|
||||
except UnicodeEncodeError:
|
||||
sys.stdout.buffer.write((text + ' %s\n' % f).encode('utf-8'))
|
||||
|
||||
|
||||
def showcase():
|
||||
|
||||
@ -30,17 +30,17 @@ alloc_sprite_map(unsigned int cell_width, unsigned int cell_height) {
|
||||
#ifdef __APPLE__
|
||||
// Since on Apple we could have multiple GPUs, with different capabilities,
|
||||
// upper bound the values according to the data from http://developer.apple.com/graphicsimaging/opengl/capabilities/
|
||||
max_texture_size = MIN(8192, sprite_map.max_texture_size);
|
||||
max_array_texture_layers = MIN(512, sprite_map.max_array_texture_layers);
|
||||
max_texture_size = MIN(8192, max_texture_size);
|
||||
max_array_texture_layers = MIN(512, max_array_texture_layers);
|
||||
#endif
|
||||
sprite_tracker_set_limits(max_texture_size, max_array_texture_layers);
|
||||
}
|
||||
SpriteMap *ans = calloc(1, sizeof(SpriteMap));
|
||||
ans->cell_width = cell_width; ans->cell_height = cell_height;
|
||||
if (ans) {
|
||||
*ans = NEW_SPRITE_MAP;
|
||||
ans->max_texture_size = max_texture_size;
|
||||
ans->max_array_texture_layers = max_array_texture_layers;
|
||||
ans->cell_width = cell_width; ans->cell_height = cell_height;
|
||||
}
|
||||
return (SPRITE_MAP_HANDLE)ans;
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
from kitty.constants import is_macos
|
||||
from kitty.fast_data_types import (
|
||||
DECAWM, set_send_sprite_to_gpu, sprite_map_set_layout,
|
||||
sprite_map_set_limits, test_render_line, test_sprite_position_for, wcwidth
|
||||
DECAWM, sprite_map_set_layout, sprite_map_set_limits, test_render_line,
|
||||
test_sprite_position_for, wcwidth
|
||||
)
|
||||
from kitty.fonts.box_drawing import box_chars
|
||||
from kitty.fonts.render import render_string, setup_for_testing, shape_string
|
||||
@ -16,12 +16,19 @@ from . import BaseTest
|
||||
class Rendering(BaseTest):
|
||||
|
||||
def setUp(self):
|
||||
self.sprites, self.cell_width, self.cell_height = setup_for_testing()
|
||||
self.assertEqual([k[0] for k in self.sprites], [0, 1, 2, 3, 4, 5])
|
||||
self.test_ctx = setup_for_testing()
|
||||
self.test_ctx.__enter__()
|
||||
self.sprites, self.cell_width, self.cell_height = self.test_ctx.__enter__()
|
||||
try:
|
||||
self.assertEqual([k[0] for k in self.sprites], [0, 1, 2, 3, 4, 5])
|
||||
except Exception:
|
||||
self.test_ctx.__exit__()
|
||||
del self.test_ctx
|
||||
raise
|
||||
|
||||
def tearDown(self):
|
||||
set_send_sprite_to_gpu(None)
|
||||
del self.sprites, self.cell_width, self.cell_height
|
||||
self.test_ctx.__exit__()
|
||||
del self.sprites, self.cell_width, self.cell_height, self.test_ctx
|
||||
|
||||
def test_sprite_map(self):
|
||||
sprite_map_set_limits(10, 2)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user