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