diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index ba7fa1240..43b602752 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -748,6 +748,11 @@ fn drawImagePlacement( @as(f32, @floatFromInt(p.source_width)), @as(f32, @floatFromInt(p.source_height)), }, + + .dest_size = .{ + @as(f32, @floatFromInt(p.width)), + @as(f32, @floatFromInt(p.height)), + }, }}); defer buf.deinit(); @@ -924,12 +929,18 @@ fn prepKittyGraphics( else image.height -| offset_y; + // Calculate the width/height of our image. + const dest_width = if (p.columns > 0) p.columns * self.cell_size.width else source_width; + const dest_height = if (p.rows > 0) p.rows * self.cell_size.height else source_height; + // Accumulate the placement if (image.width > 0 and image.height > 0) { try self.image_placements.append(self.alloc, .{ .image_id = kv.key_ptr.image_id, .x = @intCast(kv.value_ptr.point.x), .y = @intCast(viewport.y), + .width = dest_width, + .height = dest_height, .cell_offset_x = kv.value_ptr.x_offset, .cell_offset_y = kv.value_ptr.y_offset, .source_x = source_x, diff --git a/src/renderer/metal/image.zig b/src/renderer/metal/image.zig index 353dd510a..911b8356c 100644 --- a/src/renderer/metal/image.zig +++ b/src/renderer/metal/image.zig @@ -15,6 +15,10 @@ pub const Placement = struct { x: u32, y: u32, + /// The width/height of the placed image. + width: u32, + height: u32, + /// The offset in pixels from the top left of the cell. This is /// clamped to the size of a cell. cell_offset_x: u32, diff --git a/src/renderer/metal/shaders.zig b/src/renderer/metal/shaders.zig index 663be1bf9..9001ede8f 100644 --- a/src/renderer/metal/shaders.zig +++ b/src/renderer/metal/shaders.zig @@ -62,6 +62,7 @@ pub const Image = extern struct { grid_pos: [2]f32, cell_offset: [2]f32, source_rect: [4]f32, + dest_size: [2]f32, }; /// The uniforms that are passed to the terminal cell shader. @@ -360,6 +361,17 @@ fn initImagePipeline(device: objc.Object, library: objc.Object) !objc.Object { attr.setProperty("offset", @as(c_ulong, @offsetOf(Image, "source_rect"))); attr.setProperty("bufferIndex", @as(c_ulong, 0)); } + { + const attr = attrs.msgSend( + objc.Object, + objc.sel("objectAtIndexedSubscript:"), + .{@as(c_ulong, 4)}, + ); + + attr.setProperty("format", @intFromEnum(mtl.MTLVertexFormat.float2)); + attr.setProperty("offset", @as(c_ulong, @offsetOf(Image, "dest_size"))); + attr.setProperty("bufferIndex", @as(c_ulong, 0)); + } // The layout describes how and when we fetch the next vertex input. const layouts = objc.Object.fromId(desc.getProperty(?*anyopaque, "layouts")); diff --git a/src/renderer/shaders/cell.metal b/src/renderer/shaders/cell.metal index 22004f6e5..bfc68aaa4 100644 --- a/src/renderer/shaders/cell.metal +++ b/src/renderer/shaders/cell.metal @@ -201,6 +201,9 @@ struct ImageVertexIn { // The source rectangle of the texture to sample from. float4 source_rect [[ attribute(3) ]]; + + // The final width/height of the image in pixels. + float2 dest_size [[ attribute(4) ]]; }; struct ImageVertexOut { @@ -242,7 +245,7 @@ vertex ImageVertexOut image_vertex( // The position of our image starts at the top-left of the grid cell and // adds the source rect width/height components. float2 image_pos = (uniforms.cell_size * input.grid_pos) + input.cell_offset; - image_pos += input.source_rect.zw * position; + image_pos += input.dest_size * position; out.position = uniforms.projection_matrix * float4(image_pos.x, image_pos.y, 0.0f, 1.0f); out.tex_coord = tex_coord; diff --git a/src/terminal/kitty/graphics_exec.zig b/src/terminal/kitty/graphics_exec.zig index cddf63853..38c0f5817 100644 --- a/src/terminal/kitty/graphics_exec.zig +++ b/src/terminal/kitty/graphics_exec.zig @@ -176,6 +176,8 @@ fn display( .source_y = d.y, .source_width = d.width, .source_height = d.height, + .columns = d.columns, + .rows = d.rows, }; storage.addPlacement(alloc, img.id, d.placement_id, p) catch |err| { encodeError(&result, err); diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index c7baa7c75..e49cc797b 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -166,6 +166,10 @@ pub const ImageStorage = struct { source_width: u32 = 0, source_height: u32 = 0, + /// The columns/rows this image occupies. + columns: u32 = 0, + rows: u32 = 0, + /// Returns a selection of the entire rectangle this placement /// occupies within the screen. pub fn selection( @@ -173,6 +177,17 @@ pub const ImageStorage = struct { image: Image, t: *const terminal.Terminal, ) terminal.Selection { + // If we have columns/rows specified we can simplify this whole thing. + if (self.columns > 0 and self.rows > 0) { + return terminal.Selection{ + .start = self.point, + .end = .{ + .x = @min(self.point.x + self.columns, t.cols), + .y = @min(self.point.y + self.rows, t.rows), + }, + }; + } + // Calculate our cell size. const terminal_width_f64: f64 = @floatFromInt(t.width_px); const terminal_height_f64: f64 = @floatFromInt(t.height_px);