| 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 | |