use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use exun::{RawUnexpected, ResultErrorExt};
use image::RgbaImage;
use packer::{RectanglePacker, TextureAtlas};
type ExunResult<T> = Result<T, RawUnexpected>;
pub struct TextureManager {
textures: HashMap<Arc<str>, TextureMetadata>,
loaded_textures: HashMap<Arc<str>, Arc<RgbaImage>>,
packer: RectanglePacker,
target_size: usize,
current_size: usize,
}
struct TextureMetadata {
path: Arc<Path>,
size: usize,
}
impl TextureManager {
pub fn new(target_size: usize) -> Self {
Self {
textures: HashMap::new(),
loaded_textures: HashMap::new(),
packer: RectanglePacker::new(),
target_size,
current_size: 0,
}
}
pub fn add_texture(&mut self, name: Arc<str>, path: Arc<Path>, size: usize) {
self.textures.insert(name, TextureMetadata { path, size });
}
pub fn try_preload(&mut self, name: Arc<str>) -> ExunResult<Option<bool>> {
let Some(metadata) = self.textures.get(&name) else {
return Ok(None);
};
if self.current_size + metadata.size < self.target_size {
self.load(name)?;
Ok(Some(true))
} else {
Ok(Some(false))
}
}
pub fn load(&mut self, name: Arc<str>) -> ExunResult<Option<()>> {
let Some(metadata) = self.textures.get(&name) else {
return Ok(None);
};
self.current_size += metadata.size;
let texture = image::open(&metadata.path).unexpect()?.to_rgba8();
self.loaded_textures.insert(name, Arc::new(texture));
Ok(Some(()))
}
pub fn unload(&mut self, name: Arc<str>) -> Option<()> {
self.loaded_textures.remove(&name)?;
Some(())
}
pub fn pack_texture(&mut self, name: Arc<str>) -> ExunResult<()> {
if !self.loaded_textures.contains_key(&name) {
self.load(name.clone())?;
}
self.packer.add_texture(
name.clone(),
self.loaded_textures
.get(&name)
.expect("we checked to make sure it was loaded")
.clone(),
);
Ok(())
}
pub fn reset_packer(&mut self) {
self.packer = RectanglePacker::new();
}
pub fn reload_texture_atlas(
&mut self,
min_width: u32,
min_height: u32,
) -> ExunResult<TextureAtlas> {
self.packer.output(min_width, min_height)
}
}
|