embedded_graphics/iterator/
contiguous.rs

1//! Contiguous iterator.
2
3use crate::{
4    geometry::{Point, Size},
5    pixelcolor::PixelColor,
6    primitives::{rectangle, PointsIter, Rectangle},
7    Pixel,
8};
9use core::iter::Zip;
10
11/// Converts a contiguous iterator into a pixel iterator.
12#[derive(Debug)]
13#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
14pub struct IntoPixels<I>
15where
16    I: Iterator,
17    I::Item: PixelColor,
18{
19    iter: Zip<rectangle::Points, I>,
20}
21
22impl<I> IntoPixels<I>
23where
24    I: Iterator,
25    I::Item: PixelColor,
26{
27    pub(super) fn new(iter: I, bounding_box: Rectangle) -> Self {
28        Self {
29            iter: bounding_box.points().zip(iter),
30        }
31    }
32}
33
34impl<I> Iterator for IntoPixels<I>
35where
36    I: Iterator,
37    I::Item: PixelColor,
38{
39    type Item = Pixel<I::Item>;
40
41    #[inline]
42    fn next(&mut self) -> Option<Self::Item> {
43        self.iter.next().map(|(p, c)| Pixel(p, c))
44    }
45}
46
47/// Crops a part of the underlying iterator.
48#[derive(Debug)]
49#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
50pub(crate) struct Cropped<I>
51where
52    I: Iterator,
53    I::Item: PixelColor,
54{
55    iter: I,
56
57    x: u32,
58    y: u32,
59    size: Size,
60    row_skip: usize,
61}
62
63impl<I> Cropped<I>
64where
65    I: Iterator,
66    I::Item: PixelColor,
67{
68    pub(crate) fn new(mut iter: I, size: Size, crop_area: &Rectangle) -> Self {
69        let crop_area = Rectangle::new(Point::zero(), size).intersection(crop_area);
70
71        let initial_skip =
72            crop_area.top_left.y as usize * size.width as usize + crop_area.top_left.x as usize;
73
74        if initial_skip > 0 {
75            iter.nth(initial_skip - 1);
76        }
77
78        Self {
79            iter,
80            x: 0,
81            y: 0,
82            size: crop_area.size,
83            row_skip: (size.width - crop_area.size.width) as usize,
84        }
85    }
86}
87
88impl<I> Iterator for Cropped<I>
89where
90    I: Iterator,
91    I::Item: PixelColor,
92{
93    type Item = I::Item;
94
95    fn next(&mut self) -> Option<Self::Item> {
96        if self.y >= self.size.height || self.size.width == 0 {
97            return None;
98        }
99
100        if self.x < self.size.width {
101            self.x += 1;
102
103            self.iter.next()
104        } else {
105            self.x = 1;
106            self.y += 1;
107
108            if self.y < self.size.height {
109                self.iter.nth(self.row_skip)
110            } else {
111                None
112            }
113        }
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    use crate::pixelcolor::Gray8;
121
122    #[test]
123    fn cropped() {
124        let parent = (0..=255).map(Gray8::new);
125        let parent_size = Size::new(16, 16);
126
127        let crop_area = Rectangle::new(Point::new(2, 3), Size::new(4, 3));
128
129        let mut cropped_iter = Cropped::new(parent, parent_size, &crop_area);
130
131        let expected = &[
132            50, 51, 52, 53, //
133            66, 67, 68, 69, //
134            82, 83, 84, 85, //
135        ];
136
137        for value in expected {
138            assert_eq!(cropped_iter.next(), Some(Gray8::new(*value)));
139        }
140        assert_eq!(cropped_iter.next(), None);
141    }
142
143    #[test]
144    fn cropped_empty() {
145        let parent = (0..=255).map(Gray8::new);
146        let parent_size = Size::new(16, 16);
147
148        let crop_area = Rectangle::zero();
149
150        let mut cropped_iter = Cropped::new(parent, parent_size, &crop_area);
151
152        assert_eq!(cropped_iter.next(), None);
153    }
154
155    #[test]
156    fn cropped_overlapping() {
157        let parent = (0..=255).map(Gray8::new);
158        let parent_size = Size::new(16, 16);
159
160        let crop_area = Rectangle::new(Point::new(14, 10), Size::new(4, 4));
161
162        let mut cropped_iter = Cropped::new(parent, parent_size, &crop_area);
163
164        let expected = &[
165            174, 175, //
166            190, 191, //
167            206, 207, //
168            222, 223, //
169        ];
170
171        for value in expected {
172            assert_eq!(cropped_iter.next(), Some(Gray8::new(*value)));
173        }
174        assert_eq!(cropped_iter.next(), None);
175    }
176}