mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-02 12:44:01 +02:00
Give sprites a metadata row accessible in the shaders
Will allow sprites to point to where their decorations should be read from, for instance. Needed for scaled text and also if we want to implement decoration avoidance.
This commit is contained in:
@@ -8,7 +8,7 @@ layout(std140) uniform CellRenderData {
|
||||
|
||||
uint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
||||
|
||||
uint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx;
|
||||
uint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx, cell_height;
|
||||
float cursor_x, cursor_y, cursor_w, cursor_opacity;
|
||||
|
||||
// must have unique entries with 0 being default_bg and unset being UINT32_MAX
|
||||
@@ -96,15 +96,23 @@ vec3 to_color(uint c, uint defval) {
|
||||
return color_to_vec(resolve_color(c, defval));
|
||||
}
|
||||
|
||||
vec3 to_sprite_pos(uvec2 pos, uint idx) {
|
||||
uvec3 to_sprite_coords(uint idx) {
|
||||
uint sprites_per_page = sprites_xnum * sprites_ynum;
|
||||
uint z = idx / sprites_per_page;
|
||||
uint num_on_last_page = idx % sprites_per_page;
|
||||
uint y = num_on_last_page / sprites_xnum;
|
||||
uint x = num_on_last_page % sprites_xnum;
|
||||
vec2 s_xpos = vec2(x, float(x) + 1.0f) * (1.0f / float(sprites_xnum));
|
||||
vec2 s_ypos = vec2(y, float(y) + 1.0f) * (1.0f / float(sprites_ynum));
|
||||
return vec3(s_xpos[pos.x], s_ypos[pos.y], z);
|
||||
return uvec3(x, y, z);
|
||||
}
|
||||
|
||||
vec3 to_sprite_pos(uvec2 pos, uint idx) {
|
||||
uvec3 c = to_sprite_coords(idx);
|
||||
vec2 s_xpos = vec2(c.x, float(c.x) + 1.0f) * (1.0f / float(sprites_xnum));
|
||||
vec2 s_ypos = vec2(c.y, float(c.y) + 1.0f) * (1.0f / float(sprites_ynum));
|
||||
uint texture_height_px = (cell_height + 1u) * sprites_ynum;
|
||||
float row_height = 1.0f / float(texture_height_px);
|
||||
s_ypos[1] -= row_height; // skip the metadata row
|
||||
return vec3(s_xpos[pos.x], s_ypos[pos.y], c.z);
|
||||
}
|
||||
|
||||
vec3 choose_color(float q, vec3 a, vec3 b) {
|
||||
|
||||
@@ -1126,7 +1126,7 @@ def sprite_map_set_limits(w: int, h: int) -> None:
|
||||
|
||||
|
||||
def set_send_sprite_to_gpu(
|
||||
func: Optional[Callable[[int, int, int, bytes], None]]
|
||||
func: Optional[Callable[[int, int, int, bytes, bytes], None]]
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ static void initialize_font_group(FontGroup *fg);
|
||||
|
||||
static void
|
||||
ensure_canvas_can_fit(FontGroup *fg, unsigned cells, unsigned scale) {
|
||||
#define cs(cells, scale) (sizeof(fg->canvas.buf[0]) * 3u * cells * fg->fcm.cell_width * fg->fcm.cell_height * scale * scale)
|
||||
#define cs(cells, scale) (sizeof(fg->canvas.buf[0]) * 3u * cells * fg->fcm.cell_width * (fg->fcm.cell_height + 1) * scale * scale)
|
||||
size_t size_in_bytes = cs(cells, scale);
|
||||
if (size_in_bytes > fg->canvas.size_in_bytes) {
|
||||
free(fg->canvas.buf);
|
||||
@@ -294,7 +294,7 @@ sprite_tracker_current_layout(FONTS_DATA_HANDLE data, unsigned int *x, unsigned
|
||||
static void
|
||||
sprite_tracker_set_layout(GPUSpriteTracker *sprite_tracker, unsigned int cell_width, unsigned int cell_height) {
|
||||
sprite_tracker->xnum = MIN(MAX(1u, max_texture_size / cell_width), (size_t)UINT16_MAX);
|
||||
sprite_tracker->max_y = MIN(MAX(1u, max_texture_size / cell_height), (size_t)UINT16_MAX);
|
||||
sprite_tracker->max_y = MIN(MAX(1u, max_texture_size / (cell_height + 1)), (size_t)UINT16_MAX);
|
||||
sprite_tracker->ynum = 1;
|
||||
sprite_tracker->x = 0; sprite_tracker->y = 0; sprite_tracker->z = 0;
|
||||
}
|
||||
@@ -415,7 +415,8 @@ python_send_to_gpu(FONTS_DATA_HANDLE fg_, unsigned int idx, pixel *buf) {
|
||||
if (!num_font_groups) fatal("Cannot call send to gpu with no font groups");
|
||||
unsigned int x, y, z;
|
||||
sprite_index_to_pos(idx, fg->sprite_tracker.xnum, fg->sprite_tracker.ynum, &x, &y, &z);
|
||||
PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIN", x, y, z, PyBytes_FromStringAndSize((const char*)buf, sizeof(pixel) * fg->fcm.cell_width * fg->fcm.cell_height));
|
||||
const size_t sprite_size = fg->fcm.cell_width * fg->fcm.cell_height;
|
||||
PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIy#y#", x, y, z, buf, sprite_size * sizeof(pixel), buf + sprite_size, fg->fcm.cell_width * sizeof(pixel));
|
||||
if (ret == NULL) PyErr_Print();
|
||||
else Py_DECREF(ret);
|
||||
}
|
||||
@@ -795,9 +796,14 @@ apply_scale_to_font_group(FontGroup *fg, RunFont *rf) {
|
||||
return scale;
|
||||
}
|
||||
|
||||
static pixel*
|
||||
pointer_to_space_for_last_sprite(Canvas *canvas, FontCellMetrics fcm) {
|
||||
return canvas->buf + (canvas->size_in_bytes / sizeof(canvas->buf[0]) - fcm.cell_width * (fcm.cell_height + 1));
|
||||
}
|
||||
|
||||
static pixel*
|
||||
extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells) {
|
||||
pixel *ans = fg->canvas.buf + (fg->canvas.size_in_bytes / sizeof(fg->canvas.buf[0]) - fg->fcm.cell_width * fg->fcm.cell_height);
|
||||
pixel *ans = pointer_to_space_for_last_sprite(&fg->canvas, fg->fcm);
|
||||
pixel *dest = ans, *src = fg->canvas.buf + (i * fg->fcm.cell_width);
|
||||
unsigned int stride = fg->fcm.cell_width * num_cells;
|
||||
for (unsigned int r = 0; r < fg->fcm.cell_height; r++, dest += fg->fcm.cell_width, src += stride) memcpy(dest, src, fg->fcm.cell_width * sizeof(fg->canvas.buf[0]));
|
||||
@@ -834,7 +840,7 @@ static pixel*
|
||||
extract_cell_region(Canvas *canvas, unsigned i, Region *src, const Region *dest, unsigned src_width, FontCellMetrics unscaled_metrics) {
|
||||
src->left = i * unscaled_metrics.cell_width; src->right = MIN(src_width, src->left + unscaled_metrics.cell_width);
|
||||
unsigned unscaled_cell_area = unscaled_metrics.cell_width * unscaled_metrics.cell_height;
|
||||
pixel *ans = canvas->buf + (canvas->size_in_bytes / sizeof(canvas->buf[0]) - unscaled_cell_area);
|
||||
pixel *ans = pointer_to_space_for_last_sprite(canvas, unscaled_metrics);
|
||||
memset(ans, 0, sizeof(ans[0]) * unscaled_cell_area);
|
||||
unsigned width = MIN(src->right - src->left, unscaled_metrics.cell_width);
|
||||
for (unsigned srcy = src->top, desty = dest->top; srcy < src->bottom && desty < dest->bottom; srcy++, desty++) {
|
||||
|
||||
@@ -1439,7 +1439,7 @@ def test_char(ch: str, sz: int = 48) -> None:
|
||||
from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
|
||||
|
||||
from .render import display_bitmap, setup_for_testing
|
||||
with setup_for_testing('monospace', sz) as (_, width, height):
|
||||
with setup_for_testing('monospace', sz) as (_, _, width, height):
|
||||
buf = bytearray(width * height)
|
||||
try:
|
||||
render_box_char(ch, buf, width, height)
|
||||
@@ -1460,7 +1460,7 @@ def test_drawing(sz: int = 48, family: str = 'monospace', start: int = 0x2500, n
|
||||
|
||||
from .render import display_bitmap, setup_for_testing
|
||||
|
||||
with setup_for_testing(family, sz) as (_, width, height):
|
||||
with setup_for_testing(family, sz) as (_, _, width, height):
|
||||
space = bytearray(width * height)
|
||||
|
||||
def join_cells(cells: Iterable[bytes]) -> bytes:
|
||||
|
||||
@@ -236,14 +236,16 @@ class setup_for_testing:
|
||||
self.family, self.size, self.dpi = family, size, dpi
|
||||
self.main_face_path = main_face_path
|
||||
|
||||
def __enter__(self) -> tuple[dict[tuple[int, int, int], bytes], int, int]:
|
||||
def __enter__(self) -> tuple[dict[tuple[int, int, int], bytes], dict[tuple[int, int, int], bytes], int, int]:
|
||||
global descriptor_overrides
|
||||
opts = defaults._replace(font_family=parse_font_spec(self.family), font_size=self.size)
|
||||
set_options(opts)
|
||||
sprites = {}
|
||||
sprite_metadata = {}
|
||||
|
||||
def send_to_gpu(x: int, y: int, z: int, data: bytes) -> None:
|
||||
def send_to_gpu(x: int, y: int, z: int, data: bytes, metadata: bytes) -> None:
|
||||
sprites[(x, y, z)] = data
|
||||
sprite_metadata[(x, y, z)] = metadata
|
||||
|
||||
sprite_map_set_limits(self.xnum, self.ynum)
|
||||
set_send_sprite_to_gpu(send_to_gpu)
|
||||
@@ -254,7 +256,7 @@ class setup_for_testing:
|
||||
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
|
||||
return sprites, sprite_metadata, cell_width, cell_height
|
||||
except Exception:
|
||||
set_send_sprite_to_gpu(None)
|
||||
raise
|
||||
@@ -266,7 +268,7 @@ class setup_for_testing:
|
||||
|
||||
|
||||
def render_string(text: str, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0) -> tuple[int, int, list[bytes]]:
|
||||
with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):
|
||||
with setup_for_testing(family, size, dpi) as (sprites, sprite_metadata, cell_width, cell_height):
|
||||
s = Screen(None, 1, len(text)*2)
|
||||
line = s.line(0)
|
||||
s.draw(text)
|
||||
@@ -286,7 +288,7 @@ def render_string(text: str, family: str = 'monospace', size: float = 11.0, dpi:
|
||||
def shape_string(
|
||||
text: str = "abcd", family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, path: Optional[str] = None
|
||||
) -> list[tuple[int, int, int, tuple[int, ...]]]:
|
||||
with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):
|
||||
with setup_for_testing(family, size, dpi) as (sprites, sprite_metadata, cell_width, cell_height):
|
||||
s = Screen(None, 1, len(text)*2)
|
||||
line = s.line(0)
|
||||
s.draw(text)
|
||||
|
||||
@@ -118,12 +118,12 @@ realloc_sprite_texture(FONTS_DATA_HANDLE fg) {
|
||||
sprite_tracker_current_layout(fg, &xnum, &ynum, &z);
|
||||
znum = z + 1;
|
||||
SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map;
|
||||
width = xnum * fg->fcm.cell_width; height = ynum * fg->fcm.cell_height;
|
||||
width = xnum * fg->fcm.cell_width; height = ynum * (fg->fcm.cell_height + 1);
|
||||
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_SRGB8_ALPHA8, width, height, znum);
|
||||
if (sprite_map->texture_id) {
|
||||
// need to re-alloc
|
||||
src_ynum = MAX(1, sprite_map->last_ynum);
|
||||
copy_image_sub_data(sprite_map->texture_id, tex, width, src_ynum * fg->fcm.cell_height, sprite_map->last_num_of_layers);
|
||||
copy_image_sub_data(sprite_map->texture_id, tex, width, src_ynum * (fg->fcm.cell_height + 1), sprite_map->last_num_of_layers);
|
||||
glDeleteTextures(1, &sprite_map->texture_id);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
@@ -154,8 +154,8 @@ send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int idx, pixel *buf) {
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map->texture_id);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z);
|
||||
x *= fg->fcm.cell_width; y *= fg->fcm.cell_height;
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, fg->fcm.cell_width, fg->fcm.cell_height, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buf);
|
||||
x *= fg->fcm.cell_width; y *= fg->fcm.cell_height + 1;
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, fg->fcm.cell_width, fg->fcm.cell_height + 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buf);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -299,7 +299,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
|
||||
GLuint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
||||
|
||||
GLuint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx;
|
||||
GLuint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx, cell_height;
|
||||
GLfloat cursor_x, cursor_y, cursor_w, cursor_opacity;
|
||||
GLuint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;
|
||||
GLfloat bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7;
|
||||
@@ -389,6 +389,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
unsigned int x, y, z;
|
||||
sprite_tracker_current_layout(os_window->fonts_data, &x, &y, &z);
|
||||
rd->sprites_xnum = x; rd->sprites_ynum = y;
|
||||
rd->cell_height = os_window->fonts_data->fcm.cell_height;
|
||||
rd->inverted = screen_invert_colors(screen) ? 1 : 0;
|
||||
|
||||
#undef COLOR
|
||||
|
||||
@@ -268,7 +268,7 @@ class FontBaseTest(BaseTest):
|
||||
self.addCleanup(self.rmtree_ignoring_errors, self.tdir)
|
||||
path = self.path_for_font(self.font_name) if self.font_name else ''
|
||||
tc = setup_for_testing(size=self.font_size, dpi=self.dpi, main_face_path=path)
|
||||
self.sprites, self.cell_width, self.cell_height = tc.__enter__()
|
||||
self.sprites, self.sprite_metadata, self.cell_width, self.cell_height = tc.__enter__()
|
||||
self.addCleanup(tc.__exit__)
|
||||
self.assertEqual([k[0] for k in self.sprites], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
|
||||
@@ -283,7 +283,7 @@ class Rendering(FontBaseTest):
|
||||
|
||||
def test_sprite_map(self):
|
||||
sprite_map_set_limits(10, 2)
|
||||
sprite_map_set_layout(5, 5)
|
||||
sprite_map_set_layout(5, 4) # 4 because of metadata row
|
||||
self.ae(test_sprite_position_for(0), (0, 0, 0))
|
||||
self.ae(test_sprite_position_for(1), (1, 0, 0))
|
||||
self.ae(test_sprite_position_for(2), (0, 1, 0))
|
||||
|
||||
Reference in New Issue
Block a user