mod rgb;
pub use rgb::RGB;
pub use image;
use std::ops::{Index, IndexMut};
use std::path::Path;
pub type Result<T> = std::result::Result<T, image::ImageError>;
pub type Error = image::ImageError;
pub fn clamp_f64_to_u8(n: f64) -> u8 {
match n {
n if n > 255.0 => 255,
n if n < 0.0 => 0,
n => n.round() as u8,
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Img<P> {
buf: Vec<P>,
width: u32,
}
impl<P> Img<P> {
pub fn new(buf: impl IntoIterator<Item = P>, width: u32) -> Option<Self> {
let buf: Vec<P> = buf.into_iter().collect();
if width == 0 || buf.len() % width as usize != 0 {
None
} else {
Some(Img { buf, width })
}
}
pub const unsafe fn from_raw_buf(buf: Vec<P>, width: u32) -> Self {
Img { buf, width }
}
pub fn into_vec(self) -> Vec<P> {
self.buf
}
pub fn width(&self) -> u32 {
self.width
}
pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
self.into_iter()
}
pub fn iter_mut(&mut self) -> <&mut Self as IntoIterator>::IntoIter {
self.buf.iter_mut()
}
pub fn height(&self) -> u32 {
self.len() as u32 / self.width
}
pub fn convert_with<Q>(self, convert: impl Fn(P) -> Q) -> Img<Q> {
let Img { buf, width } = self;
Img {
buf: buf.into_iter().map(convert).collect(),
width,
}
}
#[inline]
fn idx(&self, (x, y): (u32, u32)) -> usize {
((y * self.width) + x) as usize
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, (x, y): (u32, u32)) -> Option<&P> {
self.buf.get(self.idx((x, y)))
}
pub fn size(&self) -> (u32, u32) {
(self.width, self.len() as u32 / self.width as u32)
}
}
impl<N: From<u8>> Img<RGB<N>> {
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
let img = image::open(&path)?.to_rgb();
Ok(Img {
buf: img.pixels().map(|p| RGB::from(p.data)).collect(),
width: img.width(),
})
}
}
impl Img<RGB<u8>> {
pub fn save(self, path: &Path) -> Result<()> {
let (width, height) = self.size();
let buf = image::RgbImage::from_raw(width, height, self.raw_buf()).unwrap();
Ok(buf.save(path)?)
}
pub fn raw_buf(self) -> Vec<u8> {
let mut raw_buf = Vec::with_capacity(self.len() * 3);
for RGB(r, g, b) in self.buf {
raw_buf.push(r);
raw_buf.push(g);
raw_buf.push(b);
}
raw_buf
}
}
impl<P> Index<(u32, u32)> for Img<P> {
type Output = P;
fn index(&self, (x, y): (u32, u32)) -> &P {
&self.buf[self.idx((x, y))]
}
}
impl<P> IndexMut<(u32, u32)> for Img<P> {
fn index_mut(&mut self, (x, y): (u32, u32)) -> &mut P {
let i = self.idx((x, y));
&mut self.buf[i]
}
}
impl<P> IntoIterator for Img<P> {
type Item = P;
type IntoIter = std::vec::IntoIter<P>;
fn into_iter(self) -> Self::IntoIter {
self.buf.into_iter()
}
}
impl<'a, P> IntoIterator for &'a Img<P> {
type Item = &'a P;
type IntoIter = std::slice::Iter<'a, P>;
fn into_iter(self) -> Self::IntoIter {
(&self.buf).iter()
}
}
impl<'a, P> IntoIterator for &'a mut Img<P> {
type Item = &'a mut P;
type IntoIter = std::slice::IterMut<'a, P>;
fn into_iter(self) -> Self::IntoIter {
(&mut self.buf).iter_mut()
}
}
#[test]
fn test_save_and_load() {
let img = load_test_image();
let mut output = std::env::current_dir().unwrap();
output.push("save_load_test.png");
img.clone().save(&output).unwrap();
assert_eq!(img, Img::load(&output).unwrap());
std::fs::remove_file(output).unwrap();
}
fn load_test_image() -> Img<RGB<u8>> {
let mut input = std::env::current_dir().unwrap();
input.push("bunny.png");
Img::load(&input).unwrap()
}