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