1use core::{
2 cmp::{max, min},
3 convert::Infallible,
4};
5
6use embedded_graphics::{
7 pixelcolor::{BinaryColor, Gray2},
8 prelude::{Dimensions, DrawTarget, GrayColor, Point, Size},
9 primitives::Rectangle,
10 Pixel,
11};
12use heapless::Vec;
13
14pub trait BufferView<const BITS: usize, const FRAMES: usize> {
16 fn window(&self) -> Rectangle;
18
19 fn data(&self) -> [&[u8]; FRAMES];
21}
22
23#[derive(Clone)]
27pub struct BinaryBuffer<const L: usize> {
28 size: Size,
29 bytes_per_row: usize,
30 data: [u8; L],
32}
33
34pub const fn binary_buffer_length(size: Size) -> usize {
36 (size.width as usize / 8) * size.height as usize
37}
38
39impl<const L: usize> BinaryBuffer<L> {
40 pub fn new(dimensions: Size) -> Self {
52 debug_assert_eq!(
53 dimensions.width % 8,
54 0,
55 "Width must be a multiple of 8 for binary packing."
56 );
57 debug_assert_eq!(
58 binary_buffer_length(dimensions),
59 L,
60 "Size must match given dimensions"
61 );
62
63 Self {
64 bytes_per_row: dimensions.width as usize / 8,
65 size: dimensions,
66 data: [0; L],
67 }
68 }
69
70 pub fn data(&self) -> &[u8] {
72 &self.data
73 }
74}
75
76impl<const L: usize> BufferView<1, 1> for BinaryBuffer<L> {
77 fn window(&self) -> Rectangle {
78 Rectangle::new(Point::zero(), self.size)
79 }
80
81 fn data(&self) -> [&[u8]; 1] {
82 [self.data()]
83 }
84}
85
86impl<const L: usize> Dimensions for BinaryBuffer<L> {
87 fn bounding_box(&self) -> Rectangle {
88 Rectangle::new(Point::zero(), self.size)
89 }
90}
91
92impl<const L: usize> DrawTarget for BinaryBuffer<L> {
93 type Color = BinaryColor;
94
95 type Error = Infallible;
96
97 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
98 where
99 I: IntoIterator<Item = Pixel<Self::Color>>,
100 {
101 for Pixel(point, color) in pixels.into_iter() {
103 if point.x < 0
104 || point.x >= self.size.width as i32
105 || point.y < 0
106 || point.y >= self.size.height as i32
107 {
108 continue; }
110
111 let byte_index = (point.x as usize) / 8 + (point.y as usize * self.bytes_per_row);
112 let bit_index = (point.x as usize) % 8;
113
114 if color == BinaryColor::On {
115 self.data[byte_index] |= 0x80 >> bit_index;
116 } else {
117 self.data[byte_index] &= !(0x80 >> bit_index);
118 }
119 }
120 Ok(())
121 }
122
123 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
124 where
125 I: IntoIterator<Item = Self::Color>,
126 {
127 {
129 let drawable_area = self.bounding_box().intersection(area);
130 if drawable_area.size.width == 0 || drawable_area.size.height == 0 {
131 return Ok(()); }
133 }
134
135 let y_start = area.top_left.y;
136 let y_end = area.top_left.y + area.size.height as i32;
137 let x_start = area.top_left.x;
138 let x_end = area.top_left.x + area.size.width as i32;
139
140 let mut colors_iter = colors.into_iter();
141 let mut byte_index = max(y_start, 0) as usize * self.bytes_per_row;
142 let row_start_byte_offset = max(x_start, 0) as usize / 8;
143 let row_end_byte_offset =
144 self.bytes_per_row - (min(x_end, self.size.width as i32) as usize / 8);
145 for y in y_start..y_end {
146 if y < 0 || y >= self.size.height as i32 {
147 for _ in x_start..x_end {
149 colors_iter.next();
150 }
151 continue;
152 }
153
154 byte_index += row_start_byte_offset;
155 let mut bit_index = (max(x_start, 0) as usize) % 8;
156
157 for x in x_start..x_end {
159 if x < 0 || x >= self.size.width as i32 {
160 colors_iter.next();
162 continue;
163 }
164
165 let Some(color) = colors_iter.next() else {
167 return Ok(());
168 };
169
170 if color == BinaryColor::On {
171 self.data[byte_index] |= 0x80 >> bit_index;
172 } else {
173 self.data[byte_index] &= !(0x80 >> bit_index);
174 }
175
176 bit_index += 1;
177 if bit_index == 8 {
178 byte_index += 1;
180 bit_index = 0;
181 }
182 }
183
184 byte_index += row_end_byte_offset;
185 }
186
187 Ok(())
188 }
189
190 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
191 let drawable_area = self.bounding_box().intersection(area);
193 if drawable_area.size.width == 0 || drawable_area.size.height == 0 {
194 return Ok(()); }
196
197 let y_start = drawable_area.top_left.y;
198 let y_end = drawable_area.top_left.y + drawable_area.size.height as i32;
199 let x_start = drawable_area.top_left.x;
200 let x_end = drawable_area.top_left.x + drawable_area.size.width as i32;
201
202 let x_full_bytes_start = min(x_start + x_start % 8, x_end);
203 let x_full_bytes_end = max(x_end - (x_end % 8), x_start);
204 let num_full_bytes_per_row = (x_full_bytes_end - x_full_bytes_start) / 8;
205
206 let mut byte_index = y_start as usize * self.bytes_per_row;
207 let row_start_byte_offset = x_start as usize / 8;
208 let row_end_byte_offset = self.bytes_per_row - (x_end as usize / 8);
209 for _y in y_start..y_end {
210 byte_index += row_start_byte_offset;
211 let mut bit_index = (x_start as usize) % 8;
212
213 macro_rules! set_next_bit {
216 () => {
217 if color == BinaryColor::On {
218 self.data[byte_index] |= 0x80 >> bit_index;
219 } else {
220 self.data[byte_index] &= !(0x80 >> bit_index);
221 }
222 bit_index += 1;
223 if bit_index == 8 {
224 byte_index += 1;
226 bit_index = 0;
227 }
228 };
229 }
230
231 if num_full_bytes_per_row == 0 {
232 for _x in x_start..x_end {
234 set_next_bit!();
235 }
236 } else {
237 for _x in x_start..x_full_bytes_start {
239 set_next_bit!();
240 }
241
242 for _ in 0..num_full_bytes_per_row {
244 if color == BinaryColor::On {
245 self.data[byte_index] = 0xFF;
246 } else {
247 self.data[byte_index] = 0x00;
248 }
249 byte_index += 1;
250 }
251
252 bit_index = x_full_bytes_end as usize % 8;
254 for _x in x_full_bytes_end..x_end {
255 set_next_bit!();
256 }
257 }
258
259 byte_index += row_end_byte_offset;
260 }
261
262 Ok(())
263 }
264}
265
266#[derive(Clone)]
268pub struct Gray2SplitBuffer<const L: usize> {
269 pub low: BinaryBuffer<L>,
270 pub high: BinaryBuffer<L>,
271}
272
273pub const fn gray2_split_buffer_length(size: Size) -> usize {
275 binary_buffer_length(size)
276}
277
278impl<const L: usize> Gray2SplitBuffer<L> {
279 pub fn new(dimensions: Size) -> Self {
291 Self {
292 low: BinaryBuffer::new(dimensions),
293 high: BinaryBuffer::new(dimensions),
294 }
295 }
296}
297
298impl<const L: usize> BufferView<1, 2> for Gray2SplitBuffer<L> {
299 fn window(&self) -> Rectangle {
300 Rectangle::new(Point::zero(), self.low.size)
301 }
302
303 fn data(&self) -> [&[u8]; 2] {
304 [self.low.data(), self.high.data()]
305 }
306}
307
308impl<const L: usize> Dimensions for Gray2SplitBuffer<L> {
309 fn bounding_box(&self) -> Rectangle {
310 Rectangle::new(Point::zero(), self.low.size)
311 }
312}
313
314fn to_low_and_high_as_binary(g: Gray2) -> (BinaryColor, BinaryColor) {
315 let luma = g.luma();
316 let low = if (luma & 1) == 0 {
317 BinaryColor::Off
318 } else {
319 BinaryColor::On
320 };
321 let high = if (luma & 0b10) == 0 {
322 BinaryColor::Off
323 } else {
324 BinaryColor::On
325 };
326 (low, high)
327}
328
329const GRAY_ITER_CHUNK_SIZE: usize = 128;
330
331impl<const L: usize> DrawTarget for Gray2SplitBuffer<L> {
332 type Color = Gray2;
333
334 type Error = Infallible;
335
336 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
337 where
338 I: IntoIterator<Item = Pixel<Self::Color>>,
339 {
340 let mut low_chunk: Vec<Pixel<BinaryColor>, GRAY_ITER_CHUNK_SIZE> = Vec::new();
344 let mut high_chunk: Vec<Pixel<BinaryColor>, GRAY_ITER_CHUNK_SIZE> = Vec::new();
345 for p in pixels.into_iter() {
346 let (low, high) = to_low_and_high_as_binary(p.1);
347 if low_chunk.is_full() {
348 self.low.draw_iter(low_chunk)?;
349 low_chunk = Vec::new();
350 self.high.draw_iter(high_chunk)?;
351 high_chunk = Vec::new();
352 }
353 unsafe {
354 low_chunk.push_unchecked(Pixel(p.0, low));
355 high_chunk.push_unchecked(Pixel(p.0, high));
356 }
357 }
358 if !low_chunk.is_empty() {
359 self.low.draw_iter(low_chunk)?;
360 self.high.draw_iter(high_chunk)?;
361 }
362 Ok(())
363 }
364
365 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
366 let (low, high) = to_low_and_high_as_binary(color);
367 self.low.fill_solid(area, low)?;
368 self.high.fill_solid(area, high)?;
369 Ok(())
370 }
371}
372
373pub trait Rotation {
374 fn inverse(&self) -> Self;
376
377 fn rotate_size(&self, size: Size) -> Size;
379
380 fn rotate_point(&self, point: Point, bounds: Size) -> Point;
393
394 fn rotate_rectangle(&self, rectangle: Rectangle, bounds: Size) -> Rectangle;
396}
397
398#[derive(Clone, Copy, Debug, PartialEq, Eq)]
400pub enum Rotate {
401 Degrees90,
402 Degrees180,
403 Degrees270,
404}
405
406impl Rotation for Rotate {
407 fn inverse(&self) -> Self {
408 match self {
409 Rotate::Degrees90 => Rotate::Degrees270,
410 Rotate::Degrees180 => Rotate::Degrees180,
411 Rotate::Degrees270 => Rotate::Degrees90,
412 }
413 }
414
415 fn rotate_size(&self, size: Size) -> Size {
416 match self {
417 Rotate::Degrees90 | Rotate::Degrees270 => Size::new(size.height, size.width),
418 Rotate::Degrees180 => size,
419 }
420 }
421
422 fn rotate_point(&self, point: Point, source_bounds: Size) -> Point {
423 match self {
424 Rotate::Degrees90 => Point::new(source_bounds.height as i32 - point.y - 1, point.x),
425 Rotate::Degrees180 => Point::new(
426 source_bounds.width as i32 - point.x - 1,
427 source_bounds.height as i32 - point.y - 1,
428 ),
429 Rotate::Degrees270 => Point::new(point.y, source_bounds.width as i32 - point.x - 1),
430 }
431 }
432
433 fn rotate_rectangle(&self, rectangle: Rectangle, source_bounds: Size) -> Rectangle {
434 match self {
435 Rotate::Degrees90 => {
436 let old_bottom_left =
437 rectangle.top_left + Point::new(0, rectangle.size.height as i32 - 1);
438 let new_top_left = self.rotate_point(old_bottom_left, source_bounds);
439 Rectangle::new(new_top_left, self.rotate_size(rectangle.size))
440 }
441 Rotate::Degrees180 => {
442 let old_bottom_right = rectangle.top_left + rectangle.size - Point::new(1, 1);
443 let new_top_left = self.rotate_point(old_bottom_right, source_bounds);
444 Rectangle::new(new_top_left, self.rotate_size(rectangle.size))
445 }
446 Rotate::Degrees270 => {
447 let old_top_right =
448 rectangle.top_left + Point::new(rectangle.size.width as i32 - 1, 0);
449 let new_top_left = self.rotate_point(old_top_right, source_bounds);
450 Rectangle::new(new_top_left, self.rotate_size(rectangle.size))
451 }
452 }
453 }
454}
455
456pub struct RotatedBuffer<B: DrawTarget, R: Rotation> {
469 bounds: Rectangle,
470 buffer: B,
471 rotation: R,
472}
473
474impl<B: DrawTarget, R: Rotation> RotatedBuffer<B, R> {
475 pub fn new(buffer: B, rotation: R) -> Self {
476 let inverse_rotation = rotation.inverse();
477 let inner_bounds = buffer.bounding_box();
478 let bounds = inverse_rotation.rotate_rectangle(inner_bounds, inner_bounds.size);
479 Self {
480 bounds,
481 buffer,
482 rotation,
483 }
484 }
485
486 pub fn inner(&mut self) -> &B {
488 &self.buffer
489 }
490
491 pub fn take_inner(self) -> B {
493 self.buffer
494 }
495}
496
497impl<B: DrawTarget, R: Rotation> Dimensions for RotatedBuffer<B, R> {
498 fn bounding_box(&self) -> Rectangle {
499 self.bounds
500 }
501}
502
503impl<B: DrawTarget, R: Rotation> DrawTarget for RotatedBuffer<B, R> {
504 type Color = B::Color;
505 type Error = B::Error;
506
507 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
508 where
509 I: IntoIterator<Item = Pixel<Self::Color>>,
510 {
511 let rotated_pixels = pixels.into_iter().map(|Pixel(point, color)| {
512 let rotated_point = self.rotation.rotate_point(point, self.bounds.size);
513 Pixel(rotated_point, color)
514 });
515 self.buffer.draw_iter(rotated_pixels)
516 }
517}
518
519#[inline(always)]
520pub(crate) fn split_low_and_high(value: u16) -> (u8, u8) {
522 let low = (value & 0xFF) as u8;
523 let high = ((value >> 8) & 0xFF) as u8;
524 (low, high)
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use embedded_graphics::pixelcolor::BinaryColor;
531
532 #[test]
533 fn test_binary_buffer_draw_iter_singles() {
534 const SIZE: Size = Size::new(16, 4);
535 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
536 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
537
538 buffer
540 .draw_iter([Pixel(Point::new(0, 0), BinaryColor::On)])
541 .unwrap();
542 assert_eq!(buffer.data[0], 0b10000000);
543
544 buffer
546 .draw_iter([Pixel(Point::new(10, 2), BinaryColor::On)])
547 .unwrap();
548 assert_eq!(buffer.data[5], 0b00100000);
549
550 buffer
552 .draw_iter([Pixel(Point::new(15, 3), BinaryColor::On)])
553 .unwrap();
554 assert_eq!(buffer.data[7], 0b1);
555 }
556
557 #[test]
558 fn test_binary_buffer_draw_iter_multiple() {
559 const SIZE: Size = Size::new(16, 4);
560 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
561 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
562
563 buffer
565 .draw_iter([
566 Pixel(Point::new(1, 0), BinaryColor::On),
567 Pixel(Point::new(2, 0), BinaryColor::On),
568 Pixel(Point::new(3, 0), BinaryColor::On),
569 Pixel(Point::new(2, 0), BinaryColor::Off),
570 Pixel(Point::new(1, 1), BinaryColor::On),
571 ])
572 .unwrap();
573
574 assert_eq!(buffer.data[0], 0b01010000);
575 assert_eq!(buffer.data[2], 0b01000000);
576 }
577
578 #[test]
579 fn test_binary_buffer_draw_iter_out_of_bounds() {
580 const SIZE: Size = Size::new(16, 4);
581 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
582 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
583 let previous_data = buffer.data;
584
585 buffer
587 .draw_iter([
588 Pixel(Point::new(-1, 0), BinaryColor::On),
589 Pixel(Point::new(0, -1), BinaryColor::On),
590 Pixel(Point::new(16, 0), BinaryColor::On),
591 Pixel(Point::new(0, 4), BinaryColor::On),
592 ])
593 .unwrap();
594
595 assert_eq!(
596 buffer.data, previous_data,
597 "Data should not change when drawing out-of-bounds pixels."
598 );
599 }
600
601 #[cfg(debug_assertions)]
602 #[test]
603 #[should_panic]
604 fn test_binary_buffer_must_have_aligned_width() {
605 let _ = BinaryBuffer::<16>::new(Size::new(10, 10));
606 }
607
608 #[cfg(debug_assertions)]
609 #[test]
610 #[should_panic]
611 fn test_binary_buffer_size_must_match_dimensions() {
612 let _ = BinaryBuffer::<16>::new(Size::new(16, 10));
613 }
614
615 #[test]
616 fn test_binary_buffer_fill_continguous() {
617 const SIZE: Size = Size::new(24, 8);
619 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
620 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
621
622 buffer
624 .fill_contiguous(
625 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
626 [BinaryColor::On; 8 * 8],
627 )
628 .unwrap();
629 buffer
630 .fill_contiguous(
631 &Rectangle::new(Point::new(6, 2), Size::new(12, 4)),
633 [BinaryColor::On; 12 * 4],
634 )
635 .unwrap();
636 buffer
637 .fill_contiguous(
638 &Rectangle::new(Point::new(20, 4), Size::new(8, 8)),
640 [BinaryColor::On; 8 * 8],
641 )
642 .unwrap();
643
644 #[rustfmt::skip]
645 let expected: [u8; 3 * 8] = [
646 0b11110000, 0b00000000, 0b00000000,
647 0b11110000, 0b00000000, 0b00000000,
648 0b11110011, 0b11111111, 0b11000000,
649 0b11110011, 0b11111111, 0b11000000,
650 0b00000011, 0b11111111, 0b11001111,
651 0b00000011, 0b11111111, 0b11001111,
652 0b00000000, 0b00000000, 0b00001111,
653 0b00000000, 0b00000000, 0b00001111,
654 ];
655 assert_eq!(buffer.data(), &expected);
656 }
657
658 #[test]
659 fn test_binary_buffer_fill_solid() {
660 const SIZE: Size = Size::new(24, 8);
662 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
663 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
664
665 buffer
667 .fill_solid(
668 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
669 BinaryColor::On,
670 )
671 .unwrap();
672 buffer
673 .fill_solid(
674 &Rectangle::new(Point::new(6, 2), Size::new(12, 4)),
676 BinaryColor::On,
677 )
678 .unwrap();
679 buffer
680 .fill_solid(
681 &Rectangle::new(Point::new(20, 4), Size::new(8, 8)),
683 BinaryColor::On,
684 )
685 .unwrap();
686
687 #[rustfmt::skip]
688 let expected: [u8; 3 * 8] = [
689 0b11110000, 0b00000000, 0b00000000,
690 0b11110000, 0b00000000, 0b00000000,
691 0b11110011, 0b11111111, 0b11000000,
692 0b11110011, 0b11111111, 0b11000000,
693 0b00000011, 0b11111111, 0b11001111,
694 0b00000011, 0b11111111, 0b11001111,
695 0b00000000, 0b00000000, 0b00001111,
696 0b00000000, 0b00000000, 0b00001111,
697 ];
698 assert_eq!(buffer.data(), &expected);
699 }
700
701 #[test]
702 fn test_gray2_split_buffer_draw_iter_singles() {
703 const SIZE: Size = Size::new(16, 4);
704 const BUFFER_LENGTH: usize = gray2_split_buffer_length(SIZE);
705 let mut buffer = Gray2SplitBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
706
707 buffer
709 .draw_iter([Pixel(Point::new(0, 0), Gray2::new(0b11))])
710 .unwrap();
711 assert_eq!(buffer.low.data[0], 0b10000000);
712 assert_eq!(buffer.high.data[0], 0b10000000);
713
714 buffer
716 .draw_iter([Pixel(Point::new(10, 2), Gray2::new(0b10))])
717 .unwrap();
718 assert_eq!(buffer.data()[0][5], 0b00000000);
719 assert_eq!(buffer.data()[1][5], 0b00100000);
720
721 buffer
723 .draw_iter([Pixel(Point::new(15, 3), Gray2::new(0b01))])
724 .unwrap();
725 assert_eq!(buffer.low.data[7], 0b1);
726 assert_eq!(buffer.high.data[7], 0b0);
727 }
728
729 #[test]
730 fn test_gray2_buffer_draw_iter_multiple() {
731 const SIZE: Size = Size::new(16, 4);
732 const BUFFER_LENGTH: usize = gray2_split_buffer_length(SIZE);
733 let mut buffer = Gray2SplitBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
734
735 buffer
737 .draw_iter([
738 Pixel(Point::new(1, 0), Gray2::new(0b11)),
739 Pixel(Point::new(2, 0), Gray2::new(0b11)),
740 Pixel(Point::new(3, 0), Gray2::new(0b01)),
741 Pixel(Point::new(2, 0), Gray2::new(0)),
742 Pixel(Point::new(1, 1), Gray2::new(0b10)),
743 ])
744 .unwrap();
745
746 assert_eq!(buffer.low.data[0], 0b01010000);
747 assert_eq!(buffer.high.data[0], 0b01000000);
748 assert_eq!(buffer.low.data[2], 0b00000000);
749 assert_eq!(buffer.high.data[2], 0b01000000);
750 }
751
752 #[test]
753 fn test_gray2_buffer_draw_iter_out_of_bounds() {
754 const SIZE: Size = Size::new(16, 4);
755 const BUFFER_LENGTH: usize = gray2_split_buffer_length(SIZE);
756 let mut buffer = Gray2SplitBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
757 let previous = buffer.clone();
758
759 buffer
761 .draw_iter([
762 Pixel(Point::new(-1, 0), Gray2::new(0b11)),
763 Pixel(Point::new(0, -1), Gray2::new(0b11)),
764 Pixel(Point::new(16, 0), Gray2::new(0b11)),
765 Pixel(Point::new(0, 4), Gray2::new(0b11)),
766 ])
767 .unwrap();
768
769 assert_eq!(
770 buffer.data(),
771 previous.data(),
772 "Data should not change when drawing out-of-bounds pixels."
773 );
774 }
775
776 #[cfg(debug_assertions)]
777 #[test]
778 #[should_panic]
779 fn test_gray2_buffer_must_have_aligned_width() {
780 let _ = Gray2SplitBuffer::<16>::new(Size::new(10, 10));
781 }
782
783 #[cfg(debug_assertions)]
784 #[test]
785 #[should_panic]
786 fn test_gray2_buffer_size_must_match_dimensions() {
787 let _ = Gray2SplitBuffer::<16>::new(Size::new(16, 10));
788 }
789
790 #[test]
791 fn test_gray2_buffer_fill_solid() {
792 const SIZE: Size = Size::new(24, 8);
794 const BUFFER_LENGTH: usize = gray2_split_buffer_length(SIZE);
795 let mut buffer = Gray2SplitBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
796
797 buffer
799 .fill_solid(
800 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
801 Gray2::new(0b11),
802 )
803 .unwrap();
804 buffer
805 .fill_solid(
806 &Rectangle::new(Point::new(6, 2), Size::new(12, 4)),
808 Gray2::new(0b10),
809 )
810 .unwrap();
811 buffer
812 .fill_solid(
813 &Rectangle::new(Point::new(20, 4), Size::new(8, 8)),
815 Gray2::new(0b01),
816 )
817 .unwrap();
818
819 #[rustfmt::skip]
820 let expected_low: [u8; 3 * 8] = [
821 0b11110000, 0b00000000, 0b00000000,
822 0b11110000, 0b00000000, 0b00000000,
823 0b11110000, 0b00000000, 0b00000000,
824 0b11110000, 0b00000000, 0b00000000,
825 0b00000000, 0b00000000, 0b00001111,
826 0b00000000, 0b00000000, 0b00001111,
827 0b00000000, 0b00000000, 0b00001111,
828 0b00000000, 0b00000000, 0b00001111,
829 ];
830 #[rustfmt::skip]
831 let expected_high: [u8; 3 * 8] = [
832 0b11110000, 0b00000000, 0b00000000,
833 0b11110000, 0b00000000, 0b00000000,
834 0b11110011, 0b11111111, 0b11000000,
835 0b11110011, 0b11111111, 0b11000000,
836 0b00000011, 0b11111111, 0b11000000,
837 0b00000011, 0b11111111, 0b11000000,
838 0b00000000, 0b00000000, 0b00000000,
839 0b00000000, 0b00000000, 0b00000000,
840 ];
841 assert_eq!(buffer.data()[0], &expected_low);
842 assert_eq!(buffer.data()[1], &expected_high);
843 }
844
845 #[test]
846 fn test_rotated_buffer_bounds() {
847 const SIZE: Size = Size::new(8, 24);
848 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
849
850 let mut rotated_buffer = RotatedBuffer::new(
851 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
852 Rotate::Degrees90,
853 );
854 assert_eq!(
855 rotated_buffer.bounding_box(),
856 Rectangle::new(Point::new(0, 0), Size::new(24, 8))
857 );
858
859 rotated_buffer = RotatedBuffer::new(
860 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
861 Rotate::Degrees180,
862 );
863 assert_eq!(
864 rotated_buffer.bounding_box(),
865 Rectangle::new(Point::new(0, 0), Size::new(8, 24))
866 );
867
868 rotated_buffer = RotatedBuffer::new(
869 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
870 Rotate::Degrees270,
871 );
872 assert_eq!(
873 rotated_buffer.bounding_box(),
874 Rectangle::new(Point::new(0, 0), Size::new(24, 8))
875 );
876 }
877
878 #[test]
879 fn test_rotated_buffer_draw_iter() {
880 const SIZE: Size = Size::new(8, 4);
881 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
882
883 let mut rotated_buffer = RotatedBuffer::new(
884 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
885 Rotate::Degrees90,
886 );
887 rotated_buffer
888 .draw_iter([
889 Pixel(Point::new(-1, -1), BinaryColor::On), Pixel(Point::new(0, 0), BinaryColor::On),
891 Pixel(Point::new(1, 1), BinaryColor::On),
892 Pixel(Point::new(2, 2), BinaryColor::On),
893 ])
894 .unwrap();
895 #[rustfmt::skip]
896 let expected: [u8; 4] = [
897 0b00000001,
898 0b00000010,
899 0b00000100,
900 0b00000000,
901 ];
902 assert_eq!(rotated_buffer.inner().data(), &expected);
903
904 rotated_buffer = RotatedBuffer::new(
905 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
906 Rotate::Degrees180,
907 );
908 rotated_buffer
909 .draw_iter([
910 Pixel(Point::new(-1, -1), BinaryColor::On), Pixel(Point::new(0, 0), BinaryColor::On),
912 Pixel(Point::new(1, 1), BinaryColor::On),
913 Pixel(Point::new(2, 2), BinaryColor::On),
914 ])
915 .unwrap();
916 #[rustfmt::skip]
917 let expected: [u8; 4] = [
918 0b00000000,
919 0b00000100,
920 0b00000010,
921 0b00000001,
922 ];
923 assert_eq!(rotated_buffer.inner().data(), &expected);
924
925 rotated_buffer = RotatedBuffer::new(
926 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
927 Rotate::Degrees270,
928 );
929 rotated_buffer
930 .draw_iter([
931 Pixel(Point::new(-1, -1), BinaryColor::On), Pixel(Point::new(0, 0), BinaryColor::On),
933 Pixel(Point::new(1, 1), BinaryColor::On),
934 Pixel(Point::new(2, 2), BinaryColor::On),
935 ])
936 .unwrap();
937 #[rustfmt::skip]
938 let expected: [u8; 4] = [
939 0b00000000,
940 0b00100000,
941 0b01000000,
942 0b10000000,
943 ];
944 assert_eq!(rotated_buffer.inner().data(), &expected);
945 }
946
947 #[test]
948 fn test_rotated_buffer_fill_contiguous() {
949 const SIZE: Size = Size::new(8, 6);
951 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
952 let buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
953
954 let mut rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees90);
955 rotated_buffer
956 .fill_contiguous(
957 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
958 [BinaryColor::On; 8 * 8],
959 )
960 .unwrap();
961
962 #[rustfmt::skip]
963 let expected: [u8; 6] = [
964 0b00001111,
965 0b00001111,
966 0b00001111,
967 0b00001111,
968 0b00000000,
969 0b00000000,
970 ];
971 assert_eq!(rotated_buffer.inner().data(), &expected);
972
973 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees180);
974 rotated_buffer
975 .fill_contiguous(
976 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
977 [BinaryColor::On; 8 * 8],
978 )
979 .unwrap();
980
981 #[rustfmt::skip]
982 let expected: [u8; 6] = [
983 0b00000000,
984 0b00000000,
985 0b00001111,
986 0b00001111,
987 0b00001111,
988 0b00001111,
989 ];
990 assert_eq!(rotated_buffer.inner().data(), &expected);
991
992 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees270);
993 rotated_buffer
994 .fill_contiguous(
995 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
996 [BinaryColor::On; 8 * 8],
997 )
998 .unwrap();
999
1000 #[rustfmt::skip]
1001 let expected: [u8; 6] = [
1002 0b00000000,
1003 0b00000000,
1004 0b11110000,
1005 0b11110000,
1006 0b11110000,
1007 0b11110000,
1008 ];
1009 assert_eq!(rotated_buffer.inner().data(), &expected);
1010 }
1011
1012 #[test]
1013 fn test_rotated_buffer_fill_solid() {
1014 const SIZE: Size = Size::new(8, 6);
1016 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
1017 let buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
1018
1019 let mut rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees90);
1020 rotated_buffer
1021 .fill_solid(
1022 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
1023 BinaryColor::On,
1024 )
1025 .unwrap();
1026
1027 #[rustfmt::skip]
1028 let expected: [u8; 6] = [
1029 0b00001111,
1030 0b00001111,
1031 0b00001111,
1032 0b00001111,
1033 0b00000000,
1034 0b00000000,
1035 ];
1036 assert_eq!(rotated_buffer.inner().data(), &expected);
1037
1038 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees180);
1039 rotated_buffer
1040 .fill_solid(
1041 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
1042 BinaryColor::On,
1043 )
1044 .unwrap();
1045
1046 #[rustfmt::skip]
1047 let expected: [u8; 6] = [
1048 0b00000000,
1049 0b00000000,
1050 0b00001111,
1051 0b00001111,
1052 0b00001111,
1053 0b00001111,
1054 ];
1055 assert_eq!(rotated_buffer.inner().data(), &expected);
1056
1057 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees270);
1058 rotated_buffer
1059 .fill_solid(
1060 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
1061 BinaryColor::On,
1062 )
1063 .unwrap();
1064
1065 #[rustfmt::skip]
1066 let expected: [u8; 6] = [
1067 0b00000000,
1068 0b00000000,
1069 0b11110000,
1070 0b11110000,
1071 0b11110000,
1072 0b11110000,
1073 ];
1074 assert_eq!(rotated_buffer.inner().data(), &expected);
1075 }
1076
1077 #[test]
1078 fn test_rotate_near_corner() {
1079 let mut r = Rotate::Degrees90;
1080 assert_eq!(
1082 Point::new(18, 1),
1083 r.rotate_point(Point::new(1, 1), Size::new(10, 20))
1084 );
1085 r = Rotate::Degrees180;
1086 assert_eq!(
1088 Point::new(8, 18),
1089 r.rotate_point(Point::new(1, 1), Size::new(10, 20))
1090 );
1091 r = Rotate::Degrees270;
1092 assert_eq!(
1094 Point::new(1, 8),
1095 r.rotate_point(Point::new(1, 1), Size::new(10, 20))
1096 );
1097 }
1098
1099 #[test]
1100 fn test_rotate_centre() {
1101 let mut r = Rotate::Degrees90;
1102 assert_eq!(
1103 Point::new(2, 2),
1104 r.rotate_point(Point::new(2, 2), Size::new(5, 5))
1105 );
1106 r = Rotate::Degrees180;
1107 assert_eq!(
1108 Point::new(2, 2),
1109 r.rotate_point(Point::new(2, 2), Size::new(5, 5))
1110 );
1111 r = Rotate::Degrees270;
1112 assert_eq!(
1113 Point::new(2, 2),
1114 r.rotate_point(Point::new(2, 2), Size::new(5, 5))
1115 );
1116 }
1117
1118 #[test]
1119 fn test_rotate_size() {
1120 let mut r = Rotate::Degrees90;
1121 assert_eq!(Size::new(5, 10), r.rotate_size(Size::new(10, 5)));
1122 r = Rotate::Degrees180;
1123 assert_eq!(Size::new(10, 5), r.rotate_size(Size::new(10, 5)));
1124 r = Rotate::Degrees270;
1125 assert_eq!(Size::new(5, 10), r.rotate_size(Size::new(10, 5)));
1126 }
1127
1128 #[test]
1129 fn test_rotate_rectangle() {
1130 let mut r = Rotate::Degrees90;
1131 let rect = Rectangle::new(Point::new(1, 1), Size::new(3, 2));
1132 let _dest_bounds = Size::new(8, 4);
1134 let mut source_bounds = Size::new(4, 8);
1135 let rotated = r.rotate_rectangle(rect, source_bounds);
1136 assert_eq!(rotated.top_left, Point::new(5, 1));
1139 assert_eq!(rotated.size, Size::new(2, 3));
1140
1141 r = Rotate::Degrees180;
1142 source_bounds = Size::new(8, 4);
1143 let rotated = r.rotate_rectangle(rect, source_bounds);
1144 assert_eq!(rotated.top_left, Point::new(4, 1));
1147 assert_eq!(rotated.size, Size::new(3, 2));
1148
1149 r = Rotate::Degrees270;
1150 source_bounds = Size::new(4, 8);
1151 let rotated = r.rotate_rectangle(rect, source_bounds);
1152 assert_eq!(rotated.top_left, Point::new(1, 0));
1155 assert_eq!(rotated.size, Size::new(2, 3));
1156 }
1157}