Опис формату bmp

структури формату

Формат bmp (від слів BitMaP - бітова карта, або, кажучи по-російськи, бітовий масив) представляє з себе нестиснене (в основному) зображення, яке досить легко Новомосковскется і виводиться в ОС Windows, в якій є спеціальні функції API, які в цьому допомагають.

Для початку наведемо графічне представлення даних в bmp (картинка взята з MSDN).


На початку варто заголовок файлу (BITMAPFILEHEADER). Він описаний в такий спосіб:

typedef struct tagBITMAPFILEHEADER
# 123;
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
# 125; BITMAPFILEHEADER, * PBITMAPFILEHEADER;

SetFilePointer # 40; hFile, bfh. bfOffBits. NULL. FILE_BEGIN # 41; ;

Тут і далі будемо вважати, що змінна bfh оголошена як BITMAPFILEHEADER bfh;

А далі йде структура BITMAPINFOHEADER, яка оголошена так:

typedef struct tagBITMAPINFOHEADER
# 123;
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
# 125; BITMAPINFOHEADER, * PBITMAPINFOHEADER;

biSize - це розмір самої структури. Її потрібно форматувати наступним чином: bih.biSize = sizeof (BITMAPINFOHEADER);
Знову тут і далі будемо вважати, що bih оголошена наступним чином: BITMAPINFOHEADER bih;
biWidth і biHeight задають відповідно ширину і висоту картинки в пікселях.
biPlanes задає кількість площин. Поки воно завжди встановлюється в 1.
biBitCount - Кількість біт на один піксель. Детальніше про це поговоримо нижче.
biCompression позначає тип стиснення. Не дивуйтеся і не лякайтеся, що в bmp і раптом стиснення. Я особисто не бачив не однієї стислій bmp (але я не кажу, що таких не існує). Якщо стиснення немає, то цей прапор треба встановлювати в BI_RGB. У цій статті ми говоримо про нестислий формат, тому інші прапори я навіть не буду перераховувати. Схоже, що ця ж структура використовується і в файлах JPEG і PNG, тому що, починаючи з Windows 98 тут з'явилися варіанти BI_JPEG, яка показує, що ця картинка - JPEG і BI_PNG, що це PNG (про формат Jpeg я нічого не знаю, я тільки зробив ці висновки виходячи з того, що написано в MSDN).
biSizeImage позначає розмір картинки в байтах. Якщо зображення не стиснені (тобто попереднє поле встановлено в BI_RGB), то тут повинен бути записаний нуль. biXPelsPerMeter і biYPelsPerMeter позначають відповідно горизонтальне і вертикальне дозвіл (в пікселях на метр) кінцевого пристрою, на яке буде виводитися бітовий масив (растр). Додаток може використовувати це значення для того, щоб вибирати з групи ресурсів найбільш підходящий бітовий масив для потрібного пристрою. Справа в тому, що формат bmp - це по суті апаратно-незалежний растр, тобто коли зовнішній вигляд того, що виходить не залежить від того, на що цей растр проектується (якщо можна так висловитися). Наприклад, картинка буде виглядати однаково незалежно від того, малюється вона на екрані монітора або друкується на принтері. Але ось дозвіл у пристроїв різний, і саме для того, щоб вибрати найбільш підходящу картинку з наявних і використовують ці параметри.
biClrUsed визначає кількість використовуваних квітів з таблиці. Якщо це значення дорівнює нулю, то в растрі використовується максимально можливу кількість квітів, які дозволені значенням biBitCount. Це актуально тільки для стиснених зображень. Якщо biClrUsed НЕ нуль і biBitCount менше 16, то biClrUsed визначає поточне число квітів графічного движка або доступного драйвера пристрою. Якщо biBitCount більше або дорівнює 16, то biClrUsed визначає розмір таблиці кольорів, використовуваної для оптимізації поточної системної палітри.
biClrImportant - це кількість важливих квітів. Визначає число квітів, які необхідні для того, щоб зобразити малюнок. Якщо це значення дорівнює 0 (як це зазвичай і буває), то всі кольори вважаються важливими.

