Заполнение в 24-битном RGB растровом изображении
Может ли кто-нибудь объяснить мне, почему в 24-битном файле RGB растрового изображения мне нужно добавить отступ, размер которого зависит от ширины изображения? Для чего?
Я имею в виду, что я должен добавить этот код в мою программу (в C):
if( read % 4 != 0 ) {
read = 4 - (read%4);
printf( "Padding: %d bytes\n", read );
fread( pixel, read, 1, inFile );
}
5 ответов
Поскольку 24 бита - это нечетное число байтов (3), и по ряду причин все строки изображения должны начинаться с адреса, кратного 4 байтам.
Согласно Википедии, формат растрового файла указывает, что:
Биты, представляющие пиксели растрового изображения, упакованы в строки. Размер каждой строки округляется до кратного 4 байта (32-битный DWORD) путем заполнения. Байты заполнения (не обязательно 0) должны быть добавлены в конец строк, чтобы увеличить длину строк до четырех байтов. Когда массив пикселей загружается в память, каждая строка должна начинаться с адреса памяти, кратного 4. Это ограничение адреса / смещения обязательно только для пиксельных массивов, загруженных в память. В целях хранения файлов только размер каждой строки должен быть кратен 4 байтам, а смещение файла может быть произвольным. 24-битное растровое изображение с шириной =1 будет иметь 3 байта данных на строку (синий, зеленый, красный) и 1 байт заполнения, в то время как ширина =2 будет иметь 2 байта заполнения, а ширина =3 будет иметь 3 байта padding и Width=4 не будут иметь никакого дополнения вообще.
Статья в Википедии, посвященная заполнению структуры данных, также является интересным прочтением, объясняющим причины, по которым заполнители обычно используются в информатике.
Я предполагаю, что это было дизайнерское решение, чтобы выровнять для лучших образцов памяти, не тратя так много места (для изображения шириной 319 пикселей вы бы потратили 3 байта или 0,25%)
Представьте, что вам нужен прямой доступ к какой-то нечетной строке. Вы можете получить доступ к первым 4 пикселям n-й строки, выполнив:
uint8_t *startRow = bmp + n * width * 3; //3 bytes per pixel
uint8_t r1 = startRow[0];
uint8_t g1 = startRow[1];
//... Repeat
uint8_t b4 = startRow[11];
Обратите внимание, что если n и width нечетные (а bmp четные), startRow будет нечетным.
Теперь, если вы попытались сделать следующее ускорение:
uint32_t *startRow = (uint32_t *) (bmp + n * width * 3);
uint32_t a = startRow[0]; //Loading register at a time is MUCH faster
uint32_t b = startRow[1]; //but only if address is aligned
uint32_t c = startRow[2]; //else code can hit bus errors!
uint8_t r1 = (a & 0xFF000000) >> 24;
uint8_t g1 = (a & 0x00FF0000) >> 16;
//... Repeat
uint8_t b4 = (c & 0x000000FF) >> 0;
Вы столкнулись с множеством проблем. В лучшем случае (то есть intel cpu) каждую вашу загрузку a, b и c нужно будет разбить на две загрузки, так как startRow не делится на 4. В худшем случае (например, sun sparc) ваша программа аварийно завершится с "ошибка шины".
В более новых разработках обычно необходимо выровнять строки по крайней мере до размера строки кэша L1 (64 байта для Intel или 128 байтов для NVIDIA GPU).
Укороченная версия
Поскольку формат файла bmp определяет строки, они должны идеально помещаться в 32-битные "ячейки памяти". Поскольку пиксели имеют размер 24 бита, некоторые комбинации пикселей не будут идеально подходить для 32-битных "ячеек". В этом случае ячейка "дополняется" полными 32 битами.
8 бит на байт ∴ ячейка: 32 бита = 4 байта ∴ пиксель: 24 бита = 3 байта
// If doesn't fit perfectly in 4 byte "cell"
if( read % 4 != 0 ) {
// find the difference between the "cell", and "the partial fit"
read = 4 - (read%4);
printf( "Padding: %d bytes\n", read );
// skip the difference
fread( pixel, read, 1, inFile );
}
Длинная версия
В вычислениях слово - это естественная единица данных, используемая конкретной схемой процессора. Слово - это фрагмент данных фиксированного размера, обрабатываемый как единое целое с помощью набора команд или аппаратного обеспечения процессора.
- wiki: Word_ (компьютерная архитектура)
Компьютерные системы в основном имеют предпочтительную "длину слова" (хотя в наши дни это не так важно). Стандартный блок данных допускает все виды оптимизаций в архитектуре компьютерной системы (подумайте, что сделали транспортные контейнеры для индустрии морских перевозок). Существует 32-битный стандарт, называемый DWORD или двойное слово (я полагаю), и именно для этого оптимизированы типичные растровые изображения.
Поэтому, если у вас есть 24 бита на пиксель, будут различные длины строк "буквальных пикселей", которые не будут хорошо вписываться в 32 бита. Так что в этом случае, набери это.
Примечание: сегодня вы, вероятно, используете компьютер с размером слова 64 бита. Проверьте свой процессор.
Это зависит от формата, есть ли заполнение в конце каждой строки.
На самом деле нет особой причины для 3х8-битных канальных изображений, так как ввод / вывод в любом случае ориентирован на байты. Для изображений с пикселями, упакованными менее чем в байт (например, 1 бит / пиксель), заполнение полезно, чтобы каждая строка начиналась со смещения байта.