ableos/ableos/src/graphics/mod.rs

343 lines
9.7 KiB
Rust

pub mod pixel_format;
use spin::Lazy;
use vga::{colors::Color16, writers::GraphicsWriter};
use crate::vga_e::VGAE;
use pixel_format::*;
pub static SCREEN_BUFFER: Lazy<spin::Mutex<ScreenBuffer>> =
Lazy::new(|| spin::Mutex::new(ScreenBuffer::new(640, 480)));
const FONT_SCALE: f32 = 1.6;
const GLYPH_HEIGHT: f32 = 18.0;
const GLYPH_WIDTH: f32 = 10.0;
#[derive(Debug)]
pub struct ScreenSize {
pub x: usize,
pub y: usize,
}
impl ScreenSize {
pub fn new(x: usize, y: usize) -> Self {
Self { x, y }
}
}
pub enum GraphicsReturn {
Ok,
ImproperScreenSize,
}
pub struct ScreenBuffer {
pub size: ScreenSize,
pub clear_color: Rgba64,
pub buff: Box<[Rgba64]>,
}
impl ScreenBuffer {
// Add optional size later
pub fn new(x: usize, y: usize) -> Self {
Self {
size: ScreenSize::new(x, y),
clear_color: 0,
buff: vec![0u64; x * y].into_boxed_slice(),
}
}
pub fn draw_filled_circle(&mut self, cx: i32, cy: i32, radius: usize, color: Rgba64) {
let r = radius as i32 * 2;
for y in 0..640 {
for x in 0..480 {
let dx = cx - x as i32 * 2 - 1;
let dy = cy - y as i32 * 2 - 1;
if dx * dx + dy * dy <= r * r {
self.set_pixel(x, y, color);
};
}
}
}
#[inline]
pub fn set_pixel(&mut self, x: usize, y: usize, color: Rgba64) {
self.buff[y * self.size.x + x] = color;
}
pub fn clear(&mut self) {
self.buff = vec![0u64; self.buff.len()].into_boxed_slice();
}
pub fn blit(&mut self, _width: usize, _height: usize) {}
pub fn draw_filled_rect(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, color: Rgba64) {
for y in y1..y2 {
for x in x1..x2 {
self.set_pixel(x, y, color);
}
}
}
pub fn draw_unfilled_rect(
&mut self,
x1: usize,
y1: usize,
x2: usize,
y2: usize,
color: Rgba64,
) {
// x1 y1 => x2 y1 => x2 y2 => x1 y2 => x1 y1
self.draw_line(x1, y1, x2, y1, color);
self.draw_line(x2, y1, x2, y2, color);
self.draw_line(x2, y2, x1, y2, color);
self.draw_line(x1, y2, x1, y1, color);
}
#[inline]
pub fn draw_line(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, color: Rgba64) {
let x = crate::graphics::get_coordinates(
x1.try_into().unwrap(),
y1.try_into().unwrap(),
x2.try_into().unwrap(),
y2.try_into().unwrap(),
);
for coord in x {
self.set_pixel(coord.0, coord.1, color);
}
}
// pub fn shade(&mut self) {
// for y in 0..100 {
// for x in 0..100 {
// let rgba_ret = evaluate_shader(x, y, self.buff[y * self.size.x + x]);
// match rgba_ret {
// Ok(pixel) => {
// // info!("{:?}", pixel);
// self.set_pixel(x, y, pixel);
// }
// Err(err) => error!("{}", err),
// }
// }
// }
// info!("Shaders done");
// }
// TODO force clear
pub fn force_redraw(&mut self) {
let vga = VGAE.lock();
vga.set_mode();
vga.clear_screen(vga::colors::Color16::Black);
}
}
pub fn get_coordinates(x1: i32, y1: i32, x2: i32, y2: i32) -> Vec<(usize, usize)> {
let mut coordinates: Vec<(usize, usize)> = vec![];
let dx: i32 = i32::abs(x2 - x1);
let dy: i32 = i32::abs(y2 - y1);
let sx: i32 = {
if x1 < x2 {
1
} else {
-1
}
};
let sy: i32 = {
if y1 < y2 {
1
} else {
-1
}
};
let mut error: i32 = (if dx > dy { dx } else { -dy }) / 2;
let mut current_x: i32 = x1;
let mut current_y: i32 = y1;
loop {
coordinates.push((current_x as usize, current_y as usize));
// info!("0 {:?}", (current_x, current_y));
if current_x == x2 && current_y == y2 {
break;
}
let error2: i32 = error;
if error2 > -dx {
error -= dy;
current_x += sx;
}
if error2 < dy {
error += dx;
current_y += sy;
}
}
coordinates
}
pub trait VgaBuffer {
fn copy_to_buffer(&self) -> GraphicsReturn;
}
impl VgaBuffer for ScreenBuffer {
fn copy_to_buffer(&self) -> GraphicsReturn {
let mode = VGAE.lock();
for y in 0..self.size.y {
for x in 0..self.size.x {
let vga_color = into_vga_16(self.buff[y * self.size.x + x]);
if into_vga_16(self.clear_color) != vga_color {
mode.set_pixel(x, y, vga_color);
}
}
}
GraphicsReturn::Ok
}
}
pub fn into_vga_16(rgba_64: Rgba64) -> Color16 {
use pixel_format::ChannelValue::*;
use vga::colors::Color16::*;
match (
get_r(rgba_64).into(),
get_g(rgba_64).into(),
get_b(rgba_64).into(),
) {
(Dark, Dark, Dark) => Black,
(Dark, Dark, Low) => Black,
(Dark, Dark, Mid) => Blue,
(Dark, Dark, High) => Blue,
(Dark, Low, Dark) => Black,
(Dark, Low, Low) => Black,
(Dark, Low, Mid) => Blue,
(Dark, Low, High) => Blue,
(Dark, Mid, Dark) => Green,
(Dark, Mid, Low) => Green,
(Dark, Mid, Mid) => Cyan,
(Dark, Mid, High) => Cyan,
(Dark, High, Dark) => Green,
(Dark, High, Low) => Green,
(Dark, High, Mid) => Green,
(Dark, High, High) => Cyan,
(Low, Dark, Dark) => Black,
(Low, Dark, Low) => Black,
(Low, Dark, Mid) => Blue,
(Low, Dark, High) => Blue,
(Low, Low, Dark) => Black,
(Low, Low, Low) => DarkGrey,
(Low, Low, Mid) => LightGrey,
(Low, Low, High) => Blue,
(Low, Mid, Dark) => DarkGrey,
(Low, Mid, Low) => LightGrey,
(Low, Mid, Mid) => Cyan,
(Low, Mid, High) => Cyan,
(Low, High, Dark) => Green,
(Low, High, Low) => Green,
(Low, High, Mid) => Cyan,
(Low, High, High) => Cyan,
(Mid, Dark, Dark) => Red,
(Mid, Dark, Low) => Red,
(Mid, Dark, Mid) => Magenta,
(Mid, Dark, High) => Magenta,
(Mid, Low, Dark) => Brown,
(Mid, Low, Low) => Red,
(Mid, Low, Mid) => DarkGrey,
(Mid, Low, High) => LightBlue,
(Mid, Mid, Dark) => Brown,
(Mid, Mid, Low) => Brown,
(Mid, Mid, Mid) => LightGrey,
(Mid, Mid, High) => LightBlue,
(Mid, High, Dark) => Green,
(Mid, High, Low) => Green,
(Mid, High, Mid) => LightGreen,
(Mid, High, High) => LightCyan,
(High, Dark, Dark) => Red,
(High, Dark, _) => Magenta,
(High, Low, Dark) => Red,
(High, Low, Low) => LightRed,
(High, Low, Mid) => Pink,
(High, Low, High) => Magenta,
(High, Mid, Dark) => Yellow,
(High, Mid, Low) => Yellow,
(High, Mid, Mid) => LightRed,
(High, Mid, High) => Pink,
(High, High, Dark) => Yellow,
(High, High, Low) => White,
(High, High, Mid) => White,
(High, High, High) => White,
}
}
// TODO: Incorporate this into the ableos vga crate
// /// Draw a glyph on the screen at the given position
// ///
// /// # Arguments
// /// * `x` - the x position of the glyph
// /// * `y` - the y position of the glyph
// /// * `glyph` - the glyph to draw
// /// * `color` - the color of the glyph
// pub fn draw_char(&mut self, mut x: u32, mut y: u32, character: char, color: Rgba64) {
// // trace!["Judy Hopps is thicc af"];
// // let mode = *VGAE.lock();
// // trace!["She got them bouncy bunny buns"];
// let basic_multingual_plane = FontRef::try_from_slice(include_bytes!(
// "../../../ableos/assets/fonts/unifont-14.0.01.ttf"
// ))
// .unwrap();
// let supplementary_multilingual_plane = FontRef::try_from_slice(include_bytes!(
// "../../../ableos/assets/fonts/unifont_upper-14.0.01.ttf"
// ))
// .unwrap();
// // let mut has_char = false;
// // for x in font.codepoint_ids() {
// // if x.1 == character {
// // has_char = true;
// // break;
// // }
// // }
// let in_supp_plane = character as u32 > 0xffff;
// let plane = match in_supp_plane {
// false => basic_multingual_plane,
// true => supplementary_multilingual_plane,
// };
// match character {
// '\n' => {
// // x = 0;
// // y += (GLYPH_HEIGHT * FONT_SCALE) as u32;
// }
// _ => {
// let q_glyph: Glyph = plane.glyph_id(character).with_scale_and_position(
// 20.0 * FONT_SCALE,
// ab_glyph::point(GLYPH_WIDTH * FONT_SCALE, GLYPH_HEIGHT * FONT_SCALE),
// );
// // elf: I don't know if GLYPH_HEIGHT is in the right units here. I'm just guessing.
// if x as usize > self.size.x {
// x = 0;
// y += (GLYPH_HEIGHT * FONT_SCALE) as u32;
// }
// if let Some(q) = plane.outline_glyph(q_glyph) {
// q.draw(|gx, gy, c| {
// if c > 0.0015 {
// let corner = q.px_bounds().min;
// self.set_pixel(
// gx as usize + corner.x as usize + x as usize,
// gy as usize + corner.y as usize + y as usize,
// color,
// );
// }
// });
// }
// }
// }
// }