121 lines
3.5 KiB
Rust
121 lines
3.5 KiB
Rust
mod util;
|
|
|
|
use embedded_graphics::{pixelcolor::Rgb888, prelude::{DrawTarget, OriginDimensions, PixelColor, RgbColor, Size}};
|
|
use multiboot2::{BootInformation, BootInformationHeader, FramebufferTag, FramebufferType, TagTrait};
|
|
pub use util::is_video_mode_enabled;
|
|
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct Color {
|
|
r: u8,
|
|
g: u8,
|
|
b: u8,
|
|
}
|
|
|
|
impl Color {
|
|
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
|
Self {r, g, b}
|
|
}
|
|
}
|
|
#[derive(Clone, Copy)]
|
|
pub struct VideoControler{
|
|
component_size: u8,
|
|
r_ofs: u8,
|
|
g_ofs: u8,
|
|
b_ofs: u8,
|
|
buf_addr: u64,
|
|
pitch: usize,
|
|
size: [usize; 2],
|
|
bpp: u8
|
|
}
|
|
|
|
impl VideoControler {
|
|
pub fn new(mb2p: *const BootInformationHeader) -> Self {
|
|
let boot_info = unsafe { BootInformation::load(mb2p).unwrap() };
|
|
let fbt = boot_info.framebuffer_tag().unwrap().unwrap();
|
|
let FramebufferType::RGB { red, green, blue } = fbt.buffer_type().unwrap() else {
|
|
panic!();
|
|
};
|
|
|
|
assert!(red.size == green.size && green.size == blue.size);
|
|
log::info!("VGA VIDEO addr: {:x}", fbt.address());
|
|
log::info!("{fbt:#?}");
|
|
|
|
Self {
|
|
component_size: red.size,
|
|
r_ofs: red.position,
|
|
g_ofs: green.position,
|
|
b_ofs: blue.position,
|
|
buf_addr: fbt.address(),
|
|
size: [fbt.width() as usize, fbt.height() as usize],
|
|
pitch: fbt.pitch() as usize,
|
|
bpp: fbt.bpp(),
|
|
}
|
|
}
|
|
|
|
pub fn set_px(&self, x: usize, y: usize, c: Color) {
|
|
assert!(x <= self.size[0] && y <= self.size[1]);
|
|
let ptr = self.buf_addr as *mut () as *mut u8;
|
|
let px = ptr.wrapping_add((self.bpp / 8) as usize * x + self.pitch * y);
|
|
unsafe {
|
|
*px.wrapping_add((self.r_ofs / 8) as usize) = c.r;
|
|
*px.wrapping_add((self.g_ofs / 8) as usize) = c.g;
|
|
*px.wrapping_add((self.b_ofs / 8) as usize) = c.b;
|
|
}
|
|
}
|
|
|
|
pub fn fill_screen(&self, c: Color) {
|
|
for x in 0..self.size[0] {
|
|
for y in 0..self.size[1] {
|
|
self.set_px(x, y, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn fill_screen_grad(&self, start: Color, end: Color) {
|
|
let diag = libm::sqrt((self.size[0].pow(2) + self.size[1].pow(2)) as f64);
|
|
for x in 0..self.size[0] {
|
|
for y in 0..self.size[1] {
|
|
let dist = libm::sqrt((x.pow(2) + y.pow(2)) as f64);
|
|
let fact = dist / diag;
|
|
let color = Color::new(
|
|
start.r + (fact * (end.r - start.r) as f64) as u8,
|
|
start.g + (fact * (end.g - start.g) as f64) as u8,
|
|
start.b + (fact * (end.b - start.b) as f64) as u8,
|
|
);
|
|
self.set_px(x, y, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DrawTarget for VideoControler {
|
|
type Color = Rgb888;
|
|
type Error = &'static str;
|
|
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
|
where
|
|
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>> {
|
|
for px in pixels {
|
|
self.set_px(
|
|
px.0.x as usize,
|
|
px.0.y as usize,
|
|
px.1.into()
|
|
)
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl OriginDimensions for VideoControler {
|
|
fn size(&self) -> embedded_graphics::prelude::Size {
|
|
Size::new(self.size[0] as u32, self.size[1] as u32)
|
|
}
|
|
}
|
|
|
|
impl From<Rgb888> for Color {
|
|
fn from(c: Rgb888) -> Self {
|
|
Color::new(c.r(), c.g(), c.b())
|
|
}
|
|
}
|