首页 > 解决方案 > C++ GDI+ 选择调色板

问题描述

我正在玩 GDI+。尝试使用

pDC->SelectPalette(CPalette::FromHandle(hLogPal), FALSE);
pDC->RealizePalette();

代替

memcpy(newBitmapInfo + sizeof(BITMAPINFO), rgbquad, palettesize);

但似乎它 memcpy(newBitmapInfo + sizeof(BITMAPINFO), rgbquad, palettesize);与 SelectPalette 一起工作,但只有黑屏。

我认为可以从位图信息或托盘中使用有关颜色的信息。

所有代码:

void ConvertTo8BitImage(BYTE** pBitmapInfo, BYTE** imageData)
{
    Gdiplus::GdiplusStartupInput tmp;
    ULONG_PTR token;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);

    Gdiplus::Bitmap *source = Gdiplus::Bitmap::FromFile(L"D:/TestImage.bmp");
    Gdiplus::Bitmap *destination = source->Clone(0, 0, source->GetWidth(), source->GetHeight(),
        PixelFormat8bppIndexed);

    int width = source->GetWidth();
    int height = source->GetHeight();

    HBITMAP hBitmap;
    Gdiplus::Color color;
    destination->GetHBITMAP(color, &hBitmap);
    int palettesize = 256 * sizeof(RGBQUAD);

    CLSID clsid_bmp;
    CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &clsid_bmp);
    *pBitmapInfo = new BYTE[(sizeof(BITMAPINFO) + palettesize)];

    BITMAPINFO* ptr = (BITMAPINFO*)*pBitmapInfo;
    ptr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    ptr->bmiHeader.biWidth = width;
    ptr->bmiHeader.biHeight = height;
    ptr->bmiHeader.biPlanes = 1;

    ptr->bmiHeader.biBitCount = 8;
    ptr->bmiHeader.biCompression = BI_RGB;
    ptr->bmiColors[0].rgbRed = 0;
    DWORD size = ((width * 8 + 31) / 32) * 4 * height;

    *imageData = new BYTE[size];

    HDC hdc = GetDC(0);
    GetDIBits(hdc, hBitmap, 0, height, *imageData, (BITMAPINFO*)*pBitmapInfo, DIB_PAL_COLORS);
    ReleaseDC(0, hdc);

    Gdiplus::GdiplusShutdown(token);
}

void CMFCApplicationColorsView::OnDraw(CDC* pDC)
{
    CMFCApplicationColorsDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    BYTE *bitmapInfo = NULL;
    BYTE *imageData = NULL;

    ConvertTo8BitImage(&bitmapInfo, &imageData);

    int palettesize = 256 * sizeof(RGBQUAD);
    BYTE *newBitmapInfo = new BYTE[(sizeof(BITMAPINFO) + palettesize)];
    ZeroMemory(newBitmapInfo, (sizeof(BITMAPINFO) + palettesize));

    BITMAPINFO *ptr = (BITMAPINFO*)newBitmapInfo;
    ptr->bmiHeader.biBitCount = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biBitCount;
    ptr->bmiHeader.biClrImportant = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biClrImportant;
    ptr->bmiHeader.biClrUsed = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biClrUsed;
    ptr->bmiHeader.biCompression = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biCompression;
    ptr->bmiHeader.biHeight = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biHeight;
    ptr->bmiHeader.biPlanes = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biPlanes;
    ptr->bmiHeader.biSize = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biSize;
    ptr->bmiHeader.biSizeImage = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biSizeImage;
    ptr->bmiHeader.biWidth = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biWidth;
    ptr->bmiHeader.biXPelsPerMeter = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biXPelsPerMeter;
    ptr->bmiHeader.biYPelsPerMeter = ((BITMAPINFO*)bitmapInfo)->bmiHeader.biYPelsPerMeter;
    ptr->bmiColors[0] = ((BITMAPINFO*)bitmapInfo)->bmiColors[0];

    RGBQUAD rgbquad[256];
    memcpy(rgbquad, bitmapInfo + sizeof(BITMAPINFO), palettesize);
    //memcpy(newBitmapInfo + sizeof(BITMAPINFO), rgbquad, palettesize);

    NPLOGPALETTE pPal = (NPLOGPALETTE)LocalAlloc(LMEM_FIXED,
        (sizeof(LOGPALETTE) +
        (sizeof(PALETTEENTRY) * (palettesize))));

    pPal->palVersion = 0x300;
    pPal->palNumEntries = 256;
    for (int i = 0; i < 256; i++)
    {
        pPal->palPalEntry[i].peRed = rgbquad[i].rgbRed;
        pPal->palPalEntry[i].peGreen = rgbquad[i].rgbGreen;
        pPal->palPalEntry[i].peBlue = rgbquad[i].rgbBlue;
        pPal->palPalEntry[i].peFlags = 0;
    }

    HPALETTE hLogPal = CreatePalette((LPLOGPALETTE)pPal);

    pDC->SelectPalette(CPalette::FromHandle(hLogPal), FALSE);
    pDC->RealizePalette();

    StretchDIBits(pDC->GetSafeHdc(), 0, 0, 1920, 1080, 0, 0, 1920, 1080,
        imageData, ptr, DIB_PAL_COLORS, SRCCOPY);


    delete[] bitmapInfo;
    delete[] imageData;
}

