mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
kitty graphics: support loading 1 channel grayscale images
This commit is contained in:
@ -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,
|
||||
@ -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 },
|
||||
|
@ -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 },
|
||||
@ -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,
|
||||
|
@ -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,
|
||||
@ -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"), .{});
|
||||
@ -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,
|
||||
} },
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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,
|
||||
@ -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);
|
||||
@ -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);
|
||||
|
@ -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,
|
||||
@ -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();
|
||||
@ -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,
|
||||
} },
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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,
|
||||
@ -246,6 +274,7 @@ pub const Image = union(enum) {
|
||||
=> true,
|
||||
|
||||
.ready,
|
||||
.pending_gray,
|
||||
.pending_gray_alpha,
|
||||
.pending_rgb,
|
||||
.pending_rgba,
|
||||
@ -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);
|
||||
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
},
|
||||
@ -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;
|
||||
}
|
||||
@ -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,
|
||||
|
Reference in New Issue
Block a user