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

Использование нескольких байтов для представления более широких строк пикселей

В предыдущем примере мы оставались простыми и использовали изображение шириной 8 пикселей. Это было удобно, потому что каждая строка идеально помещалась в один байт. Однако настоящим изображениям часто нужно больше пикселей. Как же их представить, если одного байта недостаточно? Ответ простой: используем несколько байтов. Но тогда возникает проблема. Если используется несколько байтов, как система поймёт, где заканчивается одна строка и начинается следующая?

Именно поэтому нужно точно сообщить крейту embedded-graphics ширину нашего изображения. Когда мы указываем ширину, система знает, сколько байтов использовать для каждой строки. Зная ширину и формат изображения, она может автоматически вычислить высоту.

Разбираем математику

Рассмотрим пример с изображением шириной 31 пиксель и высотой 7 пикселей. Ширина составляет 31 пиксель, и каждый пиксель занимает 1 бит. Чтобы узнать, сколько байтов нужно для каждой строки, выполним простой расчёт. Поскольку в байте 8 битов, делим 31 на 8. Получаем 3 полных байта, которые покрывают 24 пикселя. Но у нас остаётся ещё 7 пикселей, поэтому нужен ещё один байт, чтобы их разместить. Итого на каждую строку из 31 пикселя нужно 4 байта. Если изображение содержит 7 строк, то общая длина данных составит 4 × 7 = 28 байтов.

Многобайтовое изображение на OLED

Как система вычисляет высоту

Крейт embedded-graphics внутри использует примерно такой код, чтобы вычислить высоту. Вам не нужно добавлять это в свой код. Я показываю его только для того, чтобы вы видели, как это работает за кулисами:

#![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
}
}

В нашем примере массив данных содержит 28 элементов, каждый пиксель занимает 1 бит, а ширина изображения равна 31. Если выполнить этот расчёт, получится 4 байта на строку и высота 7 пикселей.

Попробуйте сами

Можно запустить этот код прямо здесь или в Rust Playground, чтобы увидеть, как работает расчёт:

// 31x7 пикселей
#[rustfmt::skip]
const IMG_DATA: &[u8] = &[
    // 1-я строка
    0b00000001,0b11111111,0b11111111,0b00000000,
    // 2-я строка
    0b00000001,0b11111111,0b11111111,0b00000000,
    // 3-я строка
    0b00000001,0b10000000,0b00000011,0b00000000,
    // 4-я строка
    0b11111111,0b10000000,0b00000011,0b11111110,
    // 5-я строка
    0b00000001,0b10000000,0b00000011,0b00000000,
    // 6-я строка
    0b00000001,0b11111111,0b11111111,0b00000000,
    // 7-я строка
    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);
}

Эти массивы байтов не обязательно создавать вручную: можно воспользоваться онлайн-инструментом, например image2bytes, который сгенерирует массив байтов за вас.