傳統方式顯示 BMP 位圖存在記憶體佔用大、速度慢、縮放失真嚴重、低色深裝置顯示高色深圖像失真等缺點。本文詳細解析 BMP 檔案結構,並介紹使用視訊函數 DrawDib 實現高效、低失真的位圖縮放顯示方案,附完整 MFC 類別實作程式碼。
BMP 檔案由四部分組成:檔案頭、位圖資訊頭、調色盤、圖像資料。
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 必須為 'BM'
DWORD bfSize; // 檔案大小 (bytes)
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; // 壓縮類型:0=無, 1=RLE8, 2=RLE4
DWORD biSizeImage; // 影像大小
LONG biXPelsPerMeter; // 水平解析度
LONG biYPelsPerMeter; // 垂直解析度
DWORD biClrUsed; // 實際使用顏色數
DWORD biClrImportant; // 重要顏色數
} BITMAPINFOHEADER;
僅當 biBitCount 為 1、4、8 時存在,分別含 2、16、256 個 RGBQUAD 項目:
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; // 必須為 0
} RGBQUAD;
24 位真彩色圖像無調色盤,像素值直接為 RGB 值。
DataSizePerLine = ((biWidth * biBitCount + 31) / 8) / 4 * 4DataSize = DataSizePerLine * biHeightGlobalAlloc 分配記憶體CreateDIBitmap 建立顯示用位圖,CreateCompatibleDC 建立相容 DCBitBlt 或 StretchBlt 繪製DeleteObject 釋放資源缺點:速度慢、記憶體佔用大、縮小失真、低色深裝置顯示高色深圖像失真。
DrawDib 系列函數提供:
DrawDibOpen()OnPaint 中呼叫 DrawDibRealize()、DrawDibDraw()DrawDibClose()HDRAWDIB m_hDrawDib; // 視訊控制代碼
HANDLE m_hBitsSrc; // 位圖檔案記憶體控制代碼
LPSTR m_pBitsSrc; // 位圖檔案記憶體位址
BITMAPINFOHEADER* m_pBitmapInfo; // 位圖資訊頭指標
CViewBitmap::CViewBitmap() {
m_hDrawDib = DrawDibOpen();
}
CViewBitmap::~CViewBitmap() {
if (m_hDrawDib != NULL) {
DrawDibClose(m_hDrawDib);
m_hDrawDib = NULL;
}
}
void CViewBitmap::GraphicDraw(void) {
CClientDC dc(this);
BITMAPFILEHEADER* pBitmapFileHeader = (BITMAPFILEHEADER*)m_pBitsSrc;
ULONG bfOffBits = pBitmapFileHeader->bfOffBits;
if (m_hDrawDib == NULL || mDispMethod == 0) {
// 傳統 GDI 方式
HBITMAP hBitmap = ::CreateDIBitmap(dc.m_hDC, m_pBitmapInfo, CBM_INIT,
m_pBitsSrc + bfOffBits, (LPBITMAPINFO)m_pBitmapInfo, DIB_RGB_COLORS);
HDC hMemDC = ::CreateCompatibleDC(dc.m_hDC);
HBITMAP hOld = (HBITMAP)::SelectObject(hMemDC, hBitmap);
// ... 座標裁切邏輯 ...
if (mFullViewTog == 0)
::BitBlt(dc.m_hDC, 0, 0, mDispR.Width(), mDispR.Height(),
hMemDC, mPos.x, mPos.y, SRCCOPY);
else
::StretchBlt(dc.m_hDC, 0, 0, mDispR.Width(), mDispR.Height(),
hMemDC, 0, 0, m_pBitmapInfo->biWidth, m_pBitmapInfo->biHeight, SRCCOPY);
::DeleteObject(::SelectObject(hMemDC, hOld));
} else {
// DrawDib 視訊函數方式
DrawDibRealize(m_hDrawDib, dc.GetSafeHdc(), TRUE);
// ... 座標裁切邏輯 ...
if (mFullViewTog == 0) {
CPoint Wid(mDispR.Width(), mDispR.Height());
if (Wid.x > m_pBitmapInfo->biWidth) Wid.x = m_pBitmapInfo->biWidth;
if (Wid.y > m_pBitmapInfo->biHeight) Wid.y = m_pBitmapInfo->biHeight;
DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(), 0, 0, Wid.x, Wid.y,
m_pBitmapInfo, (LPVOID)(m_pBitsSrc + bfOffBits),
mPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);
} else {
DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(), 0, 0,
mDispR.Width(), mDispR.Height(),
m_pBitmapInfo, (LPVOID)(m_pBitsSrc + bfOffBits),
0, 0, m_pBitmapInfo->biWidth, m_pBitmapInfo->biHeight,
DDF_BACKGROUNDPAL);
}
}
}
Bitmap0.h、Bitmap0.cpp 至專案ID_VIEW_BITMAP 項目CMainFrame 加入 OnViewBitmap 命令處理器new CViewBitmap("BITMAP.BMP", this)->ShowWindow(TRUE);DrawDib 視訊函數有效解決了傳統 GDI 顯示 BMP 的諸多痛點,特別適合需要頻繁縮放、跨色深環境顯示的應用場景。完整原始碼可作為 MFC 圖像處理模組的基礎參考。