#![allow(dead_code)]
use std::collections::HashMap;
use macroquad::prelude::{Color, FilterMode, Image, Rect, Texture2D};
#[derive(Debug, Clone, Copy)]
pub struct Sprite {
pub rect: Rect,
}
#[derive(Debug)]
pub struct Atlas {
pub sprites: HashMap<u64, Sprite>,
pub dirty: bool,
filter: FilterMode,
texture: Texture2D,
image: Image,
max_line_height: u16,
cursor_x: u16,
cursor_y: u16,
unique_id: u64,
}
impl Atlas {
const GAP: u16 = 2;
const UNIQUENESS_OFFSET: u64 = 100000;
pub fn new(filter: FilterMode) -> Atlas {
let image = Image::gen_image_color(8192, 8192, Color::new(0.0, 0.0, 0.0, 0.0));
let texture = Texture2D::from_rgba8(image.width, image.height, &image.bytes);
Atlas {
image,
texture,
filter,
cursor_x: 0,
cursor_y: 0,
dirty: false,
max_line_height: 0,
sprites: HashMap::new(),
unique_id: Self::UNIQUENESS_OFFSET,
}
}
pub fn new_unique_id(&mut self) -> u64 {
self.unique_id += 1;
self.unique_id
}
pub fn get(&self, key: u64) -> Option<Sprite> {
self.sprites.get(&key).cloned()
}
pub fn width(&self) -> u16 {
self.image.width
}
pub fn height(&self) -> u16 {
self.image.height
}
pub fn texture(&mut self) -> &Texture2D {
if self.dirty {
self.dirty = false;
if self.texture.width() != self.image.width as _
|| self.texture.height() != self.image.height as _
{
self.texture =
Texture2D::from_rgba8(self.image.width, self.image.height, &self.image.bytes[..]);
self.texture.set_filter(self.filter);
}
self.texture.update(&self.image);
}
&self.texture
}
pub fn get_uv_rect(&self, key: u64) -> Option<Rect> {
self.get(key).map(|sprite| {
let w = self.texture.width();
let h = self.texture.height();
Rect::new(
sprite.rect.x / w,
sprite.rect.y / h,
sprite.rect.w / w,
sprite.rect.h / h,
)
})
}
pub fn cache_sprite(&mut self, key: u64, sprite: Image) {
let (width, height) = (sprite.width as usize, sprite.height as usize);
let x = if self.cursor_x + (width as u16) < self.image.width {
if height as u16 > self.max_line_height {
self.max_line_height = height as u16;
}
let res = self.cursor_x + Self::GAP;
self.cursor_x += width as u16 + Self::GAP * 2;
res
} else {
self.cursor_y += self.max_line_height + Self::GAP * 2;
self.cursor_x = width as u16 + Self::GAP;
self.max_line_height = height as u16;
Self::GAP
};
let y = self.cursor_y;
if self.cursor_y + height as u16 > self.image.height {
let sprites = self.sprites.drain().collect::<Vec<_>>();
self.cursor_x = 0;
self.cursor_y = 0;
self.max_line_height = 0;
let old_image = self.image.clone();
self.image = Image::gen_image_color(
self.image.width * 2,
self.image.height * 2,
Color::new(0.0, 0.0, 0.0, 0.0),
);
for (key, sprite) in sprites {
let image = old_image.sub_image(sprite.rect);
self.cache_sprite(key, image);
}
self.cache_sprite(key, sprite);
} else {
self.dirty = true;
for j in 0..height {
for i in 0..width {
self.image.set_pixel(
x as u32 + i as u32,
y as u32 + j as u32,
sprite.get_pixel(i as u32, j as u32),
);
}
}
self.sprites.insert(
key,
Sprite {
rect: Rect::new(x as f32, y as f32, width as f32, height as f32),
},
);
}
}
}
impl Default for Atlas {
fn default() -> Self {
Atlas::new(FilterMode::Linear)
}
}