Bitmap是Windows操作系統中的標準圖像文件格式,可以分成兩類:設備相關位圖(DDB)和設備無關位圖(DIB),DDB已經基本停用。
Bitmap格式有4部分組成: 文件頭、圖像描述、顏色表(在真彩色(24或32位)模式無顏色表)和圖像數據區
1. 文件頭 14B
2B 0000-0001:文件標識,爲字母ASCII碼“BM”,即0x4d42
4B 0002-0005:文件大小,字節數,最大爲4G
4B 0006-0009:保留,每字節以“00”填寫。
4B 000A-000D:記錄圖像數據區的起始位置。各字節的信息依次含義爲:文件頭信息塊大小,圖像描述信息塊的大小,圖像顏色表的大小,保留(爲01)。
數據類型定義:(32位系統下的定義;64位系統下在編譯時需要加入-m32)
typedef unsigned char BYTE; // 1B
typedef unsigned short WORD; // 2B
typedef unsigned long DWORD; // 4B
typedef long LONG; // 4B
文件頭結構定義
typedef struct tagBITMAPFILEHEADER {
WORD magic; // "BM",即0x4d42
DWORD bfSize; //文件大小
WORD bfReserved1; //保留字,不考慮
WORD bfReserved2; //保留字,同上
DWORD bfOffBits; //實際位圖數據的偏移字節數,即前三個部分長度之和
} BITMAPFILEHEADER;
2. 圖像描述 40B
4B 000E-0011:圖像描述信息塊的大小,常爲40。
4B 0012-0015:圖像寬度。
4B 0016-0019:圖像高度。
2B 001A-001B:圖像的plane(平面?)總數(恆爲1)。
2B 001C-001D:記錄像素的位數,很重要的數值,圖像的顏色數由該值決定。
4B 001E-0021:數據壓縮方式(數值位0:不壓縮;1:8位壓縮;2:4位壓縮)。
4B 0022-0025:圖像區數據的大小。
4B 0026-0029:水平每米有多少像素,在設備無關位圖(.DIB)中,每字節以00H填寫。
4B 002A-002D:垂直每米有多少像素,在設備無關位圖(.DIB)中,每字節以00H填寫。
4B 002E-0031:此圖像所用的顏色數,如值爲0,表示所有顏色一樣重要。
4B 0032-0035:重要的顏色數
圖像信息描述結構
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //指定此結構體的長度,爲40
LONG biWidth; //位圖寬
LONG biHeight; //位圖高
WORD biPlanes; //平面數,爲1
WORD biBitCount; //採用顏色位數,可以是1,2,4,8,16,24,新的可以是32
DWORD biCompression; //壓縮方式,可以是0,1,2,其中0表示不壓縮
DWORD biSizeImage; //實際位圖數據佔用的字節數
LONG biXPelsPerMeter; //X方向分辨率
LONG biYPelsPerMeter; //Y方向分辨率
DWORD biClrUsed; //使用的顏色數,如果爲0,則表示默認值(2^顏色位數)
DWORD biClrImportant; //重要顏色數,如果爲0,則表示所有顏色都是重要的
} BITMAPINFOHEADER;
不包含顏色表的數據頭大小爲54B
在真彩色(24或32位)模式無顏色表
其他色彩圖像的顏色表的大小根據所使用的顏色模式而定:
2色圖像的顏色表大小是8字節;16色圖像的顏色表大小是64字節;256色圖像的顏色表大小是1024字節。
其中,每4字節表示一種顏色,並以B(藍色)、G(綠色)、R(紅色)、alpha(像素的透明度值,一般不需要)。即首先4字節表示顏色號0的顏色,接下來表示顏色號1的顏色,依此類推。
以16色圖像爲例:每一種顏色是4B,所以顏色表大小就是16*4=64B。
4 圖像數據區
注意情況: 每種顏色模式的行字節數要用數據“00”補齊爲4的整數倍
16色圖像,圖像寬爲19,存儲時每行則要補充4-(19/2+1)%4=2個字節(加1是因爲裏面有一個像素點要獨佔了一字節)。
256色圖像,圖像寬爲19,每行也要補充4-19%4=1個字節。
24位色彩圖,圖像寬爲19,每行也要補充4-(19*3)%4=3個字節。
32位色彩圖,圖像寬爲19,每行也要補充4-(19*4)%4=0個字節,即無需補字節,本身已經是4B對齊了
參考程序
#include <stdio.h>
#include <stdlib.h>
#define WIDTHBYTES(bits) (((bits)+31)/32*4)
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef long LONG;
//位圖文件頭信息結構定義
//其中不包含文件類型信息(由於結構體的內存結構決定,要是加了的話將不能正確讀取文件信息)
typedef struct tagBITMAPFILEHEADER {
DWORD bfSize; //文件大小
WORD bfReserved1; //保留字,不考慮
WORD bfReserved2; //保留字,同上
DWORD bfOffBits; //實際位圖數據的偏移字節數,即前三個部分長度之和
} BITMAPFILEHEADER;
//信息頭BITMAPINFOHEADER,也是一個結構,其定義如下:
typedef struct tagBITMAPINFOHEADER{
//public:
DWORD biSize; //指定此結構體的長度,爲40
LONG biWidth; //位圖寬
LONG biHeight; //位圖高
WORD biPlanes; //平面數,爲1
WORD biBitCount; //採用顏色位數,可以是1,2,4,8,16,24,新的可以是32
DWORD biCompression; //壓縮方式,可以是0,1,2,其中0表示不壓縮
DWORD biSizeImage; //實際位圖數據佔用的字節數
LONG biXPelsPerMeter; //X方向分辨率
LONG biYPelsPerMeter; //Y方向分辨率
DWORD biClrUsed; //使用的顏色數,如果爲0,則表示默認值(2^顏色位數)
DWORD biClrImportant; //重要顏色數,如果爲0,則表示所有顏色都是重要的
} BITMAPINFOHEADER;
//調色板Palette,當然,這裏是對那些需要調色板的位圖文件而言的。24位和32位是不需要調色板的。
//(似乎是調色板結構體個數等於使用的顏色數。)
typedef struct tagRGBQUAD {
//public:
BYTE rgbBlue; //該顏色的藍色分量
BYTE rgbGreen; //該顏色的綠色分量
BYTE rgbRed; //該顏色的紅色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
void showBmpHead(BITMAPFILEHEADER* pBmpHead)
{
printf("位圖文件頭:\n");
printf("文件大小:%d\n",pBmpHead->bfSize);
printf("保留字:%d\n",pBmpHead->bfReserved1);
printf("保留字:%d\n",pBmpHead->bfReserved2);
printf("實際位圖數據的偏移字節數:%d\n",pBmpHead->bfOffBits);
}
void showBmpInforHead(BITMAPINFOHEADER* pBmpInforHead)
{
printf("位圖信息頭:\n");
printf("結構體的長度:%d\n",pBmpInforHead->biSize);
printf("位圖寬:%d\n",pBmpInforHead->biWidth);
printf("位圖高:%d\n",pBmpInforHead->biHeight);
printf("biPlanes平面數:%d\n",pBmpInforHead->biPlanes);
printf("biBitCount採用顏色位數:%d\n",pBmpInforHead->biBitCount);
printf("壓縮方式:%d\n",pBmpInforHead->biCompression);
printf("biSizeImage實際位圖數據佔用的字節數:%d\n",pBmpInforHead->biSizeImage);
printf("X方向分辨率:%d\n",pBmpInforHead->biXPelsPerMeter);
printf("Y方向分辨率:%d\n",pBmpInforHead->biYPelsPerMeter);
printf("使用的顏色數:%d\n",pBmpInforHead->biClrUsed);
printf("重要顏色數:%d\n",pBmpInforHead->biClrImportant);
}
void showRgbQuan(RGBQUAD* pRGB)
{
printf("(%-3d,%-3d,%-3d) ",pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue);
}
void main()
{
#ifdef __x86_64__
printf("__x86_64__\n");
#elif __i386__
printf("__i386__\n");
#endif
BITMAPFILEHEADER bitHead;
BITMAPINFOHEADER bitInfoHead;
FILE* pfile;
char strFile[50];
printf("please input the .bmp file name:\n");
scanf("%s",strFile);
pfile = fopen(strFile,"rb");//打開文件
if(pfile!=NULL) {
printf("file bkwood.bmp open success.\n");
//讀取位圖文件頭信息
WORD fileType;
fread(&fileType,1,sizeof(WORD),pfile);
if(fileType != 0x4d42) {<span style="color:#ff0000;"> // 將magic單獨出來,沒有放到頭信息裏處理,來判斷該文件是否時bitmap文件</span>
printf("file is not .bmp file!");
return;
}
fread(&bitHead,1,sizeof(BITMAPFILEHEADER),pfile);
showBmpHead(&bitHead);
printf("\n\n");
//讀取位圖信息頭信息
fread(&bitInfoHead,1,sizeof(BITMAPINFOHEADER),pfile);
showBmpInforHead(&bitInfoHead);
printf("\n");
} else {
printf("file open fail!\n");
return;
}
RGBQUAD *pRgb ;
int width = bitInfoHead.biWidth;
int height = bitInfoHead.biHeight;
//分配內存空間把源圖存入內存
int l_width = WIDTHBYTES(width* bitInfoHead.biBitCount);//計算位圖的實際寬度並確保它爲32的倍數
BYTE *pColorData=(BYTE *)malloc(height*l_width);
memset(pColorData,0,height*l_width);
long nData = height*l_width;
//把位圖數據信息讀到數組裏
fread(pColorData,1,nData,pfile);
//將位圖數據轉化爲RGB數據
RGBQUAD* dataOfBmp;
dataOfBmp = (RGBQUAD *)malloc(width*height*sizeof(RGBQUAD));//用於保存各像素對應的RGB數據
memset(dataOfBmp,0,width*height*sizeof(RGBQUAD));
int k = 0;
int i = 0, j = 0, index = 0;
for(;i<height;i++) {
for(j=0;j<width;j++) {
k = i*l_width + j*3;
dataOfBmp[index].rgbRed = pColorData[k+2];
dataOfBmp[index].rgbGreen = pColorData[k+1];
dataOfBmp[index].rgbBlue = pColorData[k];
index++;
}
}
printf("像素數據信息:\n");
for (i=0; i<width*height; i++) {
if (i%5==0) {
printf("\n");
}
showRgbQuan(&dataOfBmp[i]);
}
fclose(pfile);
if (bitInfoHead.biBitCount<24) {
free(pRgb);
}
free(dataOfBmp);
free(pColorData);
printf("\n");
}
編譯時需要注意:
如果是64位,需要使用-m32參數。例如gcc main.c -m32
這是因爲某些數據類型如long,在32和64是不同的
參考文章: http://blog.sina.com.cn/s/blog_523491650100fk1z.html
這裏有詳細的帶有顏色表的bitmap的處理