use std::ops::Sub;
use image::GenericImageView;
use crate::color::*;
#[derive(Clone)]
pub struct Buffer {
raw_buffer: Vec<u8>,
width: u32, height: u32,
}
impl Buffer {
pub fn new(width: u32, height: u32) -> Self {
Self {
raw_buffer: vec![0; width as usize * height as usize * 4],
width,
height,
}
}
pub fn raw_buffer(&mut self) -> &mut [u8] {
&mut self.raw_buffer
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
}
pub trait Renderable {
fn render(&mut self, buffer: &mut Buffer);
}
#[derive(Clone, Copy)]
pub struct Pixel {
pub x: f32, pub y: f32,
pub color: Color,
}
impl Pixel {
pub fn new(x: f32, y: f32, color: Color) -> Self {
Self {
x, y,
color,
}
}
}
impl Renderable for Pixel {
fn render(&mut self, buffer: &mut Buffer) {
if self.x < buffer.width() as f32 &&
self.y < buffer.height() as f32 &&
self.x >= 0. &&
self.y >= 0. &&
self.color.a >= 10
{
let index = (self.y as usize * buffer.width() as usize + self.x as usize) * 4;
buffer.raw_buffer()[index..index + 4].copy_from_slice(
&[self.color.r, self.color.g, self.color.b, self.color.a]
);
}
}
}
pub fn distance<T>(pos1: (T, T), pos2: (T, T)) -> f64
where
T: Sub<Output = T> + Into<f64>
{
let dx = (pos1.0.into() - pos2.0.into()) as f64;
let dy = (pos1.1.into() - pos2.1.into()) as f64;
((dx * dx + dy * dy) as f64).sqrt()
}
#[derive(Clone, Copy)]
pub struct Rect {
pub x: f32, pub y: f32,
pub width: f32, pub height: f32,
pub color: Color,
}
impl Rect {
pub fn new(x: f32, y: f32, width: f32, height: f32, color: Color) -> Self {
Self {
x, y,
width, height,
color,
}
}
pub fn new_square(x: f32, y: f32, size: f32, color: Color) -> Self {
Self {
x, y,
width: size, height: size,
color,
}
}
pub fn get_bounds(&self) -> (f32, f32, f32, f32) {
(self.x, self.y, self.x + self.width, self.y + self.height)
}
pub fn intersects(&self, other: &Self) -> bool {
self.x < other.x + other.width &&
self.x + self.width > other.x &&
self.y < other.y + other.height &&
self.y + self.height > other.y
}
pub fn contains(&self, x: f32, y: f32) -> bool {
x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
}
pub fn area(&self) -> f32 {
self.width * self.height
}
pub fn perimeter(&self) -> f32 {
2. * (self.width + self.height)
}
pub fn scale(&mut self) -> (f32, f32) {
(self.width, self.height)
}
}
impl Renderable for Rect {
fn render(&mut self, buffer: &mut Buffer) {
let rect_x = self.x as i32;
let rect_y = self.y as i32;
for x in rect_x..rect_x + self.width as i32 {
for y in rect_y..rect_y + self.height as i32 {
let (x, y) = (x as f32, y as f32);
let mut pixel = Pixel::new(x, y, self.color);
pixel.render(buffer);
}
}
}
}
#[derive(Clone, Copy)]
pub struct Line {
pub x1: f32, pub y1: f32,
pub x2: f32, pub y2: f32,
pub color: Color
}
impl Line {
pub fn new(start: (f32, f32), end: (f32, f32), color: Color) -> Self {
Self {
x1: start.0, y1: start.1,
x2: end.0, y2: end.1,
color,
}
}
}
impl Renderable for Line {
fn render(&mut self, buffer: &mut Buffer) {
let (x1, y1) = (self.x1 as i32, self.y1 as i32);
let (x2, y2) = (self.x2 as i32, self.y2 as i32);
let dx = if x1 > x2 { x1 - x2 } else { x2 - x1 };
let sx = if x1 < x2 { 1 } else { -1 };
let dy = if y1 > y2 { y1 - y2 } else { y2 - y1 };
let sy = if y1 < y2 { 1 } else { -1 };
let mut err = if dx > dy { dx } else { -dy } / 2;
let mut err2;
let (mut x, mut y) = (x1, y1);
loop {
let mut pixel = Pixel::new(x as f32, y as f32, self.color);
pixel.render(buffer);
if x == x2 && y == y2 { break; }
err2 = err;
if err2 > -dx { err -= dy; x += sx; }
if err2 < dy { err += dx; y += sy; }
}
}
}
#[derive(Clone, Copy)]
pub struct DottedLine {
pub x1: f32, pub y1: f32,
pub x2: f32, pub y2: f32,
pub color: Color,
}
impl DottedLine {
pub fn new(start: (f32, f32), end: (f32, f32), color: Color) -> Self {
Self {
x1: start.0, y1: start.1,
x2: end.0, y2: end.1,
color,
}
}
}
impl Renderable for DottedLine {
fn render(&mut self, buffer: &mut Buffer) {
let (x1, y1) = (self.x1 as i32, self.y1 as i32);
let (x2, y2) = (self.x2 as i32, self.y2 as i32);
let dx = if x1 > x2 { x1 - x2 } else { x2 - x1 };
let sx = if x1 < x2 { 1 } else { -1 };
let dy = if y1 > y2 { y1 - y2 } else { y2 - y1 };
let sy = if y1 < y2 { 1 } else { -1 };
let mut err = if dx > dy { dx } else { -dy } / 2;
let mut err2;
let (mut x, mut y) = (x1, y1);
let mut dashed = false;
loop {
if dashed {
let mut pixel = Pixel::new(x as f32, y as f32, self.color);
pixel.render(buffer); }
if x == x2 && y == y2 { break; }
dashed = !dashed; err2 = err;
if err2 > -dx {
err -= dy; x += sx;
}
if err2 < dy {
err += dx; y += sy;
}
}
}
}
#[derive(Clone)]
pub struct Texture {
width: u32,
height: u32,
pixels: Vec<Pixel>,
}
impl Texture {
pub fn new(path: &str) -> Self {
let image = image::open(path)
.expect(&format!("Unable to open image '{}'", path));
let (width, height) = image.dimensions();
let mut pixels = vec![];
for (x, y, pixel) in image.pixels() {
let color = Color::new_rgba(pixel[0], pixel[1], pixel[2], pixel[3]);
let img_pixel = Pixel::new(x as f32, y as f32, color);
pixels.push(img_pixel);
}
Self { width, height, pixels }
}
pub fn new_from_atlas(path: &str, atlas_sprite_pos: (u32, u32), atlas_sprite_size: (u32, u32)) -> Self {
let image = image::open(path)
.expect(format!("Unable to open image '{}'", path).as_str())
.crop(atlas_sprite_pos.0, atlas_sprite_pos.1, atlas_sprite_size.0, atlas_sprite_size.1);
let mut pixels = vec![];
for y in 0..atlas_sprite_size.1 {
for x in 0..atlas_sprite_size.0 {
let pixel = image.get_pixel(x, y);
let color = Color::new_rgba(pixel[0], pixel[1], pixel[2], pixel[3]);
let img_pixel = Pixel::new(x as f32, y as f32, color);
pixels.push(img_pixel);
}
}
Self {
width: atlas_sprite_size.0,
height: atlas_sprite_size.1,
pixels,
}
}
pub fn width(&self) -> u32 { self.width }
pub fn height(&self) -> u32 { self.height }
pub fn pixels(&self) -> &Vec<Pixel> { &self.pixels }
}
#[derive(Clone)]
pub struct Sprite {
pub x: f32, pub y: f32,
pub flip_x: bool, pub flip_y: bool,
texture_width: u32,
texture_height: u32,
pixels: Vec<Pixel>,
}
impl Sprite {
pub fn new(texture: Texture, x: f32, y: f32) -> Self {
Self {
x, y,
flip_x: false, flip_y: false,
texture_width: texture.width(),
texture_height: texture.height(),
pixels: texture.pixels,
}
}
pub fn generate_rect(&self) -> Rect {
Rect::new(self.x, self.y, self.texture_width as f32, self.texture_height as f32, GREEN)
}
}
impl Renderable for Sprite {
fn render(&mut self, buffer: &mut Buffer) {
for pixel in &mut self.pixels {
let (image_width, image_height) = (self.texture_width as f32 / 2., self.texture_height as f32 / 2.);
let (offset_x, offset_y) = (self.x - image_width, self.y - image_height);
if self.flip_x { pixel.x = self.texture_width as f32 - 1. - pixel.x as f32; }
if self.flip_y { pixel.y = self.texture_height as f32 - 1. - pixel.y as f32; }
pixel.x = pixel.x as f32 + offset_x;
pixel.y = pixel.y as f32 + offset_y;
pixel.render(buffer);
}
}
}