hgame 0.26.4

CG production management structs, e.g. of assets, personnels, progress, etc.
Documentation
use super::*;
use mkutil::img_processing::pixels_from_bytes;

/// Relies on `image` crate to open image at given path.
/// SAFETY: `RetainedImage::debug_name()` must return exactly the file path,
/// in order for the `ImageSpec` to clone itself successfully afterwards.
pub fn retained_image_from_path<P: AsRef<Path>>(path: P) -> AnyResult<RetainedImage> {
    let debug_name = path.as_ref().display().to_string();
    let image = image::open(path)?;
    let img_buffer = image.to_rgba8();
    let size = img_buffer.dimensions();
    let color_img = egui::ColorImage::from_rgba_unmultiplied(
        [size.0 as usize, size.1 as usize],
        img_buffer.as_flat_samples().as_slice(),
    );
    Ok(RetainedImage::from_color_image(debug_name, color_img))
}

pub fn retained_image_from_bytes(bytes: &[u8]) -> AnyResult<RetainedImage> {
    let (size, pixels) = pixels_from_bytes(bytes).context("Failed to parse pixels from bytes")?;
    let color_img =
        egui::ColorImage::from_rgba_unmultiplied([size[0] as usize, size[1] as usize], &pixels);
    Ok(RetainedImage::from_color_image(
        "RetainedImage load from bytes",
        color_img,
    ))
}

// /// This results in LARGE size, much more than the actual file size, since
// /// it reads every pixel.
// pub fn pixels_from_file<P: AsRef<Path>>(path: P) -> AnyResult<Vec<u8>> {
//     Ok(image::open(path)?
//         .to_rgba8()
//         .as_flat_samples()
//         .as_slice()
//         .to_vec())
// }

// ----------------------------------------------------------------------------

/// `egui::TextureId` and the image's dimension.
pub struct ImageSpec(pub RetainedImage);

impl ImageSpec {
    pub fn inner(&self) -> &RetainedImage {
        &self.0
    }

    pub fn new(inner: RetainedImage) -> Self {
        ImageSpec(inner)
    }

    pub fn from_retained_image<P: AsRef<Path>>(path: P) -> Option<Self> {
        match retained_image_from_path(path) {
            Ok(img) => Some(Self::new(img)),
            Err(_) => None,
        }
    }
}

impl fmt::Debug for ImageSpec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("ImageSpec")
            .field(&self.0.debug_name())
            .finish()
    }
}

impl std::clone::Clone for ImageSpec {
    /// SAFETY: in order to have the original `RetainedImage` at the first place,
    /// the image must have been loaded successfully. We're assuming that reloading the
    /// same image will be successful again.
    fn clone(&self) -> Self {
        ImageSpec(retained_image_from_path(self.0.debug_name()).unwrap())
    }
}