use crate::pixel_target::{
PixelTarget, PixelTargetAdapter, fill_ellipse_pixels, pixel_put, pixel_put_565,
};
use embedded_graphics::{
Drawable,
pixelcolor::{Rgb565, Rgb888, raw::RawU16},
prelude::{IntoStorage, Point, Size},
primitives::Rectangle,
primitives::{Circle, Line, Primitive, PrimitiveStyle},
};
#[derive(Clone, Copy, Debug)]
pub struct Image565View {
pixels: &'static [u16],
stride: u32,
source: Rectangle,
}
impl Image565View {
#[must_use]
pub const fn new(pixels: &'static [u16], size: Size) -> Self {
assert!(
pixels.len() == size.width as usize * size.height as usize,
"Image565View pixels must match width * height"
);
Self {
pixels,
stride: size.width,
source: Rectangle::new(Point::zero(), size),
}
}
#[must_use]
pub(crate) const fn new_cropped(
pixels: &'static [u16],
stride: u32,
source: Rectangle,
) -> Self {
Self {
pixels,
stride,
source,
}
}
#[must_use]
pub const fn size(&self) -> Size {
self.source.size
}
#[must_use]
pub fn pixel_at(&self, point: Point) -> Rgb565 {
assert!(
point.x >= 0 && point.y >= 0,
"Image565View pixel coordinate must be non-negative"
);
let vx = point.x as usize;
let vy = point.y as usize;
assert!(
vx < self.source.size.width as usize && vy < self.source.size.height as usize,
"Image565View pixel coordinate must be inside the view"
);
let source_x = self.source.top_left.x as usize + vx;
let source_y = self.source.top_left.y as usize + vy;
let index = source_y * self.stride as usize + source_x;
Rgb565::from(RawU16::new(self.pixels[index]))
}
pub fn rgb565_iter(&self) -> impl Iterator<Item = Rgb565> + '_ {
Image565ViewPixels {
view: *self,
index: 0,
}
}
}
struct Image565ViewPixels {
view: Image565View,
index: usize,
}
impl Iterator for Image565ViewPixels {
type Item = Rgb565;
fn next(&mut self) -> Option<Self::Item> {
let width = self.view.source.size.width as usize;
let height = self.view.source.size.height as usize;
if self.index >= width * height {
return None;
}
let view_x = self.index % width;
let view_y = self.index / width;
let source_x = self.view.source.top_left.x as usize + view_x;
let source_y = self.view.source.top_left.y as usize + view_y;
let source_index = source_y * self.view.stride as usize + source_x;
self.index += 1;
Some(Rgb565::from(RawU16::new(self.view.pixels[source_index])))
}
}
#[derive(Clone, Copy, Debug)]
pub enum DrawItem {
Stroke {
start: (f32, f32),
end: (f32, f32),
color: Rgb888,
pixel_width: f32,
},
Ellipse {
center: (f32, f32),
axis_a: (f32, f32),
axis_b: (f32, f32),
color: Rgb888,
},
Circle {
center: (f32, f32),
pixel_radius: f32,
color: Rgb888,
},
Bitmap { view: Image565View, top_left: Point },
}
impl DrawItem {
pub fn draw<T: PixelTarget>(&self, target: &mut T) {
match *self {
DrawItem::Stroke {
start,
end,
color,
pixel_width,
} => {
let width = ((pixel_width + 0.5) as u32).max(1);
Line::new(
embedded_graphics::prelude::Point::new(start.0 as i32, start.1 as i32),
embedded_graphics::prelude::Point::new(end.0 as i32, end.1 as i32),
)
.into_styled(PrimitiveStyle::with_stroke(color, width))
.draw(&mut PixelTargetAdapter(target))
.expect("drawing onto a PixelTargetAdapter is Infallible");
}
DrawItem::Ellipse {
center,
axis_a,
axis_b,
color,
} => {
fill_ellipse_pixels(center, axis_a, axis_b, |position_x, position_y| {
pixel_put(target, position_x, position_y, color);
});
}
DrawItem::Circle {
center,
pixel_radius,
color,
} => {
let diameter = (((pixel_radius * 2.0) + 0.5) as u32).max(1);
Circle::with_center(
embedded_graphics::prelude::Point::new(center.0 as i32, center.1 as i32),
diameter,
)
.into_styled(PrimitiveStyle::with_fill(color))
.draw(&mut PixelTargetAdapter(target))
.expect("drawing onto a PixelTargetAdapter is Infallible");
}
DrawItem::Bitmap { view, top_left } => {
let size = view.size();
for dy in 0..size.height as i32 {
for dx in 0..size.width as i32 {
let view_point = Point::new(dx, dy);
let target_point = top_left + view_point;
pixel_put_565(
target,
target_point.x,
target_point.y,
view.pixel_at(view_point).into_storage(),
);
}
}
}
}
}
}