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