images: move to subfolder, use union enum for implementations
There will only ever be a handful of image implementations. Let's just use a union enum for handling them. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
parent
edb1fae2ab
commit
626a9101bd
8 changed files with 77 additions and 158 deletions
|
@ -1,84 +0,0 @@
|
|||
const std = @import("std");
|
||||
const math = std.math;
|
||||
const testing = std.testing;
|
||||
const zigimg = @import("zigimg");
|
||||
|
||||
const Window = @import("Window.zig");
|
||||
const Winsize = @import("Tty.zig").Winsize;
|
||||
|
||||
const Image = @This();
|
||||
|
||||
pub const Source = union(enum) {
|
||||
/// loads an image from a path. path can be relative to cwd, or absolute
|
||||
path: []const u8,
|
||||
/// loads an image from raw bytes
|
||||
mem: []const u8,
|
||||
};
|
||||
|
||||
pub const Protocol = enum {
|
||||
kitty,
|
||||
// TODO: sixel, full block, half block, quad block
|
||||
};
|
||||
|
||||
/// the decoded image
|
||||
img: zigimg.Image,
|
||||
|
||||
/// unique identifier for this image
|
||||
id: u32,
|
||||
|
||||
/// width of the image, in cells
|
||||
cell_width: usize,
|
||||
/// height of the image, in cells
|
||||
cell_height: usize,
|
||||
|
||||
/// initialize a new image
|
||||
pub fn init(
|
||||
alloc: std.mem.Allocator,
|
||||
winsize: Winsize,
|
||||
src: Source,
|
||||
id: u32,
|
||||
) !Image {
|
||||
const img = switch (src) {
|
||||
.path => |path| try zigimg.Image.fromFilePath(alloc, path),
|
||||
.mem => |bytes| try zigimg.Image.fromMemory(alloc, bytes),
|
||||
};
|
||||
// cell geometry
|
||||
const pix_per_col = try math.divCeil(usize, winsize.x_pixel, winsize.cols);
|
||||
const pix_per_row = try math.divCeil(usize, winsize.y_pixel, winsize.rows);
|
||||
|
||||
const cell_width = math.divCeil(usize, img.width, pix_per_col) catch 0;
|
||||
const cell_height = math.divCeil(usize, img.height, pix_per_row) catch 0;
|
||||
|
||||
return Image{
|
||||
.img = img,
|
||||
.cell_width = cell_width,
|
||||
.cell_height = cell_height,
|
||||
.id = id,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Image) void {
|
||||
self.img.deinit();
|
||||
}
|
||||
|
||||
pub fn draw(self: *Image, win: Window, placement_id: u32) !void {
|
||||
try win.writeImage(win.x_off, win.y_off, self, placement_id);
|
||||
}
|
||||
|
||||
test "image" {
|
||||
const alloc = testing.allocator;
|
||||
var img = try init(
|
||||
alloc,
|
||||
.{
|
||||
.rows = 1,
|
||||
.cols = 1,
|
||||
.x_pixel = 1,
|
||||
.y_pixel = 1,
|
||||
},
|
||||
.{ .path = "vaxis.png" },
|
||||
1,
|
||||
);
|
||||
defer img.deinit();
|
||||
try testing.expectEqual(200, img.cell_width);
|
||||
try testing.expectEqual(197, img.cell_height);
|
||||
}
|
|
@ -3,7 +3,7 @@ const assert = std.debug.assert;
|
|||
const Style = @import("cell.zig").Style;
|
||||
const Cell = @import("cell.zig").Cell;
|
||||
const Shape = @import("Mouse.zig").Shape;
|
||||
const Image = @import("Image.zig").Placement;
|
||||
const Image = @import("image/image.zig").Image;
|
||||
const Placement = @import("Screen.zig").Placement;
|
||||
|
||||
const log = std.log.scoped(.internal_screen);
|
||||
|
|
|
@ -3,7 +3,7 @@ const assert = std.debug.assert;
|
|||
|
||||
const Cell = @import("cell.zig").Cell;
|
||||
const Shape = @import("Mouse.zig").Shape;
|
||||
const Image = @import("Image.zig");
|
||||
const Image = @import("image/image.zig").Image;
|
||||
|
||||
const log = std.log.scoped(.screen);
|
||||
|
||||
|
@ -14,6 +14,14 @@ pub const Placement = struct {
|
|||
placement_id: u32,
|
||||
col: usize,
|
||||
row: usize,
|
||||
|
||||
/// two placements are considered equal if their image id and their
|
||||
/// placement id are equal
|
||||
pub fn eql(self: Placement, tgt: Placement) bool {
|
||||
if (self.img.getId() != tgt.img.getId()) return false;
|
||||
if (self.placement_id != tgt.placement_id) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
width: usize = 0,
|
||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
|
||||
const Screen = @import("Screen.zig");
|
||||
const Cell = @import("cell.zig").Cell;
|
||||
const Image = @import("Image.zig");
|
||||
const Image = @import("image/image.zig").Image;
|
||||
const gw = @import("gwidth.zig");
|
||||
|
||||
const log = std.log.scoped(.window);
|
||||
|
@ -69,17 +69,14 @@ pub fn writeCell(self: Window, col: usize, row: usize, cell: Cell) void {
|
|||
self.screen.writeCell(col + self.x_off, row + self.y_off, cell);
|
||||
}
|
||||
|
||||
/// writes a cell to the location in the window
|
||||
/// writes an image to the location in the window
|
||||
pub fn writeImage(
|
||||
self: Window,
|
||||
col: usize,
|
||||
row: usize,
|
||||
img: *Image,
|
||||
placement_id: u32,
|
||||
) !void {
|
||||
if (self.height == 0 or self.width == 0) return;
|
||||
if (self.height <= row or self.width <= col) return;
|
||||
self.screen.writeImage(col, row, img, placement_id);
|
||||
self.screen.writeImage(self.x_off, self.y_off, img, placement_id);
|
||||
}
|
||||
|
||||
/// fills the window with the default cell
|
||||
|
|
|
@ -12,63 +12,25 @@ const Kitty = @This();
|
|||
/// the decoded image
|
||||
img: zigimg.Image,
|
||||
|
||||
/// unique identifier for this image
|
||||
id: u32,
|
||||
/// unique identifier for this image. This will be managed by the screen. The ID
|
||||
/// is only null for images which have not been transmitted to the screen
|
||||
id: ?u32 = null,
|
||||
|
||||
/// width of the image, in cells
|
||||
cell_width: usize,
|
||||
/// height of the image, in cells
|
||||
cell_height: usize,
|
||||
|
||||
/// initialize a new image
|
||||
pub fn init(
|
||||
alloc: std.mem.Allocator,
|
||||
winsize: Winsize,
|
||||
src: []const u8,
|
||||
id: u32,
|
||||
) !Kitty {
|
||||
const img = switch (src) {
|
||||
.path => |path| try zigimg.Image.fromFilePath(alloc, path),
|
||||
.mem => |bytes| try zigimg.Image.fromMemory(alloc, bytes),
|
||||
};
|
||||
// cell geometry
|
||||
const pix_per_col = try math.divCeil(usize, winsize.x_pixel, winsize.cols);
|
||||
const pix_per_row = try math.divCeil(usize, winsize.y_pixel, winsize.rows);
|
||||
|
||||
const cell_width = math.divCeil(usize, img.width, pix_per_col) catch 0;
|
||||
const cell_height = math.divCeil(usize, img.height, pix_per_row) catch 0;
|
||||
|
||||
return Image{
|
||||
.img = img,
|
||||
.cell_width = cell_width,
|
||||
.cell_height = cell_height,
|
||||
.id = id,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Image) void {
|
||||
pub fn deinit(self: *Kitty) void {
|
||||
self.img.deinit();
|
||||
}
|
||||
|
||||
pub fn draw(self: *Image, win: Window, placement_id: u32) !void {
|
||||
try win.writeImage(win.x_off, win.y_off, self, placement_id);
|
||||
}
|
||||
|
||||
test "image" {
|
||||
const alloc = testing.allocator;
|
||||
var img = try init(
|
||||
alloc,
|
||||
.{
|
||||
.rows = 1,
|
||||
.cols = 1,
|
||||
.x_pixel = 1,
|
||||
.y_pixel = 1,
|
||||
},
|
||||
.{ .path = "vaxis.png" },
|
||||
0,
|
||||
.kitty,
|
||||
);
|
||||
defer img.deinit();
|
||||
try testing.expectEqual(200, img.cell_width);
|
||||
try testing.expectEqual(197, img.cell_height);
|
||||
pub fn draw(self: *Kitty, win: Window) !void {
|
||||
const row: u16 = @truncate(win.y_off);
|
||||
const col: u16 = @truncate(win.x_off);
|
||||
// the placement id has the high 16 bits as the column and the low 16
|
||||
// bits as the row. This means we can only place this image one time at
|
||||
// the same location - which is completely sane
|
||||
const pid: u32 = col << 16 | row;
|
||||
try win.writeImage(win.x_off, win.y_off, self, pid);
|
||||
}
|
||||
|
|
|
@ -8,35 +8,60 @@ const Window = @import("../Window.zig");
|
|||
|
||||
const Kitty = @import("Kitty.zig");
|
||||
|
||||
pub const Image = union(enum) {
|
||||
kitty: Kitty,
|
||||
|
||||
pub const Protocol = enum {
|
||||
pub const Protocol = enum {
|
||||
kitty,
|
||||
// TODO: sixel, full block, half block, quad block
|
||||
};
|
||||
};
|
||||
|
||||
pub const Image = union(enum) {
|
||||
kitty: Kitty,
|
||||
|
||||
/// initialize a new image
|
||||
pub fn init(
|
||||
alloc: std.mem.Allocator,
|
||||
winsize: Winsize,
|
||||
src: []const u8,
|
||||
id: u32,
|
||||
protocol: Protocol,
|
||||
) !Image {
|
||||
const img = switch (src) {
|
||||
.path => |path| try zigimg.Image.fromFilePath(alloc, path),
|
||||
.mem => |bytes| try zigimg.Image.fromMemory(alloc, bytes),
|
||||
};
|
||||
// cell geometry
|
||||
const pix_per_col = try math.divCeil(usize, winsize.x_pixel, winsize.cols);
|
||||
const pix_per_row = try math.divCeil(usize, winsize.y_pixel, winsize.rows);
|
||||
|
||||
const cell_width = math.divCeil(usize, img.width, pix_per_col) catch 0;
|
||||
const cell_height = math.divCeil(usize, img.height, pix_per_row) catch 0;
|
||||
|
||||
switch (protocol) {
|
||||
.kitty => {
|
||||
const img = try Kitty.init(alloc, winsize, src, id);
|
||||
return .{ .kitty = img };
|
||||
return .{
|
||||
.kitty = Kitty{
|
||||
.img = img,
|
||||
.cell_width = cell_width,
|
||||
.cell_height = cell_height,
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Image) void {
|
||||
self.img.deinit();
|
||||
pub fn deinit(self: Image) void {
|
||||
switch (self) {
|
||||
inline else => |case| case.deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(self: *Image, win: Window, placement_id: u32) !void {
|
||||
try win.writeImage(win.x_off, win.y_off, self, placement_id);
|
||||
pub fn draw(self: Image, win: Window) !void {
|
||||
switch (self) {
|
||||
inline else => |case| case.draw(win),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getId(self: Image) ?u32 {
|
||||
switch (self) {
|
||||
.kitty => |k| return k.id,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,7 +17,6 @@ pub fn init(comptime EventType: type, opts: Options) !Vaxis(EventType) {
|
|||
|
||||
test {
|
||||
_ = @import("GraphemeCache.zig");
|
||||
_ = @import("Image.zig");
|
||||
_ = @import("Key.zig");
|
||||
_ = @import("Mouse.zig");
|
||||
_ = @import("Options.zig");
|
||||
|
@ -29,6 +28,7 @@ test {
|
|||
_ = @import("ctlseqs.zig");
|
||||
_ = @import("event.zig");
|
||||
_ = @import("gwidth.zig");
|
||||
_ = @import("image/image.zig");
|
||||
_ = @import("queue.zig");
|
||||
_ = @import("vaxis.zig");
|
||||
}
|
||||
|
|
|
@ -280,13 +280,24 @@ pub fn Vaxis(comptime T: type) type {
|
|||
var cursor: Style = .{};
|
||||
var link: Hyperlink = .{};
|
||||
|
||||
// delete remove images from the screen by looping through the
|
||||
// current state and comparing to the next state
|
||||
// remove images from the screen by looping through the last state
|
||||
// and comparing to the next state
|
||||
for (self.screen_last.images.items) |last_img| {
|
||||
const keep: bool = for (self.screen.images.items) |next_img| {
|
||||
if (std.meta.eql(last_img, next_img)) break true;
|
||||
if (last_img.eql(next_img)) break true;
|
||||
} else false;
|
||||
if (keep) continue;
|
||||
// TODO: remove image placements
|
||||
}
|
||||
|
||||
// add new images. Could slightly optimize by knowing which images
|
||||
// we need to keep from the remove loop
|
||||
for (self.screen.images.items) |img| {
|
||||
const transmit: bool = for (self.screen_last.images.items) |last_img| {
|
||||
if (last_img.eql(img)) break false;
|
||||
} else true;
|
||||
if (!transmit) continue;
|
||||
// TODO: transmit the new image to the screen
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
|
|
Loading…
Reference in a new issue