image: use image options struct, add more functionality
Use an options struct for controlling image output. Add more functionality from the core kitty image protocol
This commit is contained in:
parent
fd6c380c57
commit
1dc90bd764
4 changed files with 179 additions and 28 deletions
|
@ -39,6 +39,8 @@ pub fn main() !void {
|
||||||
|
|
||||||
var n: usize = 0;
|
var n: usize = 0;
|
||||||
|
|
||||||
|
var clip_y: usize = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const event = loop.nextEvent();
|
const event = loop.nextEvent();
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
@ -47,7 +49,10 @@ pub fn main() !void {
|
||||||
return;
|
return;
|
||||||
} else if (key.matches('l', .{ .ctrl = true })) {
|
} else if (key.matches('l', .{ .ctrl = true })) {
|
||||||
vx.queueRefresh();
|
vx.queueRefresh();
|
||||||
}
|
} else if (key.matches('j', .{}))
|
||||||
|
clip_y += 1
|
||||||
|
else if (key.matches('k', .{}))
|
||||||
|
clip_y -|= 1;
|
||||||
},
|
},
|
||||||
.winsize => |ws| try vx.resize(alloc, ws),
|
.winsize => |ws| try vx.resize(alloc, ws),
|
||||||
}
|
}
|
||||||
|
@ -59,9 +64,9 @@ pub fn main() !void {
|
||||||
const img = imgs[n];
|
const img = imgs[n];
|
||||||
const dims = try img.cellSize(win);
|
const dims = try img.cellSize(win);
|
||||||
const center = vaxis.widgets.alignment.center(win, dims.cols, dims.rows);
|
const center = vaxis.widgets.alignment.center(win, dims.cols, dims.rows);
|
||||||
const scale = false;
|
try img.draw(center, .{ .scale = .contain, .clip_region = .{
|
||||||
const z_index = 0;
|
.y = clip_y,
|
||||||
img.draw(center, scale, z_index);
|
} });
|
||||||
|
|
||||||
try vx.render();
|
try vx.render();
|
||||||
}
|
}
|
||||||
|
|
130
src/Image.zig
130
src/Image.zig
|
@ -20,8 +20,7 @@ pub const Source = union(enum) {
|
||||||
|
|
||||||
pub const Placement = struct {
|
pub const Placement = struct {
|
||||||
img_id: u32,
|
img_id: u32,
|
||||||
z_index: i32,
|
options: Image.DrawOptions,
|
||||||
size: ?CellSize = null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CellSize = struct {
|
pub const CellSize = struct {
|
||||||
|
@ -29,29 +28,138 @@ pub const CellSize = struct {
|
||||||
cols: usize,
|
cols: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const DrawOptions = struct {
|
||||||
|
/// an offset into the top left cell, in pixels, with where to place the
|
||||||
|
/// origin of the image. These must be less than the pixel size of a single
|
||||||
|
/// cell
|
||||||
|
pixel_offset: ?struct {
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
} = null,
|
||||||
|
/// the vertical stacking order
|
||||||
|
/// < 0: Drawn beneath text
|
||||||
|
/// < -1_073_741_824: Drawn beneath "default" background cells
|
||||||
|
z_index: ?i32 = null,
|
||||||
|
/// A clip region of the source image to draw.
|
||||||
|
clip_region: ?struct {
|
||||||
|
x: ?usize = null,
|
||||||
|
y: ?usize = null,
|
||||||
|
width: ?usize = null,
|
||||||
|
height: ?usize = null,
|
||||||
|
} = null,
|
||||||
|
/// Scaling to apply to the Image
|
||||||
|
scale: enum {
|
||||||
|
/// no scaling applied. the image may extend beyond the window
|
||||||
|
none,
|
||||||
|
/// Stretch / shrink the image to fill the window
|
||||||
|
fill,
|
||||||
|
/// Scale the image to fit the window, maintaining aspect ratio
|
||||||
|
fit,
|
||||||
|
/// Scale the image to fit the window, only if needed.
|
||||||
|
contain,
|
||||||
|
} = .none,
|
||||||
|
/// the size to render the image. Generally you will not need to use this
|
||||||
|
/// field, and should prefer to use scale. `draw` will fill in this field with
|
||||||
|
/// the correct values if a scale method is applied.
|
||||||
|
size: ?struct {
|
||||||
|
rows: ?usize = null,
|
||||||
|
cols: ?usize = null,
|
||||||
|
} = null,
|
||||||
|
};
|
||||||
|
|
||||||
/// unique identifier for this image. This will be managed by the screen.
|
/// unique identifier for this image. This will be managed by the screen.
|
||||||
id: u32,
|
id: u32,
|
||||||
|
|
||||||
// width in pixels
|
/// width in pixels
|
||||||
width: usize,
|
width: usize,
|
||||||
// height in pixels
|
/// height in pixels
|
||||||
height: usize,
|
height: usize,
|
||||||
|
|
||||||
pub fn draw(self: Image, win: Window, scale: bool, z_index: i32) void {
|
pub fn draw(self: Image, win: Window, opts: DrawOptions) !void {
|
||||||
const p = Placement{
|
var p_opts = opts;
|
||||||
.img_id = self.id,
|
switch (opts.scale) {
|
||||||
.z_index = z_index,
|
.none => {},
|
||||||
.size = sz: {
|
.fill => {
|
||||||
if (!scale) break :sz null;
|
p_opts.size = .{
|
||||||
break :sz CellSize{
|
|
||||||
.rows = win.height,
|
.rows = win.height,
|
||||||
.cols = win.width,
|
.cols = win.width,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
.fit,
|
||||||
|
.contain,
|
||||||
|
=> contain: {
|
||||||
|
// cell geometry
|
||||||
|
const x_pix = win.screen.width_pix;
|
||||||
|
const y_pix = win.screen.height_pix;
|
||||||
|
const w = win.screen.width;
|
||||||
|
const h = win.screen.height;
|
||||||
|
|
||||||
|
const pix_per_col = try std.math.divCeil(usize, x_pix, w);
|
||||||
|
const pix_per_row = try std.math.divCeil(usize, y_pix, h);
|
||||||
|
|
||||||
|
const win_width_pix = pix_per_col * win.width;
|
||||||
|
const win_height_pix = pix_per_row * win.height;
|
||||||
|
|
||||||
|
const fit_x: bool = if (win_width_pix >= self.width) true else false;
|
||||||
|
const fit_y: bool = if (win_height_pix >= self.height) true else false;
|
||||||
|
|
||||||
|
// Does the image fit with no scaling?
|
||||||
|
if (opts.scale == .contain and fit_x and fit_y) break :contain;
|
||||||
|
|
||||||
|
// Does the image require vertical scaling?
|
||||||
|
if (fit_x and !fit_y)
|
||||||
|
p_opts.size = .{
|
||||||
|
.rows = win.height,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the image require horizontal scaling?
|
||||||
|
else if (!fit_x and fit_y)
|
||||||
|
p_opts.size = .{
|
||||||
|
.cols = win.height,
|
||||||
|
}
|
||||||
|
else if (!fit_x and !fit_y) {
|
||||||
|
const diff_x = self.width - win_width_pix;
|
||||||
|
const diff_y = self.height - win_height_pix;
|
||||||
|
// The width difference is larger than the height difference.
|
||||||
|
// Scale by width
|
||||||
|
if (diff_x > diff_y)
|
||||||
|
p_opts.size = .{
|
||||||
|
.cols = win.width,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// The height difference is larger than the width difference.
|
||||||
|
// Scale by height
|
||||||
|
p_opts.size = .{
|
||||||
|
.rows = win.height,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
std.debug.assert(opts.scale == .fit);
|
||||||
|
std.debug.assert(win_width_pix >= self.width);
|
||||||
|
std.debug.assert(win_height_pix >= self.height);
|
||||||
|
|
||||||
|
// Fits in both directions. Find the closer direction
|
||||||
|
const diff_x = win_width_pix - self.width;
|
||||||
|
const diff_y = win_height_pix - self.height;
|
||||||
|
// The width is closer in dimension. Scale by that
|
||||||
|
if (diff_x < diff_y)
|
||||||
|
p_opts.size = .{
|
||||||
|
.cols = win.width,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
p_opts.size = .{
|
||||||
|
.rows = win.height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const p = Placement{
|
||||||
|
.img_id = self.id,
|
||||||
|
.options = p_opts,
|
||||||
};
|
};
|
||||||
win.writeCell(0, 0, .{ .image = p });
|
win.writeCell(0, 0, .{ .image = p });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the size of the image, in cells
|
||||||
pub fn cellSize(self: Image, win: Window) !CellSize {
|
pub fn cellSize(self: Image, win: Window) !CellSize {
|
||||||
// cell geometry
|
// cell geometry
|
||||||
const x_pix = win.screen.width_pix;
|
const x_pix = win.screen.width_pix;
|
||||||
|
|
|
@ -366,19 +366,57 @@ pub fn render(self: *Vaxis) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell.image) |img| {
|
if (cell.image) |img| {
|
||||||
if (img.size) |size| {
|
try tty.buffered_writer.writer().print(
|
||||||
try std.fmt.format(
|
ctlseqs.kitty_graphics_preamble,
|
||||||
tty.buffered_writer.writer(),
|
.{img.img_id},
|
||||||
ctlseqs.kitty_graphics_scale,
|
|
||||||
.{ img.img_id, img.z_index, size.cols, size.rows },
|
|
||||||
);
|
);
|
||||||
} else {
|
if (img.options.pixel_offset) |offset| {
|
||||||
try std.fmt.format(
|
try tty.buffered_writer.writer().print(
|
||||||
tty.buffered_writer.writer(),
|
",X={d},Y={d}",
|
||||||
ctlseqs.kitty_graphics_place,
|
.{ offset.x, offset.y },
|
||||||
.{ img.img_id, img.z_index },
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (img.options.clip_region) |clip| {
|
||||||
|
if (clip.x) |x|
|
||||||
|
try tty.buffered_writer.writer().print(
|
||||||
|
",x={d}",
|
||||||
|
.{x},
|
||||||
|
);
|
||||||
|
if (clip.y) |y|
|
||||||
|
try tty.buffered_writer.writer().print(
|
||||||
|
",y={d}",
|
||||||
|
.{y},
|
||||||
|
);
|
||||||
|
if (clip.width) |width|
|
||||||
|
try tty.buffered_writer.writer().print(
|
||||||
|
",w={d}",
|
||||||
|
.{width},
|
||||||
|
);
|
||||||
|
if (clip.height) |height|
|
||||||
|
try tty.buffered_writer.writer().print(
|
||||||
|
",h={d}",
|
||||||
|
.{height},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (img.options.size) |size| {
|
||||||
|
if (size.rows) |rows|
|
||||||
|
try tty.buffered_writer.writer().print(
|
||||||
|
",r={d}",
|
||||||
|
.{rows},
|
||||||
|
);
|
||||||
|
if (size.cols) |cols|
|
||||||
|
try tty.buffered_writer.writer().print(
|
||||||
|
",c={d}",
|
||||||
|
.{cols},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (img.options.z_index) |z| {
|
||||||
|
try tty.buffered_writer.writer().print(
|
||||||
|
",z={d}",
|
||||||
|
.{z},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try tty.buffered_writer.writer().writeAll(ctlseqs.kitty_graphics_closing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// something is different, so let's loop through everything and
|
// something is different, so let's loop through everything and
|
||||||
|
|
|
@ -107,5 +107,5 @@ pub const osc22_mouse_shape = "\x1b]22;{s}\x1b\\";
|
||||||
|
|
||||||
// Kitty graphics
|
// Kitty graphics
|
||||||
pub const kitty_graphics_clear = "\x1b_Ga=d\x1b\\";
|
pub const kitty_graphics_clear = "\x1b_Ga=d\x1b\\";
|
||||||
pub const kitty_graphics_place = "\x1b_Ga=p,i={d},z={d},C=1\x1b\\";
|
pub const kitty_graphics_preamble = "\x1b_Ga=p,i={d}";
|
||||||
pub const kitty_graphics_scale = "\x1b_Ga=p,i={d},z={d},c={d},r={d},C=1\x1b\\";
|
pub const kitty_graphics_closing = ",C=1\x1b\\";
|
||||||
|
|
Loading…
Reference in a new issue