use std::{ collections::HashMap, fmt::Display, sync::{Arc, Mutex}, }; use crate::rendering::assets::{AssetLoader, ImgData}; use bevy_ecs::system::Resource; use image::EncodableLayout; use poll_promise::Promise; use resvg::{tiny_skia, usvg}; #[derive(Resource)] pub struct Assets { texture_promises: Arc>>>, textures: Arc>>, } impl AssetLoader for Assets { fn new() -> Self { Assets { textures: Arc::new(Mutex::new(HashMap::new())), texture_promises: Arc::new(Mutex::new(HashMap::new())), } } fn get(&self, local_path: impl Into) -> Option { let local_path = local_path.into(); let contains_texture = { self.textures.lock().unwrap().contains_key(&local_path) }; let contains_texture_promise = { self.texture_promises .lock() .unwrap() .contains_key(&local_path) }; if !contains_texture && !contains_texture_promise { let local_path_clone = local_path.clone(); let request_promise = poll_promise::Promise::spawn_local(async move { let window = web_sys::window().unwrap(); let request = ehttp::Request::get(format!( "{}/src/assets/textures/{}", window.location().origin().unwrap(), local_path_clone )); let response = match ehttp::fetch_async(request).await { Ok(resp) => resp, Err(e) => { panic!("{}", e); } }; if local_path_clone.ends_with(".svg") { let opt = usvg::Options { default_size: usvg::Size::from_wh(20.0, 20.0).unwrap(), ..Default::default() }; let tree = usvg::Tree::from_data(&response.bytes, &opt) .expect(&format!("Couldn't parse svg {}", local_path_clone)); let tree_size = tree.size().to_int_size(); let size = usvg::Size::from_wh(512.0, 512.0).unwrap().to_int_size(); assert!(size.width() > 0 && size.height() > 0); let mut pixmap = tiny_skia::Pixmap::new(size.width(), size.height()) .expect("Failed to construct pixmap"); resvg::render( &tree, tiny_skia::Transform::from_scale( (size.width() as f32) / (tree_size.height() as f32), (size.height() as f32) / (tree_size.height() as f32), ), &mut pixmap.as_mut(), ); let data = ImgData { bytes: pixmap.data().to_vec(), width: size.width(), height: size.height(), }; data } else if local_path_clone.ends_with(".png") { let img = image::load_from_memory(&response.bytes).unwrap(); let rgba = img.to_rgba8(); let data = ImgData { bytes: rgba.as_bytes().to_vec(), width: rgba.width(), height: rgba.height(), }; data } else { panic!("Unsupported sprite type"); } }); { self.texture_promises .lock() .unwrap() .insert(local_path.clone(), request_promise); } None } else if !contains_texture { let mut texture_promises = self.texture_promises.lock().unwrap(); let promise = texture_promises.get_mut(&local_path).unwrap(); let mut returned_value = None; if let Some(texture) = promise.ready() { self.textures .lock() .unwrap() .insert(local_path.clone(), texture.clone()); returned_value = Some(texture.clone()); texture_promises.remove(&local_path); } return returned_value; } else { self.textures.lock().unwrap().get(&local_path).cloned() } } }