use std::collections::HashMap; use std::error::Error; use std::io::Cursor; use base64::Engine; use image::ImageOutputFormat; use log::debug; use serde::{Deserialize, Serialize}; use crate::textures::{TextureManager, TextureSize}; pub const SPRITESHEET_IMAGE_FILE_FULL: &[u8] = include_bytes!("../../../assets/dist/spritesheet-full.png"); pub const SPRITESHEET_DATA_FILE_FULL: &str = include_str!("../../../assets/dist/spritesheet-full.ron"); pub const SPRITESHEET_IMAGE_FILE_375: &[u8] = include_bytes!("../../../assets/dist/spritesheet-375.png"); pub const SPRITESHEET_DATA_FILE_375: &str = include_str!("../../../assets/dist/spritesheet-375.ron"); pub const SPRITESHEET_IMAGE_FILE_125: &[u8] = include_bytes!("../../../assets/dist/spritesheet-125.png"); pub const SPRITESHEET_DATA_FILE_125: &str = include_str!("../../../assets/dist/spritesheet-125.ron"); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SpritePosition { pub name: String, pub x: f32, pub y: f32, pub width: f32, pub height: f32, pub offsets: Option<[f32; 2]>, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SerializedSpriteSheet { pub texture_width: f32, pub texture_height: f32, pub sprites: Vec, } #[derive(Debug)] pub struct TextureLoader { pub sprites: HashMap } impl TextureManager for TextureLoader { fn load(size: TextureSize) -> Result> where Self: Sized { debug!("Loading textures - starting fast texture loader (size: {})", size.to_string()); let start = js_sys::Date::now() as u64; // load the generated spritesheet data let spritesheet_data: SerializedSpriteSheet = ron::from_str(pick_data_file(size))?; // load the generated spritesheet image let spritesheet_image = image::load_from_memory(pick_image_file(size))?; if spritesheet_image.width() as f32 != spritesheet_data.texture_width { return Err("Image width mismatch between spritesheet and data file".into()); } if spritesheet_image.height() as f32 != spritesheet_data.texture_height { return Err("Image height mismatch between spritesheet and data file".into()); } let mut sprites = HashMap::new(); for sprite in spritesheet_data.sprites { debug!("Loading texture {} ({}x{}, start at {}, {})", sprite.name, sprite.width, sprite.height, sprite.x, sprite.y); let sprite_img = spritesheet_image.crop_imm(sprite.x as u32, sprite.y as u32, sprite.width as u32, sprite.height as u32); let mut image_data: Vec = Vec::new(); sprite_img.write_to(&mut Cursor::new(&mut image_data), ImageOutputFormat::Png) .unwrap(); let res_base64 = base64::engine::general_purpose::STANDARD.encode(image_data); sprites.insert(sprite.name, format!("data:image/png;base64,{}", res_base64)); } let end = js_sys::Date::now() as u64; debug!("Loaded {} sprites from spritesheet in {} ms", sprites.len(), end - start); Ok(Self { sprites, }) } fn get_texture(&self, texture_id: &str) -> Option { self.sprites.get(texture_id).map(|u| u.clone()) } } fn pick_data_file(for_size: TextureSize) -> &'static str { match for_size { TextureSize::Full => SPRITESHEET_DATA_FILE_FULL, TextureSize::Scaled375 => SPRITESHEET_DATA_FILE_375, TextureSize::Scaled125 => SPRITESHEET_DATA_FILE_125 } } fn pick_image_file(for_size: TextureSize) -> &'static [u8] { match for_size { TextureSize::Full => SPRITESHEET_IMAGE_FILE_FULL, TextureSize::Scaled375 => SPRITESHEET_IMAGE_FILE_375, TextureSize::Scaled125 => SPRITESHEET_IMAGE_FILE_125 } }