epd_waveshare_async/
buffer.rs1use core::convert::Infallible;
2
3use embedded_graphics::{
4 pixelcolor::BinaryColor,
5 prelude::{Dimensions, DrawTarget, Point, Size},
6 primitives::Rectangle,
7 Pixel,
8};
9
10pub struct BinaryBuffer<const L: usize> {
14 size: Size,
15 bytes_per_row: usize,
16 data: [u8; L],
18}
19
20pub const fn binary_buffer_length(size: Size) -> usize {
22 (size.width as usize / 8) * size.height as usize
23}
24
25impl<const L: usize> BinaryBuffer<L> {
26 pub fn new(dimensions: Size) -> Self {
38 debug_assert_eq!(
39 dimensions.width % 8,
40 0,
41 "Width must be a multiple of 8 for binary packing."
42 );
43 debug_assert_eq!(
44 binary_buffer_length(dimensions),
45 L,
46 "Size must match given dimensions"
47 );
48 Self {
49 bytes_per_row: dimensions.width as usize / 8,
50 size: dimensions,
51 data: [0; L],
52 }
53 }
54
55 pub fn data(&self) -> &[u8] {
57 &self.data
58 }
59}
60
61impl<const L: usize> Dimensions for BinaryBuffer<L> {
62 fn bounding_box(&self) -> Rectangle {
63 Rectangle::new(Point::zero(), self.size)
64 }
65}
66
67impl<const L: usize> DrawTarget for BinaryBuffer<L> {
68 type Color = BinaryColor;
69
70 type Error = Infallible;
71
72 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
73 where
74 I: IntoIterator<Item = Pixel<Self::Color>>,
75 {
76 for Pixel(point, color) in pixels.into_iter() {
77 if point.x < 0
78 || point.x >= self.size.width as i32
79 || point.y < 0
80 || point.y >= self.size.height as i32
81 {
82 continue; }
84
85 let byte_index = (point.x as usize) / 8 + (point.y as usize * self.bytes_per_row);
86 let bit_index = (point.x as usize) % 8;
87
88 if color == BinaryColor::On {
89 self.data[byte_index] |= 1 << bit_index;
90 } else {
91 self.data[byte_index] &= !(1 << bit_index);
92 }
93 }
94 Ok(())
95 }
96
97 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
98 where
99 I: IntoIterator<Item = Self::Color>,
100 {
101 let drawable_area = self.bounding_box().intersection(area);
102 if drawable_area.size.width == 0 || drawable_area.size.height == 0 {
103 return Ok(()); }
105 let y_start = area.top_left.y;
106 let y_end = area.top_left.y + area.size.height as i32;
107 let x_start = area.top_left.x;
108 let x_end = area.top_left.x + area.size.width as i32;
109 let mut row_start_byte = if y_start <= 0 {
110 0
111 } else {
112 y_start as usize * self.bytes_per_row
113 };
114 let (x_start_byte, x_start_bit) = if x_start <= 0 {
115 (0, 0)
116 } else {
117 ((x_start / 8) as usize, (x_start % 8) as usize)
118 };
119 let mut colors_iterator = colors.into_iter();
120 for y in y_start..y_end {
121 let mut byte_index = x_start_byte + row_start_byte;
122 let mut x_bit = x_start_bit;
123 for x in x_start..x_end {
124 let Some(color) = colors_iterator.next() else {
125 return Ok(()); };
127 if y < 0 || y >= self.size.height as i32 || x < 0 || x >= self.size.width as i32 {
128 continue; }
130 let byte = &mut self.data[byte_index];
131 match color {
132 BinaryColor::On => {
133 *byte |= 1 << x_bit;
134 }
135 BinaryColor::Off => {
136 *byte &= !(1 << x_bit);
137 }
138 }
139 x_bit += 1;
140 if x_bit == 8 {
141 x_bit = 0;
143 byte_index += 1;
144 }
145 }
146 row_start_byte += self.size.width as usize;
147 }
148
149 Ok(())
150 }
151
152 }
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use embedded_graphics::pixelcolor::BinaryColor;
159
160 #[test]
161 fn test_binary_buffer_draw_iter_singles() {
162 const SIZE: Size = Size::new(16, 4);
163 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
164 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
165
166 buffer
168 .draw_iter([Pixel(Point::new(0, 0), BinaryColor::On)])
169 .unwrap();
170 assert_eq!(buffer.data[0], 0b1);
171
172 buffer
174 .draw_iter([Pixel(Point::new(10, 2), BinaryColor::On)])
175 .unwrap();
176 assert_eq!(buffer.data[5], 0b100);
177
178 buffer
180 .draw_iter([Pixel(Point::new(15, 3), BinaryColor::On)])
181 .unwrap();
182 assert_eq!(buffer.data[7], 0b10000000);
183 }
184
185 #[test]
186 fn test_binary_buffer_draw_iter_multiple() {
187 const SIZE: Size = Size::new(16, 4);
188 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
189 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
190
191 buffer
193 .draw_iter([
194 Pixel(Point::new(1, 0), BinaryColor::On),
195 Pixel(Point::new(2, 0), BinaryColor::On),
196 Pixel(Point::new(3, 0), BinaryColor::On),
197 Pixel(Point::new(2, 0), BinaryColor::Off),
198 Pixel(Point::new(1, 1), BinaryColor::On),
199 ])
200 .unwrap();
201
202 assert_eq!(buffer.data[0], 0b00001010);
203 assert_eq!(buffer.data[2], 0b00000010);
204 }
205
206 #[test]
207 fn test_binary_buffer_draw_iter_out_of_bounds() {
208 const SIZE: Size = Size::new(16, 4);
209 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
210 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
211 let previous_data = buffer.data;
212
213 buffer
215 .draw_iter([
216 Pixel(Point::new(-1, 0), BinaryColor::On),
217 Pixel(Point::new(0, -1), BinaryColor::On),
218 Pixel(Point::new(16, 0), BinaryColor::On),
219 Pixel(Point::new(0, 4), BinaryColor::On),
220 ])
221 .unwrap();
222
223 assert_eq!(
224 buffer.data, previous_data,
225 "Data should not change when drawing out-of-bounds pixels."
226 );
227 }
228
229 #[cfg(debug_assertions)]
230 #[test]
231 #[should_panic]
232 fn test_binary_buffer_must_have_aligned_width() {
233 let _ = BinaryBuffer::<16>::new(Size::new(10, 10));
234 }
235
236 #[cfg(debug_assertions)]
237 #[test]
238 #[should_panic]
239 fn test_binary_buffer_size_must_match_dimensions() {
240 let _ = BinaryBuffer::<16>::new(Size::new(16, 10));
241 }
242}