aboutsummaryrefslogtreecommitdiff
path: root/hikari/display/framebuffer.zig
diff options
context:
space:
mode:
Diffstat (limited to 'hikari/display/framebuffer.zig')
-rw-r--r--hikari/display/framebuffer.zig156
1 files changed, 156 insertions, 0 deletions
diff --git a/hikari/display/framebuffer.zig b/hikari/display/framebuffer.zig
new file mode 100644
index 0000000..cf7c0ab
--- /dev/null
+++ b/hikari/display/framebuffer.zig
@@ -0,0 +1,156 @@
+//! Hikari Framebuffer
+
+const efi = @import("../efi/efi.zig");
+
+pub const Framebuffer = struct {
+ base: [*]u32,
+ width: u32,
+ height: u32,
+ stride: u32,
+ pixel_format: efi.types.graphics.PixelFormat,
+
+ pub fn initialize(gop: *efi.protocols.GraphicsOutputProtocol) Framebuffer {
+ const mode = gop.mode;
+ const info = mode.info;
+
+ return Framebuffer{
+ .base = @ptrFromInt(mode.framebuffer_base),
+ .width = info.horizontal_resolution,
+ .height = info.vertical_resolution,
+ .stride = info.pixels_per_scan_line,
+ .pixel_format = info.pixel_format,
+ };
+ }
+
+ pub fn put_pixel(self: *Framebuffer, x: u32, y: u32, color: Color) void {
+ if (x >= self.width or y >= self.height) {
+ return;
+ }
+
+ const offset = y * self.stride + x;
+ self.base[offset] = color.to_pixel(self.pixel_format);
+ }
+
+ pub fn fill_rect(self: *Framebuffer, x: u32, y: u32, w: u32, h: u32, color: Color) void {
+ const pixel = color.to_pixel(self.pixel_format);
+ const x_end = if (x + w > self.width) self.width else x + w;
+ const y_end = if (y + h > self.height) self.height else y + h;
+
+ var py = y;
+ while (py < y_end) : (py += 1) {
+ var px = x;
+ while (px < x_end) : (px += 1) {
+ const offset = py * self.stride + px;
+ self.base[offset] = pixel;
+ }
+ }
+ }
+
+ pub fn clear(self: *Framebuffer, color: Color) void {
+ self.fill_rect(0, 0, self.width, self.height, color);
+ }
+
+ pub fn draw_horizontal_line(self: *Framebuffer, x: u32, y: u32, length: u32, color: Color) void {
+ if (y >= self.height) {
+ return;
+ }
+
+ const pixel = color.to_pixel(self.pixel_format);
+ const x_end = if (x + length > self.width) self.width else x + length;
+ const row_offset = y * self.stride;
+
+ var px = x;
+ while (px < x_end) : (px += 1) {
+ self.base[row_offset + px] = pixel;
+ }
+ }
+
+ pub fn draw_vertical_line(self: *Framebuffer, x: u32, y: u32, length: u32, color: Color) void {
+ if (x >= self.width) {
+ return;
+ }
+
+ const pixel = color.to_pixel(self.pixel_format);
+ const y_end = if (y + length > self.height) self.height else y + length;
+
+ var py = y;
+ while (py < y_end) : (py += 1) {
+ self.base[py * self.stride + x] = pixel;
+ }
+ }
+
+ pub fn draw_rect(self: *Framebuffer, x: u32, y: u32, w: u32, h: u32, color: Color) void {
+ self.draw_horizontal_line(x, y, w, color);
+ self.draw_horizontal_line(x, y + h - 1, w, color);
+ self.draw_vertical_line(x, y, h, color);
+ self.draw_vertical_line(x + w - 1, y, h, color);
+ }
+
+ pub fn copy_rect(self: *Framebuffer, src_x: u32, src_y: u32, dst_x: u32, dst_y: u32, w: u32, h: u32) void {
+ if (src_y < dst_y) {
+ var row: u32 = h;
+ while (row > 0) {
+ row -= 1;
+ self.copy_row(src_x, src_y + row, dst_x, dst_y + row, w);
+ }
+ } else {
+ var row: u32 = 0;
+ while (row < h) : (row += 1) {
+ self.copy_row(src_x, src_y + row, dst_x, dst_y + row, w);
+ }
+ }
+ }
+
+ fn copy_row(self: *Framebuffer, src_x: u32, src_y: u32, dst_x: u32, dst_y: u32, w: u32) void {
+ const src_offset = src_y * self.stride + src_x;
+ const dst_offset = dst_y * self.stride + dst_x;
+
+ if (src_x < dst_x) {
+ var i: u32 = w;
+ while (i > 0) {
+ i -= 1;
+ self.base[dst_offset + i] = self.base[src_offset + i];
+ }
+ } else {
+ var i: u32 = 0;
+ while (i < w) : (i += 1) {
+ self.base[dst_offset + i] = self.base[src_offset + i];
+ }
+ }
+ }
+};
+
+pub const Color = struct {
+ r: u8,
+ g: u8,
+ b: u8,
+ a: u8,
+
+ pub fn rgb(r: u8, g: u8, b: u8) Color {
+ return Color{ .r = r, .g = g, .b = b, .a = 255 };
+ }
+
+ pub fn rgba(r: u8, g: u8, b: u8, a: u8) Color {
+ return Color{ .r = r, .g = g, .b = b, .a = a };
+ }
+
+ pub fn to_pixel(self: Color, format: efi.types.graphics.PixelFormat) u32 {
+ return switch (format) {
+ .rgb => (@as(u32, self.r) << 16) | (@as(u32, self.g) << 8) | self.b,
+ .bgr => (@as(u32, self.b) << 16) | (@as(u32, self.g) << 8) | self.r,
+ else => (@as(u32, self.r) << 16) | (@as(u32, self.g) << 8) | self.b,
+ };
+ }
+
+ pub const black = Color.rgb(0, 0, 0);
+ pub const white = Color.rgb(255, 255, 255);
+ pub const red = Color.rgb(255, 0, 0);
+ pub const green = Color.rgb(0, 255, 0);
+ pub const blue = Color.rgb(0, 0, 255);
+ pub const cyan = Color.rgb(0, 255, 255);
+ pub const magenta = Color.rgb(255, 0, 255);
+ pub const yellow = Color.rgb(255, 255, 0);
+ pub const gray = Color.rgb(128, 128, 128);
+ pub const dark_gray = Color.rgb(64, 64, 64);
+ pub const light_gray = Color.rgb(192, 192, 192);
+};