在 Windows 圖形程式設計中,裝置無關位圖(DIB)雖可直接繪製至螢幕,但若不轉換為裝置相關位圖(DDB),不僅繪圖速度大幅下降,更無法使用 BitBlt() 等高效 GDI 函式。本文詳細說明 DIB 轉 DDB 的標準流程、調色板處理細節,並提供可直接套用的 MFC 程式碼範例。
BitBlt、StretchBlt、TransparentBlt 等僅接受 DDB(HBITMAP)。LOGPALETTE 結構,填入 palVersion、palNumEntries 及各色彩項目(peRed/Green/Blue),再呼叫 CreatePalette()。SelectPalette() 將邏輯調色板選入 DC,隨即呼叫 RealizePalette() 同步至系統調色板。CreateDIBitmap(),傳入 DC、DIB 標頭、初始化旗標 CBM_INIT、像素資料指標與 BITMAPINFO。LOGPALETTE 記憶體。當 DIB 為 256 色且顯示裝置同為 256 色時,系統預設調色板(20 靜態色+ 236 可變色)可能與 DIB 色彩表不符,導致顏色錯位。解決之道是在繪圖前將建立好的邏輯調色板回傳給呼叫端,由呼叫端負責選入並實現,確保後續 BitBlt 使用正確映射。
HBITMAP DIBToDDB(HGLOBAL hDIB)
{
if (!hDIB) return NULL;
LPBITMAPINFOHEADER lpBi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
if (!lpBi) return NULL;
// 計算調色板項目數
int nColors = lpBi->biClrUsed ? lpBi->biClrUsed : (1 << lpBi->biBitCount);
// 取得像素資料起始位址
LPBITMAPINFO lpBmInfo = (LPBITMAPINFO)lpBi;
LPVOID lpDIBBits;
if (lpBmInfo->bmiHeader.biBitCount > 8) {
lpDIBBits = (LPVOID)((LPDWORD)(lpBmInfo->bmiColors + lpBmInfo->bmiHeader.biClrUsed) +
((lpBmInfo->bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0));
} else {
lpDIBBits = (LPVOID)(lpBmInfo->bmiColors + nColors);
}
CClientDC dc(NULL);
HBITMAP hBitmap = NULL;
CPalette pal, *pOldPal = NULL;
// 僅在 256 色以下且裝置支援調色板時建立
if (nColors <= 256 && (dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE)) {
UINT nSize = sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * nColors;
LOGPALETTE* pLP = (LOGPALETTE*)new BYTE[nSize];
pLP->palVersion = 0x300;
pLP->palNumEntries = (WORD)nColors;
for (int i = 0; i < nColors; i++) {
pLP->palPalEntry[i].peRed = lpBmInfo->bmiColors[i].rgbRed;
pLP->palPalEntry[i].peGreen = lpBmInfo->bmiColors[i].rgbGreen;
pLP->palPalEntry[i].peBlue = lpBmInfo->bmiColors[i].rgbBlue;
pLP->palPalEntry[i].peFlags = 0;
}
pal.CreatePalette(pLP);
delete[] pLP;
pOldPal = dc.SelectPalette(&pal, FALSE);
dc.RealizePalette();
}
// 建立 DDB
hBitmap = ::CreateDIBitmap(dc.GetSafeHdc(), &lpBi, CBM_INIT, lpDIBBits, lpBmInfo, DIB_RGB_COLORS);
// 還原調色板並釋放
if (pOldPal) dc.SelectPalette(pOldPal, FALSE);
GlobalUnlock(hDIB);
return hBitmap;
}
GlobalLock/GlobalUnlock 必須成對出現,否則導致記憶體洩漏或存取違規。lpDIBBits 時務必考慮填補位元組。BI_BITFIELDS 會在色彩表後額外佔用 3 個 DWORD(紅/綠/藍遮罩),指標運算需加 3×4 位元組。CClientDC(NULL) 取得螢幕 DC,確保調色板實現於正確裝置上下文。掌握此轉換流程後,可進一步實作:
完整掌握 DIB→DDB 轉換,是 Windows 圖形程式設計從入門邁向進階的關鍵門檻之一。