Skip to main content

eimg/
lib.rs

1mod rgb;
2pub use rgb::RGB;
3
4/// [https://crates.io/crates/image] This crate provides native rust implementations of image encoders and decoders and basic image manipulation functions.
5pub use image;
6use std::ops::{Index, IndexMut};
7use std::path::Path;
8
9/// Re-export of std::result::Result<T, image::ImageError>
10pub type Result<T> = std::result::Result<T, image::ImageError>;
11/// Re-export of image::ImageError;
12pub type Error = image::ImageError;
13
14
15/// clamp a f64 to the closest u8, rounding non-integers.
16/// ```
17/// # use eimg::clamp_f64_to_u8;
18/// assert_eq!(clamp_f64_to_u8(255.2), 255);
19/// assert_eq!(clamp_f64_to_u8(2.8), 3);
20/// ```
21pub fn clamp_f64_to_u8(n: f64) -> u8 {
22    match n {
23        n if n > 255.0 => 255,
24        n if n < 0.0 => 0,
25        n => n.round() as u8,
26    }
27}
28
29/// Image as a flat buffer of pixels; accessible by (x, y) [Index]
30#[derive(Clone, Debug, PartialEq)]
31pub struct Img<P> {
32    buf: Vec<P>,
33    width: u32,
34}
35
36impl<P> Img<P> {
37    /// create an Img<P> from a buf and width. fails if `buf.len() % buf.width() != 0`
38    pub fn new(buf: impl IntoIterator<Item = P>, width: u32) -> Option<Self> {
39        let buf: Vec<P> = buf.into_iter().collect();
40        if width == 0 || buf.len() % width as usize != 0 {
41            None
42        } else {
43            Some(Img { buf, width })
44        }
45    }
46    /// create an Img<P> from a buf and length directly, skipping the bounds check.
47    /// ```
48    /// # use eimg::*;
49    /// assert_eq!(
50    ///     unsafe{Img::from_raw_buf(vec![2, 4, 6, 8], 2)},
51    ///     Img::new(vec![2, 4, 6, 8], 2).unwrap()
52    /// );
53    /// ```
54    pub const unsafe fn from_raw_buf(buf: Vec<P>, width: u32) -> Self {
55        Img { buf, width }
56    }
57
58    /// pull the buffer out of the image as a vec.
59    /// ```
60    /// # use eimg::*;
61    /// assert_eq!(Img::new(1..=4, 2).unwrap().into_vec(), vec![1, 2, 3, 4]);
62    /// ```
63    pub fn into_vec(self) -> Vec<P> {
64        self.buf
65    }
66
67    /// get the width of the image.
68    /// ```
69    /// # use eimg::*;
70    /// assert_eq!(Img::new(0..12, 3).unwrap().width(), 3);
71    /// ```
72    pub fn width(&self) -> u32 {
73        self.width
74    }
75    /// returns an iterator over the pixels in the buffer
76    pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
77        self.into_iter()
78    }
79
80    /// returns an iterator that allows modifying each pixel
81    pub fn iter_mut(&mut self) -> <&mut Self as IntoIterator>::IntoIter {
82        self.buf.iter_mut()
83    }
84    /// the height of the image; i.e, `buf.len() / width`
85    /// ```
86    /// # use eimg::*;
87    /// assert_eq!(Img::new(0..12, 3).unwrap().height(), 4);
88    /// ```
89    pub fn height(&self) -> u32 {
90        self.len() as u32 / self.width
91    }
92    /// map a function on P across the image buffer, converting an `Img<P>` to an `Img<Q>`
93    ///
94    /// ```
95    /// # use eimg::*;
96    /// let img: Img<u8> = Img::new(1..=4, 2).unwrap();
97    /// let doubled: Img<u16> = Img::new(vec![2, 4, 6, 8], 2).unwrap();
98    /// assert_eq!(img.convert_with(|x| u16::from(x*2)), doubled);
99    /// ```
100    pub fn convert_with<Q>(self, convert: impl Fn(P) -> Q) -> Img<Q> {
101        let Img { buf, width } = self;
102        Img {
103            buf: buf.into_iter().map(convert).collect(),
104            width,
105        }
106    }
107    #[inline]
108    fn idx(&self, (x, y): (u32, u32)) -> usize {
109        ((y * self.width) + x) as usize
110    }
111    /// the length of the image, in _pixels_. equal to [Img::width()]*[Img::height()]
112    pub fn len(&self) -> usize {
113        self.buf.len()
114    }
115
116    pub fn is_empty(&self) -> bool {
117        self.len() == 0
118    }
119
120    /// Returns a reference to an element.
121    pub fn get(&self, (x, y): (u32, u32)) -> Option<&P> {
122        self.buf.get(self.idx((x, y)))
123    }
124    /// Returns a pair `(width, height)`.
125    pub fn size(&self) -> (u32, u32) {
126        (self.width, self.len() as u32 / self.width as u32)
127    }
128}
129
130impl<N: From<u8>> Img<RGB<N>> {
131    /// load an image as an RGB<N> after converting it. See [image::open] and [image::DynamicImage::to_rgb]
132    /// ```rust
133    /// use eimg::*;
134    /// let img: Img<RGB<u8>> = Img::load("bunny.png").unwrap();
135    /// assert_eq!(img.size(), (480, 320));
136    /// ```
137    pub fn load(path: impl AsRef<Path>) -> Result<Self> {
138        let img = image::open(&path)?.to_rgb();
139        Ok(Img {
140            buf: img.pixels().map(|p| RGB::from(p.data)).collect(),
141            width: img.width(),
142        })
143    }
144}
145
146impl Img<RGB<u8>> {
147    /// save an image as a `.png` or `.jpg` to the path. the path extension determines the image type.
148    /// See [image::ImageBuffer::save]
149    pub fn save(self, path: &Path) -> Result<()> {
150        let (width, height) = self.size();
151        let buf = image::RgbImage::from_raw(width, height, self.raw_buf()).unwrap();
152        Ok(buf.save(path)?)
153    }
154    /// the raw_buf flattens out each RGB triplet;
155    /// ```
156    /// use eimg::*;
157    /// let img: Img<RGB<u8>> = Img::new(vec![RGB(0, 1, 2), RGB(1, 1, 1)], 1).unwrap();
158    /// assert_eq!(img.raw_buf(), vec![0, 1, 2, 1, 1, 1]);
159    /// ```
160    pub fn raw_buf(self) -> Vec<u8> {
161        let mut raw_buf = Vec::with_capacity(self.len() * 3);
162        for RGB(r, g, b) in self.buf {
163            raw_buf.push(r);
164            raw_buf.push(g);
165            raw_buf.push(b);
166        }
167        raw_buf
168    }
169}
170
171impl<P> Index<(u32, u32)> for Img<P> {
172    type Output = P;
173    fn index(&self, (x, y): (u32, u32)) -> &P {
174        &self.buf[self.idx((x, y))]
175    }
176}
177
178impl<P> IndexMut<(u32, u32)> for Img<P> {
179    fn index_mut(&mut self, (x, y): (u32, u32)) -> &mut P {
180        let i = self.idx((x, y));
181        &mut self.buf[i]
182    }
183}
184
185impl<P> IntoIterator for Img<P> {
186    type Item = P;
187    type IntoIter = std::vec::IntoIter<P>;
188    fn into_iter(self) -> Self::IntoIter {
189        self.buf.into_iter()
190    }
191}
192
193impl<'a, P> IntoIterator for &'a Img<P> {
194    type Item = &'a P;
195    type IntoIter = std::slice::Iter<'a, P>;
196    fn into_iter(self) -> Self::IntoIter {
197        (&self.buf).iter()
198    }
199}
200
201impl<'a, P> IntoIterator for &'a mut Img<P> {
202    type Item = &'a mut P;
203    type IntoIter = std::slice::IterMut<'a, P>;
204    fn into_iter(self) -> Self::IntoIter {
205        (&mut self.buf).iter_mut()
206    }
207}
208
209#[test]
210fn test_save_and_load() {
211    let img = load_test_image();
212    let mut output = std::env::current_dir().unwrap();
213
214    output.push("save_load_test.png");
215    img.clone().save(&output).unwrap();
216
217    assert_eq!(img, Img::load(&output).unwrap());
218    std::fs::remove_file(output).unwrap();
219}
220
221fn load_test_image() -> Img<RGB<u8>> {
222    let mut input = std::env::current_dir().unwrap();
223    input.push("bunny.png");
224
225    Img::load(&input).unwrap()
226}