use crate::efi::{EfiGraphicsOutputProtocol, EfiGraphicsPixelFormat}; #[derive(Clone, Copy)] pub struct Rgb { pub red: u8, pub green: u8, pub blue: u8, } pub struct Framebuffer { base: *mut u8, width: usize, height: usize, stride: usize, pixel_format: EfiGraphicsPixelFormat, } impl Framebuffer { pub unsafe fn from_gop(gop: *mut EfiGraphicsOutputProtocol) -> Option { let mode = unsafe { (*gop).mode }; if mode.is_null() { return None; } let info = unsafe { (*mode).info }; if info.is_null() { return None; } let pixel_format = unsafe { (*info).pixel_format }; if pixel_format != EfiGraphicsPixelFormat::RedGreenBlueReserved8BitPerColor && pixel_format != EfiGraphicsPixelFormat::BlueGreenRedReserved8BitPerColor { return None; } Some(Self { base: unsafe { (*mode).frame_buffer_base as *mut u8 }, width: unsafe { (*info).horizontal_resolution as usize }, height: unsafe { (*info).vertical_resolution as usize }, stride: unsafe { (*info).pixels_per_scan_line as usize }, pixel_format, }) } pub fn width(&self) -> usize { self.width } pub fn height(&self) -> usize { self.height } pub fn clear(&mut self, color: Rgb) { self.fill_rect(0, 0, self.width, self.height, color); } pub fn fill_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: Rgb) { let end_x = x.saturating_add(width).min(self.width); let end_y = y.saturating_add(height).min(self.height); for py in y..end_y { for px in x..end_x { self.put_pixel(px, py, color); } } } pub fn scroll_region_up(&mut self, top: usize, bottom: usize, pixels: usize, fill: Rgb) { if pixels == 0 { return; } let top = top.min(self.height); let bottom = bottom.min(self.height); if top >= bottom { return; } if pixels >= bottom - top { self.fill_rect(0, top, self.width, bottom - top, fill); return; } for y in top..bottom { for x in 0..self.width { if y + pixels < bottom { self.copy_pixel(x, y + pixels, x, y); } else { self.put_pixel(x, y, fill); } } } } pub fn put_pixel(&mut self, x: usize, y: usize, color: Rgb) { if x >= self.width || y >= self.height { return; } let offset = (y * self.stride + x) * 4; unsafe { let pixel = self.base.add(offset); match self.pixel_format { EfiGraphicsPixelFormat::RedGreenBlueReserved8BitPerColor => { pixel.write_volatile(color.red); pixel.add(1).write_volatile(color.green); pixel.add(2).write_volatile(color.blue); pixel.add(3).write_volatile(0); } EfiGraphicsPixelFormat::BlueGreenRedReserved8BitPerColor => { pixel.write_volatile(color.blue); pixel.add(1).write_volatile(color.green); pixel.add(2).write_volatile(color.red); pixel.add(3).write_volatile(0); } _ => {} } } } fn copy_pixel(&mut self, source_x: usize, source_y: usize, target_x: usize, target_y: usize) { let source_offset = (source_y * self.stride + source_x) * 4; let target_offset = (target_y * self.stride + target_x) * 4; unsafe { let source = self.base.add(source_offset); let target = self.base.add(target_offset); target.write_volatile(source.read_volatile()); target.add(1).write_volatile(source.add(1).read_volatile()); target.add(2).write_volatile(source.add(2).read_volatile()); target.add(3).write_volatile(source.add(3).read_volatile()); } } }