Something went wrong. Try again.
An extremely basic PPM viewer in Zig + Vulkan
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138const std = @import("std");const debug = std.debug;const ppm_view = @import("ppmview");const knots = @import("knots");const Canvas = knots.component.Canvas;
var app_width: u32 = 800;var app_height: u32 = 600;
const Context = struct { app: knots.App, filename: [:0]const u8, file_data: std.ArrayList(ppm_view.Pixel), is_drawn: bool,};
pub fn main(init: std.process.Init) !void { // Read the file: // stat the file to get the length // allocate a buffer of that length // read the bytes into the buffer const gpa = init.gpa; const io = init.io;
var iterator = try init.minimal.args.iterateAllocator(gpa); defer iterator.deinit();
const read_filename = readFileName(&iterator);
if (read_filename) |filename| { debug.print("Opening file: {s}\n", .{filename}); const cwd = std.Io.Dir.cwd(); const file = try std.Io.Dir.openFile(cwd, io, filename, .{ .mode = .read_only }); defer file.close(io);
var ppm_buffer: [1024]u8 = undefined; var file_reader = file.reader(io, &ppm_buffer); const reader = &file_reader.interface;
const ppm_header = ppm_view.readHeader(reader) catch return; debug.print("Header: {}\n", .{ppm_header}); app_width = ppm_header.width; app_height = ppm_header.height;
const window_title = try std.mem.concat(gpa, u8, &.{ "PPMView - ", filename }); defer gpa.free(window_title);
var ppm_data: std.ArrayList(ppm_view.Pixel) = try .initCapacity(gpa, app_width * app_height); defer ppm_data.deinit(gpa); try ppm_view.readBody(&ppm_data, reader);
const app = try knots.App.init(init.io, init.gpa, .{ .window = .{ .width = app_width, .height = app_height, .title = window_title, }, });
var ctx = Context{ .app = app, .filename = filename, .file_data = ppm_data, .is_drawn = false, };
defer ctx.app.deinit();
try ctx.app.start(frameCb); }}
fn frameCb(app: *knots.App) !void { const size = app.viewport.window.getSize(); return app.e(.{ knots.component.Rect{ .width = .fixed(@floatFromInt(size.width)), .height = .fixed(@floatFromInt(size.height)), .padding = .init(16, 16, 16, 16), .dir = .column, .key = .src(@src()), }, Canvas{ .width = .fixed(@floatFromInt(size.width)), .height = .fixed(@floatFromInt(size.height)), .key = .src(@src()), .onDraw = onDraw, }, });}
fn onDraw(app: *knots.App, painter: *Canvas.Painter) !void { const ctx: *Context = @fieldParentPtr("app", app); const ppm_data = ctx.file_data; const size = app.viewport.window.getSize();
for (0..size.height) |i| { for (0..size.width) |j| { const pixel = ppm_data.items[j + (i * size.width)]; const f_x: f32 = @floatFromInt(j); const f_y: f32 = @floatFromInt(i); const r: f32 = srgbToLinear(@as(f32, @floatFromInt(pixel.r)) / 255.0); const g: f32 = srgbToLinear(@as(f32, @floatFromInt(pixel.g)) / 255.0); const b: f32 = srgbToLinear(@as(f32, @floatFromInt(pixel.b)) / 255.0); //debug.print("Pixel: {d} {d} {d}\n", .{ r, g, b }); try painter.fillRect(.{ .x = f_x, .y = f_y, .w = 1.0, .h = 1.0, .color = .{ r, g, b, 1.0 }, }); } }
ctx.is_drawn = true;
app.requestFrame();}
fn srgbToLinear(c: f32) f32 { return if (c <= 0.04045) c / 12.92 else std.math.pow(f32, (c + 0.055) / 1.055, 2.4);}
fn readFileName(iterator: *std.process.Args.Iterator) ?[:0]const u8 { // Program name is always first, then the arg we actually want var idx: u2 = 0; const file_name: ?[:0]const u8 = blk: while (iterator.next()) |key| { if (idx == 0) { idx += 1; continue; } debug.print("Key ({d}): {s}\n", .{ key.len, key }); break :blk key; } else null; return file_name;}