Galactica/src/sprite.rs

142 lines
3.3 KiB
Rust

use image::io::Reader as ImageReader;
use sdl2::{
rect::{Point, Rect},
render::{Canvas, Texture, TextureCreator},
video::{Window, WindowContext},
};
use std::collections::HashMap;
use crate::physics::Cartesian;
/// A handle for a sprite inside a SpriteAtlas
pub struct Sprite<'a> {
texture: &'a Texture<'a>,
rect: Rect,
scale: f64,
}
impl<'a> Sprite<'a> {
/// Draw this sprite on the screen.
///
/// Position represents the center of the sprite
/// on-screen position, NOT in the world.
pub fn draw(
&self,
canvas: &mut Canvas<Window>,
position: Cartesian,
angle: f64,
scale: f64,
) -> Result<(), String> {
let win_size = Cartesian::from(canvas.window().size());
let scale = scale * self.scale;
// Post-scale dimensions on the screen
let width = self.rect.width() as f64 * scale;
let height = self.rect.height() as f64 * scale;
// Don't draw if we're not on the screen.
// An offset is included to ensure we're completely
// off the screen. We add the whole width intentionally.
if position.x < -1.0 * (width as f64)
|| position.x > win_size.x + width as f64
|| position.y < -1.0 * (height as f64)
|| position.y > win_size.y + height as f64
{
return Ok(());
}
let mut dest = Rect::new(0, 0, width as u32, height as u32);
dest.center_on(Point::new((position.x) as i32, (position.y) as i32));
// copy the frame to the canvas
canvas.copy_ex(
&self.texture,
Some(self.rect),
Some(dest),
angle, // angle
Point::new((width / 2.0) as i32, (height / 2.0) as i32), // Rotation center
false,
false,
)?;
/*
canvas.set_draw_color(Color::RGB(255, 0, 0));
canvas.aa_circle(
position.x as i16,
position.y as i16,
5,
Color::RGB(255, 0, 0),
)?;
canvas.set_draw_color(Color::RGB(0, 0, 0));
*/
return Ok(());
}
}
/// A cache of textures we use when drawing the screen.
///
/// This is implemented very carefully, since SDL2 textures have tricky lifetimes.
pub struct SpriteAtlas<'a> {
data: HashMap<String, (Texture<'a>, Rect, f64)>,
}
impl<'a> SpriteAtlas<'a> {
pub fn new(texture_creator: &'a TextureCreator<WindowContext>) -> Result<Self, String> {
let mut b = Self {
data: HashMap::new(),
};
b.load_one(texture_creator, "gypsum.png", 0.75)?;
b.load_one(texture_creator, "a0.png", 1.0)?;
b.load_one(texture_creator, "small.png", 1.0)?;
b.load_one(texture_creator, "earth.png", 1.0)?;
return Ok(b);
}
pub fn get(&'a self, name: &str) -> Sprite<'a> {
let (texture, rect, scale) = self.data.get(name).unwrap();
return Sprite {
texture,
scale: scale.clone(),
rect: rect.clone(),
};
}
fn load_one(
&mut self,
texture_creator: &'a TextureCreator<WindowContext>,
s: &str,
scale: f64,
) -> Result<(), String> {
let im = ImageReader::open(format!("assets/{s}"))
.unwrap()
.decode()
.unwrap();
let width = im.width();
let height = im.height();
let mut im = im.as_bytes().to_vec();
let surface = sdl2::surface::Surface::from_data(
&mut im,
width,
height,
width * 4,
sdl2::pixels::PixelFormatEnum::RGBA32,
)?;
let texture = texture_creator
.create_texture_from_surface(&surface)
.map_err(|e| e.to_string())?;
self.data.insert(
s.to_owned(),
(texture, Rect::new(0, 0, width, height), scale),
);
return Ok(());
}
}