use crate::Result;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Image {
pub width: u32,
pub height: u32,
pub rgb: Vec<u8>,
}
impl Image {
pub fn new(width: u32, height: u32, rgb: Vec<u8>) -> Result<Self> {
let expected = width as usize * height as usize * 3;
if rgb.len() != expected {
return Err(crate::Error::Backend(format!(
"buffer length {} does not match {width}x{height} RGB ({expected} bytes)",
rgb.len()
)));
}
Ok(Self { width, height, rgb })
}
#[cfg(any(feature = "clip", feature = "sd"))]
pub fn save_png(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
let buf = image::RgbImage::from_raw(self.width, self.height, self.rgb.clone())
.ok_or_else(|| crate::Error::Backend("image buffer size mismatch".into()))?;
buf.save_with_format(path.as_ref(), image::ImageFormat::Png)
.map_err(|e| crate::Error::Backend(format!("saving PNG: {e}")))
}
#[cfg(any(feature = "clip", feature = "sd"))]
pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self> {
let rgb = image::open(path.as_ref())
.map_err(|e| crate::Error::Backend(format!("opening image: {e}")))?
.to_rgb8();
let (w, h) = rgb.dimensions();
Self::new(w, h, rgb.into_raw())
}
}
pub trait Backend {
#[must_use = "the generated image should be consumed"]
fn generate(&self, prompt: &str, seed: u64) -> Result<Image>;
}