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 = "blank-delay-1")]
183const BLANKING_DELAY: usize = 1;
184#[cfg(feature = "blank-delay-2")]
185const BLANKING_DELAY: usize = 2;
186#[cfg(feature = "blank-delay-4")]
187const BLANKING_DELAY: usize = 4;
188#[cfg(feature = "blank-delay-8")]
189const BLANKING_DELAY: usize = 8;
190
191#[cfg(not(any(
192 feature = "blank-delay-1",
193 feature = "blank-delay-2",
194 feature = "blank-delay-4",
195 feature = "blank-delay-8"
196)))]
197const BLANKING_DELAY: usize = 0;
198
199#[cfg(not(feature = "invert-oe"))]
200const OE_ACTIVE: u8 = 0b1000_0000;
201#[cfg(not(feature = "invert-oe"))]
202const OE_BLANK: u8 = 0;
203
204#[cfg(feature = "invert-oe")]
205const OE_ACTIVE: u8 = 0;
206#[cfg(feature = "invert-oe")]
207const OE_BLANK: u8 = 0b1000_0000;
208
209bitfield! {
210 #[derive(Clone, Copy, Default, PartialEq, Eq)]
245 #[repr(transparent)]
246 struct Address(u8);
247 impl Debug;
248 pub output_enable, set_output_enable: 7;
249 pub latch, set_latch: 6;
250 pub addr, set_addr: 4, 0;
251}
252
253impl Address {
254 pub const fn new() -> Self {
255 Self(0)
256 }
257}
258
259bitfield! {
260 #[derive(Clone, Copy, Default, PartialEq)]
277 #[repr(transparent)]
278 struct Entry(u8);
279 impl Debug;
280 pub output_enable, set_output_enable: 7;
281 pub latch, set_latch: 6;
282 pub blu2, set_blu2: 5;
283 pub grn2, set_grn2: 4;
284 pub red2, set_red2: 3;
285 pub blu1, set_blu1: 2;
286 pub grn1, set_grn1: 1;
287 pub red1, set_red1: 0;
288}
289
290impl Entry {
291 pub const fn new() -> Self {
292 Self(0)
293 }
294
295 const COLOR0_MASK: u8 = 0b0000_0111; const COLOR1_MASK: u8 = 0b0011_1000; #[inline]
300 fn set_color0_bits(&mut self, bits: u8) {
301 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits & Self::COLOR0_MASK);
302 }
303
304 #[inline]
305 fn set_color1_bits(&mut self, bits: u8) {
306 self.0 = (self.0 & !Self::COLOR1_MASK) | ((bits << 3) & Self::COLOR1_MASK);
307 }
308}
309
310#[derive(Clone, Copy, PartialEq, Debug)]
321#[repr(C)]
322struct Row<const COLS: usize> {
323 data: [Entry; COLS],
324 address: [Address; 4],
325}
326
327#[inline]
329const fn map_index(index: usize) -> usize {
330 #[cfg(feature = "esp32-ordering")]
331 {
332 index ^ 2
333 }
334 #[cfg(not(feature = "esp32-ordering"))]
335 {
336 index
337 }
338}
339
340const fn make_addr_table() -> [[Address; 4]; 32] {
343 let mut tbl = [[Address::new(); 4]; 32];
344 let mut addr = 0;
345 while addr < 32 {
346 let mut i = 0;
347 while i < 4 {
348 let latch = i != 3;
349 let mapped_i = map_index(i);
350 let latch_bit = if latch { 1u8 << 6 } else { 0u8 };
351 tbl[addr][mapped_i].0 = latch_bit | addr as u8;
352 i += 1;
353 }
354 addr += 1;
355 }
356 tbl
357}
358
359static ADDR_TABLE: [[Address; 4]; 32] = make_addr_table();
360
361#[cfg_attr(
364 not(any(
365 feature = "blank-delay-1",
366 feature = "blank-delay-2",
367 feature = "blank-delay-4",
368 feature = "blank-delay-8"
369 )),
370 allow(clippy::absurd_extreme_comparisons)
371)]
372const fn make_data_template<const COLS: usize>() -> [Entry; COLS] {
373 let mut data = [Entry::new(); COLS];
374 let mut i = 0;
375 while i < COLS {
376 let mapped_i = map_index(i);
377 data[mapped_i].0 = if i >= BLANKING_DELAY && i < COLS - BLANKING_DELAY - 1 {
378 OE_ACTIVE
379 } else {
380 OE_BLANK
381 };
382 i += 1;
383 }
384 data
385}
386
387impl<const COLS: usize> Row<COLS> {
388 pub const fn new() -> Self {
389 Self {
390 address: [Address::new(); 4],
391 data: [Entry::new(); COLS],
392 }
393 }
394
395 #[inline]
396 pub fn format(&mut self, addr: u8) {
397 self.address.copy_from_slice(&ADDR_TABLE[addr as usize]);
399
400 let data_template = make_data_template::<COLS>();
402 self.data.copy_from_slice(&data_template);
403 }
404
405 #[inline]
407 pub fn clear_colors(&mut self) {
408 const COLOR_CLEAR_MASK: u8 = !0b0011_1111; for entry in &mut self.data {
412 entry.0 &= COLOR_CLEAR_MASK;
413 }
414 }
415
416 #[inline]
417 pub fn set_color0(&mut self, col: usize, r: bool, g: bool, b: bool) {
418 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
419 let col = map_index(col);
420 self.data[col].set_color0_bits(bits);
421 }
422
423 #[inline]
424 pub fn set_color1(&mut self, col: usize, r: bool, g: bool, b: bool) {
425 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
426 let col = map_index(col);
427 self.data[col].set_color1_bits(bits);
428 }
429}
430
431impl<const COLS: usize> Default for Row<COLS> {
432 fn default() -> Self {
433 Self::new()
434 }
435}
436
437#[derive(Copy, Clone, Debug)]
438#[repr(C)]
439struct Frame<const ROWS: usize, const COLS: usize, const NROWS: usize> {
440 rows: [Row<COLS>; NROWS],
441}
442
443impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Frame<ROWS, COLS, NROWS> {
444 pub const fn new() -> Self {
445 Self {
446 rows: [Row::new(); NROWS],
447 }
448 }
449
450 #[inline]
451 pub fn format(&mut self) {
452 for (addr, row) in self.rows.iter_mut().enumerate() {
453 row.format(addr as u8);
454 }
455 }
456
457 #[inline]
459 pub fn clear_colors(&mut self) {
460 for row in &mut self.rows {
461 row.clear_colors();
462 }
463 }
464
465 #[inline]
466 pub fn set_pixel(&mut self, y: usize, x: usize, red: bool, green: bool, blue: bool) {
467 let row = &mut self.rows[if y < NROWS { y } else { y - NROWS }];
468 if y < NROWS {
469 row.set_color0(x, red, green, blue);
470 } else {
471 row.set_color1(x, red, green, blue);
472 }
473 }
474}
475
476impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Default
477 for Frame<ROWS, COLS, NROWS>
478{
479 fn default() -> Self {
480 Self::new()
481 }
482}
483
484#[derive(Copy, Clone)]
513#[repr(C)]
514#[repr(align(4))]
515pub struct DmaFrameBuffer<
516 const ROWS: usize,
517 const COLS: usize,
518 const NROWS: usize,
519 const BITS: u8,
520 const FRAME_COUNT: usize,
521> {
522 frames: [Frame<ROWS, COLS, NROWS>; FRAME_COUNT],
523}
524
525impl<
526 const ROWS: usize,
527 const COLS: usize,
528 const NROWS: usize,
529 const BITS: u8,
530 const FRAME_COUNT: usize,
531 > Default for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
532{
533 fn default() -> Self {
534 Self::new()
535 }
536}
537
538impl<
539 const ROWS: usize,
540 const COLS: usize,
541 const NROWS: usize,
542 const BITS: u8,
543 const FRAME_COUNT: usize,
544 > DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
545{
546 #[must_use]
562 pub fn new() -> Self {
563 let mut fb = Self {
564 frames: [Frame::new(); FRAME_COUNT],
565 };
566 fb.format();
567 fb
568 }
569
570 #[must_use]
573 pub const fn bcm_chunk_count() -> usize {
574 1
575 }
576
577 #[must_use]
580 pub const fn bcm_chunk_bytes() -> usize {
581 core::mem::size_of::<[Frame<ROWS, COLS, NROWS>; FRAME_COUNT]>()
582 }
583
584 pub fn format(&mut self) {
601 for frame in &mut self.frames {
602 frame.format();
603 }
604 }
605
606 #[inline]
623 pub fn erase(&mut self) {
624 for frame in &mut self.frames {
625 frame.clear_colors();
626 }
627 }
628
629 pub fn set_pixel(&mut self, p: Point, color: Color) {
645 if p.x < 0 || p.y < 0 {
646 return;
647 }
648 self.set_pixel_internal(p.x as usize, p.y as usize, color);
649 }
650
651 #[inline]
652 fn frames_on(v: u8) -> usize {
653 (v as usize) >> (8 - BITS)
655 }
656
657 #[inline]
658 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Rgb888) {
659 if x >= COLS || y >= ROWS {
660 return;
661 }
662
663 #[cfg(feature = "skip-black-pixels")]
666 if color == Rgb888::BLACK {
667 return;
668 }
669
670 let red_frames = Self::frames_on(color.r());
672 let green_frames = Self::frames_on(color.g());
673 let blue_frames = Self::frames_on(color.b());
674
675 for (frame_idx, frame) in self.frames.iter_mut().enumerate() {
677 frame.set_pixel(
678 y,
679 x,
680 frame_idx < red_frames,
681 frame_idx < green_frames,
682 frame_idx < blue_frames,
683 );
684 }
685 }
686}
687
688impl<
689 const ROWS: usize,
690 const COLS: usize,
691 const NROWS: usize,
692 const BITS: u8,
693 const FRAME_COUNT: usize,
694 > FrameBufferOperations for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
695{
696 #[inline]
697 fn erase(&mut self) {
698 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::erase(self);
699 }
700
701 #[inline]
702 fn set_pixel(&mut self, p: Point, color: Color) {
703 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::set_pixel(self, p, color);
704 }
705}
706
707impl<
708 const ROWS: usize,
709 const COLS: usize,
710 const NROWS: usize,
711 const BITS: u8,
712 const FRAME_COUNT: usize,
713 > embedded_graphics::prelude::OriginDimensions
714 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
715{
716 fn size(&self) -> embedded_graphics::prelude::Size {
717 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
718 }
719}
720
721impl<
722 const ROWS: usize,
723 const COLS: usize,
724 const NROWS: usize,
725 const BITS: u8,
726 const FRAME_COUNT: usize,
727 > embedded_graphics::draw_target::DrawTarget
728 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
729{
730 type Color = Color;
731
732 type Error = Infallible;
733
734 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
735 where
736 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
737 {
738 for pixel in pixels {
739 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
740 }
741 Ok(())
742 }
743}
744
745unsafe impl<
746 const ROWS: usize,
747 const COLS: usize,
748 const NROWS: usize,
749 const BITS: u8,
750 const FRAME_COUNT: usize,
751 > ReadBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
752{
753 type Word = u8;
754
755 unsafe fn read_buffer(&self) -> (*const u8, usize) {
756 let ptr = (&raw const self.frames).cast::<u8>();
757 let len = core::mem::size_of_val(&self.frames);
758 (ptr, len)
759 }
760}
761
762unsafe impl<
763 const ROWS: usize,
764 const COLS: usize,
765 const NROWS: usize,
766 const BITS: u8,
767 const FRAME_COUNT: usize,
768 > ReadBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
769{
770 type Word = u8;
771
772 unsafe fn read_buffer(&self) -> (*const u8, usize) {
773 let ptr = (&raw const self.frames).cast::<u8>();
774 let len = core::mem::size_of_val(&self.frames);
775 (ptr, len)
776 }
777}
778
779impl<
780 const ROWS: usize,
781 const COLS: usize,
782 const NROWS: usize,
783 const BITS: u8,
784 const FRAME_COUNT: usize,
785 > core::fmt::Debug for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
786{
787 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
788 let brightness_step = 1 << (8 - BITS);
789 f.debug_struct("DmaFrameBuffer")
790 .field("size", &core::mem::size_of_val(&self.frames))
791 .field("frame_count", &self.frames.len())
792 .field("frame_size", &core::mem::size_of_val(&self.frames[0]))
793 .field("brightness_step", &&brightness_step)
794 .finish()
795 }
796}
797
798#[cfg(feature = "defmt")]
799impl<
800 const ROWS: usize,
801 const COLS: usize,
802 const NROWS: usize,
803 const BITS: u8,
804 const FRAME_COUNT: usize,
805 > defmt::Format for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
806{
807 fn format(&self, f: defmt::Formatter) {
808 let brightness_step = 1 << (8 - BITS);
809 defmt::write!(
810 f,
811 "DmaFrameBuffer<{}, {}, {}, {}, {}>",
812 ROWS,
813 COLS,
814 NROWS,
815 BITS,
816 FRAME_COUNT
817 );
818 defmt::write!(f, " size: {}", core::mem::size_of_val(&self.frames));
819 defmt::write!(
820 f,
821 " frame_size: {}",
822 core::mem::size_of_val(&self.frames[0])
823 );
824 defmt::write!(f, " brightness_step: {}", brightness_step);
825 }
826}
827
828impl<
829 const ROWS: usize,
830 const COLS: usize,
831 const NROWS: usize,
832 const BITS: u8,
833 const FRAME_COUNT: usize,
834 > super::FrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
835{
836 type Word = u8;
837
838 fn plane_count(&self) -> usize {
839 1
840 }
841
842 fn plane_ptr_len(&self, plane_idx: usize) -> (*const u8, usize) {
843 assert!(plane_idx == 0, "latched DmaFrameBuffer has only 1 plane");
844 let ptr = (&raw const self.frames).cast::<u8>();
845 let len = core::mem::size_of_val(&self.frames);
846 (ptr, len)
847 }
848}
849
850impl<
851 const ROWS: usize,
852 const COLS: usize,
853 const NROWS: usize,
854 const BITS: u8,
855 const FRAME_COUNT: usize,
856 > embedded_graphics::prelude::OriginDimensions
857 for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
858{
859 fn size(&self) -> embedded_graphics::prelude::Size {
860 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
861 }
862}
863
864impl<
865 const ROWS: usize,
866 const COLS: usize,
867 const NROWS: usize,
868 const BITS: u8,
869 const FRAME_COUNT: usize,
870 > MutableFrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
871{
872}
873
874#[cfg(test)]
875mod tests {
876 extern crate std;
877
878 use std::format;
879 use std::vec;
880
881 use super::*;
882 use crate::{FrameBuffer, WordSize};
883 use embedded_graphics::pixelcolor::RgbColor;
884 use embedded_graphics::prelude::*;
885 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle};
886
887 const TEST_ROWS: usize = 32;
888 const TEST_COLS: usize = 64;
889 const TEST_NROWS: usize = TEST_ROWS / 2;
890 const TEST_BITS: u8 = 3;
891 const TEST_FRAME_COUNT: usize = (1 << TEST_BITS) - 1; type TestFrameBuffer =
894 DmaFrameBuffer<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_BITS, TEST_FRAME_COUNT>;
895
896 #[test]
897 fn test_address_construction() {
898 let addr = Address::new();
899 assert_eq!(addr.0, 0);
900 assert_eq!(addr.latch(), false);
901 assert_eq!(addr.addr(), 0);
902 }
903
904 #[test]
905 fn test_address_setters() {
906 let mut addr = Address::new();
907
908 addr.set_latch(true);
909 assert_eq!(addr.latch(), true);
910 assert_eq!(addr.0 & 0b01000000, 0b01000000);
911
912 addr.set_addr(0b11111);
913 assert_eq!(addr.addr(), 0b11111);
914 assert_eq!(addr.0 & 0b00011111, 0b00011111);
915 }
916
917 #[test]
918 fn test_address_bit_isolation() {
919 let mut addr = Address::new();
920
921 addr.set_addr(0b11111);
923 addr.set_latch(true);
924 assert_eq!(addr.addr(), 0b11111);
925 assert_eq!(addr.latch(), true);
926 }
927
928 #[test]
929 fn test_entry_construction() {
930 let entry = Entry::new();
931 assert_eq!(entry.0, 0);
932 assert_eq!(entry.output_enable(), false);
933 assert_eq!(entry.latch(), false);
934 assert_eq!(entry.red1(), false);
935 assert_eq!(entry.grn1(), false);
936 assert_eq!(entry.blu1(), false);
937 assert_eq!(entry.red2(), false);
938 assert_eq!(entry.grn2(), false);
939 assert_eq!(entry.blu2(), false);
940 }
941
942 #[test]
943 fn test_entry_setters() {
944 let mut entry = Entry::new();
945
946 entry.set_output_enable(true);
947 assert_eq!(entry.output_enable(), true);
948 assert_eq!(entry.0 & 0b10000000, 0b10000000);
949
950 entry.set_latch(true);
951 assert_eq!(entry.latch(), true);
952 assert_eq!(entry.0 & 0b01000000, 0b01000000);
953
954 entry.set_red1(true);
956 entry.set_grn1(true);
957 entry.set_blu1(true);
958 assert_eq!(entry.red1(), true);
959 assert_eq!(entry.grn1(), true);
960 assert_eq!(entry.blu1(), true);
961 assert_eq!(entry.0 & 0b00000111, 0b00000111);
962
963 entry.set_red2(true);
965 entry.set_grn2(true);
966 entry.set_blu2(true);
967 assert_eq!(entry.red2(), true);
968 assert_eq!(entry.grn2(), true);
969 assert_eq!(entry.blu2(), true);
970 assert_eq!(entry.0 & 0b00111000, 0b00111000);
971 }
972
973 #[test]
974 fn test_entry_set_color0() {
975 let mut entry = Entry::new();
976
977 let bits = (u8::from(true) << 2) | (u8::from(false) << 1) | u8::from(true); entry.set_color0_bits(bits);
979 assert_eq!(entry.red1(), true);
980 assert_eq!(entry.grn1(), false);
981 assert_eq!(entry.blu1(), true);
982 assert_eq!(entry.0 & 0b00000111, 0b00000101); }
984
985 #[test]
986 fn test_entry_set_color1() {
987 let mut entry = Entry::new();
988
989 let bits = (u8::from(true) << 2) | (u8::from(true) << 1) | u8::from(false); entry.set_color1_bits(bits);
991 assert_eq!(entry.red2(), false);
992 assert_eq!(entry.grn2(), true);
993 assert_eq!(entry.blu2(), true);
994 assert_eq!(entry.0 & 0b00111000, 0b00110000); }
996
997 #[test]
998 fn test_row_construction() {
999 let row: Row<TEST_COLS> = Row::new();
1000 assert_eq!(row.data.len(), TEST_COLS);
1001 assert_eq!(row.address.len(), 4);
1002
1003 for entry in &row.data {
1005 assert_eq!(entry.0, 0);
1006 }
1007 for addr in &row.address {
1008 assert_eq!(addr.0, 0);
1009 }
1010 }
1011
1012 #[test]
1013 fn test_row_format() {
1014 let mut row: Row<TEST_COLS> = Row::new();
1015 let test_addr = 5;
1016
1017 row.format(test_addr);
1018
1019 for addr in &row.address {
1021 assert_eq!(addr.addr(), test_addr);
1022 }
1026 let latch_false_count = row.address.iter().filter(|addr| !addr.latch()).count();
1030 assert_eq!(latch_false_count, 1);
1031
1032 for entry in &row.data {
1034 assert_eq!(entry.latch(), false);
1035 }
1036 let oe_active = !cfg!(feature = "invert-oe");
1037 let active_count = TEST_COLS.saturating_sub(2 * BLANKING_DELAY + 1);
1038 let blank_count = TEST_COLS - active_count;
1039 let oe_blank_count = row
1040 .data
1041 .iter()
1042 .filter(|entry| entry.output_enable() != oe_active)
1043 .count();
1044 assert_eq!(oe_blank_count, blank_count);
1045 }
1046
1047 #[test]
1048 fn test_row_set_color0() {
1049 let mut row: Row<TEST_COLS> = Row::new();
1050
1051 row.set_color0(0, true, false, true);
1052
1053 let mapped_col_0 = map_index(0);
1054 assert_eq!(row.data[mapped_col_0].red1(), true);
1055 assert_eq!(row.data[mapped_col_0].grn1(), false);
1056 assert_eq!(row.data[mapped_col_0].blu1(), true);
1057
1058 row.set_color0(1, false, true, false);
1060
1061 let mapped_col_1 = map_index(1);
1062 assert_eq!(row.data[mapped_col_1].red1(), false);
1063 assert_eq!(row.data[mapped_col_1].grn1(), true);
1064 assert_eq!(row.data[mapped_col_1].blu1(), false);
1065 }
1066
1067 #[test]
1068 fn test_row_set_color1() {
1069 let mut row: Row<TEST_COLS> = Row::new();
1070
1071 row.set_color1(0, true, true, false);
1072
1073 let mapped_col_0 = map_index(0);
1074 assert_eq!(row.data[mapped_col_0].red2(), true);
1075 assert_eq!(row.data[mapped_col_0].grn2(), true);
1076 assert_eq!(row.data[mapped_col_0].blu2(), false);
1077 }
1078
1079 #[test]
1080 fn test_frame_construction() {
1081 let frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1082 assert_eq!(frame.rows.len(), TEST_NROWS);
1083 }
1084
1085 #[test]
1086 fn test_frame_format() {
1087 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1088
1089 frame.format();
1090
1091 for (addr, row) in frame.rows.iter().enumerate() {
1092 for address in &row.address {
1094 assert_eq!(address.addr() as usize, addr);
1095 }
1096 }
1097 }
1098
1099 #[test]
1100 fn test_frame_set_pixel() {
1101 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1102
1103 frame.set_pixel(5, 10, true, false, true);
1105
1106 let mapped_col_10 = map_index(10);
1107 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1108 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1109 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1110
1111 frame.set_pixel(TEST_NROWS + 5, 15, false, true, false);
1113
1114 let mapped_col_15 = map_index(15);
1115 assert_eq!(frame.rows[5].data[mapped_col_15].red2(), false);
1116 assert_eq!(frame.rows[5].data[mapped_col_15].grn2(), true);
1117 assert_eq!(frame.rows[5].data[mapped_col_15].blu2(), false);
1118 }
1119
1120 #[test]
1121 fn test_row_default() {
1122 let row1: Row<TEST_COLS> = Row::new();
1123 let row2: Row<TEST_COLS> = Row::default();
1124
1125 assert_eq!(row1, row2);
1127 assert_eq!(row1.data.len(), row2.data.len());
1128 assert_eq!(row1.address.len(), row2.address.len());
1129
1130 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1132 assert_eq!(entry1.0, entry2.0);
1133 assert_eq!(entry1.0, 0);
1134 }
1135 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1136 assert_eq!(addr1.0, addr2.0);
1137 assert_eq!(addr1.0, 0);
1138 }
1139 }
1140
1141 #[test]
1142 fn test_frame_default() {
1143 let frame1: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1144 let frame2: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::default();
1145
1146 assert_eq!(frame1.rows.len(), frame2.rows.len());
1148
1149 for (row1, row2) in frame1.rows.iter().zip(frame2.rows.iter()) {
1151 assert_eq!(row1, row2);
1152
1153 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1155 assert_eq!(entry1.0, entry2.0);
1156 assert_eq!(entry1.0, 0);
1157 }
1158 for (addr1, addr2) in row1.address.iter().zip(row2.address.iter()) {
1159 assert_eq!(addr1.0, addr2.0);
1160 assert_eq!(addr1.0, 0);
1161 }
1162 }
1163 }
1164
1165 #[test]
1166 fn test_dma_framebuffer_construction() {
1167 let fb = TestFrameBuffer::new();
1168 assert_eq!(fb.frames.len(), TEST_FRAME_COUNT);
1169 }
1170
1171 #[test]
1172 fn test_bcm_chunk_info() {
1173 let expected_size =
1174 core::mem::size_of::<[Frame<TEST_ROWS, TEST_COLS, TEST_NROWS>; TEST_FRAME_COUNT]>();
1175 assert_eq!(TestFrameBuffer::bcm_chunk_bytes(), expected_size);
1176 assert_eq!(TestFrameBuffer::bcm_chunk_count(), 1);
1177 }
1178
1179 #[test]
1180 fn test_dma_framebuffer_format() {
1181 let mut fb = TestFrameBuffer {
1182 frames: [Frame::new(); TEST_FRAME_COUNT],
1183 };
1184 fb.format();
1185
1186 for frame in &fb.frames {
1188 for (addr, row) in frame.rows.iter().enumerate() {
1189 for address in &row.address {
1190 assert_eq!(address.addr() as usize, addr);
1191 }
1192 }
1193 }
1194 }
1195
1196 #[test]
1197 fn test_dma_framebuffer_set_pixel_bounds() {
1198 let mut fb = TestFrameBuffer::new();
1199
1200 fb.set_pixel(Point::new(-1, 5), Color::RED);
1202 fb.set_pixel(Point::new(5, -1), Color::RED);
1203
1204 fb.set_pixel(Point::new(TEST_COLS as i32, 5), Color::RED);
1206 fb.set_pixel(Point::new(5, TEST_ROWS as i32), Color::RED);
1207 }
1208
1209 #[test]
1210 fn test_dma_framebuffer_set_pixel_internal() {
1211 let mut fb = TestFrameBuffer::new();
1212
1213 let red_color = Rgb888::new(255, 0, 0);
1214 fb.set_pixel_internal(10, 5, red_color);
1215
1216 for frame in &fb.frames {
1220 let mapped_col_10 = map_index(10);
1222 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1223 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1224 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), false);
1225 }
1226 }
1227
1228 #[test]
1229 fn test_dma_framebuffer_brightness_modulation() {
1230 let mut fb = TestFrameBuffer::new();
1231
1232 let brightness_step = 1 << (8 - TEST_BITS); let test_brightness = brightness_step * 3; let color = Rgb888::new(test_brightness, 0, 0);
1236
1237 fb.set_pixel_internal(0, 0, color);
1238
1239 for (frame_idx, frame) in fb.frames.iter().enumerate() {
1242 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1243 let should_be_active = test_brightness >= frame_threshold;
1244
1245 let mapped_col_0 = map_index(0);
1246 assert_eq!(frame.rows[0].data[mapped_col_0].red1(), should_be_active);
1247 }
1248 }
1249
1250 #[test]
1251 fn test_origin_dimensions() {
1252 let fb = TestFrameBuffer::new();
1253 let size = fb.size();
1254 assert_eq!(size.width, TEST_COLS as u32);
1255 assert_eq!(size.height, TEST_ROWS as u32);
1256
1257 let mut fb = TestFrameBuffer::new();
1259 let fb_ref = &mut fb;
1260 let size = fb_ref.size();
1261 assert_eq!(size.width, TEST_COLS as u32);
1262 assert_eq!(size.height, TEST_ROWS as u32);
1263 }
1264
1265 #[test]
1266 fn test_draw_target() {
1267 let mut fb = TestFrameBuffer::new();
1268
1269 let pixels = vec![
1270 embedded_graphics::Pixel(Point::new(0, 0), Color::RED),
1271 embedded_graphics::Pixel(Point::new(1, 1), Color::GREEN),
1272 embedded_graphics::Pixel(Point::new(2, 2), Color::BLUE),
1273 ];
1274
1275 let result = fb.draw_iter(pixels);
1276 assert!(result.is_ok());
1277 }
1278
1279 #[test]
1280 fn test_draw_iter_pixel_verification() {
1281 let mut fb = TestFrameBuffer::new();
1282
1283 let pixels = vec![
1285 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)), ];
1299
1300 let result = fb.draw_iter(pixels);
1301 assert!(result.is_ok());
1302
1303 let first_frame = &fb.frames[0];
1305 let brightness_step = 1 << (8 - TEST_BITS); let first_frame_threshold = brightness_step; let col_idx = map_index(5);
1311 assert_eq!(
1312 first_frame.rows[2].data[col_idx].red1(),
1313 Color::RED.r() >= first_frame_threshold
1314 );
1315 assert_eq!(
1316 first_frame.rows[2].data[col_idx].grn1(),
1317 Color::RED.g() >= first_frame_threshold
1318 );
1319 assert_eq!(
1320 first_frame.rows[2].data[col_idx].blu1(),
1321 Color::RED.b() >= first_frame_threshold
1322 );
1323
1324 let col_idx = map_index(10);
1326 assert_eq!(
1327 first_frame.rows[5].data[col_idx].red1(),
1328 Color::GREEN.r() >= first_frame_threshold
1329 );
1330 assert_eq!(
1331 first_frame.rows[5].data[col_idx].grn1(),
1332 Color::GREEN.g() >= first_frame_threshold
1333 );
1334 assert_eq!(
1335 first_frame.rows[5].data[col_idx].blu1(),
1336 Color::GREEN.b() >= first_frame_threshold
1337 );
1338
1339 let col_idx = map_index(15);
1341 assert_eq!(
1342 first_frame.rows[8].data[col_idx].red1(),
1343 Color::BLUE.r() >= first_frame_threshold
1344 );
1345 assert_eq!(
1346 first_frame.rows[8].data[col_idx].grn1(),
1347 Color::BLUE.g() >= first_frame_threshold
1348 );
1349 assert_eq!(
1350 first_frame.rows[8].data[col_idx].blu1(),
1351 Color::BLUE.b() >= first_frame_threshold
1352 );
1353
1354 let col_idx = map_index(20);
1356 assert_eq!(
1357 first_frame.rows[10].data[col_idx].red1(),
1358 Color::WHITE.r() >= first_frame_threshold
1359 );
1360 assert_eq!(
1361 first_frame.rows[10].data[col_idx].grn1(),
1362 Color::WHITE.g() >= first_frame_threshold
1363 );
1364 assert_eq!(
1365 first_frame.rows[10].data[col_idx].blu1(),
1366 Color::WHITE.b() >= first_frame_threshold
1367 );
1368
1369 let col_idx = map_index(25);
1372 assert_eq!(
1373 first_frame.rows[3].data[col_idx].red2(),
1374 Color::RED.r() >= first_frame_threshold
1375 );
1376 assert_eq!(
1377 first_frame.rows[3].data[col_idx].grn2(),
1378 Color::RED.g() >= first_frame_threshold
1379 );
1380 assert_eq!(
1381 first_frame.rows[3].data[col_idx].blu2(),
1382 Color::RED.b() >= first_frame_threshold
1383 );
1384
1385 let col_idx = map_index(30);
1387 assert_eq!(
1388 first_frame.rows[7].data[col_idx].red2(),
1389 Color::GREEN.r() >= first_frame_threshold
1390 );
1391 assert_eq!(
1392 first_frame.rows[7].data[col_idx].grn2(),
1393 Color::GREEN.g() >= first_frame_threshold
1394 );
1395 assert_eq!(
1396 first_frame.rows[7].data[col_idx].blu2(),
1397 Color::GREEN.b() >= first_frame_threshold
1398 );
1399
1400 let col_idx = map_index(35);
1402 assert_eq!(
1403 first_frame.rows[12].data[col_idx].red2(),
1404 Color::BLUE.r() >= first_frame_threshold
1405 );
1406 assert_eq!(
1407 first_frame.rows[12].data[col_idx].grn2(),
1408 Color::BLUE.g() >= first_frame_threshold
1409 );
1410 assert_eq!(
1411 first_frame.rows[12].data[col_idx].blu2(),
1412 Color::BLUE.b() >= first_frame_threshold
1413 );
1414
1415 let col_idx = map_index(40);
1417 assert_eq!(first_frame.rows[1].data[col_idx].red1(), false);
1418 assert_eq!(first_frame.rows[1].data[col_idx].grn1(), false);
1419 assert_eq!(first_frame.rows[1].data[col_idx].blu1(), false);
1420
1421 let col_idx = map_index(45);
1423 assert_eq!(
1424 first_frame.rows[3].data[col_idx].red1(),
1425 16 >= first_frame_threshold
1426 ); assert_eq!(
1428 first_frame.rows[3].data[col_idx].grn1(),
1429 16 >= first_frame_threshold
1430 ); assert_eq!(
1432 first_frame.rows[3].data[col_idx].blu1(),
1433 16 >= first_frame_threshold
1434 ); }
1436
1437 #[test]
1438 fn test_embedded_graphics_integration() {
1439 let mut fb = TestFrameBuffer::new();
1440
1441 let result = Rectangle::new(Point::new(5, 5), Size::new(10, 8))
1443 .into_styled(PrimitiveStyle::with_fill(Color::RED))
1444 .draw(&mut fb);
1445 assert!(result.is_ok());
1446
1447 let result = Circle::new(Point::new(30, 15), 8)
1449 .into_styled(PrimitiveStyle::with_fill(Color::BLUE))
1450 .draw(&mut fb);
1451 assert!(result.is_ok());
1452 }
1453
1454 #[test]
1455 fn test_read_buffer_implementation() {
1456 let fb = TestFrameBuffer::new();
1457
1458 unsafe {
1460 let (ptr, len) = fb.read_buffer();
1461 assert!(!ptr.is_null());
1462 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1463 }
1464
1465 let mut fb = TestFrameBuffer::new();
1467 let fb_ref = &mut fb;
1468 unsafe {
1469 let (ptr, len) = fb_ref.read_buffer();
1470 assert!(!ptr.is_null());
1471 assert_eq!(len, core::mem::size_of_val(&fb.frames));
1472 }
1473 }
1474
1475 #[test]
1476 fn test_framebuffer_trait() {
1477 let fb = TestFrameBuffer::new();
1478 assert_eq!(fb.get_word_size(), WordSize::Eight);
1479
1480 let mut fb = TestFrameBuffer::new();
1481 let fb_ref = &mut fb;
1482 assert_eq!(fb_ref.get_word_size(), WordSize::Eight);
1483 }
1484
1485 #[test]
1486 fn test_debug_formatting() {
1487 let fb = TestFrameBuffer::new();
1488 let debug_string = format!("{:?}", fb);
1489 assert!(debug_string.contains("DmaFrameBuffer"));
1490 assert!(debug_string.contains("frame_count"));
1491 assert!(debug_string.contains("frame_size"));
1492 assert!(debug_string.contains("brightness_step"));
1493 }
1494
1495 #[test]
1496 fn test_default_implementation() {
1497 let fb1 = TestFrameBuffer::new();
1498 let fb2 = TestFrameBuffer::default();
1499
1500 assert_eq!(fb1.frames.len(), fb2.frames.len());
1502 }
1503
1504 #[cfg(feature = "esp32-ordering")]
1505 #[test]
1506 fn test_esp32_mapping() {
1507 assert_eq!(map_index(0), 2);
1509 assert_eq!(map_index(1), 3);
1510 assert_eq!(map_index(2), 0);
1511 assert_eq!(map_index(3), 1);
1512 assert_eq!(map_index(4), 6); assert_eq!(map_index(5), 7); }
1515
1516 #[test]
1517 fn test_memory_alignment() {
1518 let fb = TestFrameBuffer::new();
1519 let ptr = &fb as *const _ as usize;
1520
1521 assert_eq!(ptr % 4, 0);
1523 }
1524
1525 #[test]
1526 fn test_color_values() {
1527 let mut fb = TestFrameBuffer::new();
1528
1529 let colors = [
1531 (Color::RED, (255, 0, 0)),
1532 (Color::GREEN, (0, 255, 0)),
1533 (Color::BLUE, (0, 0, 255)),
1534 (Color::WHITE, (255, 255, 255)),
1535 (Color::BLACK, (0, 0, 0)),
1536 ];
1537
1538 for (i, (color, (r, g, b))) in colors.iter().enumerate() {
1539 fb.set_pixel(Point::new(i as i32, 0), *color);
1540 assert_eq!(color.r(), *r);
1541 assert_eq!(color.g(), *g);
1542 assert_eq!(color.b(), *b);
1543 }
1544 }
1545
1546 #[test]
1547 fn test_bits_assertion() {
1548 assert!(TEST_BITS <= 8);
1551 }
1552
1553 #[test]
1554 #[cfg(feature = "skip-black-pixels")]
1555 fn test_skip_black_pixels_enabled() {
1556 let mut fb = TestFrameBuffer::new();
1557
1558 fb.set_pixel_internal(10, 5, Color::RED);
1560
1561 let mapped_col_10 = map_index(10);
1563 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1564 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1565 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1566
1567 fb.set_pixel_internal(10, 5, Color::BLACK);
1569
1570 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1572 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1573 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1574 }
1575
1576 #[test]
1577 #[cfg(not(feature = "skip-black-pixels"))]
1578 fn test_skip_black_pixels_disabled() {
1579 let mut fb = TestFrameBuffer::new();
1580
1581 fb.set_pixel_internal(10, 5, Color::RED);
1583
1584 let mapped_col_10 = map_index(10);
1586 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1587 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1588 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1589
1590 fb.set_pixel_internal(10, 5, Color::BLACK);
1592
1593 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1595 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1596 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1597 }
1598
1599 #[test]
1600 fn test_bcm_frame_overwrite() {
1601 let mut fb = TestFrameBuffer::new();
1602
1603 fb.set_pixel_internal(10, 5, Color::WHITE);
1605
1606 let mapped_col_10 = map_index(10);
1607
1608 for frame in fb.frames.iter() {
1610 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1612 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), true);
1613 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1614 }
1615
1616 let half_white = embedded_graphics::pixelcolor::Rgb888::new(128, 128, 128);
1618 fb.set_pixel_internal(10, 5, half_white);
1619
1620 let brightness_step = 1 << (8 - TEST_BITS); for (frame_idx, frame) in fb.frames.iter().enumerate() {
1626 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1627 let should_be_active = 128 >= frame_threshold;
1628
1629 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), should_be_active);
1630 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), should_be_active);
1631 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), should_be_active);
1632 }
1633
1634 for frame_idx in 0..4 {
1637 assert_eq!(
1638 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1639 true
1640 );
1641 }
1642 for frame_idx in 4..TEST_FRAME_COUNT {
1644 assert_eq!(
1645 fb.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1646 false
1647 );
1648 }
1649 }
1650
1651 #[test]
1652 fn test_new_auto_formats() {
1653 let fb = TestFrameBuffer::new();
1654
1655 for frame in &fb.frames {
1657 for (addr, row) in frame.rows.iter().enumerate() {
1658 for address in &row.address {
1659 assert_eq!(address.addr() as usize, addr);
1660 }
1661 }
1662 }
1663 }
1664
1665 #[test]
1666 fn test_erase() {
1667 let mut fb = TestFrameBuffer::new();
1668
1669 fb.set_pixel_internal(10, 5, Color::RED);
1671 fb.set_pixel_internal(20, 10, Color::GREEN);
1672
1673 let mapped_col_10 = map_index(10);
1674 let mapped_col_20 = map_index(20);
1675
1676 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), true);
1678 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), true);
1679
1680 fb.erase();
1682
1683 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].red1(), false);
1685 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1686 assert_eq!(fb.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1687 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].red1(), false);
1688 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].grn1(), false);
1689 assert_eq!(fb.frames[0].rows[10].data[mapped_col_20].blu1(), false);
1690
1691 let oe_active = !cfg!(feature = "invert-oe");
1693 let active_count = TEST_COLS.saturating_sub(2 * BLANKING_DELAY + 1);
1694 let blank_count = TEST_COLS - active_count;
1695 for frame in &fb.frames {
1696 for (addr, row) in frame.rows.iter().enumerate() {
1697 for address in &row.address {
1699 assert_eq!(address.addr() as usize, addr);
1700 }
1701 let oe_blank_count = row
1702 .data
1703 .iter()
1704 .filter(|entry| entry.output_enable() != oe_active)
1705 .count();
1706 assert_eq!(oe_blank_count, blank_count);
1707 }
1708 }
1709 }
1710
1711 #[test]
1712 fn test_row_clear_colors() {
1713 let mut row: Row<TEST_COLS> = Row::new();
1714 row.format(5);
1715
1716 row.set_color0(0, true, false, true);
1718 row.set_color1(1, false, true, false);
1719
1720 let mapped_col_0 = map_index(0);
1721 let mapped_col_1 = map_index(1);
1722
1723 assert_eq!(row.data[mapped_col_0].red1(), true);
1725 assert_eq!(row.data[mapped_col_0].blu1(), true);
1726 assert_eq!(row.data[mapped_col_1].grn2(), true);
1727
1728 let original_oe_0 = row.data[mapped_col_0].output_enable();
1730 let original_latch_0 = row.data[mapped_col_0].latch();
1731 let original_oe_1 = row.data[mapped_col_1].output_enable();
1732 let original_latch_1 = row.data[mapped_col_1].latch();
1733
1734 row.clear_colors();
1736
1737 assert_eq!(row.data[mapped_col_0].red1(), false);
1739 assert_eq!(row.data[mapped_col_0].grn1(), false);
1740 assert_eq!(row.data[mapped_col_0].blu1(), false);
1741 assert_eq!(row.data[mapped_col_1].red2(), false);
1742 assert_eq!(row.data[mapped_col_1].grn2(), false);
1743 assert_eq!(row.data[mapped_col_1].blu2(), false);
1744
1745 assert_eq!(row.data[mapped_col_0].output_enable(), original_oe_0);
1747 assert_eq!(row.data[mapped_col_0].latch(), original_latch_0);
1748 assert_eq!(row.data[mapped_col_1].output_enable(), original_oe_1);
1749 assert_eq!(row.data[mapped_col_1].latch(), original_latch_1);
1750 }
1751
1752 #[test]
1753 fn test_make_addr_table_function() {
1754 let table = make_addr_table();
1756
1757 assert_eq!(table.len(), 32); let addr_0 = &table[0];
1762 assert_eq!(addr_0.len(), 4); let latch_false_count = addr_0.iter().filter(|addr| !addr.latch()).count();
1766 assert_eq!(latch_false_count, 1);
1767
1768 for addr in addr_0 {
1770 assert_eq!(addr.addr(), 0);
1771 }
1772
1773 let addr_31 = &table[31];
1775 let latch_false_count = addr_31.iter().filter(|addr| !addr.latch()).count();
1776 assert_eq!(latch_false_count, 1);
1777
1778 for addr in addr_31 {
1780 assert_eq!(addr.addr(), 31);
1781 }
1782 }
1783
1784 #[test]
1785 fn test_make_data_template_function() {
1786 let template = make_data_template::<TEST_COLS>();
1788
1789 assert_eq!(template.len(), TEST_COLS);
1791
1792 for entry in &template {
1794 assert_eq!(entry.latch(), false);
1795 }
1796
1797 let oe_active = !cfg!(feature = "invert-oe");
1798 let active_count = TEST_COLS.saturating_sub(2 * BLANKING_DELAY + 1);
1799 let blank_count = TEST_COLS - active_count;
1800 let oe_blank_count = template
1801 .iter()
1802 .filter(|entry| entry.output_enable() != oe_active)
1803 .count();
1804 assert_eq!(oe_blank_count, blank_count);
1805
1806 let small_template = make_data_template::<4>();
1808 assert_eq!(small_template.len(), 4);
1809
1810 let small_active = 4_usize.saturating_sub(2 * BLANKING_DELAY + 1);
1811 let small_blank = 4 - small_active;
1812 let oe_blank_count = small_template
1813 .iter()
1814 .filter(|entry| entry.output_enable() != oe_active)
1815 .count();
1816 assert_eq!(oe_blank_count, small_blank);
1817
1818 #[cfg(not(feature = "esp32-ordering"))]
1821 {
1822 let single_template = make_data_template::<1>();
1823 assert_eq!(single_template.len(), 1);
1824 assert_eq!(single_template[0].output_enable(), !oe_active);
1825 assert_eq!(single_template[0].latch(), false);
1826 }
1827 }
1828
1829 #[test]
1830 fn test_addr_table_correctness() {
1831 for addr in 0..32 {
1833 let mut expected_addresses = [Address::new(); 4];
1834
1835 for i in 0..4 {
1837 let latch = !matches!(i, 3);
1838 #[cfg(feature = "esp32-ordering")]
1839 let mapped_i = map_index(i);
1840 #[cfg(not(feature = "esp32-ordering"))]
1841 let mapped_i = i;
1842
1843 expected_addresses[mapped_i].set_latch(latch);
1844 expected_addresses[mapped_i].set_addr(addr);
1845 }
1846
1847 let table_addresses = &ADDR_TABLE[addr as usize];
1849 for i in 0..4 {
1850 assert_eq!(table_addresses[i].0, expected_addresses[i].0);
1851 }
1852 }
1853 }
1854
1855 const CHAR_W: i32 = 6;
1857 const CHAR_H: i32 = 10;
1858
1859 fn verify_glyph_at(fb: &mut TestFrameBuffer, origin: Point) {
1862 use embedded_graphics::mock_display::MockDisplay;
1863 use embedded_graphics::mono_font::ascii::FONT_6X10;
1864 use embedded_graphics::mono_font::MonoTextStyle;
1865 use embedded_graphics::text::{Baseline, Text};
1866
1867 let style = MonoTextStyle::new(&FONT_6X10, Color::WHITE);
1869 Text::with_baseline("A", origin, style, Baseline::Top)
1870 .draw(fb)
1871 .unwrap();
1872
1873 let mut reference: MockDisplay<Color> = MockDisplay::new();
1875 Text::with_baseline("A", Point::zero(), style, Baseline::Top)
1876 .draw(&mut reference)
1877 .unwrap();
1878
1879 for dy in 0..CHAR_H {
1881 for dx in 0..CHAR_W {
1882 let expected_on = reference
1883 .get_pixel(Point::new(dx, dy))
1884 .unwrap_or(Color::BLACK)
1885 != Color::BLACK;
1886
1887 let gx = (origin.x + dx) as usize;
1888 let gy = (origin.y + dy) as usize;
1889
1890 let frame0 = &fb.frames[0];
1897 let e = if gy < TEST_NROWS {
1898 &frame0.rows[gy].data[map_index(gx)]
1899 } else {
1900 &frame0.rows[gy - TEST_NROWS].data[map_index(gx)]
1901 };
1902
1903 let (r, g, b) = if gy >= TEST_NROWS {
1904 (e.red2(), e.grn2(), e.blu2())
1905 } else {
1906 (e.red1(), e.grn1(), e.blu1())
1907 };
1908
1909 if expected_on {
1910 assert!(r && g && b);
1911 } else {
1912 assert!(!r && !g && !b);
1913 }
1914 }
1915 }
1916 }
1917
1918 #[test]
1919 fn test_draw_char_corners() {
1920 let upper_left = Point::new(0, 0);
1922 let lower_right = Point::new(TEST_COLS as i32 - CHAR_W, TEST_ROWS as i32 - CHAR_H);
1923
1924 let mut fb = TestFrameBuffer::new();
1925
1926 verify_glyph_at(&mut fb, upper_left);
1928 verify_glyph_at(&mut fb, lower_right);
1930 }
1931
1932 #[test]
1933 fn test_framebuffer_operations_trait_erase() {
1934 let mut fb = TestFrameBuffer::new();
1935
1936 fb.set_pixel_internal(10, 5, Color::RED);
1938 fb.set_pixel_internal(20, 10, Color::GREEN);
1939
1940 <TestFrameBuffer as FrameBufferOperations>::erase(&mut fb);
1942
1943 let mc10 = map_index(10);
1945 let mc20 = map_index(20);
1946 assert_eq!(fb.frames[0].rows[5].data[mc10].red1(), false);
1947 assert_eq!(fb.frames[0].rows[10].data[mc20].grn1(), false);
1948
1949 let oe_active = !cfg!(feature = "invert-oe");
1951 let active_count = TEST_COLS.saturating_sub(2 * BLANKING_DELAY + 1);
1952 let blank_count = TEST_COLS - active_count;
1953 let row0 = &fb.frames[0].rows[0];
1954 let oe_blank_count = row0
1955 .data
1956 .iter()
1957 .filter(|entry| entry.output_enable() != oe_active)
1958 .count();
1959 assert_eq!(oe_blank_count, blank_count);
1960 assert!(row0.data.iter().all(|e| !e.latch()));
1961
1962 for (i, addr) in row0.address.iter().enumerate() {
1964 assert_eq!(addr.0, ADDR_TABLE[0][i].0);
1965 }
1966 }
1967
1968 #[test]
1969 fn test_framebuffer_operations_trait_set_pixel() {
1970 let mut fb = TestFrameBuffer::new();
1971
1972 <TestFrameBuffer as FrameBufferOperations>::set_pixel(
1974 &mut fb,
1975 Point::new(8, 3),
1976 Color::BLUE,
1977 );
1978
1979 let idx = map_index(8);
1981 assert_eq!(fb.frames[0].rows[3].data[idx].blu1(), true);
1982 assert_eq!(fb.frames[0].rows[3].data[idx].red1(), false);
1984 assert_eq!(fb.frames[0].rows[3].data[idx].grn1(), false);
1985 }
1986
1987 #[test]
1988 fn test_blanking_delay() {
1989 let mut row: Row<TEST_COLS> = Row::new();
1990 row.format(5);
1991
1992 let oe_active = !cfg!(feature = "invert-oe");
1993
1994 if BLANKING_DELAY > 0 {
1995 let first_blanked_idx = map_index(0);
1996 assert_eq!(row.data[first_blanked_idx].output_enable(), !oe_active);
1997
1998 let first_active_idx = map_index(BLANKING_DELAY);
1999 assert_eq!(row.data[first_active_idx].output_enable(), oe_active);
2000 }
2001
2002 let last_active_idx = map_index(TEST_COLS - BLANKING_DELAY - 2);
2003 assert_eq!(row.data[last_active_idx].output_enable(), oe_active);
2004
2005 let blanking_pixel_idx = map_index(TEST_COLS - BLANKING_DELAY - 1);
2006 assert_eq!(row.data[blanking_pixel_idx].output_enable(), !oe_active);
2007
2008 let last_pixel_idx = map_index(TEST_COLS - 1);
2009 assert_eq!(row.data[last_pixel_idx].output_enable(), !oe_active);
2010 }
2011}