Click to See Complete Forum and Search --> : CreateDIBSection() for palette bitmaps (very confusing)


MrDoomMaster
September 15th, 2006, 12:45 PM
I have pixel data in memory along with a palette. I want to draw these to a client window, so I use CreateDIBSection().

The pixel data is an array of unsigned char (indicies into a 256 entry color palette), and the palette is an array of unsigned short (only 256 entries in the palette)

I've learned that a palette can be created by calling CreatePalette() and then calling SelectPalette(), then RealizePalette() to make it useable. However, the MSDN documentation for the BITMAPINFO::bmiColors member confuses me. I can't seem to set the bmiColors member anyway, since the definition is a static array with a single element: RGBQUAD bmiColors[1];. The compiler spits errors at me if I try to assign a static array or a dynamic pointer to this member. Am I supposed to put the palette in the bmiColors member, or am I supposed to create an HPALETTE? How do I assign a value to bmiColors when it's an array with a single element? Here is what I have so far:


BITMAPINFO bi = {0};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biBitCount = 8;
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = h;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biCompression = BI_RGB;

This is as far as I have been able to get. Could someone explain where I am to put my palette? This is very confusing. Thanks.

MikeAThon
September 15th, 2006, 02:14 PM
Your palette goes right after the BITMAPINFOHEADER member of the BITMAPINFO structure. It's an an array of RGBQUAD structures, and it comprises the bmiColors member of the BITMAPINFO structure.

See http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp

Mike

MrDoomMaster
September 15th, 2006, 03:48 PM
Thanks for the info.

I still can't get the bitmaps to draw properly. Currently they draw as yellow squares.


struct SIBITMAPINFO
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColor[256];
};

CBitmap* TileWindow::CreateWindowsBitmap( const HDC dc, const unsigned char* bits, int w, int h, const unsigned short* palette )
{
// This function is particularly slow, many optimizations could be made.
// The speed difference will probably not be very noticable.
SIBITMAPINFO bi = {0};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biBitCount = 8;
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = h;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biCompression = BI_RGB;

for( int i = 0; i < 256; ++i )
{
unsigned char r, g, b;
MAKE_RGB32( palette[i], r, g, b );
bi.bmiColor[i].rgbRed = r;
bi.bmiColor[i].rgbGreen = g;
bi.bmiColor[i].rgbBlue = b;
bi.bmiColor[i].rgbReserved = 0;
}

return CBitmap::FromHandle( CreateDIBSection( dc, (BITMAPINFO*)&bi, DIB_PAL_COLORS, (void**)bits, 0, 0 ) );
}

This is the source code used to create the bitmaps. Here's the code I use to draw them:

Bitmap* bits = tile->m_pSurface->GetBitmap();

CBitmap* bitmap = CreateWindowsBitmap( dc.m_hDC, bits->GetPixelData(), bits->GetWidth(),
bits->GetHeight(), bits->GetPalette() );

CDC bitmapDC;
bitmapDC.CreateCompatibleDC( &dc );
CBitmap* oldBitmap = bitmapDC.SelectObject( bitmap );

dc.BitBlt( tileStartX, tileStartY, tileSize, tileSize, &bitmapDC, 0, 0, SRCCOPY );

bitmapDC.SelectObject( oldBitmap );
bitmap->DeleteObject();

There's a couple of variables you won't have the definitions/declarations to in these code snippets, but they are fairly irrelevant.

Thanks for any assistance.

MikeAThon
September 15th, 2006, 04:21 PM
I'm uncertain, but maybe

return CBitmap::FromHandle( CreateDIBSection( dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS /* DIB_PAL_COLORS */ , (void**)bits, 0, 0 ) );
}

Mike

MrDoomMaster
September 15th, 2006, 05:00 PM
I'm uncertain, but maybe

return CBitmap::FromHandle( CreateDIBSection( dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS /* DIB_PAL_COLORS */ , (void**)bits, 0, 0 ) );
}

Mike
This has changed all bitmaps from drawing in yellow to pink, which is the first color in my palette. Index 0 in the palette is RGB( 255, 0, 255 );

Getting closer, but why are the bitmap pixels not drawing the correct color? I've stepped through the arrays in runtime, and the pixel data references various indexes in the palette, not just index 0. Pixels that reference index 94, for example, are drawing pink... which is index 0.

I checked the palette data as well, other palette indicies are the correct colors (not pink!).

MikeAThon
September 15th, 2006, 08:17 PM
OK, here's another stab at it:
return CBitmap::FromHandle( CreateDIBSection( dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&bits, 0, 0 ) );
But in looking at the documentation for CreateDIBSection, I think that it's being used incorrectly. In CreateDIBSection, the fourth parameter, "ppvBits", is an output parameter, namely a pointer to a variable that receives a pointer to the location of the DIB bit values. From what you have described, you already have the DIB bit values, and you want to get a DDB for displaying them.

I think that after calling the function, you should be copying your bit values to the address pointed to by the returned ppvBits parameter. E.g.,
char* pBits;
HANDLE hBM = CreateDIBSection( dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0 );
memcpy( pBits, bits, <Number to copy> );
return CBitmap::FromHandle( bBM );

If I get time, I will check some archived code that does something similar, to see how it was done there.

Mike

MikeAThon
September 17th, 2006, 01:24 PM
The following code worked for me, in the OnDraw() handler of an MDI app:
void CPalettedDIBView::OnDraw(CDC* pDC)
{
CPalettedDIBDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

struct SIBITMAPINFO
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColor[3]; //exactly three colors in the palette
};

SIBITMAPINFO bi = {0};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = 256;
bi.bmiHeader.biHeight = 256; // positive number == bottom-up DIB
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 8;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biClrUsed = 3; // only three colors are in the palette


// define the three colors in the palette (R, G and B)

bi.bmiColor[ 0 ].rgbRed = 255;
bi.bmiColor[ 0 ].rgbGreen = 0;
bi.bmiColor[ 0 ].rgbBlue = 0;
bi.bmiColor[ 0 ].rgbReserved = 0;

bi.bmiColor[ 1 ].rgbRed = 0;
bi.bmiColor[ 1 ].rgbGreen = 255;
bi.bmiColor[ 1 ].rgbBlue = 0;
bi.bmiColor[ 1 ].rgbReserved = 0;


bi.bmiColor[ 2 ].rgbRed = 0;
bi.bmiColor[ 2 ].rgbGreen = 0;
bi.bmiColor[ 2 ].rgbBlue = 255;
bi.bmiColor[ 2 ].rgbReserved = 0;


BYTE* pBits;

HANDLE hBM = ::CreateDIBSection( (HDC)(*pDC), (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0 );

// Set color-index values of the pixels
// This is a bottom-up DIB, so the colors will be in horizontal bands, 64 rows high,
// in the order (bottom-up) of R, G, B

for ( int iRow=0; iRow<256; ++iRow )
{
for ( int iCol=0; iCol<256; ++iCol )
{
switch ( (iRow/64)%4 )
{
case 0: *pBits = 0; break;
case 1: *pBits = 1; break;
case 2: *pBits = 2; break;
case 3: *pBits = 0; break;
}

pBits++;
}
}

// blt the DDB bitmap to the display device context

CBitmap bm;
bm.Attach( hBM );

CDC memDC;
memDC.CreateCompatibleDC( pDC );
CBitmap* pOldBM = memDC.SelectObject( &bm );

pDC->BitBlt( 0, 0, 256, 256, &memDC, 0, 0, SRCCOPY );

memDC.SelectObject( pOldBM );

}

It produced the output shown below, on a full-depth color (32 bit) display.

Mike