embedded_graphics/draw_target/
cropped.rs

1use crate::{
2    draw_target::{DrawTarget, DrawTargetExt, Translated},
3    geometry::{OriginDimensions, Size},
4    primitives::Rectangle,
5    Pixel,
6};
7
8/// Cropped draw target.
9///
10/// Created by calling [`cropped`] on any [`DrawTarget`].
11/// See the [`cropped`] method documentation for more.
12///
13/// [`cropped`]: DrawTargetExt::cropped
14#[derive(Debug)]
15pub struct Cropped<'a, T>
16where
17    T: DrawTarget,
18{
19    parent: Translated<'a, T>,
20    size: Size,
21}
22
23impl<'a, T> Cropped<'a, T>
24where
25    T: DrawTarget,
26{
27    pub(super) fn new(parent: &'a mut T, area: &Rectangle) -> Self {
28        let area = area.intersection(&parent.bounding_box());
29
30        Self {
31            parent: parent.translated(area.top_left),
32            size: area.size,
33        }
34    }
35}
36
37impl<T> DrawTarget for Cropped<'_, T>
38where
39    T: DrawTarget,
40{
41    type Color = T::Color;
42    type Error = T::Error;
43
44    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
45    where
46        I: IntoIterator<Item = Pixel<Self::Color>>,
47    {
48        self.parent.draw_iter(pixels)
49    }
50
51    fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
52    where
53        I: IntoIterator<Item = Self::Color>,
54    {
55        self.parent.fill_contiguous(area, colors)
56    }
57
58    fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
59        self.parent.fill_solid(area, color)
60    }
61}
62
63impl<T> OriginDimensions for Cropped<'_, T>
64where
65    T: DrawTarget,
66{
67    fn size(&self) -> Size {
68        self.size
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::{
75        draw_target::{DrawTarget, DrawTargetExt},
76        geometry::Dimensions,
77        geometry::{Point, Size},
78        mock_display::MockDisplay,
79        pixelcolor::BinaryColor,
80        primitives::{Primitive, PrimitiveStyle, Rectangle},
81        Drawable, Pixel,
82    };
83
84    #[test]
85    fn draw_iter() {
86        let mut display = MockDisplay::new();
87
88        let area = Rectangle::new(Point::new(2, 3), Size::new(10, 10));
89        let mut cropped = display.cropped(&area);
90
91        let pixels = [
92            Pixel(Point::new(0, 0), BinaryColor::On),
93            Pixel(Point::new(1, 2), BinaryColor::Off),
94        ];
95        cropped.draw_iter(pixels.iter().copied()).unwrap();
96
97        display.assert_pattern(&[
98            "    ", //
99            "    ", //
100            "    ", //
101            "  # ", //
102            "    ", //
103            "   .", //
104        ]);
105    }
106
107    #[test]
108    fn fill_contiguous() {
109        let mut display = MockDisplay::new();
110
111        let area = Rectangle::new(Point::new(3, 2), Size::new(10, 10));
112        let mut cropped = display.cropped(&area);
113
114        let colors = [
115            1, 1, 1, 1, 1, //
116            0, 0, 0, 0, 1, //
117            0, 1, 0, 1, 1, //
118            1, 0, 1, 0, 1, //
119        ];
120        let area = Rectangle::new(Point::new(1, 2), Size::new(5, 4));
121        cropped
122            .fill_contiguous(&area, colors.iter().map(|c| BinaryColor::from(*c != 0)))
123            .unwrap();
124
125        display.assert_pattern(&[
126            "         ", //
127            "         ", //
128            "         ", //
129            "         ", //
130            "    #####", //
131            "    ....#", //
132            "    .#.##", //
133            "    #.#.#", //
134        ]);
135    }
136
137    #[test]
138    fn fill_solid() {
139        let mut display = MockDisplay::new();
140
141        let area = Rectangle::new(Point::new(1, 3), Size::new(10, 10));
142        let mut cropped = display.cropped(&area);
143
144        let area = Rectangle::new(Point::new(2, 1), Size::new(3, 4));
145        cropped.fill_solid(&area, BinaryColor::On).unwrap();
146
147        display.assert_pattern(&[
148            "      ", //
149            "      ", //
150            "      ", //
151            "      ", //
152            "   ###", //
153            "   ###", //
154            "   ###", //
155            "   ###", //
156        ]);
157    }
158
159    #[test]
160    fn clear() {
161        let mut display = MockDisplay::new();
162
163        let area = Rectangle::new(Point::new(1, 3), Size::new(3, 4));
164        let mut cropped = display.cropped(&area);
165        cropped.clear(BinaryColor::On).unwrap();
166
167        let mut expected = MockDisplay::new();
168        area.into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
169            .draw(&mut expected)
170            .unwrap();
171
172        display.assert_eq(&expected);
173    }
174
175    #[test]
176    fn bounding_box() {
177        let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
178
179        let size = Size::new(3, 4);
180        let area = Rectangle::new(Point::new(1, 3), size);
181        let cropped = display.cropped(&area);
182
183        assert_eq!(cropped.bounding_box(), Rectangle::new(Point::zero(), size));
184    }
185
186    #[test]
187    fn bounding_box_is_clipped() {
188        let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
189        let display_bb = display.bounding_box();
190
191        let top_left = Point::new(10, 20);
192        let size = Size::new(1000, 1000);
193        let area = Rectangle::new(top_left, size);
194        let cropped = display.cropped(&area);
195
196        let expected_size = display_bb.size - Size::new(top_left.x as u32, top_left.y as u32);
197
198        assert_eq!(
199            cropped.bounding_box(),
200            Rectangle::new(Point::zero(), expected_size),
201        );
202    }
203}