mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge pull request #2104 from qwerasd205/grayscale-kitty-images
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) {
|
switch (kv.value_ptr.image) {
|
||||||
.ready => {},
|
.ready => {},
|
||||||
|
|
||||||
|
.pending_gray,
|
||||||
.pending_gray_alpha,
|
.pending_gray_alpha,
|
||||||
.pending_rgb,
|
.pending_rgb,
|
||||||
.pending_rgba,
|
.pending_rgba,
|
||||||
|
.replace_gray,
|
||||||
.replace_gray_alpha,
|
.replace_gray_alpha,
|
||||||
.replace_rgb,
|
.replace_rgb,
|
||||||
.replace_rgba,
|
.replace_rgba,
|
||||||
@ -1870,6 +1872,7 @@ fn prepKittyImage(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const new_image: Image = switch (image.format) {
|
const new_image: Image = switch (image.format) {
|
||||||
|
.gray => .{ .pending_gray = pending },
|
||||||
.gray_alpha => .{ .pending_gray_alpha = pending },
|
.gray_alpha => .{ .pending_gray_alpha = pending },
|
||||||
.rgb => .{ .pending_rgb = pending },
|
.rgb => .{ .pending_rgb = pending },
|
||||||
.rgba => .{ .pending_rgba = pending },
|
.rgba => .{ .pending_rgba = pending },
|
||||||
|
@ -1147,6 +1147,7 @@ fn prepKittyImage(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const new_image: Image = switch (image.format) {
|
const new_image: Image = switch (image.format) {
|
||||||
|
.gray => .{ .pending_gray = pending },
|
||||||
.gray_alpha => .{ .pending_gray_alpha = pending },
|
.gray_alpha => .{ .pending_gray_alpha = pending },
|
||||||
.rgb => .{ .pending_rgb = pending },
|
.rgb => .{ .pending_rgb = pending },
|
||||||
.rgba => .{ .pending_rgba = pending },
|
.rgba => .{ .pending_rgba = pending },
|
||||||
@ -2011,9 +2012,11 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void {
|
|||||||
switch (kv.value_ptr.image) {
|
switch (kv.value_ptr.image) {
|
||||||
.ready => {},
|
.ready => {},
|
||||||
|
|
||||||
|
.pending_gray,
|
||||||
.pending_gray_alpha,
|
.pending_gray_alpha,
|
||||||
.pending_rgb,
|
.pending_rgb,
|
||||||
.pending_rgba,
|
.pending_rgba,
|
||||||
|
.replace_gray,
|
||||||
.replace_gray_alpha,
|
.replace_gray_alpha,
|
||||||
.replace_rgb,
|
.replace_rgb,
|
||||||
.replace_rgba,
|
.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
|
/// This data is owned by this union so it must be freed once the
|
||||||
/// image is uploaded.
|
/// image is uploaded.
|
||||||
|
pending_gray: Pending,
|
||||||
pending_gray_alpha: Pending,
|
pending_gray_alpha: Pending,
|
||||||
pending_rgb: Pending,
|
pending_rgb: Pending,
|
||||||
pending_rgba: Pending,
|
pending_rgba: Pending,
|
||||||
|
|
||||||
/// This is the same as the pending states but there is a texture
|
/// This is the same as the pending states but there is a texture
|
||||||
/// already allocated that we want to replace.
|
/// already allocated that we want to replace.
|
||||||
|
replace_gray: Replace,
|
||||||
replace_gray_alpha: Replace,
|
replace_gray_alpha: Replace,
|
||||||
replace_rgb: Replace,
|
replace_rgb: Replace,
|
||||||
replace_rgba: Replace,
|
replace_rgba: Replace,
|
||||||
@ -90,11 +92,17 @@ pub const Image = union(enum) {
|
|||||||
|
|
||||||
pub fn deinit(self: Image, alloc: Allocator) void {
|
pub fn deinit(self: Image, alloc: Allocator) void {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
|
.pending_gray => |p| alloc.free(p.dataSlice(1)),
|
||||||
.pending_gray_alpha => |p| alloc.free(p.dataSlice(2)),
|
.pending_gray_alpha => |p| alloc.free(p.dataSlice(2)),
|
||||||
.pending_rgb => |p| alloc.free(p.dataSlice(3)),
|
.pending_rgb => |p| alloc.free(p.dataSlice(3)),
|
||||||
.pending_rgba => |p| alloc.free(p.dataSlice(4)),
|
.pending_rgba => |p| alloc.free(p.dataSlice(4)),
|
||||||
.unload_pending => |data| alloc.free(data),
|
.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| {
|
.replace_gray_alpha => |r| {
|
||||||
alloc.free(r.pending.dataSlice(2));
|
alloc.free(r.pending.dataSlice(2));
|
||||||
r.texture.msgSend(void, objc.sel("release"), .{});
|
r.texture.msgSend(void, objc.sel("release"), .{});
|
||||||
@ -130,9 +138,13 @@ pub const Image = union(enum) {
|
|||||||
=> return,
|
=> return,
|
||||||
|
|
||||||
.ready => |obj| .{ .unload_ready = obj },
|
.ready => |obj| .{ .unload_ready = obj },
|
||||||
|
.pending_gray => |p| .{ .unload_pending = p.dataSlice(1) },
|
||||||
.pending_gray_alpha => |p| .{ .unload_pending = p.dataSlice(2) },
|
.pending_gray_alpha => |p| .{ .unload_pending = p.dataSlice(2) },
|
||||||
.pending_rgb => |p| .{ .unload_pending = p.dataSlice(3) },
|
.pending_rgb => |p| .{ .unload_pending = p.dataSlice(3) },
|
||||||
.pending_rgba => |p| .{ .unload_pending = p.dataSlice(4) },
|
.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 = .{
|
.replace_gray_alpha => |r| .{ .unload_replace = .{
|
||||||
r.pending.dataSlice(2), r.texture,
|
r.pending.dataSlice(2), r.texture,
|
||||||
} },
|
} },
|
||||||
@ -160,6 +172,12 @@ pub const Image = union(enum) {
|
|||||||
const existing: objc.Object = switch (self.*) {
|
const existing: objc.Object = switch (self.*) {
|
||||||
// For pending, we can free the old data and become pending
|
// For pending, we can free the old data and become pending
|
||||||
// ourselves.
|
// ourselves.
|
||||||
|
.pending_gray => |p| {
|
||||||
|
alloc.free(p.dataSlice(1));
|
||||||
|
self.* = img;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
.pending_gray_alpha => |p| {
|
.pending_gray_alpha => |p| {
|
||||||
alloc.free(p.dataSlice(2));
|
alloc.free(p.dataSlice(2));
|
||||||
self.* = img;
|
self.* = img;
|
||||||
@ -194,6 +212,11 @@ pub const Image = union(enum) {
|
|||||||
|
|
||||||
// If we were already pending a replacement, then we free our
|
// If we were already pending a replacement, then we free our
|
||||||
// existing pending data and use the same texture.
|
// 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: {
|
.replace_gray_alpha => |r| existing: {
|
||||||
alloc.free(r.pending.dataSlice(2));
|
alloc.free(r.pending.dataSlice(2));
|
||||||
break :existing r.texture;
|
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.
|
// We now have an existing texture, so set the proper replace key.
|
||||||
self.* = switch (img) {
|
self.* = switch (img) {
|
||||||
|
.pending_gray => |p| .{ .replace_gray = .{
|
||||||
|
.texture = existing,
|
||||||
|
.pending = p,
|
||||||
|
} },
|
||||||
|
|
||||||
.pending_gray_alpha => |p| .{ .replace_gray_alpha = .{
|
.pending_gray_alpha => |p| .{ .replace_gray_alpha = .{
|
||||||
.texture = existing,
|
.texture = existing,
|
||||||
.pending = p,
|
.pending = p,
|
||||||
@ -289,7 +317,23 @@ pub const Image = union(enum) {
|
|||||||
self.* = .{ .replace_rgba = r.* };
|
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| {
|
.pending_gray_alpha => |*p| {
|
||||||
const data = p.dataSlice(2);
|
const data = p.dataSlice(2);
|
||||||
const rgba = try gaToRgba(alloc, data);
|
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 {
|
fn gaToRgba(alloc: Allocator, data: []const u8) ![]u8 {
|
||||||
const pixels = data.len / 2;
|
const pixels = data.len / 2;
|
||||||
var rgba = try alloc.alloc(u8, pixels * 4);
|
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
|
/// This data is owned by this union so it must be freed once the
|
||||||
/// image is uploaded.
|
/// image is uploaded.
|
||||||
|
pending_gray: Pending,
|
||||||
pending_gray_alpha: Pending,
|
pending_gray_alpha: Pending,
|
||||||
pending_rgb: Pending,
|
pending_rgb: Pending,
|
||||||
pending_rgba: Pending,
|
pending_rgba: Pending,
|
||||||
|
|
||||||
/// This is the same as the pending states but there is a texture
|
/// This is the same as the pending states but there is a texture
|
||||||
/// already allocated that we want to replace.
|
/// already allocated that we want to replace.
|
||||||
|
replace_gray: Replace,
|
||||||
replace_gray_alpha: Replace,
|
replace_gray_alpha: Replace,
|
||||||
replace_rgb: Replace,
|
replace_rgb: Replace,
|
||||||
replace_rgba: Replace,
|
replace_rgba: Replace,
|
||||||
@ -88,11 +90,17 @@ pub const Image = union(enum) {
|
|||||||
|
|
||||||
pub fn deinit(self: Image, alloc: Allocator) void {
|
pub fn deinit(self: Image, alloc: Allocator) void {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
|
.pending_gray => |p| alloc.free(p.dataSlice(1)),
|
||||||
.pending_gray_alpha => |p| alloc.free(p.dataSlice(2)),
|
.pending_gray_alpha => |p| alloc.free(p.dataSlice(2)),
|
||||||
.pending_rgb => |p| alloc.free(p.dataSlice(3)),
|
.pending_rgb => |p| alloc.free(p.dataSlice(3)),
|
||||||
.pending_rgba => |p| alloc.free(p.dataSlice(4)),
|
.pending_rgba => |p| alloc.free(p.dataSlice(4)),
|
||||||
.unload_pending => |data| alloc.free(data),
|
.unload_pending => |data| alloc.free(data),
|
||||||
|
|
||||||
|
.replace_gray => |r| {
|
||||||
|
alloc.free(r.pending.dataSlice(1));
|
||||||
|
r.texture.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
.replace_gray_alpha => |r| {
|
.replace_gray_alpha => |r| {
|
||||||
alloc.free(r.pending.dataSlice(2));
|
alloc.free(r.pending.dataSlice(2));
|
||||||
r.texture.destroy();
|
r.texture.destroy();
|
||||||
@ -128,9 +136,13 @@ pub const Image = union(enum) {
|
|||||||
=> return,
|
=> return,
|
||||||
|
|
||||||
.ready => |obj| .{ .unload_ready = obj },
|
.ready => |obj| .{ .unload_ready = obj },
|
||||||
|
.pending_gray => |p| .{ .unload_pending = p.dataSlice(1) },
|
||||||
.pending_gray_alpha => |p| .{ .unload_pending = p.dataSlice(2) },
|
.pending_gray_alpha => |p| .{ .unload_pending = p.dataSlice(2) },
|
||||||
.pending_rgb => |p| .{ .unload_pending = p.dataSlice(3) },
|
.pending_rgb => |p| .{ .unload_pending = p.dataSlice(3) },
|
||||||
.pending_rgba => |p| .{ .unload_pending = p.dataSlice(4) },
|
.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 = .{
|
.replace_gray_alpha => |r| .{ .unload_replace = .{
|
||||||
r.pending.dataSlice(2), r.texture,
|
r.pending.dataSlice(2), r.texture,
|
||||||
} },
|
} },
|
||||||
@ -157,6 +169,12 @@ pub const Image = union(enum) {
|
|||||||
// the self pointer directly.
|
// the self pointer directly.
|
||||||
const existing: gl.Texture = switch (self.*) {
|
const existing: gl.Texture = switch (self.*) {
|
||||||
// For pending, we can free the old data and become pending ourselves.
|
// 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| {
|
.pending_gray_alpha => |p| {
|
||||||
alloc.free(p.dataSlice(2));
|
alloc.free(p.dataSlice(2));
|
||||||
self.* = img;
|
self.* = img;
|
||||||
@ -191,6 +209,11 @@ pub const Image = union(enum) {
|
|||||||
|
|
||||||
// If we were already pending a replacement, then we free our
|
// If we were already pending a replacement, then we free our
|
||||||
// existing pending data and use the same texture.
|
// 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: {
|
.replace_gray_alpha => |r| existing: {
|
||||||
alloc.free(r.pending.dataSlice(2));
|
alloc.free(r.pending.dataSlice(2));
|
||||||
break :existing r.texture;
|
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.
|
// We now have an existing texture, so set the proper replace key.
|
||||||
self.* = switch (img) {
|
self.* = switch (img) {
|
||||||
|
.pending_gray => |p| .{ .replace_gray = .{
|
||||||
|
.texture = existing,
|
||||||
|
.pending = p,
|
||||||
|
} },
|
||||||
|
|
||||||
.pending_gray_alpha => |p| .{ .replace_gray_alpha = .{
|
.pending_gray_alpha => |p| .{ .replace_gray_alpha = .{
|
||||||
.texture = existing,
|
.texture = existing,
|
||||||
.pending = p,
|
.pending = p,
|
||||||
@ -246,6 +274,7 @@ pub const Image = union(enum) {
|
|||||||
=> true,
|
=> true,
|
||||||
|
|
||||||
.ready,
|
.ready,
|
||||||
|
.pending_gray,
|
||||||
.pending_gray_alpha,
|
.pending_gray_alpha,
|
||||||
.pending_rgb,
|
.pending_rgb,
|
||||||
.pending_rgba,
|
.pending_rgba,
|
||||||
@ -287,7 +316,23 @@ pub const Image = union(enum) {
|
|||||||
self.* = .{ .replace_rgba = r.* };
|
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| {
|
.pending_gray_alpha => |*p| {
|
||||||
const data = p.dataSlice(2);
|
const data = p.dataSlice(2);
|
||||||
const rgba = try gaToRgba(alloc, data);
|
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 {
|
fn gaToRgba(alloc: Allocator, data: []const u8) ![]u8 {
|
||||||
const pixels = data.len / 2;
|
const pixels = data.len / 2;
|
||||||
var rgba = try alloc.alloc(u8, pixels * 4);
|
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
|
// but they are formats that a png may decode to that we
|
||||||
// support.
|
// support.
|
||||||
gray_alpha,
|
gray_alpha,
|
||||||
|
gray,
|
||||||
|
|
||||||
pub fn bpp(self: Format) u8 {
|
pub fn bpp(self: Format) u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
|
.gray => 1,
|
||||||
.gray_alpha => 2,
|
.gray_alpha => 2,
|
||||||
.rgb => 3,
|
.rgb => 3,
|
||||||
.rgba => 4,
|
.rgba => 4,
|
||||||
|
@ -145,7 +145,7 @@ pub const LoadingImage = struct {
|
|||||||
.png => stat_size,
|
.png => stat_size,
|
||||||
|
|
||||||
// For these formats we have a size we must have.
|
// 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();
|
const bpp = f.bpp();
|
||||||
break :size self.image.width * self.image.height * bpp;
|
break :size self.image.width * self.image.height * bpp;
|
||||||
},
|
},
|
||||||
@ -432,7 +432,7 @@ pub const LoadingImage = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate our bpp
|
// Validate our bpp
|
||||||
if (bpp < 2 or bpp > 4) {
|
if (bpp < 1 or bpp > 4) {
|
||||||
log.warn("png with unsupported bpp={}", .{bpp});
|
log.warn("png with unsupported bpp={}", .{bpp});
|
||||||
return error.UnsupportedDepth;
|
return error.UnsupportedDepth;
|
||||||
}
|
}
|
||||||
@ -447,6 +447,7 @@ pub const LoadingImage = struct {
|
|||||||
self.image.width = @intCast(width);
|
self.image.width = @intCast(width);
|
||||||
self.image.height = @intCast(height);
|
self.image.height = @intCast(height);
|
||||||
self.image.format = switch (bpp) {
|
self.image.format = switch (bpp) {
|
||||||
|
1 => .gray,
|
||||||
2 => .gray_alpha,
|
2 => .gray_alpha,
|
||||||
3 => .rgb,
|
3 => .rgb,
|
||||||
4 => .rgba,
|
4 => .rgba,
|
||||||
|
Reference in New Issue
Block a user