Displaying a 256 color bitmap
Anyway cutting to the chase, to display a 256 color bitmap, you have to create a logical palette from the color table in the bitmap, select the palette into the device context, realize the palette and BitBlt the bitmap to the device context.
Step 1: Load bitmap and create palette
This step shows you how to load a bitmap with the color information and create a logical palette from it. The GetBitmapAndPalette() function shown below loads the bitmap resource and creates a logical palette. Its best to save the bitmap and palette for later use rather than call this function each time the bitmap or the palette is needed.The first thing the function does is that it loads the bitmap and attaches it to a CBitmap object. We use a call to the global ::LoadImage() rather than to the CBitmap::LoadBitmap(). The reason for this is that we want to be able to access the DIBSECTION of the bitmap and the reason why we want the DIBSECTION is because we want to access the color information in the bitmap to create a logical palette that matches the colors used by the bitmap. Calling LoadBitmap() would result in the loss of the color information.
Once we have the bitmap, we start working on creating the logical palette. We determine the number of colors used by the bitmap by getting access to the DIBSECTION by calling the Cbitmap::GetObject() function. Note that the documentation for this function does not mention the DIBSECTION, you'd have to look up the documention of the ::GetObject() function in the API section instead. Sometimes the BITMAPINFOHEADER which is part of the DIBSECTION does not specify how many colors it uses. If this is the case we infer the color count from the number of bits it uses for the each pixel. For example, 8 bits can represent 256 different values and therefore indicates 256 colors. Similarly, 16 bits indicates 64K colors.
A bitmap that uses more than 256 colors does not have a color table. In this situation we simply create a halftone palette compatible with the device context. A halftone palette is basically a palette that contains a sampling of all the different colors. This is certainly not the best solution but it is the simplest.
If the bitmap has 256 colors or less, we do create the palette. We allocate enough space to hold the color table of the bitmap and call the function ::GetDIBColorTable() to retrieve it from the bitmap. We also allocate enough memory to create a logical palette and copy the color entries from the bitmap's color table. The palVersion field should be 0x300.
After creating the CPalette object, we deallocate the memory blocks allocated earlier and invalidate the window so that it can be redrawn using the new image.
BOOL GetBitmapAndPalette(UINT nIDResource, CBitmap &bitmap, CPalette &pal)
{
LPCTSTR lpszResourceName = (LPCTSTR)nIDResource;
HBITMAP hBmp = (HBITMAP)::LoadImage( AfxGetInstanceHandle(),
lpszResourceName, IMAGE_BITMAP, 0,0, LR_CREATEDIBSECTION );
if( hBmp == NULL )
return FALSE;
bitmap.Attach( hBmp );
// Create a logical palette for the bitmap
DIBSECTION ds;
BITMAPINFOHEADER &bmInfo = ds.dsBmih;
bitmap.GetObject( sizeof(ds), &ds );
int nColors = bmInfo.biClrUsed ? bmInfo.biClrUsed : 1 << bmInfo.biBitCount;
// Create a halftone palette if colors > 256.
CClientDC dc(NULL); // Desktop DC
if( nColors > 256 )
pal.CreateHalftonePalette( &dc );
else
{
// Create the palette
RGBQUAD *pRGB = new RGBQUAD[nColors];
CDC memDC;
memDC.CreateCompatibleDC(&dc);
memDC.SelectObject( &bitmap );
::GetDIBColorTable( memDC, 0, nColors, pRGB );
UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * nColors);
LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
pLP->palVersion = 0x300;
pLP->palNumEntries = nColors;
for( int i=0; i < nColors; i++)
{
pLP->palPalEntry[i].peRed = pRGB[i].rgbRed;
pLP->palPalEntry[i].peGreen = pRGB[i].rgbGreen;
pLP->palPalEntry[i].peBlue = pRGB[i].rgbBlue;
pLP->palPalEntry[i].peFlags = 0;
}
pal.CreatePalette( pLP );
delete[] pLP;
delete[] pRGB;
}
return TRUE;
}
Step 2: Draw the bitmap
You would most likely have code in the WM_PAINT handler to draw the bitmap to the device context. In the sample code below we call the GetBitmapAndPalette() function from within the OnPaint() function. This is not very efficient and has been used here only for clarity. It's always best to save the bitmap and palette object.void CMyWindow::OnPaint()
{
CPaintDC dc(this);
// Create a memory DC compatible with the paint DC
CDC memDC;
memDC.CreateCompatibleDC( &dc );
CBitmap bitmap;
CPalette palette;
GetBitmapAndPalette( IDB_BITMAP, bitmap, palette );
memDC.SelectObject( &bitmap );
// Select and realize the palette
if( dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE && palette.m_hObject != NULL )
{
dc.SelectPalette( &palette, FALSE );
dc.RealizePalette();
}
dc.BitBlt(0, 0, 180, 180, &memDC, 0, 0,SRCCOPY);
}

Comments