mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-06-27 19:31:46 +00:00
Move decorations drawing code to C form Python
Makes it faster and easier to call from C
This commit is contained in:
parent
9f84c32808
commit
6a169eedd5
7 changed files with 276 additions and 244 deletions
209
kitty/decorations.c
Normal file
209
kitty/decorations.c
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* decorations.c
|
||||
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "decorations.h"
|
||||
#include "state.h"
|
||||
|
||||
#define STRAIGHT_UNDERLINE_LOOP \
|
||||
unsigned half = fcm.underline_thickness / 2; \
|
||||
DecorationGeometry ans = {.top = half > fcm.underline_position ? 0 : fcm.underline_position - half}; \
|
||||
for (unsigned y = ans.top; fcm.underline_thickness > 0 && y < fcm.cell_height; fcm.underline_thickness--, y++, ans.height++)
|
||||
|
||||
DecorationGeometry
|
||||
add_straight_underline(uint8_t *buf, FontCellMetrics fcm) {
|
||||
STRAIGHT_UNDERLINE_LOOP {
|
||||
memset(buf + fcm.cell_width * y, 0xff, fcm.cell_width * sizeof(buf[0]));
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
DecorationGeometry
|
||||
add_strikethrough(uint8_t *buf, FontCellMetrics fcm) {
|
||||
unsigned half = fcm.strikethrough_thickness / 2;
|
||||
DecorationGeometry ans = {.top = half > fcm.strikethrough_position ? 0 : fcm.strikethrough_position - half};
|
||||
for (unsigned y = ans.top; fcm.strikethrough_thickness > 0 && y < fcm.cell_height; fcm.strikethrough_thickness--, y++, ans.height++) {
|
||||
memset(buf + fcm.cell_width * y, 0xff, fcm.cell_width * sizeof(buf[0]));
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
DecorationGeometry
|
||||
add_missing_glyph(uint8_t *buf, FontCellMetrics fcm) {
|
||||
DecorationGeometry ans = {.height=fcm.cell_height};
|
||||
unsigned thickness = MIN(fcm.underline_thickness, fcm.strikethrough_thickness);
|
||||
thickness = MIN(thickness, fcm.cell_width);
|
||||
for (unsigned y = 0; y < ans.height; y++) {
|
||||
uint8_t *line = buf + fcm.cell_width * y;
|
||||
if (y < thickness || y >= ans.height - thickness) memset(line, 0xff, fcm.cell_width);
|
||||
else {
|
||||
memset(line, 0xff, thickness);
|
||||
memset(line + fcm.cell_width - thickness, 0xff, thickness);
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
DecorationGeometry
|
||||
add_double_underline(uint8_t *buf, FontCellMetrics fcm) {
|
||||
unsigned a = fcm.underline_position > fcm.underline_thickness ? fcm.underline_position - fcm.underline_thickness : 0;
|
||||
a = MIN(a, fcm.cell_height - 1);
|
||||
unsigned b = MIN(fcm.underline_position, fcm.cell_height - 1);
|
||||
unsigned top = MIN(a, b), bottom = MAX(a, b);
|
||||
int deficit = 2 - (bottom - top);
|
||||
if (deficit > 0) {
|
||||
if (bottom + deficit < fcm.cell_height) bottom += deficit;
|
||||
else if (bottom < fcm.cell_height - 1) {
|
||||
bottom += 1;
|
||||
if (deficit > 1) top -= deficit - 1;
|
||||
} else top -= deficit;
|
||||
}
|
||||
top = MAX(0, MIN(top, fcm.cell_height - 1));
|
||||
bottom = MAX(0, MIN(bottom, fcm.cell_height - 1));
|
||||
memset(buf + fcm.cell_width * top, 0xff, fcm.cell_width);
|
||||
memset(buf + fcm.cell_width * bottom, 0xff, fcm.cell_width);
|
||||
DecorationGeometry ans = {.top=top, .height = bottom + 1 - top};
|
||||
return ans;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
distribute_dots(unsigned available_space, unsigned num_of_dots, unsigned *summed_gaps, unsigned *gaps) {
|
||||
unsigned dot_size = MAX(1, available_space / (2 * num_of_dots));
|
||||
unsigned extra = 2 * num_of_dots * dot_size;
|
||||
extra = available_space > extra ? available_space - extra : 0;
|
||||
for (unsigned i = 0; i < num_of_dots; i++) gaps[i] = dot_size;
|
||||
if (extra > 0) {
|
||||
unsigned idx = 0;
|
||||
while (extra > 0) {
|
||||
gaps[idx] += 1;
|
||||
idx = (idx + 1) % num_of_dots;
|
||||
extra--;
|
||||
}
|
||||
}
|
||||
gaps[0] /= 2;
|
||||
for (unsigned i = 0; i < num_of_dots; i++) {
|
||||
summed_gaps[i] = 0;
|
||||
for (unsigned g = 0; g <= i; g++) summed_gaps[i] += gaps[g];
|
||||
}
|
||||
return dot_size;
|
||||
}
|
||||
|
||||
DecorationGeometry
|
||||
add_dotted_underline(uint8_t *buf, FontCellMetrics fcm) {
|
||||
unsigned num_of_dots = fcm.cell_width / (2 * fcm.underline_thickness);
|
||||
RAII_ALLOC(unsigned, spacing, malloc(num_of_dots * 2 * sizeof(unsigned)));
|
||||
if (!spacing) fatal("Out of memory");
|
||||
unsigned size = distribute_dots(fcm.cell_width, num_of_dots, spacing, spacing + num_of_dots);
|
||||
STRAIGHT_UNDERLINE_LOOP {
|
||||
uint8_t *offset = buf + fcm.cell_width * y;
|
||||
for (unsigned j = 0; j < num_of_dots; j++) {
|
||||
unsigned s = spacing[j];
|
||||
memset(offset + j * size + s, 0xff, size);
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
DecorationGeometry
|
||||
add_dashed_underline(uint8_t *buf, FontCellMetrics fcm) {
|
||||
unsigned quarter_width = fcm.cell_width / 4;
|
||||
unsigned dash_width = fcm.cell_width - 3 * quarter_width;
|
||||
unsigned second_dash_start = 3 * quarter_width;
|
||||
STRAIGHT_UNDERLINE_LOOP {
|
||||
uint8_t *offset = buf + fcm.cell_width * y;
|
||||
memset(offset, 0xff, dash_width);
|
||||
memset(offset + second_dash_start, 0xff, dash_width);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static void
|
||||
add_intensity(uint8_t *buf, unsigned x, unsigned y, uint8_t val, unsigned max_y, unsigned position, unsigned cell_width) {
|
||||
y += position;
|
||||
y = MIN(y, max_y);
|
||||
unsigned idx = cell_width * y + x;
|
||||
buf[idx] = MIN(255, buf[idx] + val);
|
||||
}
|
||||
|
||||
DecorationGeometry
|
||||
add_curl_underline(uint8_t *buf, FontCellMetrics fcm) {
|
||||
unsigned max_x = fcm.cell_width - 1, max_y = fcm.cell_height - 1;
|
||||
double xfactor = ((OPT(undercurl_style) & 1) ? 4.0 : 2.0) * M_PI / max_x;
|
||||
unsigned half_thickness = fcm.underline_thickness / 2;
|
||||
unsigned top = fcm.underline_position > half_thickness ? fcm.underline_position - half_thickness : 0;
|
||||
unsigned max_height = fcm.cell_height - top; // descender from the font
|
||||
unsigned half_height = MAX(1, max_height / 4);
|
||||
unsigned thickness;
|
||||
if (OPT(undercurl_style) & 2) thickness = MAX(half_height, fcm.underline_thickness);
|
||||
else thickness = MAX(1, fcm.underline_thickness) - (fcm.underline_thickness < 3 ? 1 : 2);
|
||||
unsigned position = fcm.underline_position;
|
||||
|
||||
// Ensure curve doesn't exceed cell boundary at the bottom
|
||||
position += half_height * 2;
|
||||
if (position + half_height > max_y) position = max_y - half_height;
|
||||
|
||||
unsigned miny = fcm.cell_height, maxy = 0;
|
||||
// Use the Wu antialias algorithm to draw the curve
|
||||
// cosine waves always have slope <= 1 so are never steep
|
||||
for (unsigned x = 0; x < fcm.cell_width; x++) {
|
||||
double y = half_height * cos(x * xfactor);
|
||||
unsigned y1 = (unsigned)floor(y - thickness), y2 = (unsigned)ceil(y);
|
||||
unsigned i1 = (unsigned)(255. * fabs(y - floor(y)));
|
||||
miny = MIN(miny, y1); maxy = MAX(maxy, y2);
|
||||
add_intensity(buf, x, y1, 255 - i1, max_y, position, fcm.cell_width); // upper bound
|
||||
add_intensity(buf, x, y2, i1, max_y, position, fcm.cell_width); // lower bound
|
||||
// fill between upper and lower bound
|
||||
for (unsigned t = 1; t <= thickness; t++) add_intensity(buf, x, y1 + t, 255, max_y, position, fcm.cell_width);
|
||||
}
|
||||
DecorationGeometry ans = {.top=miny, .height=maxy-miny + 1};
|
||||
return ans;
|
||||
}
|
||||
|
||||
static void
|
||||
vert(uint8_t *ans, bool is_left_edge, double width_pt, double dpi_x, FontCellMetrics fcm) {
|
||||
unsigned width = MAX(1, MIN((unsigned)(round(width_pt * dpi_x / 72.0)), fcm.cell_width));
|
||||
const unsigned left = is_left_edge ? 0 : MAX(0, fcm.cell_width - width);
|
||||
for (unsigned y = 0; y < fcm.cell_height; y++) {
|
||||
const unsigned offset = y * fcm.cell_width + left;
|
||||
for (unsigned x = offset; x < offset + width; x++) ans[x] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned
|
||||
horz(uint8_t *ans, bool is_top_edge, double height_pt, double dpi_y, FontCellMetrics fcm) {
|
||||
unsigned height = MAX(1, MIN((unsigned)(round(height_pt * dpi_y / 72.0)), fcm.cell_height));
|
||||
const unsigned top = is_top_edge ? 0 : MAX(0, fcm.cell_height - height);
|
||||
for (unsigned y = top; y < top + height; y++) {
|
||||
const unsigned offset = y * fcm.cell_width;
|
||||
for (unsigned x = 0; x < fcm.cell_width; x++) ans[offset + x] = 0xff;
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
|
||||
DecorationGeometry
|
||||
add_beam_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x) {
|
||||
vert(buf, true, OPT(cursor_beam_thickness), dpi_x, fcm);
|
||||
DecorationGeometry ans = {.height=fcm.cell_height};
|
||||
return ans;
|
||||
}
|
||||
|
||||
DecorationGeometry
|
||||
add_underline_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_y) {
|
||||
DecorationGeometry ans = {0};
|
||||
ans.top = horz(buf, true, OPT(cursor_underline_thickness), dpi_y, fcm);
|
||||
ans.height = fcm.cell_height - ans.top;
|
||||
return ans;
|
||||
}
|
||||
|
||||
DecorationGeometry
|
||||
add_hollow_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x, double dpi_y) {
|
||||
vert(buf, true, 1.0, dpi_x, fcm); vert(buf, false, 1.0, dpi_x, fcm);
|
||||
horz(buf, true, 1.0, dpi_y, fcm); horz(buf, false, 1.0, dpi_y, fcm);
|
||||
DecorationGeometry ans = {.height=fcm.cell_height};
|
||||
return ans;
|
||||
}
|
||||
25
kitty/decorations.h
Normal file
25
kitty/decorations.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* decorations.h
|
||||
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data-types.h"
|
||||
|
||||
typedef struct DecorationGeometry {
|
||||
uint32_t top, height;
|
||||
} DecorationGeometry;
|
||||
|
||||
DecorationGeometry add_straight_underline(uint8_t *buf, FontCellMetrics fcm);
|
||||
DecorationGeometry add_double_underline(uint8_t *buf, FontCellMetrics fcm);
|
||||
DecorationGeometry add_dotted_underline(uint8_t *buf, FontCellMetrics fcm);
|
||||
DecorationGeometry add_dashed_underline(uint8_t *buf, FontCellMetrics fcm);
|
||||
DecorationGeometry add_curl_underline(uint8_t *buf, FontCellMetrics fcm);
|
||||
DecorationGeometry add_strikethrough(uint8_t *buf, FontCellMetrics fcm);
|
||||
DecorationGeometry add_missing_glyph(uint8_t *buf, FontCellMetrics fcm);
|
||||
DecorationGeometry add_beam_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x);
|
||||
DecorationGeometry add_underline_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_y);
|
||||
DecorationGeometry add_hollow_cursor(uint8_t *buf, FontCellMetrics fcm, double dpi_x, double dpi_y);
|
||||
|
|
@ -1132,11 +1132,7 @@ def set_send_sprite_to_gpu(
|
|||
|
||||
|
||||
def set_font_data(
|
||||
box_drawing_func: Callable[[int, int, int, float],
|
||||
Tuple[int, Union[bytearray, bytes, Array[c_ubyte]]]],
|
||||
prerender_func: Callable[
|
||||
[int, int, int, int, int, int, int, float, float, float, float],
|
||||
Tuple[Tuple[int, ...], Tuple[Array[c_ubyte], ...]]],
|
||||
box_drawing_func: Callable[[int, int, int, float], Tuple[int, Union[bytearray, bytes, Array[c_ubyte]]]],
|
||||
descriptor_for_idx: Callable[[int], Tuple[Union[FontObject|str], bool, bool]],
|
||||
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,
|
||||
symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "state.h"
|
||||
#include "emoji.h"
|
||||
#include "unicode-data.h"
|
||||
#include "decorations.h"
|
||||
#include "glyph-cache.h"
|
||||
|
||||
#define MISSING_GLYPH (NUM_UNDERLINE_STYLES + 2)
|
||||
|
|
@ -697,10 +698,10 @@ START_ALLOW_CASE_RANGE
|
|||
END_ALLOW_CASE_RANGE
|
||||
}
|
||||
|
||||
static PyObject* box_drawing_function = NULL, *prerender_function = NULL, *descriptor_for_idx = NULL;
|
||||
static PyObject* box_drawing_function = NULL, *descriptor_for_idx = NULL;
|
||||
|
||||
void
|
||||
render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride, pixel color_rgb) {
|
||||
render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, const Region *src_rect, const Region *dest_rect, size_t src_stride, size_t dest_stride, pixel color_rgb) {
|
||||
pixel col = color_rgb << 8;
|
||||
for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {
|
||||
pixel *d = dest + dest_stride * dr;
|
||||
|
|
@ -1744,12 +1745,12 @@ set_symbol_maps(SymbolMap **maps, size_t *num, const PyObject *sm) {
|
|||
static PyObject*
|
||||
set_font_data(PyObject UNUSED *m, PyObject *args) {
|
||||
PyObject *sm, *ns;
|
||||
Py_CLEAR(box_drawing_function); Py_CLEAR(prerender_function); Py_CLEAR(descriptor_for_idx);
|
||||
if (!PyArg_ParseTuple(args, "OOOIIIIO!dO!",
|
||||
&box_drawing_function, &prerender_function, &descriptor_for_idx,
|
||||
Py_CLEAR(box_drawing_function); Py_CLEAR(descriptor_for_idx);
|
||||
if (!PyArg_ParseTuple(args, "OOIIIIO!dO!",
|
||||
&box_drawing_function, &descriptor_for_idx,
|
||||
&descriptor_indices.bold, &descriptor_indices.italic, &descriptor_indices.bi, &descriptor_indices.num_symbol_fonts,
|
||||
&PyTuple_Type, &sm, &OPT(font_size), &PyTuple_Type, &ns)) return NULL;
|
||||
Py_INCREF(box_drawing_function); Py_INCREF(prerender_function); Py_INCREF(descriptor_for_idx);
|
||||
Py_INCREF(box_drawing_function); Py_INCREF(descriptor_for_idx);
|
||||
free_font_groups();
|
||||
clear_symbol_maps();
|
||||
set_symbol_maps(&symbol_maps, &num_symbol_maps, sm);
|
||||
|
|
@ -1763,22 +1764,37 @@ send_prerendered_sprites(FontGroup *fg) {
|
|||
// blank cell
|
||||
ensure_canvas_can_fit(fg, 1, 1);
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, 0, fg->canvas.buf);
|
||||
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
|
||||
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->fcm.cell_width, fg->fcm.cell_height, fg->fcm.baseline, fg->fcm.underline_position, fg->fcm.underline_thickness, fg->fcm.strikethrough_position, fg->fcm.strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y);
|
||||
if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); }
|
||||
PyObject *cell_addresses = PyTuple_GET_ITEM(args, 0);
|
||||
for (ssize_t i = 0; i < PyTuple_GET_SIZE(cell_addresses); i++) {
|
||||
do_increment(fg, &error);
|
||||
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
|
||||
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(cell_addresses, i));
|
||||
ensure_canvas_can_fit(fg, 1, 1); // clear canvas
|
||||
Region r = { .right = fg->fcm.cell_width, .bottom = fg->fcm.cell_height };
|
||||
render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->fcm.cell_width, fg->fcm.cell_width, 0xffffff);
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, current_sprite_index(&fg->sprite_tracker), fg->canvas.buf);
|
||||
}
|
||||
do_increment(fg, &error);
|
||||
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
|
||||
Py_CLEAR(args);
|
||||
const unsigned cell_area = fg->fcm.cell_height * fg->fcm.cell_width;
|
||||
RAII_ALLOC(uint8_t, alpha_mask, malloc(cell_area));
|
||||
if (!alpha_mask) fatal("Out of memory");
|
||||
Region r = { .right = fg->fcm.cell_width, .bottom = fg->fcm.cell_height };
|
||||
#define increment_sprite_index do_increment(fg, &error); if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
|
||||
#define do_one(call) \
|
||||
memset(alpha_mask, 0, cell_area); \
|
||||
call; \
|
||||
ensure_canvas_can_fit(fg, 1, 1); /* clear canvas */ \
|
||||
render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->fcm.cell_width, fg->fcm.cell_width, 0xffffff); \
|
||||
increment_sprite_index; \
|
||||
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, current_sprite_index(&fg->sprite_tracker), fg->canvas.buf);
|
||||
|
||||
// If you change the mapping of these cells you will need to change
|
||||
// NUM_UNDERLINE_STYLES and BEAM_IDX in shader.c and STRIKE_SPRITE_INDEX in
|
||||
// window.py and MISSING_GLYPH in font.c
|
||||
do_one(add_straight_underline(alpha_mask, fg->fcm));
|
||||
do_one(add_double_underline(alpha_mask, fg->fcm));
|
||||
do_one(add_curl_underline(alpha_mask, fg->fcm));
|
||||
do_one(add_dotted_underline(alpha_mask, fg->fcm));
|
||||
do_one(add_dashed_underline(alpha_mask, fg->fcm));
|
||||
do_one(add_strikethrough(alpha_mask, fg->fcm));
|
||||
do_one(add_missing_glyph(alpha_mask, fg->fcm));
|
||||
do_one(add_beam_cursor(alpha_mask, fg->fcm, fg->logical_dpi_x));
|
||||
do_one(add_underline_cursor(alpha_mask, fg->fcm, fg->logical_dpi_y));
|
||||
do_one(add_hollow_cursor(alpha_mask, fg->fcm, fg->logical_dpi_x, fg->logical_dpi_y));
|
||||
|
||||
increment_sprite_index;
|
||||
|
||||
#undef increment_sprite_index
|
||||
#undef do_one
|
||||
}
|
||||
|
||||
static size_t
|
||||
|
|
@ -1854,7 +1870,6 @@ finalize(void) {
|
|||
Py_CLEAR(python_send_to_gpu_impl);
|
||||
clear_symbol_maps();
|
||||
Py_CLEAR(box_drawing_function);
|
||||
Py_CLEAR(prerender_function);
|
||||
Py_CLEAR(descriptor_for_idx);
|
||||
free_font_groups();
|
||||
free(ligature_types);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ bool face_equals_descriptor(PyObject *face_, PyObject *descriptor);
|
|||
const char* postscript_name_for_face(const PyObject*);
|
||||
|
||||
void sprite_tracker_current_layout(FONTS_DATA_HANDLE data, unsigned int *x, unsigned int *y, unsigned int *z);
|
||||
void render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride, pixel color_rgb);
|
||||
void render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, const Region *src_rect, const Region *dest_rect, size_t src_stride, size_t dest_stride, pixel color_rgb);
|
||||
void render_line(FONTS_DATA_HANDLE, Line *line, index_type lnum, Cursor *cursor, DisableLigature, ListOfChars*);
|
||||
void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);
|
||||
typedef void (*free_extra_data_func)(void*);
|
||||
|
|
|
|||
|
|
@ -1434,10 +1434,6 @@ def render_box_char(ch: str, buf: BufType, width: int, height: int, dpi: float =
|
|||
return buf
|
||||
|
||||
|
||||
def render_missing_glyph(buf: BufType, width: int, height: int) -> None:
|
||||
frame(buf, width, height)
|
||||
|
||||
|
||||
def test_char(ch: str, sz: int = 48) -> None:
|
||||
# kitty +runpy "from kitty.fonts.box_drawing import test_char; test_char('XXX')"
|
||||
from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
|
||||
|
|
|
|||
|
|
@ -5,18 +5,14 @@ import ctypes
|
|||
import os
|
||||
import sys
|
||||
from collections.abc import Generator
|
||||
from functools import partial
|
||||
from math import ceil, cos, floor, pi
|
||||
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union, cast
|
||||
|
||||
from kitty.constants import fonts_dir, is_macos
|
||||
from kitty.fast_data_types import (
|
||||
NUM_UNDERLINE_STYLES,
|
||||
Screen,
|
||||
create_test_font_group,
|
||||
current_fonts,
|
||||
get_fallback_font,
|
||||
get_options,
|
||||
set_builtin_nerd_font,
|
||||
set_font_data,
|
||||
set_options,
|
||||
|
|
@ -26,7 +22,7 @@ from kitty.fast_data_types import (
|
|||
test_render_line,
|
||||
test_shape,
|
||||
)
|
||||
from kitty.fonts.box_drawing import BufType, distribute_dots, render_box_char, render_missing_glyph
|
||||
from kitty.fonts.box_drawing import BufType, render_box_char
|
||||
from kitty.options.types import Options, defaults
|
||||
from kitty.options.utils import parse_font_spec
|
||||
from kitty.types import _T
|
||||
|
|
@ -209,7 +205,7 @@ def set_font_family(opts: Optional[Options] = None, override_font_size: Optional
|
|||
ns = create_narrow_symbols(opts)
|
||||
num_symbol_fonts = len(current_faces) - before
|
||||
set_font_data(
|
||||
render_box_drawing, prerender_function, descriptor_for_idx,
|
||||
render_box_drawing, descriptor_for_idx,
|
||||
indices['bold'], indices['italic'], indices['bi'], num_symbol_fonts,
|
||||
sm, sz, ns
|
||||
)
|
||||
|
|
@ -222,211 +218,6 @@ else:
|
|||
UnderlineCallback = Callable[[CBufType, int, int, int, int], None]
|
||||
|
||||
|
||||
def add_line(buf: CBufType, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
|
||||
y = position - thickness // 2
|
||||
while thickness > 0 and -1 < y < cell_height:
|
||||
thickness -= 1
|
||||
ctypes.memset(ctypes.addressof(buf) + (cell_width * y), 255, cell_width)
|
||||
y += 1
|
||||
|
||||
|
||||
def add_dline(buf: CBufType, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
|
||||
a = min(position - thickness, cell_height - 1)
|
||||
b = min(position, cell_height - 1)
|
||||
top, bottom = min(a, b), max(a, b)
|
||||
deficit = 2 - (bottom - top)
|
||||
if deficit > 0:
|
||||
if bottom + deficit < cell_height:
|
||||
bottom += deficit
|
||||
elif bottom < cell_height - 1:
|
||||
bottom += 1
|
||||
if deficit > 1:
|
||||
top -= deficit - 1
|
||||
else:
|
||||
top -= deficit
|
||||
top = max(0, min(top, cell_height - 1))
|
||||
bottom = max(0, min(bottom, cell_height - 1))
|
||||
for y in {top, bottom}:
|
||||
ctypes.memset(ctypes.addressof(buf) + (cell_width * y), 255, cell_width)
|
||||
|
||||
|
||||
def add_curl(buf: CBufType, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
|
||||
max_x, max_y = cell_width - 1, cell_height - 1
|
||||
opts = get_options()
|
||||
xfactor = (4.0 if 'dense' in opts.undercurl_style else 2.0) * pi / max_x
|
||||
|
||||
max_height = cell_height - (position - thickness // 2) # descender from the font
|
||||
half_height = max(1, max_height // 4)
|
||||
if 'thick' in opts.undercurl_style:
|
||||
thickness = max(half_height, thickness)
|
||||
else:
|
||||
thickness = max(1, thickness) - (1 if thickness < 3 else 2)
|
||||
|
||||
def add_intensity(x: int, y: int, val: int) -> None:
|
||||
y += position
|
||||
y = min(y, max_y)
|
||||
idx = cell_width * y + x
|
||||
buf[idx] = min(255, buf[idx] + val)
|
||||
|
||||
# Ensure curve doesn't exceed cell boundary at the bottom
|
||||
position += half_height * 2
|
||||
if position + half_height > max_y:
|
||||
position = max_y - half_height
|
||||
|
||||
# Use the Wu antialias algorithm to draw the curve
|
||||
# cosine waves always have slope <= 1 so are never steep
|
||||
for x in range(cell_width):
|
||||
y = half_height * cos(x * xfactor)
|
||||
y1, y2 = floor(y - thickness), ceil(y)
|
||||
i1 = int(255 * abs(y - floor(y)))
|
||||
add_intensity(x, y1, 255 - i1) # upper bound
|
||||
add_intensity(x, y2, i1) # lower bound
|
||||
# fill between upper and lower bound
|
||||
for t in range(1, thickness + 1):
|
||||
add_intensity(x, y1 + t, 255)
|
||||
|
||||
|
||||
def add_dots(buf: CBufType, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
|
||||
spacing, size = distribute_dots(cell_width, cell_width // (2 * thickness))
|
||||
y = position - thickness // 2
|
||||
buf_addr = ctypes.addressof(buf)
|
||||
while thickness > 0 and -1 < y < cell_height:
|
||||
offset = buf_addr + cell_width * y
|
||||
for j, s in enumerate(spacing):
|
||||
ctypes.memset(offset + j * size + s, 255, size)
|
||||
thickness -= 1
|
||||
y += 1
|
||||
|
||||
|
||||
def add_dashes(buf: CBufType, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
|
||||
halfspace_width = cell_width // 4
|
||||
y = position - thickness // 2
|
||||
dash_width = cell_width - 3 * halfspace_width
|
||||
second_dash_start = 3 * halfspace_width
|
||||
buf_addr = ctypes.addressof(buf)
|
||||
while thickness > 0 and -1 < y < cell_height:
|
||||
offset = buf_addr + cell_width * y
|
||||
ctypes.memset(offset, 255, dash_width)
|
||||
ctypes.memset(offset + second_dash_start, 255, dash_width)
|
||||
thickness -= 1
|
||||
y += 1
|
||||
|
||||
|
||||
def render_special(
|
||||
underline: int = 0,
|
||||
strikethrough: bool = False,
|
||||
missing: bool = False,
|
||||
cell_width: int = 0, cell_height: int = 0,
|
||||
baseline: int = 0,
|
||||
underline_position: int = 0,
|
||||
underline_thickness: int = 0,
|
||||
strikethrough_position: int = 0,
|
||||
strikethrough_thickness: int = 0,
|
||||
dpi_x: float = 96.,
|
||||
dpi_y: float = 96.,
|
||||
) -> CBufType:
|
||||
underline_position = min(underline_position, cell_height - sum(divmod(underline_thickness, 2)))
|
||||
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
|
||||
|
||||
if missing:
|
||||
buf = bytearray(cell_width * cell_height)
|
||||
render_missing_glyph(buf, cell_width, cell_height)
|
||||
return CharTexture.from_buffer(buf)
|
||||
|
||||
ans = CharTexture()
|
||||
|
||||
def dl(f: UnderlineCallback, *a: Any) -> None:
|
||||
try:
|
||||
f(ans, cell_width, *a)
|
||||
except Exception as e:
|
||||
log_error(f'Failed to render {f.__name__} at cell_width={cell_width} and cell_height={cell_height} with error: {e}')
|
||||
|
||||
if underline:
|
||||
t = underline_thickness
|
||||
if underline > 1:
|
||||
t = max(1, min(cell_height - underline_position - 1, t))
|
||||
dl([add_line, add_line, add_dline, add_curl, add_dots, add_dashes][underline], underline_position, t, cell_height)
|
||||
if strikethrough:
|
||||
dl(add_line, strikethrough_position, strikethrough_thickness, cell_height)
|
||||
|
||||
return ans
|
||||
|
||||
|
||||
def render_cursor(
|
||||
which: int,
|
||||
cursor_beam_thickness: float,
|
||||
cursor_underline_thickness: float,
|
||||
cell_width: int = 0,
|
||||
cell_height: int = 0,
|
||||
dpi_x: float = 0,
|
||||
dpi_y: float = 0
|
||||
) -> CBufType:
|
||||
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
|
||||
ans = CharTexture()
|
||||
|
||||
def vert(edge: str, width_pt: float = 1) -> None:
|
||||
width = max(1, min(int(round(width_pt * dpi_x / 72.0)), cell_width))
|
||||
left = 0 if edge == 'left' else max(0, cell_width - width)
|
||||
for y in range(cell_height):
|
||||
offset = y * cell_width + left
|
||||
for x in range(offset, offset + width):
|
||||
ans[x] = 255
|
||||
|
||||
def horz(edge: str, height_pt: float = 1) -> None:
|
||||
height = max(1, min(int(round(height_pt * dpi_y / 72.0)), cell_height))
|
||||
top = 0 if edge == 'top' else max(0, cell_height - height)
|
||||
for y in range(top, top + height):
|
||||
offset = y * cell_width
|
||||
for x in range(cell_width):
|
||||
ans[offset + x] = 255
|
||||
|
||||
if which == 1: # beam
|
||||
vert('left', cursor_beam_thickness)
|
||||
elif which == 2: # underline
|
||||
horz('bottom', cursor_underline_thickness)
|
||||
elif which == 3: # hollow
|
||||
vert('left')
|
||||
vert('right')
|
||||
horz('top')
|
||||
horz('bottom')
|
||||
return ans
|
||||
|
||||
|
||||
def prerender_function(
|
||||
cell_width: int,
|
||||
cell_height: int,
|
||||
baseline: int,
|
||||
underline_position: int,
|
||||
underline_thickness: int,
|
||||
strikethrough_position: int,
|
||||
strikethrough_thickness: int,
|
||||
cursor_beam_thickness: float,
|
||||
cursor_underline_thickness: float,
|
||||
dpi_x: float,
|
||||
dpi_y: float
|
||||
) -> tuple[tuple[int, ...], tuple[CBufType, ...]]:
|
||||
# Pre-render the special underline, strikethrough and missing and cursor cells
|
||||
f = partial(
|
||||
render_special, cell_width=cell_width, cell_height=cell_height, baseline=baseline,
|
||||
underline_position=underline_position, underline_thickness=underline_thickness,
|
||||
strikethrough_position=strikethrough_position, strikethrough_thickness=strikethrough_thickness,
|
||||
dpi_x=dpi_x, dpi_y=dpi_y
|
||||
)
|
||||
c = partial(
|
||||
render_cursor, cursor_beam_thickness=cursor_beam_thickness,
|
||||
cursor_underline_thickness=cursor_underline_thickness, cell_width=cell_width,
|
||||
cell_height=cell_height, dpi_x=dpi_x, dpi_y=dpi_y)
|
||||
# If you change the mapping of these cells you will need to change
|
||||
# NUM_UNDERLINE_STYLES and BEAM_IDX in shader.c and STRIKE_SPRITE_INDEX in
|
||||
# window.py and MISSING_GLYPH in font.c
|
||||
cells = list(map(f, range(1, NUM_UNDERLINE_STYLES + 1))) # underline sprites
|
||||
cells.append(f(0, strikethrough=True)) # strikethrough sprite
|
||||
cells.append(f(missing=True)) # missing glyph
|
||||
cells.extend((c(1), c(2), c(3))) # cursor glyphs
|
||||
tcells = tuple(cells)
|
||||
return tuple(map(ctypes.addressof, tcells)), tcells
|
||||
|
||||
|
||||
def render_box_drawing(codepoint: int, cell_width: int, cell_height: int, dpi: float) -> tuple[int, CBufType]:
|
||||
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
|
||||
buf = CharTexture()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue