diff --git a/kittens/tui/images.py b/kittens/tui/images.py index 68a1976ca..f5677c977 100644 --- a/kittens/tui/images.py +++ b/kittens/tui/images.py @@ -15,6 +15,7 @@ from typing import ( Sequence, Tuple, Union ) +from kitty.fast_data_types import create_canvas from kitty.typing import ( CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t, HandlerType ) @@ -145,7 +146,7 @@ def identify(path: str) -> ImageData: if f.mode == 'rgba': mode = 'rgba' break - return ImageData(first['fmt'].lower(), frames[0].width, frames[0].height, mode, frames) + return ImageData(first['fmt'].lower(), frames[0].canvas_width, frames[0].canvas_height, mode, frames) class RenderedImage(ImageData): @@ -232,6 +233,14 @@ def render_image( f.path = output_prefix + f'-{index}.{m.mode}' os.rename(os.path.join(tdir, x), f.path) check_resize(f) + f = ans.frames[0] + if f.width != ans.width or f.height != ans.height: + with open(f.path, 'r+b') as ff: + data = ff.read() + ff.seek(0) + ff.truncate() + cd = create_canvas(data, f.width, f.canvas_x, f.canvas_y, ans.width, ans.height, 3 if ans.mode == 'rgb' else 4) + ff.write(cd) if unseen: raise ConvertFailed(path, f'Failed to render {len(unseen)} out of {len(m)} frames of animation') diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 00b8a0423..743cb2454 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -634,6 +634,9 @@ def patch_color_profiles( pass +def create_canvas(d: bytes, w: int, x: int, y: int, cw: int, ch: int, bpp: int) -> bytes: ... + + def os_window_font_size( os_window_id: int, new_sz: float = -1., force: bool = False ) -> float: diff --git a/kitty/graphics.c b/kitty/graphics.c index 7d46ee110..c106bfe43 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -1640,9 +1640,35 @@ PyTypeObject GraphicsManager_Type = { .tp_members = members, }; +static PyObject* +pycreate_canvas(PyObject *self UNUSED, PyObject *args) { + unsigned int bytes_per_pixel; + unsigned int over_width, width, height, x, y; + Py_ssize_t over_sz; + const uint8_t *over_data; + if (!PyArg_ParseTuple(args, "y#IIIIII", &over_data, &over_sz, &over_width, &x, &y, &width, &height, &bytes_per_pixel)) return NULL; + size_t canvas_sz = width * height * bytes_per_pixel; + PyObject *ans = PyBytes_FromStringAndSize(NULL, canvas_sz); + if (!ans) return NULL; + + uint8_t* canvas = (uint8_t*)PyBytes_AS_STRING(ans); + memset(canvas, 0, canvas_sz); + ComposeData cd = { + .needs_blending = bytes_per_pixel == 4, + .over_width = over_width, .over_height = over_sz / (bytes_per_pixel * over_width), + .under_width = width, .under_height = height, + .over_px_sz = bytes_per_pixel, .under_px_sz = bytes_per_pixel, + .over_offset_x = x, .over_offset_y = y + }; + compose(cd, canvas, over_data); + + return ans; +} + static PyMethodDef module_methods[] = { M(shm_write, METH_VARARGS), M(shm_unlink, METH_VARARGS), + M(create_canvas, METH_VARARGS), {NULL, NULL, 0, NULL} /* Sentinel */ };