BMP是bitmap的縮寫形式,bitmap顧名思義,就是位圖也即Windows位圖。它一般由4部分組成:檔案頭資訊塊、圖像描述資訊塊、顔色表(在真彩色模式無顔色表)和圖像資料區組成。在系統中以BMP為擴充名儲存。
打開Windows的畫圖程式,在儲存圖像時,可以看到三個選項:2色位圖(黑白)、16色位圖、256色位圖和24位位圖。這是最普通的生成位圖的工具,在這裡講解的BMP位圖形式,主要就是指用畫圖生成的位圖(當然,也可以用其它工具軟體生成)。
現在講解BMP的4個組成部分:
1.檔案頭資訊塊
0000-0001:檔案辨別,為字母ASCII碼“BM”。
0002-0005:檔案大小。
0006-0009:保留,每位元組以“00”填寫。
000A-000D:記錄圖像資料區的起始位置。各位元組的資訊依次含義為:檔案頭資訊塊大小,圖像描述資訊塊的大小,圖像顔色表的大小,保留(為01)。
2.圖像描述資訊塊
000E-0011:圖像描述資訊塊的大小,常為28H。
0012-0015:圖像寬度。
0016-0019:圖像高度。
001A-001B:圖像的plane(平面?)總數(恒為1)。
001C-001D:記錄像素的位數,很重要的數值,圖像的顔色數由該值決定。
001E-0021:資料壓縮方式(數值位0:不壓縮;1:8位壓縮;2:4位壓縮)。
0022-0025:圖像區資料的大小。
0026-0029:水準每米有多少像素,在裝置無關位圖(.DIB)中,每位元組以00H填寫。
002A-002D:垂直每米有多少像素,在裝置無關位圖(.DIB)中,每位元組以00H填寫。
002E-0031:此圖像所用的顔色數,如值為0,表示所有顔色一樣重要。
3.顔色表
顔色表的大小根據所使用的顔色模式而定:2色圖像為8位元組;16色圖像位64位元組;256色圖像為1024位元組。其中,每4位元組表示一種顔色,并以B(藍色)、G(綠色)、R(紅色)、alpha(像素的透明度值,一般不需要)。即首先4位元組表示顔色号0的顔色,接下來表示顔色号1的顔色,依此類推。
4.圖像資料區
顔色表接下來位為位圖檔案的圖像資料區,在此部分記錄着每點像素對應的顔色号,其記錄方式也随顔色模式而定,既2色圖像每點占1位(8位為1位元組);16色圖像每點占4位(半位元組);256色圖像每點占8位(1位元組);真彩色圖像每點占24位(3位元組)。是以,整個資料區的大小也會随之變化。究其規律而言,可的出如下計算公式:圖像資料資訊大小=(圖像寬度*圖像高度*記錄像素的位數)/8。
然而,未壓縮的圖像資訊區的大小。除了真彩色模式外,其餘的均大于或等于資料資訊的大小。這是為什麼呢?原因有兩個:
1.BMP檔案記錄一行圖像是以位元組為機關的。是以,就不存在一個位元組中的資料位資訊表示的點在不同的兩行中。也就是說,設顯示模式位16色,在每個位元組配置設定兩個點資訊時,如果圖像的寬度位奇數,那麼最後一個像素點的資訊将獨占一個位元組,這個位元組的後4位将沒有意義。接下來的一個位元組将開始記錄下一行的資訊。
2.為了顯示的友善,除了真彩色外,其他的每中顔色模式的行位元組數要用資料“00”補齊為4的整數倍。如果顯示模式為16色,當圖像寬為19時,存儲時每行則要補充4-(19/2+1)%4=2個位元組(加1是因為裡面有一個像素點要獨占了一位元組)。如果顯示模式為256色,當圖像寬為19時,每行也要補充4-19%4=1個位元組。
還有一點我要申明,當螢幕初始化為16或256色模式時,一定要設定調色闆或修正顔色值,否則無法得到正确的圖像顔色。
C代碼
1. //ReadBitMap
2. //
3. #include <string.h>
4. #include <math.h>
5. #include <stdio.h>
6. #include <stdlib.h>
7. #include <malloc.h>
8.
9.
10. #define WIDTHBYTES(bits) (((bits)+31)/32*4)
11.
12. typedef unsigned char BYTE;
13. typedef unsigned short WORD;
14. typedef unsigned long DWORD;
15. typedef long LONG;
16.
17.
18. //位圖檔案頭資訊結構定義
19. //其中不包含檔案類型資訊(由于結構體的記憶體結構決定,要是加了的話将不能正确讀取檔案資訊)
20.
21. typedef struct
22.
23. DWORD bfSize; //檔案大小
24. WORD bfReserved1; //保留字,不考慮
25. WORD bfReserved2; //保留字,同上
26. DWORD bfOffBits; //實際位圖資料的偏移位元組數,即前三個部分長度之和
27. } BITMAPFILEHEADER;
28.
29.
30. //資訊頭BITMAPINFOHEADER,也是一個結構,其定義如下:
31.
32. typedef struct
33. //public:
34. DWORD biSize; //指定此結構體的長度,為40
35. LONG biWidth; //位圖寬
36. LONG biHeight; //位圖高
37. WORD biPlanes; //平面數,為1
38. WORD biBitCount; //采用顔色位數,可以是1,2,4,8,16,24,新的可以是32
39. DWORD biCompression; //壓縮方式,可以是0,1,2,其中0表示不壓縮
40. DWORD biSizeImage; //實際位圖資料占用的位元組數
41. LONG biXPelsPerMeter; //X方向分辨率
42. LONG biYPelsPerMeter; //Y方向分辨率
43. DWORD biClrUsed; //使用的顔色數,如果為0,則表示預設值(2^顔色位數)
44. DWORD biClrImportant; //重要顔色數,如果為0,則表示所有顔色都是重要的
45. } BITMAPINFOHEADER;
46.
47.
48. //調色闆Palette,當然,這裡是對那些需要調色闆的位圖檔案而言的。24位和32位是不需要調色闆的。
49. //(似乎是調色闆結構體個數等于使用的顔色數。)
50.
51. typedef struct
52. //public:
53. BYTE rgbBlue; //該顔色的藍色分量
54. BYTE rgbGreen; //該顔色的綠色分量
55. BYTE rgbRed; //該顔色的紅色分量
56. BYTE rgbReserved; //保留值
57. } RGBQUAD;
58.
59.
60.
61. void
62. {
63. printf("位圖檔案頭:\n");
64. printf("檔案大小:%d\n",pBmpHead->bfSize);
65. printf("保留字:%d\n",pBmpHead->bfReserved1);
66. printf("保留字:%d\n",pBmpHead->bfReserved2);
67. printf("實際位圖資料的偏移位元組數:%d\n",pBmpHead->bfOffBits);
68.
69. }
70.
71.
72. void
73. {
74. printf("位圖資訊頭:\n");
75. printf("結構體的長度:%d\n",pBmpInforHead->biSize);
76. printf("位圖寬:%d\n",pBmpInforHead->biWidth);
77. printf("位圖高:%d\n",pBmpInforHead->biHeight);
78. printf("biPlanes平面數:%d\n",pBmpInforHead->biPlanes);
79. printf("biBitCount采用顔色位數:%d\n",pBmpInforHead->biBitCount);
80. printf("壓縮方式:%d\n",pBmpInforHead->biCompression);
81. printf("biSizeImage實際位圖資料占用的位元組數:%d\n",pBmpInforHead->biSizeImage);
82. printf("X方向分辨率:%d\n",pBmpInforHead->biXPelsPerMeter);
83. printf("Y方向分辨率:%d\n",pBmpInforHead->biYPelsPerMeter);
84. printf("使用的顔色數:%d\n",pBmpInforHead->biClrUsed);
85. printf("重要顔色數:%d\n",pBmpInforHead->biClrImportant);
86. }
87.
88. void
89. {
90. printf("(%-3d,%-3d,%-3d) ",pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue);
91.
92. }
93.
94.
95.
96. void
97. {
98.
99. BITMAPFILEHEADER bitHead;
100. BITMAPINFOHEADER bitInfoHead;
101. FILE* pfile;
102.
103. char
104. printf("please input the .bmp file name:\n");
105. scanf("%s",strFile);
106.
107. pfile = fopen(strFile,"rb");//打開檔案
108.
109. if(pfile!=NULL)
110. {
111. "file bkwood.bmp open success.\n");
112. //讀取位圖檔案頭資訊
113. WORD
114. sizeof(WORD),pfile);
115. if(fileType != 0x4d42)
116. {
117. "file is not .bmp file!");
118. return;
119. }
120. //fseek(pfile,2,SEEK_CUR); // "BM"
121. sizeof(tagBITMAPFILEHEADER),pfile);
122.
123. showBmpHead(&bitHead);
124. "\n\n");
125.
126. //讀取位圖資訊頭資訊
127. sizeof(BITMAPINFOHEADER),pfile);
128. showBmpInforHead(&bitInfoHead);
129. "\n");
130. }
131. else
132. {
133. "file open fail!\n");
134. return;
135. }
136.
137.
138. tagRGBQUAD *pRgb ;
139.
140. if(bitInfoHead.biBitCount < 24)//有調色闆
141. {
142. //讀取調色盤結資訊
143. long nPlantNum = long(pow(2,double(bitInfoHead.biBitCount))); // Mix color Plant Number;
144. sizeof(tagRGBQUAD));
145. sizeof(tagRGBQUAD));
146. int
147.
148. "Color Plate Number: %d\n",nPlantNum);
149.
150. "顔色闆資訊:\n");
151. for (int
152. {
153. if
154. {
155. "\n");
156. }
157. showRgbQuan(&pRgb[i]);
158.
159. }
160.
161. "\n");
162.
163. }
164.
165.
166. int
167. int
168. //配置設定記憶體空間把源圖存入記憶體
169. int l_width = WIDTHBYTES(width* bitInfoHead.biBitCount);//計算位圖的實際寬度并確定它為32的倍數
170. BYTE *pColorData=(BYTE
171. memset(pColorData,0,height*l_width);
172. long
173.
174. //把位圖資料資訊讀到數組裡
175. fread(pColorData,1,nData,pfile);
176.
177.
178.
179. //将位圖資料轉化為RGB資料
180. tagRGBQUAD* dataOfBmp;
181. dataOfBmp = (tagRGBQUAD *)malloc(width*height*sizeof(tagRGBQUAD));//用于儲存各像素對應的RGB資料
182. memset(dataOfBmp,0,width*height*sizeof(tagRGBQUAD));
183.
184. if(bitInfoHead.biBitCount<24)//有調色闆,即位圖為非真彩色
185. {
186. int
187. int
188. if
189. {
190. for(int
191. for(int
192. {
193. BYTE
194. //k:取得該像素顔色資料在實際資料數組中的序号
195. //j:提取目前像素的顔色的具體值
196. mixIndex = pColorData[k];
197. switch(j%8)
198. {
199. case
200. mixIndex = mixIndex<<7;
201. mixIndex = mixIndex>>7;
202. break;
203. case
204. mixIndex = mixIndex<<6;
205. mixIndex = mixIndex>>7;
206. break;
207. case
208. mixIndex = mixIndex<<5;
209. mixIndex = mixIndex>>7;
210. break;
211.
212. case
213. mixIndex = mixIndex<<4;
214. mixIndex = mixIndex>>7;
215. break;
216. case
217. mixIndex = mixIndex<<3;
218. mixIndex = mixIndex>>7;
219. break;
220.
221. case
222. mixIndex = mixIndex<<2;
223. mixIndex = mixIndex>>7;
224. break;
225. case
226. mixIndex = mixIndex<<1;
227. mixIndex = mixIndex>>7;
228. break;
229.
230. case
231. mixIndex = mixIndex>>7;
232. break;
233. }
234.
235. //将像素資料儲存到數組中對應的位置
236. dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
237. dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
238. dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
239. dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
240. index++;
241.
242. }
243. }
244.
245. if(bitInfoHead.biBitCount==2)
246. {
247. for(int
248. for(int
249. {
250. BYTE
251. //k:取得該像素顔色資料在實際資料數組中的序号
252. //j:提取目前像素的顔色的具體值
253. mixIndex = pColorData[k];
254. switch(j%4)
255. {
256. case
257. mixIndex = mixIndex<<6;
258. mixIndex = mixIndex>>6;
259. break;
260. case
261. mixIndex = mixIndex<<4;
262. mixIndex = mixIndex>>6;
263. break;
264. case
265. mixIndex = mixIndex<<2;
266. mixIndex = mixIndex>>6;
267. break;
268. case
269. mixIndex = mixIndex>>6;
270. break;
271. }
272.
273. //将像素資料儲存到數組中對應的位置
274. dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
275. dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
276. dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
277. dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
278. index++;
279.
280.
281. }
282. }
283. if(bitInfoHead.biBitCount == 4)
284. {
285. for(int
286. for(int
287. {
288. BYTE
289. k = i*l_width + j/2;
290. mixIndex = pColorData[k];
291. if(j%2==0)
292. //低
293. mixIndex = mixIndex<<4;
294. mixIndex = mixIndex>>4;
295. }
296. else
297. //高
298. mixIndex = mixIndex>>4;
299. }
300.
301. dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
302. dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
303. dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
304. dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
305. index++;
306.
307. }
308.
309. }
310. if(bitInfoHead.biBitCount == 8)
311. {
312. for(int
313. for(int
314. {
315. BYTE
316.
317. k = i*l_width + j;
318.
319. mixIndex = pColorData[k];
320.
321. dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
322. dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
323. dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
324. dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
325. index++;
326.
327.
328.
329. }
330. }
331. if(bitInfoHead.biBitCount == 16)
332. {
333. for(int
334. for(int
335. {
336. WORD
337.
338. k = i*l_width + j*2;
339. WORD
340. shortTemp = pColorData[k+1];
341. shortTemp = shortTemp<<8;
342.
343. mixIndex = pColorData[k] + shortTemp;
344.
345. dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
346. dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
347. dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
348. dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
349. index++;
350. }
351. }
352. }
353. else//位圖為24位真彩色
354. {
355. int
356. int
357. for(int
358. for(int
359. {
360. k = i*l_width + j*3;
361. dataOfBmp[index].rgbRed = pColorData[k+2];
362. dataOfBmp[index].rgbGreen = pColorData[k+1];
363. dataOfBmp[index].rgbBlue = pColorData[k];
364. index++;
365. }
366. }
367.
368.
369. printf("像素資料資訊:\n");
370. for (int
371. {
372. if
373. {
374. "\n");
375. }
376. showRgbQuan(&dataOfBmp[i]);
377. }
378.
379. fclose(pfile);
380. if
381. {
382. free(pRgb);
383. }
384. free(dataOfBmp);
385. free(pColorData);
386. printf("\n");
387.
388. }