Creating a GDI+ Bitmap from an 8-bit Bitmap

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by QW5keSBTY2 » Wed, 17 Jan 2007 23:42:00


Hello,
I am trying to capture a portion of the screen, and then display that using
GDI+. My current approach for doing this is to create a bitmap using
CreateCompatibleBitmap(), select that into a memory DC, and call BitBlt() to
copy the contents of the screen DC into my memory DC. I then pass the HBITMAP
for the to the GDI+ Bitmap constructor.

This works very well when the display is set to use either 16-bit or 32-bit
color. However, if the display is set to 8-bit color, then the colors in the
captured bitmap aren't displayed correctly. At first, I thought that this was
because I wasn't passing an HPALETTE to the GDI+ Bitmap, however when I
capture the current palette and pass that as well, there is no change in the
displayed image.

I am sure that I am capturing the palette correctly, because if I draw the
HBITMAP to the screen using the HPALETTE and normal GDI calls, the image is
displayed correctly.

I'm really not sure what else I have to do in order to get GDI+ to use the
palette information for the bitmap. Do you have any ideas as to what I am
doing wrong?

Here's the code that I am using to capture the screen:
HDC hDCScreen = GetDC(NULL);
SIZE szScreen = {
GetDeviceCaps(hDCScreen, HORZRES),
GetDeviceCaps(hDCScreen, VERTRES)
};

HDC hDCBuffer = CreateCompatibleDC(hDCScreen);

UINT bitDepth = GetDeviceCaps(hDCScreen, BITSPIXEL);
HPALETTE hPalOld = NULL;

LOGPALETTE* pPalette = NULL;
UINT nColors = 0;
if(bitDepth <= 8)
{
nColors = GetSystemPalet *** tries(hDCScreen, 0, 0, NULL);
pPalette =
(LOGPALETTE*)malloc(sizeof(LOGPALETTE)+(nColors-1)*sizeof(PALET *** TRY));
pPalette->palVersion = 0x300;
pPalette->palNumEntries = (WORD)nColors;
GetSystemPalet *** tries(hDCScreen, 0, nColors, pPalette->palPalEntry);
m_hPalScreen = CreatePalette(pPalette);
hPalOld = (HPALETTE)SelectObject(hDCBuffer, m_hPalScreen);
RealizePalette(hDCBuffer);
free(pPalette);
}

m_hBmpScreen = CreateCompatibleBitmap(hDCScreen, szScreen.cx, szScreen.cy);

HBITMAP hBmpOld = (HBITMAP)SelectObject(hDCBuffer, m_hBmpScreen);
BitBlt(hDCBuffer, 0, 0, szScreen.cx, szScreen.cy, hDCScreen, 0, 0, SRCCOPY);

if(NULL != m_hPalScreen)
{
SelectObject(hDCBuffer, hPalOld);
}

m_pImage = Bitmap::FromHBITMAP(m_hBmpScreen, m_hPalScreen);

SelectObject(hDCBuffer, hBmpOld);
DeleteDC(hDCBuffer);

ReleaseDC(NULL, hDCScreen);

Thanks,
Andy
 
 
 

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by Michael Ph » Thu, 18 Jan 2007 00:36:44

gt; hPalOld = (HPALETTE)SelectObject(hDCBuffer, m_hPalScreen);

You should use SelectPalette to bring the palette into the device context
and use it again to take it out of the device before creating the gdi+
bitmap.



You should select your m_hBmpScreen out of the device context(i.e.,
hDCBuffer) before you create the gdi+ bitmap.

Did you check the gdi+ bitmap's Palette object to see if it maches the
palette entries use to create it?

Keep in mind that the HPALETTE and the HBITMAP must not be deleted while the
gdi+ bitmap is in scope.

"Andy Schott" < XXXX@XXXXX.COM > wrote in message
news: XXXX@XXXXX.COM ...



 
 
 

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by QW5keSBTY2 » Thu, 18 Jan 2007 03:32:00

ichael,
Thank you for your response.

I changed my code to use SelectPalette() instead of SelectObject(), and I
also moved the SelectObject() that selects the HBITMAP out of the device
context to before the GDI+ image is created. I also verified that the HBITMAP
and HPALETTE are deleted after the GDI+ image is deleted. Unfortunately, the
colors in the image still aren't correct.

When I compared the original palette to the palette from the GDI+ image,
they were different. I then created a new GDI+ palette, copied in the
contents of the HPALETTE, and set that as the new palette for the GDI+ image.
While the image still did not look correct, it looked as if at least some of
the colors were correct.

I'm not really sure why the palette is wrong in the GDI+ image - is the
implementation of FromHBITMAP() doing something different to pull the colors
from the HPALETTE than I am doing?

It is also odd that manually setting the correct palette to the image does
not cause the image to be displayed correctly. Is there something special
that I need to do in order to draw the image correctly? I am currently just
calling Graphics::DrawImage(), passing it the Bitmap* and (0, 0) for the
location to draw the image at.

Andy

"Michael Phillips, Jr." wrote:

 
 
 

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by Michael Ph » Thu, 18 Jan 2007 04:03:56

ou could also try calling UnrealizeObject before creating your gdi+ bitmap.

This will force the system to completely remap the logical palette to the
system palette when RealizePalette is called again.

The system's palette is limited. You don't get to control all 256 colors
for an image as the system reserves colors for it's own use.

You could also try to create your screen capture with a bitmap created via
CreateDIBSection. Since the bitmap is device independent, you will have
complete control over the mapping of the DIB's colors to the palette. You
can then create a gdi+ indexed bitmap from the DIB when the screen is 8bpp.
The color table is already part of the DIB.


