在 Windows 程式開發中,直接使用 GDI 函數(如 BitBlt、StretchBlt)顯示 BMP 位圖雖然簡單,但存在記憶體佔用大、顯示速度慢、縮放失真嚴重,以及在低色深設備上顯示高色深圖像失真等缺點。本文將詳細解析 BMP 文件結構,並介紹如何利用 Video for Windows (VFW) 的 DrawDib 系列函數實現高效、平滑的位圖縮放顯示。
BMP 文件由四部分組成:文件頭、位圖信息頭、調色板(顏色表)和位圖數據。
BITMAPFILEHEADER)包含文件類型、大小、保留字及數據偏移量。
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 必須為 'BM' (0x4D42)
DWORD bfSize; // 文件大小(字節)
WORD bfReserved1; // 保留,必須為 0
WORD bfReserved2; // 保留,必須為 0
DWORD bfOffBits; // 位圖數據起始偏移量
} BITMAPFILEHEADER;
BITMAPINFOHEADER)描述位圖尺寸、色深、壓縮方式、分辨率等核心屬性。
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 結構體大小
LONG biWidth; // 寬度(像素)
LONG biHeight; // 高度(像素),正值表示底-up DIB
WORD biPlanes; // 色平面數,必須為 1
WORD biBitCount; // 色深:1, 4, 8, 24
DWORD biCompression; // 壓縮類型:BI_RGB(0), BI_RLE8(1), BI_RLE4(2)
DWORD biSizeImage; // 圖像數據大小
LONG biXPelsPerMeter; // 水平分辨率
LONG biYPelsPerMeter; // 垂直分辨率
DWORD biClrUsed; // 實際使用顏色數
DWORD biClrImportant; // 重要顏色數
} BITMAPINFOHEADER;
RGBQUAD)僅當 biBitCount 為 1, 4, 8 時存在。每個條目定義一種顏色(BGR 順序 + 保留字)。
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; // 必須為 0
} RGBQUAD;
((biWidth * biBitCount + 31) / 32) * 4GlobalAlloc 分配記憶體讀取文件CreateDIBitmap 創建 DDB(設備相關位圖)CreateCompatibleDC),選入位圖BitBlt (1:1) 或 StretchBlt (縮放) 繪製DeleteObject 釋放資源主要缺點:
StretchBlt 縮放時DrawDib 是 VFW 提供的高性能繪圖 API,支持抖動、色彩映射、高效縮放,直接操作 DIB 數據,無需創建 DDB。
DrawDibOpen() 獲取 HDRAWDIB 句柄mpBitsSrc)BITMAPINFOHEADER 指針 (mpBitmapInfo) 及數據偏移 (bfOffBits)OnPaint 中調用 DrawDibRealize 實現調色板映射,隨後調用 DrawDibDraw 繪製DrawDibClose() 釋放資源GraphicDraw):void CViewBitmap::GraphicDraw(void) {
CClientDC dc(this);
BITMAPFILEHEADER* pBmpHeader = (BITMAPFILEHEADER*)mpBitsSrc;
ULONG bfOffBits = pBmpHeader->bfOffBits;
if (m_hDrawDib == NULL || mDispMethod == 0) {
// 傳統 GDI 方式 (略)
} else {
// DrawDib 高性能方式
DrawDibRealize(m_hDrawDib, dc.GetSafeHdc(), TRUE);
if (mFullViewTog == 0) { // 1:1 顯示
int w = min(mDispR.Width(), mpBitmapInfo->biWidth);
int h = min(mDispR.Height(), mpBitmapInfo->biHeight);
DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(),
0, 0, w, h,
mpBitmapInfo, (LPVOID)(mpBitsSrc + bfOffBits),
mPos.x, mPos.y, w, h, 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; // DrawDib 句柄
HANDLE m_hBitsSrc; // 文件內存句柄
LPSTR m_pBitsSrc; // 文件內存指針
BITMAPINFOHEADER* m_pBitmapInfo; // 信息頭指針
m_hDrawDib = DrawDibOpen();if(m_hDrawDib) DrawDibClose(m_hDrawDib);Bitmap0.h/.cpp (包含 CViewBitmap) 到專案OnViewBitmap:
void CMainFrame::OnViewBitmap() {
CViewBitmap* pView = new CViewBitmap(_T("BITMAP.BMP"), this);
pView->ShowWindow(SW_SHOW);
}
#include "Bitmap0.h"BITMAP.BMP 放入執行目錄DrawDib 技術雖屬早期 VFW 技術,但其核心思想——直接 DIB 操作、硬件加速抖動縮放——在現代圖形引擎中依然具有參考價值。對於維護舊系統或嵌入式 Windows 開發,這仍是一套經典高效的方案。