首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

DIB 轉 DDB 完整教學:邏輯調色板建立與效能優化關鍵

     編輯:關於C語言

在 Windows 圖形程式設計中,裝置無關位圖(DIB)雖可直接繪製至螢幕,但若不轉換為裝置相關位圖(DDB),不僅繪圖速度大幅下降,更無法使用 BitBlt() 等高效 GDI 函式。本文詳細說明 DIB 轉 DDB 的標準流程、調色板處理細節,並提供可直接套用的 MFC 程式碼範例。

為何必須轉換?

  • 效能:DDB 位於顯示卡記憶體,Blit 操作由硬體加速;DIB 需經 CPU 軟體光柵化。
  • 功能相容:BitBlt、StretchBlt、TransparentBlt 等僅接受 DDB(HBITMAP)。
  • 色彩正確性:在 256 色裝置上,未正確實現調色板會導致色彩失真。

四步驟標準流程

  1. 建立邏輯調色板:依據 DIB 色彩表配置 LOGPALETTE 結構,填入 palVersion、palNumEntries 及各色彩項目(peRed/Green/Blue),再呼叫 CreatePalette()。
  2. 選入並實現調色板:以 SelectPalette() 將邏輯調色板選入 DC,隨即呼叫 RealizePalette() 同步至系統調色板。
  3. 建立 DDB:呼叫 CreateDIBitmap(),傳入 DC、DIB 標頭、初始化旗標 CBM_INIT、像素資料指標與 BITMAPINFO。
  4. 釋放資源:銷毀邏輯調色板物件,釋放 LOGPALETTE 記憶體。

關鍵細節:256 色環境下的色彩修正

當 DIB 為 256 色且顯示裝置同為 256 色時,系統預設調色板(20 靜態色+ 236 可變色)可能與 DIB 色彩表不符,導致顏色錯位。解決之道是在繪圖前將建立好的邏輯調色板回傳給呼叫端,由呼叫端負責選入並實現,確保後續 BitBlt 使用正確映射。

完整函式實作(MFC 版)

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 必須成對出現,否則導致記憶體洩漏或存取違規。
  • 位元對齊:DIB 每掃描列以 4 位元組對齊,計算 lpDIBBits 時務必考慮填補位元組。
  • 壓縮格式:BI_BITFIELDS 會在色彩表後額外佔用 3 個 DWORD(紅/綠/藍遮罩),指標運算需加 3×4 位元組。
  • DC 類型:建議使用 CClientDC(NULL) 取得螢幕 DC,確保調色板實現於正確裝置上下文。

延伸應用

掌握此轉換流程後,可進一步實作:

  • DIB 縮放/旋轉後再轉 DDB 顯示
  • 離屏繪圖(Off-screen Drawing)雙緩衝機制
  • 列印輸出時將 DIB 轉為高解析度 DDB

完整掌握 DIB→DDB 轉換,是 Windows 圖形程式設計從入門邁向進階的關鍵門檻之一。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved