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

CImageList 顏色失真問題:橙色變黃色的根因與 LoadResourceBitmap 完美解決方案

     編輯:關於C語言

現象:CImageList 載入位圖,橙色顯示為黃色

使用 CImageList::Create + Add 載入工具列/樹狀圖示位圖時,發現**橙色 (RGB 255,165,0) 變成黃色 (255,255,0)**,其他顏色正常。

根因:預設調色板限制與色彩壓縮

  • CImageList 預設使用**系統預設調色板 (20 色系統色 + 236 色預設色)**,共 256 色。
  • **橙色不在系統 20 色中**,映射到最接近的黃色。
  • 若位圖為 256 色 (8-bit) 且含自定義調色板,直接 Add 會忽略調色板,強制映射到系統調色板。

解決方案:LoadResourceBitmap + CreateDIBPalette 保留原始調色板

核心思路:**從資源直接載入 DIB Section,提取調色板,建立邏輯調色板,Select/Realize 到 DC,再 CreateDIBitmap**。

完整實作代碼


// 工具函數:從資源載入位圖並返回邏輯調色板
HBITMAP LoadResourceBitmap(HINSTANCE hInstance, LPSTR lpString, HPALETTE* lphPalette)
{
    HRSRC hRsrc = FindResource(hInstance, lpString, RT_BITMAP);
    if (!hRsrc) return NULL;

    HGLOBAL hGlobal = LoadResource(hInstance, hRsrc);
    if (!hGlobal) return NULL;

    LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)LockResource(hGlobal);
    if (!lpbi) return NULL;

    HDC hdc = GetDC(NULL);
    int iNumColors = 0;
    *lphPalette = CreateDIBPalette((LPBITMAPINFO)lpbi, &iNumColors);

    if (*lphPalette) {
        SelectPalette(hdc, *lphPalette, FALSE);
        RealizePalette(hdc);
    }

    HBITMAP hBitmapFinal = CreateDIBitmap(hdc,
        lpbi, CBM_INIT,
        (LPSTR)lpbi + lpbi->biSize + iNumColors * sizeof(RGBQUAD),
        (LPBITMAPINFO)lpbi, DIB_RGB_COLORS);

    ReleaseDC(NULL, hdc);
    // UnlockResource/FreeResource 不需在 Win32 顯式調用
    return hBitmapFinal;
}

// 內部:從 DIB 創建邏輯調色板
HPALETTE CreateDIBPalette(LPBITMAPINFO lpbmi, LPINT lpiNumColors)
{
    LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)lpbmi;
    if (lpbi->biBitCount <= 8)
        *lpiNumColors = 1 << lpbi->biBitCount;
    else
        *lpiNumColors = 0; // 24/32-bit 無需調色板

    if (lpbi->biClrUsed > 0)
        *lpiNumColors = lpbi->biClrUsed;

    if (*lpiNumColors == 0) return NULL;

    HANDLE hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * (*lpiNumColors));
    LPLOGPALETTE lpPal = (LPLOGPALETTE)GlobalLock(hLogPal);
    lpPal->palVersion = 0x300;
    lpPal->palNumEntries = *lpiNumColors;

    for (int i = 0; i < *lpiNumColors; ++i) {
        lpPal->palPalEntry[i].peRed   = lpbmi->bmiColors[i].rgbRed;
        lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen;
        lpPal->palPalEntry[i].peBlue  = lpbmi->bmiColors[i].rgbBlue;
        lpPal->palPalEntry[i].peFlags = 0;
    }
    GlobalUnlock(hLogPal);

    HPALETTE hPal = CreatePalette(lpPal);
    GlobalFree(hLogPal);
    return hPal;
}

在 CImageList 中使用


void CMyTreeView::OnInitialUpdate()
{
    CTreeView::OnInitialUpdate();

    HPALETTE hPal = NULL;
    HBITMAP hBmp = LoadResourceBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_TREE_IMAGES), &hPal);
    if (hBmp) {
        m_imageList.Create(16, 16, ILC_COLOR8 | ILC_MASK, 10, 5); // 8-bit 色 + 遮罩
        m_imageList.Add(CBitmap::FromHandle(hBmp), (CBitmap*)NULL); // 遮罩可另載入或用透明色
        GetTreeCtrl().SetImageList(&m_imageList, TVSIL_NORMAL);

        // 重要:若 TreeCtrl 所在 DC 需實現調色板,覆寫 OnQueryNewPalette/OnPaletteChanged
        // 參考下方「視窗調色板實現」
    }
}

視窗調色板實現:讓控制項正確顯示 256 色位圖

若父視窗/控制項不處理調色板訊息,系統不會實現您的調色板,顏色仍會錯誤。


// 在包含 CImageList 的視窗類別 (如 CTreeView, CListView, CFormView)
BOOL CMyTreeView::OnQueryNewPalette()
{
    if (m_hPal) { // 成員 HPALETTE m_hPal 保存 LoadResourceBitmap 返回的調色板
        CClientDC dc(this);
        SelectPalette(dc, m_hPal, FALSE);
        UINT u = RealizePalette(dc);
        if (u) Invalidate(); // 色彩變化需重繪
        return TRUE;
    }
    return FALSE;
}

void CMyTreeView::OnPaletteChanged(CWnd* pFocusWnd)
{
    if (pFocusWnd != this && m_hPal) {
        CClientDC dc(this);
        SelectPalette(dc, m_hPal, FALSE);
        RealizePalette(dc);
        Invalidate();
    }
}

替代方案對比

方案優點缺點適用場景
LoadResourceBitmap + 調色板完美保留 256 色位圖原始色彩需處理 WM_QUERYNEWPALETTE/WM_PALETTECHANGED、代碼較多高色彩保真需求、舊版 256 色資源
使用 24/32-bit 位圖 (ILC_COLOR24/32)無調色板問題、真彩色、現代系統預設記憶體佔用大 3/4 倍、舊系統 (Win95/NT4) 可能不支援新專案、資源可重製、不需相容極舊系統
CImageList::Add (CBitmap*, mask) 直接加載代碼簡單預設映射系統調色板,自定義色彩失真僅使用系統 20 色、或可接受色彩近似

現代建議:直接使用 32-bit 位圖 (ILC_COLOR32)


m_imageList.Create(16, 16, ILC_COLOR32 | ILC_MASK, 10, 5); // 32-bit ARGB
// 位圖資源製作為 32-bit (含 Alpha 通道),或載入後轉換
// 無需調色板處理,Win2000+ 完美支援
  • Visual Studio 資源編輯器支援導入 32-bit PNG/BMP。
  • Alpha 通道實現真正透明/半透明,優於遮罩位圖。
  • 記憶體壓力在現代系統可忽略 (圖示通常 16x16/32x32)。

總結

CImageList 橙色變黃色是**8-bit 位圖調色板映射到系統預設調色板**導致。根治方案:**LoadResourceBitmap 提取 DIB 調色板 + CreateDIBPalette 建立邏輯調色板 + 視窗處理 WM_QUERYNEWPALETTE/WM_PALETTECHANGED 實現調色板**。現代專案**首選 32-bit 位圖 (ILC_COLOR32) 徹底避開調色板問題**,兼具真彩色與 Alpha 透明。

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