Xenon/src/framebuffer.zig

204 lines
5.9 KiB
Zig

const std = @import("std");
const eql = std.mem.eql;
const ppm = @import("formats/ppm.zig");
const types = @import("types.zig");
extern const _binary_font_psf_start: comptime if (use_psf2) Psf2 else Psf1;
pub const use_psf2 = false;
pub const Pixel = packed struct {
b: u8,
g: u8,
r: u8,
a: u8 = 0,
};
pub fn color(r: u8, g: u8, b: u8) Pixel {
return .{
.r = r,
.g = g,
.b = b,
};
}
pub const PixelType = enum {
ARGB,
RGBA,
ABGR,
BGRA,
};
pub const Framebuffer = struct {
const Self = @This();
width: u32,
height: u32,
pixel_type: PixelType,
x: u32 = 0,
y: u32 = 0,
foreground: Pixel = color(0xa0, 0xa0, 0xa0),
background: Pixel = color(0x20, 0, 0x50),
buffer: []volatile u32,
pub fn init(fb: []align(4) volatile u8, width: u32, height: u32, pixel_type: PixelType) Self {
if (!eql(u8, &_binary_font_psf_start.magic, &(comptime if (use_psf2) Psf2.magic_const else Psf1.magic_const)))
@panic("Invalid kernel font");
std.debug.assert(width * height * @sizeOf(u32) <= fb.len);
return .{
.width = width,
.height = height,
.pixel_type = pixel_type,
.buffer = @ptrCast([*]volatile u32, &fb[0])[0 .. width * height],
};
}
pub fn clear(self: Self) void {
@setRuntimeSafety(false);
self.rectangle(0, 0, self.width - 1, self.height - 1, self.background);
}
pub inline fn pixel(self: Self, x: u32, y: u32, pix: Pixel) void {
@setRuntimeSafety(false);
@fence(.SeqCst);
if (x >= self.width or y >= self.height)
@panic("Framebuffer out of bounds access");
self.buffer[y * self.width + x] = switch (self.pixel_type) {
.ARGB => @bitCast(u32, pix),
.RGBA => @bitCast(u32, pix) << 8,
.ABGR => @byteSwap(u32, @bitCast(u32, pix)) >> 8,
.BGRA => @byteSwap(u32, @bitCast(u32, pix)),
};
}
// TODO Abstract this to an Image interface
pub fn image(self: *Self, x: u32, y: u32, image_stream: *ppm.Ppm) void {
const max_x = std.math.min(self.width, x + image_stream.width);
const max_y = std.math.min(self.height, y + image_stream.height);
const cut_width = image_stream.width + x - max_x;
var cy = y;
while (cy < max_y) : (cy += 1) {
var cx = x;
while (cx < max_x) : (cx += 1) {
const in_pixel = image_stream.nextPixel() orelse return;
const pix = color(in_pixel.r, in_pixel.g, in_pixel.b);
self.pixel(cx, cy, pix);
}
var consume: usize = 0;
while (consume < cut_width) : (consume += 1)
_ = image_stream.nextPixel(); // TODO Skip method
}
}
pub fn outStream(self: *Self) OutStream {
return .{
.fb = self,
.stream = OutStream.Stream{ .writeFn = OutStream.writeFn },
};
}
pub fn put(self: *Self, c: u8) void {
const width = comptime if (use_psf2) _binary_font_psf_start.width else 8;
const height = _binary_font_psf_start.height;
switch (c) {
'\r' => {
self.x = 0;
},
'\n' => {
self.x = self.width;
self.rectangle(0, (self.y + height) % self.height, self.width - 1, (self.y + 2 * height) % self.height, self.background);
},
else => {
const glyph_start: u32 = comptime if (use_psf2)
_binary_font_psf_start.header_size + c * _binary_font_psf_start.glyph_size
else
@sizeOf(Psf1) + c * @intCast(u32, _binary_font_psf_start.height);
const glyph = @ptrCast([*]const u8, &_binary_font_psf_start)[glyph_start .. glyph_start + comptime if (use_psf2) _binary_font_psf_start.glyph_size else height];
const row_bytes = if ((width + 7) / 8 != width / 8) (width / 8 + 1) * 8 else width;
var cy: u32 = 0;
while (cy < height) : (cy += 1) {
const row_start = cy * row_bytes / 8;
var cx: u32 = 0;
while (cx < width) : (cx += 1) {
const byte_start = cx / 8;
const bit: u3 = @intCast(u3, cx - byte_start * 8);
const active = (glyph[row_start + byte_start] & @as(u8, 0b1000000) >> bit) != 0;
self.pixel(cx + self.x, cy + self.y, if (active) self.foreground else self.background);
}
}
},
}
self.x += width;
if (self.x + width > self.width) {
self.x = 0;
self.y += height;
if (self.y + height > self.height) {
self.y = 0;
}
}
}
pub fn rectangle(self: Self, x1: u32, y1: u32, x2: u32, y2: u32, pix: Pixel) void {
var cy = y1;
while (cy <= y2) : (cy += 1) {
var cx = x1;
while (cx <= x2) : (cx += 1)
self.pixel(cx, cy, pix);
}
}
pub fn write(self: *Self, text: []const u8) void {
for (text) |c, i| {
self.put(c);
}
}
pub const OutStream = struct {
pub const Stream = std.io.OutStream(types.NoError);
fb: *Self,
stream: Stream,
fn writeFn(out_stream: *Stream, string: []const u8) types.NoError!void {
const self = @fieldParentPtr(OutStream, "stream", out_stream);
self.fb.write(string);
}
};
};
const Psf1 = packed struct {
magic: [2]u8,
mode: u8,
height: u8,
const magic_const: [2]u8 = [_]u8{ 0x36, 0x04 };
};
const Psf2 = packed struct {
magic: [4]u8,
version: u32,
header_size: u32,
flags: u32,
glyph_count: u32,
glyph_size: u32,
height: u32,
width: u32,
const magic_const: [4]u8 = [_]u8{ 0x72, 0xb5, 0x4a, 0x86 };
};