diff options
Diffstat (limited to 'games/rstnode/rst-client/src/assets.rs')
-rw-r--r-- | games/rstnode/rst-client/src/assets.rs | 138 |
1 files changed, 134 insertions, 4 deletions
diff --git a/games/rstnode/rst-client/src/assets.rs b/games/rstnode/rst-client/src/assets.rs index 7e368b8d4c16..d1ccdaf875ed 100644 --- a/games/rstnode/rst-client/src/assets.rs +++ b/games/rstnode/rst-client/src/assets.rs @@ -1,5 +1,19 @@ +use crate::{error::LoadError, GameSettings}; +use cairo::{Context, Format, ImageSurface, Rectangle}; use ggez::graphics::Image; -use std::{collections::BTreeMap, path::Path}; +use librsvg::{CairoRenderer, Loader}; +use std::{ + collections::BTreeMap, + error::Error, + ffi::OsStr, + fs::{read_dir, File}, + io::BufWriter, + io::Read, + path::{Path, PathBuf}, +}; +use tempfile::tempdir; + +pub type Result<T> = std::result::Result<T, LoadError>; /// Construct a `node` prefixed URI pub fn node(tt: &str) -> URI { @@ -24,21 +38,137 @@ impl From<String> for URI { } /// Asset loader +#[derive(Debug)] pub struct Assets { inner: BTreeMap<URI, Image>, } impl Assets { - pub fn load(p: &Path) -> Self { + fn new() -> Self { Self { inner: Default::default(), } } + + pub fn find<U: Into<URI>>(&self, u: U) -> Option<Image> { + self.inner.get(&u.into()).map(|i| i.clone()) + } + + /// Load an asset directory path + fn load_tree(&mut self, ctx: &mut ggez::Context, tmpdir: &Path, p: &Path) -> Result<()> { + let err: LoadError = p.to_str().unwrap().into(); + + read_dir(p) + .map_err(|_| err)? + .map(|e| { + let e = e.unwrap(); + let p = e.path(); + + let ext = OsStr::new("svg"); + + if p.is_dir() { + debug!( + "Entering directory {}", + p.file_name().unwrap().to_str().unwrap() + ); + self.load_tree(ctx, tmpdir, p.as_path())?; + } else if p.extension() == Some(ext) { + let png = load_svg(tmpdir, p.as_path())?; + + let basepath = p.with_extension(""); + + let uri_cat = p.parent().unwrap().file_name().unwrap().to_str().unwrap(); + let name = basepath.file_name().unwrap().to_str().unwrap(); + let uri = format!("{}/{}", uri_cat, name); + let path_str = png.as_path().to_str().unwrap(); + + let mut content = vec![]; + let mut f = File::open(png.as_path()).map_err(|_| { + LoadError::from(format!("No such file: {}", path_str).as_str()) + })?; + + f.read_to_end(&mut content).map_err(|e| { + LoadError::from( + format!("Read error for {}: {}", path_str, e.to_string()).as_str(), + ) + })?; + + self.inner.insert( + uri.into(), + Image::from_bytes(ctx, content.as_slice()).map_err(|e| { + LoadError::from( + format!("Read error for {}: {}", path_str, e.to_string()).as_str(), + ) + })?, + ); + } + + Ok(()) + }) + .fold(Ok(()), |acc, res| match (acc, res) { + (Ok(_), Ok(_)) => Ok(()), + (Ok(_), Err(e)) => Err(e), + (Err(e), _) => Err(e), + }) + } } +/// Load all game assets into the game +/// +/// This function performs three main steps. +/// +/// 1. Check that the provided path is a directory +/// 2. Recursively load Directories and files and call +/// [`load_svg`](self::load_svg) on each `.svg` file +/// 3. Re-load newly converted assets into [`Assets`](self::Assets) +pub fn load_tree(ctx: &mut ggez::Context, settings: &GameSettings) -> Result<Assets> { + let path = match settings.assets.clone() { + Some(s) => Ok(s), + None => Err(LoadError::from("No assets path set!")), + }?; + + debug!( + "Starting assets loading harness on {}", + path.to_str().unwrap() + ); + + let tmpdir = tempdir().unwrap(); + let mut assets = Assets::new(); + assets.load_tree(ctx, tmpdir.path(), path.as_path())?; + info!("Asset loading complete!"); + Ok(assets) +} /// A utility function to take an SVG and render it to a raster image /// according to a render spec -fn load_svg(p: &Path) -> () { - +pub fn load_svg(tmpdir: &Path, p: &Path) -> Result<PathBuf> { + let err: LoadError = p.to_str().unwrap().into(); + + let handle = Loader::new().read_path(p).map_err(|_| err.clone())?; + let renderer = CairoRenderer::new(&handle); + + let surf = ImageSurface::create(Format::ARgb32, 256, 256).map_err(|_| err.clone())?; + let cr = Context::new(&surf); + + renderer + .render_document( + &cr, + &Rectangle { + x: 0.0, + y: 0.0, + width: 256.0, + height: 256.0, + }, + ) + .map_err(|_| err.clone())?; + + let png = p.with_extension("png"); + let name = png + .file_name() + .map_or_else(|| Err(err.clone()), |name| Ok(name))?; + + let out = tmpdir.join(name.clone()); + let mut file = BufWriter::new(File::create(out.clone()).map_err(|_| err.clone())?); + surf.write_to_png(&mut file).map_err(|_| err.clone())?; + Ok(out.to_path_buf()) } |