Files
ZeroOS/src/framebuffer.rs
T
2026-07-29 10:56:30 +08:00

136 lines
4.1 KiB
Rust

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<Self> {
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());
}
}
}