1use core::convert::Infallible;
133
134use crate::{FrameBufferOperations, MutableFrameBuffer};
135use bitfield::bitfield;
136use embedded_dma::ReadBuffer;
137use embedded_graphics::pixelcolor::RgbColor;
138use embedded_graphics::prelude::Point;
139
140use super::Color;
141use super::FrameBuffer;
142
143#[cfg(feature = "lead-blank-1")]
144const LEAD_BLANK_DELAY: usize = 1;
145#[cfg(feature = "lead-blank-2")]
146const LEAD_BLANK_DELAY: usize = 2;
147#[cfg(feature = "lead-blank-4")]
148const LEAD_BLANK_DELAY: usize = 4;
149#[cfg(feature = "lead-blank-8")]
150const LEAD_BLANK_DELAY: usize = 8;
151#[cfg(feature = "lead-blank-16")]
152const LEAD_BLANK_DELAY: usize = 16;
153
154#[cfg(not(any(
155 feature = "lead-blank-1",
156 feature = "lead-blank-2",
157 feature = "lead-blank-4",
158 feature = "lead-blank-8",
159 feature = "lead-blank-16"
160)))]
161const LEAD_BLANK_DELAY: usize = 1;
162
163#[cfg(feature = "trail-blank-1")]
164const TRAIL_BLANK_DELAY: usize = 1;
165#[cfg(feature = "trail-blank-2")]
166const TRAIL_BLANK_DELAY: usize = 2;
167#[cfg(feature = "trail-blank-4")]
168const TRAIL_BLANK_DELAY: usize = 4;
169#[cfg(feature = "trail-blank-8")]
170const TRAIL_BLANK_DELAY: usize = 8;
171#[cfg(feature = "trail-blank-16")]
172const TRAIL_BLANK_DELAY: usize = 16;
173
174#[cfg(not(any(
175 feature = "trail-blank-1",
176 feature = "trail-blank-2",
177 feature = "trail-blank-4",
178 feature = "trail-blank-8",
179 feature = "trail-blank-16"
180)))]
181const TRAIL_BLANK_DELAY: usize = 1;
182
183#[cfg(feature = "inter-row-blank-4")]
184const INTER_ROW_BLANK: usize = 4;
185#[cfg(feature = "inter-row-blank-8")]
186const INTER_ROW_BLANK: usize = 8;
187#[cfg(feature = "inter-row-blank-16")]
188const INTER_ROW_BLANK: usize = 16;
189#[cfg(feature = "inter-row-blank-32")]
190const INTER_ROW_BLANK: usize = 32;
191#[cfg(not(any(
192 feature = "inter-row-blank-4",
193 feature = "inter-row-blank-8",
194 feature = "inter-row-blank-16",
195 feature = "inter-row-blank-32"
196)))]
197const INTER_ROW_BLANK: usize = 0;
198
199#[cfg(not(feature = "invert-oe"))]
200const OE_ACTIVE: u16 = 0b1_0000_0000;
201#[cfg(not(feature = "invert-oe"))]
202const OE_BLANK: u16 = 0;
203
204#[cfg(feature = "invert-oe")]
205const OE_ACTIVE: u16 = 0;
206#[cfg(feature = "invert-oe")]
207const OE_BLANK: u16 = 0b1_0000_0000;
208
209#[inline]
214const fn make_data_template<const COLS: usize>(addr: u8) -> [Entry; COLS] {
215 let mut data = [Entry::new(); COLS];
216 let mut i = 0;
217
218 while i < COLS {
219 let mut entry = Entry::new();
220 entry.0 = addr as u16 | OE_BLANK;
221
222 if i == COLS - 1 {
223 entry.0 |= 0b0010_0000; } else if i >= TRAIL_BLANK_DELAY && i < COLS.saturating_sub(LEAD_BLANK_DELAY + 1) {
225 entry.0 = (entry.0 & !0b1_0000_0000) | OE_ACTIVE;
226 }
227
228 data[map_index(i)] = entry;
229 i += 1;
230 }
231
232 data
233}
234
235bitfield! {
236 #[derive(Clone, Copy, Default, PartialEq)]
257 #[repr(transparent)]
258 struct Entry(u16);
259 dummy2, set_dummy2: 15;
260 blu2, set_blu2: 14;
261 grn2, set_grn2: 13;
262 red2, set_red2: 12;
263 blu1, set_blu1: 11;
264 grn1, set_grn1: 10;
265 red1, set_red1: 9;
266 output_enable, set_output_enable: 8;
267 dummy1, set_dummy1: 7;
268 dummy0, set_dummy0: 6;
269 latch, set_latch: 5;
270 addr, set_addr: 4, 0;
271}
272
273impl core::fmt::Debug for Entry {
274 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
275 f.debug_tuple("Entry")
276 .field(&format_args!("{:#x}", self.0))
277 .finish()
278 }
279}
280
281#[cfg(feature = "defmt")]
282impl defmt::Format for Entry {
283 fn format(&self, f: defmt::Formatter) {
284 defmt::write!(f, "Entry({=u16:#x})", self.0);
285 }
286}
287
288impl Entry {
289 const fn new() -> Self {
290 #[cfg(feature = "invert-oe")]
291 {
292 Self(0b1_0000_0000)
293 }
294 #[cfg(not(feature = "invert-oe"))]
295 {
296 Self(0)
297 }
298 }
299
300 const COLOR0_MASK: u16 = 0b0000_1110_0000_0000; const COLOR1_MASK: u16 = 0b0111_0000_0000_0000; #[inline]
305 fn set_color0_bits(&mut self, bits: u8) {
306 let bits16 = u16::from(bits) << 9;
307 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits16 & Self::COLOR0_MASK);
308 }
309
310 #[inline]
311 fn set_color1_bits(&mut self, bits: u8) {
312 let bits16 = u16::from(bits) << 12;
313 self.0 = (self.0 & !Self::COLOR1_MASK) | (bits16 & Self::COLOR1_MASK);
314 }
315}
316
317#[derive(Clone, Copy, PartialEq, Debug)]
326#[repr(C)]
327struct Row<const COLS: usize> {
328 data: [Entry; COLS],
329 gap: [Entry; INTER_ROW_BLANK],
330}
331
332const fn map_index(i: usize) -> usize {
333 #[cfg(feature = "esp32-ordering")]
334 {
335 i ^ 1
336 }
337 #[cfg(not(feature = "esp32-ordering"))]
338 {
339 i
340 }
341}
342
343impl<const COLS: usize> Default for Row<COLS> {
344 fn default() -> Self {
345 Self::new()
346 }
347}
348
349impl<const COLS: usize> Row<COLS> {
350 pub const fn new() -> Self {
351 Self {
352 data: [Entry::new(); COLS],
353 gap: [Entry::new(); INTER_ROW_BLANK],
354 }
355 }
356
357 pub const fn format(&mut self, prev_addr: u8) {
358 let template = make_data_template::<COLS>(prev_addr);
359 let mut i = 0;
360 while i < COLS {
361 self.data[i] = template[i];
362 i += 1;
363 }
364
365 let gap_val = (prev_addr as u16) | OE_BLANK;
366 let mut i = 0;
367 #[allow(clippy::absurd_extreme_comparisons)]
369 while i < INTER_ROW_BLANK {
370 self.gap[i].0 = gap_val;
371 i += 1;
372 }
373 }
374
375 #[inline]
378 pub fn clear_colors(&mut self) {
379 const COLOR_CLEAR_MASK: u16 = !0b0111_1110_0000_0000; for entry in &mut self.data {
383 entry.0 &= COLOR_CLEAR_MASK;
384 }
385 }
386
387 #[inline]
388 pub fn set_color0(&mut self, col: usize, r: bool, g: bool, b: bool) {
389 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
390 let col = map_index(col);
391 self.data[col].set_color0_bits(bits);
392 }
393
394 #[inline]
395 pub fn set_color1(&mut self, col: usize, r: bool, g: bool, b: bool) {
396 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
397 let col = map_index(col);
398 self.data[col].set_color1_bits(bits);
399 }
400}
401
402#[derive(Copy, Clone, Debug)]
403#[repr(C)]
404struct Frame<const ROWS: usize, const COLS: usize, const NROWS: usize> {
405 rows: [Row<COLS>; NROWS],
406}
407
408impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Frame<ROWS, COLS, NROWS> {
409 pub const fn new() -> Self {
410 Self {
411 rows: [Row::new(); NROWS],
412 }
413 }
414
415 pub const fn format(&mut self) {
416 let mut addr = 0;
417 while addr < NROWS {
418 let prev_addr = if addr == 0 {
419 NROWS as u8 - 1
420 } else {
421 addr as u8 - 1
422 };
423 self.rows[addr].format(prev_addr);
424 addr += 1;
425 }
426 }
427
428 #[inline]
430 pub fn clear_colors(&mut self) {
431 for row in &mut self.rows {
432 row.clear_colors();
433 }
434 }
435
436 #[inline]
437 pub fn set_pixel(&mut self, y: usize, x: usize, red: bool, green: bool, blue: bool) {
438 let row = &mut self.rows[if y < NROWS { y } else { y - NROWS }];
439 if y < NROWS {
440 row.set_color0(x, red, green, blue);
441 } else {
442 row.set_color1(x, red, green, blue);
443 }
444 }
445}
446
447impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Default
448 for Frame<ROWS, COLS, NROWS>
449{
450 fn default() -> Self {
451 Self::new()
452 }
453}
454
455#[derive(Copy, Clone, Debug)]
458#[repr(C)]
459struct FrameData<const ROWS: usize, const COLS: usize, const NROWS: usize, const FRAME_COUNT: usize>
460{
461 frames: [Frame<ROWS, COLS, NROWS>; FRAME_COUNT],
462 #[cfg(all(feature = "tail-closes-latch", feature = "esp32-ordering"))]
463 padding: Entry,
464 #[cfg(feature = "tail-closes-latch")]
465 tail: Entry,
466}
467
468impl<const ROWS: usize, const COLS: usize, const NROWS: usize, const FRAME_COUNT: usize>
469 FrameData<ROWS, COLS, NROWS, FRAME_COUNT>
470{
471 const fn new() -> Self {
472 Self {
473 frames: [Frame::new(); FRAME_COUNT],
474 #[cfg(all(feature = "esp32-ordering", feature = "tail-closes-latch"))]
475 padding: Entry::new(),
476 #[cfg(feature = "tail-closes-latch")]
477 tail: Entry::new(),
478 }
479 }
480}
481
482#[derive(Copy, Clone)]
507#[repr(C)]
508pub struct DmaFrameBuffer<
509 const ROWS: usize,
510 const COLS: usize,
511 const NROWS: usize,
512 const BITS: u8,
513 const FRAME_COUNT: usize,
514> {
515 _align: u64,
516 data: FrameData<ROWS, COLS, NROWS, FRAME_COUNT>,
517}
518
519impl<
520 const ROWS: usize,
521 const COLS: usize,
522 const NROWS: usize,
523 const BITS: u8,
524 const FRAME_COUNT: usize,
525 > Default for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
526{
527 fn default() -> Self {
528 Self::new()
529 }
530}
531
532impl<
533 const ROWS: usize,
534 const COLS: usize,
535 const NROWS: usize,
536 const BITS: u8,
537 const FRAME_COUNT: usize,
538 > DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
539{
540 #[must_use]
563 pub const fn new() -> Self {
564 assert!(BITS <= 8);
565
566 let mut instance = Self {
567 _align: 0,
568 data: FrameData::new(),
569 };
570
571 instance.format();
572 instance
573 }
574
575 #[must_use]
578 pub const fn bcm_chunk_count() -> usize {
579 1
580 }
581
582 #[must_use]
586 pub const fn bcm_chunk_bytes() -> usize {
587 core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>()
588 }
589
590 #[inline]
610 pub const fn format(&mut self) {
611 let mut i = 0;
612 while i < FRAME_COUNT {
613 self.data.frames[i].format();
614 i += 1;
615 }
616 #[cfg(feature = "tail-closes-latch")]
617 {
618 self.data.tail.0 = 0x1f | OE_BLANK;
619 }
620 #[cfg(all(feature = "esp32-ordering", feature = "tail-closes-latch"))]
621 {
622 self.data.padding.0 = 0x1f | OE_BLANK;
623 }
624 }
625
626 #[inline]
646 pub fn erase(&mut self) {
647 for frame in &mut self.data.frames {
648 frame.clear_colors();
649 }
650 }
651
652 pub fn set_pixel(&mut self, p: Point, color: Color) {
668 if p.x < 0 || p.y < 0 {
669 return;
670 }
671 self.set_pixel_internal(p.x as usize, p.y as usize, color);
672 }
673
674 #[inline]
675 fn frames_on(v: u8) -> usize {
676 (v as usize) >> (8 - BITS)
678 }
679
680 #[inline]
681 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Color) {
682 if x >= COLS || y >= ROWS {
683 return;
684 }
685
686 #[cfg(feature = "skip-black-pixels")]
689 if color == Color::BLACK {
690 return;
691 }
692
693 let red_frames = Self::frames_on(color.r());
695 let green_frames = Self::frames_on(color.g());
696 let blue_frames = Self::frames_on(color.b());
697
698 for (frame_idx, frame) in self.data.frames.iter_mut().enumerate() {
700 frame.set_pixel(
701 y,
702 x,
703 frame_idx < red_frames,
704 frame_idx < green_frames,
705 frame_idx < blue_frames,
706 );
707 }
708 }
709}
710
711impl<
712 const ROWS: usize,
713 const COLS: usize,
714 const NROWS: usize,
715 const BITS: u8,
716 const FRAME_COUNT: usize,
717 > FrameBufferOperations for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
718{
719 #[inline]
720 fn erase(&mut self) {
721 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::erase(self);
722 }
723
724 #[inline]
725 fn set_pixel(&mut self, p: Point, color: Color) {
726 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::set_pixel(self, p, color);
727 }
728}
729
730impl<
731 const ROWS: usize,
732 const COLS: usize,
733 const NROWS: usize,
734 const BITS: u8,
735 const FRAME_COUNT: usize,
736 > embedded_graphics::prelude::OriginDimensions
737 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
738{
739 fn size(&self) -> embedded_graphics::prelude::Size {
740 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
741 }
742}
743
744impl<
745 const ROWS: usize,
746 const COLS: usize,
747 const NROWS: usize,
748 const BITS: u8,
749 const FRAME_COUNT: usize,
750 > embedded_graphics::prelude::OriginDimensions
751 for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
752{
753 fn size(&self) -> embedded_graphics::prelude::Size {
754 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
755 }
756}
757
758impl<
759 const ROWS: usize,
760 const COLS: usize,
761 const NROWS: usize,
762 const BITS: u8,
763 const FRAME_COUNT: usize,
764 > embedded_graphics::draw_target::DrawTarget
765 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
766{
767 type Color = Color;
768
769 type Error = Infallible;
770
771 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
772 where
773 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
774 {
775 for pixel in pixels {
776 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
777 }
778 Ok(())
779 }
780}
781
782unsafe impl<
783 const ROWS: usize,
784 const COLS: usize,
785 const NROWS: usize,
786 const BITS: u8,
787 const FRAME_COUNT: usize,
788 > ReadBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
789{
790 type Word = u8;
791
792 unsafe fn read_buffer(&self) -> (*const u8, usize) {
793 let ptr = (&raw const self.data).cast::<u8>();
794 let len = core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>();
795 (ptr, len)
796 }
797}
798
799unsafe impl<
800 const ROWS: usize,
801 const COLS: usize,
802 const NROWS: usize,
803 const BITS: u8,
804 const FRAME_COUNT: usize,
805 > ReadBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
806{
807 type Word = u8;
808
809 unsafe fn read_buffer(&self) -> (*const u8, usize) {
810 let ptr = (&raw const self.data).cast::<u8>();
811 let len = core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>();
812 (ptr, len)
813 }
814}
815
816impl<
817 const ROWS: usize,
818 const COLS: usize,
819 const NROWS: usize,
820 const BITS: u8,
821 const FRAME_COUNT: usize,
822 > core::fmt::Debug for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
823{
824 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
825 let brightness_step = 1 << (8 - BITS);
826 let size = core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>();
827 f.debug_struct("DmaFrameBuffer")
828 .field("size", &size)
829 .field("frame_count", &self.data.frames.len())
830 .field("frame_size", &core::mem::size_of_val(&self.data.frames[0]))
831 .field("brightness_step", &&brightness_step)
832 .finish_non_exhaustive()
833 }
834}
835
836#[cfg(feature = "defmt")]
837impl<
838 const ROWS: usize,
839 const COLS: usize,
840 const NROWS: usize,
841 const BITS: u8,
842 const FRAME_COUNT: usize,
843 > defmt::Format for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
844{
845 fn format(&self, f: defmt::Formatter) {
846 let brightness_step = 1 << (8 - BITS);
847 defmt::write!(
848 f,
849 "DmaFrameBuffer<{}, {}, {}, {}, {}>",
850 ROWS,
851 COLS,
852 NROWS,
853 BITS,
854 FRAME_COUNT
855 );
856 defmt::write!(
857 f,
858 " size: {}",
859 core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>()
860 );
861 defmt::write!(
862 f,
863 " frame_size: {}",
864 core::mem::size_of_val(&self.data.frames[0])
865 );
866 defmt::write!(f, " brightness_step: {}", brightness_step);
867 }
868}
869
870impl<
871 const ROWS: usize,
872 const COLS: usize,
873 const NROWS: usize,
874 const BITS: u8,
875 const FRAME_COUNT: usize,
876 > FrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
877{
878 type Word = u16;
879
880 fn plane_count(&self) -> usize {
881 1
882 }
883
884 fn plane_ptr_len(&self, plane_idx: usize) -> (*const u8, usize) {
885 assert!(plane_idx == 0, "plain DmaFrameBuffer has only 1 plane");
886 let ptr = (&raw const self.data).cast::<u8>();
887 let len = core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>();
888 (ptr, len)
889 }
890}
891
892impl<
893 const ROWS: usize,
894 const COLS: usize,
895 const NROWS: usize,
896 const BITS: u8,
897 const FRAME_COUNT: usize,
898 > MutableFrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
899{
900}
901
902#[cfg(test)]
903mod tests {
904 extern crate std;
905
906 use std::format;
907 use std::vec;
908
909 use super::*;
910 use crate::{FrameBuffer, WordSize};
911 use embedded_graphics::pixelcolor::RgbColor;
912 use embedded_graphics::prelude::*;
913 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle};
914
915 const TEST_ROWS: usize = 32;
916 const TEST_COLS: usize = 64;
917 const TEST_NROWS: usize = TEST_ROWS / 2;
918 const TEST_BITS: u8 = 3;
919 const TEST_FRAME_COUNT: usize = (1 << TEST_BITS) - 1; type TestFrameBuffer =
922 DmaFrameBuffer<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_BITS, TEST_FRAME_COUNT>;
923
924 fn get_mapped_index(index: usize) -> usize {
926 map_index(index)
927 }
928
929 #[test]
930 fn test_entry_construction() {
931 let entry = Entry::new();
932 let expect_oe = cfg!(feature = "invert-oe");
933 assert_eq!(entry.dummy2(), false);
934 assert_eq!(entry.blu2(), false);
935 assert_eq!(entry.grn2(), false);
936 assert_eq!(entry.red2(), false);
937 assert_eq!(entry.blu1(), false);
938 assert_eq!(entry.grn1(), false);
939 assert_eq!(entry.red1(), false);
940 assert_eq!(entry.output_enable(), expect_oe);
941 assert_eq!(entry.dummy1(), false);
942 assert_eq!(entry.dummy0(), false);
943 assert_eq!(entry.latch(), false);
944 assert_eq!(entry.addr(), 0);
945 }
946
947 #[test]
948 fn test_entry_setters() {
949 let mut entry = Entry::new();
950
951 entry.set_dummy2(true);
952 assert_eq!(entry.dummy2(), true);
953 assert_eq!(entry.0 & 0b1000000000000000, 0b1000000000000000);
954
955 entry.set_blu2(true);
956 assert_eq!(entry.blu2(), true);
957 assert_eq!(entry.0 & 0b0100000000000000, 0b0100000000000000);
958
959 entry.set_grn2(true);
960 assert_eq!(entry.grn2(), true);
961 assert_eq!(entry.0 & 0b0010000000000000, 0b0010000000000000);
962
963 entry.set_red2(true);
964 assert_eq!(entry.red2(), true);
965 assert_eq!(entry.0 & 0b0001000000000000, 0b0001000000000000);
966
967 entry.set_blu1(true);
968 assert_eq!(entry.blu1(), true);
969 assert_eq!(entry.0 & 0b0000100000000000, 0b0000100000000000);
970
971 entry.set_grn1(true);
972 assert_eq!(entry.grn1(), true);
973 assert_eq!(entry.0 & 0b0000010000000000, 0b0000010000000000);
974
975 entry.set_red1(true);
976 assert_eq!(entry.red1(), true);
977 assert_eq!(entry.0 & 0b0000001000000000, 0b0000001000000000);
978
979 entry.set_output_enable(true);
980 assert_eq!(entry.output_enable(), true);
981 assert_eq!(entry.0 & 0b0000000100000000, 0b0000000100000000);
982
983 entry.set_dummy1(true);
984 assert_eq!(entry.dummy1(), true);
985 assert_eq!(entry.0 & 0b0000000010000000, 0b0000000010000000);
986
987 entry.set_dummy0(true);
988 assert_eq!(entry.dummy0(), true);
989 assert_eq!(entry.0 & 0b0000000001000000, 0b0000000001000000);
990
991 entry.set_latch(true);
992 assert_eq!(entry.latch(), true);
993 assert_eq!(entry.0 & 0b0000000000100000, 0b0000000000100000);
994
995 entry.set_addr(0b11111);
996 assert_eq!(entry.addr(), 0b11111);
997 assert_eq!(entry.0 & 0b0000000000011111, 0b0000000000011111);
998 }
999
1000 #[test]
1001 fn test_entry_bit_isolation() {
1002 let mut entry = Entry::new();
1003 let expect_oe = cfg!(feature = "invert-oe");
1004
1005 entry.set_addr(0b11111);
1007 entry.set_latch(true);
1008 assert_eq!(entry.addr(), 0b11111);
1009 assert_eq!(entry.latch(), true);
1010 assert_eq!(entry.output_enable(), expect_oe);
1011 assert_eq!(entry.red1(), false);
1012
1013 entry.set_red1(true);
1014 entry.set_grn2(true);
1015 assert_eq!(entry.addr(), 0b11111);
1016 assert_eq!(entry.latch(), true);
1017 assert_eq!(entry.red1(), true);
1018 assert_eq!(entry.grn2(), true);
1019 assert_eq!(entry.blu1(), false);
1020 assert_eq!(entry.red2(), false);
1021 }
1022
1023 #[test]
1024 fn test_entry_set_color0() {
1025 let mut entry = Entry::new();
1026
1027 let bits = (u8::from(true) << 2) | (u8::from(false) << 1) | u8::from(true); entry.set_color0_bits(bits);
1029 assert_eq!(entry.red1(), true);
1030 assert_eq!(entry.grn1(), false);
1031 assert_eq!(entry.blu1(), true);
1032 assert_eq!(entry.0 & 0b0000101000000000, 0b0000101000000000); }
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 & 0b0110000000000000, 0b0110000000000000); }
1048
1049 #[test]
1050 fn test_entry_debug_formatting() {
1051 let entry = Entry(0x1234);
1052 let debug_str = format!("{:?}", entry);
1053 assert_eq!(debug_str, "Entry(0x1234)");
1054
1055 let entry = Entry(0xabcd);
1056 let debug_str = format!("{:?}", entry);
1057 assert_eq!(debug_str, "Entry(0xabcd)");
1058 }
1059
1060 #[test]
1061 fn test_row_construction() {
1062 let row: Row<TEST_COLS> = Row::new();
1063 assert_eq!(row.data.len(), TEST_COLS);
1064
1065 let expected = Entry::new().0;
1066 for entry in &row.data {
1067 assert_eq!(entry.0, expected);
1068 }
1069 }
1070
1071 #[test]
1072 fn test_row_format() {
1073 let mut row: Row<TEST_COLS> = Row::new();
1074 let prev_addr = 4;
1075
1076 row.format(prev_addr);
1077
1078 let oe_active = !cfg!(feature = "invert-oe");
1079
1080 for (physical_i, entry) in row.data.iter().enumerate() {
1081 let logical_i = get_mapped_index(physical_i);
1082
1083 assert_eq!(entry.addr(), prev_addr as u16);
1084
1085 if logical_i == TEST_COLS - 1 {
1086 assert_eq!(entry.latch(), true);
1087 assert_eq!(entry.output_enable(), !oe_active);
1088 } else {
1089 assert_eq!(entry.latch(), false);
1090 if logical_i >= TRAIL_BLANK_DELAY && logical_i < TEST_COLS - LEAD_BLANK_DELAY - 1 {
1091 assert_eq!(entry.output_enable(), oe_active);
1092 } else {
1093 assert_eq!(entry.output_enable(), !oe_active);
1094 }
1095 }
1096 }
1097 }
1098
1099 #[test]
1100 fn test_row_set_color0() {
1101 let mut row: Row<TEST_COLS> = Row::new();
1102
1103 row.set_color0(0, true, false, true);
1104
1105 let mapped_col_0 = get_mapped_index(0);
1106 assert_eq!(row.data[mapped_col_0].red1(), true);
1107 assert_eq!(row.data[mapped_col_0].grn1(), false);
1108 assert_eq!(row.data[mapped_col_0].blu1(), true);
1109
1110 row.set_color0(1, false, true, false);
1112
1113 let mapped_col_1 = get_mapped_index(1);
1114 assert_eq!(row.data[mapped_col_1].red1(), false);
1115 assert_eq!(row.data[mapped_col_1].grn1(), true);
1116 assert_eq!(row.data[mapped_col_1].blu1(), false);
1117 }
1118
1119 #[test]
1120 fn test_row_set_color1() {
1121 let mut row: Row<TEST_COLS> = Row::new();
1122
1123 row.set_color1(0, true, true, false);
1124
1125 let mapped_col_0 = get_mapped_index(0);
1126 assert_eq!(row.data[mapped_col_0].red2(), true);
1127 assert_eq!(row.data[mapped_col_0].grn2(), true);
1128 assert_eq!(row.data[mapped_col_0].blu2(), false);
1129 }
1130
1131 #[test]
1132 fn test_row_default() {
1133 let row1: Row<TEST_COLS> = Row::new();
1134 let row2: Row<TEST_COLS> = Row::default();
1135
1136 assert_eq!(row1, row2);
1138 assert_eq!(row1.data.len(), row2.data.len());
1139
1140 let expected = Entry::new().0;
1141 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1142 assert_eq!(entry1.0, entry2.0);
1143 assert_eq!(entry1.0, expected);
1144 }
1145 }
1146
1147 #[test]
1148 fn test_frame_construction() {
1149 let frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1150 assert_eq!(frame.rows.len(), TEST_NROWS);
1151 }
1152
1153 #[test]
1154 fn test_frame_format() {
1155 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1156
1157 frame.format();
1158
1159 for addr in 0..TEST_NROWS {
1160 let prev_addr = if addr == 0 { TEST_NROWS - 1 } else { addr - 1 };
1161 let row = &frame.rows[addr];
1162
1163 let last_pixel_idx = get_mapped_index(TEST_COLS - 1);
1164 assert_eq!(row.data[last_pixel_idx].addr(), prev_addr as u16);
1165 assert_eq!(row.data[last_pixel_idx].latch(), true);
1166
1167 let first_pixel_idx = get_mapped_index(0);
1168 assert_eq!(row.data[first_pixel_idx].addr(), prev_addr as u16);
1169 assert_eq!(row.data[first_pixel_idx].latch(), false);
1170 }
1171 }
1172
1173 #[test]
1174 fn test_frame_set_pixel() {
1175 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1176
1177 frame.set_pixel(5, 10, true, false, true);
1179
1180 let mapped_col_10 = get_mapped_index(10);
1181 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1182 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1183 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1184
1185 frame.set_pixel(TEST_NROWS + 5, 15, false, true, false);
1187
1188 let mapped_col_15 = get_mapped_index(15);
1189 assert_eq!(frame.rows[5].data[mapped_col_15].red2(), false);
1190 assert_eq!(frame.rows[5].data[mapped_col_15].grn2(), true);
1191 assert_eq!(frame.rows[5].data[mapped_col_15].blu2(), false);
1192 }
1193
1194 #[test]
1195 fn test_frame_default() {
1196 let frame1: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1197 let frame2: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::default();
1198
1199 assert_eq!(frame1.rows.len(), frame2.rows.len());
1201
1202 let expected = Entry::new().0;
1203 for (row1, row2) in frame1.rows.iter().zip(frame2.rows.iter()) {
1204 assert_eq!(row1, row2);
1205
1206 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1207 assert_eq!(entry1.0, entry2.0);
1208 assert_eq!(entry1.0, expected);
1209 }
1210 }
1211 }
1212
1213 #[test]
1214 fn test_dma_framebuffer_construction() {
1215 let fb = TestFrameBuffer::new();
1216 assert_eq!(fb.data.frames.len(), TEST_FRAME_COUNT);
1217 assert_eq!(fb._align, 0);
1218 }
1219
1220 #[test]
1221 fn test_bcm_chunk_info() {
1222 let expected_size =
1223 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>();
1224 assert_eq!(TestFrameBuffer::bcm_chunk_bytes(), expected_size);
1225 assert_eq!(TestFrameBuffer::bcm_chunk_count(), 1);
1226 }
1227
1228 #[test]
1229 fn test_dma_framebuffer_erase() {
1230 let fb = TestFrameBuffer::new();
1231
1232 for frame in &fb.data.frames {
1233 for addr in 0..TEST_NROWS {
1234 let prev_addr = if addr == 0 { TEST_NROWS - 1 } else { addr - 1 };
1235 let row = &frame.rows[addr];
1236
1237 let last_pixel_idx = get_mapped_index(TEST_COLS - 1);
1238 assert_eq!(row.data[last_pixel_idx].addr(), prev_addr as u16);
1239 assert_eq!(row.data[last_pixel_idx].latch(), true);
1240
1241 let first_pixel_idx = get_mapped_index(0);
1242 assert_eq!(row.data[first_pixel_idx].addr(), prev_addr as u16);
1243 assert_eq!(row.data[first_pixel_idx].latch(), false);
1244 }
1245 }
1246 }
1247
1248 #[test]
1249 fn test_dma_framebuffer_set_pixel_bounds() {
1250 let mut fb = TestFrameBuffer::new();
1251
1252 fb.set_pixel(Point::new(-1, 5), Color::RED);
1254 fb.set_pixel(Point::new(5, -1), Color::RED);
1255
1256 fb.set_pixel(Point::new(TEST_COLS as i32, 5), Color::RED);
1258 fb.set_pixel(Point::new(5, TEST_ROWS as i32), Color::RED);
1259 }
1260
1261 #[test]
1262 fn test_dma_framebuffer_set_pixel_internal() {
1263 let mut fb = TestFrameBuffer::new();
1264
1265 let red_color = Color::RED;
1266 fb.set_pixel_internal(10, 5, red_color);
1267
1268 for frame in &fb.data.frames {
1272 let mapped_col_10 = get_mapped_index(10);
1274 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1275 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), false);
1276 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), false);
1277 }
1278 }
1279
1280 #[test]
1281 fn test_dma_framebuffer_brightness_modulation() {
1282 let mut fb = TestFrameBuffer::new();
1283
1284 let brightness_step = 1 << (8 - TEST_BITS); let test_brightness = brightness_step * 3; let color = Color::from(embedded_graphics::pixelcolor::Rgb888::new(
1288 test_brightness,
1289 0,
1290 0,
1291 ));
1292
1293 fb.set_pixel_internal(0, 0, color);
1294
1295 for (frame_idx, frame) in fb.data.frames.iter().enumerate() {
1298 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1299 let should_be_active = test_brightness >= frame_threshold;
1300
1301 let mapped_col_0 = get_mapped_index(0);
1302 assert_eq!(frame.rows[0].data[mapped_col_0].red1(), should_be_active);
1303 }
1304 }
1305
1306 #[test]
1307 fn test_origin_dimensions() {
1308 let fb = TestFrameBuffer::new();
1309 let size = fb.size();
1310 assert_eq!(size.width, TEST_COLS as u32);
1311 assert_eq!(size.height, TEST_ROWS as u32);
1312
1313 let size = (&fb).size();
1315 assert_eq!(size.width, TEST_COLS as u32);
1316 assert_eq!(size.height, TEST_ROWS as u32);
1317
1318 let mut fb = TestFrameBuffer::new();
1320 let fb_ref = &mut fb;
1321 let size = fb_ref.size();
1322 assert_eq!(size.width, TEST_COLS as u32);
1323 assert_eq!(size.height, TEST_ROWS as u32);
1324 }
1325
1326 #[test]
1327 fn test_draw_target() {
1328 let mut fb = TestFrameBuffer::new();
1329
1330 let pixels = vec![
1331 embedded_graphics::Pixel(Point::new(0, 0), Color::RED),
1332 embedded_graphics::Pixel(Point::new(1, 1), Color::GREEN),
1333 embedded_graphics::Pixel(Point::new(2, 2), Color::BLUE),
1334 ];
1335
1336 let result = fb.draw_iter(pixels);
1337 assert!(result.is_ok());
1338
1339 let result = (&mut fb).draw_iter(vec![embedded_graphics::Pixel(
1341 Point::new(3, 3),
1342 Color::WHITE,
1343 )]);
1344 assert!(result.is_ok());
1345 }
1346
1347 #[test]
1348 fn test_draw_iter_pixel_verification() {
1349 let mut fb = TestFrameBuffer::new();
1350
1351 let pixels = vec![
1353 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), ];
1365
1366 let result = fb.draw_iter(pixels);
1367 assert!(result.is_ok());
1368
1369 let first_frame = &fb.data.frames[0];
1371 let brightness_step = 1 << (8 - TEST_BITS); let first_frame_threshold = brightness_step; let col_idx = get_mapped_index(5);
1377 assert_eq!(
1378 first_frame.rows[2].data[col_idx].red1(),
1379 Color::RED.r() >= first_frame_threshold
1380 );
1381 assert_eq!(
1382 first_frame.rows[2].data[col_idx].grn1(),
1383 Color::RED.g() >= first_frame_threshold
1384 );
1385 assert_eq!(
1386 first_frame.rows[2].data[col_idx].blu1(),
1387 Color::RED.b() >= first_frame_threshold
1388 );
1389
1390 let col_idx = get_mapped_index(10);
1392 assert_eq!(
1393 first_frame.rows[5].data[col_idx].red1(),
1394 Color::GREEN.r() >= first_frame_threshold
1395 );
1396 assert_eq!(
1397 first_frame.rows[5].data[col_idx].grn1(),
1398 Color::GREEN.g() >= first_frame_threshold
1399 );
1400 assert_eq!(
1401 first_frame.rows[5].data[col_idx].blu1(),
1402 Color::GREEN.b() >= first_frame_threshold
1403 );
1404
1405 let col_idx = get_mapped_index(15);
1407 assert_eq!(
1408 first_frame.rows[8].data[col_idx].red1(),
1409 Color::BLUE.r() >= first_frame_threshold
1410 );
1411 assert_eq!(
1412 first_frame.rows[8].data[col_idx].grn1(),
1413 Color::BLUE.g() >= first_frame_threshold
1414 );
1415 assert_eq!(
1416 first_frame.rows[8].data[col_idx].blu1(),
1417 Color::BLUE.b() >= first_frame_threshold
1418 );
1419
1420 let col_idx = get_mapped_index(20);
1422 assert_eq!(
1423 first_frame.rows[10].data[col_idx].red1(),
1424 Color::WHITE.r() >= first_frame_threshold
1425 );
1426 assert_eq!(
1427 first_frame.rows[10].data[col_idx].grn1(),
1428 Color::WHITE.g() >= first_frame_threshold
1429 );
1430 assert_eq!(
1431 first_frame.rows[10].data[col_idx].blu1(),
1432 Color::WHITE.b() >= first_frame_threshold
1433 );
1434
1435 let col_idx = get_mapped_index(25);
1438 assert_eq!(
1439 first_frame.rows[3].data[col_idx].red2(),
1440 Color::RED.r() >= first_frame_threshold
1441 );
1442 assert_eq!(
1443 first_frame.rows[3].data[col_idx].grn2(),
1444 Color::RED.g() >= first_frame_threshold
1445 );
1446 assert_eq!(
1447 first_frame.rows[3].data[col_idx].blu2(),
1448 Color::RED.b() >= first_frame_threshold
1449 );
1450
1451 let col_idx = get_mapped_index(30);
1453 assert_eq!(
1454 first_frame.rows[7].data[col_idx].red2(),
1455 Color::GREEN.r() >= first_frame_threshold
1456 );
1457 assert_eq!(
1458 first_frame.rows[7].data[col_idx].grn2(),
1459 Color::GREEN.g() >= first_frame_threshold
1460 );
1461 assert_eq!(
1462 first_frame.rows[7].data[col_idx].blu2(),
1463 Color::GREEN.b() >= first_frame_threshold
1464 );
1465
1466 let col_idx = get_mapped_index(35);
1468 assert_eq!(
1469 first_frame.rows[12].data[col_idx].red2(),
1470 Color::BLUE.r() >= first_frame_threshold
1471 );
1472 assert_eq!(
1473 first_frame.rows[12].data[col_idx].grn2(),
1474 Color::BLUE.g() >= first_frame_threshold
1475 );
1476 assert_eq!(
1477 first_frame.rows[12].data[col_idx].blu2(),
1478 Color::BLUE.b() >= first_frame_threshold
1479 );
1480
1481 let col_idx = get_mapped_index(40);
1483 assert_eq!(first_frame.rows[1].data[col_idx].red1(), false);
1484 assert_eq!(first_frame.rows[1].data[col_idx].grn1(), false);
1485 assert_eq!(first_frame.rows[1].data[col_idx].blu1(), false);
1486 }
1487
1488 #[test]
1489 fn test_embedded_graphics_integration() {
1490 let mut fb = TestFrameBuffer::new();
1491
1492 let result = Rectangle::new(Point::new(5, 5), Size::new(10, 8))
1494 .into_styled(PrimitiveStyle::with_fill(Color::RED))
1495 .draw(&mut fb);
1496 assert!(result.is_ok());
1497
1498 let result = Circle::new(Point::new(30, 15), 8)
1500 .into_styled(PrimitiveStyle::with_fill(Color::BLUE))
1501 .draw(&mut fb);
1502 assert!(result.is_ok());
1503 }
1504
1505 #[test]
1506 #[cfg(feature = "skip-black-pixels")]
1507 fn test_skip_black_pixels_enabled() {
1508 let mut fb = TestFrameBuffer::new();
1509
1510 fb.set_pixel_internal(10, 5, Color::RED);
1512
1513 let mapped_col_10 = get_mapped_index(10);
1515 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].red1(), true);
1516 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1517 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1518
1519 fb.set_pixel_internal(10, 5, Color::BLACK);
1521
1522 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].red1(), true);
1524 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1525 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1526 }
1527
1528 #[test]
1529 #[cfg(not(feature = "skip-black-pixels"))]
1530 fn test_skip_black_pixels_disabled() {
1531 let mut fb = TestFrameBuffer::new();
1532
1533 fb.set_pixel_internal(10, 5, Color::RED);
1535
1536 let mapped_col_10 = get_mapped_index(10);
1538 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].red1(), true);
1539 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1540 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1541
1542 fb.set_pixel_internal(10, 5, Color::BLACK);
1544
1545 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].red1(), false);
1547 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1548 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1549 }
1550
1551 #[test]
1552 fn test_bcm_frame_overwrite() {
1553 let mut fb = TestFrameBuffer::new();
1554
1555 fb.set_pixel_internal(10, 5, Color::WHITE);
1557
1558 let mapped_col_10 = get_mapped_index(10);
1559
1560 for frame in fb.data.frames.iter() {
1562 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), true);
1564 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), true);
1565 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), true);
1566 }
1567
1568 let half_white = Color::from(embedded_graphics::pixelcolor::Rgb888::new(128, 128, 128));
1570 fb.set_pixel_internal(10, 5, half_white);
1571
1572 let brightness_step = 1 << (8 - TEST_BITS); for (frame_idx, frame) in fb.data.frames.iter().enumerate() {
1578 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1579 let should_be_active = 128 >= frame_threshold;
1580
1581 assert_eq!(frame.rows[5].data[mapped_col_10].red1(), should_be_active);
1582 assert_eq!(frame.rows[5].data[mapped_col_10].grn1(), should_be_active);
1583 assert_eq!(frame.rows[5].data[mapped_col_10].blu1(), should_be_active);
1584 }
1585
1586 for frame_idx in 0..4 {
1589 assert_eq!(
1590 fb.data.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1591 true
1592 );
1593 }
1594 for frame_idx in 4..TEST_FRAME_COUNT {
1596 assert_eq!(
1597 fb.data.frames[frame_idx].rows[5].data[mapped_col_10].red1(),
1598 false
1599 );
1600 }
1601 }
1602
1603 #[test]
1604 fn test_read_buffer_implementation() {
1605 let fb = TestFrameBuffer::new();
1607 let expected_size =
1608 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>();
1609
1610 unsafe {
1612 let (ptr, len) = <TestFrameBuffer as ReadBuffer>::read_buffer(&fb);
1613 assert!(!ptr.is_null());
1614 assert_eq!(len, expected_size);
1615 }
1616
1617 unsafe {
1619 let (ptr, len) = fb.read_buffer();
1620 assert!(!ptr.is_null());
1621 assert_eq!(len, expected_size);
1622 }
1623
1624 let fb = TestFrameBuffer::new();
1626 let fb_ref = &fb;
1627 unsafe {
1628 let (ptr, len) = fb_ref.read_buffer();
1629 assert!(!ptr.is_null());
1630 assert_eq!(
1631 len,
1632 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>(
1633 )
1634 );
1635 }
1636
1637 let mut fb = TestFrameBuffer::new();
1639 let fb_ref = &mut fb;
1640 unsafe {
1641 let (ptr, len) = fb_ref.read_buffer();
1642 assert!(!ptr.is_null());
1643 assert_eq!(
1644 len,
1645 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>(
1646 )
1647 );
1648 }
1649 }
1650
1651 #[test]
1652 fn test_read_buffer_owned_implementation() {
1653 fn test_owned_read_buffer(fb: TestFrameBuffer) -> (bool, usize) {
1656 unsafe {
1657 let (ptr, len) = fb.read_buffer();
1658 (!ptr.is_null(), len)
1659 }
1660 }
1661
1662 let fb = TestFrameBuffer::new();
1663 let expected_len =
1664 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>();
1665
1666 let (ptr_valid, actual_len) = test_owned_read_buffer(fb);
1667 assert!(ptr_valid);
1668 assert_eq!(actual_len, expected_len);
1669 }
1670
1671 #[test]
1672 fn test_framebuffer_trait() {
1673 let fb = TestFrameBuffer::new();
1674 assert_eq!(fb.get_word_size(), WordSize::Sixteen);
1675
1676 let fb_ref = &fb;
1677 assert_eq!(fb_ref.get_word_size(), WordSize::Sixteen);
1678
1679 let mut fb = TestFrameBuffer::new();
1680 let fb_ref = &mut fb;
1681 assert_eq!(fb_ref.get_word_size(), WordSize::Sixteen);
1682 }
1683
1684 #[test]
1685 fn test_debug_formatting() {
1686 let fb = TestFrameBuffer::new();
1687 let debug_string = format!("{:?}", fb);
1688 assert!(debug_string.contains("DmaFrameBuffer"));
1689 assert!(debug_string.contains("frame_count"));
1690 assert!(debug_string.contains("frame_size"));
1691 assert!(debug_string.contains("brightness_step"));
1692 }
1693
1694 #[test]
1695 fn test_default_implementation() {
1696 let fb1 = TestFrameBuffer::new();
1697 let fb2 = TestFrameBuffer::default();
1698
1699 assert_eq!(fb1.data.frames.len(), fb2.data.frames.len());
1701 assert_eq!(fb1._align, fb2._align);
1702 }
1703
1704 #[test]
1705 fn test_memory_alignment() {
1706 let fb = TestFrameBuffer::new();
1707 let ptr = &fb as *const _ as usize;
1708
1709 assert_eq!(ptr % 8, 0);
1711 }
1712
1713 #[test]
1714 fn test_color_values() {
1715 let mut fb = TestFrameBuffer::new();
1716
1717 let colors = [
1719 (Color::RED, (255, 0, 0)),
1720 (Color::GREEN, (0, 255, 0)),
1721 (Color::BLUE, (0, 0, 255)),
1722 (Color::WHITE, (255, 255, 255)),
1723 (Color::BLACK, (0, 0, 0)),
1724 ];
1725
1726 for (i, (color, (r, g, b))) in colors.iter().enumerate() {
1727 fb.set_pixel(Point::new(i as i32, 0), *color);
1728 assert_eq!(color.r(), *r);
1729 assert_eq!(color.g(), *g);
1730 assert_eq!(color.b(), *b);
1731 }
1732 }
1733
1734 #[test]
1735 fn test_blanking_delay() {
1736 let mut row: Row<TEST_COLS> = Row::new();
1737 let prev_addr = 4;
1738
1739 row.format(prev_addr);
1740
1741 let oe_active = !cfg!(feature = "invert-oe");
1742
1743 let trail_blank_idx = get_mapped_index(TRAIL_BLANK_DELAY - 1);
1745 assert_eq!(row.data[trail_blank_idx].output_enable(), !oe_active);
1746
1747 let after_trail_idx = get_mapped_index(TRAIL_BLANK_DELAY);
1748 assert_eq!(row.data[after_trail_idx].output_enable(), oe_active);
1749
1750 let before_lead_idx = get_mapped_index(TEST_COLS - LEAD_BLANK_DELAY - 2);
1752 assert_eq!(row.data[before_lead_idx].output_enable(), oe_active);
1753
1754 let lead_blank_idx = get_mapped_index(TEST_COLS - LEAD_BLANK_DELAY - 1);
1755 assert_eq!(row.data[lead_blank_idx].output_enable(), !oe_active);
1756 }
1757
1758 #[test]
1759 fn test_esp32_mapping() {
1760 #[cfg(feature = "esp32-ordering")]
1762 {
1763 assert_eq!(map_index(0), 1);
1764 assert_eq!(map_index(1), 0);
1765 assert_eq!(map_index(2), 3);
1766 assert_eq!(map_index(3), 2);
1767 assert_eq!(map_index(4), 5);
1768 assert_eq!(map_index(5), 4);
1769 }
1770 #[cfg(not(feature = "esp32-ordering"))]
1771 {
1772 assert_eq!(map_index(0), 0);
1773 assert_eq!(map_index(1), 1);
1774 assert_eq!(map_index(2), 2);
1775 assert_eq!(map_index(3), 3);
1776 }
1777 }
1778
1779 #[test]
1780 fn test_bits_assertion() {
1781 assert!(TEST_BITS <= 8);
1784 }
1785
1786 #[test]
1787 fn test_fast_clear_method() {
1788 let mut fb = TestFrameBuffer::new();
1789
1790 fb.set_pixel_internal(10, 5, Color::RED);
1792 fb.set_pixel_internal(20, 10, Color::GREEN);
1793
1794 let mapped_col_10 = get_mapped_index(10);
1796 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].red1(), true);
1797
1798 fb.erase();
1800
1801 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].red1(), false);
1803 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].grn1(), false);
1804 assert_eq!(fb.data.frames[0].rows[5].data[mapped_col_10].blu1(), false);
1805
1806 let last_col = get_mapped_index(TEST_COLS - 1);
1808 assert_eq!(fb.data.frames[0].rows[5].data[last_col].latch(), true);
1809 }
1810
1811 const CHAR_W: i32 = 6;
1815 const CHAR_H: i32 = 10;
1816
1817 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 {
1837 for dx in 0..CHAR_W {
1838 let expected_on = reference
1839 .get_pixel(Point::new(dx, dy))
1840 .unwrap_or(Color::BLACK)
1841 != Color::BLACK;
1842
1843 let gx = (origin.x + dx) as usize;
1844 let gy = (origin.y + dy) as usize;
1845
1846 let frame0 = &fb.data.frames[0];
1853 let e = if gy < TEST_NROWS {
1854 &frame0.rows[gy].data[get_mapped_index(gx)]
1855 } else {
1856 &frame0.rows[gy - TEST_NROWS].data[get_mapped_index(gx)]
1857 };
1858
1859 let (r, g, b) = if gy >= TEST_NROWS {
1860 (e.red2(), e.grn2(), e.blu2())
1861 } else {
1862 (e.red1(), e.grn1(), e.blu1())
1863 };
1864
1865 if expected_on {
1866 assert!(r && g && b,);
1867 } else {
1868 assert!(!r && !g && !b);
1869 }
1870 }
1871 }
1872 }
1873
1874 #[test]
1875 fn test_draw_char_corners() {
1876 let upper_left = Point::new(0, 0);
1877 let lower_right = Point::new(TEST_COLS as i32 - CHAR_W, TEST_ROWS as i32 - CHAR_H);
1878
1879 let mut fb = TestFrameBuffer::new();
1880
1881 verify_glyph_at(&mut fb, upper_left);
1882 verify_glyph_at(&mut fb, lower_right);
1883 }
1884
1885 #[test]
1886 fn test_framebuffer_operations_trait_erase() {
1887 let mut fb = TestFrameBuffer::new();
1888
1889 fb.set_pixel_internal(10, 5, Color::RED);
1891 fb.set_pixel_internal(20, 10, Color::GREEN);
1892
1893 <TestFrameBuffer as FrameBufferOperations>::erase(&mut fb);
1895
1896 let mc10 = get_mapped_index(10);
1898 let mc20 = get_mapped_index(20);
1899 assert_eq!(fb.data.frames[0].rows[5].data[mc10].red1(), false);
1900 assert_eq!(fb.data.frames[0].rows[10].data[mc20].grn1(), false);
1901
1902 let last_col = get_mapped_index(TEST_COLS - 1);
1904 assert!(fb.data.frames[0].rows[0].data[last_col].latch());
1905 }
1906
1907 #[test]
1908 fn test_framebuffer_operations_trait_set_pixel() {
1909 let mut fb = TestFrameBuffer::new();
1910
1911 <TestFrameBuffer as FrameBufferOperations>::set_pixel(
1913 &mut fb,
1914 Point::new(8, 3),
1915 Color::BLUE,
1916 );
1917
1918 let idx = get_mapped_index(8);
1919 assert_eq!(fb.data.frames[0].rows[3].data[idx].blu1(), true);
1920 assert_eq!(fb.data.frames[0].rows[3].data[idx].red1(), false);
1921 assert_eq!(fb.data.frames[0].rows[3].data[idx].grn1(), false);
1922 }
1923
1924 #[test]
1925 fn test_entry_new_oe_matches_feature() {
1926 let entry = Entry::new();
1927 if cfg!(feature = "invert-oe") {
1928 assert!(entry.output_enable());
1929 } else {
1930 assert!(!entry.output_enable());
1931 }
1932 }
1933
1934 #[test]
1935 fn test_make_data_template_oe_polarity() {
1936 let mut row = Row::<TEST_COLS>::new();
1937 row.format(4);
1938
1939 let active_idx = get_mapped_index(TRAIL_BLANK_DELAY);
1940 let blank_idx = get_mapped_index(TEST_COLS - LEAD_BLANK_DELAY - 1);
1941 let latch_idx = get_mapped_index(TEST_COLS - 1);
1942
1943 if cfg!(feature = "invert-oe") {
1944 assert!(!row.data[active_idx].output_enable());
1945 assert!(row.data[blank_idx].output_enable());
1946 assert!(row.data[latch_idx].output_enable());
1947 } else {
1948 assert!(row.data[active_idx].output_enable());
1949 assert!(!row.data[blank_idx].output_enable());
1950 assert!(!row.data[latch_idx].output_enable());
1951 }
1952 }
1953
1954 static STATIC_FB: TestFrameBuffer = TestFrameBuffer::new();
1955
1956 #[test]
1957 fn test_static_construction_is_formatted() {
1958 let runtime_fb = TestFrameBuffer::new();
1959
1960 for (fi, frame) in STATIC_FB.data.frames.iter().enumerate() {
1961 for (ri, row) in frame.rows.iter().enumerate() {
1962 assert_eq!(
1963 row.data, runtime_fb.data.frames[fi].rows[ri].data,
1964 "static vs runtime mismatch at frame {fi}, row {ri}"
1965 );
1966 assert_eq!(
1967 row.gap, runtime_fb.data.frames[fi].rows[ri].gap,
1968 "static vs runtime gap mismatch at frame {fi}, row {ri}"
1969 );
1970 }
1971 }
1972 }
1973
1974 #[test]
1975 fn test_format_reinitializes_at_runtime() {
1976 let mut fb = TestFrameBuffer::new();
1977 fb.erase();
1978 fb.format();
1979
1980 for (fi, frame) in fb.data.frames.iter().enumerate() {
1981 for (ri, row) in frame.rows.iter().enumerate() {
1982 assert_eq!(
1983 row.data, STATIC_FB.data.frames[fi].rows[ri].data,
1984 "re-formatted vs static mismatch at frame {fi}, row {ri}"
1985 );
1986 }
1987 }
1988 }
1989}