Форматы BMP и RLE

Формат 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, начиная от начала файла.

 

BMP без сжатия.

 

Поле 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);