Skip to content

Commit b342da6

Browse files
book: Add a graphics example (#586)
This example draws a fractal to the screen using the grahpics output protocol.
1 parent 4b2c67f commit b342da6

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [Running in a VM](tutorial/vm.md)
88
- [How-to](how_to/introduction.md)
99
- [Using Protocols](how_to/protocols.md)
10+
- [Drawing to the Screen](how_to/drawing.md)
1011
- [Crate Features](how_to/crate_features.md)
1112
- [Concepts](concepts/introduction.md)
1213
- [Boot Stages](concepts/boot_stages.md)

book/src/how_to/drawing.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Drawing to the Screen
2+
3+
This example shows how to draw to the screen using the [graphics output protocol].
4+
The code will a [Sierpiński triangle] using the "chaos game" method.
5+
6+
![screenshot](https://i.imgur.com/0tpjtV6.png)
7+
8+
The core abstraction used here is a linear buffer:
9+
```rust
10+
{{#include ../../../uefi-test-runner/examples/sierpinski.rs:buffer}}
11+
```
12+
13+
This `Buffer` type stores a `Vec` of [`BltPixel`]s, which are BGRX
14+
32-bit pixels (8 bites each for blue, green, and red, followed by 8
15+
unused bits of padding). We use the `pixel` method to alter a single
16+
pixel at a time. This is often not an efficient method; for more complex
17+
graphics you could use a crate like [`embedded-graphics`].
18+
19+
The `Buffer::blit` method calls the graphics output protocol's `blt`
20+
method to copy the buffer to the screen.
21+
22+
Most of the rest of the code is just implementing the algorithm for
23+
drawing the fractal. Here's the full example:
24+
25+
```rust
26+
{{#include ../../../uefi-test-runner/examples/sierpinski.rs:all}}
27+
```
28+
29+
You can run this example from the [uefi-rs] repository with:
30+
```console
31+
cargo xtask run --example sierpinski
32+
```
33+
34+
[Sierpiński triangle]: https://en.wikipedia.org/wiki/Sierpiński_triangle#Chaos_game
35+
[`BltPixel`]: https://docs.rs/uefi/latest/uefi/proto/console/gop/struct.BltPixel.html
36+
[`embedded-graphics`]: https://crates.io/crates/embedded-graphics
37+
[graphics output protocol]: https://docs.rs/uefi/latest/uefi/proto/console/gop/index.html
38+
[uefi-rs]: https://github.com/rust-osdev/uefi-rs
+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// ANCHOR: all
2+
#![no_main]
3+
#![no_std]
4+
#![feature(abi_efiapi)]
5+
6+
extern crate alloc;
7+
8+
use alloc::vec;
9+
use alloc::vec::Vec;
10+
use core::mem;
11+
use uefi::prelude::*;
12+
use uefi::proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput};
13+
use uefi::proto::rng::Rng;
14+
use uefi::table::boot::BootServices;
15+
use uefi::Result;
16+
17+
#[derive(Clone, Copy)]
18+
struct Point {
19+
x: f32,
20+
y: f32,
21+
}
22+
23+
impl Point {
24+
fn new(x: f32, y: f32) -> Self {
25+
Self { x, y }
26+
}
27+
}
28+
29+
// ANCHOR: buffer
30+
struct Buffer {
31+
width: usize,
32+
height: usize,
33+
pixels: Vec<BltPixel>,
34+
}
35+
36+
impl Buffer {
37+
/// Create a new `Buffer`.
38+
fn new(width: usize, height: usize) -> Self {
39+
Buffer {
40+
width,
41+
height,
42+
pixels: vec![BltPixel::new(0, 0, 0); width * height],
43+
}
44+
}
45+
46+
/// Get a single pixel.
47+
fn pixel(&mut self, x: usize, y: usize) -> Option<&mut BltPixel> {
48+
self.pixels.get_mut(y * self.width + x)
49+
}
50+
51+
/// Blit the buffer to the framebuffer.
52+
fn blit(&self, gop: &mut GraphicsOutput) -> Result {
53+
gop.blt(BltOp::BufferToVideo {
54+
buffer: &self.pixels,
55+
src: BltRegion::Full,
56+
dest: (0, 0),
57+
dims: (self.width, self.height),
58+
})
59+
}
60+
}
61+
// ANCHOR_END: buffer
62+
63+
/// Get a random `usize` value.
64+
fn get_random_usize(rng: &mut Rng) -> usize {
65+
let mut buf = [0; mem::size_of::<usize>()];
66+
rng.get_rng(None, &mut buf).expect("get_rng failed");
67+
usize::from_le_bytes(buf)
68+
}
69+
70+
fn draw_sierpinski(bt: &BootServices) -> Result {
71+
// Open graphics output protocol.
72+
let gop_handle = bt.get_handle_for_protocol::<GraphicsOutput>()?;
73+
let mut gop = bt.open_protocol_exclusive::<GraphicsOutput>(gop_handle)?;
74+
75+
// Open random number generator protocol.
76+
let rng_handle = bt.get_handle_for_protocol::<Rng>()?;
77+
let mut rng = bt.open_protocol_exclusive::<Rng>(rng_handle)?;
78+
79+
// Create a buffer to draw into.
80+
let (width, height) = gop.current_mode_info().resolution();
81+
let mut buffer = Buffer::new(width, height);
82+
83+
// Initialize the buffer with a simple gradient background.
84+
for y in 0..height {
85+
let r = ((y as f32) / ((height - 1) as f32)) * 255.0;
86+
for x in 0..width {
87+
let g = ((x as f32) / ((width - 1) as f32)) * 255.0;
88+
let pixel = buffer.pixel(x, y).unwrap();
89+
pixel.red = r as u8;
90+
pixel.green = g as u8;
91+
pixel.blue = 255;
92+
}
93+
}
94+
95+
let size = Point::new(width as f32, height as f32);
96+
97+
// Define the vertices of a big triangle.
98+
let border = 20.0;
99+
let triangle = [
100+
Point::new(size.x / 2.0, border),
101+
Point::new(border, size.y - border),
102+
Point::new(size.x - border, size.y - border),
103+
];
104+
105+
// `p` is the point to draw. Start at the center of the triangle.
106+
let mut p = Point::new(size.x / 2.0, size.y / 2.0);
107+
108+
// Loop forever, drawing the frame after each new point is changed.
109+
loop {
110+
// Choose one of the triangle's vertices at random.
111+
let v = triangle[get_random_usize(&mut rng) % 3];
112+
113+
// Move `p` halfway to the chosen vertex.
114+
p.x = (p.x + v.x) * 0.5;
115+
p.y = (p.y + v.y) * 0.5;
116+
117+
// Set `p` to black.
118+
let pixel = buffer.pixel(p.x as usize, p.y as usize).unwrap();
119+
pixel.red = 0;
120+
pixel.green = 100;
121+
pixel.blue = 0;
122+
123+
// Draw the buffer to the screen.
124+
buffer.blit(&mut gop)?;
125+
}
126+
}
127+
128+
#[entry]
129+
fn main(_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
130+
uefi_services::init(&mut system_table).unwrap();
131+
let bt = system_table.boot_services();
132+
draw_sierpinski(bt).unwrap();
133+
Status::SUCCESS
134+
}
135+
// ANCHOR_END: all

0 commit comments

Comments
 (0)