1use core::fmt::Debug;
19use core::marker::PhantomData;
20
21use embedded_graphics_core::{
22 Drawable, Pixel,
23 draw_target::DrawTarget,
24 geometry::{Dimensions, Point},
25 pixelcolor::{PixelColor, Rgb565},
26 primitives::Rectangle,
27};
28use embedded_graphics_framebuf::{FrameBuf, backends::FrameBufferBackend};
29
30use crate::DrawPrimitive;
31use crate::draw::draw;
32
33pub struct ColorAdapter<'a, C, D> {
38 inner: &'a mut D,
39 _phantom: PhantomData<C>,
40}
41
42impl<C, D> Dimensions for ColorAdapter<'_, C, D>
43where
44 C: PixelColor,
45 D: DrawTarget<Color = C>,
46{
47 fn bounding_box(&self) -> Rectangle {
48 self.inner.bounding_box()
49 }
50}
51
52impl<C, D> DrawTarget for ColorAdapter<'_, C, D>
53where
54 C: PixelColor + From<Rgb565>,
55 D: DrawTarget<Color = C>,
56{
57 type Color = Rgb565;
58 type Error = D::Error;
59
60 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
61 where
62 I: IntoIterator<Item = Pixel<Rgb565>>,
63 {
64 self.inner.draw_iter(
65 pixels
66 .into_iter()
67 .map(|Pixel(pos, color)| Pixel(pos, C::from(color))),
68 )
69 }
70}
71
72pub fn draw_to<C, D>(primitive: DrawPrimitive, fb: &mut D)
88where
89 C: PixelColor + From<Rgb565>,
90 D: DrawTarget<Color = C>,
91 D::Error: Debug,
92{
93 let mut adapter = ColorAdapter {
94 inner: fb,
95 _phantom: PhantomData,
96 };
97 draw(primitive, &mut adapter);
98}
99
100#[inline]
104pub fn nalgebra_to_eg(p: nalgebra::Point2<i32>) -> Point {
105 Point::new(p.x, p.y)
106}
107
108#[inline]
110pub fn eg_to_nalgebra(p: Point) -> nalgebra::Point2<i32> {
111 nalgebra::Point2::new(p.x, p.y)
112}
113
114pub trait AsEgPoint {
116 fn as_eg_point(&self) -> Point;
118}
119
120impl AsEgPoint for nalgebra::Point2<i32> {
121 #[inline]
122 fn as_eg_point(&self) -> Point {
123 Point::new(self.x, self.y)
124 }
125}
126
127pub trait AsNalgebraPoint {
129 fn as_nalgebra(&self) -> nalgebra::Point2<i32>;
131}
132
133impl AsNalgebraPoint for Point {
134 #[inline]
135 fn as_nalgebra(&self) -> nalgebra::Point2<i32> {
136 nalgebra::Point2::new(self.x, self.y)
137 }
138}
139
140struct SliceBackend<'a>(pub &'a mut [Rgb565]);
143
144impl FrameBufferBackend for SliceBackend<'_> {
145 type Color = Rgb565;
146 fn set(&mut self, index: usize, color: Rgb565) {
147 self.0[index] = color;
148 }
149 fn get(&self, index: usize) -> Rgb565 {
150 self.0[index]
151 }
152 fn nr_elements(&self) -> usize {
153 self.0.len()
154 }
155}
156
157pub fn render_drawable_to_buffer<D>(
181 drawable: &D,
182 buffer: &mut [Rgb565],
183 width: usize,
184 height: usize,
185) -> Result<(), ()>
186where
187 D: Drawable<Color = Rgb565>,
188{
189 if buffer.len() != width * height {
190 return Err(());
191 }
192 let mut fb = FrameBuf::new(SliceBackend(buffer), width, height);
193 drawable.draw(&mut fb).map(|_| ()).map_err(|_| ())
194}
195
196#[cfg(test)]
197mod tests {
198 extern crate std;
199
200 use super::*;
201 use embedded_graphics_core::pixelcolor::RgbColor;
202 use embedded_graphics_framebuf::backends::{EndianCorrectedBuffer, EndianCorrection};
203 use nalgebra::Point2;
204
205 struct SingleRedPixel;
206
207 impl Drawable for SingleRedPixel {
208 type Color = Rgb565;
209 type Output = ();
210
211 fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
212 where
213 D: DrawTarget<Color = Self::Color>,
214 {
215 target.draw_iter([Pixel(Point::new(0, 0), Rgb565::RED)])
216 }
217 }
218
219 #[test]
220 fn point_conversion_helpers_roundtrip() {
221 let n = Point2::new(12, -9);
222 let eg = nalgebra_to_eg(n);
223 assert_eq!(eg, Point::new(12, -9));
224 let n2 = eg_to_nalgebra(eg);
225 assert_eq!(n2, n);
226 assert_eq!(n.as_eg_point(), eg);
227 assert_eq!(eg.as_nalgebra(), n);
228 }
229
230 #[test]
231 fn render_drawable_to_buffer_validates_buffer_size() {
232 let mut too_short = [Rgb565::BLACK; 3];
233 let err = render_drawable_to_buffer(&SingleRedPixel, &mut too_short, 2, 2);
234 assert!(err.is_err());
235 }
236
237 #[test]
238 fn render_drawable_to_buffer_draws_into_slice() {
239 let mut data = [Rgb565::BLACK; 4];
240 render_drawable_to_buffer(&SingleRedPixel, &mut data, 2, 2).unwrap();
241 assert_eq!(data[0], Rgb565::RED);
242 }
243
244 #[test]
245 fn draw_to_writes_primitive_to_target() {
246 let backing = std::vec![Rgb565::BLACK; 4].leak();
247 let mut fb = FrameBuf::new(
248 EndianCorrectedBuffer::new(backing, EndianCorrection::ToLittleEndian),
249 2,
250 2,
251 );
252 draw_to::<Rgb565, _>(
253 DrawPrimitive::ColoredPoint(Point2::new(1, 1), Rgb565::new(31, 0, 0)),
254 &mut fb,
255 );
256 assert_eq!(fb.get_color_at(Point::new(1, 1)), Rgb565::new(31, 0, 0));
257 }
258}