1use embedded_graphics::prelude::*;
2use embedded_graphics::primitives::Rectangle;
3use static_assertions::const_assert;
4
5#[macro_export]
6macro_rules! make_icon {
7 ($name:ident, $size:expr, $category:expr, $file:expr) => {
8 #[derive(Debug, Copy, Clone)]
9 pub struct $name;
10
11 impl $crate::icon::sealed::IconoirInternal for $name {
12 const SIZE: u32 = $size;
13 const DATA: &'static [u8] = include_bytes!(concat!(
14 "../rendered/",
15 stringify!($size),
16 "px/",
17 $category,
18 "/",
19 $file,
20 ".bits"
21 ));
22 const INSTANCE: Self = $name;
23 }
24
25 const_assert!(
26 <$name as $crate::icon::sealed::IconoirInternal>::DATA.len()
27 >= <$name as $crate::icon::sealed::IconoirInternal>::BYTE_COUNT
28 );
29 };
31}
32
33#[macro_export]
34macro_rules! make_icon_category {
35 ($name:ident, $size:expr, $category:expr, [ $(($icon_name:ident, $file:expr)),* $(,)? ] ) => {
36 paste::paste ! {
37 pub mod [<$name:snake>] {
38 use super::*;
39 $(
40 make_icon!( $icon_name, $size, $category, $file);
41
42 )*
43 }
44 }
45 }
46}
47
48make_icon!(SomeIcon, 24, "Animals", "fish");
49
50#[derive(Debug)]
87pub struct Icon<C, Ico>
88where
89 C: PixelColor,
90 Ico: sealed::IconoirInternal,
91{
92 color: C,
93 _icon: Ico,
94}
95
96impl<C: PixelColor, Ico: sealed::IconoirInternal> Icon<C, Ico> {
97 pub fn new(color: C) -> Self {
98 Self {
99 color,
100 _icon: Ico::INSTANCE,
101 }
102 }
103
104 pub fn set_color(&mut self, color: C) {
105 self.color = color;
106 }
107
108 pub fn get_color(&self) -> C {
109 self.color
110 }
111}
112
113pub trait IconoirIcon: Sized + sealed::IconoirInternal {}
115
116impl<T> IconoirIcon for T where T: sealed::IconoirInternal {}
117
118pub trait IconoirNewIcon<C: embedded_graphics::prelude::PixelColor>: Sized
119where
120 Self: sealed::IconoirInternal,
121{
122 fn new(color: C) -> Icon<C, Self>;
123}
124
125impl<C: PixelColor, T> IconoirNewIcon<C> for T
126where
127 T: sealed::IconoirInternal,
128{
129 fn new(color: C) -> Icon<C, Self> {
130 Icon {
131 color,
132 _icon: Self::INSTANCE,
133 }
134 }
135}
136
137pub(crate) mod sealed {
138 pub trait IconoirInternal: Sized {
139 const SIZE: u32;
140 const BIT_COUNT: usize = { Self::SIZE as usize * Self::SIZE as usize };
141 const BYTE_COUNT: usize =
142 { Self::BIT_COUNT / 8 + if Self::BIT_COUNT % 8 > 0 { 1 } else { 0 } };
143 const DATA: &'static [u8];
144 const INSTANCE: Self;
145 }
146}
147
148impl<C, T> ImageDrawable for Icon<C, T>
149where
150 T: sealed::IconoirInternal,
151 C: PixelColor,
152{
153 type Color = C;
154 fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
155 where
156 D: DrawTarget<Color = Self::Color>,
157 {
158 let data = T::DATA;
159 for y in 0..T::SIZE {
160 for x in 0..T::SIZE {
161 if get_bit_unchecked(data, (x + y * T::SIZE) as usize) {
162 Pixel(Point::new(x as i32, y as i32), self.get_color()).draw(target)?;
163 }
164 }
165 }
166 Ok(())
167 }
168
169 #[inline(always)]
170 fn draw_sub_image<D>(&self, target: &mut D, area: &Rectangle) -> Result<(), D::Error>
171 where
172 D: DrawTarget<Color = Self::Color>,
173 {
174 self.draw(&mut target.translated(-area.top_left).clipped(area))
176 }
177}
178
179impl<C, T> OriginDimensions for Icon<C, T>
180where
181 T: sealed::IconoirInternal,
182 C: PixelColor,
183{
184 #[inline(always)]
185 fn size(&self) -> Size {
186 Size {
187 width: T::SIZE,
188 height: T::SIZE,
189 }
190 }
191}
192
193fn get_bit_unchecked(target: &[u8], bit: usize) -> bool {
196 let slice_index = bit / 8;
197 let bit_index = bit % 8;
198 (target[slice_index] & (1 << bit_index)) != 0
199}