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<Mutex<HashMap<String, Promise<ImgData>>>>,
textures: Arc<Mutex<HashMap<String, ImgData>>>,
}
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<String>) -> Option<ImgData> {
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()
}
}
}