split image into image8 and image32, also made run work on screens of both

This commit is contained in:
dani 2023-07-10 20:05:25 +00:00
parent 588797c4b7
commit 6afa8bafdf
10 changed files with 383 additions and 185 deletions

21
examples/img32.rs Normal file
View File

@ -0,0 +1,21 @@
use skunk2d::{run, Event, Game, Image32, WindowState};
struct World {}
impl Game<Image32> 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::<Image32, World>(1920 / 3, 1080 / 3, 60);
}

View File

@ -12,10 +12,10 @@ struct World {
fn main() { fn main() {
rand::thread_rng().gen::<Direction>(); rand::thread_rng().gen::<Direction>();
run::<World>(WIDTH, HEIGHT, 10000); run::<Image8, World>(WIDTH, HEIGHT, 10000);
} }
impl Game for World { impl Game<Image8> for World {
fn new(window_state: &mut WindowState) -> Self { fn new(window_state: &mut WindowState) -> Self {
window_state.set_palette(0, 0xc0, 0xf0, 0xd0); window_state.set_palette(0, 0xc0, 0xf0, 0xd0);
window_state.scramble_palette(); 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.clear();
target.draw_hexmap(IVec2 { x: 0, y: 0 }, &self.map); target.draw_hexmap(IVec2 { x: 0, y: 0 }, &self.map);
} }

View File

@ -1,4 +1,4 @@
use crate::{IRect, Image, Tileset}; use crate::{IRect, Image8, Tileset};
use glam::IVec2; use glam::IVec2;
use std::collections::VecDeque; 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 height = self.max_lines as i32 * font.tile_size().y;
let width = self.max_line_length as i32 * font.tile_size().x; let width = self.max_line_length as i32 * font.tile_size().x;
target.fill_rect( target.fill_rect(

View File

@ -1,5 +1,5 @@
use crate::vec_util::IVec2Helper; use crate::vec_util::IVec2Helper;
use crate::{Image, Tileset}; use crate::{Image8, Tileset};
use glam::IVec2; use glam::IVec2;
use rand::distributions::Standard; use rand::distributions::Standard;
use rand::prelude::Distribution; use rand::prelude::Distribution;
@ -121,7 +121,7 @@ impl HexMap {
} }
//pubcrate //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( target.draw_image(
self.coord_to_pixel(coord) + offset, self.coord_to_pixel(coord) + offset,
self.tileset.get(self.data[self.coord_to_idx(coord)]), self.tileset.get(self.data[self.coord_to_idx(coord)]),

View File

@ -1,151 +1,13 @@
use crate::vec_util::IVec2Helper;
use crate::{HexMap, IRect, Tileset};
use glam::IVec2; use glam::IVec2;
use std::fs;
use std::rc::Rc;
//todo: make dynamically different bitdepths pub trait Image {
pub struct Image { fn new(size: IVec2) -> Self;
data: Vec<u8>,
size: IVec2, fn clear(&mut self);
}
fn data_mut(&mut self) -> &mut [u8];
impl Image {
pub fn new(size: IVec2) -> Self { fn data(&self) -> &[u8];
Image {
data: vec![0u8; (size.x * size.y) as usize], fn size(&self) -> IVec2;
size,
}
}
pub fn load(path: &str) -> Rc<Self> {
Self::load_data(fs::read(path).unwrap().as_slice())
}
pub fn load_data(data: &[u8]) -> Rc<Self> {
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
}
} }

64
src/image32.rs Normal file
View File

@ -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<u8>,
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
}
}

173
src/image8.rs Normal file
View File

@ -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<u8>,
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> {
Self::load_data(fs::read(path).unwrap().as_slice())
}
pub fn load_data(data: &[u8]) -> Rc<Self> {
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
}
}

View File

@ -1,13 +1,16 @@
mod console; mod console;
mod hexmap; mod hexmap;
mod image; mod image;
mod image32;
mod image8;
mod rect; mod rect;
mod tileset; mod tileset;
mod vec_util; mod vec_util;
mod window; mod window;
pub use hexmap::*; pub use hexmap::*;
pub use image::*; pub use image32::*;
pub use image8::*;
pub use rect::*; pub use rect::*;
pub use tileset::*; pub use tileset::*;
pub use window::*; pub use window::*;

View File

