0

I am playing with GDI+. Trying to use

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

instead of

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

But it seem that with it's working with memcpy(newBitmapInfo + sizeof(BITMAPINFO), rgbquad, palettesize); but with SelectPalette only black screen.

I thought that information about color can be used from bitmapinfo or from pallet.

All code:

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œur
  • 37,241
  • 25
  • 195
  • 267
A191919
  • 3,422
  • 7
  • 49
  • 93
  • Its been too long ago, but GetDIBits() looks borken. No palette was selected into the HDC. Doing this *correctly* is quite convoluted, you have to calculate a palette that is the best match with the existing colors in the bitmap so the least amount of color shifts are produced. Being able to dump this kind of code 25 years ago was quite a relieve. – Hans Passant Aug 12 '18 at 17:28
  • Adrian [mentions this](https://stackoverflow.com/a/49784481/17034) as well. – Hans Passant Aug 12 '18 at 17:29

1 Answers1

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

You did convert to 8-bit bitmap, however GetHBITMAP will return a bitmap handle compatible with your video card, which is probably 32-bit. GDI+ has already processed the palette and returned a bitmap handle which is turned back in to 32-bit. HBITMAP handle can be painted directly, for example using CreateCompatibleDC and BitBlt. So there is no need to obtaining the palette and passing it to GDI, and no need for 8-bit conversion in the first place.

If this is necessary for some reason, you can get the bits and palette from 32-bit bitmap, put that in 8-bit bitmap, and draw with StretchDIBits

The main issue in your code is that it should use DIB_RGB_COLORS flag for GetDIBits/StretchDIBits, because the device context is most likely 32-bit. There is no need for SelectPalette/RealizePalette either (unless it's 8-bit display from 30 years ago)

It makes more sense to get the bits directly from GDI+ using LockBits, and get the palette directly using GetPalette, as seen in the example below.

Aside, source and destination have to be deleted before exit.

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);
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77