1use core::fmt::Debug;
16use core::marker::PhantomData;
17
18use embedded_graphics_core::{
19 Drawable, Pixel,
20 draw_target::DrawTarget,
21 geometry::{Dimensions, Point},
22 pixelcolor::{PixelColor, Rgb565},
23 primitives::Rectangle,
24};
25use embedded_graphics_framebuf::{FrameBuf, backends::FrameBufferBackend};
26
27use crate::DrawPrimitive;
28#[cfg(feature = "aa")]
29use crate::draw::ReadPixel;
30use crate::draw::draw;
31#[cfg(feature = "aa")]
32use embedded_graphics_core::pixelcolor::RgbColor;
33
34pub struct ColorAdapter<'a, C, D> {
39 inner: &'a mut D,
40 _phantom: PhantomData<C>,
41}
42
43impl<C, D> Dimensions for ColorAdapter<'_, C, D>
44where
45 C: PixelColor,
46 D: DrawTarget<Color = C>,
47{
48 fn bounding_box(&self) -> Rectangle {
49 self.inner.bounding_box()
50 }
51}
52
53impl<C, D> DrawTarget for ColorAdapter<'_, C, D>
54where
55 C: PixelColor + From<Rgb565>,
56 D: DrawTarget<Color = C>,
57{
58 type Color = Rgb565;
59 type Error = D::Error;
60
61 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
62 where
63 I: IntoIterator<Item = Pixel<Rgb565>>,
64 {
65 self.inner.draw_iter(
66 pixels
67 .into_iter()
68 .map(|Pixel(pos, color)| Pixel(pos, C::from(color))),
69 )
70 }
71}
72
73pub fn draw_to<C, D>(primitive: DrawPrimitive, fb: &mut D)
89where
90 C: PixelColor + From<Rgb565>,
91 D: DrawTarget<Color = C>,
92 D::Error: Debug,
93{
94 let mut adapter = ColorAdapter {
95 inner: fb,
96 _phantom: PhantomData,
97 };
98 draw(primitive, &mut adapter);
99}
100
101#[inline]
105pub fn nalgebra_to_eg(p: nalgebra::Point2<i32>) -> Point {
106 Point::new(p.x, p.y)
107}
108
109#[inline]
111pub fn eg_to_nalgebra(p: Point) -> nalgebra::Point2<i32> {
112 nalgebra::Point2::new(p.x, p.y)
113}
114
115pub trait AsEgPoint {
117 fn as_eg_point(&self) -> Point;
119}
120
121impl AsEgPoint for nalgebra::Point2<i32> {
122 #[inline]
123 fn as_eg_point(&self) -> Point {
124 Point::new(self.x, self.y)
125 }
126}
127
128pub trait AsNalgebraPoint {
130 fn as_nalgebra(&self) -> nalgebra::Point2<i32>;
132}
133
134impl AsNalgebraPoint for Point {
135 #[inline]
136 fn as_nalgebra(&self) -> nalgebra::Point2<i32> {
137 nalgebra::Point2::new(self.x, self.y)
138 }
139}
140
141#[cfg(feature = "aa")]
149impl<B> ReadPixel for FrameBuf<Rgb565, B>
150where
151 B: FrameBufferBackend<Color = Rgb565>,
152{
153 fn read_pixel(&self, point: Point) -> Rgb565 {
154 let w = self.width() as i32;
155 let h = self.height() as i32;
156 if point.x < 0 || point.x >= w || point.y < 0 || point.y >= h {
157 return <Rgb565 as RgbColor>::BLACK;
158 }
159 self.get_color_at(point)
160 }
161}
162
163struct SliceBackend<'a>(pub &'a mut [Rgb565]);
166
167impl FrameBufferBackend for SliceBackend<'_> {
168 type Color = Rgb565;
169 fn set(&mut self, index: usize, color: Rgb565) {
170 self.0[index] = color;
171 }
172 fn get(&self, index: usize) -> Rgb565 {
173 self.0[index]
174 }
175 fn nr_elements(&self) -> usize {
176 self.0.len()
177 }
178}
179
180pub fn render_drawable_to_buffer<D>(
204 drawable: &D,
205 buffer: &mut [Rgb565],
206 width: usize,
207 height: usize,
208) -> Result<(), ()>
209where
210 D: Drawable<Color = Rgb565>,
211{
212 if buffer.len() != width * height {
213 return Err(());
214 }
215 let mut fb = FrameBuf::new(SliceBackend(buffer), width, height);
216 drawable.draw(&mut fb).map(|_| ()).map_err(|_| ())
217}
218
219#[cfg(test)]
220mod tests {
221 extern crate std;
222
223 use super::*;
224 use embedded_graphics_core::pixelcolor::RgbColor;
225 use embedded_graphics_framebuf::backends::{EndianCorrectedBuffer, EndianCorrection};
226 use nalgebra::Point2;
227
228 struct SingleRedPixel;
229
230 impl Drawable for SingleRedPixel {
231 type Color = Rgb565;
232 type Output = ();
233
234 fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
235 where
236 D: DrawTarget<Color = Self::Color>,
237 {
238 target.draw_iter([Pixel(Point::new(0, 0), Rgb565::RED)])
239 }
240 }
241
242 #[test]
243 fn point_conversion_helpers_roundtrip() {
244 let n = Point2::new(12, -9);
245 let eg = nalgebra_to_eg(n);
246 assert_eq!(eg, Point::new(12, -9));
247 let n2 = eg_to_nalgebra(eg);
248 assert_eq!(n2, n);
249 assert_eq!(n.as_eg_point(), eg);
250 assert_eq!(eg.as_nalgebra(), n);
251 }
252
253 #[test]
254 fn render_drawable_to_buffer_validates_buffer_size() {
255 let mut too_short = [Rgb565::BLACK; 3];
256 let err = render_drawable_to_buffer(&SingleRedPixel, &mut too_short, 2, 2);
257 assert!(err.is_err());
258 }
259
260 #[test]
261 fn render_drawable_to_buffer_draws_into_slice() {
262 let mut data = [Rgb565::BLACK; 4];
263 render_drawable_to_buffer(&SingleRedPixel, &mut data, 2, 2).unwrap();
264 assert_eq!(data[0], Rgb565::RED);
265 }
266
267 #[test]
268 fn draw_to_writes_primitive_to_target() {
269 let backing = std::vec![Rgb565::BLACK; 4].leak();
270 let mut fb = FrameBuf::new(
271 EndianCorrectedBuffer::new(backing, EndianCorrection::ToLittleEndian),
272 2,
273 2,
274 );
275 draw_to::<Rgb565, _>(
276 DrawPrimitive::ColoredPoint(Point2::new(1, 1), Rgb565::new(31, 0, 0)),
277 &mut fb,
278 );
279 assert_eq!(fb.get_color_at(Point::new(1, 1)), Rgb565::new(31, 0, 0));
280 }
281}