"Andy Schott" < XXXX@XXXXX.COM > wrote in message
news: XXXX@XXXXX.COM ...


 
 
 

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by QW5keSBTY2 » Thu, 18 Jan 2007 05:47:01

k, when I create the GDI+ bitmap from a DIB, the image looks a bit better.
Most of the colors are correct, but there are still some that are wrong -
it's almost as if it isn't using all of the colors that I assigned to the
pallete. In fact, if I view the 8-bit image when my monitor is set to 32-bit,
it looks correct (i.e., identical to the screen when it was set to 8-bit
mode). That tells me that something is still a bit off with my palette.

I'm using the following code to create the DIB (this replaces the call to
CreateCompatibleBitmap() in my original code sample, below; the palette is
already selected into the DC at this point):
BYTE* pBits = NULL;
BITMAPINFO* pBmi = NULL;
if(NULL != pPalette)
{
int amtAlloc = sizeof(BITMAPINFO) + nColors*sizeof(RGBQUAD);
pBmi = (BITMAPINFO*)malloc(amtAlloc);
memset(pBmi, 0, amtAlloc);

for(unsigned int i = 0; i < nColors; i++)
{
pBmi->bmiColors[i].rgbRed = pPalette->palPalEntry[i].peRed;
pBmi->bmiColors[i].rgbGreen = pPalette->palPalEntry[i].peGreen;
pBmi->bmiColors[i].rgbBlue = pPalette->palPalEntry[i].peBlue;
}
}
else
{
pBmi = (BITMAPINFO*)malloc(sizeof(BITMAPINFO));
memset(pBmi, 0, sizeof(BITMAPINFO));
}

pBmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pBmi->bmiHeader.biWidth = szScreen.cx;
pBmi->bmiHeader.biHeight = szScreen.cy;
pBmi->bmiHeader.biPlanes = 1;
pBmi->bmiHeader.biBitCount = bitDepth;
pBmi->bmiHeader.biCompression = BI_RGB;
DWORD dwBytesPerLine = (((bitDepth * szScreen.cx) + 31) / 32 * 4);
pBmi->bmiHeader.biSizeImage = dwBytesPerLine * szScreen.cy;
pBmi->bmiHeader.biClrUsed = nColors;
m_hBmpScreen = CreateDIBSection(hDCScreen, pBmi, DIB_RGB_COLORS,
(LPVOID*)&pBits, NULL, 0);

free(pBmi);

I then call BitBlt() to copy the contents of the screen DC to the DIB.

I'm not entirely sure that I am creating the DIB correctly - is there a bug
there that is causing the incorrect colors to be displayed in the final image?

Thanks,
Andy

"Michael Phillips, Jr." wrote:

 
 
 

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by Michael Ph » Thu, 18 Jan 2007 06:40:59

ou can either select a palette into a device context or use SetDIBColors to
set the color table before using BitBlt to copy the screen.

The problem with using a 8bpp device is that the system reserves 20 colors
for itself out of the possible 256. These colors occupy the first 10 and
the last 10 slots.

If your image does not contain any of these 20 colors, then the best color
match is not going to look that great. If you were working with a 8bpp
display and needed the best palette for an image, you could try to use
color quantization to get the best 236 colors for your image. You would
then create an identity palette with the system's 20 colors and your 236
colors. That is the best that you can do when working with an 8bpp screen
depth.

"Andy Schott" < XXXX@XXXXX.COM > wrote in message
news: XXXX@XXXXX.COM ...


 
 
 

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by QW5keSBTY2 » Fri, 19 Jan 2007 03:09:01

ouldn't calling SetDIBColors() be equivalent to setting the RGBQUAD[1] in
the BITMAPINFO structure to the colors of the captured palette? I'm doing the
latter, so it seems like calling SetDIBColors() in addition to, or instead
of, my current approach would be equivalent.

As for the system taking up some of the entries in the palette - that maybe
correct, however what I find odd is that if I draw the captured 8-bit image
to the screen using GDI with the HBITMAP and the HPALETTE, the image displays
correctly (even when the display is still set to 8-bit mode). To me, this
says that the HPALETTE has the correct information, but when that palette is
given to GDI+, it isn't using it correctly. If that is indeed the case, I'm
not sure what I can do about that (if anything).

Andy

"Michael Phillips, Jr." wrote:

 
 
 

Creating a GDI+ Bitmap from an 8-bit Bitmap

Post by Michael Ph » Fri, 19 Jan 2007 04:21:02

gt; Wouldn't calling SetDIBColors() be equivalent to setting the RGBQUAD[1] in

CreateDIBSection creates an empty bitmap. The bitmap bit's get filled in
after the BitBlt. Before using BitBlt, you set the color table of the
destination bitmap either by setting the color table with SetDIBColors or
using the system palette selected into the device context.


The gdi+ documentation does state that the HBITMAP and HPALETTE can not be
selected into a device context before calling FromHBITMAP and the HPALETTE
can not represent a DIB.

Unfortunately, I do not have an 8bpp device driver to test what you have
reported. The debug symbols for gdi+ are public and available for download.
You can try to trace the call into gdi+ to see what gdi+ is doing with the
palette handle.

If gdi+ creates an identity palette from the HPALLETE that you passed in,
then the palette entries will be reordered.

You can try to create an identity palette yourself and then pass that
HPALETTE to FromHBITMAP that way you can eliminate the reordering of your
palette entries.


"Andy Schott" < XXXX@XXXXX.COM > wrote in message
news: XXXX@XXXXX.COM ...