1#[derive(Clone, Copy)]
2pub struct Sprite<Bytes: ?Sized = [u8]> {
3 shape: [u32; 2],
4 bpp: BitsPerPixel,
5 pub indices: DrawIndices,
6 bytes: Bytes,
7}
8
9impl<const N: usize> Sprite<[u8; N]> {
10 pub const fn from_byte_array(
16 bytes: [u8; N],
17 shape: [u32; 2],
18 bpp: BitsPerPixel,
19 draw_colors: DrawIndices,
20 ) -> Option<Self> {
21 let resolution = shape[0].checked_mul(shape[1]);
22 let capacity = N.checked_mul(1 << (3 - bpp as u32));
23
24 match (resolution, capacity) {
25 (Some(resolution), Some(capacity)) if resolution as usize <= capacity => unsafe {
27 Some(Self::from_bytes_unchecked(bytes, shape, bpp, draw_colors))
28 },
29 _ => None,
30 }
31 }
32}
33
34impl<Bytes> Sprite<Bytes> {
35 pub const unsafe fn from_bytes_unchecked(
45 bytes: Bytes,
46 shape: [u32; 2],
47 bpp: BitsPerPixel,
48 draw_colors: DrawIndices,
49 ) -> Self {
50 Sprite {
51 shape,
52 bpp,
53 indices: draw_colors,
54 bytes,
55 }
56 }
57}
58
59impl<Bytes: AsRef<[u8]>> Sprite<Bytes> {
60 pub fn from_bytes(
61 bytes: Bytes,
62 shape: [u32; 2],
63 bpp: BitsPerPixel,
64 draw_colors: DrawIndices,
65 ) -> Option<Self> {
66 let resolution = shape[0].checked_mul(shape[1]);
67 let capacity = bytes.as_ref().len().checked_mul(1 << (3 - bpp as u32));
68
69 match (resolution, capacity) {
70 (Some(resolution), Some(capacity)) if resolution as usize <= capacity => unsafe {
72 Some(Self::from_bytes_unchecked(bytes, shape, bpp, draw_colors))
73 },
74 _ => None,
75 }
76 }
77}
78
79impl<Bytes: ?Sized> Sprite<Bytes> {
80 pub const fn bytes(&self) -> &Bytes {
82 &self.bytes
83 }
84
85 pub const fn shape(&self) -> [u32; 2] {
87 self.shape
88 }
89
90 pub const fn width(&self) -> u32 {
92 self.shape[0]
93 }
94
95 pub const fn height(&self) -> u32 {
97 self.shape[1]
98 }
99
100 pub const fn bpp(&self) -> BitsPerPixel {
102 self.bpp
103 }
104}
105
106impl Sprite {
107 pub const fn view(&self, start: [u32; 2], shape: [u32; 2]) -> Option<SpriteView<'_>> {
109 let dst_bottom_right = match checked_add_pairs(start, shape) {
110 Some(it) => it,
111 None => return None,
112 };
113 if lt_pairs(start, self.shape) && le_pairs(dst_bottom_right, self.shape) {
114 Some(SpriteView {
115 sprite: self,
116 start,
117 shape,
118 })
119 } else {
120 None
121 }
122 }
123
124 pub const unsafe fn view_unchecked(&self, start: [u32; 2], shape: [u32; 2]) -> SpriteView<'_> {
130 SpriteView {
131 sprite: self,
132 start,
133 shape,
134 }
135 }
136}
137
138#[repr(u32)]
139#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
140pub enum BitsPerPixel {
141 One,
142 Two,
143}
144
145#[derive(Clone, Copy)]
146pub struct SpriteView<'a> {
147 sprite: &'a Sprite,
148 start: [u32; 2],
149 shape: [u32; 2],
150}
151
152impl<'a> SpriteView<'a> {
153 pub const fn sprite(&self) -> &'a Sprite {
155 self.sprite
156 }
157
158 pub const fn start(&self) -> [u32; 2] {
160 self.start
161 }
162
163 pub const fn shape(&self) -> [u32; 2] {
165 self.shape
166 }
167}
168
169#[derive(Clone, Copy, Default)]
170pub struct DrawIndices(u16);
171
172impl DrawIndices {
173 pub const TRANSPARENT: Self = DrawIndices(0);
174
175 pub const fn from_array(array: [DrawIndex; 4]) -> Self {
176 DrawIndices(
177 array[0] as u16
178 | (array[1] as u16) << 4
179 | (array[2] as u16) << 8
180 | (array[3] as u16) << 12,
181 )
182 }
183
184 pub const fn into_array(self) -> [DrawIndex; 4] {
185 unsafe {
186 [
187 DrawIndex::new_unchecked(self.0 & 0xf),
188 DrawIndex::new_unchecked(self.0 >> 4 & 0xf),
189 DrawIndex::new_unchecked(self.0 >> 8 & 0xf),
190 DrawIndex::new_unchecked(self.0 >> 12 & 0xf),
191 ]
192 }
193 }
194
195 pub const unsafe fn from_u16_unchecked(inner: u16) -> Self {
202 DrawIndices(inner)
203 }
204
205 pub const fn into_u16(self) -> u16 {
206 self.0
207 }
208}
209
210impl From<DrawIndices> for u16 {
211 fn from(v: DrawIndices) -> Self {
212 v.0
213 }
214}
215
216#[derive(Clone, Copy, Default)]
217#[repr(u16)]
218pub enum DrawIndex {
219 #[default]
220 Transparent,
221 First,
222 Second,
223 Third,
224 Fourth,
225}
226
227impl DrawIndex {
228 pub const fn new(index: u16) -> Option<Self> {
229 if index <= 4 {
230 Some(unsafe { Self::new_unchecked(index) })
231 } else {
232 None
233 }
234 }
235
236 pub const unsafe fn new_unchecked(index: u16) -> Self {
242 core::mem::transmute(index)
243 }
244}
245
246pub type Palette = [Color; 4];
247
248#[repr(transparent)]
250#[derive(Clone, Copy, Default)]
251pub struct Color(pub u32);
252
253impl Color {
254 pub const BLACK: Self = Color(0);
255
256 pub const fn blue(self) -> u8 {
257 (self.0 & 0xff) as u8
258 }
259
260 pub const fn green(self) -> u8 {
261 (self.0 >> 8 & 0xff) as u8
262 }
263
264 pub const fn red(self) -> u8 {
265 (self.0 >> 16 & 0xff) as u8
266 }
267
268 pub const fn with_blue(self, channel: u8) -> Self {
269 Color((self.0 & !0x0000ff) | (channel as u32))
270 }
271
272 pub const fn with_green(self, channel: u8) -> Self {
273 Color((self.0 & !0x00ff00) | (channel as u32) << 8)
274 }
275
276 pub const fn with_red(self, channel: u8) -> Self {
277 Color((self.0 & !0xff0000) | (channel as u32) << 16)
278 }
279}
280
281const fn checked_add_pairs(lhs: [u32; 2], rhs: [u32; 2]) -> Option<[u32; 2]> {
284 match [lhs[0].checked_add(rhs[0]), lhs[1].checked_add(rhs[1])] {
285 [Some(first), Some(second)] => Some([first, second]),
286 _ => None,
287 }
288}
289
290const fn lt_pairs(lhs: [u32; 2], rhs: [u32; 2]) -> bool {
291 lhs[0] < rhs[0] && lhs[1] < rhs[1]
292}
293
294const fn le_pairs(lhs: [u32; 2], rhs: [u32; 2]) -> bool {
295 lhs[0] <= rhs[0] && lhs[1] <= rhs[1]
296}