use image::{imageops::FilterType, DynamicImage, GenericImageView, ImageFormat};
use std::io::Cursor;
#[derive(Debug)]
pub struct RustImageData {
width: u32,
height: u32,
data: Option<DynamicImage>,
}
pub struct RustImageBuffer(Vec<u8>);
pub type ImageResult<T> =
std::result::Result<T, Box<dyn std::error::Error + Send + Sync + 'static>>;
pub trait RustImage: Sized {
fn empty() -> Self;
fn is_empty(&self) -> bool;
fn from_path(path: &str) -> ImageResult<Self>;
fn from_bytes(bytes: &[u8]) -> ImageResult<Self>;
fn from_dynamic_image(image: DynamicImage) -> Self;
fn get_size(&self) -> (u32, u32);
fn thumbnail(&self, width: u32, height: u32) -> ImageResult<Self>;
fn resize(&self, width: u32, height: u32, filter: FilterType) -> ImageResult<Self>;
fn to_jpeg(&self) -> ImageResult<RustImageBuffer>;
fn to_png(&self) -> ImageResult<RustImageBuffer>;
fn to_bitmap(&self) -> ImageResult<RustImageBuffer>;
fn save_to_path(&self, path: &str) -> ImageResult<()>;
}
macro_rules! image_to_format {
($name:ident, $format:expr) => {
fn $name(&self) -> ImageResult<RustImageBuffer> {
match &self.data {
Some(image) => {
let mut bytes: Vec<u8> = Vec::new();
image.write_to(&mut Cursor::new(&mut bytes), $format)?;
Ok(RustImageBuffer(bytes))
}
None => Err("image is empty".into()),
}
}
};
}
impl RustImage for RustImageData {
fn empty() -> Self {
RustImageData {
width: 0,
height: 0,
data: None,
}
}
fn is_empty(&self) -> bool {
self.data.is_none()
}
fn from_path(path: &str) -> ImageResult<Self> {
let image = image::open(path)?;
let (width, height) = image.dimensions();
Ok(RustImageData {
width,
height,
data: Some(image),
})
}
fn from_bytes(bytes: &[u8]) -> ImageResult<Self> {
let image = image::load_from_memory(bytes)?;
let (width, height) = image.dimensions();
Ok(RustImageData {
width,
height,
data: Some(image),
})
}
fn from_dynamic_image(image: DynamicImage) -> Self {
let (width, height) = image.dimensions();
RustImageData {
width,
height,
data: Some(image),
}
}
fn get_size(&self) -> (u32, u32) {
(self.width, self.height)
}
fn thumbnail(&self, width: u32, height: u32) -> ImageResult<Self> {
match &self.data {
Some(image) => {
let resized = image.thumbnail(width, height);
Ok(RustImageData {
width: resized.width(),
height: resized.height(),
data: Some(resized),
})
}
None => Err("image is empty".into()),
}
}
fn resize(&self, width: u32, height: u32, filter: FilterType) -> ImageResult<Self> {
match &self.data {
Some(image) => {
let resized = image.resize_exact(width, height, filter);
Ok(RustImageData {
width: resized.width(),
height: resized.height(),
data: Some(resized),
})
}
None => Err("image is empty".into()),
}
}
image_to_format!(to_jpeg, ImageFormat::Jpeg);
image_to_format!(to_png, ImageFormat::Png);
image_to_format!(to_bitmap, ImageFormat::Bmp);
fn save_to_path(&self, path: &str) -> ImageResult<()> {
match &self.data {
Some(image) => {
image.save(path)?;
Ok(())
}
None => Err("image is empty".into()),
}
}
}
impl RustImageBuffer {
pub fn get_bytes(&self) -> &[u8] {
&self.0
}
pub fn save_to_path(&self, path: &str) -> ImageResult<()> {
std::fs::write(path, &self.0)?;
Ok(())
}
}