在傳統的 BMP 位圖顯示方式中,常面臨佔用記憶體大、顯示速度慢、縮放失真嚴重,以及在低色深設備上顯示高色深圖像失真等問題。本文將詳細解析 BMP 文件結構,並介紹如何利用視頻函數 DrawDib 實現高效、平滑的位圖縮放顯示,有效解決上述痛點。
BMP 文件主要由四大部分組成:文件頭、位圖信息頭、顏色表(調色盤)以及位圖數據。
文件頭包含文件類型、大小及數據偏移等核心資訊,結構定義如下:
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 文件類型,必須為 "BM"
DWORD bfSize; // 文件大小(位元組)
WORD bfReserved1; // 保留字,必須為 0
WORD bfReserved2; // 保留字,必須為 0
DWORD bfOffBits; // 位圖數據起始偏移量(相對文件頭)
} BITMAPFILEHEADER;
記錄位圖的尺寸、色深、壓縮方式、分辨率等關鍵屬性:
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 結構體大小
LONG biWidth; // 寬度(像素)
LONG biHeight; // 高度(像素)
WORD biPlanes; // 目標設備平面數,必須為 1
WORD biBitCount; // 每像素位數 (1, 4, 8, 24)
DWORD biCompression; // 壓縮類型 (BI_RGB, BI_RLE8, BI_RLE4)
DWORD biSizeImage; // 圖像大小(位元組)
LONG biXPelsPerMeter; // 水平分辨率
LONG biYPelsPerMeter; // 垂直分辨率
DWORD biClrUsed; // 實際使用顏色數
DWORD biClrImportant; // 重要顏色數
} BITMAPINFOHEADER;
顏色表 由 RGBQUAD 結構組成,定義藍、綠、紅三色分量。當 biBitCount 為 1、4、8 時分別擁有 2、16、256 個表項;24 位真彩色無顏色表。
位圖數據 採用底部優先、從左到右的行順序存儲。Windows 規定每掃描行位元組數必須為 4 的倍數(不足補 0)。計算公式:
DataSizePerLine = (biWidth * biBitCount + 31) / 8;
DataSizePerLine = (DataSizePerLine + 3) & ~3; // 4 字節對齊
DataSize = DataSizePerLine * biHeight;
常規流程:GlobalAlloc 分配內存 → 讀取文件 → CreateDIBitmap 創建 DDB → BitBlt/StretchBlt 繪製 → DeleteObject 釋放。
StretchBlt 缺乏高質量插值算法。DrawDib 系列 API 屬於 Video for Windows (VFW) 組件,專為高性能圖形繪製設計。
DDF_BACKGROUNDPAL 標誌自動處理高色深到低色深的映射。DrawDibOpen() 獲取句柄 HDRAWDIB。BITMAPINFOHEADER 指針與像素數據偏移。OnPaint 中調用 DrawDibRealize 實現調色盤,隨後呼叫 DrawDibDraw 繪製。DrawDibClose 釋放資源。void CVIEwBitmap::GraphicDraw(void) {
CClientDC dc(this);
BITMAPFILEHEADER* pBitmapFileHeader = (BITMAPFILEHEADER*)mpBitsSrc;
ULONG bfoffBits = pBitmapFileHeader->bfOffBits;
if (m_hDrawDib == NULL || mDispMethod == 0) {
// 傳統 GDI 繪製邏輯...
} else {
// 邊界檢查 mPos...
DrawDibRealize(m_hDrawDib, dc.GetSafeHdc(), TRUE);
if (mFullViewTog == 0) { // 1:1 顯示
CPoint Wid(mDispR.Width(), mDispR.Height());
Wid.x = min(Wid.x, mpBitmapInfo->biWidth);
Wid.y = min(Wid.y, mpBitmapInfo->biHeight);
DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(),
0, 0, Wid.x, Wid.y,
mpBitmapInfo, (LPVOID)(mpBitsSrc + bfoffBits),
mPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);
} else { // 全屏縮放
DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(),
0, 0, mDispR.Width(), mDispR.Height(),
mpBitmapInfo, (LPVOID)(mpBitsSrc + bfoffBits),
0, 0, mpBitmapInfo->biWidth, mpBitmapInfo->biHeight, DDF_BACKGROUNDPAL);
}
}
}
HDRAWDIB m_hDrawDib:視頻繪製句柄。HANDLE mhBitsSrc / LPSTR mpBitsSrc:文件內存句柄與指針。BITMAPINFOHEADER* mpBitmapInfo:信息頭指針。CRect mDispR, CPoint mPos:控制顯示區域與滾動偏移。BOOL mFullViewTog, mDispMethod:全屏/1:1 切換、繪製方式切換。Bitmap0.h/cpp。CMainFrame::OnViewBitmap 中實例化並顯示視圖:pViewBitmap = new CVIEwBitmap("BITMAP.BMP", this); pViewBitmap->ShowWindow(TRUE);通過 DrawDib 替代傳統 GDI 繪製,我們以極低的代碼修改成本,獲得了卓越的性能與視覺質量。這不僅解決了 BMP 顯示的歷史難題,也為 MFC 應用中的圖像處理提供了專業級解決方案。讀者可進一步優化縮放比例保持邏輯,或擴展支持更多圖片格式。