Skip to main content

ai_image/images/
generic_image.rs

1use crate::error::{ImageError, ImageResult};
2use crate::flat::ViewOfPixel;
3use crate::math::Rect;
4use crate::traits::Pixel;
5use crate::{ImageBuffer, SubImage};
6use alloc::vec::Vec;
7
8/// Trait to inspect an image.
9///
10/// ```
11/// use ai_image::{GenericImageView, Rgb, RgbImage};
12///
13/// let buffer = RgbImage::new(10, 10);
14/// let image: &dyn GenericImageView<Pixel = Rgb<u8>> = &buffer;
15/// ```
16pub trait GenericImageView {
17    /// The type of pixel.
18    type Pixel: Pixel;
19
20    /// The width and height of this image.
21    fn dimensions(&self) -> (u32, u32);
22
23    /// The width of this image.
24    fn width(&self) -> u32 {
25        let (w, _) = self.dimensions();
26        w
27    }
28
29    /// The height of this image.
30    fn height(&self) -> u32 {
31        let (_, h) = self.dimensions();
32        h
33    }
34
35    /// Returns true if this x, y coordinate is contained inside the image.
36    fn in_bounds(&self, x: u32, y: u32) -> bool {
37        let (width, height) = self.dimensions();
38        x < width && y < height
39    }
40
41    /// Returns the pixel located at (x, y). Indexed from top left.
42    ///
43    /// # Panics
44    ///
45    /// Panics if `(x, y)` is out of bounds.
46    fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;
47
48    /// Returns the pixel located at (x, y). Indexed from top left.
49    ///
50    /// This function can be implemented in a way that ignores bounds checking.
51    /// # Safety
52    ///
53    /// The coordinates must be [`in_bounds`] of the image.
54    ///
55    /// [`in_bounds`]: #method.in_bounds
56    unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
57        self.get_pixel(x, y)
58    }
59
60    /// Returns an Iterator over the pixels of this image.
61    /// The iterator yields the coordinates of each pixel
62    /// along with their value
63    fn pixels(&self) -> Pixels<'_, Self>
64    where
65        Self: Sized,
66    {
67        let (width, height) = self.dimensions();
68
69        Pixels {
70            image: self,
71            x: 0,
72            y: 0,
73            width,
74            height,
75        }
76    }
77
78    /// Returns a subimage that is an immutable view into this image.
79    /// You can use [`GenericImage::sub_image`] if you need a mutable view instead.
80    /// The coordinates set the position of the top left corner of the view.
81    ///
82    ///  # Panics
83    ///
84    /// Panics if the dimensions provided fall out of bounds.
85    fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self>
86    where
87        Self: Sized,
88    {
89        assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
90        assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
91        SubImage::new(self, x, y, width, height)
92    }
93
94    /// Returns a subimage that is an immutable view into this image so long as
95    /// the provided coordinates and dimensions are within the bounds of this Image.
96    fn try_view(
97        &self,
98        x: u32,
99        y: u32,
100        width: u32,
101        height: u32,
102    ) -> Result<SubImage<&Self>, ImageError>
103    where
104        Self: Sized,
105    {
106        let rect = Rect {
107            x,
108            y,
109            width,
110            height,
111        };
112
113        rect.test_in_bounds(self)?;
114        Ok(SubImage::new(self, x, y, width, height))
115    }
116
117    /// Create an empty [`ImageBuffer`] with the same pixel type as this image.
118    ///
119    /// This should ensure metadata such as the color space are transferred without copying any of
120    /// the pixel data. The idea is to prepare a buffer ready to be filled with a filtered or
121    /// portion of the channel data from the current image without performing the work of copying
122    /// the data into that buffer twice.
123    ///
124    /// The default implementation defers to [`GenericImageView::buffer_like`].
125    fn buffer_like(&self) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> {
126        let (w, h) = self.dimensions();
127        self.buffer_with_dimensions(w, h)
128    }
129
130    /// Create an empty [`ImageBuffer`] with different dimensions.
131    ///
132    /// See [`GenericImageView::buffer_like`].
133    ///
134    /// Uses for this are for instances preparing a buffer for only a portion of the image, or
135    /// extracting the metadata to prepare a buffer of a different pixel type.
136    fn buffer_with_dimensions(
137        &self,
138        width: u32,
139        height: u32,
140    ) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> {
141        ImageBuffer::new(width, height)
142    }
143
144    /// If the buffer has a fitting layout, return a canonical view of the samples.
145    ///
146    /// This is the basis of optimization and by default return `None`. It lets consumers of
147    /// generic images access the sample data through a canonical descriptor of its layout directly
148    /// instead of pixel-by-pixel. This provides more efficient forms of access that the
149    /// [`GenericImageView`] trait itself does not demand from all its implementations.
150    ///
151    /// Implementation of this method should be cheap to call.
152    ///
153    /// If implemented, a [`SubImage`] proxy of this image will provide a sample view as well.
154    fn to_pixel_view(&self) -> Option<ViewOfPixel<'_, Self::Pixel>> {
155        None
156    }
157}
158
159/// Immutable pixel iterator
160#[derive(Debug)]
161pub struct Pixels<'a, I: ?Sized + 'a> {
162    image: &'a I,
163    x: u32,
164    y: u32,
165    width: u32,
166    height: u32,
167}
168
169impl<I: GenericImageView> Iterator for Pixels<'_, I> {
170    type Item = (u32, u32, I::Pixel);
171
172    fn next(&mut self) -> Option<(u32, u32, I::Pixel)> {
173        if self.x >= self.width {
174            self.x = 0;
175            self.y += 1;
176        }
177
178        if self.y >= self.height {
179            None
180        } else {
181            let pixel = self.image.get_pixel(self.x, self.y);
182            let p = (self.x, self.y, pixel);
183
184            self.x += 1;
185
186            Some(p)
187        }
188    }
189}
190
191impl<I: ?Sized> Clone for Pixels<'_, I> {
192    fn clone(&self) -> Self {
193        Pixels { ..*self }
194    }
195}
196
197/// A trait for manipulating images.
198pub trait GenericImage: GenericImageView {
199    /// Gets a reference to the mutable pixel at location `(x, y)`. Indexed from top left.
200    ///
201    /// # Panics
202    ///
203    /// Panics if `(x, y)` is out of bounds.
204    ///
205    /// Panics for dynamic images (this method is deprecated and will be removed).
206    ///
207    /// ## Known issues
208    ///
209    /// This requires the buffer to contain a unique set of continuous channels in the exact order
210    /// and byte representation that the pixel type requires. This is somewhat restrictive.
211    ///
212    /// TODO: Maybe use some kind of entry API? this would allow pixel type conversion on the fly
213    /// while still doing only one array lookup:
214    ///
215    /// ```ignore
216    /// let px = image.pixel_entry_at(x,y);
217    /// px.set_from_rgba(rgba)
218    /// ```
219    #[deprecated(since = "0.24.0", note = "Use `get_pixel` and `put_pixel` instead.")]
220    fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel;
221
222    /// Put a pixel at location (x, y). Indexed from top left.
223    ///
224    /// # Panics
225    ///
226    /// Panics if `(x, y)` is out of bounds.
227    fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
228
229    /// Puts a pixel at location (x, y). Indexed from top left.
230    ///
231    /// This function can be implemented in a way that ignores bounds checking.
232    /// # Safety
233    ///
234    /// The coordinates must be [`in_bounds`] of the image.
235    ///
236    /// [`in_bounds`]: traits.GenericImageView.html#method.in_bounds
237    unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
238        self.put_pixel(x, y, pixel);
239    }
240
241    /// Put a pixel at location (x, y), taking into account alpha channels
242    #[deprecated(
243        since = "0.24.0",
244        note = "Use iterator `pixels_mut` to blend the pixels directly"
245    )]
246    fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
247
248    /// Copies all of the pixels from another image into this image.
249    ///
250    /// The other image is copied with the top-left corner of the
251    /// other image placed at (x, y).
252    ///
253    /// In order to copy only a piece of the other image, use [`GenericImageView::view`].
254    ///
255    /// You can use [`FlatSamples`] to source pixels from an arbitrary regular raster of channel
256    /// values, for example from a foreign interface or a fixed image.
257    ///
258    /// # Returns
259    /// Returns an error if the image is too large to be copied at the given position
260    ///
261    /// [`GenericImageView::view`]: trait.GenericImageView.html#method.view
262    /// [`FlatSamples`]: flat/struct.FlatSamples.html
263    fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()>
264    where
265        O: GenericImageView<Pixel = Self::Pixel>,
266    {
267        if let Some(flat) = other.to_pixel_view() {
268            return self.copy_from_samples(flat, x, y);
269        }
270
271        // Do bounds checking here so we can use the non-bounds-checking
272        // functions to copy pixels.
273        Rect::from_image_at(other, x, y).test_in_bounds(self)?;
274
275        for k in 0..other.height() {
276            for i in 0..other.width() {
277                let p = other.get_pixel(i, k);
278                self.put_pixel(i + x, k + y, p);
279            }
280        }
281
282        Ok(())
283    }
284
285    /// Copy pixels from a regular strided matrix of pixels.
286    fn copy_from_samples(
287        &mut self,
288        samples: ViewOfPixel<'_, Self::Pixel>,
289        x: u32,
290        y: u32,
291    ) -> ImageResult<()> {
292        // Even though the implementation is the same, do not just call `Self::copy_from` here to
293        // avoid circular dependencies in careless implementations.
294        Rect::from_image_at(&samples, x, y).test_in_bounds(self)?;
295
296        for k in 0..samples.height() {
297            for i in 0..samples.width() {
298                let p = samples.get_pixel(i, k);
299                self.put_pixel(i + x, k + y, p);
300            }
301        }
302
303        Ok(())
304    }
305
306    /// Copies all of the pixels from one part of this image to another part of this image.
307    ///
308    /// The destination rectangle of the copy is specified with the top-left corner placed at (x, y).
309    ///
310    /// # Returns
311    /// `true` if the copy was successful, `false` if the image could not
312    /// be copied due to size constraints.
313    fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
314        let Rect {
315            x: sx,
316            y: sy,
317            width,
318            height,
319        } = source;
320        let dx = x;
321        let dy = y;
322        assert!(sx < self.width() && dx < self.width());
323        assert!(sy < self.height() && dy < self.height());
324        if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
325            return false;
326        }
327        // since `.rev()` creates a new dype we would either have to go with dynamic dispatch for the ranges
328        // or have quite a lot of code bloat. A macro gives us static dispatch with less visible bloat.
329        macro_rules! copy_within_impl_ {
330            ($xiter:expr, $yiter:expr) => {
331                for y in $yiter {
332                    let sy = sy + y;
333                    let dy = dy + y;
334                    for x in $xiter {
335                        let sx = sx + x;
336                        let dx = dx + x;
337                        let pixel = self.get_pixel(sx, sy);
338                        self.put_pixel(dx, dy, pixel);
339                    }
340                }
341            };
342        }
343        // check how target and source rectangles relate to each other so we dont overwrite data before we copied it.
344        match (sx < dx, sy < dy) {
345            (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
346            (true, false) => copy_within_impl_!((0..width).rev(), 0..height),
347            (false, true) => copy_within_impl_!(0..width, (0..height).rev()),
348            (false, false) => copy_within_impl_!(0..width, 0..height),
349        }
350        true
351    }
352
353    /// Returns a mutable subimage that is a view into this image.
354    /// If you want an immutable subimage instead, use [`GenericImageView::view`]
355    /// The coordinates set the position of the top left corner of the `SubImage`.
356    fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self>
357    where
358        Self: Sized,
359    {
360        assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
361        assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
362        SubImage::new(self, x, y, width, height)
363    }
364}
365
366#[cfg(test)]
367mod tests {
368    use super::{GenericImage, GenericImageView};
369
370    use crate::color::Rgba;
371    use crate::math::Rect;
372    use crate::{GrayImage, ImageBuffer};
373
374    #[test]
375    #[allow(deprecated)]
376    /// Test that alpha blending works as expected
377    fn test_image_alpha_blending() {
378        let mut target = ImageBuffer::new(1, 1);
379        target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
380        assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255]));
381        target.blend_pixel(0, 0, Rgba([0, 255, 0, 255]));
382        assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255]));
383
384        // Blending an alpha channel onto a solid background
385        target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
386        assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255]));
387
388        // Blending two alpha channels
389        target.put_pixel(0, 0, Rgba([0, 255, 0, 127]));
390        target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
391        assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190]));
392    }
393
394    #[test]
395    fn test_in_bounds() {
396        let mut target = ImageBuffer::new(2, 2);
397        target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
398
399        assert!(target.in_bounds(0, 0));
400        assert!(target.in_bounds(1, 0));
401        assert!(target.in_bounds(0, 1));
402        assert!(target.in_bounds(1, 1));
403
404        assert!(!target.in_bounds(2, 0));
405        assert!(!target.in_bounds(0, 2));
406        assert!(!target.in_bounds(2, 2));
407    }
408
409    #[test]
410    fn test_can_subimage_clone_nonmut() {
411        let mut source = ImageBuffer::new(3, 3);
412        source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
413
414        // A non-mutable copy of the source image
415        let source = source.clone();
416
417        // Clone a view into non-mutable to a separate buffer
418        let cloned = source.view(1, 1, 1, 1).to_image();
419
420        assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
421    }
422
423    #[test]
424    fn test_can_nest_views() {
425        let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
426
427        {
428            let mut sub1 = source.sub_image(0, 0, 2, 2);
429            let mut sub2 = sub1.sub_image(1, 1, 1, 1);
430            sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
431        }
432
433        assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
434
435        let view1 = source.view(0, 0, 2, 2);
436        assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
437
438        let view2 = view1.view(1, 1, 1, 1);
439        assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
440    }
441
442    #[test]
443    #[should_panic]
444    fn test_view_out_of_bounds() {
445        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
446        source.view(1, 1, 3, 3);
447    }
448
449    #[test]
450    #[should_panic]
451    fn test_view_coordinates_out_of_bounds() {
452        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
453        source.view(3, 3, 3, 3);
454    }
455
456    #[test]
457    #[should_panic]
458    fn test_view_width_out_of_bounds() {
459        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
460        source.view(1, 1, 3, 2);
461    }
462
463    #[test]
464    #[should_panic]
465    fn test_view_height_out_of_bounds() {
466        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
467        source.view(1, 1, 2, 3);
468    }
469
470    #[test]
471    #[should_panic]
472    fn test_view_x_out_of_bounds() {
473        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
474        source.view(3, 1, 3, 3);
475    }
476
477    #[test]
478    #[should_panic]
479    fn test_view_y_out_of_bounds() {
480        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
481        source.view(1, 3, 3, 3);
482    }
483
484    #[test]
485    fn test_view_in_bounds() {
486        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
487        source.view(0, 0, 3, 3);
488        source.view(1, 1, 2, 2);
489        source.view(2, 2, 0, 0);
490    }
491
492    #[test]
493    fn test_copy_sub_image() {
494        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
495        let view = source.view(0, 0, 3, 3);
496        let _view2 = view;
497        view.to_image();
498    }
499
500    #[test]
501    fn test_generic_image_copy_within_oob() {
502        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
503        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
504            Rect {
505                x: 0,
506                y: 0,
507                width: 5,
508                height: 4
509            },
510            0,
511            0
512        ));
513        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
514            Rect {
515                x: 0,
516                y: 0,
517                width: 4,
518                height: 5
519            },
520            0,
521            0
522        ));
523        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
524            Rect {
525                x: 1,
526                y: 0,
527                width: 4,
528                height: 4
529            },
530            0,
531            0
532        ));
533        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
534            Rect {
535                x: 0,
536                y: 0,
537                width: 4,
538                height: 4
539            },
540            1,
541            0
542        ));
543        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
544            Rect {
545                x: 0,
546                y: 1,
547                width: 4,
548                height: 4
549            },
550            0,
551            0
552        ));
553        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
554            Rect {
555                x: 0,
556                y: 0,
557                width: 4,
558                height: 4
559            },
560            0,
561            1
562        ));
563        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
564            Rect {
565                x: 1,
566                y: 1,
567                width: 4,
568                height: 4
569            },
570            0,
571            0
572        ));
573    }
574
575    #[test]
576    fn test_generic_image_copy_within_tl() {
577        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
578        let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
579        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
580        assert!(image.sub_image(0, 0, 4, 4).copy_within(
581            Rect {
582                x: 0,
583                y: 0,
584                width: 3,
585                height: 3
586            },
587            1,
588            1
589        ));
590        assert_eq!(&image.into_raw(), &expected);
591    }
592
593    #[test]
594    fn test_generic_image_copy_within_tr() {
595        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
596        let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
597        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
598        assert!(image.sub_image(0, 0, 4, 4).copy_within(
599            Rect {
600                x: 1,
601                y: 0,
602                width: 3,
603                height: 3
604            },
605            0,
606            1
607        ));
608        assert_eq!(&image.into_raw(), &expected);
609    }
610
611    #[test]
612    fn test_generic_image_copy_within_bl() {
613        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
614        let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15];
615        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
616        assert!(image.sub_image(0, 0, 4, 4).copy_within(
617            Rect {
618                x: 0,
619                y: 1,
620                width: 3,
621                height: 3
622            },
623            1,
624            0
625        ));
626        assert_eq!(&image.into_raw(), &expected);
627    }
628
629    #[test]
630    fn test_generic_image_copy_within_br() {
631        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
632        let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15];
633        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
634        assert!(image.sub_image(0, 0, 4, 4).copy_within(
635            Rect {
636                x: 1,
637                y: 1,
638                width: 3,
639                height: 3
640            },
641            0,
642            0
643        ));
644        assert_eq!(&image.into_raw(), &expected);
645    }
646}