1#![cfg_attr(feature = "doc-images",
27cfg_attr(all(),
28doc = ::embed_doc_image::embed_image!("latch-circuit", "images/latch-circuit.png")))]
29#![cfg_attr(
30 not(feature = "doc-images"),
31 doc = "**Doc images not enabled**. Compile with feature `doc-images` and Rust version >= 1.54 \
32 to enable."
33)]
34use core::convert::Infallible;
178
179use super::Color;
180use crate::{
181 map_row_index, slot_addresses, BcmLenReps, FrameBuffer, FrameBufferOperations,
182 MutableFrameBuffer, BCM_SEQUENCE_CAPACITY, INTER_ROW_BLANK, LEAD_BLANK_DELAY,
183 TRAIL_BLANK_DELAY,
184};
185use bitfield::bitfield;
186use embedded_dma::ReadBuffer;
187use embedded_graphics::pixelcolor::Rgb888;
188use embedded_graphics::pixelcolor::RgbColor;
189use embedded_graphics::prelude::Point;
190
191#[cfg(not(feature = "invert-oe"))]
192const OE_ACTIVE: u8 = 0b1000_0000;
193#[cfg(not(feature = "invert-oe"))]
194const OE_BLANK: u8 = 0;
195
196#[cfg(feature = "invert-oe")]
197const OE_ACTIVE: u8 = 0;
198#[cfg(feature = "invert-oe")]
199const OE_BLANK: u8 = 0b1000_0000;
200
201bitfield! {
202 #[derive(Clone, Copy, Default, PartialEq, Eq)]
237 #[repr(transparent)]
238 struct Address(u8);
239 impl Debug;
240 pub output_enable, set_output_enable: 7;
241 pub latch, set_latch: 6;
242 pub addr, set_addr: 4, 0;
243}
244
245impl Address {
246 pub const fn new() -> Self {
247 Self(0)
248 }
249}
250
251bitfield! {
252 #[derive(Clone, Copy, Default, PartialEq)]
269 #[repr(transparent)]
270 struct Entry(u8);
271 impl Debug;
272 pub output_enable, set_output_enable: 7;
273 pub latch, set_latch: 6;
274 pub blu2, set_blu2: 5;
275 pub grn2, set_grn2: 4;
276 pub red2, set_red2: 3;
277 pub blu1, set_blu1: 2;
278 pub grn1, set_grn1: 1;
279 pub red1, set_red1: 0;
280}
281
282impl Entry {
283 pub const fn new() -> Self {
284 Self(0)
285 }
286
287 const COLOR0_MASK: u8 = 0b0000_0111; const COLOR1_MASK: u8 = 0b0011_1000; #[inline]
292 fn set_color0_bits(&mut self, bits: u8) {
293 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits & Self::COLOR0_MASK);
294 }
295
296 #[inline]
297 fn set_color1_bits(&mut self, bits: u8) {
298 self.0 = (self.0 & !Self::COLOR1_MASK) | ((bits << 3) & Self::COLOR1_MASK);
299 }
300}
301
302#[derive(Clone, Copy, PartialEq, Debug)]
313#[repr(C)]
314struct Row<const COLS: usize> {
315 data: [Entry; COLS],
316 address: [Address; 4],
317 gap: [Entry; INTER_ROW_BLANK],
318}
319
320#[inline]
322const fn map_index(index: usize) -> usize {
323 #[cfg(feature = "esp32-ordering")]
324 {
325 index ^ 2
326 }
327 #[cfg(not(feature = "esp32-ordering"))]
328 {
329 index
330 }
331}
332
333const fn make_addr_table() -> [[Address; 4]; 32] {
336 let mut tbl = [[Address::new(); 4]; 32];
337 let mut addr = 0;
338 while addr < 32 {
339 let mut i = 0;
340 while i < 4 {
341 let latch = i != 3;
342 let mapped_i = map_index(i);
343 let latch_bit = if latch { 1u8 << 6 } else { 0u8 };
344 tbl[addr][mapped_i].0 = OE_BLANK | latch_bit | addr as u8;
345 i += 1;
346 }
347 addr += 1;
348 }
349 tbl
350}
351
352const ADDR_TABLE: [[Address; 4]; 32] = make_addr_table();
353
354#[allow(clippy::absurd_extreme_comparisons)]
357const fn make_data_template<const COLS: usize>() -> [Entry; COLS] {
358 let mut data = [Entry::new(); COLS];
359 let mut i = 0;
360 while i < COLS {
361 let mapped_i = map_index(i);
362 data[mapped_i].0 =
363 if i >= TRAIL_BLANK_DELAY && i < COLS.saturating_sub(LEAD_BLANK_DELAY + 1) {
364 OE_ACTIVE
365 } else {
366 OE_BLANK
367 };
368 i += 1;
369 }
370 data
371}
372
373impl<const COLS: usize> Row<COLS> {
374 pub const fn new() -> Self {
375 Self {
376 address: [Address::new(); 4],
377 data: [Entry::new(); COLS],
378 gap: [Entry::new(); INTER_ROW_BLANK],
379 }
380 }
381
382 #[inline]
383 pub const fn format(&mut self, addr: u8) {
384 let src_addr = ADDR_TABLE[addr as usize];
385 let mut i = 0;
386 while i < 4 {
387 self.address[i] = src_addr[i];
388 i += 1;
389 }
390
391 let data_template = make_data_template::<COLS>();
392 let mut i = 0;
393 while i < COLS {
394 self.data[i] = data_template[i];
395 i += 1;
396 }
397
398 let mut i = 0;
399 #[allow(clippy::absurd_extreme_comparisons)]
401 while i < INTER_ROW_BLANK {
402 self.gap[i].0 = OE_BLANK;
403 i += 1;
404 }
405 }
406
407 #[inline]
409 pub fn clear_colors(&mut self) {
410 const COLOR_CLEAR_MASK: u8 = !0b0011_1111; for entry in &mut self.data {
414 entry.0 &= COLOR_CLEAR_MASK;
415 }
416 }
417
418 #[inline]
419 pub fn set_color0(&mut self, col: usize, r: bool, g: bool, b: bool) {
420 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
421 let col = map_index(col);
422 self.data[col].set_color0_bits(bits);
423 }
424
425 #[inline]
426 pub fn set_color1(&mut self, col: usize, r: bool, g: bool, b: bool) {
427 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
428 let col = map_index(col);
429 self.data[col].set_color1_bits(bits);
430 }
431}
432
433impl<const COLS: usize> Default for Row<COLS> {
434 fn default() -> Self {
435 Self::new()
436 }
437}
438
439#[derive(Copy, Clone, Debug)]
440#[repr(C)]
441struct Frame<const ROWS: usize, const COLS: usize, const NROWS: usize> {
442 rows: [Row<COLS>; NROWS],
443}
444
445impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Frame<ROWS, COLS, NROWS> {
446 pub const fn new() -> Self {
447 Self {
448 rows: [Row::new(); NROWS],
449 }
450 }
451
452 #[inline]
453 pub const fn format(&mut self) {
454 let mut slot = 0;
455 while slot < NROWS {
456 let (_, addr) = slot_addresses::<NROWS>(slot);
457 self.rows[slot].format(addr);
458 slot += 1;
459 }
460 }
461
462 #[inline]
464 pub fn clear_colors(&mut self) {
465 for row in &mut self.rows {
466 row.clear_colors();
467 }
468 }
469
470 #[inline]
471 pub fn set_pixel(&mut self, y: usize, x: usize, red: bool, green: bool, blue: bool) {
472 let row_idx = map_row_index::<NROWS>(if y < NROWS { y } else { y - NROWS });
473 let row = &mut self.rows[row_idx];
474 if y < NROWS {
475 row.set_color0(x, red, green, blue);
476 } else {
477 row.set_color1(x, red, green, blue);
478 }
479 }
480}
481
482impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Default
483 for Frame<ROWS, COLS, NROWS>
484{
485 fn default() -> Self {
486 Self::new()
487 }
488}
489
490#[derive(Copy, Clone)]
519#[repr(C)]
520#[repr(align(4))]
521pub struct DmaFrameBuffer<
522 const ROWS: usize,
523 const COLS: usize,
524 const NROWS: usize,
525 const BITS: u8,
526 const FRAME_COUNT: usize,
527> {
528 frames: [Frame<ROWS, COLS, NROWS>; FRAME_COUNT],
529}
530
531impl<
532 const ROWS: usize,
533 const COLS: usize,
534 const NROWS: usize,
535 const BITS: u8,
536 const FRAME_COUNT: usize,
537 > Default for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
538{
539 fn default() -> Self {
540 Self::new()
541 }
542}
543
544impl<
545 const ROWS: usize,
546 const COLS: usize,
547 const NROWS: usize,
548 const BITS: u8,
549 const FRAME_COUNT: usize,
550 > DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
551{
552 #[must_use]
575 pub const fn new() -> Self {
576 #[cfg(feature = "esp32-ordering")]
577 assert!(
578 COLS % 4 == 0,
579 "esp32-ordering feature requires COLS to be divisible by 4"
580 );
581 let mut fb = Self {
582 frames: [Frame::new(); FRAME_COUNT],
583 };
584 fb.format();
585 fb
586 }
587
588 #[must_use]
591 #[deprecated(since = "0.11.0", note = "use BCM_SEQUENCE instead")]
592 pub const fn bcm_chunk_count() -> usize {
593 1
594 }
595
596 #[must_use]
599 #[deprecated(since = "0.11.0", note = "use BCM_SEQUENCE instead")]
600 pub const fn bcm_chunk_bytes() -> usize {
601 core::mem::size_of::<[Frame<ROWS, COLS, NROWS>; FRAME_COUNT]>()
602 }
603
604 pub const fn format(&mut self) {
621 let mut i = 0;
622 while i < FRAME_COUNT {
623 self.frames[i].format();
624 i += 1;
625 }
626 }
627
628 #[inline]
645 pub fn erase(&mut self) {
646 for frame in &mut self.frames {
647 frame.clear_colors();
648 }
649 }
650
651 pub fn set_pixel(&mut self, p: Point, color: Color) {
667 if p.x < 0 || p.y < 0 {
668 return;
669 }
670 self.set_pixel_internal(p.x as usize, p.y as usize, color);
671 }
672
673 #[inline]
674 fn frames_on(v: u8) -> usize {
675 (v as usize) >> (8 - BITS)
677 }
678
679 #[inline]
680 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Rgb888) {
681 if x >= COLS || y >= ROWS {
682 return;
683 }
684
685 #[cfg(feature = "skip-black-pixels")]
688 if color == Rgb888::BLACK {
689 return;
690 }
691
692 let red_frames = Self::frames_on(color.r());
694 let green_frames = Self::frames_on(color.g());
695 let blue_frames = Self::frames_on(color.b());
696
697 for (frame_idx, frame) in self.frames.iter_mut().enumerate() {
699 frame.set_pixel(
700 y,
701 x,
702 frame_idx < red_frames,
703 frame_idx < green_frames,
704 frame_idx < blue_frames,
705 );
706 }
707 }
708}
709
710impl<
711 const ROWS: usize,
712 const COLS: usize,
713 const NROWS: usize,
714 const BITS: u8,
715 const FRAME_COUNT: usize,
716 > FrameBufferOperations for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
717{
718 #[inline]
719 fn erase(&mut self) {
720 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::erase(self);
721 }
722
723 #[inline]
724 fn set_pixel(&mut self, p: Point, color: Color) {
725 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::set_pixel(self, p, color);
726 }
727}
728
729impl<
730 const ROWS: usize,
731 const COLS: usize,
732 const NROWS: usize,
733 const BITS: u8,
734 const FRAME_COUNT: usize,
735 > embedded_graphics::prelude::OriginDimensions
736 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
737{
738 fn size(&self) -> embedded_graphics::prelude::Size {
739 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
740 }
741}
742
743impl<
744 const ROWS: usize,
745 const COLS: usize,
746 const NROWS: usize,
747 const BITS: u8,
748 const FRAME_COUNT: usize,
749 > embedded_graphics::draw_target::DrawTarget
750 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
751{
752 type Color = Color;
753
754 type Error = Infallible;
755
756 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
757 where
758 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
759 {
760 for pixel in pixels {
761 if pixel.0.x < 0 || pixel.0.y < 0 {
762 continue;
763 }
764 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
765 }
766 Ok(())
767 }
768}
769
770unsafe impl<
776 const ROWS: usize,
777 const COLS: usize,
778 const NROWS: usize,
779 const BITS: u8,
780 const FRAME_COUNT: usize,
781 > ReadBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
782{
783 type Word = u8;
784
785 unsafe fn read_buffer(&self) -> (*const u8, usize) {
786 let ptr = (&raw const self.frames).cast::<u8>();
787 let len = core::mem::size_of_val(&self.frames);
788 (ptr, len)
789 }
790}
791
792unsafe impl<
798 const ROWS: usize,
799 const COLS: usize,
800 const NROWS: usize,
801 const BITS: u8,
802 const FRAME_COUNT: usize,
803 > ReadBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
804{
805 type Word = u8;
806
807 unsafe fn read_buffer(&self) -> (*const u8, usize) {
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 > core::fmt::Debug for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
821{
822 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
823 let brightness_step = 1 << (8 - BITS);
824 f.debug_struct("DmaFrameBuffer")
825 .field("size", &core::mem::size_of_val(&self.frames))
826 .field("frame_count", &self.frames.len())
827 .field("frame_size", &core::mem::size_of_val(&self.frames[0]))
828 .field("brightness_step", &&brightness_step)
829 .finish()
830 }
831}
832
833#[cfg(feature = "defmt")]
834impl<
835 const ROWS: usize,
836 const COLS: usize,
837 const NROWS: usize,
838 const BITS: u8,
839 const FRAME_COUNT: usize,
840 > defmt::Format for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
841{
842 fn format(&self, f: defmt::Formatter) {
843 let brightness_step = 1 << (8 - BITS);
844 defmt::write!(
845 f,
846 "DmaFrameBuffer<{}, {}, {}, {}, {}>",
847 ROWS,
848 COLS,
849 NROWS,
850 BITS,
851 FRAME_COUNT
852 );
853 defmt::write!(f, " size: {}", core::mem::size_of_val(&self.frames));
854 defmt::write!(
855 f,
856 " frame_size: {}",
857 core::mem::size_of_val(&self.frames[0])
858 );
859 defmt::write!(f, " brightness_step: {}", brightness_step);
860 }
861}
862
863impl<
864 const ROWS: usize,
865 const COLS: usize,
866 const NROWS: usize,
867 const BITS: u8,
868 const FRAME_COUNT: usize,
869 > embedded_graphics::prelude::OriginDimensions
870 for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
871{
872 fn size(&self) -> embedded_graphics::prelude::Size {
873 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
874 }
875}
876
877impl<
878 const ROWS: usize,
879 const COLS: usize,
880 const NROWS: usize,
881 const BITS: u8,
882 const FRAME_COUNT: usize,
883 > FrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
884{
885 type Word = u8;
886
887 const BCM_SEQUENCE: [BcmLenReps; BCM_SEQUENCE_CAPACITY] = {
888 let mut seq = [BcmLenReps::ZERO; BCM_SEQUENCE_CAPACITY];
889 seq[0] = BcmLenReps {
890 len: core::mem::size_of::<[Frame<ROWS, COLS, NROWS>; FRAME_COUNT]>(),
891 reps: 1,
892 };
893 seq
894 };
895
896 const BCM_SEQUENCE_LEN: usize = 1;
897
898 const BCM_SEQUENCE_COUNT: usize = 1;
899
900 fn bcm_segment_ptr(&self, index: usize) -> *const u8 {
901 assert!(index == 0, "threshold DmaFrameBuffer has only 1 segment");
902 (&raw const self.frames).cast::<u8>()
903 }
904}
905
906impl<
907 const ROWS: usize,
908 const COLS: usize,
909 const NROWS: usize,
910 const BITS: u8,
911 const FRAME_COUNT: usize,
912 > MutableFrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
913{
914}
915
916#[cfg(test)]
917mod tests {
918 extern crate std;
919
920 use std::format;
921 use std::vec;
922
923 use super::*;
924 use embedded_graphics::pixelcolor::RgbColor;
925 use embedded_graphics::prelude::*;
926 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle};
927
928 const TEST_ROWS: usize = 32;
929 const TEST_COLS: usize = if LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 2 > 64 {
930 128
931 } else {
932 64
933 };
934 const TEST_NROWS: usize = TEST_ROWS / 2;
935 const TEST_BITS: u8 = 3;
936 const TEST_FRAME_COUNT: usize = (1 << TEST_BITS) - 1; type TestFrameBuffer =
939 DmaFrameBuffer<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_BITS, TEST_FRAME_COUNT>;
940
941 fn get_mapped_row(row_idx: usize) -> usize {
944 map_row_index::<TEST_NROWS>(row_idx)
945 }
946
947 #[test]
948 fn test_address_construction() {
949 let addr = Address::new();
950 assert_eq!(addr.0, 0);
951 assert_eq!(addr.latch(), false);
952 assert_eq!(addr.addr(), 0);
953 }
954
955 #[test]
956 fn test_address_setters() {
957 let mut addr = Address::new();
958
959 addr.set_latch(true);
960 assert_eq!(addr.latch(), true);
961 assert_eq!(addr.0 & 0b01000000, 0b01000000);
962
963 addr.set_addr(0b11111);
964 assert_eq!(addr.addr(), 0b11111);
965 assert_eq!(addr.0 & 0b00011111, 0b00011111);
966 }
967
968 #[test]
969 fn test_address_bit_isolation() {
970 let mut addr = Address::new();
971
972 addr.set_addr(0b11111);
974 addr.set_latch(true);
975 assert_eq!(addr.addr(), 0b11111);
976 assert_eq!(addr.latch(), true);
977 }
978
979 #[test]
980 fn test_entry_construction() {
981 let entry = Entry::new();
982 assert_eq!(entry.0, 0);
983 assert_eq!(entry.output_enable(), false);
984 assert_eq!(entry.latch(), false);
985 assert_eq!(entry.red1(), false);
986 assert_eq!(entry.grn1(), false);
987 assert_eq!(entry.blu1(), false);
988 assert_eq!(entry.red2(), false);
989 assert_eq!(entry.grn2(), false);
990 assert_eq!(entry.blu2(), false);
991 }
992
993 #[test]
994 fn test_entry_setters() {
995 let mut entry = Entry::new();
996
997 entry.set_output_enable(true);
998 assert_eq!(entry.output_enable(), true);
999 assert_eq!(entry.0 & 0b10000000, 0b10000000);
1000
1001 entry.set_latch(true);
1002 assert_eq!(entry.latch(), true);
1003 assert_eq!(entry.0 & 0b01000000, 0b01000000);
1004
1005 entry.set_red1(true);
1007 entry.set_grn1(true);
1008 entry.set_blu1(true);
1009 assert_eq!(entry.red1(), true);
1010 assert_eq!(entry.grn1(), true);
1011 assert_eq!(entry.blu1(), true);
1012 assert_eq!(entry.0 & 0b00000111, 0b00000111);
1013
1014 entry.set_red2(true);
1016 entry.set_grn2(true);
1017 entry.set_blu2(true);
1018 assert_eq!(entry.red2(), true);
1019 assert_eq!(entry.grn2(), true);
1020 assert_eq!(entry.blu2(), true);
1021 assert_eq!(entry.0 & 0b00111000, 0b00111000);
1022 }
1023
1024 #[test]
1025 fn test_entry_set_color0() {
1026 let mut entry = Entry::new();
1027
1028 let bits = (u8::from(true) << 2) | (u8::from(false) << 1) | u8::from(true); entry.set_color0_bits(bits);
1030 assert_eq!(entry.red1(), true);
1031 assert_eq!(entry.grn1(), false);
1032 assert_eq!(entry.blu1(), true);
1033 assert_eq!(entry.0 & 0b00000111, 0b00000101); }
1035
1036 #[test]
1037 fn test_entry_set_color1() {
1038 let mut entry = Entry::new();
1039
1040 let bits = (u8::from(true) << 2) | (u8::from(true) << 1) | u8::from(false); entry.set_color1_bits(bits);
1042 assert_eq!(entry.red2(), false);
1043 assert_eq!(entry.grn2(), true);
1044 assert_eq!(entry.blu2(), true);
1045 assert_eq!(entry.0 & 0b00111000, 0b00110000); }
1047
1048 #[test]
1049 fn test_row_construction() {
1050 let row: Row<TEST_COLS> = Row::new();
1051 assert_eq!(row.data.len(), TEST_COLS);
1052 assert_eq!(row.address.len(), 4);
1053
1054 for entry in &row.data {
1056 assert_eq!(entry.0, 0);
1057 }
1058 for addr in &row.address {
1059 assert_eq!(addr.0, 0);
1060 }
1061 }
1062
1063 #[test]
1064 fn test_row_format() {
1065 let mut row: Row<TEST_COLS> = Row::new();
1066 let test_addr = 5;
1067
1068 row.format(test_addr);
1069
1070 for addr in &row.address {
1072 assert_eq!(addr.addr(), test_addr);
1073 }
1077 let latch_false_count = row.address.iter().filter(|addr| !addr.latch()).count();
1081 assert_eq!(latch_false_count, 1);
1082
1083 for entry in &row.data {
1085 assert_eq!(entry.latch(), false);
1086 }
1087 let oe_active = !cfg!(feature = "invert-oe");
1088 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1089 let blank_count = TEST_COLS - active_count;
1090 let oe_blank_count = row
1091 .data
1092 .iter()
1093 .filter(|entry| entry.output_enable() != oe_active)
1094 .count();
1095 assert_eq!(oe_blank_count, blank_count);
1096 }
1097
1098 #[test]
1099 fn test_row_set_color0() {
1100 let mut row: Row<TEST_COLS> = Row::new();
1101
1102 row.set_color0(0, true, false, true);
1103
1104 let mapped_col_0 = map_index(0);
1105 assert_eq!(row.data[mapped_col_0].red1(), true);
1106 assert_eq!(row.data[mapped_col_0].grn1(), false);
1107 assert_eq!(row.data[mapped_col_0].blu1(), true);
1108
1109 row.set_color0(1, false, true, false);
1111
1112 let mapped_col_1 = map_index(1);
1113 assert_eq!(row.data[mapped_col_1].red1(), false);
1114 assert_eq!(row.data[mapped_col_1].grn1(), true);
1115 assert_eq!(row.data[mapped_col_1].blu1(), false);
1116 }
1117
1118 #[test]
1119 fn test_row_set_color1() {
1120 let mut row: Row<TEST_COLS> = Row::new();
1121
1122 row.set_color1(0, true, true, false);
1123
1124 let mapped_col_0 = map_index(0);
1125 assert_eq!(row.data[mapped_col_0].red2(), true);
1126 assert_eq!(row.data[mapped_col_0].grn2(), true);
1127 assert_eq!(row.data[mapped_col_0].blu2(), false);
1128 }
1129
1130 #[test]
1131 fn test_frame_construction() {
1132 let frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1133 assert_eq!(frame.rows.len(), TEST_NROWS);
1134 }
1135
1136 #[test]
1137 fn test_frame_format() {
1138 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1139
1140 frame.format();
1141
1142 for (slot, row) in frame.rows.iter().enumerate() {
1143 let (_, addr) = slot_addresses::<TEST_NROWS>(slot);
1145 for address in &row.address {
1146 assert_eq!(address.addr(), addr);
1147 }
1148 }
1149 }
1150
1151 #[test]
1152 fn test_frame_set_pixel() {
1153 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1154
1155 frame.set_pixel(5, 10, true, false, true);
1157
1158 let mapped_col_10 = map_index(10);
1159 assert_eq!(
1160 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1161 true
1162 );
1163 assert_eq!(
1164 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1165 false
1166 );
1167 assert_eq!(
1168 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1169 true
1170 );
1171
1172 frame.set_pixel(TEST_NROWS + 5, 15, false, true, false);
1174
1175 let mapped_col_15 = map_index(15);
1176 assert_eq!(
1177 frame.rows[get_mapped_row(5)].data[mapped_col_15].red2(),
1178 false
1179 );
1180 assert_eq!(
1181 frame.rows[get_mapped_row(5)].data[mapped_col_15].grn2(),
1182 true
1183 );
1184 assert_eq!(
1185 frame.rows[get_mapped_row(5)].data[mapped_col_15].blu2(),
1186 false
1187 );
1188 }
1189
1190 #[test]
1191 #[cfg(feature = "reverse-row-order")]
1192 fn test_reverse_row_order() {
1193 let mut fb = TestFrameBuffer::new();
1194
1195 let first = &fb.frames[0].rows[0];
1199 for address in &first.address {
1200 assert_eq!(address.addr(), (TEST_NROWS - 1) as u8);
1201 }
1202 let last = &fb.frames[0].rows[TEST_NROWS - 1];
1203 for address in &last.address {
1204 assert_eq!(address.addr(), 0);
1205 }
1206
1207 fb.set_pixel(Point::new(3, 0), Color::RED);
1209 let col3 = map_index(3);
1210 assert!(fb.frames[0].rows[TEST_NROWS - 1].data[col3].red1());
1211 assert!(!fb.frames[0].rows[0].data[col3].red1());
1212 }
1213
1214 #[test]
1215 fn test_row_default() {
1216 let row1: Row<TEST_COLS> = Row::new();
1217 let row2: Row<TEST_COLS> = Row::default();
1218
1219 assert_eq!(row1, row2);
1221 assert_eq!(row1.data.len(), row2.data.len());
1222 assert_eq!(row1.address.len(), row2.address.len());
1223
1224 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1226 assert_eq!(entry1.0, entry2.0);
1227 assert_eq!(entry1.0, 0);
1228 }
1229 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1230 assert_eq!(addr1.0, addr2.0);
1231 assert_eq!(addr1.0, 0);
1232 }
1233 }
1234
1235 #[test]
1236 fn test_frame_default() {
1237 let frame1: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1238 let frame2: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::default();
1239
1240 assert_eq!(frame1.rows.len(), frame2.rows.len());
1242
1243 for (row1, row2) in frame1.rows.iter().zip(frame2.rows.iter()) {
1245 assert_eq!(row1, row2);
1246
1247 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1249 assert_eq!(entry1.0, entry2.0);
1250 assert_eq!(entry1.0, 0);
1251 }
1252 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1253 assert_eq!(addr1.0, addr2.0);
1254 assert_eq!(addr1.0, 0);
1255 }
1256 }
1257 }
1258
1259 #[test]
1260 fn test_dma_framebuffer_construction() {
1261 let fb = TestFrameBuffer::new();
1262 assert_eq!(fb.frames.len(), TEST_FRAME_COUNT);
1263 }
1264
1265 #[test]
1266 #[allow(deprecated)]
1267 fn test_bcm_chunk_info() {
1268 let expected_size =
1269 core::mem::size_of::<[Frame<TEST_ROWS, TEST_COLS, TEST_NROWS>; TEST_FRAME_COUNT]>();
1270 assert_eq!(TestFrameBuffer::bcm_chunk_bytes(), expected_size);
1271 assert_eq!(TestFrameBuffer::bcm_chunk_count(), 1);
1272 }
1273
1274 #[test]
1275 fn test_dma_framebuffer_format() {
1276 let mut fb = TestFrameBuffer {
1277 frames: [Frame::new(); TEST_FRAME_COUNT],
1278 };
1279 fb.format();
1280
1281 for frame in &fb.frames {
1283 for (slot, row) in frame.rows.iter().enumerate() {
1284 let (_, addr) = slot_addresses::<TEST_NROWS>(slot);
1285 for address in &row.address {
1286 assert_eq!(address.addr(), addr);
1287 }
1288 }
1289 }
1290 }
1291
1292 #[test]
1293 fn test_dma_framebuffer_set_pixel_bounds() {
1294 let mut fb = TestFrameBuffer::new();
1295
1296 fb.set_pixel(Point::new(-1, 5), Color::RED);
1298 fb.set_pixel(Point::new(5, -1), Color::RED);
1299
1300 fb.set_pixel(Point::new(TEST_COLS as i32, 5), Color::RED);
1302 fb.set_pixel(Point::new(5, TEST_ROWS as i32), Color::RED);
1303 }
1304
1305 #[test]
1306 fn test_dma_framebuffer_set_pixel_internal() {
1307 let mut fb = TestFrameBuffer::new();
1308
1309 let red_color = Rgb888::new(255, 0, 0);
1310 fb.set_pixel_internal(10, 5, red_color);
1311
1312 for frame in &fb.frames {
1316 let mapped_col_10 = map_index(10);
1318 assert_eq!(
1319 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1320 true
1321 );
1322 assert_eq!(
1323 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1324 false
1325 );
1326 assert_eq!(
1327 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1328 false
1329 );
1330 }
1331 }
1332
1333 #[test]
1334 fn test_dma_framebuffer_brightness_modulation() {
1335 let mut fb = TestFrameBuffer::new();
1336
1337 let brightness_step = 1 << (8 - TEST_BITS); let test_brightness = brightness_step * 3; let color = Rgb888::new(test_brightness, 0, 0);
1341
1342 fb.set_pixel_internal(0, 0, color);
1343
1344 for (frame_idx, frame) in fb.frames.iter().enumerate() {
1347 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1348 let should_be_active = test_brightness >= frame_threshold;
1349
1350 let mapped_col_0 = map_index(0);
1351 assert_eq!(
1352 frame.rows[get_mapped_row(0)].data[mapped_col_0].red1(),
1353 should_be_active
1354 );
1355 }
1356 }
1357
1358 #[test]
1359 fn test_origin_dimensions() {
1360 let fb = TestFrameBuffer::new();
1361 let size = fb.size();
1362 assert_eq!(size.width, TEST_COLS as u32);
1363 assert_eq!(size.height, TEST_ROWS as u32);
1364
1365 let mut fb = TestFrameBuffer::new();
1367 let fb_ref = &mut fb;
1368 let size = fb_ref.size();
1369 assert_eq!(size.width, TEST_COLS as u32);
1370 assert_eq!(size.height, TEST_ROWS as u32);
1371 }
1372
1373 #[test]
1374 fn test_draw_target() {
1375 let mut fb = TestFrameBuffer::new();
1376
1377 let pixels = vec![
1378 embedded_graphics::Pixel(Point::new(0, 0), Color::RED),
1379 embedded_graphics::Pixel(Point::new(1, 1), Color::GREEN),
1380 embedded_graphics::Pixel(Point::new(2, 2), Color::BLUE),
1381 ];
1382
1383 let result = fb.draw_iter(pixels);
1384 assert!(result.is_ok());
1385 }
1386
1387 #[test]
1388 fn test_draw_iter_pixel_verification() {
1389 let mut fb = TestFrameBuffer::new();
1390
1391 let pixels = vec![
1393 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)), ];
1407
1408 let result = fb.draw_iter(pixels);
1409 assert!(result.is_ok());
1410
1411 let first_frame = &fb.frames[0];
1413 let brightness_step = 1 << (8 - TEST_BITS); let first_frame_threshold = brightness_step; let col_idx = map_index(5);
1419 assert_eq!(
1420 first_frame.rows[get_mapped_row(2)].data[col_idx].red1(),
1421 Color::RED.r() >= first_frame_threshold
1422 );
1423 assert_eq!(
1424 first_frame.rows[get_mapped_row(2)].data[col_idx].grn1(),
1425 Color::RED.g() >= first_frame_threshold
1426 );
1427 assert_eq!(
1428 first_frame.rows[get_mapped_row(2)].data[col_idx].blu1(),
1429 Color::RED.b() >= first_frame_threshold
1430 );
1431
1432 let col_idx = map_index(10);
1434 assert_eq!(
1435 first_frame.rows[get_mapped_row(5)].data[col_idx].red1(),
1436 Color::GREEN.r() >= first_frame_threshold
1437 );
1438 assert_eq!(
1439 first_frame.rows[get_mapped_row(5)].data[col_idx].grn1(),
1440 Color::GREEN.g() >= first_frame_threshold
1441 );
1442 assert_eq!(
1443 first_frame.rows[get_mapped_row(5)].data[col_idx].blu1(),
1444 Color::GREEN.b() >= first_frame_threshold
1445 );
1446
1447 let col_idx = map_index(15);
1449 assert_eq!(
1450 first_frame.rows[get_mapped_row(8)].data[col_idx].red1(),
1451 Color::BLUE.r() >= first_frame_threshold
1452 );
1453 assert_eq!(
1454 first_frame.rows[get_mapped_row(8)].data[col_idx].grn1(),
1455 Color::BLUE.g() >= first_frame_threshold
1456 );
1457 assert_eq!(
1458 first_frame.rows[get_mapped_row(8)].data[col_idx].blu1(),
1459 Color::BLUE.b() >= first_frame_threshold
1460 );
1461
1462 let col_idx = map_index(20);
1464 assert_eq!(
1465 first_frame.rows[get_mapped_row(10)].data[col_idx].red1(),
1466 Color::WHITE.r() >= first_frame_threshold
1467 );
1468 assert_eq!(
1469 first_frame.rows[get_mapped_row(10)].data[col_idx].grn1(),
1470 Color::WHITE.g() >= first_frame_threshold
1471 );
1472 assert_eq!(
1473 first_frame.rows[get_mapped_row(10)].data[col_idx].blu1(),
1474 Color::WHITE.b() >= first_frame_threshold
1475 );
1476
1477 let col_idx = map_index(25);
1480 assert_eq!(
1481 first_frame.rows[get_mapped_row(3)].data[col_idx].red2(),
1482 Color::RED.r() >= first_frame_threshold
1483 );
1484 assert_eq!(
1485 first_frame.rows[get_mapped_row(3)].data[col_idx].grn2(),
1486 Color::RED.g() >= first_frame_threshold
1487 );
1488 assert_eq!(
1489 first_frame.rows[get_mapped_row(3)].data[col_idx].blu2(),
1490 Color::RED.b() >= first_frame_threshold
1491 );
1492
1493 let col_idx = map_index(30);
1495 assert_eq!(
1496 first_frame.rows[get_mapped_row(7)].data[col_idx].red2(),
1497 Color::GREEN.r() >= first_frame_threshold
1498 );
1499 assert_eq!(
1500 first_frame.rows[get_mapped_row(7)].data[col_idx].grn2(),
1501 Color::GREEN.g() >= first_frame_threshold
1502 );
1503 assert_eq!(
1504 first_frame.rows[get_mapped_row(7)].data[col_idx].blu2(),
1505 Color::GREEN.b() >= first_frame_threshold
1506 );
1507
1508 let col_idx = map_index(35);
1510 assert_eq!(
1511 first_frame.rows[get_mapped_row(12)].data[col_idx].red2(),
1512 Color::BLUE.r() >= first_frame_threshold
1513 );
1514 assert_eq!(
1515 first_frame.rows[get_mapped_row(12)].data[col_idx].grn2(),
1516 Color::BLUE.g() >= first_frame_threshold
1517 );
1518 assert_eq!(
1519 first_frame.rows[get_mapped_row(12)].data[col_idx].blu2(),
1520 Color::BLUE.b() >= first_frame_threshold
1521 );
1522
1523 let col_idx = map_index(40);
1525 assert_eq!(
1526 first_frame.rows[get_mapped_row(1)].data[col_idx].red1(),
1527 false
1528 );
1529 assert_eq!(
1530 first_frame.rows[get_mapped_row(1)].data[col_idx].grn1(),
1531 false
1532 );
1533 assert_eq!(
1534 first_frame.rows[get_mapped_row(1)].data[col_idx].blu1(),
1535 false
1536 );
1537
1538 let col_idx = map_index(45);
1540 assert_eq!(
1541 first_frame.rows[get_mapped_row(3)].data[col_idx].red1(),
1542 16 >= first_frame_threshold
1543 ); assert_eq!(
1545 first_frame.rows[get_mapped_row(3)].data[col_idx].grn1(),
1546 16 >= first_frame_threshold
1547 ); assert_eq!(
1549 first_frame.rows[get_mapped_row(3)].data[col_idx].blu1(),
1550 16 >= first_frame_threshold
1551 ); }
1553
1554 #[test]
1555 fn test_embedded_graphics_integration() {
1556 let mut fb = TestFrameBuffer::new();
1557
1558 let result = Rectangle::new(Point::new(5, 5), Size::new(10, 8))
1560 .into_styled(PrimitiveStyle::with_fill(Color::RED))
1561 .draw(&mut fb);
1562 assert!(result.is_ok());
1563
1564 let result = Circle::new(Point::new(30, 15), 8)
1566 .into_styled(PrimitiveStyle::with_fill(Color::BLUE))
1567 .draw(&mut fb);
1568 assert!(result.is_ok());
1569 }
1570
1571 #[test]
1572 fn test_read_buffer_implementation() {
1573 let fb = TestFrameBuffer::new();
1574
1575 unsafe {
1577 let (ptr, len) = fb.read_buffer();
1578 assert!(!ptr.is_null());
1579 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1580 }
1581
1582 let mut fb = TestFrameBuffer::new();
1584 let fb_ref = &mut fb;
1585 unsafe {
1586 let (ptr, len) = fb_ref.read_buffer();
1587 assert!(!ptr.is_null());
1588 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1589 }
1590 }
1591
1592 #[test]
1593 fn test_debug_formatting() {
1594 let fb = TestFrameBuffer::new();
1595 let debug_string = format!("{:?}", fb);
1596 assert!(debug_string.contains("DmaFrameBuffer"));
1597 assert!(debug_string.contains("frame_count"));
1598 assert!(debug_string.contains("frame_size"));
1599 assert!(debug_string.contains("brightness_step"));
1600 }
1601
1602 #[test]
1603 fn test_default_implementation() {
1604 let fb1 = TestFrameBuffer::new();
1605 let fb2 = TestFrameBuffer::default();
1606
1607 assert_eq!(fb1.frames.len(), fb2.frames.len());
1609 }
1610
1611 #[cfg(feature = "esp32-ordering")]
1612 #[test]
1613 fn test_esp32_mapping() {
1614 assert_eq!(map_index(0), 2);
1616 assert_eq!(map_index(1), 3);
1617 assert_eq!(map_index(2), 0);
1618 assert_eq!(map_index(3), 1);
1619 assert_eq!(map_index(4), 6); assert_eq!(map_index(5), 7); }
1622
1623 #[test]
1624 fn test_memory_alignment() {
1625 let fb = TestFrameBuffer::new();
1626 let ptr = &fb as *const _ as usize;
1627
1628 assert_eq!(ptr % 4, 0);
1630 }
1631
1632 #[test]
1633 fn test_color_values() {
1634 let mut fb = TestFrameBuffer::new();
1635
1636 let colors = [
1638 (Color::RED, (255, 0, 0)),
1639 (Color::GREEN, (0, 255, 0)),
1640 (Color::BLUE, (0, 0, 255)),
1641 (Color::WHITE, (255, 255, 255)),
1642 (Color::BLACK, (0, 0, 0)),
1643 ];
1644
1645 for (i, (color, (r, g, b))) in colors.iter().enumerate() {
1646 fb.set_pixel(Point::new(i as i32, 0), *color);
1647 assert_eq!(color.r(), *r);
1648 assert_eq!(color.g(), *g);
1649 assert_eq!(color.b(), *b);
1650 }
1651 }
1652
1653 #[test]
1654 fn test_bits_assertion() {
1655 assert!(TEST_BITS <= 8);
1658 }
1659
1660 #[test]
1661 #[cfg(feature = "skip-black-pixels")]
1662 fn test_skip_black_pixels_enabled() {
1663 let mut fb = TestFrameBuffer::new();
1664
1665 fb.set_pixel_internal(10, 5, Color::RED);
1667
1668 let mapped_col_10 = map_index(10);
1670 assert_eq!(
1671 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1672 true
1673 );
1674 assert_eq!(
1675 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1676 false
1677 );
1678 assert_eq!(
1679 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1680 false
1681 );
1682
1683 fb.set_pixel_internal(10, 5, Color::BLACK);
1685
1686 assert_eq!(
1688 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1689 true
1690 );
1691 assert_eq!(
1692 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1693 false
1694 );
1695 assert_eq!(
1696 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1697 false
1698 );
1699 }
1700
1701 #[test]
1702 #[cfg(not(feature = "skip-black-pixels"))]
1703 fn test_skip_black_pixels_disabled() {
1704 let mut fb = TestFrameBuffer::new();
1705
1706 fb.set_pixel_internal(10, 5, Color::RED);
1708
1709 let mapped_col_10 = map_index(10);
1711 assert_eq!(
1712 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1713 true
1714 );
1715 assert_eq!(
1716 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1717 false
1718 );
1719 assert_eq!(
1720 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1721 false
1722 );
1723
1724 fb.set_pixel_internal(10, 5, Color::BLACK);
1726
1727 assert_eq!(
1729 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1730 false
1731 );
1732 assert_eq!(
1733 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1734 false
1735 );
1736 assert_eq!(
1737 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1738 false
1739 );
1740 }
1741
1742 #[test]
1743 fn test_bcm_frame_overwrite() {
1744 let mut fb = TestFrameBuffer::new();
1745
1746 fb.set_pixel_internal(10, 5, Color::WHITE);
1748
1749 let mapped_col_10 = map_index(10);
1750
1751 for frame in fb.frames.iter() {
1753 assert_eq!(
1755 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1756 true
1757 );
1758 assert_eq!(
1759 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1760 true
1761 );
1762 assert_eq!(
1763 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1764 true
1765 );
1766 }
1767
1768 let half_white = embedded_graphics::pixelcolor::Rgb888::new(128, 128, 128);
1770 fb.set_pixel_internal(10, 5, half_white);
1771
1772 let brightness_step = 1 << (8 - TEST_BITS); for (frame_idx, frame) in fb.frames.iter().enumerate() {
1778 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1779 let should_be_active = 128 >= frame_threshold;
1780
1781 assert_eq!(
1782 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1783 should_be_active
1784 );
1785 assert_eq!(
1786 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1787 should_be_active
1788 );
1789 assert_eq!(
1790 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1791 should_be_active
1792 );
1793 }
1794
1795 for frame_idx in 0..4 {
1798 assert_eq!(
1799 fb.frames[frame_idx].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1800 true
1801 );
1802 }
1803 for frame_idx in 4..TEST_FRAME_COUNT {
1805 assert_eq!(
1806 fb.frames[frame_idx].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1807 false
1808 );
1809 }
1810 }
1811
1812 #[test]
1813 fn test_new_auto_formats() {
1814 let fb = TestFrameBuffer::new();
1815
1816 for frame in &fb.frames {
1818 for (slot, row) in frame.rows.iter().enumerate() {
1819 let (_, addr) = slot_addresses::<TEST_NROWS>(slot);
1820 for address in &row.address {
1821 assert_eq!(address.addr(), addr);
1822 }
1823 }
1824 }
1825 }
1826
1827 #[test]
1828 fn test_erase() {
1829 let mut fb = TestFrameBuffer::new();
1830
1831 fb.set_pixel_internal(10, 5, Color::RED);
1833 fb.set_pixel_internal(20, 10, Color::GREEN);
1834
1835 let mapped_col_10 = map_index(10);
1836 let mapped_col_20 = map_index(20);
1837
1838 assert_eq!(
1840 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1841 true
1842 );
1843 assert_eq!(
1844 fb.frames[0].rows[get_mapped_row(10)].data[mapped_col_20].grn1(),
1845 true
1846 );
1847
1848 fb.erase();
1850
1851 assert_eq!(
1853 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1854 false
1855 );
1856 assert_eq!(
1857 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1858 false
1859 );
1860 assert_eq!(
1861 fb.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1862 false
1863 );
1864 assert_eq!(
1865 fb.frames[0].rows[get_mapped_row(10)].data[mapped_col_20].red1(),
1866 false
1867 );
1868 assert_eq!(
1869 fb.frames[0].rows[get_mapped_row(10)].data[mapped_col_20].grn1(),
1870 false
1871 );
1872 assert_eq!(
1873 fb.frames[0].rows[get_mapped_row(10)].data[mapped_col_20].blu1(),
1874 false
1875 );
1876
1877 let oe_active = !cfg!(feature = "invert-oe");
1879 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1880 let blank_count = TEST_COLS - active_count;
1881 for frame in &fb.frames {
1882 for (slot, row) in frame.rows.iter().enumerate() {
1883 let (_, addr) = slot_addresses::<TEST_NROWS>(slot);
1885 for address in &row.address {
1886 assert_eq!(address.addr(), addr);
1887 }
1888 let oe_blank_count = row
1889 .data
1890 .iter()
1891 .filter(|entry| entry.output_enable() != oe_active)
1892 .count();
1893 assert_eq!(oe_blank_count, blank_count);
1894 }
1895 }
1896 }
1897
1898 #[test]
1899 fn test_row_clear_colors() {
1900 let mut row: Row<TEST_COLS> = Row::new();
1901 row.format(5);
1902
1903 row.set_color0(0, true, false, true);
1905 row.set_color1(1, false, true, false);
1906
1907 let mapped_col_0 = map_index(0);
1908 let mapped_col_1 = map_index(1);
1909
1910 assert_eq!(row.data[mapped_col_0].red1(), true);
1912 assert_eq!(row.data[mapped_col_0].blu1(), true);
1913 assert_eq!(row.data[mapped_col_1].grn2(), true);
1914
1915 let original_oe_0 = row.data[mapped_col_0].output_enable();
1917 let original_latch_0 = row.data[mapped_col_0].latch();
1918 let original_oe_1 = row.data[mapped_col_1].output_enable();
1919 let original_latch_1 = row.data[mapped_col_1].latch();
1920
1921 row.clear_colors();
1923
1924 assert_eq!(row.data[mapped_col_0].red1(), false);
1926 assert_eq!(row.data[mapped_col_0].grn1(), false);
1927 assert_eq!(row.data[mapped_col_0].blu1(), false);
1928 assert_eq!(row.data[mapped_col_1].red2(), false);
1929 assert_eq!(row.data[mapped_col_1].grn2(), false);
1930 assert_eq!(row.data[mapped_col_1].blu2(), false);
1931
1932 assert_eq!(row.data[mapped_col_0].output_enable(), original_oe_0);
1934 assert_eq!(row.data[mapped_col_0].latch(), original_latch_0);
1935 assert_eq!(row.data[mapped_col_1].output_enable(), original_oe_1);
1936 assert_eq!(row.data[mapped_col_1].latch(), original_latch_1);
1937 }
1938
1939 #[test]
1940 fn test_make_addr_table_function() {
1941 let table = make_addr_table();
1943
1944 assert_eq!(table.len(), 32); let addr_0 = &table[0];
1949 assert_eq!(addr_0.len(), 4); let latch_false_count = addr_0.iter().filter(|addr| !addr.latch()).count();
1953 assert_eq!(latch_false_count, 1);
1954
1955 for addr in addr_0 {
1957 assert_eq!(addr.addr(), 0);
1958 }
1959
1960 let addr_31 = &table[31];
1962 let latch_false_count = addr_31.iter().filter(|addr| !addr.latch()).count();
1963 assert_eq!(latch_false_count, 1);
1964
1965 for addr in addr_31 {
1967 assert_eq!(addr.addr(), 31);
1968 }
1969 }
1970
1971 #[test]
1972 fn test_make_data_template_function() {
1973 let template = make_data_template::<TEST_COLS>();
1975
1976 assert_eq!(template.len(), TEST_COLS);
1978
1979 for entry in &template {
1981 assert_eq!(entry.latch(), false);
1982 }
1983
1984 let oe_active = !cfg!(feature = "invert-oe");
1985 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1986 let blank_count = TEST_COLS - active_count;
1987 let oe_blank_count = template
1988 .iter()
1989 .filter(|entry| entry.output_enable() != oe_active)
1990 .count();
1991 assert_eq!(oe_blank_count, blank_count);
1992
1993 let small_template = make_data_template::<4>();
1995 assert_eq!(small_template.len(), 4);
1996
1997 let small_active = 4_usize.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
1998 let small_blank = 4 - small_active;
1999 let oe_blank_count = small_template
2000 .iter()
2001 .filter(|entry| entry.output_enable() != oe_active)
2002 .count();
2003 assert_eq!(oe_blank_count, small_blank);
2004
2005 #[cfg(not(feature = "esp32-ordering"))]
2008 {
2009 let single_template = make_data_template::<1>();
2010 assert_eq!(single_template.len(), 1);
2011 assert_eq!(single_template[0].output_enable(), !oe_active);
2012 assert_eq!(single_template[0].latch(), false);
2013 }
2014 }
2015
2016 #[test]
2017 fn test_addr_table_correctness() {
2018 for addr in 0..32 {
2020 let mut expected_addresses = [Address::new(); 4];
2021
2022 for i in 0..4 {
2024 let latch = !matches!(i, 3);
2025 #[cfg(feature = "esp32-ordering")]
2026 let mapped_i = map_index(i);
2027 #[cfg(not(feature = "esp32-ordering"))]
2028 let mapped_i = i;
2029
2030 expected_addresses[mapped_i].set_latch(latch);
2031 expected_addresses[mapped_i].set_addr(addr);
2032 expected_addresses[mapped_i].0 |= OE_BLANK;
2033 }
2034
2035 let table_addresses = &ADDR_TABLE[addr as usize];
2037 for i in 0..4 {
2038 assert_eq!(table_addresses[i].0, expected_addresses[i].0);
2039 }
2040 }
2041 }
2042
2043 const CHAR_W: i32 = 6;
2045 const CHAR_H: i32 = 10;
2046
2047 fn verify_glyph_at(fb: &mut TestFrameBuffer, origin: Point) {
2050 use embedded_graphics::mock_display::MockDisplay;
2051 use embedded_graphics::mono_font::ascii::FONT_6X10;
2052 use embedded_graphics::mono_font::MonoTextStyle;
2053 use embedded_graphics::text::{Baseline, Text};
2054
2055 let style = MonoTextStyle::new(&FONT_6X10, Color::WHITE);
2057 Text::with_baseline("A", origin, style, Baseline::Top)
2058 .draw(fb)
2059 .unwrap();
2060
2061 let mut reference: MockDisplay<Color> = MockDisplay::new();
2063 Text::with_baseline("A", Point::zero(), style, Baseline::Top)
2064 .draw(&mut reference)
2065 .unwrap();
2066
2067 for dy in 0..CHAR_H {
2069 for dx in 0..CHAR_W {
2070 let expected_on = reference
2071 .get_pixel(Point::new(dx, dy))
2072 .unwrap_or(Color::BLACK)
2073 != Color::BLACK;
2074
2075 let gx = (origin.x + dx) as usize;
2076 let gy = (origin.y + dy) as usize;
2077
2078 let frame0 = &fb.frames[0];
2085 let e = if gy < TEST_NROWS {
2086 &frame0.rows[get_mapped_row(gy)].data[map_index(gx)]
2087 } else {
2088 &frame0.rows[get_mapped_row(gy - TEST_NROWS)].data[map_index(gx)]
2089 };
2090
2091 let (r, g, b) = if gy >= TEST_NROWS {
2092 (e.red2(), e.grn2(), e.blu2())
2093 } else {
2094 (e.red1(), e.grn1(), e.blu1())
2095 };
2096
2097 if expected_on {
2098 assert!(r && g && b);
2099 } else {
2100 assert!(!r && !g && !b);
2101 }
2102 }
2103 }
2104 }
2105
2106 #[test]
2107 fn test_draw_char_corners() {
2108 let upper_left = Point::new(0, 0);
2110 let lower_right = Point::new(TEST_COLS as i32 - CHAR_W, TEST_ROWS as i32 - CHAR_H);
2111
2112 let mut fb = TestFrameBuffer::new();
2113
2114 verify_glyph_at(&mut fb, upper_left);
2116 verify_glyph_at(&mut fb, lower_right);
2118 }
2119
2120 #[test]
2121 fn test_framebuffer_operations_trait_erase() {
2122 let mut fb = TestFrameBuffer::new();
2123
2124 fb.set_pixel_internal(10, 5, Color::RED);
2126 fb.set_pixel_internal(20, 10, Color::GREEN);
2127
2128 <TestFrameBuffer as FrameBufferOperations>::erase(&mut fb);
2130
2131 let mc10 = map_index(10);
2133 let mc20 = map_index(20);
2134 assert_eq!(
2135 fb.frames[0].rows[get_mapped_row(5)].data[mc10].red1(),
2136 false
2137 );
2138 assert_eq!(
2139 fb.frames[0].rows[get_mapped_row(10)].data[mc20].grn1(),
2140 false
2141 );
2142
2143 let oe_active = !cfg!(feature = "invert-oe");
2145 let active_count = TEST_COLS.saturating_sub(LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 1);
2146 let blank_count = TEST_COLS - active_count;
2147 let row0 = &fb.frames[0].rows[get_mapped_row(0)];
2148 let oe_blank_count = row0
2149 .data
2150 .iter()
2151 .filter(|entry| entry.output_enable() != oe_active)
2152 .count();
2153 assert_eq!(oe_blank_count, blank_count);
2154 assert!(row0.data.iter().all(|e| !e.latch()));
2155
2156 for (i, addr) in row0.address.iter().enumerate() {
2158 assert_eq!(addr.0, ADDR_TABLE[0][i].0);
2159 }
2160 }
2161
2162 #[test]
2163 fn test_framebuffer_operations_trait_set_pixel() {
2164 let mut fb = TestFrameBuffer::new();
2165
2166 <TestFrameBuffer as FrameBufferOperations>::set_pixel(
2168 &mut fb,
2169 Point::new(8, 3),
2170 Color::BLUE,
2171 );
2172
2173 let idx = map_index(8);
2175 assert_eq!(fb.frames[0].rows[get_mapped_row(3)].data[idx].blu1(), true);
2176 assert_eq!(fb.frames[0].rows[get_mapped_row(3)].data[idx].red1(), false);
2178 assert_eq!(fb.frames[0].rows[get_mapped_row(3)].data[idx].grn1(), false);
2179 }
2180
2181 #[test]
2182 fn test_blanking_delay() {
2183 let mut row: Row<TEST_COLS> = Row::new();
2184 row.format(5);
2185
2186 let oe_active = !cfg!(feature = "invert-oe");
2187
2188 if TRAIL_BLANK_DELAY > 0 {
2190 let trail_blank_idx = map_index(TRAIL_BLANK_DELAY - 1);
2191 assert_eq!(row.data[trail_blank_idx].output_enable(), !oe_active);
2192 }
2193
2194 let first_active_idx = map_index(TRAIL_BLANK_DELAY);
2195 assert_eq!(row.data[first_active_idx].output_enable(), oe_active);
2196
2197 let last_active_idx = map_index(TEST_COLS - LEAD_BLANK_DELAY - 2);
2199 assert_eq!(row.data[last_active_idx].output_enable(), oe_active);
2200
2201 let lead_blank_idx = map_index(TEST_COLS - LEAD_BLANK_DELAY - 1);
2202 assert_eq!(row.data[lead_blank_idx].output_enable(), !oe_active);
2203
2204 let last_pixel_idx = map_index(TEST_COLS - 1);
2205 assert_eq!(row.data[last_pixel_idx].output_enable(), !oe_active);
2206 }
2207
2208 static STATIC_FB: TestFrameBuffer = TestFrameBuffer::new();
2209
2210 #[test]
2211 fn test_static_construction_is_formatted() {
2212 let runtime_fb = TestFrameBuffer::new();
2213
2214 for (fi, frame) in STATIC_FB.frames.iter().enumerate() {
2215 for (ri, row) in frame.rows.iter().enumerate() {
2216 assert_eq!(
2217 row.data, runtime_fb.frames[fi].rows[ri].data,
2218 "static vs runtime data mismatch at frame {fi}, row {ri}"
2219 );
2220 assert_eq!(
2221 row.address, runtime_fb.frames[fi].rows[ri].address,
2222 "static vs runtime address mismatch at frame {fi}, row {ri}"
2223 );
2224 assert_eq!(
2225 row.gap, runtime_fb.frames[fi].rows[ri].gap,
2226 "static vs runtime gap mismatch at frame {fi}, row {ri}"
2227 );
2228 }
2229 }
2230 }
2231
2232 #[test]
2233 fn test_format_reinitializes_at_runtime() {
2234 let mut fb = TestFrameBuffer::new();
2235 fb.erase();
2236 fb.format();
2237
2238 for (fi, frame) in fb.frames.iter().enumerate() {
2239 for (ri, row) in frame.rows.iter().enumerate() {
2240 assert_eq!(
2241 row.data, STATIC_FB.frames[fi].rows[ri].data,
2242 "re-formatted vs static mismatch at frame {fi}, row {ri}"
2243 );
2244 }
2245 }
2246 }
2247
2248 #[test]
2249 fn bcm_segment_count_is_one() {
2250 use crate::FrameBuffer;
2251 let fb = TestFrameBuffer::new();
2252 assert_eq!(fb.bcm_segment_count(), 1);
2253 }
2254
2255 #[test]
2256 fn bcm_segment_covers_entire_buffer() {
2257 use crate::FrameBuffer;
2258 let fb = TestFrameBuffer::new();
2259 let seg = fb.bcm_segment(0);
2260 let expected_ptr = (&raw const fb.frames).cast::<u8>();
2261 let expected_len = core::mem::size_of_val(&fb.frames);
2262 assert_eq!(seg.ptr, expected_ptr);
2263 assert_eq!(seg.len, expected_len);
2264 assert_eq!(seg.reps, 1);
2265 }
2266
2267 #[test]
2268 #[should_panic(expected = "out of range")]
2269 fn bcm_segment_panics_for_invalid_index() {
2270 use crate::FrameBuffer;
2271 let fb = TestFrameBuffer::new();
2272 let _ = fb.bcm_segment(1);
2273 }
2274
2275 #[test]
2276 fn bcm_sequence_matches_runtime_segments() {
2277 use crate::FrameBuffer;
2278 let fb = TestFrameBuffer::new();
2279 assert_eq!(
2280 TestFrameBuffer::BCM_SEGMENT_COUNT,
2281 TestFrameBuffer::BCM_SEQUENCE_LEN * TestFrameBuffer::BCM_SEQUENCE_COUNT
2282 );
2283 assert_eq!(fb.bcm_segment_count(), TestFrameBuffer::BCM_SEGMENT_COUNT);
2284 for i in 0..TestFrameBuffer::BCM_SEGMENT_COUNT {
2285 let entry = TestFrameBuffer::BCM_SEQUENCE[i % TestFrameBuffer::BCM_SEQUENCE_LEN];
2286 let seg = fb.bcm_segment(i);
2287 assert_eq!((seg.len, seg.reps), (entry.len, entry.reps), "segment {i}");
2288 assert!(!seg.ptr.is_null(), "segment {i} has null pointer");
2289 }
2290 for entry in &TestFrameBuffer::BCM_SEQUENCE[TestFrameBuffer::BCM_SEQUENCE_LEN..] {
2291 assert_eq!(*entry, crate::BcmLenReps::ZERO, "padding must be zero");
2292 }
2293 }
2294}