summaryrefslogtreecommitdiff
path: root/src/image.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-05 20:21:50 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitde4def3548a6729b0dfd2120495a61beef8c8c2c (patch)
tree12144f0f64dc96bb4741459c1f9db57f44349330 /src/image.zig
parent7988d9bc6e31210ff13994f18d5622dd57ba9341 (diff)
downloadpardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.tar.gz
pardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.zip
pardes v2: the rewrite, complete and organized. src/ (core + three shells), test/ (snapshot parity harness + 18 frozen goldens). One sans-IO core, vaxis tty + SDL3 GPU native + wasm web shells, 18/18 parity with the purged prototype, 7.6k lines vs 12.1k. Fix: gui shell pre-sized the core at init so the greet-releasing resize never fired (blank panes until first interaction); live sessions now init at defaults and get the real grid as a resize event (the shell contract, documented on Options).
Diffstat (limited to 'src/image.zig')
-rw-r--r--src/image.zig53
1 files changed, 53 insertions, 0 deletions
diff --git a/src/image.zig b/src/image.zig
new file mode 100644
index 00000000..a3415019
--- /dev/null
+++ b/src/image.zig
@@ -0,0 +1,53 @@
+//! Image panes. Decoding is zstbi (C stb_image) to RGBA, downscaled; the kept
+//! pixels render as PETSCII glyph art directly into the Surface — or, when the
+//! host has kitty graphics (and Petscii isn't toggled), ride the Surface as a
+//! pixel attachment the shell transmits/places (tty: vaxis kitty; SDL: blit).
+const std = @import("std");
+const zstbi = @import("zstbi");
+const ghostty_vt = @import("ghostty-vt");
+pub const petscii = @import("petscii.zig");
+
+/// the terminal's own 16 ANSI colors — what the `terminal` palette mode
+/// scores against; cells then paint as indexed colors so the real terminal
+/// resolves the RGB
+pub fn ansiPalette() [16][3]u8 {
+ var p: [16][3]u8 = undefined;
+ for (0..16) |i| {
+ const c = ghostty_vt.color.default[i];
+ p[i] = .{ c.r, c.g, c.b };
+ }
+ return p;
+}
+
+/// cap the longest side before keeping/transmitting: a pane is at most a
+/// screenful of cells, multi-thousand-pixel photos waste decode/transmit time
+const MAX_DIM: u32 = 1280;
+
+pub const PaletteMode = enum { commodore, terminal };
+
+/// call once at startup / exit (stb_image's allocator shim)
+pub fn start(io: std.Io, gpa: std.mem.Allocator) void {
+ zstbi.init(io, gpa);
+}
+pub fn stop() void {
+ zstbi.deinit();
+}
+
+/// decode + downscale to RGBA, gpa-owned. Returns null on any failure — the
+/// pane then simply shows a blank body.
+pub fn decode(gpa: std.mem.Allocator, bytes: []const u8) ?struct { rgba: []u8, w: usize, h: usize } {
+ if (bytes.len == 0) return null;
+ var img = zstbi.Image.loadFromMemory(bytes, 4) catch return null; // 4 = RGBA
+ defer img.deinit();
+ var scaled: ?zstbi.Image = null;
+ defer if (scaled) |*s| s.deinit();
+ const longest = @max(img.width, img.height);
+ const src: *const zstbi.Image = if (longest > MAX_DIM) blk: {
+ const nw = @max(1, img.width * MAX_DIM / longest);
+ const nh = @max(1, img.height * MAX_DIM / longest);
+ scaled = img.resize(nw, nh);
+ break :blk &scaled.?;
+ } else &img;
+ const rgba = gpa.dupe(u8, src.data) catch return null;
+ return .{ .rgba = rgba, .w = src.width, .h = src.height };
+}