@ -1,5 +1,6 @@
use crate::image::Image;
use crate::vec_util::IVec2Helper; use crate::vec_util::IVec2Helper;
use crate::{IRect, Image}; use crate::{IRect, Image8};
use glam::IVec2; use glam::IVec2;
use std::fs; use std::fs;
use std::rc::Rc; use std::rc::Rc;
@ -7,7 +8,7 @@ use std::rc::Rc;
pub struct Tileset { pub struct Tileset {
count: i32, count: i32,
size: IVec2, size: IVec2,
images: Vec<Image>, images: Vec<Image8>,
} }
impl Tileset { impl Tileset {
@ -16,14 +17,14 @@ impl Tileset {
} }
pub fn load_data(data: &[u8], tile_count: IVec2) -> Rc<Self> { pub fn load_data(data: &[u8], tile_count: IVec2) -> Rc<Self> {
let img = Image::load_data(data); let img = Image8::load_data(data);
let mut images: Vec<Image> = vec![]; let mut images: Vec<Image8> = vec![];
let size = IVec2 { let size = IVec2 {
x: img.size().x / tile_count.x, x: img.size().x / tile_count.x,
y: img.size().y / tile_count.y, y: img.size().y / tile_count.y,
}; };
for tile in tile_count.to_rect().iter() { for tile in tile_count.to_rect().iter() {
let mut image = Image::new(size); let mut image = Image8::new(size);
image.draw_image_partial( image.draw_image_partial(
IVec2::ZERO, IVec2::ZERO,
&img, &img,
@ -45,7 +46,7 @@ impl Tileset {
.into() .into()
} }
pub fn get(&self, idx: i32) -> &Image { pub fn get(&self, idx: i32) -> &Image8 {
&self.images[idx as usize] &self.images[idx as usize]
} }

View File

@ -1,8 +1,10 @@
use crate::console::Console; use crate::console::Console;
use crate::image::Image; use crate::image::Image;
use crate::Tileset; use crate::image8::Image8;
use crate::{Image32, Tileset};
use glam::IVec2; use glam::IVec2;
use rand::Rng; use rand::Rng;
use softbuffer::Buffer;
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -30,7 +32,7 @@ pub enum Event {
MouseClick(MouseButton, IVec2), MouseClick(MouseButton, IVec2),
} }
pub trait Game { pub trait Game<Image> {
fn new(window_state: &mut WindowState) -> Self; fn new(window_state: &mut WindowState) -> Self;
fn update(&mut self, window_state: &mut WindowState); fn update(&mut self, window_state: &mut WindowState);
fn on_event(&mut self, window_state: &mut WindowState, event: Event); fn on_event(&mut self, window_state: &mut WindowState, event: Event);
@ -75,7 +77,84 @@ impl WindowState {
} }
} }
pub fn run<T: Game + 'static>(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::<u32>();
//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<TImage: Image + RenderToScreen, TGame: Game<TImage> + 'static>(
width: i32,
height: i32,
target_fps: u32,
) {
let mut event_loop = EventLoop::new(); let mut event_loop = EventLoop::new();
let window = { let window = {
let size = LogicalSize::new(width as f64, height as f64); let size = LogicalSize::new(width as f64, height as f64);
@ -89,10 +168,10 @@ pub fn run<T: Game + 'static>(width: i32, height: i32, target_fps: u32) {
.unwrap() .unwrap()
}; };
//todo: replace Pixels with custom thingie (startup time slow because wgpu?)
let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &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, x: width,
y: height, y: height,
}); });
@ -101,7 +180,7 @@ pub fn run<T: Game + 'static>(width: i32, height: i32, target_fps: u32) {
x: width / internal_font.tile_size().x, x: width / internal_font.tile_size().x,
y: height / internal_font.tile_size().y / 3, 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"); window_state.console.add("Initialising Skunk2d");
let mut display_console = true; let mut display_console = true;
@ -200,13 +279,15 @@ pub fn run<T: Game + 'static>(width: i32, height: i32, target_fps: u32) {
if Instant::now() - last_frame >= target_frame_duration { if Instant::now() - last_frame >= target_frame_duration {
game.update(&mut window_state); game.update(&mut window_state);
game.draw(&mut screen); game.draw(&mut screen);
//todo: make this work again
/*
if display_console { if display_console {
window_state.console.draw_to_image( window_state.console.draw_to_image(
&mut screen, &mut screen,
IVec2::ZERO, IVec2::ZERO,
&internal_font, &internal_font,
); );
} } */
let size = window.inner_size(); let size = window.inner_size();
surface surface
@ -221,7 +302,6 @@ pub fn run<T: Game + 'static>(width: i32, height: i32, target_fps: u32) {
*off_y = (size.height as usize - height as usize * *scale) / 2; *off_y = (size.height as usize - height as usize * *scale) / 2;
let mut buf = surface.buffer_mut().unwrap(); let mut buf = surface.buffer_mut().unwrap();
//for y in 0..height as usize {
{ {
let width = width as usize; let width = width as usize;
@ -229,21 +309,15 @@ pub fn run<T: Game + 'static>(width: i32, height: i32, target_fps: u32) {
let scale = *scale; let scale = *scale;
let chunk_size = scale * window_width; let chunk_size = scale * window_width;
let screen_data = screen.data(); let screen_data = screen.data();
buf.chunks_exact_mut(chunk_size) screen.render_to_screen(
.enumerate() &mut buf,
.for_each(|(y, chunk)| { &window_state.palette,
for x in 0..width { width,
let p = window_width,
window_state.palette[screen_data[x + y * width] as usize]; scale,
//let p = 0; chunk_size,
for scaley in 0..scale { &screen_data,
for scalex in 0..scale { );
let sx = x * scale + scalex;
chunk[sx + scaley * window_width] = p;
}
}
}
});
} }
buf.present().unwrap(); buf.present().unwrap();