use embedded_graphics_core::{
prelude::{
Dimensions, DrawTarget, Drawable, OriginDimensions, Pixel, PixelColor, Point, PointsIter,
Size,
},
primitives::Rectangle,
};
use crate::utils::center_offset;
pub struct CCanvas<C, const W: usize, const H: usize> {
pub size: Size,
pub pixels: [[Option<C>; H]; W],
}
impl<C: Copy + PartialEq, const W: usize, const H: usize> Default for CCanvas<C, W, H> {
fn default() -> Self {
Self::new()
}
}
impl<C, const W: usize, const H: usize> CCanvas<C, W, H>
where
C: Copy + PartialEq,
{
pub fn new() -> Self {
Self {
size: Size::new(W as u32, H as u32),
pixels: [[None; H]; W],
}
}
pub fn with_default_color(default_color: C) -> Self {
Self {
size: Size::new(W as u32, H as u32),
pixels: [[Some(default_color); H]; W],
}
}
pub fn get_pixel(&self, point: Point) -> Option<C> {
let x = usize::try_from(point.x).ok()?;
let y = usize::try_from(point.y).ok()?;
self.pixels
.get(x)
.and_then(|x_row| x_row.get(y))
.copied()
.flatten()
}
pub fn center(&self) -> Point {
Point::zero() + center_offset(self.size)
}
}
impl<C, const W: usize, const H: usize> CCanvas<C, W, H>
where
C: PixelColor,
{
pub fn crop<const NW: usize, const NH: usize>(
&self,
area: &Rectangle,
) -> Option<CCanvas<C, NW, NH>> {
let mut new = CCanvas::<C, NW, NH>::new();
let area_bottom_right = area.bottom_right()?;
let new_pixels = self.pixels.iter().enumerate().flat_map(|(x, x_row)| {
x_row.iter().enumerate().filter_map(move |(y, color)| {
let color = match color {
Some(color) => *color,
None => return None,
};
let point = Point::new(x as i32, y as i32);
if point >= area.top_left && point <= area_bottom_right {
let pixel = Pixel(point - area.top_left, color);
Some(pixel)
} else {
None
}
})
});
new.draw_iter(new_pixels).ok().map(|_| new)
}
pub fn place_at(&self, top_left: Point) -> CCanvasAt<C, W, H> {
CCanvasAt {
top_left,
size: self.size,
pixels: self.pixels,
}
}
pub fn place_center(&self, center: Point) -> CCanvasAt<C, W, H> {
let top_left = center - center_offset(self.size);
self.place_at(top_left)
}
}
impl<C, const W: usize, const H: usize> OriginDimensions for CCanvas<C, W, H> {
fn size(&self) -> Size {
self.size
}
}
impl<C: PixelColor, const W: usize, const H: usize> DrawTarget for CCanvas<C, W, H> {
type Color = C;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(point, color) in pixels.into_iter() {
if let (Some(x), Some(y)) =
(usize::try_from(point.x).ok(), usize::try_from(point.y).ok())
{
self.pixels[x][y] = Some(color);
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub struct CCanvasAt<C: PixelColor, const W: usize, const H: usize> {
pub top_left: Point,
size: Size,
pub pixels: [[Option<C>; H]; W],
}
impl<C, const W: usize, const H: usize> CCanvasAt<C, W, H>
where
C: PixelColor,
{
pub fn new(top_left: Point) -> Self {
Self {
top_left,
size: Size::new(W as u32, H as u32),
pixels: [[None; H]; W],
}
}
pub fn with_default_color(top_left: Point, default_color: C) -> Self {
Self {
top_left,
size: Size::new(W as u32, H as u32),
pixels: [[Some(default_color); H]; W],
}
}
pub fn with_center(center: Point) -> Self {
let top_left = center - center_offset(Size::new(W as u32, H as u32));
Self::new(top_left)
}
pub fn center(&self) -> Point {
self.bounding_box().center()
}
pub fn get_pixel(&self, point: Point) -> Option<C> {
let point_adjusted = point - self.top_left;
let x = usize::try_from(point_adjusted.x).ok()?;
let y = usize::try_from(point_adjusted.y).ok()?;
self.pixels
.get(x)
.and_then(|x_row| x_row.get(y))
.copied()
.flatten()
}
}
impl<C: PixelColor, const W: usize, const H: usize> Dimensions for CCanvasAt<C, W, H> {
fn bounding_box(&self) -> Rectangle {
Rectangle::new(self.top_left, self.size)
}
}
impl<C: PixelColor, const W: usize, const H: usize> DrawTarget for CCanvasAt<C, W, H> {
type Color = C;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(point, color) in pixels.into_iter() {
let point_adjusted = point - self.top_left;
if let (Some(x), Some(y)) = (
usize::try_from(point_adjusted.x).ok(),
usize::try_from(point_adjusted.y).ok(),
) {
self.pixels[x][y] = Some(color);
};
}
Ok(())
}
}
impl<C, const W: usize, const H: usize> Drawable for CCanvasAt<C, W, H>
where
C: PixelColor,
{
type Color = C;
type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
where
D: DrawTarget<Color = C>,
{
let pixels_iter = self.bounding_box().points().filter_map(|point| {
self.get_pixel(point).map(|color| Pixel(point, color))
});
target.draw_iter(pixels_iter)
}
}
#[cfg(feature = "transform")]
#[cfg_attr(docsrs, doc(cfg(feature = "transform")))]
impl<C: PixelColor, const W: usize, const H: usize> embedded_graphics::transform::Transform
for CCanvasAt<C, W, H>
{
fn translate(&self, by: Point) -> Self {
Self {
top_left: self.top_left + by,
size: self.size,
pixels: self.pixels,
}
}
fn translate_mut(&mut self, by: Point) -> &mut Self {
self.top_left += by;
self
}
}