1use rusty_esp_core::error::{Error, Result};
9use rusty_esp_core::frame::{Frame, Geometry, PixelFormat};
10use rusty_esp_core::time::Micros;
11
12pub trait ImageSource {
14 fn geometry(&self) -> Geometry;
16
17 fn grab<'b>(&mut self, out: &'b mut [u8]) -> Result<Frame<'b>>;
21}
22
23#[derive(Debug, Clone)]
28pub struct TestPattern {
29 geometry: Geometry,
30 frame_micros: u64,
31 sequence: u32,
32 timestamp: Micros,
33}
34
35impl TestPattern {
36 pub fn new(geometry: Geometry, fps: u32) -> Result<Self> {
40 if fps == 0 {
41 return Err(Error::InvalidGeometry);
42 }
43 match geometry.format {
44 PixelFormat::Gray8 | PixelFormat::Rgb565 | PixelFormat::Rgb888 => Ok(TestPattern {
45 geometry,
46 frame_micros: 1_000_000 / u64::from(fps),
47 sequence: 0,
48 timestamp: Micros::ZERO,
49 }),
50 _ => Err(Error::Unsupported),
51 }
52 }
53
54 const BARS: [[u8; 3]; 7] = [
56 [0xC0, 0xC0, 0xC0], [0xC0, 0xC0, 0x00], [0x00, 0xC0, 0xC0], [0x00, 0xC0, 0x00], [0xC0, 0x00, 0xC0], [0xC0, 0x00, 0x00], [0x00, 0x00, 0xC0], ];
64
65 }
71
72fn encode_pixel(colour: [u8; 3], bpp: usize, dst: &mut [u8; 3]) {
76 let [r, g, b] = colour;
77 match bpp {
78 1 => dst[0] = ((77 * u32::from(r) + 150 * u32::from(g) + 29 * u32::from(b)) >> 8) as u8,
80 2 => dst[..2].copy_from_slice(&crate::ops::pack_rgb565(r, g, b).to_le_bytes()),
81 _ => *dst = colour,
82 }
83}
84
85impl ImageSource for TestPattern {
86 fn geometry(&self) -> Geometry {
87 self.geometry
88 }
89
90 fn grab<'b>(&mut self, out: &'b mut [u8]) -> Result<Frame<'b>> {
91 let needed = self.geometry.byte_len().ok_or(Error::Unsupported)?;
92 if out.len() < needed {
93 return Err(Error::BufferTooSmall { needed });
94 }
95 let (w, h) = (self.geometry.width, self.geometry.height);
96 let bpp = match self.geometry.format {
100 PixelFormat::Gray8 => 1usize,
101 PixelFormat::Rgb565 => 2,
102 PixelFormat::Rgb888 => 3,
103 _ => return Err(Error::Unsupported),
104 };
105 let row = w as usize * bpp;
106 if row != 0 && h != 0 {
107 let marker_y = (self.sequence % h) as usize;
114 let buf = &mut out[..row * h as usize];
115
116 let mut solid = [0u8; 3];
118 encode_pixel([0xFF, 0xFF, 0xFF], bpp, &mut solid);
119 for px in buf[marker_y * row..(marker_y + 1) * row].chunks_exact_mut(bpp) {
120 px.copy_from_slice(&solid[..bpp]);
121 }
122
123 let bars_at = if marker_y != 0 {
127 0
128 } else if h > 1 {
129 1
130 } else {
131 usize::MAX
132 };
133 if bars_at != usize::MAX {
134 let (mut bar, mut acc) = (0usize, 0u32);
135 for px in buf[bars_at * row..(bars_at + 1) * row].chunks_exact_mut(bpp) {
136 let mut p = [0u8; 3];
137 encode_pixel(Self::BARS[bar.min(6)], bpp, &mut p);
138 px.copy_from_slice(&p[..bpp]);
139 acc += 7;
140 while acc >= w {
141 acc -= w;
142 bar += 1;
143 }
144 }
145 for y in 0..h as usize {
146 if y != marker_y && y != bars_at {
147 buf.copy_within(bars_at * row..(bars_at + 1) * row, y * row);
148 }
149 }
150 }
151 }
152 let frame = Frame::packed(self.geometry, self.timestamp, self.sequence, &out[..needed])?;
153 self.sequence = self.sequence.wrapping_add(1);
154 self.timestamp = self.timestamp.add_micros(self.frame_micros);
155 Ok(frame)
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn pattern_is_deterministic_and_advances() {
165 let g = Geometry::new(14, 4, PixelFormat::Rgb888).unwrap();
166 let mut a = TestPattern::new(g, 10).unwrap();
167 let mut b = TestPattern::new(g, 10).unwrap();
168 let mut ba = [0u8; 14 * 4 * 3];
169 let mut bb = [0u8; 14 * 4 * 3];
170 let fa = a.grab(&mut ba).unwrap();
171 assert_eq!(fa.sequence, 0);
172 assert_eq!(fa.timestamp, Micros::ZERO);
173 let fb = b.grab(&mut bb).unwrap();
174 assert_eq!(fa.byte_len(), fb.byte_len());
175 assert_eq!(ba, bb);
176 let f2 = a.grab(&mut ba).unwrap();
178 assert_eq!(f2.sequence, 1);
179 assert_eq!(f2.timestamp.as_millis(), 100);
180 assert_ne!(ba, bb);
181 let row1 = 14 * 3;
183 assert_eq!(&bb[row1..row1 + 3], &[0xC0, 0xC0, 0xC0]);
184 assert_eq!(&bb[..3], &[0xFF, 0xFF, 0xFF], "marker row of frame 0");
185 assert_eq!(&ba[row1..row1 + 3], &[0xFF, 0xFF, 0xFF]);
187 }
188
189 #[test]
190 fn rejects_unsupported_and_small_buffers() {
191 let g = Geometry::new(8, 8, PixelFormat::Jpeg).unwrap();
192 assert_eq!(TestPattern::new(g, 10).err(), Some(Error::Unsupported));
193 let g = Geometry::new(8, 8, PixelFormat::Gray8).unwrap();
194 assert_eq!(TestPattern::new(g, 0).err(), Some(Error::InvalidGeometry));
195 let mut p = TestPattern::new(g, 30).unwrap();
196 let mut small = [0u8; 10];
197 assert_eq!(
198 p.grab(&mut small).err(),
199 Some(Error::BufferTooSmall { needed: 64 })
200 );
201 }
202}