From 0bb65902ebec104dac954b59f28500812419e1af Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 00:45:14 -0300 Subject: A terminal's kitty graphics draw as glyph art where pixels cannot, and by choice: Petscii on the pane, TermImages for new ones Under an outer terminal without kitty graphics a tty pardes cannot pass a terminal pane's images on, so it draws them itself as an image pane's Petscii does: each row of an image is matched to C64-palette glyph art over the cells it covers (GlyphArt.renderRect, the exact-grid half of GlyphArt.render), cached while it is on screen. yazi is still told the kitty protocol works, so it keeps sending its previews. The same art is a choice anywhere: the Petscii builtin now takes a terminal pane too (PaneKind.picture), flipping its images between pixels and glyphs, and a terminal holding images, or drawing them as glyphs by choice, says petscii:on|off in its tag as an image pane does. TermImages real|petscii (default real) is what a new terminal starts with; it is in DumpConfig, /ctl and the reference. Co-Authored-By: Claude Opus 5.5 --- src/image.zig | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'src/image.zig') diff --git a/src/image.zig b/src/image.zig index 936fe19a..3a3f1932 100644 --- a/src/image.zig +++ b/src/image.zig @@ -305,7 +305,6 @@ pub const GlyphArt = struct { pub fn render(gpa: std.mem.Allocator, rgba: []const u8, iw: usize, ih: usize, max_cols: usize, max_rows: usize, pal: Palette, ascii: bool) !Grid { if (iw == 0 or ih == 0 or max_cols == 0 or max_rows == 0) return .{ .cells = try gpa.alloc(Cell, 0), .cols = 0, .rows = 0 }; - const gset: []const Glyph = if (ascii) &glyphs_ascii else &glyphs; // contain-fit; cells are ~twice as tall as wide, so a row spans 2 width-units. var cols = max_cols; var rows = (max_cols * ih) / (2 * iw); @@ -315,16 +314,28 @@ pub const GlyphArt = struct { } cols = std.math.clamp(cols, 1, max_cols); rows = std.math.clamp(rows, 1, max_rows); + return renderRect(gpa, rgba, iw, ih, .{ .w = @intCast(iw), .h = @intCast(ih) }, cols, rows, pal, ascii); + } + /// The pixels of `src` (a rectangle of the `iw` x `ih` image) laid + /// over exactly `cols` x `rows` cells, whatever their shape: a + /// terminal's image fragment is already the size its cells are. + pub fn renderRect(gpa: std.mem.Allocator, rgba: []const u8, iw: usize, ih: usize, src: PixelRect, cols: usize, rows: usize, pal: Palette, ascii: bool) !Grid { + const x0: usize = @min(src.x, iw); + const y0: usize = @min(src.y, ih); + const sw: usize = @min(src.w, iw - x0); + const sh: usize = @min(src.h, ih - y0); + if (sw == 0 or sh == 0 or cols == 0 or rows == 0) return .{ .cells = try gpa.alloc(Cell, 0), .cols = 0, .rows = 0 }; + const gset: []const Glyph = if (ascii) &glyphs_ascii else &glyphs; const cells = try gpa.alloc(Cell, cols * rows); var cy: usize = 0; while (cy < rows) : (cy += 1) { - const ry0 = cy * ih / rows; - const ry1 = @max(ry0 + 1, (cy + 1) * ih / rows); + const ry0 = y0 + cy * sh / rows; + const ry1 = @max(ry0 + 1, y0 + (cy + 1) * sh / rows); var cx: usize = 0; while (cx < cols) : (cx += 1) { - const rx0 = cx * iw / cols; - const rx1 = @max(rx0 + 1, (cx + 1) * iw / cols); + const rx0 = x0 + cx * sw / cols; + const rx1 = @max(rx0 + 1, x0 + (cx + 1) * sw / cols); var cell: [64][3]u8 = undefined; var sy: usize = 0; while (sy < 8) : (sy += 1) { -- cgit v1.3