use crate::{serialization_helper::get_nibbles_per_row, AreaImgInfo};
use alloc::vec::Vec;
use embedded_graphics_core::{
pixelcolor::{Gray4, GrayColor},
primitives::Rectangle,
};
pub struct AreaSerializer {
area: Rectangle,
rows_per_step: usize,
buffer: Vec<u8>,
}
impl AreaSerializer {
pub fn new(area: Rectangle, color: Gray4, buffer_size: usize) -> Self {
let raw_color = color.luma();
let data_entry = raw_color << 4 | raw_color;
assert!(
buffer_size.is_multiple_of(2),
"Buffer size must be aligned to u16"
);
let entries_per_row = get_nibbles_per_row(area) as usize * 2; let rows_per_step = (buffer_size / entries_per_row).min(area.size.height as usize);
assert!(rows_per_step > 0, "Buffer size to small for one row");
let buffer = vec![data_entry; entries_per_row * rows_per_step];
AreaSerializer {
area,
rows_per_step,
buffer,
}
}
}
pub struct AreaSerializerIterator<'a> {
area_serializer: &'a AreaSerializer,
row: usize,
}
impl<'a> AreaSerializerIterator<'a> {
pub fn new(area_serializer: &'a AreaSerializer) -> AreaSerializerIterator<'a> {
AreaSerializerIterator {
area_serializer,
row: 0,
}
}
}
impl<'a> Iterator for AreaSerializerIterator<'a> {
type Item = (AreaImgInfo, &'a [u8]);
fn next(&mut self) -> Option<Self::Item> {
let area_height = self.area_serializer.area.size.height;
if self.row >= area_height as usize {
return None;
}
let start_row = self.row;
self.row = (start_row + self.area_serializer.rows_per_step).min(area_height as usize);
Some((
AreaImgInfo {
area_x: self.area_serializer.area.top_left.x as u16,
area_y: (self.area_serializer.area.top_left.y + start_row as i32) as u16,
area_w: self.area_serializer.area.size.width as u16,
area_h: (self.row - start_row) as u16,
},
&self.area_serializer.buffer,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics_core::prelude::*;
const BOUNDING_BOX_DEFAULT: Rectangle = Rectangle {
top_left: Point { x: 0, y: 0 },
size: Size {
width: 10,
height: 10,
},
};
#[test]
fn test_pixel_0() {
let area = Rectangle {
top_left: Point { x: 0, y: 0 },
size: Size {
width: 1,
height: 1,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 0,
area_y: 0,
area_w: 1,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_1() {
let area = Rectangle {
top_left: Point { x: 1, y: 1 },
size: Size {
width: 1,
height: 1,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 1,
area_y: 1,
area_w: 1,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_2() {
let area = Rectangle {
top_left: Point { x: 2, y: 1 },
size: Size {
width: 1,
height: 1,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 2,
area_y: 1,
area_w: 1,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_3() {
let area = Rectangle {
top_left: Point { x: 3, y: 1 },
size: Size {
width: 1,
height: 1,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 3,
area_y: 1,
area_w: 1,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_single_row_packed() {
let area = Rectangle {
top_left: Point { x: 4, y: 1 },
size: Size {
width: 4,
height: 1,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 4,
area_y: 1,
area_w: 4,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_single_row() {
let area = Rectangle {
top_left: Point { x: 3, y: 1 },
size: Size {
width: 3,
height: 1,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 3,
area_y: 1,
area_w: 3,
area_h: 1
},
[0xAA, 0xAA, 0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_rows_packed() {
let area = Rectangle {
top_left: Point { x: 4, y: 1 },
size: Size {
width: 4,
height: 2,
},
};
let area_s =
AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 2);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 4,
area_y: 1,
area_w: 4,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 4,
area_y: 2,
area_w: 4,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_rows() {
let area = Rectangle {
top_left: Point { x: 3, y: 1 },
size: Size {
width: 3,
height: 2,
},
};
let area_s =
AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 4);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 3,
area_y: 1,
area_w: 3,
area_h: 1
},
[0xAA, 0xAA, 0xAA, 0xAA].as_slice()
))
);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 3,
area_y: 2,
area_w: 3,
area_h: 1
},
[0xAA, 0xAA, 0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_rows_packed_multirow() {
let area = Rectangle {
top_left: Point { x: 4, y: 1 },
size: Size {
width: 4,
height: 2,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 4,
area_y: 1,
area_w: 4,
area_h: 2
},
[0xAA, 0xAA, 0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_rows_multirow() {
let area = Rectangle {
top_left: Point { x: 3, y: 1 },
size: Size {
width: 3,
height: 2,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 3,
area_y: 1,
area_w: 3,
area_h: 2
},
[0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
#[test]
fn test_pixel_non_drawable_top_left() {
let area = Rectangle {
top_left: Point { x: -1, y: -1 },
size: Size {
width: 3,
height: 2,
},
};
let area_s = AreaSerializer::new(
area.intersection(&BOUNDING_BOX_DEFAULT),
Gray4::new(0xA),
1024,
);
let mut s = AreaSerializerIterator::new(&area_s);
assert_eq!(
s.next(),
Some((
AreaImgInfo {
area_x: 0,
area_y: 0,
area_w: 2,
area_h: 1
},
[0xAA, 0xAA].as_slice()
))
);
assert_eq!(s.next(), None);
}
}