1use core::{
2 cmp::{max, min},
3 convert::Infallible,
4};
5
6use embedded_graphics::{
7 pixelcolor::BinaryColor,
8 prelude::{Dimensions, DrawTarget, Point, Size},
9 primitives::Rectangle,
10 Pixel,
11};
12
13#[derive(Clone)]
17pub struct BinaryBuffer<const L: usize> {
18 size: Size,
19 bytes_per_row: usize,
20 data: [u8; L],
22}
23
24pub const fn binary_buffer_length(size: Size) -> usize {
26 (size.width as usize / 8) * size.height as usize
27}
28
29impl<const L: usize> BinaryBuffer<L> {
30 pub fn new(dimensions: Size) -> Self {
42 #[cfg(feature = "defmt")]
43 defmt::debug_assert_eq!(
44 dimensions.width % 8,
45 0,
46 "Width must be a multiple of 8 for binary packing."
47 );
48 #[cfg(not(feature = "defmt"))]
49 debug_assert_eq!(
50 dimensions.width % 8,
51 0,
52 "Width must be a multiple of 8 for binary packing."
53 );
54 #[cfg(feature = "defmt")]
55 defmt::debug_assert_eq!(
56 binary_buffer_length(dimensions),
57 L,
58 "Size must match given dimensions"
59 );
60 #[cfg(not(feature = "defmt"))]
61 debug_assert_eq!(
62 binary_buffer_length(dimensions),
63 L,
64 "Size must match given dimensions"
65 );
66
67 Self {
68 bytes_per_row: dimensions.width as usize / 8,
69 size: dimensions,
70 data: [0; L],
71 }
72 }
73
74 pub fn data(&self) -> &[u8] {
76 &self.data
77 }
78}
79
80impl<const L: usize> Dimensions for BinaryBuffer<L> {
81 fn bounding_box(&self) -> Rectangle {
82 Rectangle::new(Point::zero(), self.size)
83 }
84}
85
86impl<const L: usize> DrawTarget for BinaryBuffer<L> {
87 type Color = BinaryColor;
88
89 type Error = Infallible;
90
91 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
92 where
93 I: IntoIterator<Item = Pixel<Self::Color>>,
94 {
95 for Pixel(point, color) in pixels.into_iter() {
97 if point.x < 0
98 || point.x >= self.size.width as i32
99 || point.y < 0
100 || point.y >= self.size.height as i32
101 {
102 continue; }
104
105 let byte_index = (point.x as usize) / 8 + (point.y as usize * self.bytes_per_row);
106 let bit_index = (point.x as usize) % 8;
107
108 if color == BinaryColor::On {
109 self.data[byte_index] |= 0x80 >> bit_index;
110 } else {
111 self.data[byte_index] &= !(0x80 >> bit_index);
112 }
113 }
114 Ok(())
115 }
116
117 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
118 where
119 I: IntoIterator<Item = Self::Color>,
120 {
121 {
123 let drawable_area = self.bounding_box().intersection(area);
124 if drawable_area.size.width == 0 || drawable_area.size.height == 0 {
125 return Ok(()); }
127 }
128
129 let y_start = area.top_left.y;
130 let y_end = area.top_left.y + area.size.height as i32;
131 let x_start = area.top_left.x;
132 let x_end = area.top_left.x + area.size.width as i32;
133
134 let mut colors_iter = colors.into_iter();
135 let mut byte_index = max(y_start, 0) as usize * self.bytes_per_row;
136 let row_start_byte_offset = max(x_start, 0) as usize / 8;
137 let row_end_byte_offset =
138 self.bytes_per_row - (min(x_end, self.size.width as i32) as usize / 8);
139 for y in y_start..y_end {
140 if y < 0 || y >= self.size.height as i32 {
141 for _ in x_start..x_end {
143 colors_iter.next();
144 }
145 continue;
146 }
147
148 byte_index += row_start_byte_offset;
149 let mut bit_index = (max(x_start, 0) as usize) % 8;
150
151 for x in x_start..x_end {
153 if x < 0 || x >= self.size.width as i32 {
154 colors_iter.next();
156 continue;
157 }
158
159 let Some(color) = colors_iter.next() else {
161 return Ok(());
162 };
163
164 if color == BinaryColor::On {
165 self.data[byte_index] |= 0x80 >> bit_index;
166 } else {
167 self.data[byte_index] &= !(0x80 >> bit_index);
168 }
169
170 bit_index += 1;
171 if bit_index == 8 {
172 byte_index += 1;
174 bit_index = 0;
175 }
176 }
177
178 byte_index += row_end_byte_offset;
179 }
180
181 Ok(())
182 }
183
184 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
185 let drawable_area = self.bounding_box().intersection(area);
187 if drawable_area.size.width == 0 || drawable_area.size.height == 0 {
188 return Ok(()); }
190
191 let y_start = drawable_area.top_left.y;
192 let y_end = drawable_area.top_left.y + drawable_area.size.height as i32;
193 let x_start = drawable_area.top_left.x;
194 let x_end = drawable_area.top_left.x + drawable_area.size.width as i32;
195
196 let x_full_bytes_start = min(x_start + x_start % 8, x_end);
197 let x_full_bytes_end = max(x_end - (x_end % 8), x_start);
198 let num_full_bytes_per_row = (x_full_bytes_end - x_full_bytes_start) / 8;
199
200 let mut byte_index = y_start as usize * self.bytes_per_row;
201 let row_start_byte_offset = x_start as usize / 8;
202 let row_end_byte_offset = self.bytes_per_row - (x_end as usize / 8);
203 for _y in y_start..y_end {
204 byte_index += row_start_byte_offset;
205 let mut bit_index = (x_start as usize) % 8;
206
207 macro_rules! set_next_bit {
210 () => {
211 if color == BinaryColor::On {
212 self.data[byte_index] |= 0x80 >> bit_index;
213 } else {
214 self.data[byte_index] &= !(0x80 >> bit_index);
215 }
216 bit_index += 1;
217 if bit_index == 8 {
218 byte_index += 1;
220 bit_index = 0;
221 }
222 };
223 }
224
225 if num_full_bytes_per_row == 0 {
226 for _x in x_start..x_end {
228 set_next_bit!();
229 }
230 } else {
231 for _x in x_start..x_full_bytes_start {
233 set_next_bit!();
234 }
235
236 for _ in 0..num_full_bytes_per_row {
238 if color == BinaryColor::On {
239 self.data[byte_index] = 0xFF;
240 } else {
241 self.data[byte_index] = 0x00;
242 }
243 byte_index += 1;
244 }
245
246 bit_index = x_full_bytes_end as usize % 8;
248 for _x in x_full_bytes_end..x_end {
249 set_next_bit!();
250 }
251 }
252
253 byte_index += row_end_byte_offset;
254 }
255
256 Ok(())
257 }
258}
259
260pub trait Rotation {
261 fn inverse(&self) -> Self;
263
264 fn rotate_size(&self, size: Size) -> Size;
266
267 fn rotate_point(&self, point: Point, bounds: Size) -> Point;
280
281 fn rotate_rectangle(&self, rectangle: Rectangle, bounds: Size) -> Rectangle;
283}
284
285#[derive(Clone, Copy, Debug, PartialEq, Eq)]
287pub enum Rotate {
288 Degrees90,
289 Degrees180,
290 Degrees270,
291}
292
293impl Rotation for Rotate {
294 fn inverse(&self) -> Self {
295 match self {
296 Rotate::Degrees90 => Rotate::Degrees270,
297 Rotate::Degrees180 => Rotate::Degrees180,
298 Rotate::Degrees270 => Rotate::Degrees90,
299 }
300 }
301
302 fn rotate_size(&self, size: Size) -> Size {
303 match self {
304 Rotate::Degrees90 | Rotate::Degrees270 => Size::new(size.height, size.width),
305 Rotate::Degrees180 => size,
306 }
307 }
308
309 fn rotate_point(&self, point: Point, source_bounds: Size) -> Point {
310 match self {
311 Rotate::Degrees90 => Point::new(source_bounds.height as i32 - point.y - 1, point.x),
312 Rotate::Degrees180 => Point::new(
313 source_bounds.width as i32 - point.x - 1,
314 source_bounds.height as i32 - point.y - 1,
315 ),
316 Rotate::Degrees270 => Point::new(point.y, source_bounds.width as i32 - point.x - 1),
317 }
318 }
319
320 fn rotate_rectangle(&self, rectangle: Rectangle, source_bounds: Size) -> Rectangle {
321 match self {
322 Rotate::Degrees90 => {
323 let old_bottom_left =
324 rectangle.top_left + Point::new(0, rectangle.size.height as i32 - 1);
325 let new_top_left = self.rotate_point(old_bottom_left, source_bounds);
326 Rectangle::new(new_top_left, self.rotate_size(rectangle.size))
327 }
328 Rotate::Degrees180 => {
329 let old_bottom_right = rectangle.top_left + rectangle.size - Point::new(1, 1);
330 let new_top_left = self.rotate_point(old_bottom_right, source_bounds);
331 Rectangle::new(new_top_left, self.rotate_size(rectangle.size))
332 }
333 Rotate::Degrees270 => {
334 let old_top_right =
335 rectangle.top_left + Point::new(rectangle.size.width as i32 - 1, 0);
336 let new_top_left = self.rotate_point(old_top_right, source_bounds);
337 Rectangle::new(new_top_left, self.rotate_size(rectangle.size))
338 }
339 }
340 }
341}
342
343pub struct RotatedBuffer<B: DrawTarget, R: Rotation> {
356 bounds: Rectangle,
357 buffer: B,
358 rotation: R,
359}
360
361impl<B: DrawTarget, R: Rotation> RotatedBuffer<B, R> {
362 pub fn new(buffer: B, rotation: R) -> Self {
363 let inverse_rotation = rotation.inverse();
364 let inner_bounds = buffer.bounding_box();
365 let bounds = inverse_rotation.rotate_rectangle(inner_bounds, inner_bounds.size);
366 Self {
367 bounds,
368 buffer,
369 rotation,
370 }
371 }
372
373 pub fn inner(&mut self) -> &B {
375 &self.buffer
376 }
377
378 pub fn take_inner(self) -> B {
380 self.buffer
381 }
382}
383
384impl<B: DrawTarget, R: Rotation> Dimensions for RotatedBuffer<B, R> {
385 fn bounding_box(&self) -> Rectangle {
386 self.bounds
387 }
388}
389
390impl<B: DrawTarget, R: Rotation> DrawTarget for RotatedBuffer<B, R> {
391 type Color = B::Color;
392 type Error = B::Error;
393
394 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
395 where
396 I: IntoIterator<Item = Pixel<Self::Color>>,
397 {
398 let rotated_pixels = pixels.into_iter().map(|Pixel(point, color)| {
399 let rotated_point = self.rotation.rotate_point(point, self.bounds.size);
400 Pixel(rotated_point, color)
401 });
402 self.buffer.draw_iter(rotated_pixels)
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409 use embedded_graphics::pixelcolor::BinaryColor;
410
411 #[test]
412 fn test_binary_buffer_draw_iter_singles() {
413 const SIZE: Size = Size::new(16, 4);
414 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
415 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
416
417 buffer
419 .draw_iter([Pixel(Point::new(0, 0), BinaryColor::On)])
420 .unwrap();
421 assert_eq!(buffer.data[0], 0b10000000);
422
423 buffer
425 .draw_iter([Pixel(Point::new(10, 2), BinaryColor::On)])
426 .unwrap();
427 assert_eq!(buffer.data[5], 0b00100000);
428
429 buffer
431 .draw_iter([Pixel(Point::new(15, 3), BinaryColor::On)])
432 .unwrap();
433 assert_eq!(buffer.data[7], 0b1);
434 }
435
436 #[test]
437 fn test_binary_buffer_draw_iter_multiple() {
438 const SIZE: Size = Size::new(16, 4);
439 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
440 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
441
442 buffer
444 .draw_iter([
445 Pixel(Point::new(1, 0), BinaryColor::On),
446 Pixel(Point::new(2, 0), BinaryColor::On),
447 Pixel(Point::new(3, 0), BinaryColor::On),
448 Pixel(Point::new(2, 0), BinaryColor::Off),
449 Pixel(Point::new(1, 1), BinaryColor::On),
450 ])
451 .unwrap();
452
453 assert_eq!(buffer.data[0], 0b01010000);
454 assert_eq!(buffer.data[2], 0b01000000);
455 }
456
457 #[test]
458 fn test_binary_buffer_draw_iter_out_of_bounds() {
459 const SIZE: Size = Size::new(16, 4);
460 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
461 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
462 let previous_data = buffer.data;
463
464 buffer
466 .draw_iter([
467 Pixel(Point::new(-1, 0), BinaryColor::On),
468 Pixel(Point::new(0, -1), BinaryColor::On),
469 Pixel(Point::new(16, 0), BinaryColor::On),
470 Pixel(Point::new(0, 4), BinaryColor::On),
471 ])
472 .unwrap();
473
474 assert_eq!(
475 buffer.data, previous_data,
476 "Data should not change when drawing out-of-bounds pixels."
477 );
478 }
479
480 #[cfg(debug_assertions)]
481 #[test]
482 #[should_panic]
483 fn test_binary_buffer_must_have_aligned_width() {
484 let _ = BinaryBuffer::<16>::new(Size::new(10, 10));
485 }
486
487 #[cfg(debug_assertions)]
488 #[test]
489 #[should_panic]
490 fn test_binary_buffer_size_must_match_dimensions() {
491 let _ = BinaryBuffer::<16>::new(Size::new(16, 10));
492 }
493
494 #[test]
495 fn test_binary_buffer_fill_continguous() {
496 const SIZE: Size = Size::new(24, 8);
498 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
499 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
500
501 buffer
503 .fill_contiguous(
504 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
505 [BinaryColor::On; 8 * 8],
506 )
507 .unwrap();
508 buffer
509 .fill_contiguous(
510 &Rectangle::new(Point::new(6, 2), Size::new(12, 4)),
512 [BinaryColor::On; 12 * 4],
513 )
514 .unwrap();
515 buffer
516 .fill_contiguous(
517 &Rectangle::new(Point::new(20, 4), Size::new(8, 8)),
519 [BinaryColor::On; 8 * 8],
520 )
521 .unwrap();
522
523 #[rustfmt::skip]
524 let expected: [u8; 3 * 8] = [
525 0b11110000, 0b00000000, 0b00000000,
526 0b11110000, 0b00000000, 0b00000000,
527 0b11110011, 0b11111111, 0b11000000,
528 0b11110011, 0b11111111, 0b11000000,
529 0b00000011, 0b11111111, 0b11001111,
530 0b00000011, 0b11111111, 0b11001111,
531 0b00000000, 0b00000000, 0b00001111,
532 0b00000000, 0b00000000, 0b00001111,
533 ];
534 assert_eq!(buffer.data(), &expected);
535 }
536
537 #[test]
538 fn test_binary_buffer_fill_solid() {
539 const SIZE: Size = Size::new(24, 8);
541 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
542 let mut buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
543
544 buffer
546 .fill_solid(
547 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
548 BinaryColor::On,
549 )
550 .unwrap();
551 buffer
552 .fill_solid(
553 &Rectangle::new(Point::new(6, 2), Size::new(12, 4)),
555 BinaryColor::On,
556 )
557 .unwrap();
558 buffer
559 .fill_solid(
560 &Rectangle::new(Point::new(20, 4), Size::new(8, 8)),
562 BinaryColor::On,
563 )
564 .unwrap();
565
566 #[rustfmt::skip]
567 let expected: [u8; 3 * 8] = [
568 0b11110000, 0b00000000, 0b00000000,
569 0b11110000, 0b00000000, 0b00000000,
570 0b11110011, 0b11111111, 0b11000000,
571 0b11110011, 0b11111111, 0b11000000,
572 0b00000011, 0b11111111, 0b11001111,
573 0b00000011, 0b11111111, 0b11001111,
574 0b00000000, 0b00000000, 0b00001111,
575 0b00000000, 0b00000000, 0b00001111,
576 ];
577 assert_eq!(buffer.data(), &expected);
578 }
579
580 #[test]
581 fn test_rotated_buffer_bounds() {
582 const SIZE: Size = Size::new(8, 24);
583 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
584
585 let mut rotated_buffer = RotatedBuffer::new(
586 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
587 Rotate::Degrees90,
588 );
589 assert_eq!(
590 rotated_buffer.bounding_box(),
591 Rectangle::new(Point::new(0, 0), Size::new(24, 8))
592 );
593
594 rotated_buffer = RotatedBuffer::new(
595 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
596 Rotate::Degrees180,
597 );
598 assert_eq!(
599 rotated_buffer.bounding_box(),
600 Rectangle::new(Point::new(0, 0), Size::new(8, 24))
601 );
602
603 rotated_buffer = RotatedBuffer::new(
604 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
605 Rotate::Degrees270,
606 );
607 assert_eq!(
608 rotated_buffer.bounding_box(),
609 Rectangle::new(Point::new(0, 0), Size::new(24, 8))
610 );
611 }
612
613 #[test]
614 fn test_rotated_buffer_draw_iter() {
615 const SIZE: Size = Size::new(8, 4);
616 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
617
618 let mut rotated_buffer = RotatedBuffer::new(
619 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
620 Rotate::Degrees90,
621 );
622 rotated_buffer
623 .draw_iter([
624 Pixel(Point::new(-1, -1), BinaryColor::On), Pixel(Point::new(0, 0), BinaryColor::On),
626 Pixel(Point::new(1, 1), BinaryColor::On),
627 Pixel(Point::new(2, 2), BinaryColor::On),
628 ])
629 .unwrap();
630 #[rustfmt::skip]
631 let expected: [u8; 4] = [
632 0b00000001,
633 0b00000010,
634 0b00000100,
635 0b00000000,
636 ];
637 assert_eq!(rotated_buffer.inner().data(), &expected);
638
639 rotated_buffer = RotatedBuffer::new(
640 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
641 Rotate::Degrees180,
642 );
643 rotated_buffer
644 .draw_iter([
645 Pixel(Point::new(-1, -1), BinaryColor::On), Pixel(Point::new(0, 0), BinaryColor::On),
647 Pixel(Point::new(1, 1), BinaryColor::On),
648 Pixel(Point::new(2, 2), BinaryColor::On),
649 ])
650 .unwrap();
651 #[rustfmt::skip]
652 let expected: [u8; 4] = [
653 0b00000000,
654 0b00000100,
655 0b00000010,
656 0b00000001,
657 ];
658 assert_eq!(rotated_buffer.inner().data(), &expected);
659
660 rotated_buffer = RotatedBuffer::new(
661 BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE),
662 Rotate::Degrees270,
663 );
664 rotated_buffer
665 .draw_iter([
666 Pixel(Point::new(-1, -1), BinaryColor::On), Pixel(Point::new(0, 0), BinaryColor::On),
668 Pixel(Point::new(1, 1), BinaryColor::On),
669 Pixel(Point::new(2, 2), BinaryColor::On),
670 ])
671 .unwrap();
672 #[rustfmt::skip]
673 let expected: [u8; 4] = [
674 0b00000000,
675 0b00100000,
676 0b01000000,
677 0b10000000,
678 ];
679 assert_eq!(rotated_buffer.inner().data(), &expected);
680 }
681
682 #[test]
683 fn test_rotated_buffer_fill_contiguous() {
684 const SIZE: Size = Size::new(8, 6);
686 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
687 let buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
688
689 let mut rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees90);
690 rotated_buffer
691 .fill_contiguous(
692 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
693 [BinaryColor::On; 8 * 8],
694 )
695 .unwrap();
696
697 #[rustfmt::skip]
698 let expected: [u8; 6] = [
699 0b00001111,
700 0b00001111,
701 0b00001111,
702 0b00001111,
703 0b00000000,
704 0b00000000,
705 ];
706 assert_eq!(rotated_buffer.inner().data(), &expected);
707
708 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees180);
709 rotated_buffer
710 .fill_contiguous(
711 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
712 [BinaryColor::On; 8 * 8],
713 )
714 .unwrap();
715
716 #[rustfmt::skip]
717 let expected: [u8; 6] = [
718 0b00000000,
719 0b00000000,
720 0b00001111,
721 0b00001111,
722 0b00001111,
723 0b00001111,
724 ];
725 assert_eq!(rotated_buffer.inner().data(), &expected);
726
727 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees270);
728 rotated_buffer
729 .fill_contiguous(
730 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
731 [BinaryColor::On; 8 * 8],
732 )
733 .unwrap();
734
735 #[rustfmt::skip]
736 let expected: [u8; 6] = [
737 0b00000000,
738 0b00000000,
739 0b11110000,
740 0b11110000,
741 0b11110000,
742 0b11110000,
743 ];
744 assert_eq!(rotated_buffer.inner().data(), &expected);
745 }
746
747 #[test]
748 fn test_rotated_buffer_fill_solid() {
749 const SIZE: Size = Size::new(8, 6);
751 const BUFFER_LENGTH: usize = binary_buffer_length(SIZE);
752 let buffer = BinaryBuffer::<{ BUFFER_LENGTH }>::new(SIZE);
753
754 let mut rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees90);
755 rotated_buffer
756 .fill_solid(
757 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
758 BinaryColor::On,
759 )
760 .unwrap();
761
762 #[rustfmt::skip]
763 let expected: [u8; 6] = [
764 0b00001111,
765 0b00001111,
766 0b00001111,
767 0b00001111,
768 0b00000000,
769 0b00000000,
770 ];
771 assert_eq!(rotated_buffer.inner().data(), &expected);
772
773 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees180);
774 rotated_buffer
775 .fill_solid(
776 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
777 BinaryColor::On,
778 )
779 .unwrap();
780
781 #[rustfmt::skip]
782 let expected: [u8; 6] = [
783 0b00000000,
784 0b00000000,
785 0b00001111,
786 0b00001111,
787 0b00001111,
788 0b00001111,
789 ];
790 assert_eq!(rotated_buffer.inner().data(), &expected);
791
792 rotated_buffer = RotatedBuffer::new(buffer.clone(), Rotate::Degrees270);
793 rotated_buffer
794 .fill_solid(
795 &Rectangle::new(Point::new(-4, -4), Size::new(8, 8)),
796 BinaryColor::On,
797 )
798 .unwrap();
799
800 #[rustfmt::skip]
801 let expected: [u8; 6] = [
802 0b00000000,
803 0b00000000,
804 0b11110000,
805 0b11110000,
806 0b11110000,
807 0b11110000,
808 ];
809 assert_eq!(rotated_buffer.inner().data(), &expected);
810 }
811
812 #[test]
813 fn test_rotate_near_corner() {
814 let mut r = Rotate::Degrees90;
815 assert_eq!(
817 Point::new(18, 1),
818 r.rotate_point(Point::new(1, 1), Size::new(10, 20))
819 );
820 r = Rotate::Degrees180;
821 assert_eq!(
823 Point::new(8, 18),
824 r.rotate_point(Point::new(1, 1), Size::new(10, 20))
825 );
826 r = Rotate::Degrees270;
827 assert_eq!(
829 Point::new(1, 8),
830 r.rotate_point(Point::new(1, 1), Size::new(10, 20))
831 );
832 }
833
834 #[test]
835 fn test_rotate_centre() {
836 let mut r = Rotate::Degrees90;
837 assert_eq!(
838 Point::new(2, 2),
839 r.rotate_point(Point::new(2, 2), Size::new(5, 5))
840 );
841 r = Rotate::Degrees180;
842 assert_eq!(
843 Point::new(2, 2),
844 r.rotate_point(Point::new(2, 2), Size::new(5, 5))
845 );
846 r = Rotate::Degrees270;
847 assert_eq!(
848 Point::new(2, 2),
849 r.rotate_point(Point::new(2, 2), Size::new(5, 5))
850 );
851 }
852
853 #[test]
854 fn test_rotate_size() {
855 let mut r = Rotate::Degrees90;
856 assert_eq!(Size::new(5, 10), r.rotate_size(Size::new(10, 5)));
857 r = Rotate::Degrees180;
858 assert_eq!(Size::new(10, 5), r.rotate_size(Size::new(10, 5)));
859 r = Rotate::Degrees270;
860 assert_eq!(Size::new(5, 10), r.rotate_size(Size::new(10, 5)));
861 }
862
863 #[test]
864 fn test_rotate_rectangle() {
865 let mut r = Rotate::Degrees90;
866 let rect = Rectangle::new(Point::new(1, 1), Size::new(3, 2));
867 let _dest_bounds = Size::new(8, 4);
869 let mut source_bounds = Size::new(4, 8);
870 let rotated = r.rotate_rectangle(rect, source_bounds);
871 assert_eq!(rotated.top_left, Point::new(5, 1));
874 assert_eq!(rotated.size, Size::new(2, 3));
875
876 r = Rotate::Degrees180;
877 source_bounds = Size::new(8, 4);
878 let rotated = r.rotate_rectangle(rect, source_bounds);
879 assert_eq!(rotated.top_left, Point::new(4, 1));
882 assert_eq!(rotated.size, Size::new(3, 2));
883
884 r = Rotate::Degrees270;
885 source_bounds = Size::new(4, 8);
886 let rotated = r.rotate_rectangle(rect, source_bounds);
887 assert_eq!(rotated.top_left, Point::new(1, 0));
890 assert_eq!(rotated.size, Size::new(2, 3));
891 }
892}