vexide_embedded_graphics/
lib.rs

1//! [`embedded-graphics`] Driver for the VEX V5
2//! 
3//! [`embedded-graphics`]: https://crates.io/crates/embedded-graphics
4//! 
5//! This crate provides a [`DrawTarget`] implementation for the VEX V5 brain display,
6//! allowing you to draw to the display using the `embedded-graphics` ecosystem.
7//! 
8//! # Usage
9//! 
10//! To begin, turn your `display` peripheral into a [`DisplayDriver`]:
11//! 
12//! ```
13//! #![no_std]
14//! #![no_main]
15//! 
16//! use vexide::prelude::*;
17//! use vexide_embedded_graphics::DisplayDriver;
18//! 
19//! #[vexide::main]
20//! async fn main(peripherals: Peripherals) {
21//!     let mut display = DisplayDriver::new(peripherals.display);
22//! }
23//! ```
24//! 
25//! [`DisplayDriver`] is a [`DrawTarget`] that the `embedded-graphics` crate is
26//! able to draw to.
27//! 
28//! ```
29//! #![no_std]
30//! #![no_main]
31//! 
32//! use vexide::prelude::*;
33//! use vexide_embedded_graphics::DisplayDriver;
34//! 
35//! use embedded_graphics::{
36//!     mono_font::{ascii::FONT_6X10, MonoTextStyle},
37//!     pixelcolor::Rgb888,
38//!     prelude::*,
39//!     text::Text,
40//! };
41//! 
42//! #[vexide::main]
43//! async fn main(peripherals: Peripherals) {
44//!     let mut display = DisplayDriver::new(peripherals.display);
45//!     let style = MonoTextStyle::new(&FONT_6X10, Rgb888::GREEN);
46//!     
47//!     Text::new("Hello,\nRust!", Point::new(2, 28), style)
48//!         .draw(&mut display)
49//!         .unwrap();
50//! }
51//! ```
52//! 
53//! Check out the [`embedded-graphics` docs] for more examples.
54//! 
55//! [`embedded-graphics` docs]: https://docs.rs/embedded-graphics/latest/embedded_graphics/examples/index.html
56
57#![no_std]
58
59use core::convert::Infallible;
60use embedded_graphics_core::{pixelcolor::Rgb888, prelude::*};
61use vexide::devices::{
62    display::{Display, TouchEvent},
63    rgb::Rgb,
64};
65
66fn rgb_into_raw(rgb: Rgb<u8>) -> u32 {
67    (u32::from(rgb.r) << 16) + (u32::from(rgb.g) << 8) + u32::from(rgb.b)
68}
69
70/// An embedded-graphics draw target for the V5 Brain display
71/// Currently, this does not support touch detection like the regular [`Display`] API.
72pub struct DisplayDriver {
73    display: Display,
74    triple_buffer:
75        [u32; Display::HORIZONTAL_RESOLUTION as usize * Display::VERTICAL_RESOLUTION as usize],
76}
77
78impl DisplayDriver {
79    /// Create a new [`DisplayDriver`] from a [`Display`].
80    /// 
81    /// The display peripheral must be moved into this struct,
82    /// as it is used to render the display and having multiple
83    /// mutable references to it is unsafe.
84    #[must_use]
85    pub fn new(mut display: Display) -> Self {
86        display.set_render_mode(vexide::devices::display::RenderMode::DoubleBuffered);
87        Self {
88            display,
89            #[allow(clippy::large_stack_arrays)] // we got plenty
90            triple_buffer: [0; Display::HORIZONTAL_RESOLUTION as usize
91                * Display::VERTICAL_RESOLUTION as usize],
92        }
93    }
94
95    /// Returns the current touch status of the display.
96    #[must_use]
97    pub fn touch_status(&self) -> TouchEvent {
98        self.display.touch_status()
99    }
100}
101
102impl OriginDimensions for DisplayDriver {
103    fn size(&self) -> Size {
104        Size {
105            width: Display::HORIZONTAL_RESOLUTION as _,
106            height: Display::VERTICAL_RESOLUTION as _,
107        }
108    }
109}
110
111impl DrawTarget for DisplayDriver {
112    type Color = Rgb888;
113
114    type Error = Infallible;
115
116    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
117    where
118        I: IntoIterator<Item = Pixel<Self::Color>>,
119    {
120        pixels
121            .into_iter()
122            .map(|p| (p.0, rgb_into_raw(Rgb::new(p.1.r(), p.1.g(), p.1.b()))))
123            .for_each(|(pos, col)| {
124                self.triple_buffer
125                    [pos.y as usize * Display::HORIZONTAL_RESOLUTION as usize + pos.x as usize] =
126                    col;
127            });
128
129        unsafe {
130            vex_sdk::vexDisplayCopyRect(
131                0,
132                0x20,
133                Display::HORIZONTAL_RESOLUTION.into(),
134                Display::VERTICAL_RESOLUTION.into(),
135                self.triple_buffer.as_mut_ptr(),
136                Display::HORIZONTAL_RESOLUTION.into(),
137            );
138        };
139        self.display.render();
140
141        Ok(())
142    }
143}