Something went wrong. Try again.
An extremely basic PPM viewer in Zig + Vulkan
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116//! By convention, root.zig is the root source file when making a package.const std = @import("std");const debug = std.debug;const Io = std.Io;const default_header = PPMHeader{ .width = 100, .height = 100, .colors = 0 };const endian = @import("builtin").target.cpu.arch.endian();
// https://netpbm.sourceforge.net/doc/ppm.htmlpub const PPMHeader = struct { width: u32, height: u32, colors: u16 };pub const Pixel = struct { r: u8, g: u8, b: u8 };
pub fn readHeader(reader: *std.Io.Reader) !PPMHeader { const p3 = try reader.takeDelimiter('\n') orelse return default_header;
if (!std.mem.eql(u8, p3, "P3")) { return default_header; }
std.debug.print("Got identifier: {s}\n", .{p3}); const witdth_x_height_str = try reader.takeDelimiter('\n') orelse return default_header; const size = std.mem.cut(u8, witdth_x_height_str, " ") orelse return default_header; if (size.@"0".len == 0 or size.@"1".len == 0) { return default_header; } debug.print("Image Size: {s}: {s}\n", .{ size.@"0", size.@"1" }); const width = try std.fmt.parseInt(u32, size.@"0", 10); const height = try std.fmt.parseInt(u32, size.@"1", 10);
const colors_str = try reader.takeDelimiter('\n') orelse return default_header; const colors = try std.fmt.parseInt(u16, colors_str, 10); debug.print("Image Colors: {d}\n", .{colors});
return PPMHeader{ .width = width, .height = height, .colors = colors, };}
pub fn readBody(list: *std.ArrayList(Pixel), reader: *std.Io.Reader) !void { while (reader.takeDelimiter('\n')) |result| if (result) |line| { var rgb: [3]u8 = undefined; parseLine(line, &rgb) catch return; list.appendAssumeCapacity(Pixel{ .r = rgb[0], .g = rgb[1], .b = rgb[2] }); } else break else |err| switch (err) { error.ReadFailed => { debug.print("Reading file failed!", .{}); }, error.StreamTooLong => { debug.print("Stream too long!!", .{}); }, }}
pub fn parseLine(line: []u8, out: *[3]u8) !void { var iter = std.mem.splitScalar(u8, line, ' '); var i: u8 = 0; while (iter.next()) |byte| : (i += 1) { const int_byte = try std.fmt.parseInt(u8, byte, 10); out[i] = int_byte; }}
test "readHeader" { var reader: std.Io.Reader = .fixed("P3\n1200 675\n255\n");
var header = try readHeader(&reader); var expected = PPMHeader{ .width = 1200, .height = 675, .colors = 255 };
try std.testing.expect(std.meta.eql(expected, header));
reader = .fixed("");
header = try readHeader(&reader); expected = PPMHeader{ .width = 100, .height = 100, .colors = 0 };
try std.testing.expect(std.meta.eql(expected, header));
// Partial, but still broken reader = .fixed("P3");
header = try readHeader(&reader); expected = PPMHeader{ .width = 100, .height = 100, .colors = 0 };
try std.testing.expect(std.meta.eql(expected, header));
// Partial, but still broken reader = .fixed("P3\n 0 \n");
header = try readHeader(&reader); expected = PPMHeader{ .width = 100, .height = 100, .colors = 0 };
try std.testing.expect(std.meta.eql(expected, header));}
test "readBody" { const gpa = std.testing.allocator; var reader: std.Io.Reader = .fixed( \\84 95 53 \\84 95 54 \\87 102 57 ); var pixel_list: std.ArrayList(Pixel) = try .initCapacity(gpa, 10); defer pixel_list.deinit(gpa); try readBody(&pixel_list, &reader);
const expected: []const Pixel = &.{ .{ .r = 84, .g = 95, .b = 53 }, .{ .r = 84, .g = 95, .b = 54 }, .{ .r = 87, .g = 102, .b = 57 } };
debug.print("Pixels: {any}\n", .{pixel_list.items}); debug.print("Expect: {any}\n", .{expected});
for (pixel_list.items, expected) |p1, p2| { try std.testing.expect(std.meta.eql(p1, p2)); }}