Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Using Multiple Bytes to Represent Wider Pixel Widths

In the previous example, we kept it simple by using an 8-pixel wide image. This made things easy because each row fit perfectly into a single byte. However, real images often need more pixels. So how do we represent them when one byte isn’t enough? The answer is simple: we use multiple bytes. But this creates a problem. If we’re using multiple bytes, how does the system know where one row ends and the next one begins?

This is exactly why we need to tell the embedded graphics crate the exact width of our image. When we specify the width, the system knows how many bytes to use for each row. Once it knows the width and the image format, it can figure out the height automatically.

Understanding the Math

Let’s look at an example with an image that’s 31 pixels wide and 7 pixels tall. The width is 31 pixels, and each pixel takes up 1 bit of space. To figure out how many bytes we need for each row, we do some simple math. Since a byte holds 8 bits, we divide 31 by 8. This gives us 3 complete bytes, which covers 24 pixels. But we still have 7 pixels left over, so we need one more byte to hold them. In total, we need 4 bytes to represent each row of 31 pixels. If the image has 7 rows then the total data length is 4 times 7, which is 28 bytes.

How the System Calculates Height

The embedded graphics crate uses code like this internally to calculate the height. You don’t need to add this to your own code. I’m showing it here just so you can see how it works behind the scenes:

#![allow(unused)]
fn main() {
let height = data.len() / bytes_per_row(width, C::Raw::BITS_PER_PIXEL);
//...
//...
const fn bytes_per_row(width: u32, bits_per_pixel: usize) -> usize {
    (width as usize * bits_per_pixel + 7) / 8
}
}

In our example, the data array has 28 entries, each pixel uses 1 bit, and the image width is 31. When you run this calculation, you get 4 bytes per row and a height of 7 pixels.

Try It Yourself

You can run this code right here or in the Rust Playground to see how the calculation works:

// 31x7 pixel
#[rustfmt::skip]
const IMG_DATA: &[u8] = &[
    // 1st row
    0b00000001,0b11111111,0b11111111,0b00000000,
    // 2nd row
    0b00000001,0b11111111,0b11111111,0b00000000,
    //3rd row
    0b00000001,0b10000000,0b00000011,0b00000000,
    //4th row
    0b11111111,0b10000000,0b00000011,0b11111110,
    //5th row
    0b00000001,0b10000000,0b00000011,0b00000000,
    //6th row
    0b00000001,0b11111111,0b11111111,0b00000000,
    //7th row
    0b00000001,0b11111111,0b11111111,0b00000000,
];

const fn bytes_per_row(width: u32, bits_per_pixel: usize) -> usize {
    (width as usize * bits_per_pixel + 7) / 8
}

fn main(){
    const BITS_PER_PIXEL: usize = 1;
    let width = 31;
    let data = IMG_DATA;
    
    println!("Bytes Per Row:{}", bytes_per_row(width,BITS_PER_PIXEL));
    let height = data.len() / bytes_per_row(width, BITS_PER_PIXEL);
    println!("Height: {}", height);
}

You dont need to manually create these byte array, you can use an online tool like imag2bytes to generate the byte array for you.