BMP 是 Windows 原生無壓縮位圖格式,結構簡單、相容性佳。本文提供可直接生產使用的 C 函數,詳細解析 BITMAPFILEHEADER、BITMAPINFOHEADER 各欄位含義、對齊陷阱與跨平台注意事項。
+------------------------+ | BITMAPFILEHEADER (14B) | 檔案類型、大小、資料偏移 +------------------------+ | BITMAPINFOHEADER (40B) | 寬高、位深、壓縮、解析度 +------------------------+ | 像素資料 (width*height*3) | BGR 順序,每行 4 位元組對齊 +------------------------+
#include// BITMAPFILEHEADER, BITMAPINFOHEADER #include #include typedef unsigned char uchar; void savebmp(uchar *pdata, const char *bmp_file, int width, int height) { // 1. 計算資料大小(24-bit RGB,每像素 3 位元組) int row_stride = ((width * 3 + 3) / 4) * 4; // 每行 4 位元組對齊 int data_size = row_stride * height; // 2. 填充檔案頭 BITMAPFILEHEADER bfh = {0}; bfh.bfType = 0x4D42; // 'BM' bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + data_size; bfh.bfReserved1 = 0; bfh.bfReserved2 = 0; bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // 3. 填充資訊頭 BITMAPINFOHEADER bih = {0}; bih.biSize = sizeof(BITMAPINFOHEADER); bih.biWidth = width; bih.biHeight = -height; // 負高度 = 自上而下 (Top-down DIB),避免圖片倒置 bih.biPlanes = 1; bih.biBitCount = 24; // 24-bit RGB bih.biCompression = BI_RGB; // 不壓縮 bih.biSizeImage = data_size; bih.biXPelsPerMeter = 2835; // 72 DPI ≈ 2835 px/m bih.biYPelsPerMeter = 2835; bih.biClrUsed = 0; bih.biClrImportant = 0; // 4. 寫入檔案 FILE *fp = fopen(bmp_file, "wb"); if (!fp) return; fwrite(&bfh, sizeof(bfh), 1, fp); fwrite(&bih, sizeof(bih), 1, fp); fwrite(pdata, data_size, 1, fp); fclose(fp); }
BMP 規定每掃描行大小必須是 4 位元組倍數。
int row_stride = ((width * 3 + 3) / 4) * 4; // 向上取整到 4 的倍數
例如寬度 10:10×3=30 → 對齊為 32 位元組,每行補 2 位元組填充。
BMP 預設從左下角開始掃描(Bottom-up)。設定 biHeight = -height 宣告為 Top-down DIB,像素資料按從上到下順序存放,直接對應常規影像陣列 data[y*width + x],免去垂直翻轉。
24-bit BMP 像素順序為 Blue, Green, Red。輸入資料若為 RGB,寫入前需交換 R/B 通道:
for (int i = 0; i < width * height; ++i) {
uchar tmp = pdata[i*3];
pdata[i*3] = pdata[i*3+2];
pdata[i*3+2] = tmp;
}
Linux/GCC 預設 4 位元組對齊,sizeof(BITMAPFILEHEADER) 可能為 16 而非 14。解決方案:
#pragma pack(push, 1)
typedef struct { ... } BITMAPFILEHEADER;
#pragma pack(pop)
// 或使用 __attribute__((packed)) (GCC/Clang)
int main() {
int w = 256, h = 256;
uchar *img = malloc(w * h * 3);
for (int y = 0; y < h; ++y)
for (int x = 0; x < w; ++x) {
int idx = (y * w + x) * 3;
img[idx] = x; // B
img[idx+1] = y; // G
img[idx+2] = (x+y)/2; // R
}
savebmp(img, "gradient.bmp", w, h);
free(img);
return 0;
}
| 現象 | 原因 | 修正 |
|---|---|---|
| 圖片倒置 | biHeight 為正 | 設為 -height |
| 顏色錯亂(紅藍互換) | RGB 順序寫入 | 轉換為 BGR |
| 右側有雜訊條紋 | 未處理行對齊 | 計算 row_stride,寫入時補齊 |
| 檔案無法開啟 | bfSize/bfOffBits 計算錯 | 嚴格按結構體大小+資料大小計算 |
| Linux 下寫入損壞 | 結構體對齊導致頭部膨脹 | 加 #pragma pack(1) |
掌握 BMP 格式細節,不僅能實現零依賴影像輸出,更能深入理解位圖資料在記憶體與磁碟上的真實佈局,為圖形程式打下堅實基礎。