Skip to content

Commit

Permalink
kitty graphics: support loading 1 channel grayscale images
Browse files Browse the repository at this point in the history
  • Loading branch information
qwerasd205 committed Aug 16, 2024
1 parent da8e4c7 commit 37872af
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/renderer/Metal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1051,9 +1051,11 @@ pub fn updateFrame(
switch (kv.value_ptr.image) {
.ready => {},

.pending_gray,
.pending_gray_alpha,
.pending_rgb,
.pending_rgba,
.replace_gray,
.replace_gray_alpha,
.replace_rgb,
.replace_rgba,
Expand Down Expand Up @@ -1870,6 +1872,7 @@ fn prepKittyImage(
};

const new_image: Image = switch (image.format) {
.gray => .{ .pending_gray = pending },
.gray_alpha => .{ .pending_gray_alpha = pending },
.rgb => .{ .pending_rgb = pending },
.rgba => .{ .pending_rgba = pending },
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/OpenGL.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,7 @@ fn prepKittyImage(
};

const new_image: Image = switch (image.format) {
.gray => .{ .pending_gray = pending },
.gray_alpha => .{ .pending_gray_alpha = pending },
.rgb => .{ .pending_rgb = pending },
.rgba => .{ .pending_rgba = pending },
Expand Down Expand Up @@ -2011,9 +2012,11 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void {
switch (kv.value_ptr.image) {
.ready => {},

.pending_gray,
.pending_gray_alpha,
.pending_rgb,
.pending_rgba,
.replace_gray,
.replace_gray_alpha,
.replace_rgb,
.replace_rgba,
Expand Down
62 changes: 61 additions & 1 deletion src/renderer/metal/image.zig
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ pub const Image = union(enum) {
///
/// This data is owned by this union so it must be freed once the
/// image is uploaded.
pending_gray: Pending,
pending_gray_alpha: Pending,
pending_rgb: Pending,
pending_rgba: Pending,

/// This is the same as the pending states but there is a texture
/// already allocated that we want to replace.
replace_gray: Replace,
replace_gray_alpha: Replace,
replace_rgb: Replace,
replace_rgba: Replace,
Expand Down Expand Up @@ -90,11 +92,17 @@ pub const Image = union(enum) {

pub fn deinit(self: Image, alloc: Allocator) void {
switch (self) {
.pending_gray => |p| alloc.free(p.dataSlice(1)),
.pending_gray_alpha => |p| alloc.free(p.dataSlice(2)),
.pending_rgb => |p| alloc.free(p.dataSlice(3)),
.pending_rgba => |p| alloc.free(p.dataSlice(4)),
.unload_pending => |data| alloc.free(data),

.replace_gray => |r| {
alloc.free(r.pending.dataSlice(1));
r.texture.msgSend(void, objc.sel("release"), .{});
},

.replace_gray_alpha => |r| {
alloc.free(r.pending.dataSlice(2));
r.texture.msgSend(void, objc.sel("release"), .{});
Expand Down Expand Up @@ -130,9 +138,13 @@ pub const Image = union(enum) {
=> return,

.ready => |obj| .{ .unload_ready = obj },
.pending_gray => |p| .{ .unload_pending = p.dataSlice(1) },
.pending_gray_alpha => |p| .{ .unload_pending = p.dataSlice(2) },
.pending_rgb => |p| .{ .unload_pending = p.dataSlice(3) },
.pending_rgba => |p| .{ .unload_pending = p.dataSlice(4) },
.replace_gray => |r| .{ .unload_replace = .{
r.pending.dataSlice(1), r.texture,
} },
.replace_gray_alpha => |r| .{ .unload_replace = .{
r.pending.dataSlice(2), r.texture,
} },
Expand Down Expand Up @@ -160,6 +172,12 @@ pub const Image = union(enum) {
const existing: objc.Object = switch (self.*) {
// For pending, we can free the old data and become pending
// ourselves.
.pending_gray => |p| {
alloc.free(p.dataSlice(1));
self.* = img;
return;
},

.pending_gray_alpha => |p| {
alloc.free(p.dataSlice(2));
self.* = img;
Expand Down Expand Up @@ -194,6 +212,11 @@ pub const Image = union(enum) {

// If we were already pending a replacement, then we free our
// existing pending data and use the same texture.
.replace_gray => |r| existing: {
alloc.free(r.pending.dataSlice(1));
break :existing r.texture;
},

.replace_gray_alpha => |r| existing: {
alloc.free(r.pending.dataSlice(2));
break :existing r.texture;
Expand All @@ -217,6 +240,11 @@ pub const Image = union(enum) {

// We now have an existing texture, so set the proper replace key.
self.* = switch (img) {
.pending_gray => |p| .{ .replace_gray = .{
.texture = existing,
.pending = p,
} },

.pending_gray_alpha => |p| .{ .replace_gray_alpha = .{
.texture = existing,
.pending = p,
Expand Down Expand Up @@ -289,7 +317,23 @@ pub const Image = union(enum) {
self.* = .{ .replace_rgba = r.* };
},

// GA needs to be converted to RGBA, too.
// Gray and Gray+Alpha need to be converted to RGBA, too.
.pending_gray => |*p| {
const data = p.dataSlice(1);
const rgba = try grayToRgba(alloc, data);
alloc.free(data);
p.data = rgba.ptr;
self.* = .{ .pending_rgba = p.* };
},

.replace_gray => |*r| {
const data = r.pending.dataSlice(2);
const rgba = try grayToRgba(alloc, data);
alloc.free(data);
r.pending.data = rgba.ptr;
self.* = .{ .replace_rgba = r.* };
},

.pending_gray_alpha => |*p| {
const data = p.dataSlice(2);
const rgba = try gaToRgba(alloc, data);
Expand All @@ -308,6 +352,22 @@ pub const Image = union(enum) {
}
}

fn grayToRgba(alloc: Allocator, data: []const u8) ![]u8 {
const pixels = data.len;
var rgba = try alloc.alloc(u8, pixels * 4);
errdefer alloc.free(rgba);
var i: usize = 0;
while (i < pixels) : (i += 1) {
const rgba_i = i * 4;
rgba[rgba_i] = data[i];
rgba[rgba_i + 1] = data[i];
rgba[rgba_i + 2] = data[i];
rgba[rgba_i + 3] = 255;
}

return rgba;
}

fn gaToRgba(alloc: Allocator, data: []const u8) ![]u8 {
const pixels = data.len / 2;
var rgba = try alloc.alloc(u8, pixels * 4);
Expand Down
63 changes: 62 additions & 1 deletion src/renderer/opengl/image.zig
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ pub const Image = union(enum) {
///
/// This data is owned by this union so it must be freed once the
/// image is uploaded.
pending_gray: Pending,
pending_gray_alpha: Pending,
pending_rgb: Pending,
pending_rgba: Pending,

/// This is the same as the pending states but there is a texture
/// already allocated that we want to replace.
replace_gray: Replace,
replace_gray_alpha: Replace,
replace_rgb: Replace,
replace_rgba: Replace,
Expand Down Expand Up @@ -88,11 +90,17 @@ pub const Image = union(enum) {

pub fn deinit(self: Image, alloc: Allocator) void {
switch (self) {
.pending_gray => |p| alloc.free(p.dataSlice(1)),
.pending_gray_alpha => |p| alloc.free(p.dataSlice(2)),
.pending_rgb => |p| alloc.free(p.dataSlice(3)),
.pending_rgba => |p| alloc.free(p.dataSlice(4)),
.unload_pending => |data| alloc.free(data),

.replace_gray => |r| {
alloc.free(r.pending.dataSlice(1));
r.texture.destroy();
},

.replace_gray_alpha => |r| {
alloc.free(r.pending.dataSlice(2));
r.texture.destroy();
Expand Down Expand Up @@ -128,9 +136,13 @@ pub const Image = union(enum) {
=> return,

.ready => |obj| .{ .unload_ready = obj },
.pending_gray => |p| .{ .unload_pending = p.dataSlice(1) },
.pending_gray_alpha => |p| .{ .unload_pending = p.dataSlice(2) },
.pending_rgb => |p| .{ .unload_pending = p.dataSlice(3) },
.pending_rgba => |p| .{ .unload_pending = p.dataSlice(4) },
.replace_gray => |r| .{ .unload_replace = .{
r.pending.dataSlice(1), r.texture,
} },
.replace_gray_alpha => |r| .{ .unload_replace = .{
r.pending.dataSlice(2), r.texture,
} },
Expand All @@ -157,6 +169,12 @@ pub const Image = union(enum) {
// the self pointer directly.
const existing: gl.Texture = switch (self.*) {
// For pending, we can free the old data and become pending ourselves.
.pending_gray => |p| {
alloc.free(p.dataSlice(1));
self.* = img;
return;
},

.pending_gray_alpha => |p| {
alloc.free(p.dataSlice(2));
self.* = img;
Expand Down Expand Up @@ -191,6 +209,11 @@ pub const Image = union(enum) {

// If we were already pending a replacement, then we free our
// existing pending data and use the same texture.
.replace_gray => |r| existing: {
alloc.free(r.pending.dataSlice(1));
break :existing r.texture;
},

.replace_gray_alpha => |r| existing: {
alloc.free(r.pending.dataSlice(2));
break :existing r.texture;
Expand All @@ -214,6 +237,11 @@ pub const Image = union(enum) {

// We now have an existing texture, so set the proper replace key.
self.* = switch (img) {
.pending_gray => |p| .{ .replace_gray = .{
.texture = existing,
.pending = p,
} },

.pending_gray_alpha => |p| .{ .replace_gray_alpha = .{
.texture = existing,
.pending = p,
Expand Down Expand Up @@ -246,6 +274,7 @@ pub const Image = union(enum) {
=> true,

.ready,
.pending_gray,
.pending_gray_alpha,
.pending_rgb,
.pending_rgba,
Expand Down Expand Up @@ -287,7 +316,23 @@ pub const Image = union(enum) {
self.* = .{ .replace_rgba = r.* };
},

// GA needs to be converted to RGBA, too.
// Gray and Gray+Alpha need to be converted to RGBA, too.
.pending_gray => |*p| {
const data = p.dataSlice(1);
const rgba = try grayToRgba(alloc, data);
alloc.free(data);
p.data = rgba.ptr;
self.* = .{ .pending_rgba = p.* };
},

.replace_gray => |*r| {
const data = r.pending.dataSlice(2);
const rgba = try grayToRgba(alloc, data);
alloc.free(data);
r.pending.data = rgba.ptr;
self.* = .{ .replace_rgba = r.* };
},

.pending_gray_alpha => |*p| {
const data = p.dataSlice(2);
const rgba = try gaToRgba(alloc, data);
Expand All @@ -306,6 +351,22 @@ pub const Image = union(enum) {
}
}

fn grayToRgba(alloc: Allocator, data: []const u8) ![]u8 {
const pixels = data.len;
var rgba = try alloc.alloc(u8, pixels * 4);
errdefer alloc.free(rgba);
var i: usize = 0;
while (i < pixels) : (i += 1) {
const rgba_i = i * 4;
rgba[rgba_i] = data[i];
rgba[rgba_i + 1] = data[i];
rgba[rgba_i + 2] = data[i];
rgba[rgba_i + 3] = 255;
}

return rgba;
}

fn gaToRgba(alloc: Allocator, data: []const u8) ![]u8 {
const pixels = data.len / 2;
var rgba = try alloc.alloc(u8, pixels * 4);
Expand Down
2 changes: 2 additions & 0 deletions src/terminal/kitty/graphics_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,11 @@ pub const Transmission = struct {
// but they are formats that a png may decode to that we
// support.
gray_alpha,
gray,

pub fn bpp(self: Format) u8 {
return switch (self) {
.gray => 1,
.gray_alpha => 2,
.rgb => 3,
.rgba => 4,
Expand Down
5 changes: 3 additions & 2 deletions src/terminal/kitty/graphics_image.zig
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ pub const LoadingImage = struct {
.png => stat_size,

// For these formats we have a size we must have.
.gray_alpha, .rgb, .rgba => |f| size: {
.gray, .gray_alpha, .rgb, .rgba => |f| size: {
const bpp = f.bpp();
break :size self.image.width * self.image.height * bpp;
},
Expand Down Expand Up @@ -432,7 +432,7 @@ pub const LoadingImage = struct {
}

// Validate our bpp
if (bpp < 2 or bpp > 4) {
if (bpp < 1 or bpp > 4) {
log.warn("png with unsupported bpp={}", .{bpp});
return error.UnsupportedDepth;
}
Expand All @@ -447,6 +447,7 @@ pub const LoadingImage = struct {
self.image.width = @intCast(width);
self.image.height = @intCast(height);
self.image.format = switch (bpp) {
1 => .gray,
2 => .gray_alpha,
3 => .rgb,
4 => .rgba,
Expand Down

0 comments on commit 37872af

Please sign in to comment.