Last time, we saw
that you can use the SetDIBitsToDevice
function
to draw a DIB with an alternate color table without having
to modify the HBITMAP
.
In that version of the function, we selected the HBITMAP
into a device context in preparation for drawing from it,
but in fact that step isn’t necessary for drawing.
It was merely necessary to get the original color table
so we could build our grayscale color table.
If you don’t care what the original colors are,
then you can skip that step.
And even if you care what the old colors are,
and if you assume that the colors don’t change,
then you only need to ask once.
To demonstrate, that
all the work of building the BITMAPINFO
structure could have been done ahead of time,
let’s use this alternate version of our program:
HBITMAP g_hbm; struct BITMAPINFO256 { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[256]; } g_bmiGray; void *g_pvBits; BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) { // change path as appropriate g_hbm = (HBITMAP)LoadImage(g_hinst, TEXT("C:\\Windows\\Gone Fishing.bmp"), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE); if (g_hbm) { BITMAP bm; if (GetObject(g_hbm, sizeof(bm), &bm) == sizeof(bm) && bm.bmBits != NULL && bm.bmPlanes * bm.bmBitsPixel <= 8) { ZeroMemory(&g_bmiGray, sizeof(g_bmiGray)); HDC hdc = CreateCompatibleDC(NULL); if (hdc) { HBITMAP hbmPrev = SelectBitmap(hdc, g_hbm); UINT cColors = GetDIBColorTable(hdc, 0, 256, g_bmiGray.bmiColors); for (UINT iColor = 0; iColor < cColors; iColor++) { BYTE b = (BYTE)((30 * g_bmiGray.bmiColors[iColor].rgbRed + 59 * g_bmiGray.bmiColors[iColor].rgbGreen + 11 * g_bmiGray.bmiColors[iColor].rgbBlue) / 100); g_bmiGray.bmiColors[iColor].rgbRed = b; g_bmiGray.bmiColors[iColor].rgbGreen = b; g_bmiGray.bmiColors[iColor].rgbBlue = b; } g_bmiGray.bmiHeader.biSize = sizeof(g_bmiGray.bmiHeader); g_bmiGray.bmiHeader.biWidth = bm.bmWidth; g_bmiGray.bmiHeader.biHeight = bm.bmHeight; g_bmiGray.bmiHeader.biPlanes = bm.bmPlanes; g_bmiGray.bmiHeader.biBitCount = bm.bmBitsPixel; g_bmiGray.bmiHeader.biCompression = BI_RGB; g_bmiGray.bmiHeader.biClrUsed = cColors; g_pvBits = bm.bmBits; DeleteDC(hdc); } } return TRUE; } void PaintContent(HWND hwnd, PAINTSTRUCT *pps) { if (g_pvBits) { SetDIBitsToDevice(pps->hdc, 0, 0, g_bmiGray.bmiHeader.biWidth, g_bmiGray.bmiHeader.biHeight, 0, 0, 0, g_bmiGray.bmiHeader.biHeight, g_pvBits, (BITMAPINFO*)&g_bmiGray, DIB_RGB_COLORS); } }
I moved the blue code from PaintContent
to OnCreate
to demonstrate that pretty much all
of the work we used to do in PaintContent
could have been done ahead of time.
The only other thing we had to do was save the pointer to the bits
so we could pass them to SetDIBitsToDevice
.
(Of course, that pointer becomes invalid once the controlling
HBITMAP
is destroyed, so be careful!
In practice, you probably would be better off calling
GetObject
immediately before drawing
to protect against the case that somebody deleted the bitmap
out from under you.)
Next time,
we’ll look at another operation we can perform
when we have a BITMAPINFO
and a collection of pixels.
(Note that there are issues with this technique which will be taken up on Friday.)
0 comments