xpanse_api/interfaces/
video.rs1use alloc::sync::Arc;
9use core::{
10 cell::RefCell,
11 marker::PhantomData,
12 sync::atomic::{AtomicU32, Ordering},
13};
14
15use embassy_sync::blocking_mutex::{CriticalSectionMutex, ThreadModeMutex};
16
17pub use slint::platform::software_renderer::Rgb565Pixel;
18
19static NEXT_FRAME_ID: AtomicU32 = AtomicU32::new(1);
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
23pub enum FrameBufferError {
24 InvalidDimensions,
26}
27
28struct FrameStorage {
29 pixels: ThreadModeMutex<RefCell<&'static mut [Rgb565Pixel]>>,
30 len: usize,
31 width: u16,
32 height: u16,
33}
34
35struct ActiveFrame {
36 id: u32,
37 width: u16,
38 height: u16,
39 revision: AtomicU32,
40}
41
42struct FrameLink {
43 storage: FrameStorage,
44 active: CriticalSectionMutex<RefCell<Option<Arc<ActiveFrame>>>>,
45}
46
47pub struct Rgb565FrameBuffer {
53 link: Arc<FrameLink>,
54}
55
56pub struct Rgb565FrameDisplay {
62 link: Arc<FrameLink>,
63}
64
65pub fn rgb565_frame_buffer(
91 pixels: &'static mut [Rgb565Pixel],
92 width: u16,
93 height: u16,
94) -> Result<(Rgb565FrameBuffer, Rgb565FrameDisplay), FrameBufferError> {
95 let expected_len = usize::from(width)
96 .checked_mul(usize::from(height))
97 .filter(|len| *len > 0)
98 .ok_or(FrameBufferError::InvalidDimensions)?;
99 if pixels.len() != expected_len {
100 return Err(FrameBufferError::InvalidDimensions);
101 }
102
103 let len = pixels.len();
104 let link = Arc::new(FrameLink {
105 storage: FrameStorage {
106 pixels: ThreadModeMutex::new(RefCell::new(pixels)),
107 len,
108 width,
109 height,
110 },
111 active: CriticalSectionMutex::new(RefCell::new(None)),
112 });
113 Ok((
114 Rgb565FrameBuffer {
115 link: Arc::clone(&link),
116 },
117 Rgb565FrameDisplay { link },
118 ))
119}
120
121impl Rgb565FrameBuffer {
122 pub fn start(
132 &mut self,
133 width: u16,
134 height: u16,
135 ) -> Result<Rgb565FrameSession<'_>, FrameBufferError> {
136 let len = usize::from(width)
137 .checked_mul(usize::from(height))
138 .filter(|len| *len > 0)
139 .ok_or(FrameBufferError::InvalidDimensions)?;
140 if width > self.link.storage.width
141 || height > self.link.storage.height
142 || len > self.link.storage.len
143 {
144 return Err(FrameBufferError::InvalidDimensions);
145 }
146
147 let frame = Arc::new(ActiveFrame {
148 id: NEXT_FRAME_ID.fetch_add(1, Ordering::Relaxed),
149 width,
150 height,
151 revision: AtomicU32::new(0),
152 });
153 self.link
154 .storage
155 .pixels
156 .lock(|pixels| pixels.borrow_mut()[..len].fill(Rgb565Pixel(0)));
157 self.link
158 .active
159 .lock(|active| *active.borrow_mut() = Some(Arc::clone(&frame)));
160
161 Ok(Rgb565FrameSession {
162 link: Arc::clone(&self.link),
163 frame,
164 resource: PhantomData,
165 })
166 }
167}
168
169pub struct Rgb565FrameSession<'a> {
174 link: Arc<FrameLink>,
175 frame: Arc<ActiveFrame>,
176 resource: PhantomData<&'a mut Rgb565FrameBuffer>,
177}
178
179impl Rgb565FrameSession<'_> {
180 pub fn set_pixel(&self, x: u16, y: u16, color: Rgb565Pixel) {
184 if x >= self.frame.width || y >= self.frame.height {
185 return;
186 }
187 let index = usize::from(y) * usize::from(self.frame.width) + usize::from(x);
188 self.link
189 .storage
190 .pixels
191 .lock(|pixels| pixels.borrow_mut()[index] = color);
192 }
193
194 pub fn present(&self) {
199 self.frame.revision.fetch_add(1, Ordering::Release);
200 }
201}
202
203impl Drop for Rgb565FrameSession<'_> {
204 fn drop(&mut self) {
205 self.link.active.lock(|active| {
206 let mut active = active.borrow_mut();
207 if active
208 .as_ref()
209 .is_some_and(|frame| Arc::ptr_eq(frame, &self.frame))
210 {
211 *active = None;
212 }
213 });
214 }
215}
216
217pub struct PresentedFrame {
222 link: Arc<FrameLink>,
223 frame: Arc<ActiveFrame>,
224}
225
226impl PresentedFrame {
227 pub fn width(&self) -> u16 {
229 self.frame.width
230 }
231
232 pub fn height(&self) -> u16 {
234 self.frame.height
235 }
236
237 pub fn len(&self) -> usize {
239 usize::from(self.frame.width) * usize::from(self.frame.height)
240 }
241
242 pub fn is_empty(&self) -> bool {
244 self.len() == 0
245 }
246
247 pub fn with_pixels<R>(&self, f: impl FnOnce(&[Rgb565Pixel]) -> R) -> R {
249 let len = self.len();
250 self.link
251 .storage
252 .pixels
253 .lock(|pixels| f(&pixels.borrow()[..len]))
254 }
255
256 pub fn token(&self) -> u64 {
262 let revision = self.frame.revision.load(Ordering::Acquire);
263 (u64::from(self.frame.id) << 32) | u64::from(revision)
264 }
265}
266
267impl Rgb565FrameDisplay {
268 pub fn active_frame(&self) -> Option<PresentedFrame> {
272 self.link.active.lock(|active| {
273 active
274 .borrow()
275 .as_ref()
276 .cloned()
277 .map(|frame| PresentedFrame {
278 link: Arc::clone(&self.link),
279 frame,
280 })
281 })
282 }
283
284 pub fn with_buffer_mut<R>(&self, f: impl FnOnce(&mut [Rgb565Pixel]) -> R) -> Option<R> {
288 if self.link.active.lock(|active| active.borrow().is_some()) {
289 return None;
290 }
291 self.link.storage.pixels.lock(|pixels| {
292 let mut pixels = pixels.borrow_mut();
293 Some(f(&mut pixels))
294 })
295 }
296}