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