embedded_graphics_transform/
lib.rs

1//! Add simple coordinate transforms for embedded graphics displays
2//!
3//! This crate adds [`DrawTarget`] implementations which apply various simple
4//! transformations to coordinates as they're being written. This allows
5//! graphics output to be rotated or mirrored to display correctly on a specific
6//! display device.
7//!
8//! Specifically, it implements:
9//! - rotation by 90/180/270 degrees (and 0, for consistency)
10//! - mirroring
11//! - transposition
12//!
13//! Note that these transformations can be composed if needed.
14//!
15//! Because this is a completely generic implementation, it cannot take
16//! advantage of any hardware or driver specific specializations. In particular,
17//! [`DrawTarget::fill_contiguous`] must fall back to a generic implementation
18//! using [`draw_iter`](DrawTarget::draw_iter).
19//! ([`fill_solid`](DrawTarget::fill_solid) and [`clear`](DrawTarget::clear) can
20//! use specialized implementations, however.)
21//!
22//! All the transforms implement [`AsRef<D>`]/[`AsMut<D>`] to get access to the
23//! underlying display object so that its inherent functions can be called.
24#![no_std]
25
26use core::ops::{Deref, DerefMut};
27use embedded_graphics_core::{prelude::*, primitives::Rectangle};
28
29#[cfg(test)]
30mod tests;
31
32macro_rules! xform_type {
33    ($inner:ident , ) => { $inner };
34    ($inner:ident , $xform: ident $($rest:ident)*) => {
35        r#impl::$xform < xform_type!($inner, $($rest)*) >
36    };
37}
38
39macro_rules! xform_new {
40    ($inner:ident , ) => {
41        $inner
42    };
43    ($inner:ident , $xform:ident $($rest:ident)*) => {
44        r#impl::$xform::new(xform_new!($inner, $($rest)*))
45    };
46}
47
48macro_rules! impl_as_ref {
49    ($_asref:ident, $expr:expr, ) => { $expr };
50
51    ($asref:ident, $expr:expr, $xform:ident $($rest:ident)*) => {
52        impl_as_ref!($asref, r#impl::$xform::$asref($expr), $($rest)*)
53    };
54}
55
56macro_rules! impl_xform {
57    ($($(#[$attr:meta])* $name:ident : $($xforms:ident)* ; )*) => {
58        $(
59            $(#[$attr])*
60            pub struct $name<D> {
61                target: xform_type!(D, $($xforms)*)
62            }
63
64            impl<D> $name<D> {
65                /// Apply a transformation to display implementing [`DrawTarget`].
66                #[allow(clippy::redundant_field_names)]
67                pub fn new(target: D) -> Self {
68                    $name {
69                        target: xform_new!(target, $($xforms)*)
70                    }
71                }
72
73                /// Recover the inner display instance.
74                pub fn into_inner(self) -> D {
75                    impl_as_ref!(into_inner, self.target, $($xforms)*)
76                }
77            }
78
79            impl<D> Deref for $name<D> {
80                type Target = D;
81
82                fn deref(&self) -> &D {
83                    self.as_ref()
84                }
85            }
86
87            impl<D> DerefMut for $name<D> {
88                fn deref_mut(&mut self) -> &mut D {
89                    self.as_mut()
90                }
91            }
92
93            impl<D> AsRef<D> for $name<D> {
94                #[inline]
95                fn as_ref(&self) -> &D {
96                    impl_as_ref!(as_ref, &self.target, $($xforms)*)
97                }
98            }
99
100            impl<D> AsMut<D> for $name<D> {
101                #[inline]
102                fn as_mut(&mut self) -> &mut D {
103                    impl_as_ref!(as_mut, &mut self.target, $($xforms)*)
104                }
105            }
106
107            impl<D: Dimensions> Dimensions for $name<D> {
108                #[inline]
109                fn bounding_box(&self) -> Rectangle {
110                    self.target.bounding_box()
111                }
112            }
113
114            impl<D: DrawTarget> DrawTarget for $name<D> {
115                type Color = D::Color;
116                type Error = D::Error;
117
118                #[inline]
119                fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
120                where
121                    I: IntoIterator<Item = Pixel<Self::Color>>,
122                {
123                    self.target.draw_iter(pixels)
124                }
125
126                #[inline]
127                fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
128                where
129                    I: IntoIterator<Item = Self::Color>,
130                {
131                    self.target.fill_contiguous(area, colors)
132                }
133
134                #[inline]
135                fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
136                    self.target.fill_solid(area, color)
137                }
138
139                #[inline]
140                fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
141                    self.target.clear(color)
142                }
143            }
144        )*
145    }
146}
147
148// Define rotations in terms of transpose and flip. Note that transforms are
149// applied in order from last to first.
150impl_xform! {
151    /// No-op (identity) rotation for completeness.
152    Rotate0: ;
153    /// Rotate image 90 degrees to the right.
154    Rotate90: MirrorY TransposeXY;
155    /// Rotate image 90 degrees to the left.
156    Rotate270: TransposeXY MirrorY;
157    /// Rotate image 180 degrees.
158    Rotate180: MirrorX MirrorY;
159
160    /// Transpose X and Y coordinates.
161    Transpose: TransposeXY;
162    /// Mirror image around X axis.
163    FlipX: MirrorX;
164    /// Mirror image around Y axis.
165    FlipY: MirrorY;
166}
167
168/// Image rotation direction and amount.
169#[derive(Debug, Clone, Copy, Eq, PartialEq)]
170pub enum Rotation {
171    /// No-op (identity) rotation.
172    Rotate0,
173    /// Rotate 90 degrees to the right.
174    Rotate90,
175    /// Rotate 180 degrees.
176    Rotate180,
177    /// Rotate 90 degrees to the left.
178    Rotate270,
179}
180
181enum RotateInner<D> {
182    Rotate0(Rotate0<D>),
183    Rotate90(Rotate90<D>),
184    Rotate180(Rotate180<D>),
185    Rotate270(Rotate270<D>),
186}
187
188/// Rotate an image with runtime configuration.
189///
190/// Unlike the [`Rotate90`]/[`Rotate180`]/[`Rotate270`] types, this allows
191/// rotation to be defined as a runtime paramter. It is simply a wrapper over
192/// the other implementations, so it should be functionally identical. The only
193/// overhead is the cost of dispatching to the appropriate implementation on
194/// each call.
195pub struct Rotate<D> {
196    target: RotateInner<D>,
197}
198
199macro_rules! rotate_impl {
200    (& $rot:expr, $func:ident ( $($args:expr),* $(,)?)) => {
201        match &$rot.target {
202            RotateInner::Rotate0(inner) => inner.$func($($args),*),
203            RotateInner::Rotate90(inner) => inner.$func($($args),*),
204            RotateInner::Rotate180(inner) => inner.$func($($args),*),
205            RotateInner::Rotate270(inner) => inner.$func($($args),*),
206        }
207    };
208    (&mut $rot:expr, $func:ident ( $($args:expr),* $(,)?)) => {
209        match &mut $rot.target {
210            RotateInner::Rotate0(inner) => inner.$func($($args),*),
211            RotateInner::Rotate90(inner) => inner.$func($($args),*),
212            RotateInner::Rotate180(inner) => inner.$func($($args),*),
213            RotateInner::Rotate270(inner) => inner.$func($($args),*),
214        }
215    };
216    ($rot:expr, $func:ident ( $($args:expr),* $(,)?)) => {
217        match $rot.target {
218            RotateInner::Rotate0(inner) => inner.$func($($args),*),
219            RotateInner::Rotate90(inner) => inner.$func($($args),*),
220            RotateInner::Rotate180(inner) => inner.$func($($args),*),
221            RotateInner::Rotate270(inner) => inner.$func($($args),*),
222        }
223    };
224}
225
226impl<D> Rotate<D> {
227    /// Create a new rotation transformation using the given [`Rotation`].
228    pub fn new(rot: Rotation, target: D) -> Self {
229        let target = match rot {
230            Rotation::Rotate0 => RotateInner::Rotate0(Rotate0::new(target)),
231            Rotation::Rotate90 => RotateInner::Rotate90(Rotate90::new(target)),
232            Rotation::Rotate180 => RotateInner::Rotate180(Rotate180::new(target)),
233            Rotation::Rotate270 => RotateInner::Rotate270(Rotate270::new(target)),
234        };
235        Rotate { target }
236    }
237
238    /// Recover the inner display instance.
239    pub fn into_inner(self) -> D {
240        rotate_impl!(self, into_inner())
241    }
242}
243
244impl<D> Deref for Rotate<D> {
245    type Target = D;
246
247    fn deref(&self) -> &D {
248        self.as_ref()
249    }
250}
251
252impl<D> DerefMut for Rotate<D> {
253    fn deref_mut(&mut self) -> &mut D {
254        self.as_mut()
255    }
256}
257
258impl<D> AsRef<D> for Rotate<D> {
259    fn as_ref(&self) -> &D {
260        rotate_impl!(&self, as_ref())
261    }
262}
263
264impl<D> AsMut<D> for Rotate<D> {
265    fn as_mut(&mut self) -> &mut D {
266        rotate_impl!(&mut self, as_mut())
267    }
268}
269
270impl<D: Dimensions> Dimensions for Rotate<D> {
271    fn bounding_box(&self) -> Rectangle {
272        rotate_impl!(&self, bounding_box())
273    }
274}
275
276impl<D: DrawTarget> DrawTarget for Rotate<D> {
277    type Color = D::Color;
278    type Error = D::Error;
279
280    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
281    where
282        I: IntoIterator<Item = Pixel<Self::Color>>,
283    {
284        rotate_impl!(&mut self, draw_iter(pixels))
285    }
286
287    fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
288    where
289        I: IntoIterator<Item = Self::Color>,
290    {
291        rotate_impl!(&mut self, fill_contiguous(area, colors))
292    }
293
294    fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
295        rotate_impl!(&mut self, fill_solid(area, color))
296    }
297
298    fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
299        rotate_impl!(&mut self, clear(color))
300    }
301}
302
303mod r#impl {
304    use embedded_graphics_core::{prelude::*, primitives::Rectangle};
305
306    pub(crate) trait Transpose {
307        fn transpose(self) -> Self;
308    }
309
310    impl Transpose for Point {
311        #[inline]
312        fn transpose(self) -> Point {
313            Point {
314                x: self.y,
315                y: self.x,
316            }
317        }
318    }
319
320    impl Transpose for Size {
321        #[inline]
322        fn transpose(self) -> Size {
323            Size {
324                width: self.height,
325                height: self.width,
326            }
327        }
328    }
329
330    impl Transpose for Rectangle {
331        #[inline]
332        fn transpose(self) -> Rectangle {
333            Rectangle {
334                top_left: self.top_left.transpose(),
335                size: self.size.transpose(),
336            }
337        }
338    }
339
340    pub(crate) struct TransposeXY<D> {
341        target: D,
342    }
343
344    impl<D> TransposeXY<D> {
345        pub(crate) fn new(target: D) -> Self {
346            TransposeXY { target }
347        }
348
349        pub(crate) fn into_inner(self) -> D {
350            self.target
351        }
352    }
353
354    impl<D> AsRef<D> for TransposeXY<D> {
355        fn as_ref(&self) -> &D {
356            &self.target
357        }
358    }
359
360    impl<D> AsMut<D> for TransposeXY<D> {
361        fn as_mut(&mut self) -> &mut D {
362            &mut self.target
363        }
364    }
365
366    impl<D: Dimensions> Dimensions for TransposeXY<D> {
367        fn bounding_box(&self) -> Rectangle {
368            self.target.bounding_box().transpose()
369        }
370    }
371
372    impl<D: DrawTarget> DrawTarget for TransposeXY<D> {
373        type Color = D::Color;
374        type Error = D::Error;
375
376        fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
377        where
378            I: IntoIterator<Item = Pixel<Self::Color>>,
379        {
380            self.target.draw_iter(
381                pixels
382                    .into_iter()
383                    .map(|Pixel(loc, col)| Pixel(loc.transpose(), col)),
384            )
385        }
386
387        fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
388            let area = area.transpose();
389            self.target.fill_solid(&area, color)
390        }
391
392        #[inline]
393        fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
394            self.target.clear(color)
395        }
396    }
397
398    pub(crate) struct MirrorX<D> {
399        target: D,
400    }
401
402    impl<D> MirrorX<D> {
403        pub(crate) fn new(target: D) -> Self {
404            MirrorX { target }
405        }
406
407        pub(crate) fn into_inner(self) -> D {
408            self.target
409        }
410    }
411
412    impl<D> AsRef<D> for MirrorX<D> {
413        fn as_ref(&self) -> &D {
414            &self.target
415        }
416    }
417
418    impl<D> AsMut<D> for MirrorX<D> {
419        fn as_mut(&mut self) -> &mut D {
420            &mut self.target
421        }
422    }
423
424    impl<D: Dimensions> Dimensions for MirrorX<D> {
425        #[inline]
426        fn bounding_box(&self) -> Rectangle {
427            self.target.bounding_box()
428        }
429    }
430
431    impl<D: DrawTarget> DrawTarget for MirrorX<D> {
432        type Color = D::Color;
433        type Error = D::Error;
434
435        fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
436        where
437            I: IntoIterator<Item = Pixel<Self::Color>>,
438        {
439            let width = self.bounding_box().size.width as i32 - 1;
440
441            self.target.draw_iter(
442                pixels
443                    .into_iter()
444                    .map(|Pixel(Point { x, y }, col)| Pixel(Point { x: width - x, y }, col)),
445            )
446        }
447
448        fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
449            let width = self.bounding_box().size.width as i32 - 1;
450            let area = Rectangle {
451                top_left: Point {
452                    x: width - area.top_left.x - area.size.width as i32,
453                    y: area.top_left.y,
454                },
455                size: area.size,
456            };
457            self.target.fill_solid(&area, color)
458        }
459
460        #[inline]
461        fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
462            self.target.clear(color)
463        }
464    }
465
466    pub(crate) struct MirrorY<D> {
467        target: D,
468    }
469
470    impl<D> MirrorY<D> {
471        pub(crate) fn new(target: D) -> Self {
472            MirrorY { target }
473        }
474
475        pub(crate) fn into_inner(self) -> D {
476            self.target
477        }
478    }
479
480    impl<D> AsRef<D> for MirrorY<D> {
481        fn as_ref(&self) -> &D {
482            &self.target
483        }
484    }
485
486    impl<D> AsMut<D> for MirrorY<D> {
487        fn as_mut(&mut self) -> &mut D {
488            &mut self.target
489        }
490    }
491
492    impl<D: Dimensions> Dimensions for MirrorY<D> {
493        #[inline]
494        fn bounding_box(&self) -> Rectangle {
495            self.target.bounding_box()
496        }
497    }
498
499    impl<D: DrawTarget> DrawTarget for MirrorY<D> {
500        type Color = D::Color;
501        type Error = D::Error;
502
503        fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
504        where
505            I: IntoIterator<Item = Pixel<Self::Color>>,
506        {
507            let height = self.bounding_box().size.height as i32 - 1;
508
509            self.target.draw_iter(
510                pixels
511                    .into_iter()
512                    .map(|Pixel(Point { x, y }, col)| Pixel(Point { x, y: height - y }, col)),
513            )
514        }
515
516        fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
517            let height = self.bounding_box().size.height as i32 - 1;
518            let area = Rectangle {
519                top_left: Point {
520                    x: area.top_left.x,
521                    y: height - area.top_left.y - area.size.height as i32,
522                },
523                size: area.size,
524            };
525            self.target.fill_solid(&area, color)
526        }
527
528        #[inline]
529        fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
530            self.target.clear(color)
531        }
532    }
533}