在 Windows 程式設計中,BMP(Bitmap)作為最基礎的圖像格式,雖然結構簡單,但若採用傳統 GDI 方式載入顯示,往往面臨記憶體佔用大、繪圖速度慢、縮放失真嚴重等問題,尤其在低色深裝置上顯示高色深圖像時,色彩失真更為明顯。本文將系統剖析 BMP 文件結構,並介紹如何利用視訊函數(Video Functions)實現高效、無失真的位圖顯示與平滑縮放。
標準 BMP 文件由四大部分依序組成:
其中前三部分統稱為「位圖資訊」,僅像素資料區大小隨圖像尺寸與色深變化。
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 檔案類型,必須為 'BM' (0x4D42)
DWORD bfSize; // 檔案總大小(位元組)
WORD bfReserved1; // 保留,必須為 0
WORD bfReserved2; // 保留,必須為 0
DWORD bfOffBits; // 像素資料起始偏移量(相對文件開頭)
} BITMAPFILEHEADER;
bfOffBits 是定位像素資料的關鍵,讀取時直接 Seek 到此位置即可開始解碼。
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 結構體大小,固定 40
LONG biWidth; // 圖像寬度(像素),正數
LONG biHeight; // 圖像高度(像素),正數=底部優先,負數=頂部優先
WORD biPlanes; // 色板數,固定 1
WORD biBitCount; // 色深:1/4/8/16/24/32
DWORD biCompression; // 壓縮類型:BI_RGB(0), BI_RLE8(1), BI_RLE4(2), BI_BITFIELDS(3)
DWORD biSizeImage; // 像素資料大小(可為 0 表示未壓縮)
LONG biXPelsPerMeter; // 水平解析度
LONG biYPelsPerMeter; // 垂直解析度
DWORD biClrUsed; // 實際使用的調色盤索引數
DWORD biClrImportant; // 重要顏色數(0 表示全部重要)
} BITMAPINFOHEADER;
注意:biHeight 為負值時表示採用「頂部優先」儲存順序(Top-down DIB),此時無需翻轉即可直接繪製。
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 藍色分量 0-255
BYTE rgbGreen; // 綠色分量 0-255
BYTE rgbRed; // 紅色分量 0-255
BYTE rgbReserved; // 保留,必須為 0
} RGBQUAD;
調色盤條目數由 biBitCount 決定:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1]; // 柔性陣列,實際大小由 biClrUsed 決定
} BITMAPINFO;
BMP 採用 4 位元組邊界對齊:每行像素資料長度必須是 4 的倍數,不足者以 0x00 填充。計算公式:
行寬位元組數 = ((biWidth * biBitCount + 31) / 32) * 4
儲存順序預設為 從下往上(Bottom-up),即文件中第一行像素對應圖像最底行。若 biHeight < 0 則為從上往下。
傳統 StretchBlt 縮放品質較差。利用 DirectDraw / Direct2D / GDI+ 等視訊介面,可發揮 GPU 硬體加速:
// 1. 讀取文件頭與資訊頭
BITMAPFILEHEADER fh; BITMAPINFOHEADER ih;
fread(&fh, 1, sizeof(fh), fp);
fread(&ih, 1, sizeof(ih), fp);
// 2. 讀取調色盤(若有)
RGBQUAD *palette = nullptr;
if (ih.biBitCount <= 8) {
int nColors = (ih.biClrUsed ? ih.biClrUsed : (1 << ih.biBitCount));
palette = new RGBQUAD[nColors];
fread(palette, sizeof(RGBQUAD), nColors, fp);
}
// 3. 定位像素資料並一次性讀入
fseek(fp, fh.bfOffBits, SEEK_SET);
int stride = ((ih.biWidth * ih.biBitCount + 31) >> 5) << 2;
BYTE *bits = new BYTE[stride * abs(ih.biHeight)];
fread(bits, 1, stride * abs(ih.biHeight), fp);
// 4. 建立 DIBSection 或直接上傳至 GPU 紋理
// ... 後續交由 Direct2D/GDI+ 繪製與縮放
掌握 BMP 結構是圖形程式設計的基礎;結合現代視訊 API,可輕鬆解決記憶體、速度、失真三大痛點,實現專業級圖像瀏覽與編輯體驗。