Skip to content

Commit 8d7635b

Browse files
committed
Save screenshots as PNGs
1 parent 30f3716 commit 8d7635b

2 files changed

Lines changed: 73 additions & 2 deletions

File tree

src/deecy.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,7 +1926,7 @@ fn save_screenshot_impl(self: *const @This()) !void {
19261926
const day_seconds = epoch_seconds.getDaySeconds();
19271927
const year_day = epoch_seconds.getEpochDay().calculateYearDay();
19281928
const month_day = year_day.calculateMonthDay();
1929-
const filepath = try std.fmt.allocPrint(self._allocator, "screenshots/{s}_{d:0>4}-{d:0>2}-{d:0>2}_{d:0>2}-{d:0>2}-{d:0>2}.bmp", .{
1929+
const filepath = try std.fmt.allocPrint(self._allocator, "screenshots/{s}_{d:0>4}-{d:0>2}-{d:0>2}_{d:0>2}-{d:0>2}-{d:0>2}.png", .{
19301930
self.get_product_name(),
19311931
year_day.year,
19321932
month_day.month.numeric(),
@@ -1946,7 +1946,7 @@ fn save_screenshot_impl(self: *const @This()) !void {
19461946

19471947
var buffer: [1024]u8 = undefined;
19481948
var file_writer = file.writer(self.io, &buffer);
1949-
try screen.write_bmp(&file_writer.interface);
1949+
try screen.write_png(self._allocator, &file_writer.interface);
19501950

19511951
deecy_log.info(termcolor.green("Screenshot saved as '{s}'"), .{filepath});
19521952
self.ui.notifications.push("Screenshot Saved", .{}, "Screenshot saved as '{s}.", .{filepath});

src/image.zig

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,74 @@ pub fn write_bmp(self: @This(), writer: *std.Io.Writer) !void {
3939

4040
try writer.flush();
4141
}
42+
43+
const PNGChunkWriter = struct {
44+
writer: *std.Io.Writer,
45+
crc: std.hash.Crc32 = .init(),
46+
47+
pub fn init(writer: *std.Io.Writer, data_length: u32, chunk_type: []const u8) !@This() {
48+
try writer.writeInt(u32, data_length, .big);
49+
var r = @This(){ .writer = writer };
50+
std.debug.assert(chunk_type.len == 4);
51+
try r.write(chunk_type);
52+
return r;
53+
}
54+
55+
pub fn writeInt(self: *@This(), val: u32) !void {
56+
return self.write(std.mem.asBytes(&@byteSwap(val)));
57+
}
58+
59+
pub fn write(self: *@This(), data: []const u8) !void {
60+
try self.writer.writeAll(data);
61+
self.crc.update(data);
62+
}
63+
64+
pub fn finish(self: *@This()) !void {
65+
try self.writer.writeInt(u32, self.crc.final(), .big);
66+
}
67+
};
68+
69+
pub fn write_png(self: @This(), allocator: std.mem.Allocator, writer: *std.Io.Writer) !void {
70+
var alloc_writer = try std.Io.Writer.Allocating.initCapacity(allocator, self.bgra.len);
71+
defer alloc_writer.deinit();
72+
var compress_buffer: [std.compress.flate.max_window_len]u8 = undefined;
73+
var compress = try std.compress.flate.Compress.init(&alloc_writer.writer, &compress_buffer, .zlib, .default);
74+
for (0..self.height) |y| {
75+
try compress.writer.writeByte(0); // Filter Type
76+
for (0..self.width) |x| {
77+
const bgra = self.bgra[y * self.width * 4 + 4 * x ..][0..4];
78+
try compress.writer.writeAll(&[_]u8{ bgra[2], bgra[1], bgra[0] });
79+
}
80+
}
81+
try compress.finish();
82+
83+
const compressed = try alloc_writer.toOwnedSlice();
84+
defer allocator.free(compressed);
85+
86+
// PNG Signature
87+
try writer.writeAll(&[_]u8{ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A });
88+
{
89+
var w = try PNGChunkWriter.init(writer, 13, "IHDR");
90+
try w.writeInt(self.width);
91+
try w.writeInt(self.height);
92+
try w.write(&[_]u8{
93+
8, // Bit-depth
94+
2, // Color type (2: Truecolour)
95+
0, // Compression Method
96+
0, // Filter Method
97+
0, // Interlace Method
98+
});
99+
try w.finish();
100+
}
101+
{
102+
var w = try PNGChunkWriter.init(writer, @intCast(compressed.len), "IDAT");
103+
try w.write(compressed);
104+
try w.finish();
105+
}
106+
{
107+
var w = try PNGChunkWriter.init(writer, 0, "IEND");
108+
try w.finish();
109+
}
110+
111+
try writer.flush();
112+
}

0 commit comments

Comments
 (0)