device_envoy_core/cyd/display/
draw_item.rs1use crate::pixel_target::{
2 PixelTarget, PixelTargetAdapter, fill_ellipse_pixels, pixel_put, pixel_put_565,
3};
4use embedded_graphics::{
5 Drawable,
6 pixelcolor::{Rgb565, Rgb888, raw::RawU16},
7 prelude::{IntoStorage, Point, Size},
8 primitives::Rectangle,
9 primitives::{Circle, Line, Primitive, PrimitiveStyle},
10};
11
12#[derive(Clone, Copy, Debug)]
21pub struct Image565View {
22 pixels: &'static [u16],
23 stride: u32,
24 source: Rectangle,
25}
26
27impl Image565View {
28 #[must_use]
32 pub const fn new(pixels: &'static [u16], size: Size) -> Self {
33 assert!(
34 pixels.len() == size.width as usize * size.height as usize,
35 "Image565View pixels must match width * height"
36 );
37 Self {
38 pixels,
39 stride: size.width,
40 source: Rectangle::new(Point::zero(), size),
41 }
42 }
43
44 #[must_use]
47 pub(crate) const fn new_cropped(
48 pixels: &'static [u16],
49 stride: u32,
50 source: Rectangle,
51 ) -> Self {
52 Self {
53 pixels,
54 stride,
55 source,
56 }
57 }
58
59 #[must_use]
60 pub const fn size(&self) -> Size {
61 self.source.size
62 }
63
64 #[must_use]
67 pub fn pixel_at(&self, point: Point) -> Rgb565 {
68 assert!(
69 point.x >= 0 && point.y >= 0,
70 "Image565View pixel coordinate must be non-negative"
71 );
72 let vx = point.x as usize;
73 let vy = point.y as usize;
74 assert!(
75 vx < self.source.size.width as usize && vy < self.source.size.height as usize,
76 "Image565View pixel coordinate must be inside the view"
77 );
78 let source_x = self.source.top_left.x as usize + vx;
79 let source_y = self.source.top_left.y as usize + vy;
80 let index = source_y * self.stride as usize + source_x;
81 Rgb565::from(RawU16::new(self.pixels[index]))
82 }
83
84 pub fn rgb565_iter(&self) -> impl Iterator<Item = Rgb565> + '_ {
89 Image565ViewPixels {
90 view: *self,
91 index: 0,
92 }
93 }
94}
95
96struct Image565ViewPixels {
97 view: Image565View,
98 index: usize,
99}
100
101impl Iterator for Image565ViewPixels {
102 type Item = Rgb565;
103
104 fn next(&mut self) -> Option<Self::Item> {
105 let width = self.view.source.size.width as usize;
106 let height = self.view.source.size.height as usize;
107 if self.index >= width * height {
108 return None;
109 }
110
111 let view_x = self.index % width;
112 let view_y = self.index / width;
113 let source_x = self.view.source.top_left.x as usize + view_x;
114 let source_y = self.view.source.top_left.y as usize + view_y;
115 let source_index = source_y * self.view.stride as usize + source_x;
116 self.index += 1;
117 Some(Rgb565::from(RawU16::new(self.view.pixels[source_index])))
118 }
119}
120
121#[derive(Clone, Copy, Debug)]
128pub enum DrawItem {
129 Stroke {
131 start: (f32, f32),
132 end: (f32, f32),
133 color: Rgb888,
134 pixel_width: f32,
135 },
136 Ellipse {
140 center: (f32, f32),
141 axis_a: (f32, f32),
142 axis_b: (f32, f32),
143 color: Rgb888,
144 },
145 Circle {
147 center: (f32, f32),
148 pixel_radius: f32,
149 color: Rgb888,
150 },
151 Bitmap { view: Image565View, top_left: Point },
153}
154
155impl DrawItem {
156 pub fn draw<T: PixelTarget>(&self, target: &mut T) {
162 match *self {
163 DrawItem::Stroke {
164 start,
165 end,
166 color,
167 pixel_width,
168 } => {
169 let width = ((pixel_width + 0.5) as u32).max(1);
170 Line::new(
171 embedded_graphics::prelude::Point::new(start.0 as i32, start.1 as i32),
172 embedded_graphics::prelude::Point::new(end.0 as i32, end.1 as i32),
173 )
174 .into_styled(PrimitiveStyle::with_stroke(color, width))
175 .draw(&mut PixelTargetAdapter(target))
176 .expect("drawing onto a PixelTargetAdapter is Infallible");
177 }
178 DrawItem::Ellipse {
179 center,
180 axis_a,
181 axis_b,
182 color,
183 } => {
184 fill_ellipse_pixels(center, axis_a, axis_b, |position_x, position_y| {
185 pixel_put(target, position_x, position_y, color);
186 });
187 }
188 DrawItem::Circle {
189 center,
190 pixel_radius,
191 color,
192 } => {
193 let diameter = (((pixel_radius * 2.0) + 0.5) as u32).max(1);
194 Circle::with_center(
195 embedded_graphics::prelude::Point::new(center.0 as i32, center.1 as i32),
196 diameter,
197 )
198 .into_styled(PrimitiveStyle::with_fill(color))
199 .draw(&mut PixelTargetAdapter(target))
200 .expect("drawing onto a PixelTargetAdapter is Infallible");
201 }
202 DrawItem::Bitmap { view, top_left } => {
203 let size = view.size();
204 for dy in 0..size.height as i32 {
205 for dx in 0..size.width as i32 {
206 let view_point = Point::new(dx, dy);
207 let target_point = top_left + view_point;
208 pixel_put_565(
209 target,
210 target_point.x,
211 target_point.y,
212 view.pixel_at(view_point).into_storage(),
213 );
214 }
215 }
216 }
217 }
218 }
219}