1use core::convert::Infallible;
138
139use crate::{
140 map_row_index, slot_addresses, BcmLenReps, FrameBuffer, FrameBufferOperations,
141 MutableFrameBuffer, BCM_SEQUENCE_CAPACITY,
142};
143use bitfield::bitfield;
144use embedded_dma::ReadBuffer;
145use embedded_graphics::pixelcolor::RgbColor;
146use embedded_graphics::prelude::Point;
147
148use super::Color;
149use crate::{INTER_ROW_BLANK, LEAD_BLANK_DELAY, TRAIL_BLANK_DELAY};
150
151#[cfg(not(feature = "invert-oe"))]
152const OE_ACTIVE: u16 = 0b1_0000_0000;
153#[cfg(not(feature = "invert-oe"))]
154const OE_BLANK: u16 = 0;
155
156#[cfg(feature = "invert-oe")]
157const OE_ACTIVE: u16 = 0;
158#[cfg(feature = "invert-oe")]
159const OE_BLANK: u16 = 0b1_0000_0000;
160
161#[allow(clippy::absurd_extreme_comparisons)]
166#[inline]
167const fn make_data_template<const COLS: usize>(addr: u8) -> [Entry; COLS] {
168 let mut data = [Entry::new(); COLS];
169 let mut i = 0;
170
171 while i < COLS {
172 let mut entry = Entry::new();
173 entry.0 = addr as u16 | OE_BLANK;
174
175 if i == COLS - 1 {
176 entry.0 |= 0b0010_0000; } else if i >= TRAIL_BLANK_DELAY && i < COLS.saturating_sub(LEAD_BLANK_DELAY + 1) {
178 entry.0 = (entry.0 & !0b1_0000_0000) | OE_ACTIVE;
179 }
180
181 data[map_index(i)] = entry;
182 i += 1;
183 }
184
185 data
186}
187
188bitfield! {
189 #[derive(Clone, Copy, Default, PartialEq)]
210 #[repr(transparent)]
211 struct Entry(u16);
212 dummy2, set_dummy2: 15;
213 blu2, set_blu2: 14;
214 grn2, set_grn2: 13;
215 red2, set_red2: 12;
216 blu1, set_blu1: 11;
217 grn1, set_grn1: 10;
218 red1, set_red1: 9;
219 output_enable, set_output_enable: 8;
220 dummy1, set_dummy1: 7;
221 dummy0, set_dummy0: 6;
222 latch, set_latch: 5;
223 addr, set_addr: 4, 0;
224}
225
226impl core::fmt::Debug for Entry {
227 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
228 f.debug_tuple("Entry")
229 .field(&format_args!("{:#x}", self.0))
230 .finish()
231 }
232}
233
234#[cfg(feature = "defmt")]
235impl defmt::Format for Entry {
236 fn format(&self, f: defmt::Formatter) {
237 defmt::write!(f, "Entry({=u16:#x})", self.0);
238 }
239}
240
241impl Entry {
242 const fn new() -> Self {
243 #[cfg(feature = "invert-oe")]
244 {
245 Self(0b1_0000_0000)
246 }
247 #[cfg(not(feature = "invert-oe"))]
248 {
249 Self(0)
250 }
251 }
252
253 const COLOR0_MASK: u16 = 0b0000_1110_0000_0000; const COLOR1_MASK: u16 = 0b0111_0000_0000_0000; #[inline]
258 fn set_color0_bits(&mut self, bits: u8) {
259 let bits16 = u16::from(bits) << 9;
260 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits16 & Self::COLOR0_MASK);
261 }
262
263 #[inline]
264 fn set_color1_bits(&mut self, bits: u8) {
265 let bits16 = u16::from(bits) << 12;
266 self.0 = (self.0 & !Self::COLOR1_MASK) | (bits16 & Self::COLOR1_MASK);
267 }
268}
269
270#[derive(Clone, Copy, PartialEq, Debug)]
279#[repr(C)]
280struct Row<const COLS: usize> {
281 data: [Entry; COLS],
282 gap: [Entry; INTER_ROW_BLANK],
283}
284
285const fn map_index(i: usize) -> usize {
286 #[cfg(feature = "esp32-ordering")]
287 {
288 i ^ 1
289 }
290 #[cfg(not(feature = "esp32-ordering"))]
291 {
292 i
293 }
294}
295
296impl<const COLS: usize> Default for Row<COLS> {
297 fn default() -> Self {
298 Self::new()
299 }
300}
301
302impl<const COLS: usize> Row<COLS> {
303 pub const fn new() -> Self {
304 Self {
305 data: [Entry::new(); COLS],
306 gap: [Entry::new(); INTER_ROW_BLANK],
307 }
308 }
309
310 pub const fn format(&mut self, prev_addr: u8) {
311 let template = make_data_template::<COLS>(prev_addr);
312 let mut i = 0;
313 while i < COLS {
314 self.data[i] = template[i];
315 i += 1;
316 }
317
318 let gap_val = (prev_addr as u16) | OE_BLANK;
319 let mut i = 0;
320 #[allow(clippy::absurd_extreme_comparisons)]
322 while i < INTER_ROW_BLANK {
323 self.gap[i].0 = gap_val;
324 i += 1;
325 }
326 }
327
328 #[inline]
331 pub fn clear_colors(&mut self) {
332 const COLOR_CLEAR_MASK: u16 = !0b0111_1110_0000_0000; for entry in &mut self.data {
336 entry.0 &= COLOR_CLEAR_MASK;
337 }
338 }
339
340 #[inline]
341 pub fn set_color0(&mut self, col: usize, r: bool, g: bool, b: bool) {
342 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
343 let col = map_index(col);
344 self.data[col].set_color0_bits(bits);
345 }
346
347 #[inline]
348 pub fn set_color1(&mut self, col: usize, r: bool, g: bool, b: bool) {
349 let bits = (u8::from(b) << 2) | (u8::from(g) << 1) | u8::from(r);
350 let col = map_index(col);
351 self.data[col].set_color1_bits(bits);
352 }
353}
354
355#[derive(Copy, Clone, Debug)]
356#[repr(C)]
357struct Frame<const ROWS: usize, const COLS: usize, const NROWS: usize> {
358 rows: [Row<COLS>; NROWS],
359}
360
361impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Frame<ROWS, COLS, NROWS> {
362 pub const fn new() -> Self {
363 Self {
364 rows: [Row::new(); NROWS],
365 }
366 }
367
368 pub const fn format(&mut self) {
369 let mut slot = 0;
370 while slot < NROWS {
371 let (prev_addr, _) = slot_addresses::<NROWS>(slot);
372 self.rows[slot].format(prev_addr);
373 slot += 1;
374 }
375 }
376
377 #[inline]
379 pub fn clear_colors(&mut self) {
380 for row in &mut self.rows {
381 row.clear_colors();
382 }
383 }
384
385 #[inline]
386 pub fn set_pixel(&mut self, y: usize, x: usize, red: bool, green: bool, blue: bool) {
387 let row_idx = map_row_index::<NROWS>(if y < NROWS { y } else { y - NROWS });
388 let row = &mut self.rows[row_idx];
389 if y < NROWS {
390 row.set_color0(x, red, green, blue);
391 } else {
392 row.set_color1(x, red, green, blue);
393 }
394 }
395}
396
397impl<const ROWS: usize, const COLS: usize, const NROWS: usize> Default
398 for Frame<ROWS, COLS, NROWS>
399{
400 fn default() -> Self {
401 Self::new()
402 }
403}
404
405#[derive(Copy, Clone, Debug)]
408#[repr(C)]
409struct FrameData<const ROWS: usize, const COLS: usize, const NROWS: usize, const FRAME_COUNT: usize>
410{
411 frames: [Frame<ROWS, COLS, NROWS>; FRAME_COUNT],
412 #[cfg(all(feature = "tail-closes-latch", feature = "esp32-ordering"))]
413 padding: Entry,
414 #[cfg(feature = "tail-closes-latch")]
415 tail: Entry,
416}
417
418impl<const ROWS: usize, const COLS: usize, const NROWS: usize, const FRAME_COUNT: usize>
419 FrameData<ROWS, COLS, NROWS, FRAME_COUNT>
420{
421 const fn new() -> Self {
422 Self {
423 frames: [Frame::new(); FRAME_COUNT],
424 #[cfg(all(feature = "esp32-ordering", feature = "tail-closes-latch"))]
425 padding: Entry::new(),
426 #[cfg(feature = "tail-closes-latch")]
427 tail: Entry::new(),
428 }
429 }
430}
431
432#[derive(Copy, Clone)]
457#[repr(C)]
458pub struct DmaFrameBuffer<
459 const ROWS: usize,
460 const COLS: usize,
461 const NROWS: usize,
462 const BITS: u8,
463 const FRAME_COUNT: usize,
464> {
465 _align: u64,
466 data: FrameData<ROWS, COLS, NROWS, FRAME_COUNT>,
467}
468
469impl<
470 const ROWS: usize,
471 const COLS: usize,
472 const NROWS: usize,
473 const BITS: u8,
474 const FRAME_COUNT: usize,
475 > Default for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
476{
477 fn default() -> Self {
478 Self::new()
479 }
480}
481
482impl<
483 const ROWS: usize,
484 const COLS: usize,
485 const NROWS: usize,
486 const BITS: u8,
487 const FRAME_COUNT: usize,
488 > DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
489{
490 #[must_use]
515 pub const fn new() -> Self {
516 assert!(BITS <= 8);
517 #[cfg(feature = "esp32-ordering")]
518 assert!(
519 COLS % 2 == 0,
520 "esp32-ordering feature requires COLS to be even"
521 );
522
523 let mut instance = Self {
524 _align: 0,
525 data: FrameData::new(),
526 };
527
528 instance.format();
529 instance
530 }
531
532 #[must_use]
535 #[deprecated(since = "0.11.0", note = "use BCM_SEQUENCE instead")]
536 pub const fn bcm_chunk_count() -> usize {
537 1
538 }
539
540 #[must_use]
544 #[deprecated(since = "0.11.0", note = "use BCM_SEQUENCE instead")]
545 pub const fn bcm_chunk_bytes() -> usize {
546 core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>()
547 }
548
549 #[inline]
569 pub const fn format(&mut self) {
570 let mut i = 0;
571 while i < FRAME_COUNT {
572 self.data.frames[i].format();
573 i += 1;
574 }
575 #[cfg(feature = "tail-closes-latch")]
576 {
577 self.data.tail.0 = 0x1f | OE_BLANK;
578 }
579 #[cfg(all(feature = "esp32-ordering", feature = "tail-closes-latch"))]
580 {
581 self.data.padding.0 = 0x1f | OE_BLANK;
582 }
583 }
584
585 #[inline]
605 pub fn erase(&mut self) {
606 for frame in &mut self.data.frames {
607 frame.clear_colors();
608 }
609 }
610
611 pub fn set_pixel(&mut self, p: Point, color: Color) {
627 if p.x < 0 || p.y < 0 {
628 return;
629 }
630 self.set_pixel_internal(p.x as usize, p.y as usize, color);
631 }
632
633 #[inline]
634 fn frames_on(v: u8) -> usize {
635 (v as usize) >> (8 - BITS)
637 }
638
639 #[inline]
640 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Color) {
641 if x >= COLS || y >= ROWS {
642 return;
643 }
644
645 #[cfg(feature = "skip-black-pixels")]
648 if color == Color::BLACK {
649 return;
650 }
651
652 let red_frames = Self::frames_on(color.r());
654 let green_frames = Self::frames_on(color.g());
655 let blue_frames = Self::frames_on(color.b());
656
657 for (frame_idx, frame) in self.data.frames.iter_mut().enumerate() {
659 frame.set_pixel(
660 y,
661 x,
662 frame_idx < red_frames,
663 frame_idx < green_frames,
664 frame_idx < blue_frames,
665 );
666 }
667 }
668}
669
670impl<
671 const ROWS: usize,
672 const COLS: usize,
673 const NROWS: usize,
674 const BITS: u8,
675 const FRAME_COUNT: usize,
676 > FrameBufferOperations for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
677{
678 #[inline]
679 fn erase(&mut self) {
680 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::erase(self);
681 }
682
683 #[inline]
684 fn set_pixel(&mut self, p: Point, color: Color) {
685 DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::set_pixel(self, p, color);
686 }
687}
688
689impl<
690 const ROWS: usize,
691 const COLS: usize,
692 const NROWS: usize,
693 const BITS: u8,
694 const FRAME_COUNT: usize,
695 > embedded_graphics::prelude::OriginDimensions
696 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
697{
698 fn size(&self) -> embedded_graphics::prelude::Size {
699 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
700 }
701}
702
703impl<
704 const ROWS: usize,
705 const COLS: usize,
706 const NROWS: usize,
707 const BITS: u8,
708 const FRAME_COUNT: usize,
709 > embedded_graphics::prelude::OriginDimensions
710 for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
711{
712 fn size(&self) -> embedded_graphics::prelude::Size {
713 embedded_graphics::prelude::Size::new(COLS as u32, ROWS as u32)
714 }
715}
716
717impl<
718 const ROWS: usize,
719 const COLS: usize,
720 const NROWS: usize,
721 const BITS: u8,
722 const FRAME_COUNT: usize,
723 > embedded_graphics::draw_target::DrawTarget
724 for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
725{
726 type Color = Color;
727
728 type Error = Infallible;
729
730 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
731 where
732 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
733 {
734 for pixel in pixels {
735 if pixel.0.x < 0 || pixel.0.y < 0 {
736 continue;
737 }
738 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
739 }
740 Ok(())
741 }
742}
743
744unsafe impl<
750 const ROWS: usize,
751 const COLS: usize,
752 const NROWS: usize,
753 const BITS: u8,
754 const FRAME_COUNT: usize,
755 > ReadBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
756{
757 type Word = u8;
758
759 unsafe fn read_buffer(&self) -> (*const u8, usize) {
760 let ptr = (&raw const self.data).cast::<u8>();
761 let len = core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>();
762 (ptr, len)
763 }
764}
765
766unsafe impl<
772 const ROWS: usize,
773 const COLS: usize,
774 const NROWS: usize,
775 const BITS: u8,
776 const FRAME_COUNT: usize,
777 > ReadBuffer for &mut DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
778{
779 type Word = u8;
780
781 unsafe fn read_buffer(&self) -> (*const u8, usize) {
782 let ptr = (&raw const self.data).cast::<u8>();
783 let len = core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>();
784 (ptr, len)
785 }
786}
787
788impl<
789 const ROWS: usize,
790 const COLS: usize,
791 const NROWS: usize,
792 const BITS: u8,
793 const FRAME_COUNT: usize,
794 > core::fmt::Debug for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
795{
796 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
797 let brightness_step = 1 << (8 - BITS);
798 let size = core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>();
799 f.debug_struct("DmaFrameBuffer")
800 .field("size", &size)
801 .field("frame_count", &self.data.frames.len())
802 .field("frame_size", &core::mem::size_of_val(&self.data.frames[0]))
803 .field("brightness_step", &&brightness_step)
804 .finish_non_exhaustive()
805 }
806}
807
808#[cfg(feature = "defmt")]
809impl<
810 const ROWS: usize,
811 const COLS: usize,
812 const NROWS: usize,
813 const BITS: u8,
814 const FRAME_COUNT: usize,
815 > defmt::Format for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
816{
817 fn format(&self, f: defmt::Formatter) {
818 let brightness_step = 1 << (8 - BITS);
819 defmt::write!(
820 f,
821 "DmaFrameBuffer<{}, {}, {}, {}, {}>",
822 ROWS,
823 COLS,
824 NROWS,
825 BITS,
826 FRAME_COUNT
827 );
828 defmt::write!(
829 f,
830 " size: {}",
831 core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>()
832 );
833 defmt::write!(
834 f,
835 " frame_size: {}",
836 core::mem::size_of_val(&self.data.frames[0])
837 );
838 defmt::write!(f, " brightness_step: {}", brightness_step);
839 }
840}
841
842impl<
843 const ROWS: usize,
844 const COLS: usize,
845 const NROWS: usize,
846 const BITS: u8,
847 const FRAME_COUNT: usize,
848 > FrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
849{
850 type Word = u16;
851
852 const BCM_SEQUENCE: [BcmLenReps; BCM_SEQUENCE_CAPACITY] = {
853 let mut seq = [BcmLenReps::ZERO; BCM_SEQUENCE_CAPACITY];
854 seq[0] = BcmLenReps {
855 len: core::mem::size_of::<FrameData<ROWS, COLS, NROWS, FRAME_COUNT>>(),
856 reps: 1,
857 };
858 seq
859 };
860
861 const BCM_SEQUENCE_LEN: usize = 1;
862
863 const BCM_SEQUENCE_COUNT: usize = 1;
864
865 fn bcm_segment_ptr(&self, index: usize) -> *const u8 {
866 assert!(index == 0, "threshold DmaFrameBuffer has only 1 segment");
867 (&raw const self.data).cast::<u8>()
868 }
869}
870
871impl<
872 const ROWS: usize,
873 const COLS: usize,
874 const NROWS: usize,
875 const BITS: u8,
876 const FRAME_COUNT: usize,
877 > MutableFrameBuffer for DmaFrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>
878{
879}
880
881#[cfg(test)]
882mod tests {
883 extern crate std;
884
885 use std::format;
886 use std::vec;
887
888 use super::*;
889 use embedded_graphics::pixelcolor::RgbColor;
890 use embedded_graphics::prelude::*;
891 use embedded_graphics::primitives::{Circle, PrimitiveStyle, Rectangle};
892
893 const TEST_ROWS: usize = 32;
894 const TEST_COLS: usize = if LEAD_BLANK_DELAY + TRAIL_BLANK_DELAY + 2 > 64 {
895 128
896 } else {
897 64
898 };
899 const TEST_NROWS: usize = TEST_ROWS / 2;
900 const TEST_BITS: u8 = 3;
901 const TEST_FRAME_COUNT: usize = (1 << TEST_BITS) - 1; type TestFrameBuffer =
904 DmaFrameBuffer<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_BITS, TEST_FRAME_COUNT>;
905
906 fn get_mapped_index(index: usize) -> usize {
908 map_index(index)
909 }
910
911 fn get_mapped_row(row_idx: usize) -> usize {
914 map_row_index::<TEST_NROWS>(row_idx)
915 }
916
917 #[test]
918 fn test_entry_construction() {
919 let entry = Entry::new();
920 let expect_oe = cfg!(feature = "invert-oe");
921 assert_eq!(entry.dummy2(), false);
922 assert_eq!(entry.blu2(), false);
923 assert_eq!(entry.grn2(), false);
924 assert_eq!(entry.red2(), false);
925 assert_eq!(entry.blu1(), false);
926 assert_eq!(entry.grn1(), false);
927 assert_eq!(entry.red1(), false);
928 assert_eq!(entry.output_enable(), expect_oe);
929 assert_eq!(entry.dummy1(), false);
930 assert_eq!(entry.dummy0(), false);
931 assert_eq!(entry.latch(), false);
932 assert_eq!(entry.addr(), 0);
933 }
934
935 #[test]
936 fn test_entry_setters() {
937 let mut entry = Entry::new();
938
939 entry.set_dummy2(true);
940 assert_eq!(entry.dummy2(), true);
941 assert_eq!(entry.0 & 0b1000000000000000, 0b1000000000000000);
942
943 entry.set_blu2(true);
944 assert_eq!(entry.blu2(), true);
945 assert_eq!(entry.0 & 0b0100000000000000, 0b0100000000000000);
946
947 entry.set_grn2(true);
948 assert_eq!(entry.grn2(), true);
949 assert_eq!(entry.0 & 0b0010000000000000, 0b0010000000000000);
950
951 entry.set_red2(true);
952 assert_eq!(entry.red2(), true);
953 assert_eq!(entry.0 & 0b0001000000000000, 0b0001000000000000);
954
955 entry.set_blu1(true);
956 assert_eq!(entry.blu1(), true);
957 assert_eq!(entry.0 & 0b0000100000000000, 0b0000100000000000);
958
959 entry.set_grn1(true);
960 assert_eq!(entry.grn1(), true);
961 assert_eq!(entry.0 & 0b0000010000000000, 0b0000010000000000);
962
963 entry.set_red1(true);
964 assert_eq!(entry.red1(), true);
965 assert_eq!(entry.0 & 0b0000001000000000, 0b0000001000000000);
966
967 entry.set_output_enable(true);
968 assert_eq!(entry.output_enable(), true);
969 assert_eq!(entry.0 & 0b0000000100000000, 0b0000000100000000);
970
971 entry.set_dummy1(true);
972 assert_eq!(entry.dummy1(), true);
973 assert_eq!(entry.0 & 0b0000000010000000, 0b0000000010000000);
974
975 entry.set_dummy0(true);
976 assert_eq!(entry.dummy0(), true);
977 assert_eq!(entry.0 & 0b0000000001000000, 0b0000000001000000);
978
979 entry.set_latch(true);
980 assert_eq!(entry.latch(), true);
981 assert_eq!(entry.0 & 0b0000000000100000, 0b0000000000100000);
982
983 entry.set_addr(0b11111);
984 assert_eq!(entry.addr(), 0b11111);
985 assert_eq!(entry.0 & 0b0000000000011111, 0b0000000000011111);
986 }
987
988 #[test]
989 fn test_entry_bit_isolation() {
990 let mut entry = Entry::new();
991 let expect_oe = cfg!(feature = "invert-oe");
992
993 entry.set_addr(0b11111);
995 entry.set_latch(true);
996 assert_eq!(entry.addr(), 0b11111);
997 assert_eq!(entry.latch(), true);
998 assert_eq!(entry.output_enable(), expect_oe);
999 assert_eq!(entry.red1(), false);
1000
1001 entry.set_red1(true);
1002 entry.set_grn2(true);
1003 assert_eq!(entry.addr(), 0b11111);
1004 assert_eq!(entry.latch(), true);
1005 assert_eq!(entry.red1(), true);
1006 assert_eq!(entry.grn2(), true);
1007 assert_eq!(entry.blu1(), false);
1008 assert_eq!(entry.red2(), false);
1009 }
1010
1011 #[test]
1012 fn test_entry_set_color0() {
1013 let mut entry = Entry::new();
1014
1015 let bits = (u8::from(true) << 2) | (u8::from(false) << 1) | u8::from(true); entry.set_color0_bits(bits);
1017 assert_eq!(entry.red1(), true);
1018 assert_eq!(entry.grn1(), false);
1019 assert_eq!(entry.blu1(), true);
1020 assert_eq!(entry.0 & 0b0000101000000000, 0b0000101000000000); }
1023
1024 #[test]
1025 fn test_entry_set_color1() {
1026 let mut entry = Entry::new();
1027
1028 let bits = (u8::from(true) << 2) | (u8::from(true) << 1) | u8::from(false); entry.set_color1_bits(bits);
1030 assert_eq!(entry.red2(), false);
1031 assert_eq!(entry.grn2(), true);
1032 assert_eq!(entry.blu2(), true);
1033 assert_eq!(entry.0 & 0b0110000000000000, 0b0110000000000000); }
1036
1037 #[test]
1038 fn test_entry_debug_formatting() {
1039 let entry = Entry(0x1234);
1040 let debug_str = format!("{:?}", entry);
1041 assert_eq!(debug_str, "Entry(0x1234)");
1042
1043 let entry = Entry(0xabcd);
1044 let debug_str = format!("{:?}", entry);
1045 assert_eq!(debug_str, "Entry(0xabcd)");
1046 }
1047
1048 #[test]
1049 fn test_row_construction() {
1050 let row: Row<TEST_COLS> = Row::new();
1051 assert_eq!(row.data.len(), TEST_COLS);
1052
1053 let expected = Entry::new().0;
1054 for entry in &row.data {
1055 assert_eq!(entry.0, expected);
1056 }
1057 }
1058
1059 #[test]
1060 fn test_row_format() {
1061 let mut row: Row<TEST_COLS> = Row::new();
1062 let prev_addr = 4;
1063
1064 row.format(prev_addr);
1065
1066 let oe_active = !cfg!(feature = "invert-oe");
1067
1068 for (physical_i, entry) in row.data.iter().enumerate() {
1069 let logical_i = get_mapped_index(physical_i);
1070
1071 assert_eq!(entry.addr(), u16::from(prev_addr));
1072
1073 if logical_i == TEST_COLS - 1 {
1074 assert_eq!(entry.latch(), true);
1075 assert_eq!(entry.output_enable(), !oe_active);
1076 } else {
1077 assert_eq!(entry.latch(), false);
1078 if logical_i >= TRAIL_BLANK_DELAY && logical_i < TEST_COLS - LEAD_BLANK_DELAY - 1 {
1079 assert_eq!(entry.output_enable(), oe_active);
1080 } else {
1081 assert_eq!(entry.output_enable(), !oe_active);
1082 }
1083 }
1084 }
1085 }
1086
1087 #[test]
1088 fn test_row_set_color0() {
1089 let mut row: Row<TEST_COLS> = Row::new();
1090
1091 row.set_color0(0, true, false, true);
1092
1093 let mapped_col_0 = get_mapped_index(0);
1094 assert_eq!(row.data[mapped_col_0].red1(), true);
1095 assert_eq!(row.data[mapped_col_0].grn1(), false);
1096 assert_eq!(row.data[mapped_col_0].blu1(), true);
1097
1098 row.set_color0(1, false, true, false);
1100
1101 let mapped_col_1 = get_mapped_index(1);
1102 assert_eq!(row.data[mapped_col_1].red1(), false);
1103 assert_eq!(row.data[mapped_col_1].grn1(), true);
1104 assert_eq!(row.data[mapped_col_1].blu1(), false);
1105 }
1106
1107 #[test]
1108 fn test_row_set_color1() {
1109 let mut row: Row<TEST_COLS> = Row::new();
1110
1111 row.set_color1(0, true, true, false);
1112
1113 let mapped_col_0 = get_mapped_index(0);
1114 assert_eq!(row.data[mapped_col_0].red2(), true);
1115 assert_eq!(row.data[mapped_col_0].grn2(), true);
1116 assert_eq!(row.data[mapped_col_0].blu2(), false);
1117 }
1118
1119 #[test]
1120 fn test_row_default() {
1121 let row1: Row<TEST_COLS> = Row::new();
1122 let row2: Row<TEST_COLS> = Row::default();
1123
1124 assert_eq!(row1, row2);
1126 assert_eq!(row1.data.len(), row2.data.len());
1127
1128 let expected = Entry::new().0;
1129 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1130 assert_eq!(entry1.0, entry2.0);
1131 assert_eq!(entry1.0, expected);
1132 }
1133 }
1134
1135 #[test]
1136 fn test_frame_construction() {
1137 let frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1138 assert_eq!(frame.rows.len(), TEST_NROWS);
1139 }
1140
1141 #[test]
1142 fn test_frame_format() {
1143 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1144
1145 frame.format();
1146
1147 for slot in 0..TEST_NROWS {
1148 let (prev_addr, _) = slot_addresses::<TEST_NROWS>(slot);
1149 let row = &frame.rows[slot];
1150
1151 let last_pixel_idx = get_mapped_index(TEST_COLS - 1);
1152 assert_eq!(row.data[last_pixel_idx].addr(), u16::from(prev_addr));
1153 assert_eq!(row.data[last_pixel_idx].latch(), true);
1154
1155 let first_pixel_idx = get_mapped_index(0);
1156 assert_eq!(row.data[first_pixel_idx].addr(), u16::from(prev_addr));
1157 assert_eq!(row.data[first_pixel_idx].latch(), false);
1158 }
1159 }
1160
1161 #[test]
1162 fn test_frame_set_pixel() {
1163 let mut frame: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1164
1165 frame.set_pixel(5, 10, true, false, true);
1167
1168 let mapped_col_10 = get_mapped_index(10);
1169 assert_eq!(
1170 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1171 true
1172 );
1173 assert_eq!(
1174 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1175 false
1176 );
1177 assert_eq!(
1178 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1179 true
1180 );
1181
1182 frame.set_pixel(TEST_NROWS + 5, 15, false, true, false);
1184
1185 let mapped_col_15 = get_mapped_index(15);
1186 assert_eq!(
1187 frame.rows[get_mapped_row(5)].data[mapped_col_15].red2(),
1188 false
1189 );
1190 assert_eq!(
1191 frame.rows[get_mapped_row(5)].data[mapped_col_15].grn2(),
1192 true
1193 );
1194 assert_eq!(
1195 frame.rows[get_mapped_row(5)].data[mapped_col_15].blu2(),
1196 false
1197 );
1198 }
1199
1200 #[test]
1201 #[cfg(feature = "reverse-row-order")]
1202 fn test_reverse_row_order() {
1203 let mut fb = TestFrameBuffer::new();
1204
1205 let first = &fb.data.frames[0].rows[0];
1209 assert_eq!(first.data[get_mapped_index(0)].addr(), 0);
1210 let last = &fb.data.frames[0].rows[TEST_NROWS - 1];
1212 assert_eq!(last.data[get_mapped_index(0)].addr(), 1);
1213
1214 fb.set_pixel(Point::new(3, 0), Color::RED);
1216 let col3 = get_mapped_index(3);
1217 assert!(fb.data.frames[0].rows[TEST_NROWS - 1].data[col3].red1());
1218 assert!(!fb.data.frames[0].rows[0].data[col3].red1());
1219 }
1220
1221 #[test]
1222 fn test_frame_default() {
1223 let frame1: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::new();
1224 let frame2: Frame<TEST_ROWS, TEST_COLS, TEST_NROWS> = Frame::default();
1225
1226 assert_eq!(frame1.rows.len(), frame2.rows.len());
1228
1229 let expected = Entry::new().0;
1230 for (row1, row2) in frame1.rows.iter().zip(frame2.rows.iter()) {
1231 assert_eq!(row1, row2);
1232
1233 for (entry1, entry2) in row1.data.iter().zip(row2.data.iter()) {
1234 assert_eq!(entry1.0, entry2.0);
1235 assert_eq!(entry1.0, expected);
1236 }
1237 }
1238 }
1239
1240 #[test]
1241 fn test_dma_framebuffer_construction() {
1242 let fb = TestFrameBuffer::new();
1243 assert_eq!(fb.data.frames.len(), TEST_FRAME_COUNT);
1244 assert_eq!(fb._align, 0);
1245 }
1246
1247 #[test]
1248 #[allow(deprecated)]
1249 fn test_bcm_chunk_info() {
1250 let expected_size =
1251 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>();
1252 assert_eq!(TestFrameBuffer::bcm_chunk_bytes(), expected_size);
1253 assert_eq!(TestFrameBuffer::bcm_chunk_count(), 1);
1254 }
1255
1256 #[test]
1257 fn test_dma_framebuffer_erase() {
1258 let fb = TestFrameBuffer::new();
1259
1260 for frame in &fb.data.frames {
1261 for slot in 0..TEST_NROWS {
1262 let (prev_addr, _) = slot_addresses::<TEST_NROWS>(slot);
1263 let row = &frame.rows[slot];
1264
1265 let last_pixel_idx = get_mapped_index(TEST_COLS - 1);
1266 assert_eq!(row.data[last_pixel_idx].addr(), u16::from(prev_addr));
1267 assert_eq!(row.data[last_pixel_idx].latch(), true);
1268
1269 let first_pixel_idx = get_mapped_index(0);
1270 assert_eq!(row.data[first_pixel_idx].addr(), u16::from(prev_addr));
1271 assert_eq!(row.data[first_pixel_idx].latch(), false);
1272 }
1273 }
1274 }
1275
1276 #[test]
1277 fn test_dma_framebuffer_set_pixel_bounds() {
1278 let mut fb = TestFrameBuffer::new();
1279
1280 fb.set_pixel(Point::new(-1, 5), Color::RED);
1282 fb.set_pixel(Point::new(5, -1), Color::RED);
1283
1284 fb.set_pixel(Point::new(TEST_COLS as i32, 5), Color::RED);
1286 fb.set_pixel(Point::new(5, TEST_ROWS as i32), Color::RED);
1287 }
1288
1289 #[test]
1290 fn test_dma_framebuffer_set_pixel_internal() {
1291 let mut fb = TestFrameBuffer::new();
1292
1293 let red_color = Color::RED;
1294 fb.set_pixel_internal(10, 5, red_color);
1295
1296 for frame in &fb.data.frames {
1300 let mapped_col_10 = get_mapped_index(10);
1302 assert_eq!(
1303 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1304 true
1305 );
1306 assert_eq!(
1307 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1308 false
1309 );
1310 assert_eq!(
1311 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1312 false
1313 );
1314 }
1315 }
1316
1317 #[test]
1318 fn test_dma_framebuffer_brightness_modulation() {
1319 let mut fb = TestFrameBuffer::new();
1320
1321 let brightness_step = 1 << (8 - TEST_BITS); let test_brightness = brightness_step * 3; let color = Color::from(embedded_graphics::pixelcolor::Rgb888::new(
1325 test_brightness,
1326 0,
1327 0,
1328 ));
1329
1330 fb.set_pixel_internal(0, 0, color);
1331
1332 for (frame_idx, frame) in fb.data.frames.iter().enumerate() {
1335 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1336 let should_be_active = test_brightness >= frame_threshold;
1337
1338 let mapped_col_0 = get_mapped_index(0);
1339 assert_eq!(
1340 frame.rows[get_mapped_row(0)].data[mapped_col_0].red1(),
1341 should_be_active
1342 );
1343 }
1344 }
1345
1346 #[test]
1347 fn test_origin_dimensions() {
1348 let fb = TestFrameBuffer::new();
1349 let size = fb.size();
1350 assert_eq!(size.width, TEST_COLS as u32);
1351 assert_eq!(size.height, TEST_ROWS as u32);
1352
1353 let size = (&fb).size();
1355 assert_eq!(size.width, TEST_COLS as u32);
1356 assert_eq!(size.height, TEST_ROWS as u32);
1357
1358 let mut fb = TestFrameBuffer::new();
1360 let fb_ref = &mut fb;
1361 let size = fb_ref.size();
1362 assert_eq!(size.width, TEST_COLS as u32);
1363 assert_eq!(size.height, TEST_ROWS as u32);
1364 }
1365
1366 #[test]
1367 fn test_draw_target() {
1368 let mut fb = TestFrameBuffer::new();
1369
1370 let pixels = vec![
1371 embedded_graphics::Pixel(Point::new(0, 0), Color::RED),
1372 embedded_graphics::Pixel(Point::new(1, 1), Color::GREEN),
1373 embedded_graphics::Pixel(Point::new(2, 2), Color::BLUE),
1374 ];
1375
1376 let result = fb.draw_iter(pixels);
1377 assert!(result.is_ok());
1378
1379 let result = (&mut fb).draw_iter(vec![embedded_graphics::Pixel(
1381 Point::new(3, 3),
1382 Color::WHITE,
1383 )]);
1384 assert!(result.is_ok());
1385 }
1386
1387 #[test]
1388 fn test_draw_iter_pixel_verification() {
1389 let mut fb = TestFrameBuffer::new();
1390
1391 let pixels = vec![
1393 embedded_graphics::Pixel(Point::new(5, 2), Color::RED), embedded_graphics::Pixel(Point::new(10, 5), Color::GREEN), embedded_graphics::Pixel(Point::new(15, 8), Color::BLUE), embedded_graphics::Pixel(Point::new(20, 10), Color::WHITE), embedded_graphics::Pixel(Point::new(25, (TEST_NROWS + 3) as i32), Color::RED), embedded_graphics::Pixel(Point::new(30, (TEST_NROWS + 7) as i32), Color::GREEN), embedded_graphics::Pixel(Point::new(35, (TEST_NROWS + 12) as i32), Color::BLUE), embedded_graphics::Pixel(Point::new(40, 1), Color::BLACK), ];
1405
1406 let result = fb.draw_iter(pixels);
1407 assert!(result.is_ok());
1408
1409 let first_frame = &fb.data.frames[0];
1411 let brightness_step = 1 << (8 - TEST_BITS); let first_frame_threshold = brightness_step; let col_idx = get_mapped_index(5);
1417 assert_eq!(
1418 first_frame.rows[get_mapped_row(2)].data[col_idx].red1(),
1419 Color::RED.r() >= first_frame_threshold
1420 );
1421 assert_eq!(
1422 first_frame.rows[get_mapped_row(2)].data[col_idx].grn1(),
1423 Color::RED.g() >= first_frame_threshold
1424 );
1425 assert_eq!(
1426 first_frame.rows[get_mapped_row(2)].data[col_idx].blu1(),
1427 Color::RED.b() >= first_frame_threshold
1428 );
1429
1430 let col_idx = get_mapped_index(10);
1432 assert_eq!(
1433 first_frame.rows[get_mapped_row(5)].data[col_idx].red1(),
1434 Color::GREEN.r() >= first_frame_threshold
1435 );
1436 assert_eq!(
1437 first_frame.rows[get_mapped_row(5)].data[col_idx].grn1(),
1438 Color::GREEN.g() >= first_frame_threshold
1439 );
1440 assert_eq!(
1441 first_frame.rows[get_mapped_row(5)].data[col_idx].blu1(),
1442 Color::GREEN.b() >= first_frame_threshold
1443 );
1444
1445 let col_idx = get_mapped_index(15);
1447 assert_eq!(
1448 first_frame.rows[get_mapped_row(8)].data[col_idx].red1(),
1449 Color::BLUE.r() >= first_frame_threshold
1450 );
1451 assert_eq!(
1452 first_frame.rows[get_mapped_row(8)].data[col_idx].grn1(),
1453 Color::BLUE.g() >= first_frame_threshold
1454 );
1455 assert_eq!(
1456 first_frame.rows[get_mapped_row(8)].data[col_idx].blu1(),
1457 Color::BLUE.b() >= first_frame_threshold
1458 );
1459
1460 let col_idx = get_mapped_index(20);
1462 assert_eq!(
1463 first_frame.rows[get_mapped_row(10)].data[col_idx].red1(),
1464 Color::WHITE.r() >= first_frame_threshold
1465 );
1466 assert_eq!(
1467 first_frame.rows[get_mapped_row(10)].data[col_idx].grn1(),
1468 Color::WHITE.g() >= first_frame_threshold
1469 );
1470 assert_eq!(
1471 first_frame.rows[get_mapped_row(10)].data[col_idx].blu1(),
1472 Color::WHITE.b() >= first_frame_threshold
1473 );
1474
1475 let col_idx = get_mapped_index(25);
1478 assert_eq!(
1479 first_frame.rows[get_mapped_row(3)].data[col_idx].red2(),
1480 Color::RED.r() >= first_frame_threshold
1481 );
1482 assert_eq!(
1483 first_frame.rows[get_mapped_row(3)].data[col_idx].grn2(),
1484 Color::RED.g() >= first_frame_threshold
1485 );
1486 assert_eq!(
1487 first_frame.rows[get_mapped_row(3)].data[col_idx].blu2(),
1488 Color::RED.b() >= first_frame_threshold
1489 );
1490
1491 let col_idx = get_mapped_index(30);
1493 assert_eq!(
1494 first_frame.rows[get_mapped_row(7)].data[col_idx].red2(),
1495 Color::GREEN.r() >= first_frame_threshold
1496 );
1497 assert_eq!(
1498 first_frame.rows[get_mapped_row(7)].data[col_idx].grn2(),
1499 Color::GREEN.g() >= first_frame_threshold
1500 );
1501 assert_eq!(
1502 first_frame.rows[get_mapped_row(7)].data[col_idx].blu2(),
1503 Color::GREEN.b() >= first_frame_threshold
1504 );
1505
1506 let col_idx = get_mapped_index(35);
1508 assert_eq!(
1509 first_frame.rows[get_mapped_row(12)].data[col_idx].red2(),
1510 Color::BLUE.r() >= first_frame_threshold
1511 );
1512 assert_eq!(
1513 first_frame.rows[get_mapped_row(12)].data[col_idx].grn2(),
1514 Color::BLUE.g() >= first_frame_threshold
1515 );
1516 assert_eq!(
1517 first_frame.rows[get_mapped_row(12)].data[col_idx].blu2(),
1518 Color::BLUE.b() >= first_frame_threshold
1519 );
1520
1521 let col_idx = get_mapped_index(40);
1523 assert_eq!(
1524 first_frame.rows[get_mapped_row(1)].data[col_idx].red1(),
1525 false
1526 );
1527 assert_eq!(
1528 first_frame.rows[get_mapped_row(1)].data[col_idx].grn1(),
1529 false
1530 );
1531 assert_eq!(
1532 first_frame.rows[get_mapped_row(1)].data[col_idx].blu1(),
1533 false
1534 );
1535 }
1536
1537 #[test]
1538 fn test_embedded_graphics_integration() {
1539 let mut fb = TestFrameBuffer::new();
1540
1541 let result = Rectangle::new(Point::new(5, 5), Size::new(10, 8))
1543 .into_styled(PrimitiveStyle::with_fill(Color::RED))
1544 .draw(&mut fb);
1545 assert!(result.is_ok());
1546
1547 let result = Circle::new(Point::new(30, 15), 8)
1549 .into_styled(PrimitiveStyle::with_fill(Color::BLUE))
1550 .draw(&mut fb);
1551 assert!(result.is_ok());
1552 }
1553
1554 #[test]
1555 #[cfg(feature = "skip-black-pixels")]
1556 fn test_skip_black_pixels_enabled() {
1557 let mut fb = TestFrameBuffer::new();
1558
1559 fb.set_pixel_internal(10, 5, Color::RED);
1561
1562 let mapped_col_10 = get_mapped_index(10);
1564 assert_eq!(
1565 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1566 true
1567 );
1568 assert_eq!(
1569 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1570 false
1571 );
1572 assert_eq!(
1573 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1574 false
1575 );
1576
1577 fb.set_pixel_internal(10, 5, Color::BLACK);
1579
1580 assert_eq!(
1582 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1583 true
1584 );
1585 assert_eq!(
1586 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1587 false
1588 );
1589 assert_eq!(
1590 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1591 false
1592 );
1593 }
1594
1595 #[test]
1596 #[cfg(not(feature = "skip-black-pixels"))]
1597 fn test_skip_black_pixels_disabled() {
1598 let mut fb = TestFrameBuffer::new();
1599
1600 fb.set_pixel_internal(10, 5, Color::RED);
1602
1603 let mapped_col_10 = get_mapped_index(10);
1605 assert_eq!(
1606 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1607 true
1608 );
1609 assert_eq!(
1610 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1611 false
1612 );
1613 assert_eq!(
1614 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1615 false
1616 );
1617
1618 fb.set_pixel_internal(10, 5, Color::BLACK);
1620
1621 assert_eq!(
1623 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1624 false
1625 );
1626 assert_eq!(
1627 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1628 false
1629 );
1630 assert_eq!(
1631 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1632 false
1633 );
1634 }
1635
1636 #[test]
1637 fn test_bcm_frame_overwrite() {
1638 let mut fb = TestFrameBuffer::new();
1639
1640 fb.set_pixel_internal(10, 5, Color::WHITE);
1642
1643 let mapped_col_10 = get_mapped_index(10);
1644
1645 for frame in fb.data.frames.iter() {
1647 assert_eq!(
1649 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1650 true
1651 );
1652 assert_eq!(
1653 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1654 true
1655 );
1656 assert_eq!(
1657 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1658 true
1659 );
1660 }
1661
1662 let half_white = Color::from(embedded_graphics::pixelcolor::Rgb888::new(128, 128, 128));
1664 fb.set_pixel_internal(10, 5, half_white);
1665
1666 let brightness_step = 1 << (8 - TEST_BITS); for (frame_idx, frame) in fb.data.frames.iter().enumerate() {
1672 let frame_threshold = (frame_idx as u8 + 1) * brightness_step;
1673 let should_be_active = 128 >= frame_threshold;
1674
1675 assert_eq!(
1676 frame.rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1677 should_be_active
1678 );
1679 assert_eq!(
1680 frame.rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1681 should_be_active
1682 );
1683 assert_eq!(
1684 frame.rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1685 should_be_active
1686 );
1687 }
1688
1689 for frame_idx in 0..4 {
1692 assert_eq!(
1693 fb.data.frames[frame_idx].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1694 true
1695 );
1696 }
1697 for frame_idx in 4..TEST_FRAME_COUNT {
1699 assert_eq!(
1700 fb.data.frames[frame_idx].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1701 false
1702 );
1703 }
1704 }
1705
1706 #[test]
1707 #[allow(deprecated)]
1708 fn test_read_buffer_implementation() {
1709 let fb = TestFrameBuffer::new();
1711 let expected_size =
1712 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>();
1713
1714 unsafe {
1716 let (ptr, len) = <TestFrameBuffer as ReadBuffer>::read_buffer(&fb);
1717 assert!(!ptr.is_null());
1718 assert_eq!(len, expected_size);
1719 }
1720
1721 unsafe {
1723 let (ptr, len) = fb.read_buffer();
1724 assert!(!ptr.is_null());
1725 assert_eq!(len, expected_size);
1726 }
1727
1728 let fb = TestFrameBuffer::new();
1730 let fb_ref = &fb;
1731 unsafe {
1732 let (ptr, len) = fb_ref.read_buffer();
1733 assert!(!ptr.is_null());
1734 assert_eq!(
1735 len,
1736 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>(
1737 )
1738 );
1739 }
1740
1741 let mut fb = TestFrameBuffer::new();
1743 let fb_ref = &mut fb;
1744 unsafe {
1745 let (ptr, len) = fb_ref.read_buffer();
1746 assert!(!ptr.is_null());
1747 assert_eq!(
1748 len,
1749 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>(
1750 )
1751 );
1752 }
1753 }
1754
1755 #[test]
1756 #[allow(deprecated)]
1757 fn test_read_buffer_owned_implementation() {
1758 fn test_owned_read_buffer(fb: TestFrameBuffer) -> (bool, usize) {
1761 unsafe {
1762 let (ptr, len) = fb.read_buffer();
1763 (!ptr.is_null(), len)
1764 }
1765 }
1766
1767 let fb = TestFrameBuffer::new();
1768 let expected_len =
1769 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>();
1770
1771 let (ptr_valid, actual_len) = test_owned_read_buffer(fb);
1772 assert!(ptr_valid);
1773 assert_eq!(actual_len, expected_len);
1774 }
1775
1776 #[test]
1777 fn test_debug_formatting() {
1778 let fb = TestFrameBuffer::new();
1779 let debug_string = format!("{:?}", fb);
1780 assert!(debug_string.contains("DmaFrameBuffer"));
1781 assert!(debug_string.contains("frame_count"));
1782 assert!(debug_string.contains("frame_size"));
1783 assert!(debug_string.contains("brightness_step"));
1784 }
1785
1786 #[test]
1787 fn test_default_implementation() {
1788 let fb1 = TestFrameBuffer::new();
1789 let fb2 = TestFrameBuffer::default();
1790
1791 assert_eq!(fb1.data.frames.len(), fb2.data.frames.len());
1793 assert_eq!(fb1._align, fb2._align);
1794 }
1795
1796 #[test]
1797 fn test_memory_alignment() {
1798 let fb = TestFrameBuffer::new();
1799 let ptr = &fb as *const _ as usize;
1800
1801 assert_eq!(ptr % 8, 0);
1803 }
1804
1805 #[test]
1806 fn test_color_values() {
1807 let mut fb = TestFrameBuffer::new();
1808
1809 let colors = [
1811 (Color::RED, (255, 0, 0)),
1812 (Color::GREEN, (0, 255, 0)),
1813 (Color::BLUE, (0, 0, 255)),
1814 (Color::WHITE, (255, 255, 255)),
1815 (Color::BLACK, (0, 0, 0)),
1816 ];
1817
1818 for (i, (color, (r, g, b))) in colors.iter().enumerate() {
1819 fb.set_pixel(Point::new(i as i32, 0), *color);
1820 assert_eq!(color.r(), *r);
1821 assert_eq!(color.g(), *g);
1822 assert_eq!(color.b(), *b);
1823 }
1824 }
1825
1826 #[test]
1827 fn test_blanking_delay() {
1828 let mut row: Row<TEST_COLS> = Row::new();
1829 let prev_addr = 4;
1830
1831 row.format(prev_addr);
1832
1833 let oe_active = !cfg!(feature = "invert-oe");
1834
1835 if TRAIL_BLANK_DELAY > 0 {
1837 let trail_blank_idx = get_mapped_index(TRAIL_BLANK_DELAY - 1);
1838 assert_eq!(row.data[trail_blank_idx].output_enable(), !oe_active);
1839 }
1840
1841 let after_trail_idx = get_mapped_index(TRAIL_BLANK_DELAY);
1842 assert_eq!(row.data[after_trail_idx].output_enable(), oe_active);
1843
1844 let before_lead_idx = get_mapped_index(TEST_COLS - LEAD_BLANK_DELAY - 2);
1846 assert_eq!(row.data[before_lead_idx].output_enable(), oe_active);
1847
1848 let lead_blank_idx = get_mapped_index(TEST_COLS - LEAD_BLANK_DELAY - 1);
1849 assert_eq!(row.data[lead_blank_idx].output_enable(), !oe_active);
1850 }
1851
1852 #[test]
1853 fn test_esp32_mapping() {
1854 #[cfg(feature = "esp32-ordering")]
1856 {
1857 assert_eq!(map_index(0), 1);
1858 assert_eq!(map_index(1), 0);
1859 assert_eq!(map_index(2), 3);
1860 assert_eq!(map_index(3), 2);
1861 assert_eq!(map_index(4), 5);
1862 assert_eq!(map_index(5), 4);
1863 }
1864 #[cfg(not(feature = "esp32-ordering"))]
1865 {
1866 assert_eq!(map_index(0), 0);
1867 assert_eq!(map_index(1), 1);
1868 assert_eq!(map_index(2), 2);
1869 assert_eq!(map_index(3), 3);
1870 }
1871 }
1872
1873 #[test]
1874 fn test_bits_assertion() {
1875 assert!(TEST_BITS <= 8);
1878 }
1879
1880 #[test]
1881 fn test_fast_clear_method() {
1882 let mut fb = TestFrameBuffer::new();
1883
1884 fb.set_pixel_internal(10, 5, Color::RED);
1886 fb.set_pixel_internal(20, 10, Color::GREEN);
1887
1888 let mapped_col_10 = get_mapped_index(10);
1890 assert_eq!(
1891 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1892 true
1893 );
1894
1895 fb.erase();
1897
1898 assert_eq!(
1900 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].red1(),
1901 false
1902 );
1903 assert_eq!(
1904 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].grn1(),
1905 false
1906 );
1907 assert_eq!(
1908 fb.data.frames[0].rows[get_mapped_row(5)].data[mapped_col_10].blu1(),
1909 false
1910 );
1911
1912 let last_col = get_mapped_index(TEST_COLS - 1);
1914 assert_eq!(
1915 fb.data.frames[0].rows[get_mapped_row(5)].data[last_col].latch(),
1916 true
1917 );
1918 }
1919
1920 const CHAR_W: i32 = 6;
1924 const CHAR_H: i32 = 10;
1925
1926 fn verify_glyph_at(fb: &mut TestFrameBuffer, origin: Point) {
1928 use embedded_graphics::mock_display::MockDisplay;
1929 use embedded_graphics::mono_font::ascii::FONT_6X10;
1930 use embedded_graphics::mono_font::MonoTextStyle;
1931 use embedded_graphics::text::{Baseline, Text};
1932
1933 let style = MonoTextStyle::new(&FONT_6X10, Color::WHITE);
1935 Text::with_baseline("A", origin, style, Baseline::Top)
1936 .draw(fb)
1937 .unwrap();
1938
1939 let mut reference: MockDisplay<Color> = MockDisplay::new();
1941 Text::with_baseline("A", Point::zero(), style, Baseline::Top)
1942 .draw(&mut reference)
1943 .unwrap();
1944
1945 for dy in 0..CHAR_H {
1946 for dx in 0..CHAR_W {
1947 let expected_on = reference
1948 .get_pixel(Point::new(dx, dy))
1949 .unwrap_or(Color::BLACK)
1950 != Color::BLACK;
1951
1952 let gx = (origin.x + dx) as usize;
1953 let gy = (origin.y + dy) as usize;
1954
1955 let frame0 = &fb.data.frames[0];
1962 let e = if gy < TEST_NROWS {
1963 &frame0.rows[get_mapped_row(gy)].data[get_mapped_index(gx)]
1964 } else {
1965 &frame0.rows[get_mapped_row(gy - TEST_NROWS)].data[get_mapped_index(gx)]
1966 };
1967
1968 let (r, g, b) = if gy >= TEST_NROWS {
1969 (e.red2(), e.grn2(), e.blu2())
1970 } else {
1971 (e.red1(), e.grn1(), e.blu1())
1972 };
1973
1974 if expected_on {
1975 assert!(r && g && b,);
1976 } else {
1977 assert!(!r && !g && !b);
1978 }
1979 }
1980 }
1981 }
1982
1983 #[test]
1984 fn test_draw_char_corners() {
1985 let upper_left = Point::new(0, 0);
1986 let lower_right = Point::new(TEST_COLS as i32 - CHAR_W, TEST_ROWS as i32 - CHAR_H);
1987
1988 let mut fb = TestFrameBuffer::new();
1989
1990 verify_glyph_at(&mut fb, upper_left);
1991 verify_glyph_at(&mut fb, lower_right);
1992 }
1993
1994 #[test]
1995 fn test_framebuffer_operations_trait_erase() {
1996 let mut fb = TestFrameBuffer::new();
1997
1998 fb.set_pixel_internal(10, 5, Color::RED);
2000 fb.set_pixel_internal(20, 10, Color::GREEN);
2001
2002 <TestFrameBuffer as FrameBufferOperations>::erase(&mut fb);
2004
2005 let mc10 = get_mapped_index(10);
2007 let mc20 = get_mapped_index(20);
2008 assert_eq!(
2009 fb.data.frames[0].rows[get_mapped_row(5)].data[mc10].red1(),
2010 false
2011 );
2012 assert_eq!(
2013 fb.data.frames[0].rows[get_mapped_row(10)].data[mc20].grn1(),
2014 false
2015 );
2016
2017 let last_col = get_mapped_index(TEST_COLS - 1);
2019 assert!(fb.data.frames[0].rows[get_mapped_row(0)].data[last_col].latch());
2020 }
2021
2022 #[test]
2023 fn test_framebuffer_operations_trait_set_pixel() {
2024 let mut fb = TestFrameBuffer::new();
2025
2026 <TestFrameBuffer as FrameBufferOperations>::set_pixel(
2028 &mut fb,
2029 Point::new(8, 3),
2030 Color::BLUE,
2031 );
2032
2033 let idx = get_mapped_index(8);
2034 assert_eq!(
2035 fb.data.frames[0].rows[get_mapped_row(3)].data[idx].blu1(),
2036 true
2037 );
2038 assert_eq!(
2039 fb.data.frames[0].rows[get_mapped_row(3)].data[idx].red1(),
2040 false
2041 );
2042 assert_eq!(
2043 fb.data.frames[0].rows[get_mapped_row(3)].data[idx].grn1(),
2044 false
2045 );
2046 }
2047
2048 #[test]
2049 fn test_entry_new_oe_matches_feature() {
2050 let entry = Entry::new();
2051 if cfg!(feature = "invert-oe") {
2052 assert!(entry.output_enable());
2053 } else {
2054 assert!(!entry.output_enable());
2055 }
2056 }
2057
2058 #[test]
2059 fn test_make_data_template_oe_polarity() {
2060 let mut row = Row::<TEST_COLS>::new();
2061 row.format(4);
2062
2063 let active_idx = get_mapped_index(TRAIL_BLANK_DELAY);
2064 let blank_idx = get_mapped_index(TEST_COLS - LEAD_BLANK_DELAY - 1);
2065 let latch_idx = get_mapped_index(TEST_COLS - 1);
2066
2067 if cfg!(feature = "invert-oe") {
2068 assert!(!row.data[active_idx].output_enable());
2069 assert!(row.data[blank_idx].output_enable());
2070 assert!(row.data[latch_idx].output_enable());
2071 } else {
2072 assert!(row.data[active_idx].output_enable());
2073 assert!(!row.data[blank_idx].output_enable());
2074 assert!(!row.data[latch_idx].output_enable());
2075 }
2076 }
2077
2078 static STATIC_FB: TestFrameBuffer = TestFrameBuffer::new();
2079
2080 #[test]
2081 fn test_static_construction_is_formatted() {
2082 let runtime_fb = TestFrameBuffer::new();
2083
2084 for (fi, frame) in STATIC_FB.data.frames.iter().enumerate() {
2085 for (ri, row) in frame.rows.iter().enumerate() {
2086 assert_eq!(
2087 row.data, runtime_fb.data.frames[fi].rows[ri].data,
2088 "static vs runtime mismatch at frame {fi}, row {ri}"
2089 );
2090 assert_eq!(
2091 row.gap, runtime_fb.data.frames[fi].rows[ri].gap,
2092 "static vs runtime gap mismatch at frame {fi}, row {ri}"
2093 );
2094 }
2095 }
2096 }
2097
2098 #[test]
2099 fn test_format_reinitializes_at_runtime() {
2100 let mut fb = TestFrameBuffer::new();
2101 fb.erase();
2102 fb.format();
2103
2104 for (fi, frame) in fb.data.frames.iter().enumerate() {
2105 for (ri, row) in frame.rows.iter().enumerate() {
2106 assert_eq!(
2107 row.data, STATIC_FB.data.frames[fi].rows[ri].data,
2108 "re-formatted vs static mismatch at frame {fi}, row {ri}"
2109 );
2110 }
2111 }
2112 }
2113
2114 #[test]
2115 fn bcm_segment_count_is_one() {
2116 use crate::FrameBuffer;
2117 let fb = TestFrameBuffer::new();
2118 assert_eq!(fb.bcm_segment_count(), 1);
2119 }
2120
2121 #[test]
2122 fn bcm_segment_covers_entire_buffer() {
2123 use crate::FrameBuffer;
2124 let fb = TestFrameBuffer::new();
2125 let seg = fb.bcm_segment(0);
2126 let expected_ptr = (&raw const fb.data).cast::<u8>();
2127 let expected_len =
2128 core::mem::size_of::<FrameData<TEST_ROWS, TEST_COLS, TEST_NROWS, TEST_FRAME_COUNT>>();
2129 assert_eq!(seg.ptr, expected_ptr);
2130 assert_eq!(seg.len, expected_len);
2131 assert_eq!(seg.reps, 1);
2132 }
2133
2134 #[test]
2135 #[should_panic(expected = "out of range")]
2136 fn bcm_segment_panics_for_invalid_index() {
2137 use crate::FrameBuffer;
2138 let fb = TestFrameBuffer::new();
2139 let _ = fb.bcm_segment(1);
2140 }
2141
2142 #[test]
2143 fn bcm_sequence_matches_runtime_segments() {
2144 use crate::FrameBuffer;
2145 let fb = TestFrameBuffer::new();
2146 assert_eq!(
2147 TestFrameBuffer::BCM_SEGMENT_COUNT,
2148 TestFrameBuffer::BCM_SEQUENCE_LEN * TestFrameBuffer::BCM_SEQUENCE_COUNT
2149 );
2150 assert_eq!(fb.bcm_segment_count(), TestFrameBuffer::BCM_SEGMENT_COUNT);
2151 for i in 0..TestFrameBuffer::BCM_SEGMENT_COUNT {
2152 let entry = TestFrameBuffer::BCM_SEQUENCE[i % TestFrameBuffer::BCM_SEQUENCE_LEN];
2153 let seg = fb.bcm_segment(i);
2154 assert_eq!((seg.len, seg.reps), (entry.len, entry.reps), "segment {i}");
2155 assert!(!seg.ptr.is_null(), "segment {i} has null pointer");
2156 }
2157 for entry in &TestFrameBuffer::BCM_SEQUENCE[TestFrameBuffer::BCM_SEQUENCE_LEN..] {
2158 assert_eq!(*entry, crate::BcmLenReps::ZERO, "padding must be zero");
2159 }
2160 }
2161}