diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 77b46264c..a0c4eabac 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const command = @import("graphics_command.zig"); +const point = @import("../point.zig"); const internal_os = @import("../../os/main.zig"); const stb = @import("../../stb/main.zig"); @@ -382,6 +383,22 @@ pub const Image = struct { } }; +/// The rect taken up by some image placement, in grid cells. This will +/// be rounded up to the nearest grid cell since we can't place images +/// in partial grid cells. +pub const Rect = struct { + top_left: point.ScreenPoint = .{}, + bottom_right: point.ScreenPoint = .{}, + + /// True if the rect contains a given screen point. + pub fn contains(self: Rect, p: point.ScreenPoint) bool { + return p.y >= self.top_left.y and + p.y <= self.bottom_right.y and + p.x >= self.top_left.x and + p.x <= self.bottom_right.x; + } +}; + /// Easy base64 encoding function. fn testB64(alloc: Allocator, data: []const u8) ![]const u8 { const B64Encoder = std.base64.standard.Encoder; diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index 7b17d38c7..4b8a4b561 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -9,6 +9,7 @@ const command = @import("graphics_command.zig"); const Screen = @import("../Screen.zig"); const LoadingImage = @import("graphics_image.zig").LoadingImage; const Image = @import("graphics_image.zig").Image; +const Rect = @import("graphics_image.zig").Rect; const Command = command.Command; const ScreenPoint = point.ScreenPoint; @@ -218,8 +219,8 @@ pub const ImageStorage = struct { var it = self.placements.iterator(); while (it.next()) |entry| { const img = self.imageById(entry.key_ptr.image_id) orelse continue; - const sel = entry.value_ptr.selection(img, t); - if (sel.contains(p)) { + const rect = entry.value_ptr.rect(img, t); + if (rect.contains(p)) { if (filter) |f| if (!f(filter_ctx, entry.value_ptr.*)) continue; self.placements.removeByPtr(entry.key_ptr); if (delete_unused) self.deleteIfUnused(alloc, img.id); @@ -256,6 +257,51 @@ pub const ImageStorage = struct { /// The z-index for this placement. z: i32 = 0, + /// Returns a selection of the entire rectangle this placement + /// occupies within the screen. + pub fn rect( + self: Placement, + image: Image, + t: *const terminal.Terminal, + ) Rect { + // If we have columns/rows specified we can simplify this whole thing. + if (self.columns > 0 and self.rows > 0) { + return .{ + .top_left = self.point, + .bottom_right = .{ + .x = @min(self.point.x + self.columns, t.cols - 1), + .y = self.point.y + self.rows, + }, + }; + } + + // Calculate our cell size. + const terminal_width_f64: f64 = @floatFromInt(t.width_px); + const terminal_height_f64: f64 = @floatFromInt(t.height_px); + const grid_columns_f64: f64 = @floatFromInt(t.cols); + const grid_rows_f64: f64 = @floatFromInt(t.rows); + const cell_width_f64 = terminal_width_f64 / grid_columns_f64; + const cell_height_f64 = terminal_height_f64 / grid_rows_f64; + + // Our image width + const width_px = if (self.source_width > 0) self.source_width else image.width; + const height_px = if (self.source_height > 0) self.source_height else image.height; + + // Calculate our image size in grid cells + const width_f64: f64 = @floatFromInt(width_px); + const height_f64: f64 = @floatFromInt(height_px); + const width_cells: u32 = @intFromFloat(@ceil(width_f64 / cell_width_f64)); + const height_cells: u32 = @intFromFloat(@ceil(height_f64 / cell_height_f64)); + + return .{ + .top_left = self.point, + .bottom_right = .{ + .x = @min(self.point.x + width_cells, t.cols - 1), + .y = self.point.y + height_cells, + }, + }; + } + /// Returns a selection of the entire rectangle this placement /// occupies within the screen. pub fn selection(