// TODO --- this should become it's own engine module const std = @import("std"); const spng = @import("spng"); const core = @import("core.zig"); pub const PngContents = struct { path: []const u8, pixels: []u8, size: core.Vector2u, allocator: std.mem.Allocator, pub fn initFromPathSpec(path: []const u8, allocator: std.mem.Allocator) !@This() { var pngContents: PngContents = undefined; var cookedPath = std.ArrayList(u8).init(allocator); defer cookedPath.deinit(); try cookedPath.appendSlice("_cooked/"); try cookedPath.appendSlice(path); try cookedPath.appendSlice(".Texture"); if (core.fs().fileExists(cookedPath.items)) { pngContents = try PngContents.initFromFSCooked(core.fs(), allocator, cookedPath.items); } else { pngContents = try PngContents.initFromFS(core.fs(), allocator, path); } return pngContents; } pub fn initFromFSCooked(fs: *core.FileSystem, allocator: std.mem.Allocator, path: []const u8) !@This() { const mapping = try fs.loadFile(path); defer fs.unmap(mapping); core.engine_log("loading cooked version of file: {s}", .{path}); const rv: @This() = .{ .size = .{ .x = @as(u32, @bitCast(mapping.bytes[0..4].*)), .y = @as(u32, @bitCast(mapping.bytes[4..8].*)), }, .path = try allocator.dupe(u8, path), .pixels = try allocator.dupe(u8, mapping.bytes[8..]), .allocator = allocator, }; return rv; } pub fn toBuffer(self: @This()) !std.ArrayList(u8) { var buffer = std.ArrayList(u8).init(self.allocator); try buffer.appendSlice(&@as([4]u8, @bitCast(self.size.x))); try buffer.appendSlice(&@as([4]u8, @bitCast(self.size.y))); try buffer.appendSlice(self.pixels); return buffer; } pub fn initFromFS(fs: *core.FileSystem, allocator: std.mem.Allocator, path: []const u8) !@This() { // 1. check the fs to see if a cooked version of the file exists // 2. load that one if possible // 3. otherwise, load the other one. const mapping = try fs.loadFile(path); defer fs.unmap(mapping); return try initFromBytes(allocator, path, mapping.bytes); } pub fn initFromBytes(allocator: std.mem.Allocator, pathName: []const u8, pngFileContents: []const u8) !@This() { var decoder = try spng.SpngContext.newDecoder(); defer decoder.deinit(); try decoder.setBuffer(pngFileContents); const header = try decoder.getHeader(); const imageSize = @as(usize, @intCast(header.width * header.height * 4)); const pixels: []u8 = try allocator.alloc(u8, imageSize); const len = try decoder.decode(pixels, spng.SPNG_FMT_RGBA8, spng.SPNG_DECODE_TRNS); try core.assertf(len == pixels.len, "decoded pixel size not buffer size {d} != {d}", .{ len, pixels.len }); return PngContents{ .path = try core.dupe(u8, allocator, pathName), .pixels = pixels, .size = .{ .x = header.width, .y = header.height }, .allocator = allocator, }; } pub fn deinit(self: *@This()) void { self.allocator.free(self.path); self.allocator.free(self.pixels); } };