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}