diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index e528e133fd..116b3b659f 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -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 }, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index d8aa9b92e2..1a426975d6 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -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, diff --git a/src/renderer/metal/image.zig b/src/renderer/metal/image.zig index 2693a48e1d..5675705347 100644 --- a/src/renderer/metal/image.zig +++ b/src/renderer/metal/image.zig @@ -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); diff --git a/src/renderer/opengl/image.zig b/src/renderer/opengl/image.zig index a67d8920f2..f9904d8b51 100644 --- a/src/renderer/opengl/image.zig +++ b/src/renderer/opengl/image.zig @@ -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); diff --git a/src/terminal/kitty/graphics_command.zig b/src/terminal/kitty/graphics_command.zig index cec5a2a648..9bcdc3f028 100644 --- a/src/terminal/kitty/graphics_command.zig +++ b/src/terminal/kitty/graphics_command.zig @@ -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, diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index a4941dbb27..2dd12ccc63 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -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,