1 | use 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))] |
21 | pub struct SubImage<'a, T> { |
22 | parent: &'a T, |
23 | area: Rectangle, |
24 | } |
25 | |
26 | impl<'a, T> SubImage<'a, T> |
27 | where |
28 | T: ImageDrawable, |
29 | { |
30 | pub(super) fn new(parent: &'a T, area: &Rectangle) -> Self { |
31 | let area: Rectangle = 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 | |
41 | impl<T> OriginDimensions for SubImage<'_, T> { |
42 | fn size(&self) -> crate::prelude::Size { |
43 | self.area.size |
44 | } |
45 | } |
46 | |
47 | impl<'a, T> ImageDrawable for SubImage<'a, T> |
48 | where |
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: Rectangle = area.translate(self.area.top_left); |
65 | |
66 | self.parent.draw_sub_image(target, &area) |
67 | } |
68 | } |
69 | |
70 | #[cfg (test)] |
71 | mod 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 | } |
168 | |