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#[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
87pub 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 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
167fn 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}