this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

WIP: images

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>

+165 -2
+7
build.zig
··· 12 12 }); 13 13 vaxis.addImport("ziglyph", ziglyph.module("ziglyph")); 14 14 15 + const zigimg = b.dependency("zigimg", .{ 16 + .optimize = optimize, 17 + .target = target, 18 + }); 19 + vaxis.addImport("zigimg", zigimg.module("zigimg")); 20 + 15 21 const exe = b.addExecutable(.{ 16 22 .name = "vaxis", 17 23 .root_source_file = .{ .path = "examples/text_input.zig" }, ··· 39 45 .optimize = optimize, 40 46 }); 41 47 lib_unit_tests.root_module.addImport("ziglyph", ziglyph.module("ziglyph")); 48 + lib_unit_tests.root_module.addImport("zigimg", zigimg.module("zigimg")); 42 49 43 50 const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); 44 51
+4
build.zig.zon
··· 14 14 .url = "https://codeberg.org/dude_the_builder/ziglyph/archive/main.tar.gz", 15 15 .hash = "12208553f3f47e51494e187f4c0e6f6b3844e3993436cad4a0e8c4db4e99645967b5", 16 16 }, 17 + .zigimg = .{ 18 + .url = "https://github.com/zigimg/zigimg/archive/f6998808f283f8d3c2ef34e8b4af423bc1786f32.tar.gz", 19 + .hash = "12202ee5d22ade0c300e9e7eae4c1951bda3d5f236fe1a139eb3613b43e2f12a88db", 20 + } 17 21 }, 18 22 19 23 .paths = .{
+83
src/Image.zig
··· 1 + const std = @import("std"); 2 + const math = std.math; 3 + const testing = std.testing; 4 + const zigimg = @import("zigimg"); 5 + 6 + const Window = @import("Window.zig"); 7 + const Winsize = @import("Tty.zig").Winsize; 8 + 9 + const Image = @This(); 10 + 11 + pub const Source = union(enum) { 12 + /// loads an image from a path. path can be relative to cwd, or absolute 13 + path: []const u8, 14 + /// loads an image from raw bytes 15 + mem: []const u8, 16 + }; 17 + 18 + pub const Protocol = enum { 19 + kitty, 20 + // TODO: sixel, full block, half block, quad block 21 + }; 22 + 23 + /// the decoded image 24 + img: zigimg.Image, 25 + 26 + /// unique identifier for this image 27 + id: u32, 28 + 29 + /// width of the image, in cells 30 + cell_width: usize, 31 + /// height of the image, in cells 32 + cell_height: usize, 33 + 34 + /// initialize a new image 35 + pub fn init( 36 + alloc: std.mem.Allocator, 37 + winsize: Winsize, 38 + src: Source, 39 + id: u32, 40 + ) !Image { 41 + const img = switch (src) { 42 + .path => |path| try zigimg.Image.fromFilePath(alloc, path), 43 + .mem => |bytes| try zigimg.Image.fromMemory(alloc, bytes), 44 + }; 45 + // cell geometry 46 + const pix_per_col = try math.divCeil(usize, winsize.x_pixel, winsize.cols); 47 + const pix_per_row = try math.divCeil(usize, winsize.y_pixel, winsize.rows); 48 + 49 + const cell_width = math.divCeil(usize, img.width, pix_per_col) catch 0; 50 + const cell_height = math.divCeil(usize, img.height, pix_per_row) catch 0; 51 + 52 + return Image{ 53 + .img = img, 54 + .cell_width = cell_width, 55 + .cell_height = cell_height, 56 + .id = id, 57 + }; 58 + } 59 + 60 + pub fn deinit(self: *Image) void { 61 + self.img.deinit(); 62 + } 63 + 64 + pub fn draw(self: *Image, win: Window, placement_id: u32) !void { 65 + try win.writeImage(win.x_off, win.y_off, self, placement_id); 66 + } 67 + 68 + test "image" { 69 + const alloc = testing.allocator; 70 + var img = try init( 71 + alloc, 72 + .{ 73 + .rows = 1, 74 + .cols = 1, 75 + .x_pixel = 1, 76 + .y_pixel = 1, 77 + }, 78 + .{ .path = "vaxis.png" }, 79 + ); 80 + defer img.deinit(); 81 + try testing.expectEqual(1, img.cell_width); 82 + try testing.expectEqual(1, img.cell_height); 83 + }
+9 -2
src/InternalScreen.zig
··· 3 3 const Style = @import("cell.zig").Style; 4 4 const Cell = @import("cell.zig").Cell; 5 5 const Shape = @import("Mouse.zig").Shape; 6 + const Image = @import("Image.zig").Placement; 7 + const Placement = @import("Screen.zig").Placement; 6 8 7 9 const log = std.log.scoped(.internal_screen); 8 10 ··· 35 37 36 38 mouse_shape: Shape = .default, 37 39 40 + images: std.ArrayList(Placement) = undefined, 41 + 38 42 /// sets each cell to the default cell 39 43 pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !InternalScreen { 40 - var screen = InternalScreen{}; 41 - screen.buf = try alloc.alloc(InternalCell, w * h); 44 + var screen = InternalScreen{ 45 + .buf = try alloc.alloc(InternalCell, w * h), 46 + .images = std.ArrayList(Placement).init(alloc), 47 + }; 42 48 for (screen.buf, 0..) |_, i| { 43 49 screen.buf[i] = .{ 44 50 .char = try std.ArrayList(u8).initCapacity(alloc, 1), ··· 52 58 } 53 59 54 60 pub fn deinit(self: *InternalScreen, alloc: std.mem.Allocator) void { 61 + self.images.deinit(); 55 62 for (self.buf, 0..) |_, i| { 56 63 self.buf[i].char.deinit(); 57 64 self.buf[i].uri.deinit();
+28
src/Screen.zig
··· 3 3 4 4 const Cell = @import("cell.zig").Cell; 5 5 const Shape = @import("Mouse.zig").Shape; 6 + const Image = @import("Image.zig"); 6 7 7 8 const log = std.log.scoped(.screen); 8 9 9 10 const Screen = @This(); 10 11 12 + pub const Placement = struct { 13 + img: *Image, 14 + placement_id: u32, 15 + col: usize, 16 + row: usize, 17 + }; 18 + 11 19 width: usize = 0, 12 20 height: usize = 0, 13 21 ··· 21 29 22 30 mouse_shape: Shape = .default, 23 31 32 + images: std.ArrayList(Placement) = undefined, 33 + 24 34 pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !Screen { 25 35 var self = Screen{ 26 36 .buf = try alloc.alloc(Cell, w * h), 27 37 .width = w, 28 38 .height = h, 39 + .images = std.ArrayList(Placement).init(alloc), 29 40 }; 30 41 for (self.buf, 0..) |_, i| { 31 42 self.buf[i] = .{}; ··· 34 45 } 35 46 pub fn deinit(self: *Screen, alloc: std.mem.Allocator) void { 36 47 alloc.free(self.buf); 48 + self.images.deinit(); 37 49 } 38 50 39 51 /// writes a cell to a location. 0 indexed ··· 50 62 assert(i < self.buf.len); 51 63 self.buf[i] = cell; 52 64 } 65 + 66 + pub fn writeImage( 67 + self: *Screen, 68 + col: usize, 69 + row: usize, 70 + img: *Image, 71 + placement_id: u32, 72 + ) !void { 73 + const p = Placement{ 74 + .img = img, 75 + .placement_id = placement_id, 76 + .col = col, 77 + .row = row, 78 + }; 79 + try self.images.append(p); 80 + }
+22
src/Window.zig
··· 2 2 3 3 const Screen = @import("Screen.zig"); 4 4 const Cell = @import("cell.zig").Cell; 5 + const Image = @import("Image.zig"); 5 6 const gw = @import("gwidth.zig"); 6 7 7 8 const log = std.log.scoped(.window); ··· 68 69 self.screen.writeCell(col + self.x_off, row + self.y_off, cell); 69 70 } 70 71 72 + /// writes a cell to the location in the window 73 + pub fn writeImage( 74 + self: Window, 75 + col: usize, 76 + row: usize, 77 + img: *Image, 78 + placement_id: u32, 79 + ) !void { 80 + if (self.height == 0 or self.width == 0) return; 81 + if (self.height <= row or self.width <= col) return; 82 + self.screen.writeImage(col, row, img, placement_id); 83 + } 84 + 71 85 /// fills the window with the default cell 72 86 pub fn clear(self: Window) void { 73 87 self.fill(.{}); 88 + // we clear any image with it's first cell within this window 89 + for (self.screen.images.items, 0..) |p, i| { 90 + if (p.col >= self.x_off and p.col < self.width and 91 + p.row >= self.y_off and p.row < self.height) 92 + { 93 + _ = self.screen.images.swapRemove(i); 94 + } 95 + } 74 96 } 75 97 76 98 /// returns the width of the grapheme. This depends on the terminal capabilities
+1
src/main.zig
··· 17 17 18 18 test { 19 19 _ = @import("GraphemeCache.zig"); 20 + _ = @import("Image.zig"); 20 21 _ = @import("Key.zig"); 21 22 _ = @import("Mouse.zig"); 22 23 _ = @import("Options.zig");
+11
src/vaxis.zig
··· 14 14 const Hyperlink = @import("cell.zig").Hyperlink; 15 15 const gwidth = @import("gwidth.zig"); 16 16 const Shape = @import("Mouse.zig").Shape; 17 + const Placement = Screen.Placement; 17 18 18 19 /// Vaxis is the entrypoint for a Vaxis application. The provided type T should 19 20 /// be a tagged union which contains all of the events the application will ··· 242 243 // the next render call will refresh the entire screen 243 244 pub fn queueRefresh(self: *Self) void { 244 245 self.refresh = true; 246 + self.screen_last.images.clearRetainingCapacity(); 245 247 } 246 248 247 249 /// draws the screen to the terminal ··· 277 279 var col: usize = 0; 278 280 var cursor: Style = .{}; 279 281 var link: Hyperlink = .{}; 282 + 283 + // delete remove images from the screen by looping through the 284 + // current state and comparing to the next state 285 + for (self.screen_last.images.items) |last_img| { 286 + const keep: bool = for (self.screen.images.items) |next_img| { 287 + if (std.meta.eql(last_img, next_img)) break true; 288 + } else false; 289 + if (keep) continue; 290 + } 280 291 281 292 var i: usize = 0; 282 293 while (i < self.screen.buf.len) {
vaxis.png

This is a binary file and will not be displayed.