BMP是英文Bitmap(位圖)的簡寫,這種格式沒有對圖像數(shù)據(jù)進行壓縮,文件里保存了各個像素實際的RGB值,理解起來比較直觀,對學習圖像文件格式的入門很有幫助。
下面列出文件的結構

bmp_3.png

bmp_6.png

bmp_7.png
示例代碼bmp.h
#ifndef __BMP_H__
#define __BMP_H__
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG;
// bmp文件頭(bmp file header)
// 提供文件的格式、大小等信息
typedef struct
{
WORD bfType; // 文件類型,BMP文件用0x4d42表示,即字符串"BM"。
DWORD bfSize; // 文件大小,單位為字節(jié)
WORD bfReserved1; // 保留,目前必須設置為0
WORD bfReserved2; // 保留,目前必須設置為0
DWORD bfOffBits; // 從文件開頭到圖像數(shù)據(jù)的偏移量
}BITMAPFILEHEADER;
// 位圖信息頭(bitmap information)
// 提供圖像數(shù)據(jù)的尺寸、位平面數(shù)、壓縮方式、顏色索引等信息
typedef struct
{
DWORD biSize; // 位圖信息頭的長度,40字節(jié)
LONG biWidth; // 位圖的寬度
LONG biHeight; // 位圖的高度
WORD biPlanes; // 目標設備級別,必須為1
WORD biBitCount; // 每個像素所占位數(shù)(bit),二值圖像為1,灰度圖像為8,真彩色圖像為24
DWORD biCompression; // 位圖壓縮類型
DWORD biSizeImage; // 實際的位圖數(shù)據(jù)占用的字節(jié)數(shù)
LONG biXPelsPerMeter; // 指定目標設備的水平分辨率
LONG biYPelsPerMeter; // 指定目標設備的垂直分辨率
DWORD biClrUsed; // 位圖實際用到的顏色數(shù)
DWORD biClrImportant; // 位圖顯示過程中重要的顏色數(shù)
}BITMAPINFOHEADER;
// 調色板(color palette)
// 可選,如使用索引來表示圖像,調色板就是索引與其對應的顏色的映射表
// 24位的真彩色不需要這部分
typedef struct
{
BYTE rgbBlue; // 藍色分量
BYTE rgbGreen; // 綠色分量
BYTE rgbRed; // 紅色分量
BYTE rgbReserved; // 保留字節(jié),暫時不用
}RGBQUAD;
bool SaveRgb888ToBmpSaveRgb888ToBmp(char * fileName,unsigned char *imgBuffer, int imWidth, int imHeight);
#endif
示例代碼bmp.cpp
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include"bmp.h"
int SaveRgb888ToBmp(char * fileName, unsigned char *imgBuffer, int imgWidth, int imgHeight)
{
if (!imgBuffer)
{
return -1;
}
int biBitCount = 8;// RGB888格式,每個顏色通道8bit
int colorTableSize = 0;// RGB888 不需要灰度圖像顏色表
// windows規(guī)定一個掃描行所占的字節(jié)數(shù)必須是4的倍數(shù),不足4的倍數(shù)則要對其進行擴充。
int lineByte = (imgWidth * biBitCount / 8 + 3) / 4 * 4;
FILE *fp = fopen(fileName, "wb");
if (!fp)
{
return -1;
}
// 填充文件頭
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;
fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
colorTableSize + (lineByte * imHeight);
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize;
// 寫文件頭
fwrite(&fileHead , sizeof(LBITMAPfileHeadER), 1, fp);
// 填充文件信息頭
BITMAPINFOHEADER infoHead;
infoHead.biSize = sizeof(BITMAPINFOHEADER); //40;
infoHead.biWidth = imgWidth;
infoHead.biHeight = 0 - imgHeight; // 正數(shù)表示RGB圖像數(shù)據(jù)行的順序倒置,負數(shù)表示RGB圖像數(shù)據(jù)行的順序正常
infoHead.biBitCount = biBitCount;
infoHead.biPlanes = 1;
infoHead.biCompression = 0;
infoHead.biSizeImage = lineByte * imgHeight;
infoHead.biXPelsPerMeter = 0;
infoHead.biYPelsPerMeter = 0;
infoHead.biClrUsed = 0;
infoHead.biClrImportant = 0;
//寫文件信息頭
fwrite(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);
// RGB888不需要顏色表
/*
RGBQUAD *pColorTable = new RGBQUAD[256];
for (int i = 0 ; i < 256 ; i++)
{
pColorTable[i].rgbBlue = i;
pColorTable[i].rgbGreen = i;
pColorTable[i].rgbRed = i;
//pColorTable[i].rgbReserved = 0;
}
fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
*/
// RGB to BGR, 這里需要把R和B的順序兌換一下,具體原因還不知道
unsigned char tmp;
long imgSize = imgWidth * imgHeight * 3;
for(unsigned long i=0; i<=imgSize-3; i+=3)
{
tmp = imgBuffer[i];
imgBuffer[i] = imgBuffer[i+2];
imgBuffer[i+2] = tmp;
}
// 寫真正的圖像數(shù)據(jù)
fwrite(imgBuffer, imgHeight*lineByte, 1, fp);
fclose(fp);
return 0;
}