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 = Result; pub struct TextureManager { textures: HashMap, TextureMetadata>, loaded_textures: HashMap, Arc>, packer: RectanglePacker, target_size: usize, current_size: usize, } struct TextureMetadata { path: Arc, 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, path: Arc, size: usize) { self.textures.insert(name, TextureMetadata { path, size }); } pub fn try_preload(&mut self, name: Arc) -> ExunResult> { 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) -> ExunResult> { 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) -> Option<()> { self.loaded_textures.remove(&name)?; Some(()) } pub fn pack_texture(&mut self, name: Arc) -> 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 { self.packer.output(min_width, min_height) } }