标签: c++mfcgdi+bmp

解决方案


HBITMAP hBitmap;
Gdiplus::Color color;
destination->GetHBITMAP(color, &hBitmap);

您确实转换为 8 位位图,但是GetHBITMAP将返回与您的视频卡兼容的位图句柄,这可能是 32 位的。GDI+ 已经处理了调色板并返回了一个位图句柄,该句柄又变成了 32 位。HBITMAP可以直接绘制句柄,例如使用CreateCompatibleDCand BitBlt。因此不需要获取调色板并将其传递给 GDI,也不需要首先进行 8 位转换。

如果出于某种原因需要这样做,您可以从 32 位位图中获取位和调色板,将其放入 8 位位图中,然后使用StretchDIBits

您的代码中的主要问题是它应该使用DIB_RGB_COLORS标志GetDIBits/StretchDIBits,因为设备上下文很可能是 32 位的。两者都不需要SelectPalette/RealizePalette(除非它是 30 年前的 8 位显示器)

LockBits使用 直接从 GDI+ 获取位,使用直接获取调色板更有意义GetPalette,如下例所示。

抛开,source并且destination必须在退出前删除。

void draw(HDC hdc)
{
    Gdiplus::Bitmap *source = Gdiplus::Bitmap::FromFile(L"D:/TestImage.bmp");
    if(!source)
        return;
    int width = source->GetWidth();
    int height = source->GetHeight();

    Gdiplus::Bitmap *destination = source->Clone(0, 0, width, height,
        PixelFormat8bppIndexed);

    //get bitmap bits from GDI+
    Gdiplus::BitmapData data;
    Gdiplus::Rect rect(0, 0, width, height);
    destination->LockBits(&rect, Gdiplus::ImageLockModeRead,
        destination->GetPixelFormat(), &data);
    int bufsize = data.Stride * data.Height;
    BYTE *buf = new BYTE[bufsize];
    memcpy(buf, data.Scan0, bufsize);
    destination->UnlockBits(&data);

    //setup BITMAPINFO
    int bmpinfo_size = sizeof(BITMAPINFO) + 256 * 4;
    BITMAPINFO* bmpinfo = (BITMAPINFO*)new BYTE[bmpinfo_size];
    memset(bmpinfo, 0, bmpinfo_size);
    bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpinfo->bmiHeader.biWidth = width;
    bmpinfo->bmiHeader.biHeight = -height;
    bmpinfo->bmiHeader.biPlanes = 1;
    bmpinfo->bmiHeader.biBitCount = 8;
    bmpinfo->bmiHeader.biCompression = BI_RGB;
    bmpinfo->bmiHeader.biSizeImage = bufsize;

    //get palette from GDI+
    int palsize = destination->GetPaletteSize();
    Gdiplus::ColorPalette *palette = (Gdiplus::ColorPalette*)new BYTE[palsize];
    destination->GetPalette(palette, palsize);

    //set palette for BITMAPINFO
    memset(&bmpinfo->bmiColors[0], 0, 256 * 4);
    for(int i = 0; i < palette->Count; i++)
    {
        auto clr = Gdiplus::Color(palette->Entries[i]);
        bmpinfo->bmiColors[i].rgbRed = clr.GetR();
        bmpinfo->bmiColors[i].rgbGreen = clr.GetG();
        bmpinfo->bmiColors[i].rgbBlue = clr.GetB();
        bmpinfo->bmiColors[i].rgbReserved = 0;      
    }

    StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height,
        buf, bmpinfo, DIB_RGB_COLORS, SRCCOPY);

    delete[] buf;
    delete[] bmpinfo;
    delete[] palette;
    delete destination;
    delete source;
}

void CMFCApplicationColorsView::OnDraw(CDC* pDC)
{
    CMFCApplicationColorsDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    Gdiplus::GdiplusStartupInput tmp;
    ULONG_PTR token;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);

    draw(pDC->GetSafeHdc());

    Gdiplus::GdiplusShutdown(token);
}

推荐阅读