BMP (Bitmap) 是 Windows 標準圖像格式,結構簡單、無壓縮,適合學習圖像處理基礎。核心難點在於行數據 4 字節對齊、顏色表處理(灰度圖 vs 真彩色)、以及文件頭/信息頭的正確填充。
// 全局變量 unsigned char *gPBmpBuf; // 像素數據指針 int gBmpWidth, gBmpHeight; // 寬高 RGBQUAD *gPColorTable; // 調色盤指針 (8位圖) int gBiBitCount; // 位深:8/24/32 // 4字節對齊宏:(bits+31)/32*4 等價於 ((bits+31)&~31)/8 #define WIDTHBYTES(bits) (((bits) + 31) & (~31)) / 8
loadBmpbool loadBmp(char *bmpName) {
FILE *fp = fopen(bmpName, "rb");
if (!fp) return false;
// 1. 跳過文件頭 (14字節)
fseek(fp, sizeof(BITMAPFILEHEADER), SEEK_SET);
// 2. 讀取信息頭 (40字節)
BITMAPINFOHEADER infoHead;
fread(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);
gBmpWidth = infoHead.biWidth;
gBmpHeight = infoHead.biHeight;
gBiBitCount = infoHead.biBitCount;
// 3. 計算行字節數 (4字節對齊)
int lineByte = (gBmpWidth * gBiBitCount / 8 + 3) / 4 * 4;
// 4. 讀取調色盤 (僅 8位圖)
if (8 == gBiBitCount) {
gPColorTable = (RGBQUAD*)malloc(sizeof(RGBQUAD) * 256);
fread(gPColorTable, sizeof(RGBQUAD), 256, fp); // 修正: 讀256項
}
// 5. 讀取像素數據
gPBmpBuf = (unsigned char*)malloc(gBmpHeight * lineByte);
fread(gPBmpBuf, 1, gBmpHeight * lineByte, fp);
fclose(fp);
return true;
}
storeBmpbool storeBmp(char *bmpName, unsigned char *imgBuf, int width, int height,
int bitCount, RGBQUAD *colorTable) {
if (!imgBuf) return false;
int lineByte = (width * bitCount / 8 + 3) / 4 * 4;
int colorTableSize = (bitCount == 8) ? 1024 : 0; // 256 * 4
FILE *fp = fopen(bmpName, "wb");
if (!fp) return false;
// 1. 文件頭
BITMAPFILEHEADER fileHead = {0};
fileHead.bfType = 0x4D42; // 'BM'
fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colorTableSize + lineByte * height;
fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colorTableSize;
fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
// 2. 信息頭
BITMAPINFOHEADER infoHead = {0};
infoHead.biSize = 40;
infoHead.biWidth = width;
infoHead.biHeight = height;
infoHead.biPlanes = 1;
infoHead.biBitCount = bitCount;
infoHead.biCompression = 0; // BI_RGB 無壓縮
infoHead.biSizeImage = lineByte * height;
fwrite(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);
// 3. 調色盤
if (bitCount == 8 && colorTable) {
fwrite(colorTable, sizeof(RGBQUAD), 256, fp);
}
// 4. 像素數據
fwrite(imgBuf, lineByte * height, 1, fp);
fclose(fp);
return true;
}
int main() {
char *src = "E:/test_pic.bmp";
char *dst = "E:/save_pic.bmp";
if (loadBmp(src) && storeBmp(dst, gPBmpBuf, gBmpWidth, gBmpHeight, gBiBitCount, gPColorTable)) {
printf("BMP 讀寫測試成功!\n");
} else {
printf("失敗\n");
}
// 內存釋放
if (gBiBitCount == 8) free(gPColorTable);
free(gPBmpBuf);
return 0;
}
| 項目 | 8位灰度圖 | 24位真彩色 | 32位帶Alpha |
|---|---|---|---|
| biBitCount | 8 | 24 | 32 |
| 調色盤 | 有 (256*RGBQUAD) | 無 | 無 |
| 像素存儲 | 索引值 (1字節) | BGR (3字節) | BGRA (4字節) |
| 行對齊 | 必須 | 必須 | 天然對齊 (4的倍數) |
(width * bitCount / 8 + 3) / 4 * 4 或位運算版 ((width * bitCount + 31) & ~31) / 8,切勿直接用 width * bytesPerPixel。fread(colorTable, sizeof(RGBQUAD), 256, fp),第三參數是項數非字節數。biHeight > 0 為底-up (掃描線從底部開始),讀取時注意行序;biHeight < 0 為頂-down。#pragma pack(1) 或逐字段讀寫。BMP 讀寫雖屬基礎,但涵蓋了文件格式解析、內存對齊、調色盤管理、二進制 I/O 等核心系統編程技能。掌握此流程,可輕鬆擴展至圖像旋轉、縮放、濾鏡、格式轉換 (BMP↔JPG/PNG) 等高級圖像處理任務。