From 6afa8bafdfafbfb9b103d3b4b76947ccdcc949a0 Mon Sep 17 00:00:00 2001 From: dani Date: Mon, 10 Jul 2023 20:05:25 +0000 Subject: [PATCH] split image into image8 and image32, also made run work on screens of both --- examples/img32.rs | 21 ++++++ examples/test.rs | 6 +- src/console.rs | 4 +- src/hexmap.rs | 4 +- src/image.rs | 158 +++--------------------------------------- src/image32.rs | 64 +++++++++++++++++ src/image8.rs | 173 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- src/tileset.rs | 13 ++-- src/window.rs | 120 ++++++++++++++++++++++++++------ 10 files changed, 383 insertions(+), 185 deletions(-) create mode 100644 examples/img32.rs create mode 100644 src/image32.rs create mode 100644 src/image8.rs diff --git a/examples/img32.rs b/examples/img32.rs new file mode 100644 index 0000000..61b1cd4 --- /dev/null +++ b/examples/img32.rs @@ -0,0 +1,21 @@ +use skunk2d::{run, Event, Game, Image32, WindowState}; + +struct World {} + +impl Game for World { + fn new(window_state: &mut WindowState) -> Self { + World {} + } + + fn update(&mut self, window_state: &mut WindowState) {} + + fn on_event(&mut self, window_state: &mut WindowState, event: Event) {} + + fn draw(&self, target: &mut Image32) { + target.fill([0xc0, 0xf0, 0xd0, 0xff]); + } +} + +fn main() { + run::(1920 / 3, 1080 / 3, 60); +} diff --git a/examples/test.rs b/examples/test.rs index 792ef1b..dc9a358 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -12,10 +12,10 @@ struct World { fn main() { rand::thread_rng().gen::(); - run::(WIDTH, HEIGHT, 10000); + run::(WIDTH, HEIGHT, 10000); } -impl Game for World { +impl Game for World { fn new(window_state: &mut WindowState) -> Self { window_state.set_palette(0, 0xc0, 0xf0, 0xd0); window_state.scramble_palette(); @@ -48,7 +48,7 @@ impl Game for World { } } - fn draw(&self, target: &mut Image) { + fn draw(&self, target: &mut Image8) { target.clear(); target.draw_hexmap(IVec2 { x: 0, y: 0 }, &self.map); } diff --git a/src/console.rs b/src/console.rs index 89be93c..93287af 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,4 +1,4 @@ -use crate::{IRect, Image, Tileset}; +use crate::{IRect, Image8, Tileset}; use glam::IVec2; use std::collections::VecDeque; @@ -30,7 +30,7 @@ impl Console { } } - pub(crate) fn draw_to_image(&self, target: &mut Image, pos: IVec2, font: &Tileset) { + pub(crate) fn draw_to_image(&self, target: &mut Image8, pos: IVec2, font: &Tileset) { let height = self.max_lines as i32 * font.tile_size().y; let width = self.max_line_length as i32 * font.tile_size().x; target.fill_rect( diff --git a/src/hexmap.rs b/src/hexmap.rs index b6de7f6..2734bd0 100644 --- a/src/hexmap.rs +++ b/src/hexmap.rs @@ -1,5 +1,5 @@ use crate::vec_util::IVec2Helper; -use crate::{Image, Tileset}; +use crate::{Image8, Tileset}; use glam::IVec2; use rand::distributions::Standard; use rand::prelude::Distribution; @@ -121,7 +121,7 @@ impl HexMap { } //pubcrate - pub(crate) fn draw_tile_to_image(&self, target: &mut Image, coord: IVec2, offset: IVec2) { + pub(crate) fn draw_tile_to_image(&self, target: &mut Image8, coord: IVec2, offset: IVec2) { target.draw_image( self.coord_to_pixel(coord) + offset, self.tileset.get(self.data[self.coord_to_idx(coord)]), diff --git a/src/image.rs b/src/image.rs index 69cb55e..2883a09 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,151 +1,13 @@ -use crate::vec_util::IVec2Helper; -use crate::{HexMap, IRect, Tileset}; use glam::IVec2; -use std::fs; -use std::rc::Rc; -//todo: make dynamically different bitdepths -pub struct Image { - data: Vec, - size: IVec2, -} - -impl Image { - pub fn new(size: IVec2) -> Self { - Image { - data: vec![0u8; (size.x * size.y) as usize], - size, - } - } - - pub fn load(path: &str) -> Rc { - Self::load_data(fs::read(path).unwrap().as_slice()) - } - - pub fn load_data(data: &[u8]) -> Rc { - let mut options = gif::DecodeOptions::new(); - options.set_color_output(gif::ColorOutput::Indexed); - let mut decoder = options.read_info(data).unwrap(); - - let x = decoder.width(); - let y = decoder.height(); - - let mut data = vec![0u8; (x * y) as usize]; - decoder.next_frame_info().unwrap(); - decoder.read_into_buffer(&mut data).unwrap(); - Image { - data, - size: IVec2 { - x: x as i32, - y: y as i32, - }, - } - .into() - } - - pub fn clear(&mut self) { - self.fill(0); - } - - pub fn data_mut(&mut self) -> &mut [u8] { - self.data.as_mut_slice() - } - - pub fn data(&self) -> &[u8] { - self.data.as_slice() - } - - pub fn draw_hexmap(&mut self, pos: IVec2, hexmap: &HexMap) { - for i in hexmap.size().to_rect().iter() { - hexmap.draw_tile_to_image(self, i, pos); - } - } - - pub fn draw_image(&mut self, pos: IVec2, image: &Image) { - self.draw_image_partial( - pos, - image, - IRect { - pos: IVec2::ZERO, - size: image.size, - }, - ); - } - - pub fn draw_image_partial(&mut self, pos: IVec2, image: &Image, src_rect: IRect) { - //todo: write proper implementation later - for i in IVec2::ZERO.iter_to(src_rect.size) { - //todo: implement better(very stupid to do per pixel) - if self.size.to_rect().contains(i + pos) { - let p = image.get_pixel(i + src_rect.pos); - if p > 0 { - self.set_pixel(i + pos, p); - } - } - } - } - - pub fn draw_line(&mut self, pos1: IVec2, pos2: IVec2, color: u8) { - let (x1, y1, x2, y2) = (pos1.x, pos1.y, pos2.x, pos2.y); - - let mut x = x1 as f32; - let mut y = y1 as f32; - let xdiff = (x2 - x1) as f32; - let ydiff = (y2 - y1) as f32; - let step = if xdiff.abs() > ydiff.abs() { - xdiff.abs() - } else { - ydiff.abs() - }; - let xs = xdiff / step; - let ys = ydiff / step; - for _ in 1..=(step as i32) { - self.set_pixel( - IVec2 { - x: x as i32, - y: y as i32, - }, - color, - ); - x += xs; - y += ys; - } - } - - pub fn draw_string(&mut self, pos: IVec2, str: &str, font: &Tileset) { - assert!(str.is_ascii()); - let array = str.as_bytes(); - //for i in 0..array.len() - for (i, idx) in array.iter().enumerate() { - self.draw_image( - IVec2 { - x: pos.x + font.tile_size().x * i as i32, - y: pos.y, - }, - font.get(*idx as i32), - ); - } - } - - pub fn fill(&mut self, color: u8) { - self.data.fill(color); - } - - pub fn fill_rect(&mut self, rect: IRect, color: u8) { - for pos in rect.iter() { - self.set_pixel(pos, color); - } - } - - pub fn get_pixel(&self, pos: IVec2) -> u8 { - self.data[(pos.x + self.size.x * pos.y) as usize] - } - - pub fn set_pixel(&mut self, pos: IVec2, color: u8) { - self.data[(pos.x + self.size.x * pos.y) as usize] = color; - } - - pub fn size(&self) -> IVec2 { - self.size - } +pub trait Image { + fn new(size: IVec2) -> Self; + + fn clear(&mut self); + + fn data_mut(&mut self) -> &mut [u8]; + + fn data(&self) -> &[u8]; + + fn size(&self) -> IVec2; } diff --git a/src/image32.rs b/src/image32.rs new file mode 100644 index 0000000..021b114 --- /dev/null +++ b/src/image32.rs @@ -0,0 +1,64 @@ +use crate::image::Image; +use crate::vec_util::IVec2Helper; +use crate::IRect; +use glam::IVec2; + +pub type Pixel32 = [u8; 4]; + +pub struct Image32 { + data: Vec, + size: IVec2, +} + +impl Image32 { + pub fn fill(&mut self, color: Pixel32) { + //todo: performance + for i in (0..self.size.size() * 4).step_by(4) { + self.data[i..i + 4].copy_from_slice(&color); + } + } + + pub fn fill_rect(&mut self, rect: IRect, color: Pixel32) { + for pos in rect.iter() { + self.set_pixel(pos, color); + } + } + + pub fn get_pixel(&self, pos: IVec2) -> Pixel32 { + //todo: check if performance? + let i = (pos.x + self.size.x * pos.y * 4) as usize; + let mut a = [0u8; 4]; + a.copy_from_slice(&self.data[i..i + 4]); + a + } + + pub fn set_pixel(&mut self, pos: IVec2, color: Pixel32) { + let i = (pos.x + self.size.x * pos.y * 4) as usize; + self.data[i..i + 4].copy_from_slice(&color); + } +} + +impl Image for Image32 { + fn new(size: IVec2) -> Self { + Image32 { + data: vec![0u8; (size.x * size.y) as usize * 4], + size, + } + } + + fn clear(&mut self) { + self.data.fill(0); + } + + fn data_mut(&mut self) -> &mut [u8] { + self.data.as_mut_slice() + } + + fn data(&self) -> &[u8] { + self.data.as_slice() + } + + fn size(&self) -> IVec2 { + self.size + } +} diff --git a/src/image8.rs b/src/image8.rs new file mode 100644 index 0000000..1513a6d --- /dev/null +++ b/src/image8.rs @@ -0,0 +1,173 @@ +use crate::image::Image; +use crate::vec_util::IVec2Helper; +use crate::{HexMap, IRect, Tileset}; +use glam::IVec2; +use std::fs; +use std::rc::Rc; + +//todo: make dynamically different bitdepths +pub struct Image8 { + data: Vec, + size: IVec2, +} + +impl Image8 { + pub fn new(size: IVec2) -> Self { + Image8 { + data: vec![0u8; (size.x * size.y) as usize], + size, + } + } + + pub fn load(path: &str) -> Rc { + Self::load_data(fs::read(path).unwrap().as_slice()) + } + + pub fn load_data(data: &[u8]) -> Rc { + let mut options = gif::DecodeOptions::new(); + options.set_color_output(gif::ColorOutput::Indexed); + let mut decoder = options.read_info(data).unwrap(); + + let x = decoder.width(); + let y = decoder.height(); + + let mut data = vec![0u8; (x * y) as usize]; + decoder.next_frame_info().unwrap(); + decoder.read_into_buffer(&mut data).unwrap(); + Image8 { + data, + size: IVec2 { + x: x as i32, + y: y as i32, + }, + } + .into() + } + + pub fn clear(&mut self) { + self.fill(0); + } + + pub fn data_mut(&mut self) -> &mut [u8] { + self.data.as_mut_slice() + } + + pub fn data(&self) -> &[u8] { + self.data.as_slice() + } + + pub fn draw_hexmap(&mut self, pos: IVec2, hexmap: &HexMap) { + for i in hexmap.size().to_rect().iter() { + hexmap.draw_tile_to_image(self, i, pos); + } + } + + pub fn draw_image(&mut self, pos: IVec2, image: &Image8) { + self.draw_image_partial( + pos, + image, + IRect { + pos: IVec2::ZERO, + size: image.size, + }, + ); + } + + pub fn draw_image_partial(&mut self, pos: IVec2, image: &Image8, src_rect: IRect) { + //todo: write proper implementation later + for i in IVec2::ZERO.iter_to(src_rect.size) { + //todo: implement better(very stupid to do per pixel) + if self.size.to_rect().contains(i + pos) { + let p = image.get_pixel(i + src_rect.pos); + if p > 0 { + self.set_pixel(i + pos, p); + } + } + } + } + + pub fn draw_line(&mut self, pos1: IVec2, pos2: IVec2, color: u8) { + let (x1, y1, x2, y2) = (pos1.x, pos1.y, pos2.x, pos2.y); + + let mut x = x1 as f32; + let mut y = y1 as f32; + let xdiff = (x2 - x1) as f32; + let ydiff = (y2 - y1) as f32; + let step = if xdiff.abs() > ydiff.abs() { + xdiff.abs() + } else { + ydiff.abs() + }; + let xs = xdiff / step; + let ys = ydiff / step; + for _ in 1..=(step as i32) { + self.set_pixel( + IVec2 { + x: x as i32, + y: y as i32, + }, + color, + ); + x += xs; + y += ys; + } + } + + pub fn draw_string(&mut self, pos: IVec2, str: &str, font: &Tileset) { + assert!(str.is_ascii()); + let array = str.as_bytes(); + //for i in 0..array.len() + for (i, idx) in array.iter().enumerate() { + self.draw_image( + IVec2 { + x: pos.x + font.tile_size().x * i as i32, + y: pos.y, + }, + font.get(*idx as i32), + ); + } + } + + pub fn fill(&mut self, color: u8) { + self.data.fill(color); + } + + pub fn fill_rect(&mut self, rect: IRect, color: u8) { + for pos in rect.iter() { + self.set_pixel(pos, color); + } + } + + pub fn get_pixel(&self, pos: IVec2) -> u8 { + self.data[(pos.x + self.size.x * pos.y) as usize] + } + + pub fn set_pixel(&mut self, pos: IVec2, color: u8) { + self.data[(pos.x + self.size.x * pos.y) as usize] = color; + } +} + +impl Image for Image8 { + fn new(size: IVec2) -> Self { + Image8 { + data: vec![0u8; (size.x * size.y) as usize], + size, + } + } + + fn clear(&mut self) { + self.fill(0); + } + + fn data_mut(&mut self) -> &mut [u8] { + self.data.as_mut_slice() + } + + fn data(&self) -> &[u8] { + self.data.as_slice() + } + + fn size(&self) -> IVec2 { + self.size + } +} diff --git a/src/lib.rs b/src/lib.rs index ccdf3b4..e617fd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,16 @@ mod console; mod hexmap; mod image; +mod image32; +mod image8; mod rect; mod tileset; mod vec_util; mod window; pub use hexmap::*; -pub use image::*; +pub use image32::*; +pub use image8::*; pub use rect::*; pub use tileset::*; pub use window::*; diff --git a/src/tileset.rs b/src/tileset.rs index 77ce5c9..ef4d497 100644 --- a/src/tileset.rs +++ b/src/tileset.rs @@ -1,5 +1,6 @@ +use crate::image::Image; use crate::vec_util::IVec2Helper; -use crate::{IRect, Image}; +use crate::{IRect, Image8}; use glam::IVec2; use std::fs; use std::rc::Rc; @@ -7,7 +8,7 @@ use std::rc::Rc; pub struct Tileset { count: i32, size: IVec2, - images: Vec, + images: Vec, } impl Tileset { @@ -16,14 +17,14 @@ impl Tileset { } pub fn load_data(data: &[u8], tile_count: IVec2) -> Rc { - let img = Image::load_data(data); - let mut images: Vec = vec![]; + let img = Image8::load_data(data); + let mut images: Vec = vec![]; let size = IVec2 { x: img.size().x / tile_count.x, y: img.size().y / tile_count.y, }; for tile in tile_count.to_rect().iter() { - let mut image = Image::new(size); + let mut image = Image8::new(size); image.draw_image_partial( IVec2::ZERO, &img, @@ -45,7 +46,7 @@ impl Tileset { .into() } - pub fn get(&self, idx: i32) -> &Image { + pub fn get(&self, idx: i32) -> &Image8 { &self.images[idx as usize] } diff --git a/src/window.rs b/src/window.rs index 6ca724b..c765443 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,8 +1,10 @@ use crate::console::Console; use crate::image::Image; -use crate::Tileset; +use crate::image8::Image8; +use crate::{Image32, Tileset}; use glam::IVec2; use rand::Rng; +use softbuffer::Buffer; use std::cmp::{max, min}; use std::num::NonZeroU32; use std::time::{Duration, Instant}; @@ -30,7 +32,7 @@ pub enum Event { MouseClick(MouseButton, IVec2), } -pub trait Game { +pub trait Game { fn new(window_state: &mut WindowState) -> Self; fn update(&mut self, window_state: &mut WindowState); fn on_event(&mut self, window_state: &mut WindowState, event: Event); @@ -75,7 +77,84 @@ impl WindowState { } } -pub fn run(width: i32, height: i32, target_fps: u32) { +pub trait RenderToScreen { + #[allow(clippy::too_many_arguments)] + fn render_to_screen( + &self, + buf: &mut Buffer, + palette: &[u32; 256], + width: usize, + window_width: usize, + scale: usize, + chunk_size: usize, + screen_data: &[u8], + ); +} + +impl RenderToScreen for Image8 { + fn render_to_screen( + &self, + buf: &mut Buffer, + palette: &[u32; 256], + width: usize, + window_width: usize, + scale: usize, + chunk_size: usize, + screen_data: &[u8], + ) { + buf.chunks_exact_mut(chunk_size) + .enumerate() + .for_each(|(y, chunk)| { + for x in 0..width { + let p = palette[screen_data[x + y * width] as usize]; + for scaley in 0..scale { + for scalex in 0..scale { + let sx = x * scale + scalex; + chunk[sx + scaley * window_width] = p; + } + } + } + }); + } +} + +impl RenderToScreen for Image32 { + fn render_to_screen( + &self, + buf: &mut Buffer, + _palette: &[u32; 256], + width: usize, + window_width: usize, + scale: usize, + chunk_size: usize, + screen_data: &[u8], + ) { + let screen_data32 = unsafe { + let (_, middle, _) = screen_data.align_to::(); + //todo: add assert? + middle + }; + buf.chunks_exact_mut(chunk_size) + .enumerate() + .for_each(|(y, chunk)| { + for x in 0..width { + let p = screen_data32[x + y * width]; + for scaley in 0..scale { + for scalex in 0..scale { + let sx = x * scale + scalex; + chunk[sx + scaley * window_width] = p; + } + } + } + }); + } +} + +pub fn run + 'static>( + width: i32, + height: i32, + target_fps: u32, +) { let mut event_loop = EventLoop::new(); let window = { let size = LogicalSize::new(width as f64, height as f64); @@ -89,10 +168,10 @@ pub fn run(width: i32, height: i32, target_fps: u32) { .unwrap() }; - //todo: replace Pixels with custom thingie (startup time slow because wgpu?) let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); - let mut screen = Image::new(IVec2 { + + let mut screen = TImage::new(IVec2 { x: width, y: height, }); @@ -101,7 +180,7 @@ pub fn run(width: i32, height: i32, target_fps: u32) { x: width / internal_font.tile_size().x, y: height / internal_font.tile_size().y / 3, }); - let mut game = T::new(&mut window_state); + let mut game = TGame::new(&mut window_state); window_state.console.add("Initialising Skunk2d"); let mut display_console = true; @@ -200,13 +279,15 @@ pub fn run(width: i32, height: i32, target_fps: u32) { if Instant::now() - last_frame >= target_frame_duration { game.update(&mut window_state); game.draw(&mut screen); + //todo: make this work again + /* if display_console { window_state.console.draw_to_image( &mut screen, IVec2::ZERO, &internal_font, ); - } + } */ let size = window.inner_size(); surface @@ -221,7 +302,6 @@ pub fn run(width: i32, height: i32, target_fps: u32) { *off_y = (size.height as usize - height as usize * *scale) / 2; let mut buf = surface.buffer_mut().unwrap(); - //for y in 0..height as usize { { let width = width as usize; @@ -229,21 +309,15 @@ pub fn run(width: i32, height: i32, target_fps: u32) { let scale = *scale; let chunk_size = scale * window_width; let screen_data = screen.data(); - buf.chunks_exact_mut(chunk_size) - .enumerate() - .for_each(|(y, chunk)| { - for x in 0..width { - let p = - window_state.palette[screen_data[x + y * width] as usize]; - //let p = 0; - for scaley in 0..scale { - for scalex in 0..scale { - let sx = x * scale + scalex; - chunk[sx + scaley * window_width] = p; - } - } - } - }); + screen.render_to_screen( + &mut buf, + &window_state.palette, + width, + window_width, + scale, + chunk_size, + &screen_data, + ); } buf.present().unwrap();