2023-09-12 18:25:33 +00:00
|
|
|
use std::{fs::File, sync::Arc, path::Path, ffi::OsStr, io::Read};
|
|
|
|
|
|
|
|
use image::ImageError;
|
|
|
|
|
|
|
|
use crate::{resource_manager::ResourceStorage, texture::Texture, resource::Resource};
|
|
|
|
|
|
|
|
use super::{LoaderError, ResourceLoader};
|
|
|
|
|
|
|
|
impl From<ImageError> for LoaderError {
|
|
|
|
fn from(value: ImageError) -> Self {
|
|
|
|
LoaderError::DecodingError(anyhow::Error::from(value))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A struct that implements the `ResourceLoader` trait used for loading textures.
|
2023-09-12 23:07:03 +00:00
|
|
|
#[derive(Default)]
|
2023-09-12 18:25:33 +00:00
|
|
|
pub struct TextureLoader;
|
|
|
|
|
|
|
|
impl ResourceLoader for TextureLoader {
|
|
|
|
fn extensions(&self) -> &[&str] {
|
|
|
|
&[
|
|
|
|
// the extensions of these are the names of the formats
|
|
|
|
"bmp", "dds", "gif", "ico", "jpeg", "jpg", "png", "qoi", "tga", "tiff", "webp",
|
|
|
|
|
|
|
|
// farbfeld
|
|
|
|
"ff",
|
|
|
|
|
|
|
|
// pnm
|
|
|
|
"pnm", "pbm", "pgm", "ppm", "pam",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn does_support_file(&self, path: &str) -> bool {
|
|
|
|
match Path::new(path).extension().and_then(OsStr::to_str) {
|
|
|
|
Some(ext) => {
|
|
|
|
self.extensions().contains(&ext)
|
|
|
|
},
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load(&self, path: &str) -> Result<Arc<dyn ResourceStorage>, LoaderError> {
|
|
|
|
// check if the file is supported by this loader
|
|
|
|
if !self.does_support_file(path) {
|
|
|
|
return Err(LoaderError::UnsupportedExtension(path.to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// read file bytes
|
|
|
|
let mut file = File::open(path)?;
|
|
|
|
let mut buf = vec![];
|
|
|
|
file.read_to_end(&mut buf)?;
|
|
|
|
|
|
|
|
// load the image and construct Resource<Texture>
|
2023-09-21 13:36:44 +00:00
|
|
|
let image = image::load_from_memory(&buf)
|
|
|
|
.map_err(|e| match e {
|
|
|
|
ImageError::IoError(e) => LoaderError::IOError(e),
|
|
|
|
_ => LoaderError::DecodingError(e.into()),
|
|
|
|
})?;
|
2023-09-12 18:25:33 +00:00
|
|
|
let texture = Texture {
|
|
|
|
image,
|
|
|
|
};
|
|
|
|
let res = Resource::with_data(path, texture);
|
|
|
|
|
|
|
|
Ok(Arc::new(res))
|
|
|
|
}
|
2023-09-21 13:36:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
fn get_image(path: &str) -> String {
|
|
|
|
let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
|
|
|
|
|
|
format!("{manifest}/test_files/img/{path}")
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Ensure that `does_support_file` works
|
|
|
|
#[test]
|
|
|
|
fn check_support() {
|
|
|
|
let loader = TextureLoader::default();
|
|
|
|
let extensions = loader.extensions();
|
|
|
|
let fake_paths: Vec<String> = extensions.iter().map(|e| format!("a.{}", e)).collect();
|
|
|
|
for path in fake_paths.iter() {
|
|
|
|
assert!(loader.does_support_file(&path));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn check_unsupport() {
|
|
|
|
let loader = TextureLoader::default();
|
|
|
|
assert_eq!(loader.does_support_file("test.gltf"), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests loading an image
|
|
|
|
#[test]
|
|
|
|
fn image_load() {
|
|
|
|
let loader = TextureLoader::default();
|
|
|
|
loader.load(&get_image("squiggles.png")).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn image_load_unsupported() {
|
|
|
|
let loader = TextureLoader::default();
|
|
|
|
assert!(loader.load(&get_image("squiggles.gltf")).is_err());
|
|
|
|
}
|
2023-09-12 18:25:33 +00:00
|
|
|
}
|