Види формату BMP

Всі різновиди формату bmp умовно можна розділити на два типи: палітровие і беспалітровие. Тобто використовується в даному з форматі палітра чи ні. Зауважте, що палітра може бути навіть в беспалітрових форматах, тільки там вона не використовується. У беспалітрових bmp колір вираховується прямо з тих бітів, які йдуть в файлі, починаючи з деякого місця. А в палітрових кожен байт описує один або кілька пікселів, причому значення байта (або бітів) - це індекс кольору в палітрі. Для початку наведу таблицю, яка порівнює можливі варіанти. Вид картинки (палітровая або беспалітровая) залежить від того, скільки біт віддається на один піксель, тобто від значення biBitCount структури BITMAPINFOHEADER.

Палітровий або беспалітровий формат

приклади програм

Привіт 1. Створення картинки в форматі bmp.
Тут створюється однотонна картинка. У прикладах таких функцій три: створення bmp 8, 16 і 24 біт. Я приведу тільки для 16-бітових.

// Створимо картинку у форматі bmp 16 біт типу 5-5-5, яка буде просто однотонною
void CreateBmp555 # 40; char * fname, WORD color # 41;
# 123;
HANDLE hFile;
DWORD RW;
int i, j;

// Оголосимо потрібні структури
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
BYTE Palette # 91; 1024 # 93; ; // Палітра

// Нехай у нас буде картинка розміром 35 x 50 пікселів
int Width = 35;
int Height = 50;

memset # 40; Palette, 0. тисячі двадцять чотири # 41; ; // У палітрі у нас нулі заповнимо їх
memset # 40; bfh, 0. sizeof # 40; bfh # 41; # 41; ;

bfh. bfType = 0x4D42; // Позначимо, що це bmp 'BM'
bfh. bfOffBits = sizeof # 40; bfh # 41; + sizeof # 40; bih # 41; + 1024; // Палітра займає 1Kb, але ми його використовувати не будемо
bfh. bfSize = bfh. bfOffBits +
sizeof # 40; color # 41; * Width * Height +
Height * # 40; # 40; sizeof # 40; color # 41; * Width # 41; % 4 # 41; ; // Порахуємо розмір кінцевого файлу
memset # 40; bih, 0. sizeof # 40; bih # 41; # 41; ;
bih. biSize = sizeof # 40; bih # 41; ; // Так положено
bih. biBitCount = 16; // 16 біт на піксель
bih. biClrUsed = 32768; // Ми використовуємо 5-5-5
bih. biCompression = BI_RGB; // Без стиснення
bih. biHeight = Height;
bih. biWidth = Width;
bih. biPlanes = 1; // Повинно бути 1
// А решта поля залишаються 0

hFile = CreateFile # 40; fname, GENERIC_WRITE, 0. NULL. CREATE_ALWAYS, 0. NULL # 41; ;
if # 40; hFile == INVALID_HANDLE_VALUE # 41;
return;

// Запишемо заголовки
WriteFile # 40; hFile, bfh, sizeof # 40; bfh # 41 ;. RW, NULL # 41; ;
WriteFile # 40; hFile, bih, sizeof # 40; bih # 41 ;. RW, NULL # 41; ;

// Запишемо палітру
WriteFile # 40; hFile, Palette, 1024. RW, NULL # 41; ;
for # 40; i = 0; i # 123;
for # 40; j = 0; j # 123;
WriteFile # 40; hFile, color, sizeof # 40; color # 41 ;. RW, NULL # 41; ;
# 125;

// Вирівняємо по кордоні
WriteFile # 40; hFile, Palette, # 40; sizeof # 40; color # 41; * Width # 41; % 4. RW, NULL # 41; ;
# 125;
CloseHandle # 40; hFile # 41; ;
# 125;

