uefi_graphics2/
lib.rs

1#![no_std]
2
3extern crate alloc;
4use alloc::vec::Vec;
5use embedded_graphics::pixelcolor::RgbColor;
6use embedded_graphics::{
7    draw_target::DrawTarget,
8    geometry::{OriginDimensions, Size},
9    pixelcolor::{IntoStorage, Rgb888},
10    prelude::Point,
11    primitives::Rectangle,
12    Pixel,
13};
14use uefi::proto::console::gop::{FrameBuffer, ModeInfo};
15
16pub use crate::error::UefiDisplayError;
17pub mod error;
18
19/// A double‑buffered UEFI display that implements `embedded_graphics::DrawTarget<Rgb888>`.
20#[derive(Debug)]
21pub struct UefiDisplay {
22    /// Raw UEFI frame buffer pointer
23    fb_ptr: *mut u8,
24    /// In‑memory double buffer (fully owned)
25    buffer: Vec<u8>,
26    stride: u32,
27    size: (u32, u32),
28}
29
30impl UefiDisplay {
31    /// Create a new, cleared-to-black `UefiDisplay`.
32    pub fn new(
33        mut frame_buffer: FrameBuffer,
34        mode_info: ModeInfo,
35    ) -> Result<Self, UefiDisplayError> {
36        let (width, height) = (
37            mode_info.resolution().0 as u32,
38            mode_info.resolution().1 as u32,
39        );
40        let stride = mode_info.stride() as u32;
41        let buf_len = width
42            .checked_mul(height)
43            .and_then(|p| p.checked_mul(4))
44            .ok_or(UefiDisplayError::InvalidResolution)?;
45
46        // Allocate zeroed buffer to avoid UB
47        let mut buffer = Vec::new();
48        buffer.resize(buf_len as usize, 0);
49
50        let mut display = UefiDisplay {
51            fb_ptr: frame_buffer.as_mut_ptr(),
52            buffer,
53            stride,
54            size: (width, height),
55        };
56
57        // Fill to black
58        display.fill_solid(
59            &Rectangle::new(Point::zero(), Size::new(width, height)),
60            Rgb888::BLACK,
61        )?;
62        display.flush();
63
64        Ok(display)
65    }
66
67    /// Copies the in‑memory buffer out to the UEFI frame buffer.
68    pub fn flush(&self) {
69        // SAFETY: We know both buffers are at least buffer.len() bytes long.
70        unsafe {
71            core::ptr::copy_nonoverlapping(self.buffer.as_ptr(), self.fb_ptr, self.buffer.len());
72        }
73    }
74}
75
76impl OriginDimensions for UefiDisplay {
77    fn size(&self) -> Size {
78        Size::new(self.size.0, self.size.1)
79    }
80}
81
82impl DrawTarget for UefiDisplay {
83    type Color = Rgb888;
84    type Error = UefiDisplayError;
85
86    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
87    where
88        I: IntoIterator<Item = Pixel<Self::Color>>,
89    {
90        let (width, _height) = self.size;
91        let stride = self.stride as usize;
92        let buf = &mut self.buffer;
93
94        for Pixel(Point { x, y }, color) in pixels.into_iter() {
95            // bounds‑check
96            if x < 0 || y < 0 {
97                continue;
98            }
99            let (x, y): (usize, usize) = (x as usize, y as usize);
100            if x >= width as usize {
101                continue;
102            }
103
104            // compute byte index safely
105            let idx = y
106                .checked_mul(stride)
107                .and_then(|row| row.checked_add(x))
108                .and_then(|pix| pix.checked_mul(4))
109                .ok_or(UefiDisplayError::OutOfBounds)?;
110
111            let pixel_val: u32 = color.into_storage();
112            let pixel_bytes = pixel_val.to_le_bytes();
113            buf[idx..idx + 4].copy_from_slice(&pixel_bytes);
114        }
115        Ok(())
116    }
117}