Skip to main content

rlvgl_core/
icon_bitmap.rs

1//! 1-bit bitmap icons for the file browser.
2//!
3//! Each icon is stored as 1-bit-per-pixel row-major data with MSB mapping to
4//! the leftmost pixel. Icons are 8 pixels wide (1 byte per row) for trivial
5//! packing. Rendering scales to a `target_height` so the icon matches the
6//! active font height.
7
8use crate::renderer::Renderer;
9use crate::widget::Color;
10
11/// A 1-bit icon bitmap that can be drawn at any integer scale.
12pub struct IconBitmap {
13    /// Width of the icon in native pixels.
14    pub width: u8,
15    /// Height of the icon in native pixels.
16    pub height: u8,
17    /// 1-bit-per-pixel row-major packed data, MSB = leftmost pixel.
18    pub data: &'static [u8],
19}
20
21impl IconBitmap {
22    /// Draw the icon at `(x, y)` scaled so it reaches `target_height` display
23    /// pixels. Each native pixel becomes a `scale x scale` filled rectangle.
24    pub fn draw(
25        &self,
26        renderer: &mut dyn Renderer,
27        x: i32,
28        y: i32,
29        target_height: i32,
30        color: Color,
31    ) {
32        let scale = self.compute_scale(target_height);
33        let w = self.width as usize;
34        for row in 0..self.height as usize {
35            for col in 0..w {
36                let bit_index = row * w + col;
37                let byte_index = bit_index / 8;
38                let bit_offset = 7 - (bit_index % 8);
39                if byte_index < self.data.len() && (self.data[byte_index] >> bit_offset) & 1 == 1 {
40                    renderer.fill_rect(
41                        crate::widget::Rect {
42                            x: x + col as i32 * scale,
43                            y: y + row as i32 * scale,
44                            width: scale,
45                            height: scale,
46                        },
47                        color,
48                    );
49                }
50            }
51        }
52    }
53
54    /// Scaled width in display pixels for a given target height.
55    pub fn scaled_width(&self, target_height: i32) -> i32 {
56        self.width as i32 * self.compute_scale(target_height)
57    }
58
59    fn compute_scale(&self, target_height: i32) -> i32 {
60        let s = if self.height > 0 {
61            target_height / self.height as i32
62        } else {
63            1
64        };
65        if s < 1 { 1 } else { s }
66    }
67}
68
69/// Folder icon (8x10 native pixels, 10 bytes).
70///
71/// ```text
72/// .###....   tab
73/// ########   top edge
74/// #......#   walls
75/// #......#
76/// #......#
77/// #......#
78/// #......#
79/// #......#
80/// #......#
81/// ########   bottom edge
82/// ```
83pub static ICON_FOLDER: IconBitmap = IconBitmap {
84    width: 8,
85    height: 10,
86    data: &[
87        0b0111_0000, // .###....
88        0b1111_1111, // ########
89        0b1000_0001, // #......#
90        0b1000_0001,
91        0b1000_0001,
92        0b1000_0001,
93        0b1000_0001,
94        0b1000_0001,
95        0b1000_0001,
96        0b1111_1111, // ########
97    ],
98};
99
100/// File icon (8x10 native pixels, 10 bytes).
101///
102/// ```text
103/// ######..   top with dog-ear space
104/// #....##.   dog-ear fold
105/// #.....#.   body
106/// #.....#.
107/// #.....#.
108/// #.....#.
109/// #.....#.
110/// #.....#.
111/// #.....#.
112/// #######.   bottom
113/// ```
114pub static ICON_FILE: IconBitmap = IconBitmap {
115    width: 8,
116    height: 10,
117    data: &[
118        0b1111_1100, // ######..
119        0b1000_0110, // #....##.
120        0b1000_0010, // #.....#.
121        0b1000_0010,
122        0b1000_0010,
123        0b1000_0010,
124        0b1000_0010,
125        0b1000_0010,
126        0b1000_0010,
127        0b1111_1110, // #######.
128    ],
129};
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134    use crate::widget::Rect;
135
136    struct CountRenderer {
137        rects: usize,
138    }
139
140    impl Renderer for CountRenderer {
141        fn fill_rect(&mut self, _rect: Rect, _color: Color) {
142            self.rects += 1;
143        }
144        fn draw_text(&mut self, _pos: (i32, i32), _text: &str, _color: Color) {}
145    }
146
147    #[test]
148    fn folder_icon_draws_pixels() {
149        let mut r = CountRenderer { rects: 0 };
150        ICON_FOLDER.draw(&mut r, 0, 0, 20, Color(255, 255, 255, 255));
151        assert!(r.rects > 0);
152    }
153
154    #[test]
155    fn scaled_width_matches() {
156        assert_eq!(ICON_FOLDER.scaled_width(20), 16); // 8 * (20/10) = 16
157        assert_eq!(ICON_FILE.scaled_width(20), 16);
158    }
159
160    #[test]
161    fn scale_floor_one() {
162        assert_eq!(ICON_FOLDER.scaled_width(5), 8); // 8 * 1
163    }
164}