microbit_common/display/nonblocking/image.rs
1//! Static 5×5 greyscale and black-and-white images.
2
3use tiny_led_matrix::{Render, MAX_BRIGHTNESS};
4
5/// A 5×5 image supporting the full range of brightnesses for each LED.
6///
7/// Uses 25 bytes of storage.
8#[derive(Copy, Clone, Debug)]
9pub struct GreyscaleImage([[u8; 5]; 5]);
10
11impl GreyscaleImage {
12 /// Constructs a GreyscaleImage from an array of brightnesses.
13 ///
14 /// The data should be an array of 5 rows (top first), each of which is an
15 /// array of 5 brightness values (left first).
16 ///
17 /// # Example
18 ///
19 /// ```no_run
20 /// # use microbit_common as microbit;
21 /// # use microbit::display::nonblocking::GreyscaleImage;
22 /// const GREY_HEART: GreyscaleImage = GreyscaleImage::new(&[
23 /// [0, 9, 0, 9, 0],
24 /// [9, 5, 9, 5, 9],
25 /// [9, 5, 5, 5, 9],
26 /// [0, 9, 5, 9, 0],
27 /// [0, 0, 9, 0, 0],
28 /// ]);
29 /// ```
30 pub const fn new(data: &[[u8; 5]; 5]) -> GreyscaleImage {
31 GreyscaleImage(*data)
32 }
33
34 /// Construct a GreyscaleImage with all LEDs turned off.
35 pub const fn blank() -> GreyscaleImage {
36 GreyscaleImage([[0; 5]; 5])
37 }
38}
39
40impl Render for GreyscaleImage {
41 fn brightness_at(&self, x: usize, y: usize) -> u8 {
42 self.0[y][x]
43 }
44}
45
46impl Render for &GreyscaleImage {
47 fn brightness_at(&self, x: usize, y: usize) -> u8 {
48 GreyscaleImage::brightness_at(self, x, y)
49 }
50}
51
52/// A 5×5 image supporting only two levels of brightness (on and off).
53///
54/// Uses 5 bytes of storage.
55///
56/// For display, each pixel is treated as having brightness either 0 or
57/// MAX_BRIGHTNESS.
58#[derive(Copy, Clone, Debug)]
59pub struct BitImage([u8; 5]);
60
61impl BitImage {
62 /// Constructs a BitImage from an array of brightnesses.
63 ///
64 /// The data should be an array of 5 rows (top first), each of which is an
65 /// array of 5 values (left first). Each value should be either 0 or 1.
66 ///
67 /// # Example
68 ///
69 /// ```no_run
70 /// # use microbit_common as microbit;
71 /// # use microbit::display::nonblocking::BitImage;
72 /// const HEART: BitImage = BitImage::new(&[
73 /// [0, 1, 0, 1, 0],
74 /// [1, 0, 1, 0, 1],
75 /// [1, 0, 0, 0, 1],
76 /// [0, 1, 0, 1, 0],
77 /// [0, 0, 1, 0, 0],
78 /// ]);
79 /// ```
80 pub const fn new(im: &[[u8; 5]; 5]) -> BitImage {
81 // FIXME: can we reject values other than 0 or 1?
82 const fn row_byte(row: [u8; 5]) -> u8 {
83 row[0] | (row[1] << 1) | (row[2] << 2) | (row[3] << 3) | (row[4] << 4)
84 }
85 BitImage([
86 row_byte(im[0]),
87 row_byte(im[1]),
88 row_byte(im[2]),
89 row_byte(im[3]),
90 row_byte(im[4]),
91 ])
92 }
93
94 /// Returns a new blank BitImage.
95 ///
96 /// All pixel values are 0.
97 pub const fn blank() -> BitImage {
98 BitImage([0; 5])
99 }
100}
101
102impl Render for BitImage {
103 fn brightness_at(&self, x: usize, y: usize) -> u8 {
104 let rowdata = self.0[y];
105 if rowdata & (1 << x) != 0 {
106 MAX_BRIGHTNESS
107 } else {
108 0
109 }
110 }
111}
112
113impl Render for &BitImage {
114 fn brightness_at(&self, x: usize, y: usize) -> u8 {
115 BitImage::brightness_at(self, x, y)
116 }
117}