use std::fs;
use camino::{Utf8Path, Utf8PathBuf};
use thiserror::Error;
use crate::error::{BuildError, HauchiwaError};
use crate::loader::{Assets, GlobAssetsTask, Input};
use crate::{Blueprint, Handle};
#[derive(Debug, Error)]
pub enum ImageError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Image processing error: {0}")]
Image(#[from] image::ImageError),
#[error("Build error: {0}")]
Build(#[from] BuildError),
}
#[derive(Clone)]
pub struct Image {
pub path: Utf8PathBuf,
}
impl<G> Blueprint<G>
where
G: Send + Sync + 'static,
{
pub fn load_images(
&mut self,
path_glob: &'static [&'static str],
) -> Result<Handle<Assets<Image>>, HauchiwaError> {
Ok(self.add_task_opaque(GlobAssetsTask::new(
path_glob.to_vec(),
path_glob.to_vec(),
move |_, _, input: Input| {
let path = build_image(&input)?;
Ok((input.path, Image { path }))
},
)?))
}
}
fn process_image(buffer: &[u8]) -> Result<Vec<u8>, ImageError> {
let img = image::load_from_memory(buffer)?;
let w = img.width();
let h = img.height();
let mut out = Vec::new();
let encoder = image::codecs::webp::WebPEncoder::new_lossless(&mut out);
encoder.encode(&img.to_rgba8(), w, h, image::ExtendedColorType::Rgba8)?;
Ok(out)
}
fn build_image(file: &Input) -> Result<Utf8PathBuf, ImageError> {
let hash = file.hash.to_hex();
let path_root = Utf8Path::new("/hash/img/")
.join(&hash)
.with_extension("webp");
let path_hash = Utf8Path::new(".cache/hash/img/")
.join(&hash)
.with_extension("webp");
let path_dist = Utf8Path::new("dist/hash/img/")
.join(&hash)
.with_extension("webp");
if !path_hash.exists() {
let buffer = file.read()?;
let buffer = process_image(&buffer)?;
fs::create_dir_all(".cache/hash/img/")?;
fs::write(&path_hash, buffer)?;
}
let dir = path_dist.parent().unwrap_or(&path_dist);
fs::create_dir_all(dir)?;
fs::copy(&path_hash, &path_dist)?;
Ok(path_root)
}