Start work on line based rendering
This commit is contained in:
parent
8431eef970
commit
3643a78b18
@ -11,7 +11,7 @@ from .fast_data_types import (
|
|||||||
GLFW_KEY_DOWN, GLFW_KEY_UP, ChildMonitor, destroy_global_data,
|
GLFW_KEY_DOWN, GLFW_KEY_UP, ChildMonitor, destroy_global_data,
|
||||||
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map
|
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map
|
||||||
)
|
)
|
||||||
from .fonts.render import render_cell_wrapper, set_font_family
|
from .fonts.render import render_cell_wrapper, set_font_family, resize_fonts
|
||||||
from .keys import get_key_map, get_sent_data, get_shortcut
|
from .keys import get_key_map, get_sent_data, get_shortcut
|
||||||
from .session import create_session
|
from .session import create_session
|
||||||
from .tabs import SpecialWindow, TabManager
|
from .tabs import SpecialWindow, TabManager
|
||||||
@ -142,8 +142,7 @@ class Boss:
|
|||||||
self.current_font_size = new_size
|
self.current_font_size = new_size
|
||||||
w, h = cell_size.width, cell_size.height
|
w, h = cell_size.width, cell_size.height
|
||||||
windows = tuple(filter(None, self.window_id_map.values()))
|
windows = tuple(filter(None, self.window_id_map.values()))
|
||||||
cell_size.width, cell_size.height = set_font_family(
|
cell_size.width, cell_size.height = resize_fonts(self.current_font_size)
|
||||||
self.opts, override_font_size=self.current_font_size)
|
|
||||||
layout_sprite_map(cell_size.width, cell_size.height, render_cell_wrapper)
|
layout_sprite_map(cell_size.width, cell_size.height, render_cell_wrapper)
|
||||||
for window in windows:
|
for window in windows:
|
||||||
window.screen.rescale_images(w, h)
|
window.screen.rescale_images(w, h)
|
||||||
|
|||||||
@ -165,8 +165,7 @@ def parse_symbol_map(val):
|
|||||||
return abort()
|
return abort()
|
||||||
if b < a or max(a, b) > sys.maxunicode or min(a, b) < 1:
|
if b < a or max(a, b) > sys.maxunicode or min(a, b) < 1:
|
||||||
return abort()
|
return abort()
|
||||||
for y in range(a, b + 1):
|
symbol_map[(a, b)] = family
|
||||||
symbol_map[chr(y)] = family
|
|
||||||
return symbol_map
|
return symbol_map
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -157,6 +157,7 @@ extern int init_Screen(PyObject *);
|
|||||||
extern int init_Face(PyObject *);
|
extern int init_Face(PyObject *);
|
||||||
extern bool init_freetype_library(PyObject*);
|
extern bool init_freetype_library(PyObject*);
|
||||||
extern bool init_fontconfig_library(PyObject*);
|
extern bool init_fontconfig_library(PyObject*);
|
||||||
|
extern bool init_fonts(PyObject*);
|
||||||
extern bool init_glfw(PyObject *m);
|
extern bool init_glfw(PyObject *m);
|
||||||
extern bool init_sprites(PyObject *module);
|
extern bool init_sprites(PyObject *module);
|
||||||
extern bool init_state(PyObject *module);
|
extern bool init_state(PyObject *module);
|
||||||
@ -201,6 +202,7 @@ PyInit_fast_data_types(void) {
|
|||||||
if (!init_freetype_library(m)) return NULL;
|
if (!init_freetype_library(m)) return NULL;
|
||||||
if (!init_fontconfig_library(m)) return NULL;
|
if (!init_fontconfig_library(m)) return NULL;
|
||||||
#endif
|
#endif
|
||||||
|
if (!init_fonts(m)) return NULL;
|
||||||
|
|
||||||
#define OOF(n) #n, offsetof(Cell, n)
|
#define OOF(n) #n, offsetof(Cell, n)
|
||||||
if (PyModule_AddObject(m, "CELL", Py_BuildValue("{sI sI sI sI sI sI sI sI sI}",
|
if (PyModule_AddObject(m, "CELL", Py_BuildValue("{sI sI sI sI sI sI sI sI sI}",
|
||||||
|
|||||||
@ -45,6 +45,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
|
|||||||
#define DECORATION_MASK 3
|
#define DECORATION_MASK 3
|
||||||
#define BOLD_SHIFT 4
|
#define BOLD_SHIFT 4
|
||||||
#define ITALIC_SHIFT 5
|
#define ITALIC_SHIFT 5
|
||||||
|
#define BI_VAL(attrs) ((attrs >> 4) & 3)
|
||||||
#define REVERSE_SHIFT 6
|
#define REVERSE_SHIFT 6
|
||||||
#define STRIKE_SHIFT 7
|
#define STRIKE_SHIFT 7
|
||||||
#define COL_MASK 0xFFFFFFFF
|
#define COL_MASK 0xFFFFFFFF
|
||||||
@ -73,6 +74,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
|
|||||||
#define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d)
|
#define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d)
|
||||||
|
|
||||||
#define METHOD(name, arg_type) {#name, (PyCFunction)name, arg_type, name##_doc},
|
#define METHOD(name, arg_type) {#name, (PyCFunction)name, arg_type, name##_doc},
|
||||||
|
#define METHODB(name, arg_type) {#name, (PyCFunction)name, arg_type, ""}
|
||||||
|
|
||||||
#define BOOL_GETSET(type, x) \
|
#define BOOL_GETSET(type, x) \
|
||||||
static PyObject* x##_get(type *self, void UNUSED *closure) { PyObject *ans = self->x ? Py_True : Py_False; Py_INCREF(ans); return ans; } \
|
static PyObject* x##_get(type *self, void UNUSED *closure) { PyObject *ans = self->x ? Py_True : Py_False; Py_INCREF(ans); return ans; } \
|
||||||
|
|||||||
216
kitty/fonts.c
Normal file
216
kitty/fonts.c
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* fonts.c
|
||||||
|
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fonts.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
typedef uint16_t glyph_index;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sprite_index x, y, z;
|
||||||
|
} SpriteIndex;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject *face;
|
||||||
|
hb_font_t *hb_font;
|
||||||
|
// Map glyph ids to sprite map co-ords
|
||||||
|
SpriteIndex *sprite_map;
|
||||||
|
bool bold, italic;
|
||||||
|
} Font;
|
||||||
|
|
||||||
|
static Font medium_font = {0}, bold_font = {0}, italic_font = {0}, bi_font = {0}, box_font = {0}, missing_font = {0}, blank_font = {0};
|
||||||
|
static Font fallback_fonts[256] = {{0}};
|
||||||
|
static PyObject *get_fallback_font = NULL;
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
alloc_font(Font *f, PyObject *face, bool bold, bool italic) {
|
||||||
|
f->sprite_map = calloc(1 << (sizeof(glyph_index) * 8), sizeof(SpriteIndex));
|
||||||
|
if (f->sprite_map == NULL) return false;
|
||||||
|
f->face = face; Py_INCREF(face);
|
||||||
|
f->hb_font = harfbuzz_font_for_face(face);
|
||||||
|
f->bold = bold; f->italic = italic;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
clear_font(Font *f) {
|
||||||
|
Py_CLEAR(f->face);
|
||||||
|
free(f->sprite_map); f->sprite_map = NULL;
|
||||||
|
f->hb_font = NULL;
|
||||||
|
f->bold = false; f->italic = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_position = 0, underline_thickness = 0;
|
||||||
|
|
||||||
|
static inline PyObject*
|
||||||
|
update_cell_metrics(float pt_sz, float xdpi, float ydpi) {
|
||||||
|
#define CALL(f) { if ((f)->face && !set_size_for_face((f)->face, pt_sz, xdpi, ydpi)) return NULL; }
|
||||||
|
CALL(&medium_font); CALL(&bold_font); CALL(&italic_font); CALL(&bi_font);
|
||||||
|
for (size_t i = 0; fallback_fonts[i].face != NULL; i++) {
|
||||||
|
CALL(fallback_fonts + i);
|
||||||
|
}
|
||||||
|
#undef CALL
|
||||||
|
cell_metrics(medium_font.face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
|
||||||
|
if (!cell_width) { PyErr_SetString(PyExc_ValueError, "Failed to calculate cell width for the specified font."); return NULL; }
|
||||||
|
if (OPT(adjust_line_height_px) != 0) cell_height += OPT(adjust_line_height_px);
|
||||||
|
if (OPT(adjust_line_height_frac) != 0.f) cell_height *= OPT(adjust_line_height_frac);
|
||||||
|
if (cell_height < 4) { PyErr_SetString(PyExc_ValueError, "line height too small after adjustment"); return NULL; }
|
||||||
|
if (cell_height > 1000) { PyErr_SetString(PyExc_ValueError, "line height too large after adjustment"); return NULL; }
|
||||||
|
underline_position = MIN(cell_height - 1, underline_position);
|
||||||
|
return Py_BuildValue("IIIII", cell_width, cell_height, baseline, underline_position, underline_thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
set_font_size(PyObject UNUSED *m, PyObject *args) {
|
||||||
|
float pt_sz, xdpi, ydpi;
|
||||||
|
if (!PyArg_ParseTuple(args, "fff", &pt_sz, &xdpi, &ydpi)) return NULL;
|
||||||
|
return update_cell_metrics(pt_sz, xdpi, ydpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
has_cell_text(Font *self, Cell *cell) {
|
||||||
|
if (!face_has_codepoint(self->face, cell->ch)) return false;
|
||||||
|
if (cell->cc) {
|
||||||
|
if (!face_has_codepoint(self->face, cell->cc & CC_MASK)) return false;
|
||||||
|
char_type cc = cell->cc >> 16;
|
||||||
|
if (cc && !face_has_codepoint(self->face, cc)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline Font*
|
||||||
|
fallback_font(Cell *cell) {
|
||||||
|
bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
|
||||||
|
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; fallback_fonts[i].face != NULL; i++) {
|
||||||
|
if (fallback_fonts[i].bold == bold && fallback_fonts[i].italic == italic && has_cell_text(fallback_fonts + i, cell)) {
|
||||||
|
return fallback_fonts + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (get_fallback_font == NULL || i == (sizeof(fallback_fonts)/sizeof(fallback_fonts[0])-1)) return &missing_font;
|
||||||
|
Py_UCS4 buf[10];
|
||||||
|
size_t n = cell_as_unicode(cell, true, buf);
|
||||||
|
PyObject *face = PyObject_CallFunction(get_fallback_font, "NOO", PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n), bold ? Py_True : Py_False, italic ? Py_True : Py_False);
|
||||||
|
if (face == NULL) { PyErr_Print(); return &missing_font; }
|
||||||
|
if (face == Py_None) { Py_DECREF(face); return &missing_font; }
|
||||||
|
if (!alloc_font(fallback_fonts + i, face, bold, italic)) { fatal("Out of memory"); }
|
||||||
|
return fallback_fonts + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_type left, right;
|
||||||
|
size_t font_idx;
|
||||||
|
} SymbolMap;
|
||||||
|
static SymbolMap* symbol_maps = NULL;
|
||||||
|
static Font *symbol_map_fonts = NULL;
|
||||||
|
static size_t symbol_maps_count = 0, symbol_map_fonts_count = 0;
|
||||||
|
|
||||||
|
static inline Font*
|
||||||
|
in_symbol_maps(char_type ch) {
|
||||||
|
for (size_t i = 0; i < symbol_maps_count; i++) {
|
||||||
|
if (symbol_maps[i].left <= ch && ch <= symbol_maps[i].right) return symbol_map_fonts + symbol_maps[i].font_idx;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Font*
|
||||||
|
font_for_cell(Cell *cell) {
|
||||||
|
Font *ans;
|
||||||
|
START_ALLOW_CASE_RANGE
|
||||||
|
switch(cell->ch) {
|
||||||
|
case 0:
|
||||||
|
return &blank_font;
|
||||||
|
case 0x2500 ... 0x2570:
|
||||||
|
case 0x2574 ... 0x2577:
|
||||||
|
case 0xe0b0:
|
||||||
|
case 0xe0b2:
|
||||||
|
return &box_font;
|
||||||
|
default:
|
||||||
|
ans = in_symbol_maps(cell->ch);
|
||||||
|
if (ans != NULL) return ans;
|
||||||
|
switch(BI_VAL(cell->attrs)) {
|
||||||
|
case 0:
|
||||||
|
ans = &medium_font;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ans = bold_font.face ? &bold_font : &medium_font;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ans = italic_font.face ? &italic_font : &medium_font;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
ans = bi_font.face ? &bi_font : &medium_font;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (has_cell_text(ans, cell)) return ans;
|
||||||
|
return fallback_font(cell);
|
||||||
|
|
||||||
|
}
|
||||||
|
END_ALLOW_CASE_RANGE
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
set_font(PyObject UNUSED *m, PyObject *args) {
|
||||||
|
PyObject *sm, *smf, *medium, *bold = NULL, *italic = NULL, *bi = NULL;
|
||||||
|
float xdpi, ydpi, pt_sz;
|
||||||
|
if (!PyArg_ParseTuple(args, "OO!O!fffO|OOO", &get_fallback_font, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &pt_sz, &xdpi, &ydpi, &medium, &bold, &italic, &bi)) return NULL;
|
||||||
|
if (!alloc_font(&medium_font, medium, false, false)) return PyErr_NoMemory();
|
||||||
|
if (bold && !alloc_font(&bold_font, bold, false, false)) return PyErr_NoMemory();
|
||||||
|
if (italic && !alloc_font(&italic_font, italic, false, false)) return PyErr_NoMemory();
|
||||||
|
if (bi && !alloc_font(&bi_font, bi, false, false)) return PyErr_NoMemory();
|
||||||
|
|
||||||
|
symbol_maps_count = PyTuple_GET_SIZE(sm);
|
||||||
|
if (symbol_maps_count > 0) {
|
||||||
|
symbol_maps = malloc(symbol_maps_count * sizeof(SymbolMap));
|
||||||
|
symbol_map_fonts_count = PyTuple_GET_SIZE(smf);
|
||||||
|
symbol_map_fonts = calloc(symbol_map_fonts_count, sizeof(Font));
|
||||||
|
if (symbol_maps == NULL || symbol_map_fonts == NULL) return PyErr_NoMemory();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < symbol_map_fonts_count; i++) {
|
||||||
|
PyObject *face;
|
||||||
|
int bold, italic;
|
||||||
|
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, i), "Opp", &face, &bold, &italic)) return NULL;
|
||||||
|
if (!alloc_font(symbol_map_fonts + i, face, bold != 0, italic != 0)) return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < symbol_maps_count; i++) {
|
||||||
|
unsigned int left, right, font_idx;
|
||||||
|
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(sm, i), "III", &left, &right, &font_idx)) return NULL;
|
||||||
|
symbol_maps[i].left = left; symbol_maps[i].right = right; symbol_maps[i].font_idx = font_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return update_cell_metrics(pt_sz, xdpi, ydpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
finalize(void) {
|
||||||
|
Py_CLEAR(get_fallback_font);
|
||||||
|
clear_font(&medium_font); clear_font(&bold_font); clear_font(&italic_font); clear_font(&bi_font);
|
||||||
|
for (size_t i = 0; fallback_fonts[i].face != NULL; i++) clear_font(fallback_fonts + i);
|
||||||
|
for (size_t i = 0; symbol_map_fonts_count; i++) clear_font(symbol_map_fonts + i);
|
||||||
|
free(symbol_maps); free(symbol_map_fonts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
METHODB(set_font_size, METH_VARARGS),
|
||||||
|
METHODB(set_font, METH_VARARGS),
|
||||||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
init_fonts(PyObject *module) {
|
||||||
|
if (Py_AtExit(finalize) != 0) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Failed to register the fonts module at exit handler");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
20
kitty/fonts.h
Normal file
20
kitty/fonts.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lineops.h"
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
#include <hb.h>
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool face_has_codepoint(PyObject *, char_type);
|
||||||
|
hb_font_t* harfbuzz_font_for_face(PyObject*);
|
||||||
|
bool set_size_for_face(PyObject*, float, float, float);
|
||||||
|
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
||||||
@ -3,19 +3,18 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from kitty.fast_data_types import Face, get_fontconfig_font
|
from kitty.fast_data_types import Face, get_fontconfig_font
|
||||||
|
|
||||||
|
|
||||||
def escape_family_name(name):
|
def face_from_font(font, pt_sz, xdpi, ydpi):
|
||||||
return re.sub(r'([-:,\\])', lambda m: '\\' + m.group(1), name)
|
return Face(font.path, font.index, font.hinting, font.hintstyle, pt_sz, xdpi, ydpi)
|
||||||
|
|
||||||
|
|
||||||
Font = namedtuple(
|
Font = namedtuple(
|
||||||
'Font',
|
'Font',
|
||||||
'face hinting hintstyle bold italic scalable outline weight slant index'
|
'path hinting hintstyle bold italic scalable outline weight slant index'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -76,13 +75,21 @@ def find_font_for_characters(
|
|||||||
size_in_pts=size_in_pts,
|
size_in_pts=size_in_pts,
|
||||||
dpi=dpi
|
dpi=dpi
|
||||||
)
|
)
|
||||||
if not ans.face or not os.path.exists(ans.face):
|
if not ans.path or not os.path.exists(ans.path):
|
||||||
raise FontNotFound(
|
raise FontNotFound(
|
||||||
'Failed to find font for characters: {!r}'.format(chars)
|
'Failed to find font for characters: {!r}'.format(chars)
|
||||||
)
|
)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def font_for_text(text, current_font_family, pt_sz, xdpi, ydpi, bold=False, italic=False):
|
||||||
|
dpi = (xdpi + ydpi) / 2
|
||||||
|
try:
|
||||||
|
return find_font_for_characters(current_font_family, text, bold=bold, italic=italic, size_in_pts=pt_sz, dpi=dpi)
|
||||||
|
except FontNotFound:
|
||||||
|
return find_font_for_characters(current_font_family, text, bold=bold, italic=italic, size_in_pts=pt_sz, dpi=dpi, allow_bitmaped_fonts=True)
|
||||||
|
|
||||||
|
|
||||||
def get_font_information(family, bold=False, italic=False):
|
def get_font_information(family, bold=False, italic=False):
|
||||||
return get_font(family, bold, italic)
|
return get_font(family, bold, italic)
|
||||||
|
|
||||||
@ -102,7 +109,7 @@ def get_font_files(opts):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
n = get_font_information(get_family())
|
n = get_font_information(get_family())
|
||||||
ans['regular'] = n._replace(face=Face(n.face, n.index, n.hinting, n.hintstyle))
|
ans['medium'] = n
|
||||||
|
|
||||||
def do(key):
|
def do(key):
|
||||||
b = get_font_information(
|
b = get_font_information(
|
||||||
@ -110,8 +117,8 @@ def get_font_files(opts):
|
|||||||
bold=key in ('bold', 'bi'),
|
bold=key in ('bold', 'bi'),
|
||||||
italic=key in ('italic', 'bi')
|
italic=key in ('italic', 'bi')
|
||||||
)
|
)
|
||||||
if b.face != n.face:
|
if b.path != n.path:
|
||||||
ans[key] = b._replace(face=Face(b.face, b.index, b.hinting, b.hintstyle))
|
ans[key] = b
|
||||||
|
|
||||||
do('bold'), do('italic'), do('bi')
|
do('bold'), do('italic'), do('bi')
|
||||||
return ans
|
return ans
|
||||||
@ -119,4 +126,4 @@ def get_font_files(opts):
|
|||||||
|
|
||||||
def font_for_family(family):
|
def font_for_family(family):
|
||||||
ans = get_font_information(family)
|
ans = get_font_information(family)
|
||||||
return ans._replace(face=Face(ans.face, ans.index, ans.hinting, ans.hintstyle))
|
return ans
|
||||||
|
|||||||
@ -3,14 +3,69 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
|
from collections import namedtuple
|
||||||
from math import sin, pi, ceil, floor, sqrt
|
from math import sin, pi, ceil, floor, sqrt
|
||||||
|
|
||||||
from kitty.constants import isosx
|
from kitty.constants import isosx
|
||||||
|
from kitty.utils import get_logical_dpi
|
||||||
|
from kitty.fast_data_types import set_font, set_font_size
|
||||||
from .box_drawing import render_box_char, is_renderable_box_char
|
from .box_drawing import render_box_char, is_renderable_box_char
|
||||||
if isosx:
|
if isosx:
|
||||||
from .core_text import set_font_family, render_cell as rc, current_cell # noqa
|
pass
|
||||||
else:
|
else:
|
||||||
from .freetype import set_font_family, render_cell as rc, current_cell # noqa
|
from .fontconfig import get_font_files, font_for_text, face_from_font, font_for_family
|
||||||
|
|
||||||
|
|
||||||
|
def create_face(font):
|
||||||
|
s = set_font_family.state
|
||||||
|
return face_from_font(font, s.pt_sz, s.xdpi, s.ydpi)
|
||||||
|
|
||||||
|
|
||||||
|
def create_symbol_map(opts):
|
||||||
|
val = opts.symbol_map
|
||||||
|
family_map = {}
|
||||||
|
faces = []
|
||||||
|
for family in val.values():
|
||||||
|
if family not in family_map:
|
||||||
|
o = create_face(font_for_family(family))
|
||||||
|
family_map[family] = len(faces)
|
||||||
|
faces.append(o)
|
||||||
|
sm = tuple((a, b, family_map[f]) for (a, b), f in val.items())
|
||||||
|
return sm, tuple(faces)
|
||||||
|
|
||||||
|
|
||||||
|
FontState = namedtuple('FontState', 'family pt_sz xdpi ydpi cell_width cell_height baseline underline_position underline_thickness')
|
||||||
|
|
||||||
|
|
||||||
|
def get_fallback_font(text, bold, italic):
|
||||||
|
state = set_font_family.state
|
||||||
|
return create_face(font_for_text(text, state.family, state.pt_sz, state.xdpi, state.ydpi, bold, italic))
|
||||||
|
|
||||||
|
|
||||||
|
def set_font_family(opts, override_font_size=None):
|
||||||
|
if hasattr(set_font_family, 'state'):
|
||||||
|
raise ValueError('Cannot set font family more than once, use resize_fonts() to change size')
|
||||||
|
sz = override_font_size or opts.font_size
|
||||||
|
xdpi, ydpi = get_logical_dpi()
|
||||||
|
set_font_family.state = FontState('', sz, xdpi, ydpi, 0, 0, 0, 0, 0)
|
||||||
|
font_map = get_font_files(opts)
|
||||||
|
faces = [create_face(font_map['medium'])]
|
||||||
|
for k in 'bold italic bi'.split():
|
||||||
|
if k in font_map:
|
||||||
|
faces.append(create_face(font_map[k]))
|
||||||
|
sm, sfaces = create_symbol_map(opts)
|
||||||
|
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font(get_fallback_font, sm, sfaces, sz, xdpi, ydpi, *faces)
|
||||||
|
set_font_family.state = FontState(opts.font_family, sz, xdpi, ydpi, cell_width, cell_height, baseline, underline_position, underline_thickness)
|
||||||
|
return cell_width, cell_height
|
||||||
|
|
||||||
|
|
||||||
|
def resize_fonts(new_sz, xdpi=None, ydpi=None):
|
||||||
|
s = set_font_family.state
|
||||||
|
xdpi = xdpi or s.xdpi
|
||||||
|
ydpi = ydpi or s.ydpi
|
||||||
|
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(new_sz, xdpi, ydpi)
|
||||||
|
set_font_family.state = FontState(
|
||||||
|
s.family, new_sz, xdpi, ydpi, cell_width, cell_height, baseline, underline_position, underline_thickness)
|
||||||
|
|
||||||
|
|
||||||
def add_line(buf, cell_width, position, thickness, cell_height):
|
def add_line(buf, cell_width, position, thickness, cell_height):
|
||||||
|
|||||||
301
kitty/freetype.c
301
kitty/freetype.c
@ -5,14 +5,10 @@
|
|||||||
* Distributed under terms of the GPL3 license.
|
* Distributed under terms of the GPL3 license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "data-types.h"
|
#include "fonts.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
||||||
#include <hb.h>
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#include <hb-ft.h>
|
#include <hb-ft.h>
|
||||||
|
|
||||||
#if HB_VERSION_MAJOR > 1 || (HB_VERSION_MAJOR == 1 && (HB_VERSION_MINOR > 0 || (HB_VERSION_MINOR == 0 && HB_VERSION_MICRO >= 5)))
|
#if HB_VERSION_MAJOR > 1 || (HB_VERSION_MAJOR == 1 && (HB_VERSION_MINOR > 0 || (HB_VERSION_MINOR == 0 && HB_VERSION_MICRO >= 5)))
|
||||||
@ -31,10 +27,10 @@ typedef struct {
|
|||||||
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness;
|
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness;
|
||||||
int hinting, hintstyle;
|
int hinting, hintstyle;
|
||||||
bool is_scalable;
|
bool is_scalable;
|
||||||
|
float size_in_pts;
|
||||||
FT_F26Dot6 char_width, char_height;
|
FT_F26Dot6 char_width, char_height;
|
||||||
FT_UInt xdpi, ydpi;
|
FT_UInt xdpi, ydpi;
|
||||||
PyObject *path;
|
PyObject *path;
|
||||||
hb_buffer_t *harfbuzz_buffer;
|
|
||||||
hb_font_t *harfbuzz_font;
|
hb_font_t *harfbuzz_font;
|
||||||
} Face;
|
} Face;
|
||||||
|
|
||||||
@ -94,6 +90,12 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
|
|||||||
return !error;
|
return !error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
set_size_for_face(PyObject *self, float pt_sz, float xdpi, float ydpi) {
|
||||||
|
FT_UInt w = (FT_UInt)(ceilf(pt_sz * 64));
|
||||||
|
((Face*)self)->size_in_pts = pt_sz;
|
||||||
|
return set_font_size((Face*)self, w, w, (FT_UInt)xdpi, (FT_UInt) ydpi);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||||
@ -101,8 +103,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
char *path;
|
char *path;
|
||||||
int error, hinting, hintstyle;
|
int error, hinting, hintstyle;
|
||||||
long index;
|
long index;
|
||||||
/* unsigned int columns=80, lines=24, scrollback=0; */
|
unsigned int size_in_pts, xdpi, ydpi;
|
||||||
if (!PyArg_ParseTuple(args, "slii", &path, &index, &hinting, &hintstyle)) return NULL;
|
if (!PyArg_ParseTuple(args, "sliifff", &path, &index, &hinting, &hintstyle, &size_in_pts, &xdpi, &ydpi)) return NULL;
|
||||||
|
|
||||||
self = (Face *)type->tp_alloc(type, 0);
|
self = (Face *)type->tp_alloc(type, 0);
|
||||||
if (self != NULL) {
|
if (self != NULL) {
|
||||||
@ -114,10 +116,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
CPY(units_per_EM); CPY(ascender); CPY(descender); CPY(height); CPY(max_advance_width); CPY(max_advance_height); CPY(underline_position); CPY(underline_thickness);
|
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
|
#undef CPY
|
||||||
self->is_scalable = FT_IS_SCALABLE(self->face);
|
self->is_scalable = FT_IS_SCALABLE(self->face);
|
||||||
self->harfbuzz_buffer = hb_buffer_create();
|
|
||||||
self->hinting = hinting; self->hintstyle = hintstyle;
|
self->hinting = hinting; self->hintstyle = hintstyle;
|
||||||
if (self->harfbuzz_buffer == NULL || !hb_buffer_allocation_successful(self->harfbuzz_buffer) || !hb_buffer_pre_allocate(self->harfbuzz_buffer, 20)) { Py_CLEAR(self); return PyErr_NoMemory(); }
|
if (!set_size_for_face((PyObject*)self, size_in_pts, xdpi, ydpi)) { Py_CLEAR(self); return NULL; }
|
||||||
if (!set_font_size(self, 10, 20, 96, 96)) { Py_CLEAR(self); return NULL; }
|
|
||||||
self->harfbuzz_font = hb_ft_font_create(self->face, NULL);
|
self->harfbuzz_font = hb_ft_font_create(self->face, NULL);
|
||||||
if (self->harfbuzz_font == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); }
|
if (self->harfbuzz_font == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); }
|
||||||
}
|
}
|
||||||
@ -126,7 +126,6 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
dealloc(Face* self) {
|
dealloc(Face* self) {
|
||||||
if (self->harfbuzz_buffer) hb_buffer_destroy(self->harfbuzz_buffer);
|
|
||||||
if (self->harfbuzz_font) hb_font_destroy(self->harfbuzz_font);
|
if (self->harfbuzz_font) hb_font_destroy(self->harfbuzz_font);
|
||||||
if (self->face) FT_Done_Face(self->face);
|
if (self->face) FT_Done_Face(self->face);
|
||||||
Py_CLEAR(self->path);
|
Py_CLEAR(self->path);
|
||||||
@ -143,17 +142,6 @@ repr(Face *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
set_char_size(Face *self, PyObject *args) {
|
|
||||||
#define set_char_size_doc "set_char_size(width, height, xdpi, ydpi) -> set the character size. width, height is in 1/64th of a pt. dpi is in pixels per inch"
|
|
||||||
long char_width, char_height;
|
|
||||||
unsigned int xdpi, ydpi;
|
|
||||||
if (!PyArg_ParseTuple(args, "llII", &char_width, &char_height, &xdpi, &ydpi)) return NULL;
|
|
||||||
if (!set_font_size(self, char_width, char_height, xdpi, ydpi)) return NULL;
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
get_load_flags(int hinting, int hintstyle, int base) {
|
get_load_flags(int hinting, int hintstyle, int base) {
|
||||||
int flags = base;
|
int flags = base;
|
||||||
@ -172,253 +160,38 @@ load_glyph(Face *self, int glyph_index) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static inline unsigned int
|
||||||
get_char_index(Face *self, PyObject *args) {
|
|
||||||
#define get_char_index_doc ""
|
|
||||||
int code;
|
|
||||||
unsigned int ans;
|
|
||||||
if (!PyArg_ParseTuple(args, "C", &code)) return NULL;
|
|
||||||
ans = FT_Get_Char_Index(self->face, code);
|
|
||||||
|
|
||||||
return Py_BuildValue("I", ans);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
calc_cell_width(Face *self) {
|
calc_cell_width(Face *self) {
|
||||||
#define calc_cell_width_doc ""
|
unsigned int ans = 0;
|
||||||
unsigned long ans = 0;
|
|
||||||
for (char_type i = 32; i < 128; i++) {
|
for (char_type i = 32; i < 128; i++) {
|
||||||
int glyph_index = FT_Get_Char_Index(self->face, i);
|
int glyph_index = FT_Get_Char_Index(self->face, i);
|
||||||
if (!load_glyph(self, glyph_index)) return NULL;
|
if (load_glyph(self, glyph_index)) {
|
||||||
ans = MAX(ans, (unsigned long)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
ans = MAX(ans, (unsigned long)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
||||||
}
|
}
|
||||||
return PyLong_FromUnsignedLong(ans);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyStructSequence_Field shape_fields[] = {
|
|
||||||
{"glyph_id", NULL},
|
|
||||||
{"cluster", NULL},
|
|
||||||
{"mask", NULL},
|
|
||||||
{"x_offset", NULL},
|
|
||||||
{"y_offset", NULL},
|
|
||||||
{"x_advance", NULL},
|
|
||||||
{"y_advance", NULL},
|
|
||||||
{NULL}
|
|
||||||
};
|
|
||||||
static PyStructSequence_Desc shape_fields_desc = {"Shape", NULL, shape_fields, 7};
|
|
||||||
static PyTypeObject ShapeFieldsType = {{{0}}};
|
|
||||||
|
|
||||||
static inline PyObject*
|
|
||||||
shape_to_py(unsigned int i, hb_glyph_info_t *info, hb_glyph_position_t *pos) {
|
|
||||||
PyObject *ans = PyStructSequence_New(&ShapeFieldsType);
|
|
||||||
if (ans == NULL) return NULL;
|
|
||||||
#define SI(num, src, attr, conv, func, div) PyStructSequence_SET_ITEM(ans, num, func(((conv)src[i].attr) / div)); if (PyStructSequence_GET_ITEM(ans, num) == NULL) { Py_CLEAR(ans); return PyErr_NoMemory(); }
|
|
||||||
#define INFO(num, attr) SI(num, info, attr, unsigned long, PyLong_FromUnsignedLong, 1)
|
|
||||||
#define POS(num, attr) SI(num + 3, pos, attr, double, PyFloat_FromDouble, 64.0)
|
|
||||||
INFO(0, codepoint); INFO(1, cluster); INFO(2, mask);
|
|
||||||
POS(0, x_offset); POS(1, y_offset); POS(2, x_advance); POS(3, y_advance);
|
|
||||||
#undef INFO
|
|
||||||
#undef POS
|
|
||||||
#undef SI
|
|
||||||
return ans;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned int length;
|
|
||||||
hb_glyph_info_t *info;
|
|
||||||
hb_glyph_position_t *positions;
|
|
||||||
} ShapeData;
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
_shape(Face *self, const char *string, int len, ShapeData *ans) {
|
|
||||||
hb_buffer_clear_contents(self->harfbuzz_buffer);
|
|
||||||
#ifdef HARBUZZ_HAS_LOAD_FLAGS
|
|
||||||
hb_ft_font_set_load_flags(self->harfbuzz_font, get_load_flags(self->hinting, self->hintstyle, FT_LOAD_DEFAULT));
|
|
||||||
#endif
|
|
||||||
hb_buffer_add_utf8(self->harfbuzz_buffer, string, len, 0, len);
|
|
||||||
hb_buffer_guess_segment_properties(self->harfbuzz_buffer);
|
|
||||||
hb_shape(self->harfbuzz_font, self->harfbuzz_buffer, NULL, 0);
|
|
||||||
|
|
||||||
unsigned int info_length, positions_length;
|
|
||||||
ans->info = hb_buffer_get_glyph_infos(self->harfbuzz_buffer, &info_length);
|
|
||||||
ans->positions = hb_buffer_get_glyph_positions(self->harfbuzz_buffer, &positions_length);
|
|
||||||
ans->length = MIN(info_length, positions_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
shape(Face *self, PyObject *args) {
|
|
||||||
#define shape_doc "shape(text)"
|
|
||||||
const char *string;
|
|
||||||
int len;
|
|
||||||
if (!PyArg_ParseTuple(args, "s#", &string, &len)) return NULL;
|
|
||||||
|
|
||||||
ShapeData sd;
|
|
||||||
_shape(self, string, len, &sd);
|
|
||||||
PyObject *ans = PyTuple_New(sd.length);
|
|
||||||
if (ans == NULL) return NULL;
|
|
||||||
for (unsigned int i = 0; i < sd.length; i++) {
|
|
||||||
PyObject *s = shape_to_py(i, sd.info, sd.positions);
|
|
||||||
if (s == NULL) { Py_CLEAR(ans); return NULL; }
|
|
||||||
PyTuple_SET_ITEM(ans, i, s);
|
|
||||||
}
|
}
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
static inline int
|
||||||
unsigned char* buf;
|
font_units_to_pixels(Face *self, int x) {
|
||||||
size_t start_x, width, stride;
|
return (int)ceilf(((float)x * (((float)self->size_in_pts * (float)self->ydpi) / (72.f * (float)self->units_per_EM))));
|
||||||
size_t rows;
|
|
||||||
} ProcessedBitmap;
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
trim_borders(ProcessedBitmap *ans, size_t extra) {
|
|
||||||
bool column_has_text = false;
|
|
||||||
|
|
||||||
// Trim empty columns from the right side of the bitmap
|
|
||||||
for (ssize_t x = ans->width - 1; !column_has_text && x > -1 && extra > 0; x--) {
|
|
||||||
for (size_t y = 0; y < ans->rows && !column_has_text; y++) {
|
|
||||||
if (ans->buf[x + y * ans->stride] > 200) column_has_text = true;
|
|
||||||
}
|
|
||||||
if (!column_has_text) { ans->width--; extra--; }
|
|
||||||
}
|
|
||||||
|
|
||||||
ans->start_x = extra;
|
|
||||||
ans->width -= extra;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
static inline bool
|
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
|
||||||
render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int num_cells, int bold, int italic, bool rescale) {
|
Face *self = (Face*)s;
|
||||||
if (!load_glyph(self, glyph_id)) return false;
|
*cell_width = calc_cell_width(self);
|
||||||
unsigned int max_width = cell_width * num_cells;
|
*cell_height = font_units_to_pixels(self, self->height);
|
||||||
FT_Bitmap *bitmap = &self->face->glyph->bitmap;
|
*baseline = font_units_to_pixels(self, self->ascender);
|
||||||
ans->buf = bitmap->buffer;
|
*underline_position = MIN(*cell_height - 1, *baseline - font_units_to_pixels(self, self->underline_position));
|
||||||
ans->start_x = 0; ans->width = bitmap->width;
|
*underline_thickness = font_units_to_pixels(self, self->underline_thickness);
|
||||||
ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;
|
|
||||||
ans->rows = bitmap->rows;
|
|
||||||
if (ans->width > max_width) {
|
|
||||||
size_t extra = bitmap->width - max_width;
|
|
||||||
if (italic && extra < cell_width / 2) {
|
|
||||||
trim_borders(ans, extra);
|
|
||||||
} else if (rescale && self->is_scalable && extra > MAX(2, cell_width / 3)) {
|
|
||||||
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
|
|
||||||
float ar = (float)max_width / (float)bitmap->width;
|
|
||||||
if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi)) {
|
|
||||||
if (!render_bitmap(self, glyph_id, ans, cell_width, num_cells, bold, italic, false)) return false;
|
|
||||||
if (!set_font_size(self, char_width, char_height, self->xdpi, self->ydpi)) return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
bool
|
||||||
place_bitmap_in_cell(unsigned char *cell, ProcessedBitmap *bm, size_t cell_width, size_t cell_height, float x_offset, float y_offset, FT_Glyph_Metrics *metrics, size_t baseline) {
|
face_has_codepoint(PyObject *s, char_type cp) {
|
||||||
// We want the glyph to be positioned inside the cell based on the bearingX
|
return FT_Get_Char_Index(((Face*)s)->face, cp) > 0;
|
||||||
// and bearingY values, making sure that it does not overflow the cell.
|
|
||||||
|
|
||||||
// Calculate column bounds
|
|
||||||
ssize_t xoff = (ssize_t)(x_offset + (float)metrics->horiBearingX / 64.f);
|
|
||||||
size_t src_start_column = bm->start_x, dest_start_column = 0, extra;
|
|
||||||
if (xoff < 0) src_start_column += -xoff;
|
|
||||||
else dest_start_column = xoff;
|
|
||||||
// Move the dest start column back if the width overflows because of it
|
|
||||||
if (dest_start_column > 0 && dest_start_column + bm->width > cell_width) {
|
|
||||||
extra = dest_start_column + bm->width - cell_width;
|
|
||||||
dest_start_column = extra > dest_start_column ? 0 : dest_start_column - extra;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate row bounds
|
|
||||||
ssize_t yoff = (ssize_t)(y_offset + (float)metrics->horiBearingY / 64.f);
|
|
||||||
size_t src_start_row, dest_start_row;
|
|
||||||
if (yoff > 0 && (size_t)yoff > baseline) {
|
|
||||||
src_start_row = 0;
|
|
||||||
dest_start_row = 0;
|
|
||||||
} else {
|
|
||||||
src_start_row = 0;
|
|
||||||
dest_start_row = baseline - yoff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* printf("src_start_row: %zu src_start_column: %zu dest_start_row: %zu dest_start_column: %zu\n", src_start_row, src_start_column, dest_start_row, dest_start_column); */
|
|
||||||
|
|
||||||
for (size_t sr = src_start_row, dr = dest_start_row; sr < bm->rows && dr < cell_height; sr++, dr++) {
|
|
||||||
for(size_t sc = src_start_column, dc = dest_start_column; sc < bm->width && dc < cell_width; sc++, dc++) {
|
|
||||||
uint16_t val = cell[dr * cell_width + dc];
|
|
||||||
val = (val + bm->buf[sr * bm->stride + sc]) % 256;
|
|
||||||
cell[dr * cell_width + dc] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
draw_single_glyph(Face *self, PyObject *args) {
|
|
||||||
#define draw_single_glyph_doc "draw_complex_glyph(codepoint, cell_width, cell_height, cell_buffer, num_cells, bold, italic, baseline)"
|
|
||||||
int bold, italic;
|
|
||||||
unsigned int cell_width, cell_height, num_cells, baseline, codepoint;
|
|
||||||
PyObject *addr;
|
|
||||||
if (!PyArg_ParseTuple(args, "IIIO!IppI", &codepoint, &cell_width, &cell_height, &PyLong_Type, &addr, &num_cells, &bold, &italic, &baseline)) return NULL;
|
|
||||||
unsigned char *cell = PyLong_AsVoidPtr(addr);
|
|
||||||
int glyph_id = FT_Get_Char_Index(self->face, codepoint);
|
|
||||||
ProcessedBitmap bm;
|
|
||||||
if (!render_bitmap(self, glyph_id, &bm, cell_width, num_cells, bold, italic, true)) return NULL;
|
|
||||||
place_bitmap_in_cell(cell, &bm, cell_width * num_cells, cell_height, 0, 0, &self->face->glyph->metrics, baseline);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
draw_complex_glyph(Face *self, PyObject *args) {
|
|
||||||
#define draw_complex_glyph_doc "draw_complex_glyph(text, cell_width, cell_height, cell_buffer, num_cells, bold, italic, baseline)"
|
|
||||||
const char *text;
|
|
||||||
int text_len, bold, italic;
|
|
||||||
unsigned int cell_width, cell_height, num_cells, baseline;
|
|
||||||
PyObject *addr;
|
|
||||||
float x = 0.f, y = 0.f;
|
|
||||||
if (!PyArg_ParseTuple(args, "s#IIO!IppI", &text, &text_len, &cell_width, &cell_height, &PyLong_Type, &addr, &num_cells, &bold, &italic, &baseline)) return NULL;
|
|
||||||
unsigned char *cell = PyLong_AsVoidPtr(addr);
|
|
||||||
ShapeData sd;
|
|
||||||
_shape(self, text, text_len, &sd);
|
|
||||||
ProcessedBitmap bm;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < sd.length; i++) {
|
|
||||||
if (sd.info[i].codepoint == 0) continue;
|
|
||||||
if (!render_bitmap(self, sd.info[i].codepoint, &bm, cell_width, num_cells, bold, italic, true)) return NULL;
|
|
||||||
x += (float)sd.positions[i].x_offset / 64.0f;
|
|
||||||
y = (float)sd.positions[i].y_offset / 64.0f;
|
|
||||||
place_bitmap_in_cell(cell, &bm, cell_width * num_cells, cell_height, x, y, &self->face->glyph->metrics, baseline);
|
|
||||||
x += (float)sd.positions[i].x_advance / 64.0f;
|
|
||||||
|
|
||||||
}
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
split_cells(Face UNUSED *self, PyObject *args) {
|
|
||||||
#define split_cells_doc "split_cells(cell_width, cell_height, src, *cells)"
|
|
||||||
unsigned int cell_width, cell_height;
|
|
||||||
unsigned char *cells[10], *src;
|
|
||||||
size_t num_cells = PyTuple_GET_SIZE(args) - 3;
|
|
||||||
if (num_cells > sizeof(cells)/sizeof(cells[0])) { PyErr_SetString(PyExc_ValueError, "Too many cells being split"); return NULL; }
|
|
||||||
cell_width = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(args, 0));
|
|
||||||
cell_height = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(args, 1));
|
|
||||||
src = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, 2));
|
|
||||||
for (size_t i = 3; i < num_cells + 3; i++) cells[i - 3] = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, i));
|
|
||||||
|
|
||||||
size_t stride = num_cells * cell_width;
|
|
||||||
for (size_t y = 0; y < cell_height; y++) {
|
|
||||||
for (size_t i = 0; i < num_cells; i++) {
|
|
||||||
unsigned char *dest = cells[i] + y * cell_width;
|
|
||||||
for (size_t x = 0; x < cell_width; x++) {
|
|
||||||
dest[x] = src[y * stride + i * cell_width + x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Boilerplate {{{
|
// Boilerplate {{{
|
||||||
|
|
||||||
static PyMemberDef members[] = {
|
static PyMemberDef members[] = {
|
||||||
@ -437,13 +210,6 @@ static PyMemberDef members[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
METHOD(set_char_size, METH_VARARGS)
|
|
||||||
METHOD(shape, METH_VARARGS)
|
|
||||||
METHOD(draw_complex_glyph, METH_VARARGS)
|
|
||||||
METHOD(draw_single_glyph, METH_VARARGS)
|
|
||||||
METHOD(split_cells, METH_VARARGS)
|
|
||||||
METHOD(get_char_index, METH_VARARGS)
|
|
||||||
METHOD(calc_cell_width, METH_NOARGS)
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -482,13 +248,6 @@ init_freetype_library(PyObject *m) {
|
|||||||
PyErr_SetString(FreeType_Exception, "Failed to register the freetype library at exit handler");
|
PyErr_SetString(FreeType_Exception, "Failed to register the freetype library at exit handler");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (PyStructSequence_InitType2(&ShapeFieldsType, &shape_fields_desc) != 0) return false;
|
|
||||||
PyModule_AddObject(m, "ShapeFields", (PyObject*)&ShapeFieldsType);
|
|
||||||
PyModule_AddIntMacro(m, FT_LOAD_RENDER);
|
|
||||||
PyModule_AddIntMacro(m, FT_LOAD_TARGET_NORMAL);
|
|
||||||
PyModule_AddIntMacro(m, FT_LOAD_TARGET_LIGHT);
|
|
||||||
PyModule_AddIntMacro(m, FT_LOAD_NO_HINTING);
|
|
||||||
PyModule_AddIntMacro(m, FT_PIXEL_MODE_GRAY);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
kitty/line.c
27
kitty/line.c
@ -165,6 +165,22 @@ text_at(Line* self, Py_ssize_t xval) {
|
|||||||
return line_text_at(self->cells[xval].ch, self->cells[xval].cc);
|
return line_text_at(self->cells[xval].ch, self->cells[xval].cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf) {
|
||||||
|
size_t n = 1;
|
||||||
|
buf[0] = cell->ch;
|
||||||
|
if (include_cc) {
|
||||||
|
char_type cc = cell->cc;
|
||||||
|
Py_UCS4 cc1 = cc & CC_MASK, cc2;
|
||||||
|
if (cc1) {
|
||||||
|
buf[1] = cc1; n++;
|
||||||
|
cc2 = cc >> 16;
|
||||||
|
if (cc2) { buf[2] = cc2; n++; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char) {
|
unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char) {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
@ -177,16 +193,7 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
|
|||||||
if (previous_width == 2) { previous_width = 0; continue; };
|
if (previous_width == 2) { previous_width = 0; continue; };
|
||||||
ch = ' ';
|
ch = ' ';
|
||||||
}
|
}
|
||||||
buf[n++] = ch;
|
n += cell_as_unicode(self->cells + i, include_cc, buf + n);
|
||||||
if (include_cc) {
|
|
||||||
char_type cc = self->cells[i].cc;
|
|
||||||
Py_UCS4 cc1 = cc & CC_MASK, cc2;
|
|
||||||
if (cc1) {
|
|
||||||
buf[n++] = cc1;
|
|
||||||
cc2 = cc >> 16;
|
|
||||||
if (cc2) buf[n++] = cc2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previous_width = self->cells[i].attrs & WIDTH_MASK;
|
previous_width = self->cells[i].attrs & WIDTH_MASK;
|
||||||
}
|
}
|
||||||
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n);
|
||||||
|
|||||||
@ -67,6 +67,7 @@ index_type line_url_start_at(Line *self, index_type x);
|
|||||||
index_type line_url_end_at(Line *self, index_type x);
|
index_type line_url_end_at(Line *self, index_type x);
|
||||||
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
|
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
|
||||||
unsigned int line_length(Line *self);
|
unsigned int line_length(Line *self);
|
||||||
|
size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf);
|
||||||
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
||||||
|
|
||||||
void linebuf_init_line(LineBuf *, index_type);
|
void linebuf_init_line(LineBuf *, index_type);
|
||||||
|
|||||||
@ -180,6 +180,16 @@ PYWRAP1(set_options) {
|
|||||||
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
||||||
|
|
||||||
Py_DECREF(chars);
|
Py_DECREF(chars);
|
||||||
|
|
||||||
|
PyObject *al = PyObject_GetAttrString(args, "adjust_line_height");
|
||||||
|
if (PyFloat_Check(al)) {
|
||||||
|
OPT(adjust_line_height_frac) = (float)PyFloat_AsDouble(al);
|
||||||
|
OPT(adjust_line_height_px) = 0;
|
||||||
|
} else {
|
||||||
|
OPT(adjust_line_height_frac) = 0;
|
||||||
|
OPT(adjust_line_height_px) = (int)PyLong_AsLong(al);
|
||||||
|
}
|
||||||
|
Py_DECREF(al);
|
||||||
#undef S
|
#undef S
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,8 @@ typedef struct {
|
|||||||
double repaint_delay, input_delay;
|
double repaint_delay, input_delay;
|
||||||
bool focus_follows_mouse;
|
bool focus_follows_mouse;
|
||||||
bool macos_option_as_alt;
|
bool macos_option_as_alt;
|
||||||
|
int adjust_line_height_px;
|
||||||
|
float adjust_line_height_frac;
|
||||||
} Options;
|
} Options;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user