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(not(feature = "invert-oe"))]
223const OE_ACTIVE: u8 = 0b1000_0000;
224#[cfg(not(feature = "invert-oe"))]
225const OE_BLANK: u8 = 0;
226
227#[cfg(feature = "invert-oe")]
228const OE_ACTIVE: u8 = 0;
229#[cfg(feature = "invert-oe")]
230const OE_BLANK: u8 = 0b1000_0000;
231
232bitfield! {
233 #[derive(Clone, Copy, Default, PartialEq, Eq)]
268 #[repr(transparent)]
269 struct Address(u8);
270 impl Debug;
271 pub output_enable, set_output_enable: 7;
272 pub latch, set_latch: 6;
273 pub addr, set_addr: 4, 0;
274}
275
276impl Address {
277 pub const fn new() -> Self {
278 Self(0)
279 }
280}
281
282bitfield! {
283 #[derive(Clone, Copy, Default, PartialEq)]
300 #[repr(transparent)]
301 struct Entry(u8);
302 impl Debug;
303 pub output_enable, set_output_enable: 7;
304 pub latch, set_latch: 6;
305 pub blu2, set_blu2: 5;
306 pub grn2, set_grn2: 4;
307 pub red2, set_red2: 3;
308 pub blu1, set_blu1: 2;
309 pub grn1, set_grn1: 1;
310 pub red1, set_red1: 0;
311}
312
313impl Entry {
314 pub const fn new() -> Self {
315 Self(0)
316 }
317
318 const COLOR0_MASK: u8 = 0b0000_0111; const COLOR1_MASK: u8 = 0b0011_1000; #[inline]
323 fn set_color0_bits(&mut self, bits: u8) {
324 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits & Self::COLOR0_MASK);
325 }
326
327 #[inline]
328 fn set_color1_bits(&mut self, bits: u8) {
329 self.0 = (self.0 & !Self::COLOR1_MASK) | ((bits << 3) & Self::COLOR1_MASK);
330 }
331}
332
333#[derive(Clone, Copy, PartialEq, Debug)]
344#[repr(C)]
345struct Row<const COLS: usize> {
346 data: [Entry; COLS],
347 address: [Address; 4],
348}
349
350#[inline]
352const fn map_index(index: usize) -> usize {
353 #[cfg(feature = "esp32-ordering")]
354 {
355 index ^ 2
356 }
357 #[cfg(not(feature = "esp32-ordering"))]
358 {
359 index
360 }
361}
362
363const fn make_addr_table() -> [[Address; 4]; 32] {
366 let mut tbl = [[Address::new(); 4]; 32];
367 let mut addr = 0;
368 while addr < 32 {
369 let mut i = 0;
370 while i < 4 {
371 let latch = i != 3;
372 let mapped_i = map_index(i);
373 let latch_bit = if latch { 1u8 << 6 } else { 0u8 };
374 tbl[addr][mapped_i].0 = OE_BLANK | latch_bit | addr as u8;
375 i += 1;
376 }
377 addr += 1;
378 }
379 tbl
380}
381
382static ADDR_TABLE: [[Address; 4]; 32] = make_addr_table();
383
384#[allow(clippy::absurd_extreme_comparisons)]
387const fn make_data_template<const COLS: usize>() -> [Entry; COLS] {
388 let mut data = [Entry::new(); COLS];
389 let mut i = 0;
390 while i < COLS {
391 let mapped_i = map_index(i);
392 data[mapped_i].0 =
393 if i >= TRAIL_BLANK_DELAY && i < COLS.saturating_sub(LEAD_BLANK_DELAY + 1) {
394 OE_ACTIVE
395 } else {
396 OE_BLANK
397 };
398 i += 1;
399 }
400 data
401}
402
403impl<const COLS: usize> Row<COLS> {
404 pub const fn new() -> Self {
405 Self {
406 address: [Address::new(); 4],
407 data: [Entry::new(); COLS],
408 }
409 }
410
411 #[inline]
412 pub fn format(&mut self, addr: u8) {
413 self.address.copy_from_slice(&ADDR_TABLE[addr as usize]);
415
416 let data_template = make_data_template::<COLS>();
418 self.data.copy_from_slice(&data_template);
419 }
420
421 #[inline]
423 pub fn clear_colors(&mut self) {
424 const COLOR_CLEAR_MASK: u8 = !0b0011_1111; for entry in &mut self.data {
428 entry.0 &= COLOR_CLEAR_MASK;
429 }
430 }
431
432 #[inline]
433 pub fn set_color0(&mut self, col: usize, r: bool, g: bool, b: bool) {
434 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
435 let col = map_index(col);
436 self.data[col].set_color0_bits(bits);
437 }
438
439 #[inline]
440 pub fn set_color1(&mut self, col: usize, r: bool, g: bool, b: bool) {
441 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
442 let col = map_index(col);
443 self.data[col].set_color1_bits(bits);
444 }
445}
446
447impl<const COLS: usize> Default for Row<COLS> {
448 fn default() -> Self {
449 Self::new()
450 }
451}
452
453#[derive(Copy, Clone, Debug)]
454#[repr(C)]
455struct Frame<const ROWS: usize, const COLS: usize, const NROWS: usize> {
456 rows: [Row<COLS>; NROWS],
457}
458
459impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Frame<ROWS, COLS, NROWS> {
460 pub const fn new() -> Self {
461 Self {
462 rows: [Row::new(); NROWS],
463 }
464 }
465
466 #[inline]
467 pub fn format(&mut self) {
468 for (addr, row) in self.rows.iter_mut().enumerate() {
469 row.format(addr as u8);
470 }
471 }
472
473 #[inline]
475 pub fn clear_colors(&mut self) {
476 for row in &mut self.rows {
477 row.clear_colors();
478 }
479 }
480
481 #[inline]
482 pub fn set_pixel(&mut self, y: usize, x: usize, red: bool, green: bool, blue: bool) {
483 let row = &mut self.rows[if y < NROWS { y } else { y - NROWS }];
484 if y < NROWS {
485 row.set_color0(x, red, green, blue);
486 } else {
487 row.set_color1(x, red, green, blue);
488 }
489 }
490}
491
492impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Default
493 for Frame<ROWS, COLS, NROWS>
494{
495 fn default() -> Self {
496 Self::new()
497 }
498}
499
500#[derive(Copy, Clone)]
529#[repr(C)]
530#[repr(align(4))]
531pub struct DmaFrameBuffer<
532 const ROWS: usize,
533 const COLS: usize,
534 const NROWS: usize,
535 const BITS: u8,
536 const FRAME_COUNT: usize,
537> {
538 frames: [Frame<ROWS, COLS, NROWS>; FRAME_COUNT],
539}
540
541impl<
542 const ROWS: usize,
543 const COLS: usize,
544 const NROWS: usize,
545 const BITS: u8,
546 const FRAME_COUNT: usize,
547 > Default for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
548{
549 fn default() -> Self {
550 Self::new()
551 }
552}
553
554impl<
555 const ROWS: usize,
556 const COLS: usize,
557 const NROWS: usize,
558 const BITS: u8,
559 const FRAME_COUNT: usize,
560 > DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
561{
562 #[must_use]
578 pub fn new() -> Self {
579 let mut fb = Self {
580 frames: [Frame::new(); FRAME_COUNT],
581 };
582 fb.format();
583 fb
584 }
585
586 #[must_use]
589 pub const fn bcm_chunk_count() -> usize {
590 1
591 }
592
593 #[must_use]
596 pub const fn bcm_chunk_bytes() -> usize {
597 core::mem::size_of::<[Frame<ROWS, COLS, NROWS>; FRAME_COUNT]>()
598 }
599
600 pub fn format(&mut self) {
617 for frame in &mut self.frames {
618 frame.format();
619 }
620 }
621
622 #[inline]
639 pub fn erase(&mut self) {
640 for frame in &mut self.frames {
641 frame.clear_colors();
642 }
643 }
644
645 pub fn set_pixel(&mut self, p: Point, color: Color) {
661 if p.x < 0 || p.y < 0 {
662 return;
663 }
664 self.set_pixel_internal(p.x as usize, p.y as usize, color);
665 }
666
667 #[inline]
668 fn frames_on(v: u8) -> usize {
669 (v as usize) >> (8 - BITS)
671 }
672
673 #[inline]
674 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Rgb888) {
675 if x >= COLS || y >= ROWS {
676 return;
677 }
678
679 #[cfg(feature = "skip-black-pixels")]
682 if color == Rgb888::BLACK {
683 return;
684 }
685
686 let red_frames = Self::frames_on(color.r());
688 let green_frames = Self::frames_on(color.g());
689 let blue_frames = Self::frames_on(color.b());
690
691 for (frame_idx, frame) in self.frames.iter_mut().enumerate() {
693 frame.set_pixel(
694 y,
695 x,
696 frame_idx < red_frames,
697 frame_idx < green_frames,
698 frame_idx < blue_frames,
699 );
700 }
701 }
702}
703
704impl<
705 const ROWS: usize,
706 const COLS: usize,
707 const NROWS: usize,
708 const BITS: u8,
709 const FRAME_COUNT: usize,
710 > FrameBufferOperations for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
711{
712 #[inline]
713 fn erase(&mut self) {
714 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::erase(self);
715 }
716
717 #[inline]
718 fn set_pixel(&mut self, p: Point, color: Color) {
719 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::set_pixel(self, p, color);
720 }
721}
722
723impl<
724 const ROWS: usize,
725 const COLS: usize,
726 const NROWS: usize,
727 const BITS: u8,
728 const FRAME_COUNT: usize,
729 > embedded_graphics::prelude::OriginDimensions
730 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
731{
732 fn size(&self) -> embedded_graphics::prelude::Size {
733 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
734 }
735}
736
737impl<
738 const ROWS: usize,
739 const COLS: usize,
740 const NROWS: usize,
741 const BITS: u8,
742 const FRAME_COUNT: usize,
743 > embedded_graphics::draw_target::DrawTarget
744 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
745{
746 type Color = Color;
747
748 type Error = Infallible;
749
750 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
751 where
752 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
753 {
754 for pixel in pixels {
755 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
756 }
757 Ok(())
758 }
759}
760
761unsafe impl<
762 const ROWS: usize,
763 const COLS: usize,
764 const NROWS: usize,
765 const BITS: u8,
766 const FRAME_COUNT: usize,
767 > ReadBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
768{
769 type Word = u8;
770
771 unsafe fn read_buffer(&self) -> (*const u8, usize) {
772 let ptr = (&raw const self.frames).cast::<u8>();
773 let len = core::mem::size_of_val(&self.frames);
774 (ptr, len)
775 }
776}
777
778unsafe impl<
779 const ROWS: usize,
780 const COLS: usize,
781 const NROWS: usize,
782 const BITS: u8,
783 const FRAME_COUNT: usize,
784 > ReadBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
785{
786 type Word = u8;
787
788 unsafe fn read_buffer(&self) -> (*const u8, usize) {
789 let ptr = (&raw const self.frames).cast::<u8>();
790 let len = core::mem::size_of_val(&self.frames);
791 (ptr, len)
792 }
793}
794
795impl<
796 const ROWS: usize,
797 const COLS: usize,
798 const NROWS: usize,
799 const BITS: u8,
800 const FRAME_COUNT: usize,
801 > core::fmt::Debug for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
802{
803 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
804 let brightness_step = 1 << (8 - BITS);
805 f.debug_struct("DmaFrameBuffer")
806 .field("size", &core::mem::size_of_val(&self.frames))
807 .field("frame_count", &self.frames.len())
808 .field("frame_size", &core::mem::size_of_val(&self.frames[0]))
809 .field("brightness_step", &&brightness_step)
810 .finish()
811 }
812}
813
814#[cfg(feature = "defmt")]
815impl<
816 const ROWS: usize,
817 const COLS: usize,
818 const NROWS: usize,
819 const BITS: u8,
820 const FRAME_COUNT: usize,
821 > defmt::Format for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
822{
823 fn format(&self, f: defmt::Formatter) {
824 let brightness_step = 1 << (8 - BITS);
825 defmt::write!(
826 f,
827 "DmaFrameBuffer<{}, {}, {}, {}, {}>",
828 ROWS,
829 COLS,
830 NROWS,
831 BITS,
832 FRAME_COUNT
833 );
834 defmt::write!(f, " size: {}", core::mem::size_of_val(&self.frames));
835 defmt::write!(
836 f,
837 " frame_size: {}",
838 core::mem::size_of_val(&self.frames[0])
839 );
840 defmt::write!(f, " brightness_step: {}", brightness_step);
841 }
842}
843
844impl<
845 const ROWS: usize,
846 const COLS: usize,
847 const NROWS: usize,
848 const BITS: u8,
849 const FRAME_COUNT: usize,
850 > super::FrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
851{
852 type Word = u8;
853
854 fn plane_count(&self) -> usize {
855 1
856 }
857
858 fn plane_ptr_len(&self, plane_idx: usize) -> (*const u8, usize) {
859 assert!(plane_idx == 0, "latched DmaFrameBuffer has only 1 plane");
860 let ptr = (&raw const self.frames).cast::<u8>();
861 let len = core::mem::size_of_val(&self.frames);
862 (ptr, len)
863 }
864}
865
866impl<
867 const ROWS: usize,
868 const COLS: usize,
869 const NROWS: usize,
870 const BITS: u8,
871 const FRAME_COUNT: usize,
872 > embedded_graphics::prelude::OriginDimensions
873 for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
874{
875 fn size(&self) -> embedded_graphics::prelude::Size {
876 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
877 }
878}
879
880impl<
881 const ROWS: usize,
882 const COLS: usize,
883 const NROWS: usize,
884 const BITS: u8,
885 const FRAME_COUNT: usize,
886 > MutableFrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
887{
888}
889
890#[cfg(test)]
891mod tests {
892 extern crate std;
893
894 use std::format;
895 use std::vec;
896
897 use super::*;
898 use crate::{FrameBuffer, WordSize};
899 use embedded_graphics::pixelcolor::RgbColor;
900 use embedded_graphics::prelude::*;
901 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle};
902
903 const TEST_ROWS: usize = 32;
904 const TEST_COLS: usize = 64;
905 const TEST_NROWS: usize = TEST_ROWS / 2;
906 const TEST_BITS: u8 = 3;
907 const TEST_FRAME_COUNT: usize = (1 << TEST_BITS) - 1; type TestFrameBuffer =
910 DmaFrameBuffer<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_BITS, TEST_FRAME_COUNT>;
911
912 #[test]
913 fn test_address_construction() {
914 let addr = Address::new();
915 assert_eq!(addr.0, 0);
916 assert_eq!(addr.latch(), false);
917 assert_eq!(addr.addr(), 0);
918 }
919
920 #[test]
921 fn test_address_setters() {
922 let mut addr = Address::new();
923
924 addr.set_latch(true);
925 assert_eq!(addr.latch(), true);
926 assert_eq!(addr.0 & 0b01000000, 0b01000000);
927
928 addr.set_addr(0b11111);
929 assert_eq!(addr.addr(), 0b11111);
930 assert_eq!(addr.0 & 0b00011111, 0b00011111);
931 }
932
933 #[test]
934 fn test_address_bit_isolation() {
935 let mut addr = Address::new();
936
937 addr.set_addr(0b11111);
939 addr.set_latch(true);
940 assert_eq!(addr.addr(), 0b11111);
941 assert_eq!(addr.latch(), true);
942 }
943
944 #[test]
945 fn test_entry_construction() {
946 let entry = Entry::new();
947 assert_eq!(entry.0, 0);
948 assert_eq!(entry.output_enable(), false);
949 assert_eq!(entry.latch(), false);
950 assert_eq!(entry.red1(), false);
951 assert_eq!(entry.grn1(), false);
952 assert_eq!(entry.blu1(), false);
953 assert_eq!(entry.red2(), false);
954 assert_eq!(entry.grn2(), false);
955 assert_eq!(entry.blu2(), false);
956 }
957
958 #[test]
959 fn test_entry_setters() {
960 let mut entry = Entry::new();
961
962 entry.set_output_enable(true);
963 assert_eq!(entry.output_enable(), true);
964 assert_eq!(entry.0 & 0b10000000, 0b10000000);
965
966 entry.set_latch(true);
967 assert_eq!(entry.latch(), true);
968 assert_eq!(entry.0 & 0b01000000, 0b01000000);
969
970 entry.set_red1(true);
972 entry.set_grn1(true);
973 entry.set_blu1(true);
974 assert_eq!(entry.red1(), true);
975 assert_eq!(entry.grn1(), true);
976 assert_eq!(entry.blu1(), true);
977 assert_eq!(entry.0 & 0b00000111, 0b00000111);
978
979 entry.set_red2(true);
981 entry.set_grn2(true);
982 entry.set_blu2(true);
983 assert_eq!(entry.red2(), true);
984 assert_eq!(entry.grn2(), true);
985 assert_eq!(entry.blu2(), true);
986 assert_eq!(entry.0 & 0b00111000, 0b00111000);
987 }
988
989 #[test]
990 fn test_entry_set_color0() {
991 let mut entry = Entry::new();
992
993 let bits = (u8::from(true) << 2) | (u8::from(false) << 1) | u8::from(true); entry.set_color0_bits(bits);
995 assert_eq!(entry.red1(), true);
996 assert_eq!(entry.grn1(), false);
997 assert_eq!(entry.blu1(), true);
998 assert_eq!(entry.0 & 0b00000111, 0b00000101); }
1000
1001 #[test]
1002 fn test_entry_set_color1() {
1003 let mut entry = Entry::new();
1004
1005 let bits = (u8::from(true) << 2) | (u8::from(true) << 1) | u8::from(false); entry.set_color1_bits(bits);
1007 assert_eq!(entry.red2(), false);
1008 assert_eq!(entry.grn2(), true);
1009 assert_eq!(entry.blu2(), true);
1010 assert_eq!(entry.0 & 0b00111000, 0b00110000); }
1012
1013 #[test]
1014 fn test_row_construction() {
1015 let row: Row<TEST_COLS> = Row::new();
1016 assert_eq!(row.data.len(), TEST_COLS);
1017 assert_eq!(row.address.len(), 4);
1018
1019 for entry in &row.data {
1021 assert_eq!(entry.0, 0);
1022 }
1023 for addr in &row.address {
1024 assert_eq!(addr.0, 0);
1025 }
1026 }
1027
1028 #[test]
1029 fn test_row_format() {
1030 let mut row: Row<TEST_COLS> = Row::new();
1031 let test_addr = 5;
1032
1033 row.format(test_addr);
1034
1035 for addr in &row.address {
1037 assert_eq!(addr.addr(), test_addr);
1038 }
1042 let latch_false_count = row.address.iter().filter(|addr| !addr.latch()).count();
1046 assert_eq!(latch_false_count, 1);
1047
1048 for entry in &row.data {
1050 assert_eq!(entry.latch(), false);
1051 }
1052 let oe_active = !cfg!(feature = "invert-oe");
1053 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1054 let blank_count = TEST_COLS - active_count;
1055 let oe_blank_count = row
1056 .data
1057 .iter()
1058 .filter(|entry| entry.output_enable() != oe_active)
1059 .count();
1060 assert_eq!(oe_blank_count, blank_count);
1061 }
1062
1063 #[test]
1064 fn test_row_set_color0() {
1065 let mut row: Row<TEST_COLS> = Row::new();
1066
1067 row.set_color0(0, true, false, true);
1068
1069 let mapped_col_0 = map_index(0);
1070 assert_eq!(row.data[mapped_col_0].red1(), true);
1071 assert_eq!(row.data[mapped_col_0].grn1(), false);
1072 assert_eq!(row.data[mapped_col_0].blu1(), true);
1073
1074 row.set_color0(1, false, true, false);
1076
1077 let mapped_col_1 = map_index(1);
1078 assert_eq!(row.data[mapped_col_1].red1(), false);
1079 assert_eq!(row.data[mapped_col_1].grn1(), true);
1080 assert_eq!(row.data[mapped_col_1].blu1(), false);
1081 }
1082
1083 #[test]
1084 fn test_row_set_color1() {
1085 let mut row: Row<TEST_COLS> = Row::new();
1086
1087 row.set_color1(0, true, true, false);
1088
1089 let mapped_col_0 = map_index(0);
1090 assert_eq!(row.data[mapped_col_0].red2(), true);
1091 assert_eq!(row.data[mapped_col_0].grn2(), true);
1092 assert_eq!(row.data[mapped_col_0].blu2(), false);
1093 }
1094
1095 #[test]
1096 fn test_frame_construction() {
1097 let frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1098 assert_eq!(frame.rows.len(), TEST_NROWS);
1099 }
1100
1101 #[test]
1102 fn test_frame_format() {
1103 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1104
1105 frame.format();
1106
1107 for (addr, row) in frame.rows.iter().enumerate() {
1108 for address in &row.address {
1110 assert_eq!(address.addr() as usize, addr);
1111 }
1112 }
1113 }
1114
1115 #[test]
1116 fn test_frame_set_pixel() {
1117 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1118
1119 frame.set_pixel(5, 10, true, false, true);
1121
1122 let mapped_col_10 = map_index(10);
1123 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1124 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1125 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1126
1127 frame.set_pixel(TEST_NROWS + 5, 15, false, true, false);
1129
1130 let mapped_col_15 = map_index(15);
1131 assert_eq!(frame.rows[5].data[mapped_col_15].red2(), false);
1132 assert_eq!(frame.rows[5].data[mapped_col_15].grn2(), true);
1133 assert_eq!(frame.rows[5].data[mapped_col_15].blu2(), false);
1134 }
1135
1136 #[test]
1137 fn test_row_default() {
1138 let row1: Row<TEST_COLS> = Row::new();
1139 let row2: Row<TEST_COLS> = Row::default();
1140
1141 assert_eq!(row1, row2);
1143 assert_eq!(row1.data.len(), row2.data.len());
1144 assert_eq!(row1.address.len(), row2.address.len());
1145
1146 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1148 assert_eq!(entry1.0, entry2.0);
1149 assert_eq!(entry1.0, 0);
1150 }
1151 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1152 assert_eq!(addr1.0, addr2.0);
1153 assert_eq!(addr1.0, 0);
1154 }
1155 }
1156
1157 #[test]
1158 fn test_frame_default() {
1159 let frame1: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1160 let frame2: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::default();
1161
1162 assert_eq!(frame1.rows.len(), frame2.rows.len());
1164
1165 for (row1, row2) in frame1.rows.iter().zip(frame2.rows.iter()) {
1167 assert_eq!(row1, row2);
1168
1169 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1171 assert_eq!(entry1.0, entry2.0);
1172 assert_eq!(entry1.0, 0);
1173 }
1174 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1175 assert_eq!(addr1.0, addr2.0);
1176 assert_eq!(addr1.0, 0);
1177 }
1178 }
1179 }
1180
1181 #[test]
1182 fn test_dma_framebuffer_construction() {
1183 let fb = TestFrameBuffer::new();
1184 assert_eq!(fb.frames.len(), TEST_FRAME_COUNT);
1185 }
1186
1187 #[test]
1188 fn test_bcm_chunk_info() {
1189 let expected_size =
1190 core::mem::size_of::<[Frame<TEST_ROWS, TEST_COLS, TEST_NROWS>; TEST_FRAME_COUNT]>();
1191 assert_eq!(TestFrameBuffer::bcm_chunk_bytes(), expected_size);
1192 assert_eq!(TestFrameBuffer::bcm_chunk_count(), 1);
1193 }
1194
1195 #[test]
1196 fn test_dma_framebuffer_format() {
1197 let mut fb = TestFrameBuffer {
1198 frames: [Frame::new(); TEST_FRAME_COUNT],
1199 };
1200 fb.format();
1201
1202 for frame in &fb.frames {
1204 for (addr, row) in frame.rows.iter().enumerate() {
1205 for address in &row.address {
1206 assert_eq!(address.addr() as usize, addr);
1207 }
1208 }
1209 }
1210 }
1211
1212 #[test]
1213 fn test_dma_framebuffer_set_pixel_bounds() {
1214 let mut fb = TestFrameBuffer::new();
1215
1216 fb.set_pixel(Point::new(-1, 5), Color::RED);
1218 fb.set_pixel(Point::new(5, -1), Color::RED);
1219
1220 fb.set_pixel(Point::new(TEST_COLS as i32, 5), Color::RED);
1222 fb.set_pixel(Point::new(5, TEST_ROWS as i32), Color::RED);
1223 }
1224
1225 #[test]
1226 fn test_dma_framebuffer_set_pixel_internal() {
1227 let mut fb = TestFrameBuffer::new();
1228
1229 let red_color = Rgb888::new(255, 0, 0);
1230 fb.set_pixel_internal(10, 5, red_color);
1231
1232 for frame in &fb.frames {
1236 let mapped_col_10 = map_index(10);
1238 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1239 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1240 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), false);
1241 }
1242 }
1243
1244 #[test]
1245 fn test_dma_framebuffer_brightness_modulation() {
1246 let mut fb = TestFrameBuffer::new();
1247
1248 let brightness_step = 1 << (8 - TEST_BITS); let test_brightness = brightness_step * 3; let color = Rgb888::new(test_brightness, 0, 0);
1252
1253 fb.set_pixel_internal(0, 0, color);
1254
1255 for (frame_idx, frame) in fb.frames.iter().enumerate() {
1258 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1259 let should_be_active = test_brightness >= frame_threshold;
1260
1261 let mapped_col_0 = map_index(0);
1262 assert_eq!(frame.rows[0].data[mapped_col_0].red1(), should_be_active);
1263 }
1264 }
1265
1266 #[test]
1267 fn test_origin_dimensions() {
1268 let fb = TestFrameBuffer::new();
1269 let size = fb.size();
1270 assert_eq!(size.width, TEST_COLS as u32);
1271 assert_eq!(size.height, TEST_ROWS as u32);
1272
1273 let mut fb = TestFrameBuffer::new();
1275 let fb_ref = &mut fb;
1276 let size = fb_ref.size();
1277 assert_eq!(size.width, TEST_COLS as u32);
1278 assert_eq!(size.height, TEST_ROWS as u32);
1279 }
1280
1281 #[test]
1282 fn test_draw_target() {
1283 let mut fb = TestFrameBuffer::new();
1284
1285 let pixels = vec![
1286 embedded_graphics::Pixel(Point::new(0, 0), Color::RED),
1287 embedded_graphics::Pixel(Point::new(1, 1), Color::GREEN),
1288 embedded_graphics::Pixel(Point::new(2, 2), Color::BLUE),
1289 ];
1290
1291 let result = fb.draw_iter(pixels);
1292 assert!(result.is_ok());
1293 }
1294
1295 #[test]
1296 fn test_draw_iter_pixel_verification() {
1297 let mut fb = TestFrameBuffer::new();
1298
1299 let pixels = vec![
1301 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)), ];
1315
1316 let result = fb.draw_iter(pixels);
1317 assert!(result.is_ok());
1318
1319 let first_frame = &fb.frames[0];
1321 let brightness_step = 1 << (8 - TEST_BITS); let first_frame_threshold = brightness_step; let col_idx = map_index(5);
1327 assert_eq!(
1328 first_frame.rows[2].data[col_idx].red1(),
1329 Color::RED.r() >= first_frame_threshold
1330 );
1331 assert_eq!(
1332 first_frame.rows[2].data[col_idx].grn1(),
1333 Color::RED.g() >= first_frame_threshold
1334 );
1335 assert_eq!(
1336 first_frame.rows[2].data[col_idx].blu1(),
1337 Color::RED.b() >= first_frame_threshold
1338 );
1339
1340 let col_idx = map_index(10);
1342 assert_eq!(
1343 first_frame.rows[5].data[col_idx].red1(),
1344 Color::GREEN.r() >= first_frame_threshold
1345 );
1346 assert_eq!(
1347 first_frame.rows[5].data[col_idx].grn1(),
1348 Color::GREEN.g() >= first_frame_threshold
1349 );
1350 assert_eq!(
1351 first_frame.rows[5].data[col_idx].blu1(),
1352 Color::GREEN.b() >= first_frame_threshold
1353 );
1354
1355 let col_idx = map_index(15);
1357 assert_eq!(
1358 first_frame.rows[8].data[col_idx].red1(),
1359 Color::BLUE.r() >= first_frame_threshold
1360 );
1361 assert_eq!(
1362 first_frame.rows[8].data[col_idx].grn1(),
1363 Color::BLUE.g() >= first_frame_threshold
1364 );
1365 assert_eq!(
1366 first_frame.rows[8].data[col_idx].blu1(),
1367 Color::BLUE.b() >= first_frame_threshold
1368 );
1369
1370 let col_idx = map_index(20);
1372 assert_eq!(
1373 first_frame.rows[10].data[col_idx].red1(),
1374 Color::WHITE.r() >= first_frame_threshold
1375 );
1376 assert_eq!(
1377 first_frame.rows[10].data[col_idx].grn1(),
1378 Color::WHITE.g() >= first_frame_threshold
1379 );
1380 assert_eq!(
1381 first_frame.rows[10].data[col_idx].blu1(),
1382 Color::WHITE.b() >= first_frame_threshold
1383 );
1384
1385 let col_idx = map_index(25);
1388 assert_eq!(
1389 first_frame.rows[3].data[col_idx].red2(),
1390 Color::RED.r() >= first_frame_threshold
1391 );
1392 assert_eq!(
1393 first_frame.rows[3].data[col_idx].grn2(),
1394 Color::RED.g() >= first_frame_threshold
1395 );
1396 assert_eq!(
1397 first_frame.rows[3].data[col_idx].blu2(),
1398 Color::RED.b() >= first_frame_threshold
1399 );
1400
1401 let col_idx = map_index(30);
1403 assert_eq!(
1404 first_frame.rows[7].data[col_idx].red2(),
1405 Color::GREEN.r() >= first_frame_threshold
1406 );
1407 assert_eq!(
1408 first_frame.rows[7].data[col_idx].grn2(),
1409 Color::GREEN.g() >= first_frame_threshold
1410 );
1411 assert_eq!(
1412 first_frame.rows[7].data[col_idx].blu2(),
1413 Color::GREEN.b() >= first_frame_threshold
1414 );
1415
1416 let col_idx = map_index(35);
1418 assert_eq!(
1419 first_frame.rows[12].data[col_idx].red2(),
1420 Color::BLUE.r() >= first_frame_threshold
1421 );
1422 assert_eq!(
1423 first_frame.rows[12].data[col_idx].grn2(),
1424 Color::BLUE.g() >= first_frame_threshold
1425 );
1426 assert_eq!(
1427 first_frame.rows[12].data[col_idx].blu2(),
1428 Color::BLUE.b() >= first_frame_threshold
1429 );
1430
1431 let col_idx = map_index(40);
1433 assert_eq!(first_frame.rows[1].data[col_idx].red1(), false);
1434 assert_eq!(first_frame.rows[1].data[col_idx].grn1(), false);
1435 assert_eq!(first_frame.rows[1].data[col_idx].blu1(), false);
1436
1437 let col_idx = map_index(45);
1439 assert_eq!(
1440 first_frame.rows[3].data[col_idx].red1(),
1441 16 >= first_frame_threshold
1442 ); assert_eq!(
1444 first_frame.rows[3].data[col_idx].grn1(),
1445 16 >= first_frame_threshold
1446 ); assert_eq!(
1448 first_frame.rows[3].data[col_idx].blu1(),
1449 16 >= first_frame_threshold
1450 ); }
1452
1453 #[test]
1454 fn test_embedded_graphics_integration() {
1455 let mut fb = TestFrameBuffer::new();
1456
1457 let result = Rectangle::new(Point::new(5, 5), Size::new(10, 8))
1459 .into_styled(PrimitiveStyle::with_fill(Color::RED))
1460 .draw(&mut fb);
1461 assert!(result.is_ok());
1462
1463 let result = Circle::new(Point::new(30, 15), 8)
1465 .into_styled(PrimitiveStyle::with_fill(Color::BLUE))
1466 .draw(&mut fb);
1467 assert!(result.is_ok());
1468 }
1469
1470 #[test]
1471 fn test_read_buffer_implementation() {
1472 let fb = TestFrameBuffer::new();
1473
1474 unsafe {
1476 let (ptr, len) = fb.read_buffer();
1477 assert!(!ptr.is_null());
1478 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1479 }
1480
1481 let mut fb = TestFrameBuffer::new();
1483 let fb_ref = &mut fb;
1484 unsafe {
1485 let (ptr, len) = fb_ref.read_buffer();
1486 assert!(!ptr.is_null());
1487 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1488 }
1489 }
1490
1491 #[test]
1492 fn test_framebuffer_trait() {
1493 let fb = TestFrameBuffer::new();
1494 assert_eq!(fb.get_word_size(), WordSize::Eight);
1495
1496 let mut fb = TestFrameBuffer::new();
1497 let fb_ref = &mut fb;
1498 assert_eq!(fb_ref.get_word_size(), WordSize::Eight);
1499 }
1500
1501 #[test]
1502 fn test_debug_formatting() {
1503 let fb = TestFrameBuffer::new();
1504 let debug_string = format!("{:?}", fb);
1505 assert!(debug_string.contains("DmaFrameBuffer"));
1506 assert!(debug_string.contains("frame_count"));
1507 assert!(debug_string.contains("frame_size"));
1508 assert!(debug_string.contains("brightness_step"));
1509 }
1510
1511 #[test]
1512 fn test_default_implementation() {
1513 let fb1 = TestFrameBuffer::new();
1514 let fb2 = TestFrameBuffer::default();
1515
1516 assert_eq!(fb1.frames.len(), fb2.frames.len());
1518 }
1519
1520 #[cfg(feature = "esp32-ordering")]
1521 #[test]
1522 fn test_esp32_mapping() {
1523 assert_eq!(map_index(0), 2);
1525 assert_eq!(map_index(1), 3);
1526 assert_eq!(map_index(2), 0);
1527 assert_eq!(map_index(3), 1);
1528 assert_eq!(map_index(4), 6); assert_eq!(map_index(5), 7); }
1531
1532 #[test]
1533 fn test_memory_alignment() {
1534 let fb = TestFrameBuffer::new();
1535 let ptr = &fb as *const _ as usize;
1536
1537 assert_eq!(ptr % 4, 0);
1539 }
1540
1541 #[test]
1542 fn test_color_values() {
1543 let mut fb = TestFrameBuffer::new();
1544
1545 let colors = [
1547 (Color::RED, (255, 0, 0)),
1548 (Color::GREEN, (0, 255, 0)),
1549 (Color::BLUE, (0, 0, 255)),
1550 (Color::WHITE, (255, 255, 255)),
1551 (Color::BLACK, (0, 0, 0)),
1552 ];
1553
1554 for (i, (color, (r, g, b))) in colors.iter().enumerate() {
1555 fb.set_pixel(Point::new(i as i32, 0), *color);
1556 assert_eq!(color.r(), *r);
1557 assert_eq!(color.g(), *g);
1558 assert_eq!(color.b(), *b);
1559 }
1560 }
1561
1562 #[test]
1563 fn test_bits_assertion() {
1564 assert!(TEST_BITS <= 8);
1567 }
1568
1569 #[test]
1570 #[cfg(feature = "skip-black-pixels")]
1571 fn test_skip_black_pixels_enabled() {
1572 let mut fb = TestFrameBuffer::new();
1573
1574 fb.set_pixel_internal(10, 5, Color::RED);
1576
1577 let mapped_col_10 = map_index(10);
1579 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1580 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1581 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1582
1583 fb.set_pixel_internal(10, 5, Color::BLACK);
1585
1586 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1588 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1589 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1590 }
1591
1592 #[test]
1593 #[cfg(not(feature = "skip-black-pixels"))]
1594 fn test_skip_black_pixels_disabled() {
1595 let mut fb = TestFrameBuffer::new();
1596
1597 fb.set_pixel_internal(10, 5, Color::RED);
1599
1600 let mapped_col_10 = map_index(10);
1602 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1603 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1604 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1605
1606 fb.set_pixel_internal(10, 5, Color::BLACK);
1608
1609 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1611 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1612 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1613 }
1614
1615 #[test]
1616 fn test_bcm_frame_overwrite() {
1617 let mut fb = TestFrameBuffer::new();
1618
1619 fb.set_pixel_internal(10, 5, Color::WHITE);
1621
1622 let mapped_col_10 = map_index(10);
1623
1624 for frame in fb.frames.iter() {
1626 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1628 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), true);
1629 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1630 }
1631
1632 let half_white = embedded_graphics::pixelcolor::Rgb888::new(128, 128, 128);
1634 fb.set_pixel_internal(10, 5, half_white);
1635
1636 let brightness_step = 1 << (8 - TEST_BITS); for (frame_idx, frame) in fb.frames.iter().enumerate() {
1642 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1643 let should_be_active = 128 >= frame_threshold;
1644
1645 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), should_be_active);
1646 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), should_be_active);
1647 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), should_be_active);
1648 }
1649
1650 for frame_idx in 0..4 {
1653 assert_eq!(
1654 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1655 true
1656 );
1657 }
1658 for frame_idx in 4..TEST_FRAME_COUNT {
1660 assert_eq!(
1661 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1662 false
1663 );
1664 }
1665 }
1666
1667 #[test]
1668 fn test_new_auto_formats() {
1669 let fb = TestFrameBuffer::new();
1670
1671 for frame in &fb.frames {
1673 for (addr, row) in frame.rows.iter().enumerate() {
1674 for address in &row.address {
1675 assert_eq!(address.addr() as usize, addr);
1676 }
1677 }
1678 }
1679 }
1680
1681 #[test]
1682 fn test_erase() {
1683 let mut fb = TestFrameBuffer::new();
1684
1685 fb.set_pixel_internal(10, 5, Color::RED);
1687 fb.set_pixel_internal(20, 10, Color::GREEN);
1688
1689 let mapped_col_10 = map_index(10);
1690 let mapped_col_20 = map_index(20);
1691
1692 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1694 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), true);
1695
1696 fb.erase();
1698
1699 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1701 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1702 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1703 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].red1(), false);
1704 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), false);
1705 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].blu1(), false);
1706
1707 let oe_active = !cfg!(feature = "invert-oe");
1709 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1710 let blank_count = TEST_COLS - active_count;
1711 for frame in &fb.frames {
1712 for (addr, row) in frame.rows.iter().enumerate() {
1713 for address in &row.address {
1715 assert_eq!(address.addr() as usize, addr);
1716 }
1717 let oe_blank_count = row
1718 .data
1719 .iter()
1720 .filter(|entry| entry.output_enable() != oe_active)
1721 .count();
1722 assert_eq!(oe_blank_count, blank_count);
1723 }
1724 }
1725 }
1726
1727 #[test]
1728 fn test_row_clear_colors() {
1729 let mut row: Row<TEST_COLS> = Row::new();
1730 row.format(5);
1731
1732 row.set_color0(0, true, false, true);
1734 row.set_color1(1, false, true, false);
1735
1736 let mapped_col_0 = map_index(0);
1737 let mapped_col_1 = map_index(1);
1738
1739 assert_eq!(row.data[mapped_col_0].red1(), true);
1741 assert_eq!(row.data[mapped_col_0].blu1(), true);
1742 assert_eq!(row.data[mapped_col_1].grn2(), true);
1743
1744 let original_oe_0 = row.data[mapped_col_0].output_enable();
1746 let original_latch_0 = row.data[mapped_col_0].latch();
1747 let original_oe_1 = row.data[mapped_col_1].output_enable();
1748 let original_latch_1 = row.data[mapped_col_1].latch();
1749
1750 row.clear_colors();
1752
1753 assert_eq!(row.data[mapped_col_0].red1(), false);
1755 assert_eq!(row.data[mapped_col_0].grn1(), false);
1756 assert_eq!(row.data[mapped_col_0].blu1(), false);
1757 assert_eq!(row.data[mapped_col_1].red2(), false);
1758 assert_eq!(row.data[mapped_col_1].grn2(), false);
1759 assert_eq!(row.data[mapped_col_1].blu2(), false);
1760
1761 assert_eq!(row.data[mapped_col_0].output_enable(), original_oe_0);
1763 assert_eq!(row.data[mapped_col_0].latch(), original_latch_0);
1764 assert_eq!(row.data[mapped_col_1].output_enable(), original_oe_1);
1765 assert_eq!(row.data[mapped_col_1].latch(), original_latch_1);
1766 }
1767
1768 #[test]
1769 fn test_make_addr_table_function() {
1770 let table = make_addr_table();
1772
1773 assert_eq!(table.len(), 32); let addr_0 = &table[0];
1778 assert_eq!(addr_0.len(), 4); let latch_false_count = addr_0.iter().filter(|addr| !addr.latch()).count();
1782 assert_eq!(latch_false_count, 1);
1783
1784 for addr in addr_0 {
1786 assert_eq!(addr.addr(), 0);
1787 }
1788
1789 let addr_31 = &table[31];
1791 let latch_false_count = addr_31.iter().filter(|addr| !addr.latch()).count();
1792 assert_eq!(latch_false_count, 1);
1793
1794 for addr in addr_31 {
1796 assert_eq!(addr.addr(), 31);
1797 }
1798 }
1799
1800 #[test]
1801 fn test_make_data_template_function() {
1802 let template = make_data_template::<TEST_COLS>();
1804
1805 assert_eq!(template.len(), TEST_COLS);
1807
1808 for entry in &template {
1810 assert_eq!(entry.latch(), false);
1811 }
1812
1813 let oe_active = !cfg!(feature = "invert-oe");
1814 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1815 let blank_count = TEST_COLS - active_count;
1816 let oe_blank_count = template
1817 .iter()
1818 .filter(|entry| entry.output_enable() != oe_active)
1819 .count();
1820 assert_eq!(oe_blank_count, blank_count);
1821
1822 let small_template = make_data_template::<4>();
1824 assert_eq!(small_template.len(), 4);
1825
1826 let small_active = 4_usize.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1827 let small_blank = 4 - small_active;
1828 let oe_blank_count = small_template
1829 .iter()
1830 .filter(|entry| entry.output_enable() != oe_active)
1831 .count();
1832 assert_eq!(oe_blank_count, small_blank);
1833
1834 #[cfg(not(feature = "esp32-ordering"))]
1837 {
1838 let single_template = make_data_template::<1>();
1839 assert_eq!(single_template.len(), 1);
1840 assert_eq!(single_template[0].output_enable(), !oe_active);
1841 assert_eq!(single_template[0].latch(), false);
1842 }
1843 }
1844
1845 #[test]
1846 fn test_addr_table_correctness() {
1847 for addr in 0..32 {
1849 let mut expected_addresses = [Address::new(); 4];
1850
1851 for i in 0..4 {
1853 let latch = !matches!(i, 3);
1854 #[cfg(feature = "esp32-ordering")]
1855 let mapped_i = map_index(i);
1856 #[cfg(not(feature = "esp32-ordering"))]
1857 let mapped_i = i;
1858
1859 expected_addresses[mapped_i].set_latch(latch);
1860 expected_addresses[mapped_i].set_addr(addr);
1861 expected_addresses[mapped_i].0 |= OE_BLANK;
1862 }
1863
1864 let table_addresses = &ADDR_TABLE[addr as usize];
1866 for i in 0..4 {
1867 assert_eq!(table_addresses[i].0, expected_addresses[i].0);
1868 }
1869 }
1870 }
1871
1872 const CHAR_W: i32 = 6;
1874 const CHAR_H: i32 = 10;
1875
1876 fn verify_glyph_at(fb: &mut TestFrameBuffer, origin: Point) {
1879 use embedded_graphics::mock_display::MockDisplay;
1880 use embedded_graphics::mono_font::ascii::FONT_6X10;
1881 use embedded_graphics::mono_font::MonoTextStyle;
1882 use embedded_graphics::text::{Baseline, Text};
1883
1884 let style = MonoTextStyle::new(&FONT_6X10, Color::WHITE);
1886 Text::with_baseline("A", origin, style, Baseline::Top)
1887 .draw(fb)
1888 .unwrap();
1889
1890 let mut reference: MockDisplay<Color> = MockDisplay::new();
1892 Text::with_baseline("A", Point::zero(), style, Baseline::Top)
1893 .draw(&mut reference)
1894 .unwrap();
1895
1896 for dy in 0..CHAR_H {
1898 for dx in 0..CHAR_W {
1899 let expected_on = reference
1900 .get_pixel(Point::new(dx, dy))
1901 .unwrap_or(Color::BLACK)
1902 != Color::BLACK;
1903
1904 let gx = (origin.x + dx) as usize;
1905 let gy = (origin.y + dy) as usize;
1906
1907 let frame0 = &fb.frames[0];
1914 let e = if gy < TEST_NROWS {
1915 &frame0.rows[gy].data[map_index(gx)]
1916 } else {
1917 &frame0.rows[gy - TEST_NROWS].data[map_index(gx)]
1918 };
1919
1920 let (r, g, b) = if gy >= TEST_NROWS {
1921 (e.red2(), e.grn2(), e.blu2())
1922 } else {
1923 (e.red1(), e.grn1(), e.blu1())
1924 };
1925
1926 if expected_on {
1927 assert!(r && g && b);
1928 } else {
1929 assert!(!r && !g && !b);
1930 }
1931 }
1932 }
1933 }
1934
1935 #[test]
1936 fn test_draw_char_corners() {
1937 let upper_left = Point::new(0, 0);
1939 let lower_right = Point::new(TEST_COLS as i32 - CHAR_W, TEST_ROWS as i32 - CHAR_H);
1940
1941 let mut fb = TestFrameBuffer::new();
1942
1943 verify_glyph_at(&mut fb, upper_left);
1945 verify_glyph_at(&mut fb, lower_right);
1947 }
1948
1949 #[test]
1950 fn test_framebuffer_operations_trait_erase() {
1951 let mut fb = TestFrameBuffer::new();
1952
1953 fb.set_pixel_internal(10, 5, Color::RED);
1955 fb.set_pixel_internal(20, 10, Color::GREEN);
1956
1957 <TestFrameBuffer as FrameBufferOperations>::erase(&mut fb);
1959
1960 let mc10 = map_index(10);
1962 let mc20 = map_index(20);
1963 assert_eq!(fb.frames[0].rows[5].data[mc10].red1(), false);
1964 assert_eq!(fb.frames[0].rows[10].data[mc20].grn1(), false);
1965
1966 let oe_active = !cfg!(feature = "invert-oe");
1968 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1969 let blank_count = TEST_COLS - active_count;
1970 let row0 = &fb.frames[0].rows[0];
1971 let oe_blank_count = row0
1972 .data
1973 .iter()
1974 .filter(|entry| entry.output_enable() != oe_active)
1975 .count();
1976 assert_eq!(oe_blank_count, blank_count);
1977 assert!(row0.data.iter().all(|e| !e.latch()));
1978
1979 for (i, addr) in row0.address.iter().enumerate() {
1981 assert_eq!(addr.0, ADDR_TABLE[0][i].0);
1982 }
1983 }
1984
1985 #[test]
1986 fn test_framebuffer_operations_trait_set_pixel() {
1987 let mut fb = TestFrameBuffer::new();
1988
1989 <TestFrameBuffer as FrameBufferOperations>::set_pixel(
1991 &mut fb,
1992 Point::new(8, 3),
1993 Color::BLUE,
1994 );
1995
1996 let idx = map_index(8);
1998 assert_eq!(fb.frames[0].rows[3].data[idx].blu1(), true);
1999 assert_eq!(fb.frames[0].rows[3].data[idx].red1(), false);
2001 assert_eq!(fb.frames[0].rows[3].data[idx].grn1(), false);
2002 }
2003
2004 #[test]
2005 fn test_blanking_delay() {
2006 let mut row: Row<TEST_COLS> = Row::new();
2007 row.format(5);
2008
2009 let oe_active = !cfg!(feature = "invert-oe");
2010
2011 if TRAIL_BLANK_DELAY > 0 {
2013 let trail_blank_idx = map_index(TRAIL_BLANK_DELAY - 1);
2014 assert_eq!(row.data[trail_blank_idx].output_enable(), !oe_active);
2015 }
2016
2017 let first_active_idx = map_index(TRAIL_BLANK_DELAY);
2018 assert_eq!(row.data[first_active_idx].output_enable(), oe_active);
2019
2020 let last_active_idx = map_index(TEST_COLS - LEAD_BLANK_DELAY - 2);
2022 assert_eq!(row.data[last_active_idx].output_enable(), oe_active);
2023
2024 let lead_blank_idx = map_index(TEST_COLS - LEAD_BLANK_DELAY - 1);
2025 assert_eq!(row.data[lead_blank_idx].output_enable(), !oe_active);
2026
2027 let last_pixel_idx = map_index(TEST_COLS - 1);
2028 assert_eq!(row.data[last_pixel_idx].output_enable(), !oe_active);
2029 }
2030}