Skip to main content

plex_boot/core/
display.rs

1//! Module implementing embedded_graphics_core::DrawTarget
2//! for the Rust UEFI crate. It is actually just a buffer,
3//! and the push to uefi is done via blit during flush().
4use alloc::vec;
5use alloc::vec::Vec;
6use embedded_graphics::{pixelcolor::Rgb888, prelude::*};
7use uefi::proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput};
8
9/// An `embedded-graphics` draw target using the UEFI Graphics Output Protocol (Gop).
10///
11/// This maintains an internal buffer where drawing operations happen. To display
12/// the drawn image, you must call `flush()` which blits the entire buffer to the screen.
13pub struct GopDisplay<'a> {
14    width: usize,
15    height: usize,
16    buffer: Vec<BltPixel>,
17    gop: &'a mut GraphicsOutput,
18}
19
20impl<'a> GopDisplay<'a> {
21    /// Create a new `GopDisplay` matching the current GOP mode resolution.
22    pub fn new(gop: &'a mut GraphicsOutput) -> Self {
23        let (width, height) = gop.current_mode_info().resolution();
24        Self {
25            width,
26            height,
27            buffer: vec![BltPixel::new(0, 0, 0); width * height],
28            gop,
29        }
30    }
31
32    /// Blit the entire buffer to the framebuffer.
33    pub fn flush(&mut self) -> Result<(), uefi::Error> {
34        self.gop.blt(BltOp::BufferToVideo {
35            buffer: &self.buffer,
36            src: BltRegion::Full,
37            dest: (0, 0),
38            dims: (self.width, self.height),
39        })
40    }
41
42    /// Clear the buffer to a specific color.
43    pub fn clear(&mut self, color: Rgb888) {
44        let pixel = BltPixel::new(color.r(), color.g(), color.b());
45        self.buffer.fill(pixel);
46    }
47}
48
49impl<'a> DrawTarget for GopDisplay<'a> {
50    type Color = Rgb888;
51    type Error = core::convert::Infallible;
52
53    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
54    where
55        I: IntoIterator<Item = Pixel<Self::Color>>,
56    {
57        for Pixel(coord, color) in pixels.into_iter() {
58            // Bounds check - discard out of bounds pixels per DrawTarget requirements
59            let (x, y) = match (coord.x, coord.y) {
60                (x, y) if x >= 0 && y >= 0 => (x as usize, y as usize),
61                _ => continue,
62            };
63
64            if x < self.width && y < self.height {
65                let index = y * self.width + x;
66                let pixel = &mut self.buffer[index];
67                pixel.red = color.r();
68                pixel.green = color.g();
69                pixel.blue = color.b();
70            }
71        }
72        Ok(())
73    }
74}
75
76impl<'a> OriginDimensions for GopDisplay<'a> {
77    fn size(&self) -> Size {
78        Size::new(self.width as u32, self.height as u32)
79    }
80}