Формат
BMP исторически
является основным форматом представления изображений в ОС Microsoft Windows*. Постараемся
дать, по возможности, полное описание формата.
Обычно формат не
использует алгоритмов сжатия. Поэтому не представляет особого труда написать
собственную программу, позволяющую считывать изображения с диска (т.е.
конвертировать их из BMP формата в формат, удобный для непосредственного использования) и
записывать их на диск.
Данный формат дает возможность представлять изображения с палитрой или без нее. При наличии палитры значение цвета в каждом пикселе задается номером цвета. Сам цвет определяется по его номеру в палитре, представляющей собой массив
unsigned char pal[256][4];
Каждая строка в массиве палитры задает один цвет с помощью указания его RGB составляющих, соответственно, в ячейках с номерами 0, 1, 2. Количество строк зависит от количества используемых цветов и, обычно, не превосходит 256 (что соответствует изображению, в котором на один пиксел отводится 8 бит).
Данные можно представлять в формате true color, когда каждый пиксел задается 4-мя байтами. Из них используется 3 байта для размещения RGB компонент цвета (по одному байту на каждую компоненту).
Возможно большое количество нестандартных вариаций данного формата. Например, отсутствие палитры в изображениях с толщиной 8 бит на пиксел может обозначать, что кодируется серое изображение, в каждом байте которого хранится яркость пиксела. Некоторые программы понимают форматы BMP в которых отводится 3 байта на пиксел (формат true color) или даже 2 байта на пиксел (в пикселе хранится только яркость, т.е. кодируется серое изображение).
Файл состоит из следующих разделов:
· Заголовка
· Возможно – палитры
· Собственно данных
Заголовок файла представляется следующей структурой
struct BMPHEAD
{
unsigned short int Signature
; //
Must be 0x4d42 == ”BM” //0
unsigned long FileLength
; // в байтах //2
unsigned long Zero ; // Must be 0 //6
unsigned long Ptr ; // смещение к области данных //10
unsigned long Version ;// длина оставшейся части заголовка=0x28 //14
unsigned long Width ; // ширина изображения в пикселах //18
unsigned long Height ;
// высота изображения в
пикселах //22
unsigned short int Planes ; // к-во битовых плоскостей //26
unsigned short int BitsPerPixel ; // к-во бит на папиксел //28
unsigned long Compression ; // сжатие: 0 или 1 или 2 //30
unsigned long SizeImage ; // размер блока данных в байтах //34
unsigned long XPelsPerMeter ; //
в ширину: пикселов на метр //38
unsigned long YPelsPerMeter ; //
в высчоту: пикселов на метр //42
unsigned long ClrUsed ; // к-во цветов в палитре //46
unsigned long ClrImportant ; // к-во используемых цветов в палитре //50
} ;
Предполагается, что sizeof(unsigned long)==4, sizeof(unsigned short
int)==2.
Соответствующие поля в файле с данным форматом непрерывно располагаются в указанном порядке.
Палитра (если она есть) следует сразу за заголовком. Данные начинаются с байта номер Ptr, начиная от начала файла.
Поле Compression определяет способ сжатия данных. Обычно значение этого поля=0, что соответствует отсутствию сжатия. При этом данные записываются по битам подряд. BMP формат со сжатием часто называется RLE форматом.
Длина каждой строки округляется в большую сторону до кратности 32 битам (4 байта). Т.о., например, при отсутствии сжатия если Width=3, то каждая строка на диске будет занимать
(Width* BitsPerPixel + 31)/8=4 байт.
Предполагается, что байты располагаются в порядке их нумерации, старший бит слева. Т.о., если BitsPerPixel =1, то самый первый пиксел ляжет в старший бит самого первого байта данных.
Для хранения всего изображения структуру struct BMPHEAD следует дополнить массивом палитры и массивом самих данных. Если предположить, что мы будем иметь дело с изображениями не более 8 бит на пиксел, то для данных можно завести, например, массив unsigned char **v . Пиксел с координатами (i,j) можно хранить в переменной v[i][j].
Итак, все изображение можно хранить в структуре
struct CBMP
{
unsigned short int Signature
; //
Must be 0x4d42 == ”BM” //0
unsigned long FileLength
; // в байтах //2
unsigned long Zero ; // Must be 0 //6
unsigned long Ptr ; // смещение к области данных //10
unsigned long Version ;// длина оставшейся части заголовка=0x28 //14
unsigned long Width ; // ширина изображения в пикселах //18
unsigned long Height ; // высота изображения в пикселах //22
unsigned short int Planes ; // к-во битовых плоскостей //26
unsigned short int BitsPerPixel ; // к-во бит на папиксел //28
unsigned long Compression ; // сжатие: 0 или 1 или 2 //30
unsigned long SizeImage ; // размер блока данных в байтах //34
unsigned long XPelsPerMeter ; //
в ширину: пикселов на метр //38
unsigned long YPelsPerMeter ; //
в высчоту: пикселов на метр //42
unsigned long ClrUsed ; // к-во
цветов в палитре //46
unsigned long ClrImportant ; // к-во используемых цветов в палитре //50
unsigned char pal[256][4];
unsigned char **v;
} ;
Отвести память можно, например, следующим образом:
struct CBMP pic; int i;
…
pic.v=(unsigned
char**)malloc(pic.Height*sizeof(char*));
for(i=0;i<pic.Height;i++)pic.v[i]=
(unsigned char*)malloc(pic.Width);
Однако следующий способ гораздо более эффективен:
struct CBMP pic; int i;
…
pic.v=(unsigned
char**)malloc(pic.Height*sizeof(char*)+pic.Height*pic.Width);
pic.v[0]= (unsigned char**)(pic.v+pic.Height);
for(i=1;i<pic.Height;i++)pic.v[i]=pic.v[i-1]+pic.Width;
Преимуществом такого способа отведения памяти является уменьшение накладных расходов и упрощение процедуры освобождения памяти. Для освобождения отведенной памяти надо вызвать всего один оператор:
free(pic.v);