use ab_glyph::{Font, FontRef, Glyph}; use shadeable::{ evaluate_shader, pixel_format::{get_b, get_g, get_r, Rgba64}, }; use spin::Lazy; use vga::{colors::Color16, writers::GraphicsWriter}; use crate::vga_e::VGAE; pub static SCREEN_BUFFER: Lazy> = 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); } /// 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, ); } }); } } } } } 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 { // use shadeable::pixel_format::into_vga_16; 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 shadeable::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, } }