embedded_graphics/image/
sub_image.rs

1use crate::{
2    draw_target::DrawTarget,
3    geometry::{Dimensions, OriginDimensions},
4    image::ImageDrawable,
5    primitives::Rectangle,
6    transform::Transform,
7};
8
9/// Sub image.
10///
11/// A sub image is rectangular subsection of an [`ImageDrawable`]. It can, for example, be used to
12/// draw individual sprites from a larger sprite atlas.
13///
14/// To create a sub image call the [`sub_image`] method on the parent [`ImageDrawable`]. See the
15/// [module-level documentation] for an example.
16///
17/// [`sub_image`]: trait.ImageDrawableExt.html#tymethod.sub_image
18/// [module-level documentation]: super#sub-images
19#[derive(Debug)]
20#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
21pub struct SubImage<'a, T> {
22    parent: &'a T,
23    area: Rectangle,
24}
25
26impl<'a, T> SubImage<'a, T>
27where
28    T: ImageDrawable,
29{
30    pub(super) fn new(parent: &'a T, area: &Rectangle) -> Self {
31        let area = parent.bounding_box().intersection(area);
32
33        Self { parent, area }
34    }
35
36    pub(crate) const fn new_unchecked(parent: &'a T, area: Rectangle) -> Self {
37        Self { parent, area }
38    }
39}
40
41impl<T> OriginDimensions for SubImage<'_, T> {
42    fn size(&self) -> crate::prelude::Size {
43        self.area.size
44    }
45}
46
47impl<'a, T> ImageDrawable for SubImage<'a, T>
48where
49    T: ImageDrawable,
50{
51    type Color = T::Color;
52
53    fn draw<DT>(&self, target: &mut DT) -> Result<(), DT::Error>
54    where
55        DT: DrawTarget<Color = Self::Color>,
56    {
57        self.parent.draw_sub_image(target, &self.area)
58    }
59
60    fn draw_sub_image<DT>(&self, target: &mut DT, area: &Rectangle) -> Result<(), DT::Error>
61    where
62        DT: DrawTarget<Color = Self::Color>,
63    {
64        let area = area.translate(self.area.top_left);
65
66        self.parent.draw_sub_image(target, &area)
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::{
74        geometry::{Point, Size},
75        image::ImageDrawableExt,
76        mock_display::MockDisplay,
77        pixelcolor::BinaryColor,
78    };
79
80    struct MockImageDrawable {
81        expected_area: Rectangle,
82    }
83
84    impl ImageDrawable for MockImageDrawable {
85        type Color = BinaryColor;
86
87        fn draw<DT>(&self, _target: &mut DT) -> Result<(), DT::Error>
88        where
89            DT: DrawTarget<Color = BinaryColor>,
90        {
91            panic!("draw shouldn't have been called on MockImageDrawable")
92        }
93
94        fn draw_sub_image<DT>(&self, _target: &mut DT, area: &Rectangle) -> Result<(), DT::Error>
95        where
96            DT: DrawTarget<Color = BinaryColor>,
97        {
98            assert_eq!(area, &self.expected_area);
99
100            Ok(())
101        }
102    }
103
104    impl OriginDimensions for MockImageDrawable {
105        fn size(&self) -> Size {
106            Size::new(8, 10)
107        }
108    }
109
110    #[test]
111    fn sub_image() {
112        let area = Rectangle::new(Point::new(2, 3), Size::new(3, 4));
113
114        let image = MockImageDrawable {
115            expected_area: area,
116        };
117
118        let mut display = MockDisplay::new();
119        image.sub_image(&area).draw(&mut display).unwrap();
120    }
121
122    #[test]
123    fn area_larger_than_parent() {
124        let area = Rectangle::new(Point::new(-5, -5), Size::new(20, 20));
125
126        let image = MockImageDrawable {
127            expected_area: Rectangle::new(Point::zero(), Size::new(8, 10)),
128        };
129
130        let mut display = MockDisplay::new();
131        image.sub_image(&area).draw(&mut display).unwrap();
132    }
133
134    #[test]
135    fn sub_image_of_sub_image() {
136        let area1 = Rectangle::new(Point::new(2, 3), Size::new(3, 4));
137        let area2 = Rectangle::new(Point::new(1, 1), Size::new(2, 2));
138
139        let image = MockImageDrawable {
140            expected_area: Rectangle::new(area1.top_left + area2.top_left, area2.size),
141        };
142
143        let mut display = MockDisplay::new();
144        image
145            .sub_image(&area1)
146            .sub_image(&area2)
147            .draw(&mut display)
148            .unwrap();
149    }
150
151    #[test]
152    fn sub_image_of_sub_image_area_larger_than_parent() {
153        let area1 = Rectangle::new(Point::new(2, 3), Size::new(3, 4));
154        let area2 = Rectangle::new(Point::new(-10, -10), Size::new(20, 20));
155
156        let image = MockImageDrawable {
157            expected_area: area1,
158        };
159
160        let mut display = MockDisplay::new();
161        image
162            .sub_image(&area1)
163            .sub_image(&area2)
164            .draw(&mut display)
165            .unwrap();
166    }
167}