capsules_extra/screen/
screen_adapters.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! Tools for adapting to different screen formats.
6
7use core::cell::Cell;
8
9use kernel::hil::screen::{Screen, ScreenClient, ScreenPixelFormat, ScreenRotation};
10use kernel::utilities::cells::OptionalCell;
11use kernel::utilities::leasable_buffer::SubSliceMut;
12use kernel::ErrorCode;
13
14/// Convert an ARGB8888 formatted screen to a Mono8BitPage formatted screen of
15/// the same resolution.
16///
17/// All pixels are converted to mono (black and white)
18pub struct ScreenARGB8888ToMono8BitPage<'a, S: Screen<'a>> {
19    screen: &'a S,
20    draw_buffer: OptionalCell<SubSliceMut<'static, u8>>,
21    draw_width: Cell<usize>,
22    client_buffer: OptionalCell<SubSliceMut<'static, u8>>,
23    client: OptionalCell<&'a dyn ScreenClient>,
24}
25
26impl<'a, S: Screen<'a>> ScreenARGB8888ToMono8BitPage<'a, S> {
27    pub fn new(screen: &'a S, draw_buffer: &'static mut [u8]) -> Self {
28        assert!(draw_buffer.len() % 4 == 0);
29
30        ScreenARGB8888ToMono8BitPage {
31            screen,
32            draw_buffer: OptionalCell::new(SubSliceMut::new(draw_buffer)),
33            draw_width: Cell::new(0),
34            client_buffer: OptionalCell::empty(),
35            client: OptionalCell::empty(),
36        }
37    }
38}
39
40struct EightRowColumnPixelIter<'a> {
41    buf: &'a mut [u8],
42    width: usize,
43    row: usize,
44    col: usize,
45}
46
47impl<'a> EightRowColumnPixelIter<'a> {
48    pub fn new(buf: &'a mut [u8], width: usize) -> Self {
49        EightRowColumnPixelIter {
50            buf,
51            width,
52            row: 0,
53            col: 0,
54        }
55    }
56
57    // When trying to implement this as an iterator, we get lifetime issues:
58    //
59    // impl<'a> Iterator for EightRowColumnPixelIter<'a> {
60    //     type Item = &'a mut [u8];
61    //     ...
62    // }
63    fn next(&mut self) -> Option<&mut [u8]> {
64        let pixel_offset = self
65            .row
66            .checked_mul(self.width)
67            .and_then(|off| off.checked_add(self.col))
68            .and_then(|off| off.checked_mul(4))?;
69
70        self.row += 1;
71        if self.row % 8 == 0 {
72            self.row -= 8;
73            self.col += 1;
74        }
75        if self.col == self.width {
76            self.col = 0;
77            self.row += 8;
78        }
79
80        self.buf.get_mut(pixel_offset..(pixel_offset + 4))
81    }
82}
83
84impl<'a, S: Screen<'a>> Screen<'a> for ScreenARGB8888ToMono8BitPage<'a, S> {
85    fn set_client(&self, client: &'a dyn ScreenClient) {
86        self.client.replace(client);
87    }
88
89    fn get_resolution(&self) -> (usize, usize) {
90        self.screen.get_resolution()
91    }
92
93    fn get_pixel_format(&self) -> ScreenPixelFormat {
94        ScreenPixelFormat::Mono_8BitPage
95    }
96
97    fn get_rotation(&self) -> ScreenRotation {
98        self.screen.get_rotation()
99    }
100
101    fn set_write_frame(
102        &self,
103        x: usize,
104        y: usize,
105        width: usize,
106        height: usize,
107    ) -> Result<(), ErrorCode> {
108        // We can only write 8 full rows at a time:
109        if y % 8 != 0 || height % 8 != 0 {
110            return Err(ErrorCode::INVAL);
111        }
112
113        self.draw_width.set(width);
114        self.screen.set_write_frame(x, y, width, height)
115    }
116
117    fn write(
118        &self,
119        buffer: SubSliceMut<'static, u8>,
120        continue_write: bool,
121    ) -> Result<(), ErrorCode> {
122        fn into_bits(byte: u8) -> [bool; 8] {
123            let mut dst = [false; 8];
124            for (i, d) in dst.iter_mut().enumerate() {
125                *d = (byte & (1 << i)) != 0;
126            }
127            dst
128        }
129
130        let Some(mut draw_buffer) = self.draw_buffer.take() else {
131            return Err(ErrorCode::BUSY);
132        };
133
134        draw_buffer.reset();
135
136        // For each bit in the client buffer, we require 4 bytes in the draw
137        // buffer. So, for a full byte in the source, that's 32 bytes in the
138        // draw buffer:
139        let mut bytes_written = 0;
140        let mut dst_iter =
141            EightRowColumnPixelIter::new(draw_buffer.as_mut_slice(), self.draw_width.get());
142        for src_mono_8bit_page in buffer.as_slice().iter() {
143            // Now, write an "8 set" of rows:
144            for v in into_bits(*src_mono_8bit_page) {
145                dst_iter.next().unwrap().copy_from_slice(&[
146                    0x00,
147                    0xFF * (v as u8),
148                    0xFF * (v as u8),
149                    0xFF * (v as u8),
150                ]);
151                bytes_written += 4;
152            }
153        }
154        draw_buffer.slice(..bytes_written);
155
156        // Now, draw this buffer:
157        assert!(self.client_buffer.replace(buffer).is_none());
158        self.screen.write(draw_buffer, continue_write)
159    }
160
161    fn set_brightness(&self, brightness: u16) -> Result<(), ErrorCode> {
162        self.screen.set_brightness(brightness)
163    }
164
165    fn set_power(&self, enabled: bool) -> Result<(), ErrorCode> {
166        self.screen.set_power(enabled)
167    }
168
169    fn set_invert(&self, enabled: bool) -> Result<(), ErrorCode> {
170        self.screen.set_invert(enabled)
171    }
172}
173
174impl<'a, S: Screen<'a>> ScreenClient for ScreenARGB8888ToMono8BitPage<'a, S> {
175    fn command_complete(&self, result: Result<(), ErrorCode>) {
176        self.client.map(|c| c.command_complete(result));
177    }
178
179    fn write_complete(&self, buffer: SubSliceMut<'static, u8>, _result: Result<(), ErrorCode>) {
180        self.draw_buffer.replace(buffer);
181        self.client
182            .map(|c| c.write_complete(self.client_buffer.take().unwrap(), Ok(())));
183    }
184
185    fn screen_is_ready(&self) {
186        self.client.map(|c| c.screen_is_ready());
187    }
188}