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;
}
HBITMAP hBitmap;
Gdiplus::Color color;
destination->GetHBITMAP(color, &hBitmap);

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

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

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

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

除此之外,sourcedestination必须在退出之前删除。

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);
}