1#[cfg(feature = "clip")]
3#[path = "frame_clip.rs"]
4pub mod clip;
5#[cfg(feature = "offset")]
6#[path = "frame_offset.rs"]
7pub mod offset;
8#[cfg(feature = "rotate")]
9#[path = "frame_rotate.rs"]
10pub mod rotate;
11
12use super::color::{PixelColor, Rgb565};
13use std::fmt::{self, Write};
14use std::ops::{Index, IndexMut};
15
16#[derive(Copy, Clone)]
18pub struct FrameLine([Rgb565; 64]);
19
20impl FrameLine {
21 fn new() -> Self {
23 FrameLine([Rgb565::default(); 64])
24 }
25
26 pub fn from_slice(bytes: &[u8; 128]) -> Self {
28 let colors = bytes
29 .chunks(2)
30 .map(|chunk| Rgb565::from([chunk[0], chunk[1]]))
31 .enumerate()
32 .fold(
33 [Rgb565::default(); 64],
34 |mut color_array, (index, color)| {
35 color_array[index] = color;
36 color_array
37 },
38 );
39 FrameLine(colors)
40 }
41
42 pub fn from_pixels(pixels: &[PixelColor; 64]) -> Self {
44 let colors = pixels.iter().map(Rgb565::from).enumerate().fold(
45 [Rgb565::default(); 64],
46 |mut color_array, (index, color)| {
47 color_array[index] = color;
48 color_array
49 },
50 );
51 FrameLine(colors)
52 }
53
54 pub fn as_bytes(&self) -> [u8; 128] {
56 self.0
57 .iter()
58 .cloned()
59 .map(|color| {
60 let bytes: [u8; 2] = color.into();
61 bytes
62 })
63 .enumerate()
64 .fold([0u8; 128], |mut byte_array, (index, color)| {
65 byte_array[index * 2] = color[0];
66 byte_array[(index * 2) + 1] = color[1];
67 byte_array
68 })
69 }
70}
71
72impl Default for FrameLine {
73 fn default() -> Self {
74 FrameLine::new()
75 }
76}
77
78impl fmt::Debug for FrameLine {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 let rows = self.0.chunks(8).fold(String::new(), |mut s, row| {
81 write!(&mut s, "\n[").unwrap();
82 for &px in row {
83 let rgbu16: u16 = px.into();
84 write!(&mut s, " {:04X}", rgbu16).unwrap();
85 }
86 write!(&mut s, " ]").unwrap();
87 s
88 });
89 write!(f, "FrameLine:\n{}", rows)
90 }
91}
92
93impl PartialEq for FrameLine {
94 fn eq(&self, other: &FrameLine) -> bool {
95 self.0
96 .iter()
97 .zip(other.0.iter())
98 .fold(true, |eq, (a, b)| eq && a == b)
99 }
100}
101
102#[derive(Copy, Clone)]
104pub struct PixelFrame([PixelColor; 64]);
105
106impl fmt::Debug for PixelFrame {
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 let rows = self.0.chunks(8).fold(String::new(), |mut s, row| {
109 writeln!(&mut s, "{:?}", row).unwrap();
110 s
111 });
112 write!(f, "PixelFrame:\n{}", rows)
113 }
114}
115
116impl Default for PixelFrame {
117 fn default() -> Self {
118 PixelFrame([PixelColor::BLACK; 64])
119 }
120}
121
122impl PartialEq for PixelFrame {
123 fn eq(&self, other: &PixelFrame) -> bool {
124 self.0
125 .iter()
126 .zip(other.0.iter())
127 .fold(true, |eq, (a, b)| eq && a == b)
128 }
129}
130
131impl PixelFrame {
132 pub const BLACK: PixelFrame = PixelFrame([PixelColor::BLACK; 64]);
133 pub const RED: PixelFrame = PixelFrame([PixelColor::RED; 64]);
134 pub const BLUE: PixelFrame = PixelFrame([PixelColor::BLUE; 64]);
135 pub const GREEN: PixelFrame = PixelFrame([PixelColor::GREEN; 64]);
136 pub const WHITE: PixelFrame = PixelFrame([PixelColor::WHITE; 64]);
137 pub const YELLOW: PixelFrame = PixelFrame([PixelColor::YELLOW; 64]);
138 pub const CYAN: PixelFrame = PixelFrame([PixelColor::CYAN; 64]);
139 pub const MAGENTA: PixelFrame = PixelFrame([PixelColor::MAGENTA; 64]);
140}
141
142impl PixelFrame {
143 pub fn new(pixels: &[PixelColor; 64]) -> Self {
145 PixelFrame(*pixels)
146 }
147 pub fn frame_line(&self) -> FrameLine {
149 let colors = self
150 .0
151 .iter()
152 .enumerate()
153 .fold([PixelColor::BLACK; 64], |mut c, (idx, px)| {
154 c[idx] = *px;
155 c
156 });
157 FrameLine::from_pixels(&colors)
158 }
159
160 pub fn transpose(&mut self) {
162 for row in 0..8 {
163 for col in row..8 {
164 let idx = row * 8 + col;
165 let idx_transpose = col * 8 + row;
166 self.0.swap(idx, idx_transpose);
167 }
168 }
169 }
170
171 pub fn flip_h(&mut self) {
173 for row in self.0.chunks_mut(8) {
174 row.reverse();
175 }
176 }
177
178 pub fn flip_v(&mut self) {
180 self.reverse();
181 self.flip_h();
182 }
183
184 pub fn reverse(&mut self) {
186 self.0.reverse();
187 }
188
189 pub fn as_rows(&self) -> [[PixelColor; 8]; 8] {
191 let pixels = self.0;
192 let mut rows = [[PixelColor::default(); 8]; 8];
193 pixels.chunks(8).enumerate().for_each(|(idx, row)| {
194 rows[idx].copy_from_slice(row);
195 });
196 rows
197 }
198
199 pub fn as_columns(&self) -> [[PixelColor; 8]; 8] {
201 let mut pixels = *self;
202 pixels.transpose();
203 let mut columns = [[PixelColor::default(); 8]; 8];
204 pixels.0.chunks(8).enumerate().for_each(|(idx, col)| {
205 columns[idx].copy_from_slice(col);
206 });
207 columns
208 }
209
210 pub fn from_rows(rows: &[[PixelColor; 8]; 8]) -> Self {
212 let mut pixels = [PixelColor::default(); 64];
213 for (row_idx, row) in rows.iter().enumerate() {
214 for (col_idx, &px) in row.iter().enumerate() {
215 pixels[row_idx * 8 + col_idx] = px;
216 }
217 }
218 PixelFrame(pixels)
219 }
220
221 pub fn from_columns(columns: &[[PixelColor; 8]; 8]) -> Self {
223 let mut pixels = [PixelColor::default(); 64];
224 for (col_idx, col) in columns.iter().enumerate() {
225 for (row_idx, &px) in col.iter().enumerate() {
226 pixels[row_idx * 8 + col_idx] = px;
227 }
228 }
229 PixelFrame(pixels)
230 }
231}
232
233impl<'a> From<&'a [PixelColor; 64]> for PixelFrame {
234 fn from(array: &'a [PixelColor; 64]) -> Self {
235 PixelFrame::new(array)
236 }
237}
238
239impl From<[PixelColor; 64]> for PixelFrame {
240 fn from(array: [PixelColor; 64]) -> Self {
241 PixelFrame(array)
242 }
243}
244
245impl Into<[PixelColor; 64]> for PixelFrame {
246 fn into(self) -> [PixelColor; 64] {
247 self.0
248 }
249}
250
251impl Index<usize> for PixelFrame {
252 type Output = PixelColor;
253
254 fn index(&self, index: usize) -> &Self::Output {
255 &self.0[index]
256 }
257}
258
259impl IndexMut<usize> for PixelFrame {
260 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
261 &mut self.0[index]
262 }
263}
264
265#[cfg(any(feature = "offset", feature = "clip"))]
267#[derive(Copy, Clone, Debug, PartialEq)]
268pub enum Offset {
269 Left(u8),
270 Right(u8),
271 Bottom(u8),
272 Top(u8),
273}
274
275#[cfg(any(feature = "offset", feature = "clip"))]
276impl Offset {
277 pub fn left(offset: u8) -> Self {
282 assert!(offset < 9);
283 Offset::Left(offset)
284 }
285
286 pub fn right(offset: u8) -> Self {
291 assert!(offset < 9);
292 Offset::Right(offset)
293 }
294
295 pub fn bottom(offset: u8) -> Self {
300 assert!(offset < 9);
301 Offset::Bottom(offset)
302 }
303
304 pub fn top(offset: u8) -> Self {
309 assert!(offset < 9);
310 Offset::Top(offset)
311 }
312}
313
314#[cfg(any(feature = "offset", feature = "clip"))]
315fn clip_pixel_frames_offset_left(first: PixelFrame, second: PixelFrame, offset: u8) -> PixelFrame {
316 assert!(offset < 9);
317 match offset as usize {
318 0 => first,
319 8 => second,
320 n => {
321 let mut orig = first.as_columns();
322 let second = second.as_columns();
323 {
324 orig.rotate_left(n);
325 let (_, right) = orig.split_at_mut(8 - n);
326 right.copy_from_slice(&second[..n]);
327 }
328 PixelFrame::from_columns(&orig)
329 }
330 }
331}
332
333#[cfg(any(feature = "offset", feature = "clip"))]
334fn clip_pixel_frames_offset_right(first: PixelFrame, second: PixelFrame, offset: u8) -> PixelFrame {
335 match offset as usize {
336 0 => first,
337 8 => second,
338 n => {
339 let mut orig = first.as_columns();
340 let second = second.as_columns();
341 {
342 orig.rotate_right(n);
343 let (left, _) = orig.split_at_mut(n);
344 left.copy_from_slice(&second[8 - n..]);
345 }
346 PixelFrame::from_columns(&orig)
347 }
348 }
349}
350
351#[cfg(any(feature = "offset", feature = "clip"))]
352fn clip_pixel_frames_offset_top(first: PixelFrame, second: PixelFrame, offset: u8) -> PixelFrame {
353 match offset as usize {
354 0 => first,
355 8 => second,
356 n => {
357 let mut orig = first.as_rows();
358 let second = second.as_rows();
359 {
360 orig.rotate_left(n);
361 let (_, right) = orig.split_at_mut(8 - n);
362 right.copy_from_slice(&second[..n]);
363 }
364 PixelFrame::from_rows(&orig)
365 }
366 }
367}
368
369#[cfg(any(feature = "offset", feature = "clip"))]
370fn clip_pixel_frames_offset_bottom(
371 first: PixelFrame,
372 second: PixelFrame,
373 offset: u8,
374) -> PixelFrame {
375 match offset as usize {
376 0 => first,
377 8 => second,
378 n => {
379 let mut orig = first.as_rows();
380 let second = second.as_rows();
381 {
382 orig.rotate_right(n);
383 let (left, _) = orig.split_at_mut(n);
384 left.copy_from_slice(&second[8 - n..]);
385 }
386 PixelFrame::from_rows(&orig)
387 }
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394
395 const RED: PixelColor = PixelColor::RED;
396 const ONE: PixelColor = PixelColor::WHITE;
397 const TWO: PixelColor = PixelColor::BLUE;
398 const PIXEL_FRAME: &[PixelColor; 64] = &[
399 RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, RED, ONE, RED, TWO, ];
408 fn test_rows() -> [[PixelColor; 8]; 8] {
409 [[RED, ONE, RED, TWO, RED, ONE, RED, TWO]; 8]
410 }
411 fn test_columns() -> [[PixelColor; 8]; 8] {
412 [
413 [RED; 8], [ONE; 8], [RED; 8], [TWO; 8], [RED; 8], [ONE; 8], [RED; 8], [TWO; 8],
414 ]
415 }
416
417 #[test]
418 fn frame_line_is_created_from_slice_of_bytes() {
419 let color: [u8; 128] = [0xE0; 128];
420 let frame_line = FrameLine::from_slice(&color);
421 frame_line
422 .as_bytes()
423 .iter()
424 .zip(color.iter())
425 .for_each(|(a, b)| {
426 assert_eq!(a, b);
427 });
428 }
429
430 #[cfg(not(feature = "big-endian"))]
431 #[test]
432 fn frame_line_is_created_from_slice_of_pixel_color() {
433 let blue = PixelColor::from_rgb565_bytes([0x1F, 0x00]);
434 let frame_line = FrameLine::from_pixels(&[blue; 64]);
435 frame_line.as_bytes().chunks(2).for_each(|chunk| {
436 assert_eq!([chunk[0], chunk[1]], [0x1F, 0x00]);
437 });
438 }
439
440 #[cfg(feature = "big-endian")]
441 #[test]
442 fn frame_line_is_created_from_slice_of_pixel_color() {
443 let blue = PixelColor::from_rgb565_bytes([0x00, 0x1F]);
444 let frame_line = FrameLine::from_pixels(&[blue; 64]);
445 frame_line.as_bytes().chunks(2).for_each(|chunk| {
446 assert_eq!([chunk[0], chunk[1]], [0x00, 0x1F]);
447 });
448 }
449
450 #[test]
451 fn pixel_frame_is_created_from_a_slice_of_pixel_color() {
452 let color_frame = [PixelColor::YELLOW; 64];
453 let pixel_frame = PixelFrame::new(&color_frame);
454 pixel_frame
455 .0
456 .iter()
457 .zip(color_frame.iter())
458 .for_each(|(a, b)| {
459 assert_eq!(a, b);
460 });
461 }
462
463 #[test]
464 fn pixel_frame_creates_a_frame_line_of_the_current_state() {
465 let color_frame = [PixelColor::GREEN; 64];
466 let pixel_frame = PixelFrame::new(&color_frame);
467 assert_eq!(
468 pixel_frame.frame_line(),
469 FrameLine::from_pixels(&color_frame)
470 );
471 }
472
473 #[test]
474 fn pixel_frame_is_represented_as_rows_of_pixel_color() {
475 let pixel_frame = PixelFrame::new(PIXEL_FRAME);
476 assert_eq!(pixel_frame.as_rows(), test_rows());
477 }
478
479 #[test]
480 fn pixel_frame_is_represented_as_columns_of_pixel_color() {
481 let pixel_frame = PixelFrame::new(PIXEL_FRAME);
482 assert_eq!(pixel_frame.as_columns(), test_columns());
483 }
484
485 #[test]
486 fn pixel_frame_is_created_from_rows_of_pixel_color() {
487 let pixel_frame = PixelFrame::new(PIXEL_FRAME);
488 assert_eq!(PixelFrame::from_rows(&test_rows()), pixel_frame);
489 }
490
491 #[test]
492 fn pixel_frame_is_created_from_columns_of_pixel_color() {
493 let pixel_frame = PixelFrame::new(PIXEL_FRAME);
494 assert_eq!(PixelFrame::from_columns(&test_columns()), pixel_frame);
495 }
496}