Implement bitmap loading
This commit is contained in:
parent
04e8d69c37
commit
ee88f58e53
|
@ -2,6 +2,8 @@ pub const ascii = @import("./ascii.zig");
|
|||
|
||||
pub const dag = @import("./dag.zig");
|
||||
|
||||
pub const files = @import("./files.zig");
|
||||
|
||||
pub const hashes = @import("./hashes.zig");
|
||||
|
||||
pub const heap = @import("./heap.zig");
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
const builtin = @import("builtin");
|
||||
|
||||
const io = @import("./io.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Error = error {
|
||||
FileNotFound,
|
||||
FileInaccessible,
|
||||
};
|
||||
|
||||
pub const Stat = struct {
|
||||
size: u64,
|
||||
};
|
||||
|
||||
pub const Storage = struct {
|
||||
userdata: *anyopaque,
|
||||
vtable: *const VTable,
|
||||
|
||||
pub const VTable = struct {
|
||||
stat: *const fn (*anyopaque, []const u8) Error!Stat,
|
||||
read: *const fn (*anyopaque, []const u8, usize, []io.Byte) Error!usize,
|
||||
};
|
||||
|
||||
pub fn read_bytes(self: Storage, path: []const u8, offset: usize, output: []io.Byte) Error!usize {
|
||||
return self.vtable.read(self.userdata, path, offset, output);
|
||||
}
|
||||
|
||||
pub fn read_foreign(self: Storage, path: []const u8, offset: usize, comptime Type: type) Error!?Type {
|
||||
const decoded = (try self.read_native(path, offset, Type)) orelse {
|
||||
return null;
|
||||
};
|
||||
|
||||
return switch (@typeInfo(Type)) {
|
||||
.Struct => std.mem.byteSwapAllFields(Type, &decoded),
|
||||
else => @byteSwap(decoded),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn read_native(self: Storage, path: []const u8, offset: usize, comptime Type: type) Error!?Type {
|
||||
var buffer = @as([@sizeOf(Type)]io.Byte, undefined);
|
||||
|
||||
if (try self.vtable.read(self.userdata, path, offset, &buffer) != buffer.len) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return @as(*align(1) const Type, @ptrCast(&buffer)).*;
|
||||
}
|
||||
|
||||
pub const read_little = switch (native_endian) {
|
||||
.little => read_native,
|
||||
.big => read_foreign,
|
||||
};
|
||||
|
||||
pub const read_big = switch (native_endian) {
|
||||
.little => read_foreign,
|
||||
.big => read_native,
|
||||
};
|
||||
};
|
||||
|
||||
pub const bundle = init: {
|
||||
const Bundle = struct {
|
||||
fn full_path(path: []const u8) Error![4095:0]u8 {
|
||||
var buffer = [_:0]u8{0} ** 4095;
|
||||
|
||||
_ = std.fs.cwd().realpath(path, &buffer) catch {
|
||||
return error.FileInaccessible;
|
||||
};
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
fn read(_: *anyopaque, path: []const u8, offset: usize, output: []io.Byte) Error!usize {
|
||||
var file = std.fs.openFileAbsoluteZ(&(try full_path(path)), .{.mode = .read_only}) catch |open_error| {
|
||||
return switch (open_error) {
|
||||
error.FileNotFound => error.FileNotFound,
|
||||
else => error.FileInaccessible,
|
||||
};
|
||||
};
|
||||
|
||||
defer file.close();
|
||||
|
||||
if (offset != 0) {
|
||||
file.seekTo(offset) catch {
|
||||
return error.FileInaccessible;
|
||||
};
|
||||
}
|
||||
|
||||
return file.read(output) catch error.FileInaccessible;
|
||||
}
|
||||
|
||||
fn stat(_: *anyopaque, path: []const u8) Error!Stat {
|
||||
const file_stat = get: {
|
||||
var file = std.fs.openFileAbsoluteZ(&(try full_path(path)), .{.mode = .read_only}) catch |open_error| {
|
||||
return switch (open_error) {
|
||||
error.FileNotFound => error.FileNotFound,
|
||||
else => error.FileInaccessible,
|
||||
};
|
||||
};
|
||||
|
||||
defer file.close();
|
||||
|
||||
break: get file.stat() catch {
|
||||
return error.FileInaccessible;
|
||||
};
|
||||
};
|
||||
|
||||
return .{
|
||||
.size = file_stat.size,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
break: init Storage{
|
||||
.userdata = undefined,
|
||||
|
||||
.vtable = &.{
|
||||
.stat = Bundle.stat,
|
||||
.read = Bundle.read,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const native_endian = builtin.cpu.arch.endian();
|
|
@ -6,23 +6,30 @@ const slices = @import("./slices.zig");
|
|||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Byte = u8;
|
||||
pub const Writable = struct {
|
||||
data: []Byte,
|
||||
|
||||
pub const Decoder = coral.io.Functor(coral.io.Error!void, &.{[]coral.Byte});
|
||||
pub fn writer(self: *Writable) Writer {
|
||||
return Writer.bind(Writable, self, write);
|
||||
}
|
||||
|
||||
fn write(self: *Writable, buffer: []const u8) !usize {
|
||||
const range = @min(buffer.len, self.data.len);
|
||||
|
||||
@memcpy(self.data[0 .. range], buffer[0 .. range]);
|
||||
|
||||
self.data = self.data[range ..];
|
||||
|
||||
return buffer.len;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Byte = u8;
|
||||
|
||||
pub const Error = error {
|
||||
UnavailableResource,
|
||||
};
|
||||
|
||||
pub fn FixedBuffer(comptime len: usize, comptime default_value: anytype) type {
|
||||
const Value = @TypeOf(default_value);
|
||||
|
||||
return struct {
|
||||
filled: usize = 0,
|
||||
values: [len]Value = [_]Value{default_value} ** len,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Functor(comptime Output: type, comptime input_types: []const type) type {
|
||||
const InputTuple = std.meta.Tuple(input_types);
|
||||
|
||||
|
@ -119,20 +126,6 @@ pub fn Generator(comptime Output: type, comptime input_types: []const type) type
|
|||
};
|
||||
}
|
||||
|
||||
pub const NullWritable = struct {
|
||||
written: usize = 0,
|
||||
|
||||
pub fn writer(self: *NullWritable) Writer {
|
||||
return Writer.bind(NullWritable, self, write);
|
||||
}
|
||||
|
||||
pub fn write(self: *NullWritable, buffer: []const u8) !usize {
|
||||
self.written += buffer.len;
|
||||
|
||||
return buffer.len;
|
||||
}
|
||||
};
|
||||
|
||||
pub const PrintError = Error || error {
|
||||
IncompleteWrite,
|
||||
};
|
||||
|
@ -141,8 +134,6 @@ pub const Reader = Generator(Error!usize, &.{[]coral.Byte});
|
|||
|
||||
pub const Writer = Generator(Error!usize, &.{[]const coral.Byte});
|
||||
|
||||
const native_endian = builtin.cpu.arch.endian();
|
||||
|
||||
pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.Byte {
|
||||
const buffer = coral.Stack(coral.Byte){.allocator = allocator};
|
||||
|
||||
|
@ -153,20 +144,6 @@ pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.
|
|||
return buffer.to_allocation(streamed);
|
||||
}
|
||||
|
||||
pub fn are_equal(a: []const Byte, b: []const Byte) bool {
|
||||
if (a.len != b.len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (0 .. a.len) |i| {
|
||||
if (a[i] != b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub const bits_per_byte = 8;
|
||||
|
||||
pub fn bytes_of(value: anytype) []const Byte {
|
||||
|
@ -179,14 +156,6 @@ pub fn bytes_of(value: anytype) []const Byte {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn ends_with(haystack: []const Byte, needle: []const Byte) bool {
|
||||
if (needle.len > haystack.len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return are_equal(haystack[haystack.len - needle.len ..], needle);
|
||||
}
|
||||
|
||||
pub fn print(writer: Writer, utf8: []const u8) PrintError!void {
|
||||
if (try writer.yield(.{utf8}) != utf8.len) {
|
||||
return error.IncompleteWrite;
|
||||
|
@ -208,35 +177,6 @@ pub fn skip_n(input: Reader, distance: u64) Error!void {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read_foreign(input: Reader, comptime Type: type) Error!Type {
|
||||
const decoded = try read_native(input, Type);
|
||||
|
||||
return switch (@typeInfo(input)) {
|
||||
.Struct => std.mem.byteSwapAllFields(Type, &decoded),
|
||||
else => @byteSwap(decoded),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn read_native(input: Reader, comptime Type: type) Error!Type {
|
||||
var buffer = @as([@sizeOf(Type)]coral.Byte, undefined);
|
||||
|
||||
if (try input.yield(.{&buffer}) != buffer.len) {
|
||||
return error.UnavailableResource;
|
||||
}
|
||||
|
||||
return @as(*align(1) const Type, @ptrCast(&buffer)).*;
|
||||
}
|
||||
|
||||
pub const read_little = switch (native_endian) {
|
||||
.little => read_native,
|
||||
.big => read_foreign,
|
||||
};
|
||||
|
||||
pub const read_big = switch (native_endian) {
|
||||
.little => read_foreign,
|
||||
.big => read_native,
|
||||
};
|
||||
|
||||
pub fn slice_sentineled(comptime sen: anytype, ptr: [*:sen]const @TypeOf(sen)) [:sen]const @TypeOf(sen) {
|
||||
var len = @as(usize, 0);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ const ona = @import("ona");
|
|||
|
||||
const Actors = struct {
|
||||
instances: coral.stack.Sequential(ona.gfx.Point2D) = .{.allocator = coral.heap.allocator},
|
||||
quad_mesh_2d: ona.gfx.Handle = .none,
|
||||
body_texture: ona.gfx.Handle = .none,
|
||||
};
|
||||
|
||||
|
@ -24,6 +25,7 @@ pub fn main() !void {
|
|||
fn load(display: coral.Write(ona.gfx.Display), actors: coral.Write(Actors), assets: coral.Write(ona.gfx.Assets)) !void {
|
||||
display.res.width, display.res.height = .{1280, 720};
|
||||
actors.res.body_texture = try assets.res.open_file("actor.bmp");
|
||||
actors.res.quad_mesh_2d = try assets.res.open_quad_mesh_2d(@splat(1));
|
||||
|
||||
try actors.res.instances.push_grow(.{0, 0});
|
||||
}
|
||||
|
@ -32,11 +34,11 @@ fn exit(actors: coral.Write(Actors)) void {
|
|||
actors.res.instances.deinit();
|
||||
}
|
||||
|
||||
fn render(queue: ona.gfx.Queue, actors: coral.Write(Actors), assets: coral.Read(ona.gfx.Assets)) !void {
|
||||
fn render(queue: ona.gfx.Queue, actors: coral.Write(Actors)) !void {
|
||||
for (actors.res.instances.values) |instance| {
|
||||
try queue.commands.append(.{
|
||||
.instance_2d = .{
|
||||
.mesh_2d = assets.res.primitives.quad_mesh,
|
||||
.mesh_2d = actors.res.quad_mesh_2d,
|
||||
.texture = actors.res.body_texture,
|
||||
|
||||
.transform = .{
|
||||
|
|
154
src/ona/gfx.zig
154
src/ona/gfx.zig
|
@ -14,41 +14,99 @@ const std = @import("std");
|
|||
|
||||
pub const Assets = struct {
|
||||
context: device.Context,
|
||||
primitives: Primitives,
|
||||
formats: coral.stack.Sequential(Format),
|
||||
staging_arena: std.heap.ArenaAllocator,
|
||||
|
||||
pub const Format = struct {
|
||||
extension: []const u8,
|
||||
open: *const fn ([]const u8) std.mem.Allocator.Error!Handle,
|
||||
open_file: *const fn (*std.heap.ArenaAllocator, []const u8) Error!Desc,
|
||||
|
||||
pub const Error = std.mem.Allocator.Error || coral.files.Error || error {
|
||||
Unsupported,
|
||||
};
|
||||
};
|
||||
|
||||
const Primitives = struct {
|
||||
quad_mesh: Handle,
|
||||
};
|
||||
pub fn open_file(self: *Assets, path: []const u8) (std.mem.Allocator.Error || Format.Error)!Handle {
|
||||
defer {
|
||||
const max_cache_size = 536870912;
|
||||
|
||||
pub fn close(self: *Assets, handle: Handle) void {
|
||||
return self.context.close(handle);
|
||||
}
|
||||
if (!self.staging_arena.reset(.{.retain_with_limit = max_cache_size})) {
|
||||
std.log.warn("failed to retain staging arena size of {} bytes", .{max_cache_size});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_file(self: *Assets, path: []const u8) std.mem.Allocator.Error!Handle {
|
||||
for (self.formats.values) |format| {
|
||||
if (!std.mem.endsWith(u8, path, format.extension)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return format.open(path);
|
||||
return self.context.open(try format.open_file(&self.staging_arena, path));
|
||||
}
|
||||
|
||||
return .none;
|
||||
}
|
||||
|
||||
pub fn open_mesh_2d(self: *Assets, mesh_2d: Mesh2D) std.mem.Allocator.Error!Handle {
|
||||
return self.context.open(.{.mesh_2d = mesh_2d});
|
||||
pub fn open_quad_mesh_2d(self: *Assets, extents: Point2D) std.mem.Allocator.Error!Handle {
|
||||
const width, const height = extents / @as(Point2D, @splat(2));
|
||||
|
||||
return self.context.open(.{
|
||||
.mesh_2d = .{
|
||||
.indices = &.{0, 1, 2, 0, 2, 3},
|
||||
|
||||
.vertices = &.{
|
||||
.{.xy = .{-width, height}, .uv = .{0, 1}},
|
||||
.{.xy = .{width, height}, .uv = .{1, 1}},
|
||||
.{.xy = .{width, -height}, .uv = .{1, 0}},
|
||||
.{.xy = .{-width, -height}, .uv = .{0, 0}},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pub const Color = @Vector(4, f32);
|
||||
|
||||
pub const Desc = union (enum) {
|
||||
texture: Texture,
|
||||
mesh_2d: Mesh2D,
|
||||
|
||||
pub const Mesh2D = struct {
|
||||
vertices: []const Vertex,
|
||||
indices: []const u16,
|
||||
|
||||
pub const Vertex = struct {
|
||||
xy: Point2D,
|
||||
uv: Point2D,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Texture = struct {
|
||||
data: []const coral.io.Byte,
|
||||
width: u16,
|
||||
format: Format,
|
||||
access: Access,
|
||||
|
||||
pub const Access = enum {
|
||||
static,
|
||||
};
|
||||
|
||||
pub const Format = enum {
|
||||
rgba8888,
|
||||
bgra8888,
|
||||
argb8888,
|
||||
rgb888,
|
||||
bgr888,
|
||||
|
||||
pub fn byte_size(self: Format) usize {
|
||||
return switch (self) {
|
||||
.rgba8888, .bgra8888, .argb8888 => 4,
|
||||
.rgb888, .bgr888 => 3,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pub const Display = struct {
|
||||
width: u16 = 1280,
|
||||
height: u16 = 720,
|
||||
|
@ -85,16 +143,6 @@ pub const Input = union (enum) {
|
|||
|
||||
pub const Point2D = @Vector(2, f32);
|
||||
|
||||
pub const Mesh2D = struct {
|
||||
vertices: []const Vertex,
|
||||
indices: []const u16,
|
||||
|
||||
pub const Vertex = struct {
|
||||
xy: Point2D,
|
||||
uv: Point2D,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Queue = struct {
|
||||
commands: *device.RenderList,
|
||||
|
||||
|
@ -104,6 +152,10 @@ pub const Queue = struct {
|
|||
|
||||
pub fn bind(_: coral.system.BindContext) std.mem.Allocator.Error!State {
|
||||
// TODO: Review how good of an idea this global state is, even if bind is guaranteed to always be ran on main.
|
||||
if (renders.is_empty()) {
|
||||
renders = .{.allocator = coral.heap.allocator};
|
||||
}
|
||||
|
||||
const command_index = renders.len();
|
||||
|
||||
try renders.push_grow(device.RenderChain.init(coral.heap.allocator));
|
||||
|
@ -121,39 +173,21 @@ pub const Queue = struct {
|
|||
|
||||
pub fn unbind(state: *State) void {
|
||||
std.debug.assert(!renders.is_empty());
|
||||
std.mem.swap(device.RenderChain, &renders.values[state.command_index], renders.get_ptr().?);
|
||||
|
||||
const render = &renders.values[state.command_index];
|
||||
|
||||
render.deinit();
|
||||
std.mem.swap(device.RenderChain, render, renders.get_ptr().?);
|
||||
std.debug.assert(renders.pop());
|
||||
|
||||
if (renders.is_empty()) {
|
||||
Queue.renders.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
var renders = coral.stack.Sequential(device.RenderChain){.allocator = coral.heap.allocator};
|
||||
};
|
||||
|
||||
pub const Texture = struct {
|
||||
data: []const coral.io.Byte,
|
||||
width: u16,
|
||||
format: Format,
|
||||
access: Access,
|
||||
|
||||
pub const Access = enum {
|
||||
static,
|
||||
};
|
||||
|
||||
pub const Format = enum {
|
||||
rgba8888,
|
||||
bgra8888,
|
||||
argb8888,
|
||||
rgb888,
|
||||
bgr888,
|
||||
|
||||
pub fn byte_size(self: Format) usize {
|
||||
return switch (self) {
|
||||
.rgba8888, .bgra8888, .argb8888 => 4,
|
||||
.rgb888, .bgr888 => 3,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const Transform2D = extern struct {
|
||||
xbasis: Point2D = .{1, 0},
|
||||
ybasis: Point2D = .{0, 1},
|
||||
|
@ -163,7 +197,7 @@ pub const Transform2D = extern struct {
|
|||
const builtin_formats = [_]Assets.Format{
|
||||
.{
|
||||
.extension = "bmp",
|
||||
.open = formats.open_bmp,
|
||||
.open_file = formats.load_bmp,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -210,24 +244,8 @@ pub fn setup(world: *coral.World, events: App.Events) (error {Unsupported} || st
|
|||
try registered_formats.grow(builtin_formats.len);
|
||||
std.debug.assert(registered_formats.push_all(&builtin_formats));
|
||||
|
||||
const half_extent = 0.5;
|
||||
|
||||
try world.set_resource(.none, Assets{
|
||||
.primitives = .{
|
||||
.quad_mesh = try context.open(.{
|
||||
.mesh_2d = .{
|
||||
.indices = &.{0, 1, 2, 0, 2, 3},
|
||||
|
||||
.vertices = &.{
|
||||
.{.xy = .{-half_extent, half_extent}, .uv = .{0, 1}},
|
||||
.{.xy = .{half_extent, half_extent}, .uv = .{1, 1}},
|
||||
.{.xy = .{half_extent, -half_extent}, .uv = .{1, 0}},
|
||||
.{.xy = .{-half_extent, -half_extent}, .uv = .{0, 0}},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
.staging_arena = std.heap.ArenaAllocator.init(coral.heap.allocator),
|
||||
.formats = registered_formats,
|
||||
.context = context,
|
||||
});
|
||||
|
@ -239,6 +257,8 @@ pub fn setup(world: *coral.World, events: App.Events) (error {Unsupported} || st
|
|||
}
|
||||
|
||||
pub fn stop(assets: coral.Write(Assets)) void {
|
||||
assets.res.staging_arena.deinit();
|
||||
assets.res.formats.deinit();
|
||||
assets.res.context.deinit();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,11 +10,6 @@ const sokol = @import("sokol");
|
|||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Asset = union (enum) {
|
||||
texture: gfx.Texture,
|
||||
mesh_2d: gfx.Mesh2D,
|
||||
};
|
||||
|
||||
pub const Context = struct {
|
||||
window: *ext.SDL_Window,
|
||||
thread: std.Thread,
|
||||
|
@ -42,6 +37,7 @@ pub const Context = struct {
|
|||
self.loop.is_running.store(false, .monotonic);
|
||||
self.loop.ready.post();
|
||||
self.thread.join();
|
||||
self.loop.deinit();
|
||||
coral.heap.allocator.destroy(self.loop);
|
||||
ext.SDL_DestroyWindow(self.window);
|
||||
|
||||
|
@ -84,16 +80,20 @@ pub const Context = struct {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn open(self: *Context, asset: Asset) std.mem.Allocator.Error!gfx.Handle {
|
||||
pub fn open(self: *Context, desc: gfx.Desc) std.mem.Allocator.Error!gfx.Handle {
|
||||
const open_commands = self.loop.opens.pending();
|
||||
const index = self.loop.closed_indices.get() orelse open_commands.stack.len();
|
||||
|
||||
try open_commands.append(.{
|
||||
.index = index,
|
||||
.payload = asset,
|
||||
.desc = desc,
|
||||
});
|
||||
|
||||
try self.loop.closes.pending().stack.grow(1);
|
||||
const pending_closes = self.loop.closes.pending();
|
||||
|
||||
if (pending_closes.stack.len() == pending_closes.stack.cap) {
|
||||
try pending_closes.stack.grow(1);
|
||||
}
|
||||
|
||||
_ = self.loop.closed_indices.pop();
|
||||
|
||||
|
@ -145,13 +145,13 @@ const Loop = struct {
|
|||
|
||||
const OpenCommand = struct {
|
||||
index: usize,
|
||||
payload: Asset,
|
||||
desc: gfx.Desc,
|
||||
|
||||
fn clone(command: OpenCommand, arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!OpenCommand {
|
||||
const allocator = arena.allocator();
|
||||
|
||||
return .{
|
||||
.payload = switch (command.payload) {
|
||||
.desc = switch (command.desc) {
|
||||
.texture => |texture| .{
|
||||
.texture = .{
|
||||
.data = try allocator.dupe(coral.io.Byte, texture.data),
|
||||
|
@ -163,7 +163,7 @@ const Loop = struct {
|
|||
|
||||
.mesh_2d => |mesh_2d| .{
|
||||
.mesh_2d = .{
|
||||
.vertices = try allocator.dupe(gfx.Mesh2D.Vertex, mesh_2d.vertices),
|
||||
.vertices = try allocator.dupe(gfx.Desc.Mesh2D.Vertex, mesh_2d.vertices),
|
||||
.indices = try allocator.dupe(u16, mesh_2d.indices),
|
||||
},
|
||||
},
|
||||
|
@ -178,6 +178,12 @@ const Loop = struct {
|
|||
|
||||
const OpenChain = commands.Chain(OpenCommand, OpenCommand.clone);
|
||||
|
||||
fn deinit(self: *Loop) void {
|
||||
self.closes.deinit();
|
||||
self.opens.deinit();
|
||||
self.closed_indices.deinit();
|
||||
}
|
||||
|
||||
fn run(self: *Loop, window: *ext.SDL_Window) !void {
|
||||
const context = configure_and_create: {
|
||||
var result = @as(c_int, 0);
|
||||
|
@ -230,7 +236,7 @@ const Loop = struct {
|
|||
defer open_commands.clear();
|
||||
|
||||
for (open_commands.stack.values) |command| {
|
||||
switch (command.payload) {
|
||||
switch (command.desc) {
|
||||
.texture => |texture| {
|
||||
const stride = texture.width * texture.format.byte_size();
|
||||
|
||||
|
|
|
@ -1,9 +1,69 @@
|
|||
const coral = @import("coral");
|
||||
|
||||
const gfx = @import("../gfx.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub fn open_bmp(path: []const u8) std.mem.Allocator.Error!gfx.Handle {
|
||||
// TODO: Implement.
|
||||
_ = path;
|
||||
unreachable;
|
||||
pub fn load_bmp(arena: *std.heap.ArenaAllocator, path: []const u8) gfx.Assets.Format.Error!gfx.Desc {
|
||||
const header = try coral.files.bundle.read_little(path, 0, extern struct {
|
||||
type: [2]u8 align (1),
|
||||
file_size: u32 align (1),
|
||||
reserved: [2]u16 align (1),
|
||||
image_offset: u32 align (1),
|
||||
header_size: u32 align (1),
|
||||
pixel_width: i32 align (1),
|
||||
pixel_height: i32 align (1),
|
||||
color_planes: u16 align (1),
|
||||
bits_per_pixel: u16 align (1),
|
||||
compression_method: u32 align (1),
|
||||
image_size: u32 align(1),
|
||||
pixels_per_meter_x: i32 align (1),
|
||||
pixels_per_meter_y: i32 align (1),
|
||||
palette_colors_used: u32 align (1),
|
||||
important_colors_used: u32 align (1),
|
||||
}) orelse {
|
||||
return error.Unsupported;
|
||||
};
|
||||
|
||||
if (!std.mem.eql(u8, &header.type, "BM")) {
|
||||
return error.Unsupported;
|
||||
}
|
||||
|
||||
const pixel_width = std.math.cast(u16, header.pixel_width) orelse {
|
||||
return error.Unsupported;
|
||||
};
|
||||
|
||||
const pixels = try arena.allocator().alloc(coral.io.Byte, header.image_size);
|
||||
const bytes_per_pixel = header.bits_per_pixel / coral.io.bits_per_byte;
|
||||
const alignment = 4;
|
||||
const byte_stride = pixel_width * bytes_per_pixel;
|
||||
const padded_byte_stride = alignment * @divTrunc((byte_stride + alignment - 1), alignment);
|
||||
const byte_padding = coral.scalars.sub(padded_byte_stride, byte_stride) orelse 0;
|
||||
var buffer_offset: usize = 0;
|
||||
var file_offset = @as(usize, header.image_offset);
|
||||
|
||||
while (buffer_offset < pixels.len) {
|
||||
const line = pixels[buffer_offset .. buffer_offset + byte_stride];
|
||||
|
||||
if (try coral.files.bundle.read_bytes(path, file_offset, line) != byte_stride) {
|
||||
return error.Unsupported;
|
||||
}
|
||||
|
||||
file_offset = line.len + byte_padding;
|
||||
buffer_offset += padded_byte_stride;
|
||||
}
|
||||
|
||||
return .{
|
||||
.texture = .{
|
||||
.format = switch (header.bits_per_pixel) {
|
||||
24 => .bgr888,
|
||||
32 => .bgra8888,
|
||||
else => return error.Unsupported,
|
||||
},
|
||||
|
||||
.width = pixel_width,
|
||||
.data = pixels,
|
||||
.access = .static,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue