pub extern crate blit;
pub extern crate specs;
use anyhow::Result;
use blit::BlitBuffer;
use lazy_static::lazy_static;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
use specs::prelude::*;
use std::sync::RwLock;
lazy_static! {
static ref SPRITES: RwLock<Vec<BlitBuffer>> = RwLock::new(vec![]);
}
#[derive(Debug, Clone)]
pub struct Sprite {
pub(crate) reference: SpriteRef,
pos: (i32, i32),
rot: i16,
}
impl Component for Sprite {
type Storage = VecStorage<Self>;
}
impl Sprite {
pub fn new(sprite_reference: SpriteRef) -> Self {
Self {
reference: sprite_reference,
pos: (0, 0),
rot: 0,
}
}
pub fn set_pos(&mut self, x: i32, y: i32) {
self.pos.0 = x;
self.pos.1 = y;
}
pub fn pos(&self) -> (i32, i32) {
self.pos
}
pub fn set_rot(&mut self, rotation: i16) {
let mut rotation = rotation;
while rotation < 0 {
rotation += 360;
}
while rotation > 360 {
rotation -= 360;
}
self.rot = rotation;
}
pub fn rot(&self) -> i16 {
self.rot
}
pub(crate) fn render_info(&self) -> (usize, i32, i32) {
self.reference.render_info(self.rot)
}
}
#[derive(Debug, Clone)]
pub struct SpriteRef {
rot_range_start: i16,
rot_divisor: f64,
sprites: Vec<(usize, i32, i32)>,
}
impl SpriteRef {
pub(crate) fn render_info(&self, rotation: i16) -> (usize, i32, i32) {
let rotation_index = (((rotation - self.rot_range_start) % 360) as f64) / self.rot_divisor;
*self
.sprites
.get(rotation_index as usize)
.unwrap_or(&self.sprites[0])
}
}
#[derive(Debug, Default)]
pub struct PixelBuffer {
pub(crate) pixels: Vec<u32>,
pub(crate) width: usize,
pub(crate) height: usize,
}
impl PixelBuffer {
pub fn new(width: usize, height: usize) -> Self {
Self {
pixels: vec![0; width * height],
width,
height,
}
}
pub fn pixels(&self) -> &Vec<u32> {
&self.pixels
}
pub fn pixels_mut(&mut self) -> &mut Vec<u32> {
&mut self.pixels
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn clear(&mut self, color: u32) {
for pixel in self.pixels.iter_mut() {
*pixel = color;
}
}
}
pub struct RenderSystem;
impl<'a> System<'a> for RenderSystem {
type SystemData = (Write<'a, PixelBuffer>, ReadStorage<'a, Sprite>);
fn run(&mut self, (mut buffer, sprites): Self::SystemData) {
let width = buffer.width;
for sprite_component in sprites.join() {
let (index, x_offset, y_offset) = sprite_component.render_info();
let sprite = &SPRITES.read().unwrap()[index];
let pos = (
sprite_component.pos.0 + x_offset,
sprite_component.pos.1 + y_offset,
);
sprite.blit(&mut buffer.pixels, width, pos);
}
}
}
pub fn load(sprite: BlitBuffer) -> Result<SpriteRef> {
load_rotations(sprite, 1)
}
pub fn load_rotations(sprite: BlitBuffer, rotations: u16) -> Result<SpriteRef> {
load_rotations_range(sprite, rotations, (0, 360))
}
#[cfg(not(feature = "parallel"))]
pub fn load_rotations_range(
sprite: BlitBuffer,
rotations: u16,
range: (i16, i16),
) -> Result<SpriteRef> {
let rotations = if rotations == 0 { 1 } else { rotations };
let rot_divisor = (range.1 - range.0) as f64 / (rotations as f64);
let raw_buffer = sprite.to_raw_buffer();
let sprites = (0..rotations)
.into_iter()
.map(|r| {
let (rotated_width, rotated_height, rotated) = rotsprite::rotsprite(
&raw_buffer,
&sprite.mask_color().u32(),
sprite.size().0 as usize,
range.0 as f64 + (r as f64 * rot_divisor),
)?;
let rotated_sprite =
BlitBuffer::from_buffer(&rotated, rotated_width as i32, sprite.mask_color());
let mut sprites_vec = SPRITES.write().unwrap();
sprites_vec.push(rotated_sprite);
let index = sprites_vec.len() - 1;
let x_offset = (sprite.width() - rotated_width as i32) / 2;
let y_offset = (sprite.height() - rotated_height as i32) / 2;
Ok((index, x_offset, y_offset))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.collect::<_>();
Ok(SpriteRef {
rot_range_start: range.0,
rot_divisor,
sprites,
})
}
#[cfg(feature = "parallel")]
pub fn load_rotations_range(
sprite: BlitBuffer,
rotations: u16,
range: (i16, i16),
) -> Result<SpriteRef> {
let rotations = if rotations == 0 { 1 } else { rotations };
let rot_divisor = (range.1 - range.0) as f64 / (rotations as f64);
let raw_buffer = sprite.to_raw_buffer();
let sprites = (0..rotations)
.into_par_iter()
.map(|r| {
let (rotated_width, rotated_height, rotated) = rotsprite::rotsprite(
&raw_buffer,
&sprite.mask_color().u32(),
sprite.size().0 as usize,
range.0 as f64 + (r as f64 * rot_divisor),
)?;
let rotated_sprite =
BlitBuffer::from_buffer(&rotated, rotated_width as i32, sprite.mask_color());
let mut sprites_vec = SPRITES.write().unwrap();
sprites_vec.push(rotated_sprite);
let index = sprites_vec.len() - 1;
let x_offset = (sprite.width() - rotated_width as i32) / 2;
let y_offset = (sprite.height() - rotated_height as i32) / 2;
Ok((index, x_offset, y_offset))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.collect::<_>();
Ok(SpriteRef {
rot_range_start: range.0,
rot_divisor,
sprites,
})
}
pub unsafe fn clear_all() {
SPRITES.write().unwrap().clear();
}