Приклад 2. Перетворення картинки з формату 8 біт (256 кольорів) в 24 біт.

BOOL Convert256To24 # 40; char * fin, char * fout # 41;
# 123;
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
int Width, Height;
RGBQUAD Palette # 91; 256 # 93; ;
BYTE * inBuf;
RGBTRIPLE * outBuf;
HANDLE hIn, hOut;
DWORD RW;
DWORD OffBits;
int i, j;

hIn = CreateFile # 40; fin, GENERIC_READ, FILE_SHARE_READ, NULL. OPEN_EXISTING, 0. NULL # 41; ;
if # 40; hIn == INVALID_HANDLE_VALUE # 41;
return FALSE;

hOut = CreateFile # 40; fout, GENERIC_WRITE, 0. NULL. CREATE_ALWAYS, 0. NULL # 41; ;
if # 40; hOut == INVALID_HANDLE_VALUE # 41;
# 123;
CloseHandle # 40; hIn # 41; ;
return FALSE;
# 125;

// Прочитаємо дані
ReadFile # 40; hIn, bfh, sizeof # 40; bfh # 41 ;. RW, NULL # 41; ;
ReadFile # 40; hIn, bih, sizeof # 40; bih # 41 ;. RW, NULL # 41; ;
ReadFile # 40; hIn, Palette, 256 * sizeof # 40; RGBQUAD # 41 ;. RW, NULL # 41; ;

// Встановимо покажчик на початок растра
SetFilePointer # 40; hIn, bfh. bfOffBits. NULL. FILE_BEGIN # 41; ;
Width = bih. biWidth;
Height = bih. biHeight;
OffBits = bfh. bfOffBits;

// Виділимо пам'ять
inBuf = new BYTE # 91; Width # 93; ;
outBuf = new RGBTRIPLE # 91; Width # 93; ;

// Заповнимо заголовки
bfh. bfOffBits = sizeof # 40; bfh # 41; + sizeof # 40; bih # 41; ; // Не будемо писати палітру
bih. biBitCount = 24;
bfh. bfSize = bfh. bfOffBits + 4 * Width * Height + Height * # 40; Width% 4 # 41; ; // Розмір файла

// А решта не змінюється
// Запишемо заголовки
WriteFile # 40; hOut, bfh, sizeof # 40; bfh # 41 ;. RW, NULL # 41; ;
WriteFile # 40; hOut, bih, sizeof # 40; bih # 41 ;. RW, NULL # 41; ;

// Почнемо перетворювати
for # 40; i = 0; i # 123;
ReadFile # 40; hIn, inBuf, Width, RW, NULL # 41; ;
for # 40; j = 0; j # 123;
outBuf # 91; j # 93 ;. rgbtRed = Palette # 91; inBuf # 91; j # 93; # 93 ;. rgbRed;
outBuf # 91; j # 93 ;. rgbtGreen = Palette # 91; inBuf # 91; j # 93; # 93 ;. rgbGreen;
outBuf # 91; j # 93 ;. rgbtBlue = Palette # 91; inBuf # 91; j # 93; # 93 ;. rgbBlue;
# 125;
WriteFile # 40; hOut, outBuf, sizeof # 40; RGBTRIPLE # 41; * Width, RW, NULL # 41; ;

// Пишемо сміття для вирівнювання
WriteFile # 40; hOut, Palette, Width% 4. RW, NULL # 41; ;
SetFilePointer # 40; hIn, # 40; 3 * Width # 41; % 4. NULL. FILE_CURRENT # 41; ;
# 125;

delete inBuf;
delete outBuf;
CloseHandle # 40; hIn # 41; ;
CloseHandle # 40; hOut # 41; ;
return TRUE;
# 125;

У функцію треба передавати імена вихідного і кінцевого файлу відповідно.