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
182#[cfg(feature = "lead-blank-1")]
183const LEAD_BLANK_DELAY: usize = 1;
184#[cfg(feature = "lead-blank-2")]
185const LEAD_BLANK_DELAY: usize = 2;
186#[cfg(feature = "lead-blank-4")]
187const LEAD_BLANK_DELAY: usize = 4;
188#[cfg(feature = "lead-blank-8")]
189const LEAD_BLANK_DELAY: usize = 8;
190#[cfg(feature = "lead-blank-16")]
191const LEAD_BLANK_DELAY: usize = 16;
192
193#[cfg(not(any(
194 feature = "lead-blank-1",
195 feature = "lead-blank-2",
196 feature = "lead-blank-4",
197 feature = "lead-blank-8",
198 feature = "lead-blank-16"
199)))]
200const LEAD_BLANK_DELAY: usize = 0;
201
202#[cfg(feature = "trail-blank-1")]
203const TRAIL_BLANK_DELAY: usize = 1;
204#[cfg(feature = "trail-blank-2")]
205const TRAIL_BLANK_DELAY: usize = 2;
206#[cfg(feature = "trail-blank-4")]
207const TRAIL_BLANK_DELAY: usize = 4;
208#[cfg(feature = "trail-blank-8")]
209const TRAIL_BLANK_DELAY: usize = 8;
210#[cfg(feature = "trail-blank-16")]
211const TRAIL_BLANK_DELAY: usize = 16;
212
213#[cfg(not(any(
214 feature = "trail-blank-1",
215 feature = "trail-blank-2",
216 feature = "trail-blank-4",
217 feature = "trail-blank-8",
218 feature = "trail-blank-16"
219)))]
220const TRAIL_BLANK_DELAY: usize = 0;
221
222#[cfg(feature = "inter-row-blank-4")]
223const INTER_ROW_BLANK: usize = 4;
224#[cfg(feature = "inter-row-blank-8")]
225const INTER_ROW_BLANK: usize = 8;
226#[cfg(feature = "inter-row-blank-16")]
227const INTER_ROW_BLANK: usize = 16;
228#[cfg(feature = "inter-row-blank-32")]
229const INTER_ROW_BLANK: usize = 32;
230#[cfg(not(any(
231 feature = "inter-row-blank-4",
232 feature = "inter-row-blank-8",
233 feature = "inter-row-blank-16",
234 feature = "inter-row-blank-32"
235)))]
236const INTER_ROW_BLANK: usize = 0;
237
238#[cfg(not(feature = "invert-oe"))]
239const OE_ACTIVE: u8 = 0b1000_0000;
240#[cfg(not(feature = "invert-oe"))]
241const OE_BLANK: u8 = 0;
242
243#[cfg(feature = "invert-oe")]
244const OE_ACTIVE: u8 = 0;
245#[cfg(feature = "invert-oe")]
246const OE_BLANK: u8 = 0b1000_0000;
247
248bitfield! {
249 #[derive(Clone, Copy, Default, PartialEq, Eq)]
284 #[repr(transparent)]
285 struct Address(u8);
286 impl Debug;
287 pub output_enable, set_output_enable: 7;
288 pub latch, set_latch: 6;
289 pub addr, set_addr: 4, 0;
290}
291
292impl Address {
293 pub const fn new() -> Self {
294 Self(0)
295 }
296}
297
298bitfield! {
299 #[derive(Clone, Copy, Default, PartialEq)]
316 #[repr(transparent)]
317 struct Entry(u8);
318 impl Debug;
319 pub output_enable, set_output_enable: 7;
320 pub latch, set_latch: 6;
321 pub blu2, set_blu2: 5;
322 pub grn2, set_grn2: 4;
323 pub red2, set_red2: 3;
324 pub blu1, set_blu1: 2;
325 pub grn1, set_grn1: 1;
326 pub red1, set_red1: 0;
327}
328
329impl Entry {
330 pub const fn new() -> Self {
331 Self(0)
332 }
333
334 const COLOR0_MASK: u8 = 0b0000_0111; const COLOR1_MASK: u8 = 0b0011_1000; #[inline]
339 fn set_color0_bits(&mut self, bits: u8) {
340 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits & Self::COLOR0_MASK);
341 }
342
343 #[inline]
344 fn set_color1_bits(&mut self, bits: u8) {
345 self.0 = (self.0 & !Self::COLOR1_MASK) | ((bits << 3) & Self::COLOR1_MASK);
346 }
347}
348
349#[derive(Clone, Copy, PartialEq, Debug)]
360#[repr(C)]
361struct Row<const COLS: usize> {
362 data: [Entry; COLS],
363 address: [Address; 4],
364 gap: [Entry; INTER_ROW_BLANK],
365}
366
367#[inline]
369const fn map_index(index: usize) -> usize {
370 #[cfg(feature = "esp32-ordering")]
371 {
372 index ^ 2
373 }
374 #[cfg(not(feature = "esp32-ordering"))]
375 {
376 index
377 }
378}
379
380const fn make_addr_table() -> [[Address; 4]; 32] {
383 let mut tbl = [[Address::new(); 4]; 32];
384 let mut addr = 0;
385 while addr < 32 {
386 let mut i = 0;
387 while i < 4 {
388 let latch = i != 3;
389 let mapped_i = map_index(i);
390 let latch_bit = if latch { 1u8 << 6 } else { 0u8 };
391 tbl[addr][mapped_i].0 = OE_BLANK | latch_bit | addr as u8;
392 i += 1;
393 }
394 addr += 1;
395 }
396 tbl
397}
398
399const ADDR_TABLE: [[Address; 4]; 32] = make_addr_table();
400
401#[allow(clippy::absurd_extreme_comparisons)]
404const fn make_data_template<const COLS: usize>() -> [Entry; COLS] {
405 let mut data = [Entry::new(); COLS];
406 let mut i = 0;
407 while i < COLS {
408 let mapped_i = map_index(i);
409 data[mapped_i].0 =
410 if i >= TRAIL_BLANK_DELAY && i < COLS.saturating_sub(LEAD_BLANK_DELAY + 1) {
411 OE_ACTIVE
412 } else {
413 OE_BLANK
414 };
415 i += 1;
416 }
417 data
418}
419
420impl<const COLS: usize> Row<COLS> {
421 pub const fn new() -> Self {
422 Self {
423 address: [Address::new(); 4],
424 data: [Entry::new(); COLS],
425 gap: [Entry::new(); INTER_ROW_BLANK],
426 }
427 }
428
429 #[inline]
430 pub const fn format(&mut self, addr: u8) {
431 let src_addr = ADDR_TABLE[addr as usize];
432 let mut i = 0;
433 while i < 4 {
434 self.address[i] = src_addr[i];
435 i += 1;
436 }
437
438 let data_template = make_data_template::<COLS>();
439 let mut i = 0;
440 while i < COLS {
441 self.data[i] = data_template[i];
442 i += 1;
443 }
444
445 let mut i = 0;
446 #[allow(clippy::absurd_extreme_comparisons)]
448 while i < INTER_ROW_BLANK {
449 self.gap[i].0 = OE_BLANK;
450 i += 1;
451 }
452 }
453
454 #[inline]
456 pub fn clear_colors(&mut self) {
457 const COLOR_CLEAR_MASK: u8 = !0b0011_1111; for entry in &mut self.data {
461 entry.0 &= COLOR_CLEAR_MASK;
462 }
463 }
464
465 #[inline]
466 pub fn set_color0(&mut self, col: usize, r: bool, g: bool, b: bool) {
467 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
468 let col = map_index(col);
469 self.data[col].set_color0_bits(bits);
470 }
471
472 #[inline]
473 pub fn set_color1(&mut self, col: usize, r: bool, g: bool, b: bool) {
474 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
475 let col = map_index(col);
476 self.data[col].set_color1_bits(bits);
477 }
478}
479
480impl<const COLS: usize> Default for Row<COLS> {
481 fn default() -> Self {
482 Self::new()
483 }
484}
485
486#[derive(Copy, Clone, Debug)]
487#[repr(C)]
488struct Frame<const ROWS: usize, const COLS: usize, const NROWS: usize> {
489 rows: [Row<COLS>; NROWS],
490}
491
492impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Frame<ROWS, COLS, NROWS> {
493 pub const fn new() -> Self {
494 Self {
495 rows: [Row::new(); NROWS],
496 }
497 }
498
499 #[inline]
500 pub const fn format(&mut self) {
501 let mut addr = 0;
502 while addr < NROWS {
503 self.rows[addr].format(addr as u8);
504 addr += 1;
505 }
506 }
507
508 #[inline]
510 pub fn clear_colors(&mut self) {
511 for row in &mut self.rows {
512 row.clear_colors();
513 }
514 }
515
516 #[inline]
517 pub fn set_pixel(&mut self, y: usize, x: usize, red: bool, green: bool, blue: bool) {
518 let row = &mut self.rows[if y < NROWS { y } else { y - NROWS }];
519 if y < NROWS {
520 row.set_color0(x, red, green, blue);
521 } else {
522 row.set_color1(x, red, green, blue);
523 }
524 }
525}
526
527impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Default
528 for Frame<ROWS, COLS, NROWS>
529{
530 fn default() -> Self {
531 Self::new()
532 }
533}
534
535#[derive(Copy, Clone)]
564#[repr(C)]
565#[repr(align(4))]
566pub struct DmaFrameBuffer<
567 const ROWS: usize,
568 const COLS: usize,
569 const NROWS: usize,
570 const BITS: u8,
571 const FRAME_COUNT: usize,
572> {
573 frames: [Frame<ROWS, COLS, NROWS>; FRAME_COUNT],
574}
575
576impl<
577 const ROWS: usize,
578 const COLS: usize,
579 const NROWS: usize,
580 const BITS: u8,
581 const FRAME_COUNT: usize,
582 > Default for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
583{
584 fn default() -> Self {
585 Self::new()
586 }
587}
588
589impl<
590 const ROWS: usize,
591 const COLS: usize,
592 const NROWS: usize,
593 const BITS: u8,
594 const FRAME_COUNT: usize,
595 > DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
596{
597 #[must_use]
613 pub const fn new() -> Self {
614 let mut fb = Self {
615 frames: [Frame::new(); FRAME_COUNT],
616 };
617 fb.format();
618 fb
619 }
620
621 #[must_use]
624 pub const fn bcm_chunk_count() -> usize {
625 1
626 }
627
628 #[must_use]
631 pub const fn bcm_chunk_bytes() -> usize {
632 core::mem::size_of::<[Frame<ROWS, COLS, NROWS>; FRAME_COUNT]>()
633 }
634
635 pub const fn format(&mut self) {
652 let mut i = 0;
653 while i < FRAME_COUNT {
654 self.frames[i].format();
655 i += 1;
656 }
657 }
658
659 #[inline]
676 pub fn erase(&mut self) {
677 for frame in &mut self.frames {
678 frame.clear_colors();
679 }
680 }
681
682 pub fn set_pixel(&mut self, p: Point, color: Color) {
698 if p.x < 0 || p.y < 0 {
699 return;
700 }
701 self.set_pixel_internal(p.x as usize, p.y as usize, color);
702 }
703
704 #[inline]
705 fn frames_on(v: u8) -> usize {
706 (v as usize) >> (8 - BITS)
708 }
709
710 #[inline]
711 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Rgb888) {
712 if x >= COLS || y >= ROWS {
713 return;
714 }
715
716 #[cfg(feature = "skip-black-pixels")]
719 if color == Rgb888::BLACK {
720 return;
721 }
722
723 let red_frames = Self::frames_on(color.r());
725 let green_frames = Self::frames_on(color.g());
726 let blue_frames = Self::frames_on(color.b());
727
728 for (frame_idx, frame) in self.frames.iter_mut().enumerate() {
730 frame.set_pixel(
731 y,
732 x,
733 frame_idx < red_frames,
734 frame_idx < green_frames,
735 frame_idx < blue_frames,
736 );
737 }
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 > FrameBufferOperations for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
748{
749 #[inline]
750 fn erase(&mut self) {
751 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::erase(self);
752 }
753
754 #[inline]
755 fn set_pixel(&mut self, p: Point, color: Color) {
756 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::set_pixel(self, p, color);
757 }
758}
759
760impl<
761 const ROWS: usize,
762 const COLS: usize,
763 const NROWS: usize,
764 const BITS: u8,
765 const FRAME_COUNT: usize,
766 > embedded_graphics::prelude::OriginDimensions
767 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
768{
769 fn size(&self) -> embedded_graphics::prelude::Size {
770 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
771 }
772}
773
774impl<
775 const ROWS: usize,
776 const COLS: usize,
777 const NROWS: usize,
778 const BITS: u8,
779 const FRAME_COUNT: usize,
780 > embedded_graphics::draw_target::DrawTarget
781 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
782{
783 type Color = Color;
784
785 type Error = Infallible;
786
787 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
788 where
789 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
790 {
791 for pixel in pixels {
792 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
793 }
794 Ok(())
795 }
796}
797
798unsafe impl<
799 const ROWS: usize,
800 const COLS: usize,
801 const NROWS: usize,
802 const BITS: u8,
803 const FRAME_COUNT: usize,
804 > ReadBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
805{
806 type Word = u8;
807
808 unsafe fn read_buffer(&self) -> (*const u8, usize) {
809 let ptr = (&raw const self.frames).cast::<u8>();
810 let len = core::mem::size_of_val(&self.frames);
811 (ptr, len)
812 }
813}
814
815unsafe impl<
816 const ROWS: usize,
817 const COLS: usize,
818 const NROWS: usize,
819 const BITS: u8,
820 const FRAME_COUNT: usize,
821 > ReadBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
822{
823 type Word = u8;
824
825 unsafe fn read_buffer(&self) -> (*const u8, usize) {
826 let ptr = (&raw const self.frames).cast::<u8>();
827 let len = core::mem::size_of_val(&self.frames);
828 (ptr, len)
829 }
830}
831
832impl<
833 const ROWS: usize,
834 const COLS: usize,
835 const NROWS: usize,
836 const BITS: u8,
837 const FRAME_COUNT: usize,
838 > core::fmt::Debug for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
839{
840 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
841 let brightness_step = 1 << (8 - BITS);
842 f.debug_struct("DmaFrameBuffer")
843 .field("size", &core::mem::size_of_val(&self.frames))
844 .field("frame_count", &self.frames.len())
845 .field("frame_size", &core::mem::size_of_val(&self.frames[0]))
846 .field("brightness_step", &&brightness_step)
847 .finish()
848 }
849}
850
851#[cfg(feature = "defmt")]
852impl<
853 const ROWS: usize,
854 const COLS: usize,
855 const NROWS: usize,
856 const BITS: u8,
857 const FRAME_COUNT: usize,
858 > defmt::Format for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
859{
860 fn format(&self, f: defmt::Formatter) {
861 let brightness_step = 1 << (8 - BITS);
862 defmt::write!(
863 f,
864 "DmaFrameBuffer<{}, {}, {}, {}, {}>",
865 ROWS,
866 COLS,
867 NROWS,
868 BITS,
869 FRAME_COUNT
870 );
871 defmt::write!(f, " size: {}", core::mem::size_of_val(&self.frames));
872 defmt::write!(
873 f,
874 " frame_size: {}",
875 core::mem::size_of_val(&self.frames[0])
876 );
877 defmt::write!(f, " brightness_step: {}", brightness_step);
878 }
879}
880
881impl<
882 const ROWS: usize,
883 const COLS: usize,
884 const NROWS: usize,
885 const BITS: u8,
886 const FRAME_COUNT: usize,
887 > super::FrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
888{
889 type Word = u8;
890
891 fn plane_count(&self) -> usize {
892 1
893 }
894
895 fn plane_ptr_len(&self, plane_idx: usize) -> (*const u8, usize) {
896 assert!(plane_idx == 0, "latched DmaFrameBuffer has only 1 plane");
897 let ptr = (&raw const self.frames).cast::<u8>();
898 let len = core::mem::size_of_val(&self.frames);
899 (ptr, len)
900 }
901}
902
903impl<
904 const ROWS: usize,
905 const COLS: usize,
906 const NROWS: usize,
907 const BITS: u8,
908 const FRAME_COUNT: usize,
909 > embedded_graphics::prelude::OriginDimensions
910 for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
911{
912 fn size(&self) -> embedded_graphics::prelude::Size {
913 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
914 }
915}
916
917impl<
918 const ROWS: usize,
919 const COLS: usize,
920 const NROWS: usize,
921 const BITS: u8,
922 const FRAME_COUNT: usize,
923 > MutableFrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
924{
925}
926
927#[cfg(test)]
928mod tests {
929 extern crate std;
930
931 use std::format;
932 use std::vec;
933
934 use super::*;
935 use crate::{FrameBuffer, WordSize};
936 use embedded_graphics::pixelcolor::RgbColor;
937 use embedded_graphics::prelude::*;
938 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle};
939
940 const TEST_ROWS: usize = 32;
941 const TEST_COLS: usize = 64;
942 const TEST_NROWS: usize = TEST_ROWS / 2;
943 const TEST_BITS: u8 = 3;
944 const TEST_FRAME_COUNT: usize = (1 << TEST_BITS) - 1; type TestFrameBuffer =
947 DmaFrameBuffer<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_BITS, TEST_FRAME_COUNT>;
948
949 #[test]
950 fn test_address_construction() {
951 let addr = Address::new();
952 assert_eq!(addr.0, 0);
953 assert_eq!(addr.latch(), false);
954 assert_eq!(addr.addr(), 0);
955 }
956
957 #[test]
958 fn test_address_setters() {
959 let mut addr = Address::new();
960
961 addr.set_latch(true);
962 assert_eq!(addr.latch(), true);
963 assert_eq!(addr.0 & 0b01000000, 0b01000000);
964
965 addr.set_addr(0b11111);
966 assert_eq!(addr.addr(), 0b11111);
967 assert_eq!(addr.0 & 0b00011111, 0b00011111);
968 }
969
970 #[test]
971 fn test_address_bit_isolation() {
972 let mut addr = Address::new();
973
974 addr.set_addr(0b11111);
976 addr.set_latch(true);
977 assert_eq!(addr.addr(), 0b11111);
978 assert_eq!(addr.latch(), true);
979 }
980
981 #[test]
982 fn test_entry_construction() {
983 let entry = Entry::new();
984 assert_eq!(entry.0, 0);
985 assert_eq!(entry.output_enable(), false);
986 assert_eq!(entry.latch(), false);
987 assert_eq!(entry.red1(), false);
988 assert_eq!(entry.grn1(), false);
989 assert_eq!(entry.blu1(), false);
990 assert_eq!(entry.red2(), false);
991 assert_eq!(entry.grn2(), false);
992 assert_eq!(entry.blu2(), false);
993 }
994
995 #[test]
996 fn test_entry_setters() {
997 let mut entry = Entry::new();
998
999 entry.set_output_enable(true);
1000 assert_eq!(entry.output_enable(), true);
1001 assert_eq!(entry.0 & 0b10000000, 0b10000000);
1002
1003 entry.set_latch(true);
1004 assert_eq!(entry.latch(), true);
1005 assert_eq!(entry.0 & 0b01000000, 0b01000000);
1006
1007 entry.set_red1(true);
1009 entry.set_grn1(true);
1010 entry.set_blu1(true);
1011 assert_eq!(entry.red1(), true);
1012 assert_eq!(entry.grn1(), true);
1013 assert_eq!(entry.blu1(), true);
1014 assert_eq!(entry.0 & 0b00000111, 0b00000111);
1015
1016 entry.set_red2(true);
1018 entry.set_grn2(true);
1019 entry.set_blu2(true);
1020 assert_eq!(entry.red2(), true);
1021 assert_eq!(entry.grn2(), true);
1022 assert_eq!(entry.blu2(), true);
1023 assert_eq!(entry.0 & 0b00111000, 0b00111000);
1024 }
1025
1026 #[test]
1027 fn test_entry_set_color0() {
1028 let mut entry = Entry::new();
1029
1030 let bits = (u8::from(true) << 2) | (u8::from(false) << 1) | u8::from(true); entry.set_color0_bits(bits);
1032 assert_eq!(entry.red1(), true);
1033 assert_eq!(entry.grn1(), false);
1034 assert_eq!(entry.blu1(), true);
1035 assert_eq!(entry.0 & 0b00000111, 0b00000101); }
1037
1038 #[test]
1039 fn test_entry_set_color1() {
1040 let mut entry = Entry::new();
1041
1042 let bits = (u8::from(true) << 2) | (u8::from(true) << 1) | u8::from(false); entry.set_color1_bits(bits);
1044 assert_eq!(entry.red2(), false);
1045 assert_eq!(entry.grn2(), true);
1046 assert_eq!(entry.blu2(), true);
1047 assert_eq!(entry.0 & 0b00111000, 0b00110000); }
1049
1050 #[test]
1051 fn test_row_construction() {
1052 let row: Row<TEST_COLS> = Row::new();
1053 assert_eq!(row.data.len(), TEST_COLS);
1054 assert_eq!(row.address.len(), 4);
1055
1056 for entry in &row.data {
1058 assert_eq!(entry.0, 0);
1059 }
1060 for addr in &row.address {
1061 assert_eq!(addr.0, 0);
1062 }
1063 }
1064
1065 #[test]
1066 fn test_row_format() {
1067 let mut row: Row<TEST_COLS> = Row::new();
1068 let test_addr = 5;
1069
1070 row.format(test_addr);
1071
1072 for addr in &row.address {
1074 assert_eq!(addr.addr(), test_addr);
1075 }
1079 let latch_false_count = row.address.iter().filter(|addr| !addr.latch()).count();
1083 assert_eq!(latch_false_count, 1);
1084
1085 for entry in &row.data {
1087 assert_eq!(entry.latch(), false);
1088 }
1089 let oe_active = !cfg!(feature = "invert-oe");
1090 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1091 let blank_count = TEST_COLS - active_count;
1092 let oe_blank_count = row
1093 .data
1094 .iter()
1095 .filter(|entry| entry.output_enable() != oe_active)
1096 .count();
1097 assert_eq!(oe_blank_count, blank_count);
1098 }
1099
1100 #[test]
1101 fn test_row_set_color0() {
1102 let mut row: Row<TEST_COLS> = Row::new();
1103
1104 row.set_color0(0, true, false, true);
1105
1106 let mapped_col_0 = map_index(0);
1107 assert_eq!(row.data[mapped_col_0].red1(), true);
1108 assert_eq!(row.data[mapped_col_0].grn1(), false);
1109 assert_eq!(row.data[mapped_col_0].blu1(), true);
1110
1111 row.set_color0(1, false, true, false);
1113
1114 let mapped_col_1 = map_index(1);
1115 assert_eq!(row.data[mapped_col_1].red1(), false);
1116 assert_eq!(row.data[mapped_col_1].grn1(), true);
1117 assert_eq!(row.data[mapped_col_1].blu1(), false);
1118 }
1119
1120 #[test]
1121 fn test_row_set_color1() {
1122 let mut row: Row<TEST_COLS> = Row::new();
1123
1124 row.set_color1(0, true, true, false);
1125
1126 let mapped_col_0 = map_index(0);
1127 assert_eq!(row.data[mapped_col_0].red2(), true);
1128 assert_eq!(row.data[mapped_col_0].grn2(), true);
1129 assert_eq!(row.data[mapped_col_0].blu2(), false);
1130 }
1131
1132 #[test]
1133 fn test_frame_construction() {
1134 let frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1135 assert_eq!(frame.rows.len(), TEST_NROWS);
1136 }
1137
1138 #[test]
1139 fn test_frame_format() {
1140 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1141
1142 frame.format();
1143
1144 for (addr, row) in frame.rows.iter().enumerate() {
1145 for address in &row.address {
1147 assert_eq!(address.addr() as usize, addr);
1148 }
1149 }
1150 }
1151
1152 #[test]
1153 fn test_frame_set_pixel() {
1154 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1155
1156 frame.set_pixel(5, 10, true, false, true);
1158
1159 let mapped_col_10 = map_index(10);
1160 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1161 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1162 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1163
1164 frame.set_pixel(TEST_NROWS + 5, 15, false, true, false);
1166
1167 let mapped_col_15 = map_index(15);
1168 assert_eq!(frame.rows[5].data[mapped_col_15].red2(), false);
1169 assert_eq!(frame.rows[5].data[mapped_col_15].grn2(), true);
1170 assert_eq!(frame.rows[5].data[mapped_col_15].blu2(), false);
1171 }
1172
1173 #[test]
1174 fn test_row_default() {
1175 let row1: Row<TEST_COLS> = Row::new();
1176 let row2: Row<TEST_COLS> = Row::default();
1177
1178 assert_eq!(row1, row2);
1180 assert_eq!(row1.data.len(), row2.data.len());
1181 assert_eq!(row1.address.len(), row2.address.len());
1182
1183 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1185 assert_eq!(entry1.0, entry2.0);
1186 assert_eq!(entry1.0, 0);
1187 }
1188 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1189 assert_eq!(addr1.0, addr2.0);
1190 assert_eq!(addr1.0, 0);
1191 }
1192 }
1193
1194 #[test]
1195 fn test_frame_default() {
1196 let frame1: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1197 let frame2: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::default();
1198
1199 assert_eq!(frame1.rows.len(), frame2.rows.len());
1201
1202 for (row1, row2) in frame1.rows.iter().zip(frame2.rows.iter()) {
1204 assert_eq!(row1, row2);
1205
1206 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1208 assert_eq!(entry1.0, entry2.0);
1209 assert_eq!(entry1.0, 0);
1210 }
1211 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1212 assert_eq!(addr1.0, addr2.0);
1213 assert_eq!(addr1.0, 0);
1214 }
1215 }
1216 }
1217
1218 #[test]
1219 fn test_dma_framebuffer_construction() {
1220 let fb = TestFrameBuffer::new();
1221 assert_eq!(fb.frames.len(), TEST_FRAME_COUNT);
1222 }
1223
1224 #[test]
1225 fn test_bcm_chunk_info() {
1226 let expected_size =
1227 core::mem::size_of::<[Frame<TEST_ROWS, TEST_COLS, TEST_NROWS>; TEST_FRAME_COUNT]>();
1228 assert_eq!(TestFrameBuffer::bcm_chunk_bytes(), expected_size);
1229 assert_eq!(TestFrameBuffer::bcm_chunk_count(), 1);
1230 }
1231
1232 #[test]
1233 fn test_dma_framebuffer_format() {
1234 let mut fb = TestFrameBuffer {
1235 frames: [Frame::new(); TEST_FRAME_COUNT],
1236 };
1237 fb.format();
1238
1239 for frame in &fb.frames {
1241 for (addr, row) in frame.rows.iter().enumerate() {
1242 for address in &row.address {
1243 assert_eq!(address.addr() as usize, addr);
1244 }
1245 }
1246 }
1247 }
1248
1249 #[test]
1250 fn test_dma_framebuffer_set_pixel_bounds() {
1251 let mut fb = TestFrameBuffer::new();
1252
1253 fb.set_pixel(Point::new(-1, 5), Color::RED);
1255 fb.set_pixel(Point::new(5, -1), Color::RED);
1256
1257 fb.set_pixel(Point::new(TEST_COLS as i32, 5), Color::RED);
1259 fb.set_pixel(Point::new(5, TEST_ROWS as i32), Color::RED);
1260 }
1261
1262 #[test]
1263 fn test_dma_framebuffer_set_pixel_internal() {
1264 let mut fb = TestFrameBuffer::new();
1265
1266 let red_color = Rgb888::new(255, 0, 0);
1267 fb.set_pixel_internal(10, 5, red_color);
1268
1269 for frame in &fb.frames {
1273 let mapped_col_10 = map_index(10);
1275 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1276 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1277 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), false);
1278 }
1279 }
1280
1281 #[test]
1282 fn test_dma_framebuffer_brightness_modulation() {
1283 let mut fb = TestFrameBuffer::new();
1284
1285 let brightness_step = 1 << (8 - TEST_BITS); let test_brightness = brightness_step * 3; let color = Rgb888::new(test_brightness, 0, 0);
1289
1290 fb.set_pixel_internal(0, 0, color);
1291
1292 for (frame_idx, frame) in fb.frames.iter().enumerate() {
1295 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1296 let should_be_active = test_brightness >= frame_threshold;
1297
1298 let mapped_col_0 = map_index(0);
1299 assert_eq!(frame.rows[0].data[mapped_col_0].red1(), should_be_active);
1300 }
1301 }
1302
1303 #[test]
1304 fn test_origin_dimensions() {
1305 let fb = TestFrameBuffer::new();
1306 let size = fb.size();
1307 assert_eq!(size.width, TEST_COLS as u32);
1308 assert_eq!(size.height, TEST_ROWS as u32);
1309
1310 let mut fb = TestFrameBuffer::new();
1312 let fb_ref = &mut fb;
1313 let size = fb_ref.size();
1314 assert_eq!(size.width, TEST_COLS as u32);
1315 assert_eq!(size.height, TEST_ROWS as u32);
1316 }
1317
1318 #[test]
1319 fn test_draw_target() {
1320 let mut fb = TestFrameBuffer::new();
1321
1322 let pixels = vec![
1323 embedded_graphics::Pixel(Point::new(0, 0), Color::RED),
1324 embedded_graphics::Pixel(Point::new(1, 1), Color::GREEN),
1325 embedded_graphics::Pixel(Point::new(2, 2), Color::BLUE),
1326 ];
1327
1328 let result = fb.draw_iter(pixels);
1329 assert!(result.is_ok());
1330 }
1331
1332 #[test]
1333 fn test_draw_iter_pixel_verification() {
1334 let mut fb = TestFrameBuffer::new();
1335
1336 let pixels = vec![
1338 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)), ];
1352
1353 let result = fb.draw_iter(pixels);
1354 assert!(result.is_ok());
1355
1356 let first_frame = &fb.frames[0];
1358 let brightness_step = 1 << (8 - TEST_BITS); let first_frame_threshold = brightness_step; let col_idx = map_index(5);
1364 assert_eq!(
1365 first_frame.rows[2].data[col_idx].red1(),
1366 Color::RED.r() >= first_frame_threshold
1367 );
1368 assert_eq!(
1369 first_frame.rows[2].data[col_idx].grn1(),
1370 Color::RED.g() >= first_frame_threshold
1371 );
1372 assert_eq!(
1373 first_frame.rows[2].data[col_idx].blu1(),
1374 Color::RED.b() >= first_frame_threshold
1375 );
1376
1377 let col_idx = map_index(10);
1379 assert_eq!(
1380 first_frame.rows[5].data[col_idx].red1(),
1381 Color::GREEN.r() >= first_frame_threshold
1382 );
1383 assert_eq!(
1384 first_frame.rows[5].data[col_idx].grn1(),
1385 Color::GREEN.g() >= first_frame_threshold
1386 );
1387 assert_eq!(
1388 first_frame.rows[5].data[col_idx].blu1(),
1389 Color::GREEN.b() >= first_frame_threshold
1390 );
1391
1392 let col_idx = map_index(15);
1394 assert_eq!(
1395 first_frame.rows[8].data[col_idx].red1(),
1396 Color::BLUE.r() >= first_frame_threshold
1397 );
1398 assert_eq!(
1399 first_frame.rows[8].data[col_idx].grn1(),
1400 Color::BLUE.g() >= first_frame_threshold
1401 );
1402 assert_eq!(
1403 first_frame.rows[8].data[col_idx].blu1(),
1404 Color::BLUE.b() >= first_frame_threshold
1405 );
1406
1407 let col_idx = map_index(20);
1409 assert_eq!(
1410 first_frame.rows[10].data[col_idx].red1(),
1411 Color::WHITE.r() >= first_frame_threshold
1412 );
1413 assert_eq!(
1414 first_frame.rows[10].data[col_idx].grn1(),
1415 Color::WHITE.g() >= first_frame_threshold
1416 );
1417 assert_eq!(
1418 first_frame.rows[10].data[col_idx].blu1(),
1419 Color::WHITE.b() >= first_frame_threshold
1420 );
1421
1422 let col_idx = map_index(25);
1425 assert_eq!(
1426 first_frame.rows[3].data[col_idx].red2(),
1427 Color::RED.r() >= first_frame_threshold
1428 );
1429 assert_eq!(
1430 first_frame.rows[3].data[col_idx].grn2(),
1431 Color::RED.g() >= first_frame_threshold
1432 );
1433 assert_eq!(
1434 first_frame.rows[3].data[col_idx].blu2(),
1435 Color::RED.b() >= first_frame_threshold
1436 );
1437
1438 let col_idx = map_index(30);
1440 assert_eq!(
1441 first_frame.rows[7].data[col_idx].red2(),
1442 Color::GREEN.r() >= first_frame_threshold
1443 );
1444 assert_eq!(
1445 first_frame.rows[7].data[col_idx].grn2(),
1446 Color::GREEN.g() >= first_frame_threshold
1447 );
1448 assert_eq!(
1449 first_frame.rows[7].data[col_idx].blu2(),
1450 Color::GREEN.b() >= first_frame_threshold
1451 );
1452
1453 let col_idx = map_index(35);
1455 assert_eq!(
1456 first_frame.rows[12].data[col_idx].red2(),
1457 Color::BLUE.r() >= first_frame_threshold
1458 );
1459 assert_eq!(
1460 first_frame.rows[12].data[col_idx].grn2(),
1461 Color::BLUE.g() >= first_frame_threshold
1462 );
1463 assert_eq!(
1464 first_frame.rows[12].data[col_idx].blu2(),
1465 Color::BLUE.b() >= first_frame_threshold
1466 );
1467
1468 let col_idx = map_index(40);
1470 assert_eq!(first_frame.rows[1].data[col_idx].red1(), false);
1471 assert_eq!(first_frame.rows[1].data[col_idx].grn1(), false);
1472 assert_eq!(first_frame.rows[1].data[col_idx].blu1(), false);
1473
1474 let col_idx = map_index(45);
1476 assert_eq!(
1477 first_frame.rows[3].data[col_idx].red1(),
1478 16 >= first_frame_threshold
1479 ); assert_eq!(
1481 first_frame.rows[3].data[col_idx].grn1(),
1482 16 >= first_frame_threshold
1483 ); assert_eq!(
1485 first_frame.rows[3].data[col_idx].blu1(),
1486 16 >= first_frame_threshold
1487 ); }
1489
1490 #[test]
1491 fn test_embedded_graphics_integration() {
1492 let mut fb = TestFrameBuffer::new();
1493
1494 let result = Rectangle::new(Point::new(5, 5), Size::new(10, 8))
1496 .into_styled(PrimitiveStyle::with_fill(Color::RED))
1497 .draw(&mut fb);
1498 assert!(result.is_ok());
1499
1500 let result = Circle::new(Point::new(30, 15), 8)
1502 .into_styled(PrimitiveStyle::with_fill(Color::BLUE))
1503 .draw(&mut fb);
1504 assert!(result.is_ok());
1505 }
1506
1507 #[test]
1508 fn test_read_buffer_implementation() {
1509 let fb = TestFrameBuffer::new();
1510
1511 unsafe {
1513 let (ptr, len) = fb.read_buffer();
1514 assert!(!ptr.is_null());
1515 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1516 }
1517
1518 let mut fb = TestFrameBuffer::new();
1520 let fb_ref = &mut fb;
1521 unsafe {
1522 let (ptr, len) = fb_ref.read_buffer();
1523 assert!(!ptr.is_null());
1524 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1525 }
1526 }
1527
1528 #[test]
1529 fn test_framebuffer_trait() {
1530 let fb = TestFrameBuffer::new();
1531 assert_eq!(fb.get_word_size(), WordSize::Eight);
1532
1533 let mut fb = TestFrameBuffer::new();
1534 let fb_ref = &mut fb;
1535 assert_eq!(fb_ref.get_word_size(), WordSize::Eight);
1536 }
1537
1538 #[test]
1539 fn test_debug_formatting() {
1540 let fb = TestFrameBuffer::new();
1541 let debug_string = format!("{:?}", fb);
1542 assert!(debug_string.contains("DmaFrameBuffer"));
1543 assert!(debug_string.contains("frame_count"));
1544 assert!(debug_string.contains("frame_size"));
1545 assert!(debug_string.contains("brightness_step"));
1546 }
1547
1548 #[test]
1549 fn test_default_implementation() {
1550 let fb1 = TestFrameBuffer::new();
1551 let fb2 = TestFrameBuffer::default();
1552
1553 assert_eq!(fb1.frames.len(), fb2.frames.len());
1555 }
1556
1557 #[cfg(feature = "esp32-ordering")]
1558 #[test]
1559 fn test_esp32_mapping() {
1560 assert_eq!(map_index(0), 2);
1562 assert_eq!(map_index(1), 3);
1563 assert_eq!(map_index(2), 0);
1564 assert_eq!(map_index(3), 1);
1565 assert_eq!(map_index(4), 6); assert_eq!(map_index(5), 7); }
1568
1569 #[test]
1570 fn test_memory_alignment() {
1571 let fb = TestFrameBuffer::new();
1572 let ptr = &fb as *const _ as usize;
1573
1574 assert_eq!(ptr % 4, 0);
1576 }
1577
1578 #[test]
1579 fn test_color_values() {
1580 let mut fb = TestFrameBuffer::new();
1581
1582 let colors = [
1584 (Color::RED, (255, 0, 0)),
1585 (Color::GREEN, (0, 255, 0)),
1586 (Color::BLUE, (0, 0, 255)),
1587 (Color::WHITE, (255, 255, 255)),
1588 (Color::BLACK, (0, 0, 0)),
1589 ];
1590
1591 for (i, (color, (r, g, b))) in colors.iter().enumerate() {
1592 fb.set_pixel(Point::new(i as i32, 0), *color);
1593 assert_eq!(color.r(), *r);
1594 assert_eq!(color.g(), *g);
1595 assert_eq!(color.b(), *b);
1596 }
1597 }
1598
1599 #[test]
1600 fn test_bits_assertion() {
1601 assert!(TEST_BITS <= 8);
1604 }
1605
1606 #[test]
1607 #[cfg(feature = "skip-black-pixels")]
1608 fn test_skip_black_pixels_enabled() {
1609 let mut fb = TestFrameBuffer::new();
1610
1611 fb.set_pixel_internal(10, 5, Color::RED);
1613
1614 let mapped_col_10 = map_index(10);
1616 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1617 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1618 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1619
1620 fb.set_pixel_internal(10, 5, Color::BLACK);
1622
1623 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1625 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1626 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1627 }
1628
1629 #[test]
1630 #[cfg(not(feature = "skip-black-pixels"))]
1631 fn test_skip_black_pixels_disabled() {
1632 let mut fb = TestFrameBuffer::new();
1633
1634 fb.set_pixel_internal(10, 5, Color::RED);
1636
1637 let mapped_col_10 = map_index(10);
1639 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1640 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1641 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1642
1643 fb.set_pixel_internal(10, 5, Color::BLACK);
1645
1646 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1648 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1649 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1650 }
1651
1652 #[test]
1653 fn test_bcm_frame_overwrite() {
1654 let mut fb = TestFrameBuffer::new();
1655
1656 fb.set_pixel_internal(10, 5, Color::WHITE);
1658
1659 let mapped_col_10 = map_index(10);
1660
1661 for frame in fb.frames.iter() {
1663 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1665 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), true);
1666 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1667 }
1668
1669 let half_white = embedded_graphics::pixelcolor::Rgb888::new(128, 128, 128);
1671 fb.set_pixel_internal(10, 5, half_white);
1672
1673 let brightness_step = 1 << (8 - TEST_BITS); for (frame_idx, frame) in fb.frames.iter().enumerate() {
1679 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1680 let should_be_active = 128 >= frame_threshold;
1681
1682 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), should_be_active);
1683 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), should_be_active);
1684 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), should_be_active);
1685 }
1686
1687 for frame_idx in 0..4 {
1690 assert_eq!(
1691 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1692 true
1693 );
1694 }
1695 for frame_idx in 4..TEST_FRAME_COUNT {
1697 assert_eq!(
1698 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1699 false
1700 );
1701 }
1702 }
1703
1704 #[test]
1705 fn test_new_auto_formats() {
1706 let fb = TestFrameBuffer::new();
1707
1708 for frame in &fb.frames {
1710 for (addr, row) in frame.rows.iter().enumerate() {
1711 for address in &row.address {
1712 assert_eq!(address.addr() as usize, addr);
1713 }
1714 }
1715 }
1716 }
1717
1718 #[test]
1719 fn test_erase() {
1720 let mut fb = TestFrameBuffer::new();
1721
1722 fb.set_pixel_internal(10, 5, Color::RED);
1724 fb.set_pixel_internal(20, 10, Color::GREEN);
1725
1726 let mapped_col_10 = map_index(10);
1727 let mapped_col_20 = map_index(20);
1728
1729 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1731 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), true);
1732
1733 fb.erase();
1735
1736 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1738 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1739 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1740 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].red1(), false);
1741 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), false);
1742 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].blu1(), false);
1743
1744 let oe_active = !cfg!(feature = "invert-oe");
1746 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1747 let blank_count = TEST_COLS - active_count;
1748 for frame in &fb.frames {
1749 for (addr, row) in frame.rows.iter().enumerate() {
1750 for address in &row.address {
1752 assert_eq!(address.addr() as usize, addr);
1753 }
1754 let oe_blank_count = row
1755 .data
1756 .iter()
1757 .filter(|entry| entry.output_enable() != oe_active)
1758 .count();
1759 assert_eq!(oe_blank_count, blank_count);
1760 }
1761 }
1762 }
1763
1764 #[test]
1765 fn test_row_clear_colors() {
1766 let mut row: Row<TEST_COLS> = Row::new();
1767 row.format(5);
1768
1769 row.set_color0(0, true, false, true);
1771 row.set_color1(1, false, true, false);
1772
1773 let mapped_col_0 = map_index(0);
1774 let mapped_col_1 = map_index(1);
1775
1776 assert_eq!(row.data[mapped_col_0].red1(), true);
1778 assert_eq!(row.data[mapped_col_0].blu1(), true);
1779 assert_eq!(row.data[mapped_col_1].grn2(), true);
1780
1781 let original_oe_0 = row.data[mapped_col_0].output_enable();
1783 let original_latch_0 = row.data[mapped_col_0].latch();
1784 let original_oe_1 = row.data[mapped_col_1].output_enable();
1785 let original_latch_1 = row.data[mapped_col_1].latch();
1786
1787 row.clear_colors();
1789
1790 assert_eq!(row.data[mapped_col_0].red1(), false);
1792 assert_eq!(row.data[mapped_col_0].grn1(), false);
1793 assert_eq!(row.data[mapped_col_0].blu1(), false);
1794 assert_eq!(row.data[mapped_col_1].red2(), false);
1795 assert_eq!(row.data[mapped_col_1].grn2(), false);
1796 assert_eq!(row.data[mapped_col_1].blu2(), false);
1797
1798 assert_eq!(row.data[mapped_col_0].output_enable(), original_oe_0);
1800 assert_eq!(row.data[mapped_col_0].latch(), original_latch_0);
1801 assert_eq!(row.data[mapped_col_1].output_enable(), original_oe_1);
1802 assert_eq!(row.data[mapped_col_1].latch(), original_latch_1);
1803 }
1804
1805 #[test]
1806 fn test_make_addr_table_function() {
1807 let table = make_addr_table();
1809
1810 assert_eq!(table.len(), 32); let addr_0 = &table[0];
1815 assert_eq!(addr_0.len(), 4); let latch_false_count = addr_0.iter().filter(|addr| !addr.latch()).count();
1819 assert_eq!(latch_false_count, 1);
1820
1821 for addr in addr_0 {
1823 assert_eq!(addr.addr(), 0);
1824 }
1825
1826 let addr_31 = &table[31];
1828 let latch_false_count = addr_31.iter().filter(|addr| !addr.latch()).count();
1829 assert_eq!(latch_false_count, 1);
1830
1831 for addr in addr_31 {
1833 assert_eq!(addr.addr(), 31);
1834 }
1835 }
1836
1837 #[test]
1838 fn test_make_data_template_function() {
1839 let template = make_data_template::<TEST_COLS>();
1841
1842 assert_eq!(template.len(), TEST_COLS);
1844
1845 for entry in &template {
1847 assert_eq!(entry.latch(), false);
1848 }
1849
1850 let oe_active = !cfg!(feature = "invert-oe");
1851 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1852 let blank_count = TEST_COLS - active_count;
1853 let oe_blank_count = template
1854 .iter()
1855 .filter(|entry| entry.output_enable() != oe_active)
1856 .count();
1857 assert_eq!(oe_blank_count, blank_count);
1858
1859 let small_template = make_data_template::<4>();
1861 assert_eq!(small_template.len(), 4);
1862
1863 let small_active = 4_usize.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1864 let small_blank = 4 - small_active;
1865 let oe_blank_count = small_template
1866 .iter()
1867 .filter(|entry| entry.output_enable() != oe_active)
1868 .count();
1869 assert_eq!(oe_blank_count, small_blank);
1870
1871 #[cfg(not(feature = "esp32-ordering"))]
1874 {
1875 let single_template = make_data_template::<1>();
1876 assert_eq!(single_template.len(), 1);
1877 assert_eq!(single_template[0].output_enable(), !oe_active);
1878 assert_eq!(single_template[0].latch(), false);
1879 }
1880 }
1881
1882 #[test]
1883 fn test_addr_table_correctness() {
1884 for addr in 0..32 {
1886 let mut expected_addresses = [Address::new(); 4];
1887
1888 for i in 0..4 {
1890 let latch = !matches!(i, 3);
1891 #[cfg(feature = "esp32-ordering")]
1892 let mapped_i = map_index(i);
1893 #[cfg(not(feature = "esp32-ordering"))]
1894 let mapped_i = i;
1895
1896 expected_addresses[mapped_i].set_latch(latch);
1897 expected_addresses[mapped_i].set_addr(addr);
1898 expected_addresses[mapped_i].0 |= OE_BLANK;
1899 }
1900
1901 let table_addresses = &ADDR_TABLE[addr as usize];
1903 for i in 0..4 {
1904 assert_eq!(table_addresses[i].0, expected_addresses[i].0);
1905 }
1906 }
1907 }
1908
1909 const CHAR_W: i32 = 6;
1911 const CHAR_H: i32 = 10;
1912
1913 fn verify_glyph_at(fb: &mut TestFrameBuffer, origin: Point) {
1916 use embedded_graphics::mock_display::MockDisplay;
1917 use embedded_graphics::mono_font::ascii::FONT_6X10;
1918 use embedded_graphics::mono_font::MonoTextStyle;
1919 use embedded_graphics::text::{Baseline, Text};
1920
1921 let style = MonoTextStyle::new(&FONT_6X10, Color::WHITE);
1923 Text::with_baseline("A", origin, style, Baseline::Top)
1924 .draw(fb)
1925 .unwrap();
1926
1927 let mut reference: MockDisplay<Color> = MockDisplay::new();
1929 Text::with_baseline("A", Point::zero(), style, Baseline::Top)
1930 .draw(&mut reference)
1931 .unwrap();
1932
1933 for dy in 0..CHAR_H {
1935 for dx in 0..CHAR_W {
1936 let expected_on = reference
1937 .get_pixel(Point::new(dx, dy))
1938 .unwrap_or(Color::BLACK)
1939 != Color::BLACK;
1940
1941 let gx = (origin.x + dx) as usize;
1942 let gy = (origin.y + dy) as usize;
1943
1944 let frame0 = &fb.frames[0];
1951 let e = if gy < TEST_NROWS {
1952 &frame0.rows[gy].data[map_index(gx)]
1953 } else {
1954 &frame0.rows[gy - TEST_NROWS].data[map_index(gx)]
1955 };
1956
1957 let (r, g, b) = if gy >= TEST_NROWS {
1958 (e.red2(), e.grn2(), e.blu2())
1959 } else {
1960 (e.red1(), e.grn1(), e.blu1())
1961 };
1962
1963 if expected_on {
1964 assert!(r && g && b);
1965 } else {
1966 assert!(!r && !g && !b);
1967 }
1968 }
1969 }
1970 }
1971
1972 #[test]
1973 fn test_draw_char_corners() {
1974 let upper_left = Point::new(0, 0);
1976 let lower_right = Point::new(TEST_COLS as i32 - CHAR_W, TEST_ROWS as i32 - CHAR_H);
1977
1978 let mut fb = TestFrameBuffer::new();
1979
1980 verify_glyph_at(&mut fb, upper_left);
1982 verify_glyph_at(&mut fb, lower_right);
1984 }
1985
1986 #[test]
1987 fn test_framebuffer_operations_trait_erase() {
1988 let mut fb = TestFrameBuffer::new();
1989
1990 fb.set_pixel_internal(10, 5, Color::RED);
1992 fb.set_pixel_internal(20, 10, Color::GREEN);
1993
1994 <TestFrameBuffer as FrameBufferOperations>::erase(&mut fb);
1996
1997 let mc10 = map_index(10);
1999 let mc20 = map_index(20);
2000 assert_eq!(fb.frames[0].rows[5].data[mc10].red1(), false);
2001 assert_eq!(fb.frames[0].rows[10].data[mc20].grn1(), false);
2002
2003 let oe_active = !cfg!(feature = "invert-oe");
2005 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
2006 let blank_count = TEST_COLS - active_count;
2007 let row0 = &fb.frames[0].rows[0];
2008 let oe_blank_count = row0
2009 .data
2010 .iter()
2011 .filter(|entry| entry.output_enable() != oe_active)
2012 .count();
2013 assert_eq!(oe_blank_count, blank_count);
2014 assert!(row0.data.iter().all(|e| !e.latch()));
2015
2016 for (i, addr) in row0.address.iter().enumerate() {
2018 assert_eq!(addr.0, ADDR_TABLE[0][i].0);
2019 }
2020 }
2021
2022 #[test]
2023 fn test_framebuffer_operations_trait_set_pixel() {
2024 let mut fb = TestFrameBuffer::new();
2025
2026 <TestFrameBuffer as FrameBufferOperations>::set_pixel(
2028 &mut fb,
2029 Point::new(8, 3),
2030 Color::BLUE,
2031 );
2032
2033 let idx = map_index(8);
2035 assert_eq!(fb.frames[0].rows[3].data[idx].blu1(), true);
2036 assert_eq!(fb.frames[0].rows[3].data[idx].red1(), false);
2038 assert_eq!(fb.frames[0].rows[3].data[idx].grn1(), false);
2039 }
2040
2041 #[test]
2042 fn test_blanking_delay() {
2043 let mut row: Row<TEST_COLS> = Row::new();
2044 row.format(5);
2045
2046 let oe_active = !cfg!(feature = "invert-oe");
2047
2048 if TRAIL_BLANK_DELAY > 0 {
2050 let trail_blank_idx = map_index(TRAIL_BLANK_DELAY - 1);
2051 assert_eq!(row.data[trail_blank_idx].output_enable(), !oe_active);
2052 }
2053
2054 let first_active_idx = map_index(TRAIL_BLANK_DELAY);
2055 assert_eq!(row.data[first_active_idx].output_enable(), oe_active);
2056
2057 let last_active_idx = map_index(TEST_COLS - LEAD_BLANK_DELAY - 2);
2059 assert_eq!(row.data[last_active_idx].output_enable(), oe_active);
2060
2061 let lead_blank_idx = map_index(TEST_COLS - LEAD_BLANK_DELAY - 1);
2062 assert_eq!(row.data[lead_blank_idx].output_enable(), !oe_active);
2063
2064 let last_pixel_idx = map_index(TEST_COLS - 1);
2065 assert_eq!(row.data[last_pixel_idx].output_enable(), !oe_active);
2066 }
2067
2068 static STATIC_FB: TestFrameBuffer = TestFrameBuffer::new();
2069
2070 #[test]
2071 fn test_static_construction_is_formatted() {
2072 let runtime_fb = TestFrameBuffer::new();
2073
2074 for (fi, frame) in STATIC_FB.frames.iter().enumerate() {
2075 for (ri, row) in frame.rows.iter().enumerate() {
2076 assert_eq!(
2077 row.data, runtime_fb.frames[fi].rows[ri].data,
2078 "static vs runtime data mismatch at frame {fi}, row {ri}"
2079 );
2080 assert_eq!(
2081 row.address, runtime_fb.frames[fi].rows[ri].address,
2082 "static vs runtime address mismatch at frame {fi}, row {ri}"
2083 );
2084 assert_eq!(
2085 row.gap, runtime_fb.frames[fi].rows[ri].gap,
2086 "static vs runtime gap mismatch at frame {fi}, row {ri}"
2087 );
2088 }
2089 }
2090 }
2091
2092 #[test]
2093 fn test_format_reinitializes_at_runtime() {
2094 let mut fb = TestFrameBuffer::new();
2095 fb.erase();
2096 fb.format();
2097
2098 for (fi, frame) in fb.frames.iter().enumerate() {
2099 for (ri, row) in frame.rows.iter().enumerate() {
2100 assert_eq!(
2101 row.data, STATIC_FB.frames[fi].rows[ri].data,
2102 "re-formatted vs static mismatch at frame {fi}, row {ri}"
2103 );
2104 }
2105 }
2106 }
2107}