Demilade Sonuga's blog
All postsDrawing Bitmaps I
In the previous post, we learned some things about bitmaps. Now, we're going to draw them. First, download the image we're going to draw from
The image we're drawing is a block:
Before we proceed, we must first ask ourselves: What are the steps that must be taken to get the block on the screen?
When we say you "downloaded that BMP image", what we really mean is that the bits representing the image have been transferred over the network from the GitHub servers to your computer. Those bits are in the BMP file format. This means that bits in specific locations have meaning according to the specification of the BMP file format which was described in the previous post.
So, drawing the bitmap on screen is just a matter of getting those bits into our code and putting the colors described by the pixel array into the screen's memory. Our steps to draw bitmaps on screen now looks like this:
- Get the block's bits into our code.
- Get the colors described by the pixel array on the screen.
To get the block's bits into our code, we first put the image in our directory:
blasterball/
| .cargo/
| | config.toml
| src/
| | font.rs
| | main.rs
| | uefi.rs
| | block.bmp // NEW
| .gitignore
| Cargo.lock
| Cargo.toml
Now, we modify our efi_main
:
#[no_mangle]
extern "efiapi" fn efi_main(
handle: *const core::ffi::c_void,
sys_table: *mut SystemTable,
) -> usize {
// ... Others
let framebuffer_base = mode.framebuffer_base;
let screen = framebuffer_base as *mut Screen;
let screen = unsafe { &mut *screen };
// DELETED: print_str(screen, "Hello World!");
// Throw block.bmp's bits into the output binary
// Retrieve a slice to those bits
let block_bytes = include_bytes!("./block.bmp");
0
}
The include_bytes!("./block.bmp")
above tells the compiler that it should look for the file block.bmp
in the current directory, grab the file's bits, and put it somewhere in the output binary at build time. If you
build now and compare the output binary before adding this line to after, you'll see that there is a significant
increase in the binary's size. This is because the BMP image is fully contained in the binary.
block_bytes
is a slice of bytes (&[u8]
) which refers to the block.bmp bits. So, block_bytes[0]
is the
first byte in the block.bmp file, and block_bytes[block_bytes.len() - 1]
is the last byte in the block.bmp
file.
Now, we're done with step 1. We have the block's bits in our code.
Onto step 2:
- Get the colors described by the pixel array on the screen.
From the specification of the BMP file format, we can determine where exactly the pixel array and the color table will be.
The color table, which defines the index to color mapping is at an offset of 124 bytes from the start of
the file. So, the first byte in block.bmp is block_bytes[124]
. The pixel array which contains the
pixel color descriptions of the picture in terms of color table indexes is immediately after the color
table. So, the first byte in the pixel array is block_bytes[124 + color_table_length]
. The pixel array
describes pixel colors in terms of rows and columns, so the length of one row will be the image width
and the length of one column will be the image height. We can get the image width and height at offsets
4 and 8 from the starting point of the file, respectively.
At this point, we have all we need to start drawing on the screen but doing it now will be messy and it will be easy to make a mistake.
Before we proceed to draw the actual bitmap on the screen, we first need to model the structure of a bitmap.
Take Away
include_bytes(filename)
puts the file with the name filename into the output binary.
Code till now:
Directory view:
blasterball/
| .cargo/
| | config.toml
| src/
| | block.bmp
| | font.rs
| | main.rs
| | uefi.rs
| .gitignore
| Cargo.lock
| Cargo.toml
main.rs
contents
#[no_mangle]
extern "efiapi" fn efi_main(
handle: *const core::ffi::c_void,
sys_table: *mut SystemTable,
) -> usize {
// ... Others
let framebuffer_base = mode.framebuffer_base;
let screen = framebuffer_base as *mut Screen;
let screen = unsafe { &mut *screen };
// DELETED: print_str(screen, "Hello World!");
// Throw block.bmp's bits into the output binary
// Retrieve a slice to those bits
let block_bytes = include_bytes!("./block.bmp");
0
}
In the Next Post
We'll get on to modeling the bitmap