Backlog/engine/core/src/png.zig

94 lines
3.3 KiB
Zig

// 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);
}
};