204 lines
5.9 KiB
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 };
|
|
};
|