Skip to main content

st7305_lcd/framebuffer/
rotation.rs

1/// Orientation of the logical (drawing) coordinate system relative to the
2/// native one, i.e. to the `W` x `H` layout of the GRAM described by a
3/// [`PixelMap`](super::pixel_map::PixelMap).
4///
5/// The picture is turned **clockwise** by the given angle, as in most display
6/// drivers: with `Deg90` the logical top-left corner of the picture lands in
7/// the native top-right corner, the logical size is `H` x `W` and a logical
8/// `(x, y)` is the native `(W - 1 - y, x)`. To read it you turn the device
9/// counter-clockwise by the same angle.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12pub enum Rotation {
13    /// Logical = native, size `W` x `H`
14    #[default]
15    Deg0,
16    /// Size `H` x `W`, native `(W - 1 - y, x)`
17    Deg90,
18    /// Size `W` x `H`, native `(W - 1 - x, H - 1 - y)`
19    Deg180,
20    /// Size `H` x `W`, native `(y, H - 1 - x)`
21    Deg270,
22}
23
24impl Rotation {
25    /// Logical size for a native `w` x `h` layout.
26    pub const fn size(self, w: u32, h: u32) -> (u32, u32) {
27        match self {
28            Rotation::Deg0 | Rotation::Deg180 => (w, h),
29            Rotation::Deg90 | Rotation::Deg270 => (h, w),
30        }
31    }
32
33    /// Converts a logical `(x, y)` into the native `(xn, yn)` of a `w` x `h`
34    /// layout. `None` if `(x, y)` is outside the logical size.
35    pub const fn to_native(self, w: u32, h: u32, x: u32, y: u32) -> Option<(u32, u32)> {
36        let (lw, lh) = self.size(w, h);
37        if x >= lw || y >= lh {
38            return None;
39        }
40        Some(match self {
41            Rotation::Deg0 => (x, y),
42            Rotation::Deg90 => (w - 1 - y, x),
43            Rotation::Deg180 => (w - 1 - x, h - 1 - y),
44            Rotation::Deg270 => (y, h - 1 - x),
45        })
46    }
47}
48
49const _: () = assert!(matches!(Rotation::Deg0.size(400, 300), (400, 300)));
50const _: () = assert!(matches!(Rotation::Deg90.size(400, 300), (300, 400)));
51const _: () = assert!(matches!(Rotation::Deg180.size(400, 300), (400, 300)));
52const _: () = assert!(matches!(Rotation::Deg270.size(400, 300), (300, 400)));
53
54// The logical origin lands in a different native corner for every orientation.
55const _: () = assert!(matches!(
56    Rotation::Deg0.to_native(400, 300, 0, 0),
57    Some((0, 0))
58));
59const _: () = assert!(matches!(
60    Rotation::Deg90.to_native(400, 300, 0, 0),
61    Some((399, 0))
62));
63const _: () = assert!(matches!(
64    Rotation::Deg180.to_native(400, 300, 0, 0),
65    Some((399, 299))
66));
67const _: () = assert!(matches!(
68    Rotation::Deg270.to_native(400, 300, 0, 0),
69    Some((0, 299))
70));
71const _: () = assert!(Rotation::Deg90.to_native(400, 300, 300, 0).is_none());
72const _: () = assert!(Rotation::Deg90.to_native(400, 300, 0, 400).is_none());