embedded_icon/
icon.rs

1use embedded_graphics::prelude::*;
2use embedded_graphics::primitives::Rectangle;
3
4#[macro_export]
5macro_rules! include_icon {
6    ($icon_name:ident, $group:ident, $filename:literal, $size:literal) => {
7        #[derive(Debug, Copy, Clone)]
8        pub struct $icon_name;
9
10        impl $crate::icon::sealed::IconInternal for $icon_name {
11            const SIZE: u32 = $size;
12            const DATA: &'static [u8] =
13                include_bytes!(concat!(stringify!($size), "px/", $filename, ".bits"));
14            const INSTANCE: Self = $icon_name;
15        }
16
17        static_assertions::const_assert!(
18            <$icon_name as $crate::icon::sealed::IconInternal>::DATA.len()
19                >= <$icon_name as $crate::icon::sealed::IconInternal>::BYTE_COUNT
20        );
21    };
22}
23
24/// Struct to store icon color and properties.
25///
26/// There are two ways to instantiate an icon:
27/// ```rust
28/// # use embedded_graphics::pixelcolor::BinaryColor;
29/// # use embedded_icon::Icon;
30/// # use embedded_icon::prelude::*;
31/// // using constructors on icons (recommended)
32/// let icon = icons::size24px::actions::Download::new(BinaryColor::On);
33/// // using types
34/// let icon: Icon<_, icons::size24px::actions::Download> = Icon::new(BinaryColor::On);
35/// ```
36/// Both result in the same icon (`Icon<COLOR, ICON>`). Use whichever you prefer.
37///
38///
39///
40/// ## Full Usage Example
41///
42/// ```rust
43/// # use embedded_graphics::image::Image;
44/// # use embedded_graphics::pixelcolor::{BinaryColor};
45/// # use embedded_graphics::prelude::*;
46/// # use embedded_graphics::mock_display::MockDisplay;
47/// # let mut  display = MockDisplay::new();
48/// // Import icons and traits
49/// use embedded_icon::prelude::*;
50///
51/// // Create an icon
52/// let icon = icons::size24px::actions::Download::new(BinaryColor::On);
53///
54/// // Wrap it in an embedded_graphics image
55/// let image = Image::new(&icon, Point::zero());
56///
57/// // Draw it to a display
58/// image.draw(&mut display).unwrap();
59/// ```
60#[derive(Debug)]
61pub struct Icon<C, Ico>
62where
63    C: PixelColor,
64    Ico: sealed::IconInternal,
65{
66    color: C,
67    _icon: Ico,
68}
69
70impl<C: PixelColor, Ico: sealed::IconInternal> Icon<C, Ico> {
71    pub fn new(color: C) -> Self {
72        Self {
73            color,
74            _icon: Ico::INSTANCE,
75        }
76    }
77
78    pub fn set_color(&mut self, color: C) {
79        self.color = color;
80    }
81
82    pub fn get_color(&self) -> C {
83        self.color
84    }
85}
86
87/// Marker Trait for all Icons
88pub trait EmbeddedIcon: Sized + sealed::IconInternal {}
89
90impl<T> EmbeddedIcon for T where T: sealed::IconInternal {}
91
92pub trait NewIcon<C: embedded_graphics::prelude::PixelColor>: Sized
93where
94    Self: sealed::IconInternal,
95{
96    fn new(color: C) -> Icon<C, Self>;
97}
98
99impl<C: PixelColor, T> NewIcon<C> for T
100where
101    T: sealed::IconInternal,
102{
103    fn new(color: C) -> Icon<C, Self> {
104        Icon {
105            color,
106            _icon: Self::INSTANCE,
107        }
108    }
109}
110
111pub(crate) mod sealed {
112    pub trait IconInternal: Sized {
113        const SIZE: u32;
114        const BIT_COUNT: usize = { Self::SIZE as usize * Self::SIZE as usize };
115        const BYTE_COUNT: usize =
116            { Self::BIT_COUNT / 8 + if Self::BIT_COUNT % 8 > 0 { 1 } else { 0 } };
117        const DATA: &'static [u8];
118        const INSTANCE: Self;
119    }
120}
121
122impl<C, T> ImageDrawable for Icon<C, T>
123where
124    T: sealed::IconInternal,
125    C: PixelColor,
126{
127    type Color = C;
128    fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
129    where
130        D: DrawTarget<Color = Self::Color>,
131    {
132        let data = T::DATA;
133        for y in 0..T::SIZE {
134            for x in 0..T::SIZE {
135                if get_bit_unchecked(data, (x + y * T::SIZE) as usize) {
136                    Pixel(Point::new(x as i32, y as i32), self.get_color()).draw(target)?;
137                }
138            }
139        }
140        Ok(())
141    }
142
143    #[inline(always)]
144    fn draw_sub_image<D>(&self, target: &mut D, area: &Rectangle) -> Result<(), D::Error>
145    where
146        D: DrawTarget<Color = Self::Color>,
147    {
148        // from tinytga
149        self.draw(&mut target.translated(-area.top_left).clipped(area))
150    }
151}
152
153impl<C, T> OriginDimensions for Icon<C, T>
154where
155    T: sealed::IconInternal,
156    C: PixelColor,
157{
158    #[inline(always)]
159    fn size(&self) -> Size {
160        Size {
161            width: T::SIZE,
162            height: T::SIZE,
163        }
164    }
165}
166
167/// Retrieve the n-th bit from a slice of bytes
168/// without performing in-bounds checking
169fn get_bit_unchecked(target: &[u8], bit: usize) -> bool {
170    let slice_index = bit / 8;
171    let bit_index = bit % 8;
172    (target[slice_index] & (1 << bit_index)) != 0
173}