mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-06-02 21:48:38 +03:00
Fix aspect ratio when rendering images with kitty protocol (#6675)
Fixes https://github.com/ghostty-org/ghostty/issues/6673 <img width="914" alt="image" src="https://github.com/user-attachments/assets/010a1304-0d46-46ec-9a82-87a8d8fbea1b" />
This commit is contained in:
@ -2032,13 +2032,16 @@ fn prepKittyPlacement(
|
||||
break :offset_y @intCast(offset_pixels);
|
||||
} else 0;
|
||||
|
||||
// Get the grid size that respects aspect ratio
|
||||
const grid_size = p.gridSize(image.*, t);
|
||||
|
||||
// If we specify `rows` then our offset above is in viewport space
|
||||
// and not in the coordinate space of the source image. Without `rows`
|
||||
// that's one and the same.
|
||||
const source_offset_y: u32 = if (p.rows > 0) source_offset_y: {
|
||||
const source_offset_y: u32 = if (grid_size.rows > 0) source_offset_y: {
|
||||
// Determine the scale factor to apply for this row height.
|
||||
const image_height: f64 = @floatFromInt(image.height);
|
||||
const viewport_height: f64 = @floatFromInt(p.rows * self.grid_metrics.cell_height);
|
||||
const viewport_height: f64 = @floatFromInt(grid_size.rows * self.grid_metrics.cell_height);
|
||||
const scale: f64 = image_height / viewport_height;
|
||||
|
||||
// Apply the scale to the offset
|
||||
@ -2071,11 +2074,11 @@ fn prepKittyPlacement(
|
||||
image.height -| source_y;
|
||||
|
||||
// Calculate the width/height of our image.
|
||||
const dest_width = if (p.columns > 0) p.columns * self.grid_metrics.cell_width else source_width;
|
||||
const dest_height = if (p.rows > 0) rows: {
|
||||
const dest_width = grid_size.cols * self.grid_metrics.cell_width;
|
||||
const dest_height = if (grid_size.rows > 0) rows: {
|
||||
// Clip to the viewport to handle scrolling. offset_y is already in
|
||||
// viewport scale so we can subtract it directly.
|
||||
break :rows (p.rows * self.grid_metrics.cell_height) - offset_y;
|
||||
break :rows (grid_size.rows * self.grid_metrics.cell_height) - offset_y;
|
||||
} else source_height;
|
||||
|
||||
// Accumulate the placement
|
||||
|
@ -1073,13 +1073,16 @@ fn prepKittyPlacement(
|
||||
break :offset_y @intCast(offset_pixels);
|
||||
} else 0;
|
||||
|
||||
// Get the grid size that respects aspect ratio
|
||||
const grid_size = p.gridSize(image.*, t);
|
||||
|
||||
// If we specify `rows` then our offset above is in viewport space
|
||||
// and not in the coordinate space of the source image. Without `rows`
|
||||
// that's one and the same.
|
||||
const source_offset_y: u32 = if (p.rows > 0) source_offset_y: {
|
||||
const source_offset_y: u32 = if (grid_size.rows > 0) source_offset_y: {
|
||||
// Determine the scale factor to apply for this row height.
|
||||
const image_height: f64 = @floatFromInt(image.height);
|
||||
const viewport_height: f64 = @floatFromInt(p.rows * self.grid_metrics.cell_height);
|
||||
const viewport_height: f64 = @floatFromInt(grid_size.rows * self.grid_metrics.cell_height);
|
||||
const scale: f64 = image_height / viewport_height;
|
||||
|
||||
// Apply the scale to the offset
|
||||
@ -1112,11 +1115,11 @@ fn prepKittyPlacement(
|
||||
image.height -| source_y;
|
||||
|
||||
// Calculate the width/height of our image.
|
||||
const dest_width = if (p.columns > 0) p.columns * self.grid_metrics.cell_width else source_width;
|
||||
const dest_height = if (p.rows > 0) rows: {
|
||||
const dest_width = grid_size.cols * self.grid_metrics.cell_width;
|
||||
const dest_height = if (grid_size.rows > 0) rows: {
|
||||
// Clip to the viewport to handle scrolling. offset_y is already in
|
||||
// viewport scale so we can subtract it directly.
|
||||
break :rows (p.rows * self.grid_metrics.cell_height) - offset_y;
|
||||
break :rows (grid_size.rows * self.grid_metrics.cell_height) - offset_y;
|
||||
} else source_height;
|
||||
|
||||
// Accumulate the placement
|
||||
|
@ -687,6 +687,33 @@ pub const ImageStorage = struct {
|
||||
// Calculate our image size in grid cells
|
||||
const width_f64: f64 = @floatFromInt(width_px);
|
||||
const height_f64: f64 = @floatFromInt(height_px);
|
||||
|
||||
// If only columns is specified, calculate rows based on aspect ratio
|
||||
if (self.columns > 0 and self.rows == 0) {
|
||||
const cols_f64: f64 = @floatFromInt(self.columns);
|
||||
const cols_px = cols_f64 * cell_width_f64;
|
||||
const aspect_ratio = height_f64 / width_f64;
|
||||
const rows_px = cols_px * aspect_ratio;
|
||||
const rows_cells = rows_px / cell_height_f64;
|
||||
return .{
|
||||
.cols = self.columns,
|
||||
.rows = @intFromFloat(@ceil(rows_cells)),
|
||||
};
|
||||
}
|
||||
|
||||
// If only rows is specified, calculate columns based on aspect ratio
|
||||
if (self.rows > 0 and self.columns == 0) {
|
||||
const rows_f64: f64 = @floatFromInt(self.rows);
|
||||
const rows_px = rows_f64 * cell_height_f64;
|
||||
const aspect_ratio = width_f64 / height_f64;
|
||||
const cols_px = rows_px * aspect_ratio;
|
||||
const cols_cells = cols_px / cell_width_f64;
|
||||
return .{
|
||||
.cols = @intFromFloat(@ceil(cols_cells)),
|
||||
.rows = self.rows,
|
||||
};
|
||||
}
|
||||
|
||||
const width_cells: u32 = @intFromFloat(@ceil(width_f64 / cell_width_f64));
|
||||
const height_cells: u32 = @intFromFloat(@ceil(height_f64 / cell_height_f64));
|
||||
|
||||
@ -1235,3 +1262,43 @@ test "storage: delete images by range 4" {
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: aspect ratio calculation when only columns or rows specified" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var t = try terminal.Terminal.init(alloc, .{ .cols = 100, .rows = 100 });
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
|
||||
// Case 1: Only columns specified
|
||||
{
|
||||
const image = Image{ .id = 1, .width = 4, .height = 2 };
|
||||
var placement = ImageStorage.Placement{
|
||||
.location = .{ .virtual = {} },
|
||||
.columns = 6,
|
||||
.rows = 0,
|
||||
};
|
||||
|
||||
const grid_size = placement.gridSize(image, &t);
|
||||
// 6 columns * (2/4) = 3 rows
|
||||
try testing.expectEqual(@as(u32, 6), grid_size.cols);
|
||||
try testing.expectEqual(@as(u32, 3), grid_size.rows);
|
||||
}
|
||||
|
||||
// Case 2: Only rows specified
|
||||
{
|
||||
const image = Image{ .id = 2, .width = 2, .height = 4 };
|
||||
var placement = ImageStorage.Placement{
|
||||
.location = .{ .virtual = {} },
|
||||
.columns = 0,
|
||||
.rows = 6,
|
||||
};
|
||||
|
||||
const grid_size = placement.gridSize(image, &t);
|
||||
// 6 rows * (2/4) = 3 columns
|
||||
try testing.expectEqual(@as(u32, 3), grid_size.cols);
|
||||
try testing.expectEqual(@as(u32, 6), grid_size.rows);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user