1#![cfg_attr(feature = "doc-images",
22cfg_attr(all(),
23doc = ::embed_doc_image::embed_image!("latch-circuit", "images/latch-circuit.png")))]
24#![cfg_attr(
25 not(feature = "doc-images"),
26 doc = "**Doc images not enabled**. Compile with feature `doc-images` and Rust version >= 1.54 \
27 to enable."
28)]
29use core::convert::Infallible;
173
174use super::Color;
175use crate::{FrameBufferOperations, MutableFrameBuffer};
176use bitfield::bitfield;
177use embedded_dma::ReadBuffer;
178use embedded_graphics::pixelcolor::Rgb888;
179use embedded_graphics::pixelcolor::RgbColor;
180use embedded_graphics::prelude::Point;
181
182bitfield! {
183 #[derive(Clone, Copy, Default, PartialEq, Eq)]
218 #[repr(transparent)]
219 struct Address(u8);
220 impl Debug;
221 pub output_enable, set_output_enable: 7;
222 pub latch, set_latch: 6;
223 pub addr, set_addr: 4, 0;
224}
225
226impl Address {
227 pub const fn new() -> Self {
228 Self(0)
229 }
230}
231
232bitfield! {
233 #[derive(Clone, Copy, Default, PartialEq)]
250 #[repr(transparent)]
251 struct Entry(u8);
252 impl Debug;
253 pub output_enable, set_output_enable: 7;
254 pub latch, set_latch: 6;
255 pub blu2, set_blu2: 5;
256 pub grn2, set_grn2: 4;
257 pub red2, set_red2: 3;
258 pub blu1, set_blu1: 2;
259 pub grn1, set_grn1: 1;
260 pub red1, set_red1: 0;
261}
262
263impl Entry {
264 pub const fn new() -> Self {
265 Self(0)
266 }
267
268 const COLOR0_MASK: u8 = 0b0000_0111; const COLOR1_MASK: u8 = 0b0011_1000; #[inline]
273 fn set_color0_bits(&mut self, bits: u8) {
274 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits & Self::COLOR0_MASK);
275 }
276
277 #[inline]
278 fn set_color1_bits(&mut self, bits: u8) {
279 self.0 = (self.0 & !Self::COLOR1_MASK) | ((bits << 3) & Self::COLOR1_MASK);
280 }
281}
282
283#[derive(Clone, Copy, PartialEq, Debug)]
294#[repr(C)]
295struct Row<const COLS: usize> {
296 data: [Entry; COLS],
297 address: [Address; 4],
298}
299
300#[inline]
302const fn map_index(index: usize) -> usize {
303 #[cfg(feature = "esp32-ordering")]
304 {
305 index ^ 2
306 }
307 #[cfg(not(feature = "esp32-ordering"))]
308 {
309 index
310 }
311}
312
313const fn make_addr_table() -> [[Address; 4]; 32] {
316 let mut tbl = [[Address::new(); 4]; 32];
317 let mut addr = 0;
318 while addr < 32 {
319 let mut i = 0;
320 while i < 4 {
321 let latch = i != 3;
322 let mapped_i = map_index(i);
323 let latch_bit = if latch { 1u8 << 6 } else { 0u8 };
324 tbl[addr][mapped_i].0 = latch_bit | addr as u8;
325 i += 1;
326 }
327 addr += 1;
328 }
329 tbl
330}
331
332static ADDR_TABLE: [[Address; 4]; 32] = make_addr_table();
333
334const fn make_data_template<const COLS: usize>() -> [Entry; COLS] {
337 let mut data = [Entry::new(); COLS];
338 let mut i = 0;
339 while i < COLS {
340 let mapped_i = map_index(i);
341 data[mapped_i].0 = if i == COLS - 1 { 0 } else { 0b1000_0000 }; i += 1;
345 }
346 data
347}
348
349impl<const COLS: usize> Row<COLS> {
350 pub const fn new() -> Self {
351 Self {
352 address: [Address::new(); 4],
353 data: [Entry::new(); COLS],
354 }
355 }
356
357 #[inline]
358 pub fn format(&mut self, addr: u8) {
359 self.address.copy_from_slice(&ADDR_TABLE[addr as usize]);
361
362 let data_template = make_data_template::<COLS>();
364 self.data.copy_from_slice(&data_template);
365 }
366
367 #[inline]
369 pub fn clear_colors(&mut self) {
370 const COLOR_CLEAR_MASK: u8 = !0b0011_1111; for entry in &mut self.data {
374 entry.0 &= COLOR_CLEAR_MASK;
375 }
376 }
377
378 #[inline]
379 pub fn set_color0(&mut self, col: usize, r: bool, g: bool, b: bool) {
380 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
381 let col = map_index(col);
382 self.data[col].set_color0_bits(bits);
383 }
384
385 #[inline]
386 pub fn set_color1(&mut self, col: usize, r: bool, g: bool, b: bool) {
387 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
388 let col = map_index(col);
389 self.data[col].set_color1_bits(bits);
390 }
391}
392
393impl<const COLS: usize> Default for Row<COLS> {
394 fn default() -> Self {
395 Self::new()
396 }
397}
398
399#[derive(Copy, Clone, Debug)]
400#[repr(C)]
401struct Frame<const ROWS: usize, const COLS: usize, const NROWS: usize> {
402 rows: [Row<COLS>; NROWS],
403}
404
405impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Frame<ROWS, COLS, NROWS> {
406 pub const fn new() -> Self {
407 Self {
408 rows: [Row::new(); NROWS],
409 }
410 }
411
412 #[inline]
413 pub fn format(&mut self) {
414 for (addr, row) in self.rows.iter_mut().enumerate() {
415 row.format(addr as u8);
416 }
417 }
418
419 #[inline]
421 pub fn clear_colors(&mut self) {
422 for row in &mut self.rows {
423 row.clear_colors();
424 }
425 }
426
427 #[inline]
428 pub fn set_pixel(&mut self, y: usize, x: usize, red: bool, green: bool, blue: bool) {
429 let row = &mut self.rows[if y < NROWS { y } else { y - NROWS }];
430 if y < NROWS {
431 row.set_color0(x, red, green, blue);
432 } else {
433 row.set_color1(x, red, green, blue);
434 }
435 }
436}
437
438impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Default
439 for Frame<ROWS, COLS, NROWS>
440{
441 fn default() -> Self {
442 Self::new()
443 }
444}
445
446#[derive(Copy, Clone)]
475#[repr(C)]
476#[repr(align(4))]
477pub struct DmaFrameBuffer<
478 const ROWS: usize,
479 const COLS: usize,
480 const NROWS: usize,
481 const BITS: u8,
482 const FRAME_COUNT: usize,
483> {
484 frames: [Frame<ROWS, COLS, NROWS>; FRAME_COUNT],
485}
486
487impl<
488 const ROWS: usize,
489 const COLS: usize,
490 const NROWS: usize,
491 const BITS: u8,
492 const FRAME_COUNT: usize,
493 > Default for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
494{
495 fn default() -> Self {
496 Self::new()
497 }
498}
499
500impl<
501 const ROWS: usize,
502 const COLS: usize,
503 const NROWS: usize,
504 const BITS: u8,
505 const FRAME_COUNT: usize,
506 > DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
507{
508 #[must_use]
524 pub fn new() -> Self {
525 let mut fb = Self {
526 frames: [Frame::new(); FRAME_COUNT],
527 };
528 fb.format();
529 fb
530 }
531
532 #[must_use]
535 pub const fn bcm_chunk_count() -> usize {
536 1
537 }
538
539 #[must_use]
542 pub const fn bcm_chunk_bytes() -> usize {
543 core::mem::size_of::<[Frame<ROWS, COLS, NROWS>; FRAME_COUNT]>()
544 }
545
546 pub fn format(&mut self) {
563 for frame in &mut self.frames {
564 frame.format();
565 }
566 }
567
568 #[inline]
585 pub fn erase(&mut self) {
586 for frame in &mut self.frames {
587 frame.clear_colors();
588 }
589 }
590
591 pub fn set_pixel(&mut self, p: Point, color: Color) {
607 if p.x < 0 || p.y < 0 {
608 return;
609 }
610 self.set_pixel_internal(p.x as usize, p.y as usize, color);
611 }
612
613 #[inline]
614 fn frames_on(v: u8) -> usize {
615 (v as usize) >> (8 - BITS)
617 }
618
619 #[inline]
620 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Rgb888) {
621 if x >= COLS || y >= ROWS {
622 return;
623 }
624
625 #[cfg(feature = "skip-black-pixels")]
628 if color == Rgb888::BLACK {
629 return;
630 }
631
632 let red_frames = Self::frames_on(color.r());
634 let green_frames = Self::frames_on(color.g());
635 let blue_frames = Self::frames_on(color.b());
636
637 for (frame_idx, frame) in self.frames.iter_mut().enumerate() {
639 frame.set_pixel(
640 y,
641 x,
642 frame_idx < red_frames,
643 frame_idx < green_frames,
644 frame_idx < blue_frames,
645 );
646 }
647 }
648}
649
650impl<
651 const ROWS: usize,
652 const COLS: usize,
653 const NROWS: usize,
654 const BITS: u8,
655 const FRAME_COUNT: usize,
656 > FrameBufferOperations for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
657{
658 #[inline]
659 fn erase(&mut self) {
660 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::erase(self);
661 }
662
663 #[inline]
664 fn set_pixel(&mut self, p: Point, color: Color) {
665 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::set_pixel(self, p, color);
666 }
667}
668
669impl<
670 const ROWS: usize,
671 const COLS: usize,
672 const NROWS: usize,
673 const BITS: u8,
674 const FRAME_COUNT: usize,
675 > embedded_graphics::prelude::OriginDimensions
676 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
677{
678 fn size(&self) -> embedded_graphics::prelude::Size {
679 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
680 }
681}
682
683impl<
684 const ROWS: usize,
685 const COLS: usize,
686 const NROWS: usize,
687 const BITS: u8,
688 const FRAME_COUNT: usize,
689 > embedded_graphics::draw_target::DrawTarget
690 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
691{
692 type Color = Color;
693
694 type Error = Infallible;
695
696 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
697 where
698 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
699 {
700 for pixel in pixels {
701 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
702 }
703 Ok(())
704 }
705}
706
707unsafe impl<
708 const ROWS: usize,
709 const COLS: usize,
710 const NROWS: usize,
711 const BITS: u8,
712 const FRAME_COUNT: usize,
713 > ReadBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
714{
715 type Word = u8;
716
717 unsafe fn read_buffer(&self) -> (*const u8, usize) {
718 let ptr = (&raw const self.frames).cast::<u8>();
719 let len = core::mem::size_of_val(&self.frames);
720 (ptr, len)
721 }
722}
723
724unsafe impl<
725 const ROWS: usize,
726 const COLS: usize,
727 const NROWS: usize,
728 const BITS: u8,
729 const FRAME_COUNT: usize,
730 > ReadBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
731{
732 type Word = u8;
733
734 unsafe fn read_buffer(&self) -> (*const u8, usize) {
735 let ptr = (&raw const self.frames).cast::<u8>();
736 let len = core::mem::size_of_val(&self.frames);
737 (ptr, len)
738 }
739}
740
741impl<
742 const ROWS: usize,
743 const COLS: usize,
744 const NROWS: usize,
745 const BITS: u8,
746 const FRAME_COUNT: usize,
747 > core::fmt::Debug for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
748{
749 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
750 let brightness_step = 1 << (8 - BITS);
751 f.debug_struct("DmaFrameBuffer")
752 .field("size", &core::mem::size_of_val(&self.frames))
753 .field("frame_count", &self.frames.len())
754 .field("frame_size", &core::mem::size_of_val(&self.frames[0]))
755 .field("brightness_step", &&brightness_step)
756 .finish()
757 }
758}
759
760#[cfg(feature = "defmt")]
761impl<
762 const ROWS: usize,
763 const COLS: usize,
764 const NROWS: usize,
765 const BITS: u8,
766 const FRAME_COUNT: usize,
767 > defmt::Format for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
768{
769 fn format(&self, f: defmt::Formatter) {
770 let brightness_step = 1 << (8 - BITS);
771 defmt::write!(
772 f,
773 "DmaFrameBuffer<{}, {}, {}, {}, {}>",
774 ROWS,
775 COLS,
776 NROWS,
777 BITS,
778 FRAME_COUNT
779 );
780 defmt::write!(f, " size: {}", core::mem::size_of_val(&self.frames));
781 defmt::write!(
782 f,
783 " frame_size: {}",
784 core::mem::size_of_val(&self.frames[0])
785 );
786 defmt::write!(f, " brightness_step: {}", brightness_step);
787 }
788}
789
790impl<
791 const ROWS: usize,
792 const COLS: usize,
793 const NROWS: usize,
794 const BITS: u8,
795 const FRAME_COUNT: usize,
796 > super::FrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
797{
798 fn get_word_size(&self) -> super::WordSize {
799 super::WordSize::Eight
800 }
801
802 fn plane_count(&self) -> usize {
803 1
804 }
805
806 fn plane_ptr_len(&self, plane_idx: usize) -> (*const u8, usize) {
807 assert!(plane_idx == 0, "latched DmaFrameBuffer has only 1 plane");
808 let ptr = (&raw const self.frames).cast::<u8>();
809 let len = core::mem::size_of_val(&self.frames);
810 (ptr, len)
811 }
812}
813
814impl<
815 const ROWS: usize,
816 const COLS: usize,
817 const NROWS: usize,
818 const BITS: u8,
819 const FRAME_COUNT: usize,
820 > embedded_graphics::prelude::OriginDimensions
821 for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
822{
823 fn size(&self) -> embedded_graphics::prelude::Size {
824 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
825 }
826}
827
828impl<
829 const ROWS: usize,
830 const COLS: usize,
831 const NROWS: usize,
832 const BITS: u8,
833 const FRAME_COUNT: usize,
834 > super::FrameBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
835{
836 fn get_word_size(&self) -> super::WordSize {
837 super::WordSize::Eight
838 }
839
840 fn plane_count(&self) -> usize {
841 1
842 }
843
844 fn plane_ptr_len(&self, plane_idx: usize) -> (*const u8, usize) {
845 assert!(plane_idx == 0, "latched DmaFrameBuffer has only 1 plane");
846 let ptr = (&raw const self.frames).cast::<u8>();
847 let len = core::mem::size_of_val(&self.frames);
848 (ptr, len)
849 }
850}
851
852impl<
853 const ROWS: usize,
854 const COLS: usize,
855 const NROWS: usize,
856 const BITS: u8,
857 const FRAME_COUNT: usize,
858 > MutableFrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
859{
860}
861
862#[cfg(test)]
863mod tests {
864 extern crate std;
865
866 use std::format;
867 use std::vec;
868
869 use super::*;
870 use crate::{FrameBuffer, WordSize};
871 use embedded_graphics::pixelcolor::RgbColor;
872 use embedded_graphics::prelude::*;
873 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle};
874
875 const TEST_ROWS: usize = 32;
876 const TEST_COLS: usize = 64;
877 const TEST_NROWS: usize = TEST_ROWS / 2;
878 const TEST_BITS: u8 = 3;
879 const TEST_FRAME_COUNT: usize = (1 << TEST_BITS) - 1; type TestFrameBuffer =
882 DmaFrameBuffer<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_BITS, TEST_FRAME_COUNT>;
883
884 #[test]
885 fn test_address_construction() {
886 let addr = Address::new();
887 assert_eq!(addr.0, 0);
888 assert_eq!(addr.latch(), false);
889 assert_eq!(addr.addr(), 0);
890 }
891
892 #[test]
893 fn test_address_setters() {
894 let mut addr = Address::new();
895
896 addr.set_latch(true);
897 assert_eq!(addr.latch(), true);
898 assert_eq!(addr.0 & 0b01000000, 0b01000000);
899
900 addr.set_addr(0b11111);
901 assert_eq!(addr.addr(), 0b11111);
902 assert_eq!(addr.0 & 0b00011111, 0b00011111);
903 }
904
905 #[test]
906 fn test_address_bit_isolation() {
907 let mut addr = Address::new();
908
909 addr.set_addr(0b11111);
911 addr.set_latch(true);
912 assert_eq!(addr.addr(), 0b11111);
913 assert_eq!(addr.latch(), true);
914 }
915
916 #[test]
917 fn test_entry_construction() {
918 let entry = Entry::new();
919 assert_eq!(entry.0, 0);
920 assert_eq!(entry.output_enable(), false);
921 assert_eq!(entry.latch(), false);
922 assert_eq!(entry.red1(), false);
923 assert_eq!(entry.grn1(), false);
924 assert_eq!(entry.blu1(), false);
925 assert_eq!(entry.red2(), false);
926 assert_eq!(entry.grn2(), false);
927 assert_eq!(entry.blu2(), false);
928 }
929
930 #[test]
931 fn test_entry_setters() {
932 let mut entry = Entry::new();
933
934 entry.set_output_enable(true);
935 assert_eq!(entry.output_enable(), true);
936 assert_eq!(entry.0 & 0b10000000, 0b10000000);
937
938 entry.set_latch(true);
939 assert_eq!(entry.latch(), true);
940 assert_eq!(entry.0 & 0b01000000, 0b01000000);
941
942 entry.set_red1(true);
944 entry.set_grn1(true);
945 entry.set_blu1(true);
946 assert_eq!(entry.red1(), true);
947 assert_eq!(entry.grn1(), true);
948 assert_eq!(entry.blu1(), true);
949 assert_eq!(entry.0 & 0b00000111, 0b00000111);
950
951 entry.set_red2(true);
953 entry.set_grn2(true);
954 entry.set_blu2(true);
955 assert_eq!(entry.red2(), true);
956 assert_eq!(entry.grn2(), true);
957 assert_eq!(entry.blu2(), true);
958 assert_eq!(entry.0 & 0b00111000, 0b00111000);
959 }
960
961 #[test]
962 fn test_entry_set_color0() {
963 let mut entry = Entry::new();
964
965 let bits = (u8::from(true) << 2) | (u8::from(false) << 1) | u8::from(true); entry.set_color0_bits(bits);
967 assert_eq!(entry.red1(), true);
968 assert_eq!(entry.grn1(), false);
969 assert_eq!(entry.blu1(), true);
970 assert_eq!(entry.0 & 0b00000111, 0b00000101); }
972
973 #[test]
974 fn test_entry_set_color1() {
975 let mut entry = Entry::new();
976
977 let bits = (u8::from(true) << 2) | (u8::from(true) << 1) | u8::from(false); entry.set_color1_bits(bits);
979 assert_eq!(entry.red2(), false);
980 assert_eq!(entry.grn2(), true);
981 assert_eq!(entry.blu2(), true);
982 assert_eq!(entry.0 & 0b00111000, 0b00110000); }
984
985 #[test]
986 fn test_row_construction() {
987 let row: Row<TEST_COLS> = Row::new();
988 assert_eq!(row.data.len(), TEST_COLS);
989 assert_eq!(row.address.len(), 4);
990
991 for entry in &row.data {
993 assert_eq!(entry.0, 0);
994 }
995 for addr in &row.address {
996 assert_eq!(addr.0, 0);
997 }
998 }
999
1000 #[test]
1001 fn test_row_format() {
1002 let mut row: Row<TEST_COLS> = Row::new();
1003 let test_addr = 5;
1004
1005 row.format(test_addr);
1006
1007 for addr in &row.address {
1009 assert_eq!(addr.addr(), test_addr);
1010 }
1014 let latch_false_count = row.address.iter().filter(|addr| !addr.latch()).count();
1018 assert_eq!(latch_false_count, 1);
1019
1020 for entry in &row.data {
1022 assert_eq!(entry.latch(), false);
1023 }
1024 let oe_false_count = row
1029 .data
1030 .iter()
1031 .filter(|entry| !entry.output_enable())
1032 .count();
1033 assert_eq!(oe_false_count, 1);
1034 }
1035
1036 #[test]
1037 fn test_row_set_color0() {
1038 let mut row: Row<TEST_COLS> = Row::new();
1039
1040 row.set_color0(0, true, false, true);
1041
1042 let mapped_col_0 = map_index(0);
1043 assert_eq!(row.data[mapped_col_0].red1(), true);
1044 assert_eq!(row.data[mapped_col_0].grn1(), false);
1045 assert_eq!(row.data[mapped_col_0].blu1(), true);
1046
1047 row.set_color0(1, false, true, false);
1049
1050 let mapped_col_1 = map_index(1);
1051 assert_eq!(row.data[mapped_col_1].red1(), false);
1052 assert_eq!(row.data[mapped_col_1].grn1(), true);
1053 assert_eq!(row.data[mapped_col_1].blu1(), false);
1054 }
1055
1056 #[test]
1057 fn test_row_set_color1() {
1058 let mut row: Row<TEST_COLS> = Row::new();
1059
1060 row.set_color1(0, true, true, false);
1061
1062 let mapped_col_0 = map_index(0);
1063 assert_eq!(row.data[mapped_col_0].red2(), true);
1064 assert_eq!(row.data[mapped_col_0].grn2(), true);
1065 assert_eq!(row.data[mapped_col_0].blu2(), false);
1066 }
1067
1068 #[test]
1069 fn test_frame_construction() {
1070 let frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1071 assert_eq!(frame.rows.len(), TEST_NROWS);
1072 }
1073
1074 #[test]
1075 fn test_frame_format() {
1076 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1077
1078 frame.format();
1079
1080 for (addr, row) in frame.rows.iter().enumerate() {
1081 for address in &row.address {
1083 assert_eq!(address.addr() as usize, addr);
1084 }
1085 }
1086 }
1087
1088 #[test]
1089 fn test_frame_set_pixel() {
1090 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1091
1092 frame.set_pixel(5, 10, true, false, true);
1094
1095 let mapped_col_10 = map_index(10);
1096 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1097 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1098 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1099
1100 frame.set_pixel(TEST_NROWS + 5, 15, false, true, false);
1102
1103 let mapped_col_15 = map_index(15);
1104 assert_eq!(frame.rows[5].data[mapped_col_15].red2(), false);
1105 assert_eq!(frame.rows[5].data[mapped_col_15].grn2(), true);
1106 assert_eq!(frame.rows[5].data[mapped_col_15].blu2(), false);
1107 }
1108
1109 #[test]
1110 fn test_row_default() {
1111 let row1: Row<TEST_COLS> = Row::new();
1112 let row2: Row<TEST_COLS> = Row::default();
1113
1114 assert_eq!(row1, row2);
1116 assert_eq!(row1.data.len(), row2.data.len());
1117 assert_eq!(row1.address.len(), row2.address.len());
1118
1119 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1121 assert_eq!(entry1.0, entry2.0);
1122 assert_eq!(entry1.0, 0);
1123 }
1124 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1125 assert_eq!(addr1.0, addr2.0);
1126 assert_eq!(addr1.0, 0);
1127 }
1128 }
1129
1130 #[test]
1131 fn test_frame_default() {
1132 let frame1: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1133 let frame2: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::default();
1134
1135 assert_eq!(frame1.rows.len(), frame2.rows.len());
1137
1138 for (row1, row2) in frame1.rows.iter().zip(frame2.rows.iter()) {
1140 assert_eq!(row1, row2);
1141
1142 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1144 assert_eq!(entry1.0, entry2.0);
1145 assert_eq!(entry1.0, 0);
1146 }
1147 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1148 assert_eq!(addr1.0, addr2.0);
1149 assert_eq!(addr1.0, 0);
1150 }
1151 }
1152 }
1153
1154 #[test]
1155 fn test_dma_framebuffer_construction() {
1156 let fb = TestFrameBuffer::new();
1157 assert_eq!(fb.frames.len(), TEST_FRAME_COUNT);
1158 }
1159
1160 #[test]
1161 fn test_bcm_chunk_info() {
1162 let expected_size =
1163 core::mem::size_of::<[Frame<TEST_ROWS, TEST_COLS, TEST_NROWS>; TEST_FRAME_COUNT]>();
1164 assert_eq!(TestFrameBuffer::bcm_chunk_bytes(), expected_size);
1165 assert_eq!(TestFrameBuffer::bcm_chunk_count(), 1);
1166 }
1167
1168 #[test]
1169 fn test_dma_framebuffer_format() {
1170 let mut fb = TestFrameBuffer {
1171 frames: [Frame::new(); TEST_FRAME_COUNT],
1172 };
1173 fb.format();
1174
1175 for frame in &fb.frames {
1177 for (addr, row) in frame.rows.iter().enumerate() {
1178 for address in &row.address {
1179 assert_eq!(address.addr() as usize, addr);
1180 }
1181 }
1182 }
1183 }
1184
1185 #[test]
1186 fn test_dma_framebuffer_set_pixel_bounds() {
1187 let mut fb = TestFrameBuffer::new();
1188
1189 fb.set_pixel(Point::new(-1, 5), Color::RED);
1191 fb.set_pixel(Point::new(5, -1), Color::RED);
1192
1193 fb.set_pixel(Point::new(TEST_COLS as i32, 5), Color::RED);
1195 fb.set_pixel(Point::new(5, TEST_ROWS as i32), Color::RED);
1196 }
1197
1198 #[test]
1199 fn test_dma_framebuffer_set_pixel_internal() {
1200 let mut fb = TestFrameBuffer::new();
1201
1202 let red_color = Rgb888::new(255, 0, 0);
1203 fb.set_pixel_internal(10, 5, red_color);
1204
1205 for frame in &fb.frames {
1209 let mapped_col_10 = map_index(10);
1211 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1212 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1213 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), false);
1214 }
1215 }
1216
1217 #[test]
1218 fn test_dma_framebuffer_brightness_modulation() {
1219 let mut fb = TestFrameBuffer::new();
1220
1221 let brightness_step = 1 << (8 - TEST_BITS); let test_brightness = brightness_step * 3; let color = Rgb888::new(test_brightness, 0, 0);
1225
1226 fb.set_pixel_internal(0, 0, color);
1227
1228 for (frame_idx, frame) in fb.frames.iter().enumerate() {
1231 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1232 let should_be_active = test_brightness >= frame_threshold;
1233
1234 let mapped_col_0 = map_index(0);
1235 assert_eq!(frame.rows[0].data[mapped_col_0].red1(), should_be_active);
1236 }
1237 }
1238
1239 #[test]
1240 fn test_origin_dimensions() {
1241 let fb = TestFrameBuffer::new();
1242 let size = fb.size();
1243 assert_eq!(size.width, TEST_COLS as u32);
1244 assert_eq!(size.height, TEST_ROWS as u32);
1245
1246 let mut fb = TestFrameBuffer::new();
1248 let fb_ref = &mut fb;
1249 let size = fb_ref.size();
1250 assert_eq!(size.width, TEST_COLS as u32);
1251 assert_eq!(size.height, TEST_ROWS as u32);
1252 }
1253
1254 #[test]
1255 fn test_draw_target() {
1256 let mut fb = TestFrameBuffer::new();
1257
1258 let pixels = vec![
1259 embedded_graphics::Pixel(Point::new(0, 0), Color::RED),
1260 embedded_graphics::Pixel(Point::new(1, 1), Color::GREEN),
1261 embedded_graphics::Pixel(Point::new(2, 2), Color::BLUE),
1262 ];
1263
1264 let result = fb.draw_iter(pixels);
1265 assert!(result.is_ok());
1266 }
1267
1268 #[test]
1269 fn test_draw_iter_pixel_verification() {
1270 let mut fb = TestFrameBuffer::new();
1271
1272 let pixels = vec![
1274 embedded_graphics::Pixel(Point::new(5, 2), Color::RED), embedded_graphics::Pixel(Point::new(10, 5), Color::GREEN), embedded_graphics::Pixel(Point::new(15, 8), Color::BLUE), embedded_graphics::Pixel(Point::new(20, 10), Color::WHITE), embedded_graphics::Pixel(Point::new(25, (TEST_NROWS + 3) as i32), Color::RED), embedded_graphics::Pixel(Point::new(30, (TEST_NROWS + 7) as i32), Color::GREEN), embedded_graphics::Pixel(Point::new(35, (TEST_NROWS + 12) as i32), Color::BLUE), embedded_graphics::Pixel(Point::new(40, 1), Color::BLACK), embedded_graphics::Pixel(Point::new(45, 3), Rgb888::new(16, 16, 16)), ];
1288
1289 let result = fb.draw_iter(pixels);
1290 assert!(result.is_ok());
1291
1292 let first_frame = &fb.frames[0];
1294 let brightness_step = 1 << (8 - TEST_BITS); let first_frame_threshold = brightness_step; let col_idx = map_index(5);
1300 assert_eq!(
1301 first_frame.rows[2].data[col_idx].red1(),
1302 Color::RED.r() >= first_frame_threshold
1303 );
1304 assert_eq!(
1305 first_frame.rows[2].data[col_idx].grn1(),
1306 Color::RED.g() >= first_frame_threshold
1307 );
1308 assert_eq!(
1309 first_frame.rows[2].data[col_idx].blu1(),
1310 Color::RED.b() >= first_frame_threshold
1311 );
1312
1313 let col_idx = map_index(10);
1315 assert_eq!(
1316 first_frame.rows[5].data[col_idx].red1(),
1317 Color::GREEN.r() >= first_frame_threshold
1318 );
1319 assert_eq!(
1320 first_frame.rows[5].data[col_idx].grn1(),
1321 Color::GREEN.g() >= first_frame_threshold
1322 );
1323 assert_eq!(
1324 first_frame.rows[5].data[col_idx].blu1(),
1325 Color::GREEN.b() >= first_frame_threshold
1326 );
1327
1328 let col_idx = map_index(15);
1330 assert_eq!(
1331 first_frame.rows[8].data[col_idx].red1(),
1332 Color::BLUE.r() >= first_frame_threshold
1333 );
1334 assert_eq!(
1335 first_frame.rows[8].data[col_idx].grn1(),
1336 Color::BLUE.g() >= first_frame_threshold
1337 );
1338 assert_eq!(
1339 first_frame.rows[8].data[col_idx].blu1(),
1340 Color::BLUE.b() >= first_frame_threshold
1341 );
1342
1343 let col_idx = map_index(20);
1345 assert_eq!(
1346 first_frame.rows[10].data[col_idx].red1(),
1347 Color::WHITE.r() >= first_frame_threshold
1348 );
1349 assert_eq!(
1350 first_frame.rows[10].data[col_idx].grn1(),
1351 Color::WHITE.g() >= first_frame_threshold
1352 );
1353 assert_eq!(
1354 first_frame.rows[10].data[col_idx].blu1(),
1355 Color::WHITE.b() >= first_frame_threshold
1356 );
1357
1358 let col_idx = map_index(25);
1361 assert_eq!(
1362 first_frame.rows[3].data[col_idx].red2(),
1363 Color::RED.r() >= first_frame_threshold
1364 );
1365 assert_eq!(
1366 first_frame.rows[3].data[col_idx].grn2(),
1367 Color::RED.g() >= first_frame_threshold
1368 );
1369 assert_eq!(
1370 first_frame.rows[3].data[col_idx].blu2(),
1371 Color::RED.b() >= first_frame_threshold
1372 );
1373
1374 let col_idx = map_index(30);
1376 assert_eq!(
1377 first_frame.rows[7].data[col_idx].red2(),
1378 Color::GREEN.r() >= first_frame_threshold
1379 );
1380 assert_eq!(
1381 first_frame.rows[7].data[col_idx].grn2(),
1382 Color::GREEN.g() >= first_frame_threshold
1383 );
1384 assert_eq!(
1385 first_frame.rows[7].data[col_idx].blu2(),
1386 Color::GREEN.b() >= first_frame_threshold
1387 );
1388
1389 let col_idx = map_index(35);
1391 assert_eq!(
1392 first_frame.rows[12].data[col_idx].red2(),
1393 Color::BLUE.r() >= first_frame_threshold
1394 );
1395 assert_eq!(
1396 first_frame.rows[12].data[col_idx].grn2(),
1397 Color::BLUE.g() >= first_frame_threshold
1398 );
1399 assert_eq!(
1400 first_frame.rows[12].data[col_idx].blu2(),
1401 Color::BLUE.b() >= first_frame_threshold
1402 );
1403
1404 let col_idx = map_index(40);
1406 assert_eq!(first_frame.rows[1].data[col_idx].red1(), false);
1407 assert_eq!(first_frame.rows[1].data[col_idx].grn1(), false);
1408 assert_eq!(first_frame.rows[1].data[col_idx].blu1(), false);
1409
1410 let col_idx = map_index(45);
1412 assert_eq!(
1413 first_frame.rows[3].data[col_idx].red1(),
1414 16 >= first_frame_threshold
1415 ); assert_eq!(
1417 first_frame.rows[3].data[col_idx].grn1(),
1418 16 >= first_frame_threshold
1419 ); assert_eq!(
1421 first_frame.rows[3].data[col_idx].blu1(),
1422 16 >= first_frame_threshold
1423 ); }
1425
1426 #[test]
1427 fn test_embedded_graphics_integration() {
1428 let mut fb = TestFrameBuffer::new();
1429
1430 let result = Rectangle::new(Point::new(5, 5), Size::new(10, 8))
1432 .into_styled(PrimitiveStyle::with_fill(Color::RED))
1433 .draw(&mut fb);
1434 assert!(result.is_ok());
1435
1436 let result = Circle::new(Point::new(30, 15), 8)
1438 .into_styled(PrimitiveStyle::with_fill(Color::BLUE))
1439 .draw(&mut fb);
1440 assert!(result.is_ok());
1441 }
1442
1443 #[test]
1444 fn test_read_buffer_implementation() {
1445 let fb = TestFrameBuffer::new();
1446
1447 unsafe {
1449 let (ptr, len) = fb.read_buffer();
1450 assert!(!ptr.is_null());
1451 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1452 }
1453
1454 let mut fb = TestFrameBuffer::new();
1456 let fb_ref = &mut fb;
1457 unsafe {
1458 let (ptr, len) = fb_ref.read_buffer();
1459 assert!(!ptr.is_null());
1460 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1461 }
1462 }
1463
1464 #[test]
1465 fn test_framebuffer_trait() {
1466 let fb = TestFrameBuffer::new();
1467 assert_eq!(fb.get_word_size(), WordSize::Eight);
1468
1469 let mut fb = TestFrameBuffer::new();
1470 let fb_ref = &mut fb;
1471 assert_eq!(fb_ref.get_word_size(), WordSize::Eight);
1472 }
1473
1474 #[test]
1475 fn test_debug_formatting() {
1476 let fb = TestFrameBuffer::new();
1477 let debug_string = format!("{:?}", fb);
1478 assert!(debug_string.contains("DmaFrameBuffer"));
1479 assert!(debug_string.contains("frame_count"));
1480 assert!(debug_string.contains("frame_size"));
1481 assert!(debug_string.contains("brightness_step"));
1482 }
1483
1484 #[test]
1485 fn test_default_implementation() {
1486 let fb1 = TestFrameBuffer::new();
1487 let fb2 = TestFrameBuffer::default();
1488
1489 assert_eq!(fb1.frames.len(), fb2.frames.len());
1491 }
1492
1493 #[cfg(feature = "esp32-ordering")]
1494 #[test]
1495 fn test_esp32_mapping() {
1496 assert_eq!(map_index(0), 2);
1498 assert_eq!(map_index(1), 3);
1499 assert_eq!(map_index(2), 0);
1500 assert_eq!(map_index(3), 1);
1501 assert_eq!(map_index(4), 6); assert_eq!(map_index(5), 7); }
1504
1505 #[test]
1506 fn test_memory_alignment() {
1507 let fb = TestFrameBuffer::new();
1508 let ptr = &fb as *const _ as usize;
1509
1510 assert_eq!(ptr % 4, 0);
1512 }
1513
1514 #[test]
1515 fn test_color_values() {
1516 let mut fb = TestFrameBuffer::new();
1517
1518 let colors = [
1520 (Color::RED, (255, 0, 0)),
1521 (Color::GREEN, (0, 255, 0)),
1522 (Color::BLUE, (0, 0, 255)),
1523 (Color::WHITE, (255, 255, 255)),
1524 (Color::BLACK, (0, 0, 0)),
1525 ];
1526
1527 for (i, (color, (r, g, b))) in colors.iter().enumerate() {
1528 fb.set_pixel(Point::new(i as i32, 0), *color);
1529 assert_eq!(color.r(), *r);
1530 assert_eq!(color.g(), *g);
1531 assert_eq!(color.b(), *b);
1532 }
1533 }
1534
1535 #[test]
1536 fn test_bits_assertion() {
1537 assert!(TEST_BITS <= 8);
1540 }
1541
1542 #[test]
1543 #[cfg(feature = "skip-black-pixels")]
1544 fn test_skip_black_pixels_enabled() {
1545 let mut fb = TestFrameBuffer::new();
1546
1547 fb.set_pixel_internal(10, 5, Color::RED);
1549
1550 let mapped_col_10 = map_index(10);
1552 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1553 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1554 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1555
1556 fb.set_pixel_internal(10, 5, Color::BLACK);
1558
1559 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1561 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1562 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1563 }
1564
1565 #[test]
1566 #[cfg(not(feature = "skip-black-pixels"))]
1567 fn test_skip_black_pixels_disabled() {
1568 let mut fb = TestFrameBuffer::new();
1569
1570 fb.set_pixel_internal(10, 5, Color::RED);
1572
1573 let mapped_col_10 = map_index(10);
1575 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1576 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1577 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1578
1579 fb.set_pixel_internal(10, 5, Color::BLACK);
1581
1582 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1584 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1585 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1586 }
1587
1588 #[test]
1589 fn test_bcm_frame_overwrite() {
1590 let mut fb = TestFrameBuffer::new();
1591
1592 fb.set_pixel_internal(10, 5, Color::WHITE);
1594
1595 let mapped_col_10 = map_index(10);
1596
1597 for frame in fb.frames.iter() {
1599 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1601 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), true);
1602 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1603 }
1604
1605 let half_white = embedded_graphics::pixelcolor::Rgb888::new(128, 128, 128);
1607 fb.set_pixel_internal(10, 5, half_white);
1608
1609 let brightness_step = 1 << (8 - TEST_BITS); for (frame_idx, frame) in fb.frames.iter().enumerate() {
1615 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1616 let should_be_active = 128 >= frame_threshold;
1617
1618 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), should_be_active);
1619 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), should_be_active);
1620 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), should_be_active);
1621 }
1622
1623 for frame_idx in 0..4 {
1626 assert_eq!(
1627 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1628 true
1629 );
1630 }
1631 for frame_idx in 4..TEST_FRAME_COUNT {
1633 assert_eq!(
1634 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1635 false
1636 );
1637 }
1638 }
1639
1640 #[test]
1641 fn test_new_auto_formats() {
1642 let fb = TestFrameBuffer::new();
1643
1644 for frame in &fb.frames {
1646 for (addr, row) in frame.rows.iter().enumerate() {
1647 for address in &row.address {
1648 assert_eq!(address.addr() as usize, addr);
1649 }
1650 }
1651 }
1652 }
1653
1654 #[test]
1655 fn test_erase() {
1656 let mut fb = TestFrameBuffer::new();
1657
1658 fb.set_pixel_internal(10, 5, Color::RED);
1660 fb.set_pixel_internal(20, 10, Color::GREEN);
1661
1662 let mapped_col_10 = map_index(10);
1663 let mapped_col_20 = map_index(20);
1664
1665 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1667 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), true);
1668
1669 fb.erase();
1671
1672 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1674 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1675 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1676 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].red1(), false);
1677 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), false);
1678 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].blu1(), false);
1679
1680 for frame in &fb.frames {
1682 for (addr, row) in frame.rows.iter().enumerate() {
1683 for address in &row.address {
1685 assert_eq!(address.addr() as usize, addr);
1686 }
1687 let oe_false_count = row
1689 .data
1690 .iter()
1691 .filter(|entry| !entry.output_enable())
1692 .count();
1693 assert_eq!(oe_false_count, 1);
1694 }
1695 }
1696 }
1697
1698 #[test]
1699 fn test_row_clear_colors() {
1700 let mut row: Row<TEST_COLS> = Row::new();
1701 row.format(5);
1702
1703 row.set_color0(0, true, false, true);
1705 row.set_color1(1, false, true, false);
1706
1707 let mapped_col_0 = map_index(0);
1708 let mapped_col_1 = map_index(1);
1709
1710 assert_eq!(row.data[mapped_col_0].red1(), true);
1712 assert_eq!(row.data[mapped_col_0].blu1(), true);
1713 assert_eq!(row.data[mapped_col_1].grn2(), true);
1714
1715 let original_oe_0 = row.data[mapped_col_0].output_enable();
1717 let original_latch_0 = row.data[mapped_col_0].latch();
1718 let original_oe_1 = row.data[mapped_col_1].output_enable();
1719 let original_latch_1 = row.data[mapped_col_1].latch();
1720
1721 row.clear_colors();
1723
1724 assert_eq!(row.data[mapped_col_0].red1(), false);
1726 assert_eq!(row.data[mapped_col_0].grn1(), false);
1727 assert_eq!(row.data[mapped_col_0].blu1(), false);
1728 assert_eq!(row.data[mapped_col_1].red2(), false);
1729 assert_eq!(row.data[mapped_col_1].grn2(), false);
1730 assert_eq!(row.data[mapped_col_1].blu2(), false);
1731
1732 assert_eq!(row.data[mapped_col_0].output_enable(), original_oe_0);
1734 assert_eq!(row.data[mapped_col_0].latch(), original_latch_0);
1735 assert_eq!(row.data[mapped_col_1].output_enable(), original_oe_1);
1736 assert_eq!(row.data[mapped_col_1].latch(), original_latch_1);
1737 }
1738
1739 #[test]
1740 fn test_make_addr_table_function() {
1741 let table = make_addr_table();
1743
1744 assert_eq!(table.len(), 32); let addr_0 = &table[0];
1749 assert_eq!(addr_0.len(), 4); let latch_false_count = addr_0.iter().filter(|addr| !addr.latch()).count();
1753 assert_eq!(latch_false_count, 1);
1754
1755 for addr in addr_0 {
1757 assert_eq!(addr.addr(), 0);
1758 }
1759
1760 let addr_31 = &table[31];
1762 let latch_false_count = addr_31.iter().filter(|addr| !addr.latch()).count();
1763 assert_eq!(latch_false_count, 1);
1764
1765 for addr in addr_31 {
1767 assert_eq!(addr.addr(), 31);
1768 }
1769 }
1770
1771 #[test]
1772 fn test_make_data_template_function() {
1773 let template = make_data_template::<TEST_COLS>();
1775
1776 assert_eq!(template.len(), TEST_COLS);
1778
1779 for entry in &template {
1781 assert_eq!(entry.latch(), false);
1782 }
1783
1784 let oe_false_count = template
1786 .iter()
1787 .filter(|entry| !entry.output_enable())
1788 .count();
1789 assert_eq!(oe_false_count, 1);
1790
1791 let small_template = make_data_template::<4>();
1793 assert_eq!(small_template.len(), 4);
1794
1795 let oe_false_count = small_template
1796 .iter()
1797 .filter(|entry| !entry.output_enable())
1798 .count();
1799 assert_eq!(oe_false_count, 1);
1800
1801 #[cfg(not(feature = "esp32-ordering"))]
1804 {
1805 let single_template = make_data_template::<1>();
1806 assert_eq!(single_template.len(), 1);
1807 assert_eq!(single_template[0].output_enable(), false); assert_eq!(single_template[0].latch(), false);
1809 }
1810 }
1811
1812 #[test]
1813 fn test_addr_table_correctness() {
1814 for addr in 0..32 {
1816 let mut expected_addresses = [Address::new(); 4];
1817
1818 for i in 0..4 {
1820 let latch = !matches!(i, 3);
1821 #[cfg(feature = "esp32-ordering")]
1822 let mapped_i = map_index(i);
1823 #[cfg(not(feature = "esp32-ordering"))]
1824 let mapped_i = i;
1825
1826 expected_addresses[mapped_i].set_latch(latch);
1827 expected_addresses[mapped_i].set_addr(addr);
1828 }
1829
1830 let table_addresses = &ADDR_TABLE[addr as usize];
1832 for i in 0..4 {
1833 assert_eq!(table_addresses[i].0, expected_addresses[i].0);
1834 }
1835 }
1836 }
1837
1838 const CHAR_W: i32 = 6;
1840 const CHAR_H: i32 = 10;
1841
1842 fn verify_glyph_at(fb: &mut TestFrameBuffer, origin: Point) {
1845 use embedded_graphics::mock_display::MockDisplay;
1846 use embedded_graphics::mono_font::ascii::FONT_6X10;
1847 use embedded_graphics::mono_font::MonoTextStyle;
1848 use embedded_graphics::text::{Baseline, Text};
1849
1850 let style = MonoTextStyle::new(&FONT_6X10, Color::WHITE);
1852 Text::with_baseline("A", origin, style, Baseline::Top)
1853 .draw(fb)
1854 .unwrap();
1855
1856 let mut reference: MockDisplay<Color> = MockDisplay::new();
1858 Text::with_baseline("A", Point::zero(), style, Baseline::Top)
1859 .draw(&mut reference)
1860 .unwrap();
1861
1862 for dy in 0..CHAR_H {
1864 for dx in 0..CHAR_W {
1865 let expected_on = reference
1866 .get_pixel(Point::new(dx, dy))
1867 .unwrap_or(Color::BLACK)
1868 != Color::BLACK;
1869
1870 let gx = (origin.x + dx) as usize;
1871 let gy = (origin.y + dy) as usize;
1872
1873 let frame0 = &fb.frames[0];
1880 let e = if gy < TEST_NROWS {
1881 &frame0.rows[gy].data[map_index(gx)]
1882 } else {
1883 &frame0.rows[gy - TEST_NROWS].data[map_index(gx)]
1884 };
1885
1886 let (r, g, b) = if gy >= TEST_NROWS {
1887 (e.red2(), e.grn2(), e.blu2())
1888 } else {
1889 (e.red1(), e.grn1(), e.blu1())
1890 };
1891
1892 if expected_on {
1893 assert!(r && g && b);
1894 } else {
1895 assert!(!r && !g && !b);
1896 }
1897 }
1898 }
1899 }
1900
1901 #[test]
1902 fn test_draw_char_corners() {
1903 let upper_left = Point::new(0, 0);
1905 let lower_right = Point::new(TEST_COLS as i32 - CHAR_W, TEST_ROWS as i32 - CHAR_H);
1906
1907 let mut fb = TestFrameBuffer::new();
1908
1909 verify_glyph_at(&mut fb, upper_left);
1911 verify_glyph_at(&mut fb, lower_right);
1913 }
1914
1915 #[test]
1916 fn test_framebuffer_operations_trait_erase() {
1917 let mut fb = TestFrameBuffer::new();
1918
1919 fb.set_pixel_internal(10, 5, Color::RED);
1921 fb.set_pixel_internal(20, 10, Color::GREEN);
1922
1923 <TestFrameBuffer as FrameBufferOperations>::erase(&mut fb);
1925
1926 let mc10 = map_index(10);
1928 let mc20 = map_index(20);
1929 assert_eq!(fb.frames[0].rows[5].data[mc10].red1(), false);
1930 assert_eq!(fb.frames[0].rows[10].data[mc20].grn1(), false);
1931
1932 let row0 = &fb.frames[0].rows[0];
1934 let oe_false_count = row0
1935 .data
1936 .iter()
1937 .filter(|entry| !entry.output_enable())
1938 .count();
1939 assert_eq!(oe_false_count, 1);
1940 assert!(row0.data.iter().all(|e| !e.latch()));
1941
1942 for (i, addr) in row0.address.iter().enumerate() {
1944 assert_eq!(addr.0, ADDR_TABLE[0][i].0);
1945 }
1946 }
1947
1948 #[test]
1949 fn test_framebuffer_operations_trait_set_pixel() {
1950 let mut fb = TestFrameBuffer::new();
1951
1952 <TestFrameBuffer as FrameBufferOperations>::set_pixel(
1954 &mut fb,
1955 Point::new(8, 3),
1956 Color::BLUE,
1957 );
1958
1959 let idx = map_index(8);
1961 assert_eq!(fb.frames[0].rows[3].data[idx].blu1(), true);
1962 assert_eq!(fb.frames[0].rows[3].data[idx].red1(), false);
1964 assert_eq!(fb.frames[0].rows[3].data[idx].grn1(), false);
1965 }
1966}