If you create a DIB section at 8bpp or lower, then it will come with a color table. Pixels in the bitmap are represented not by their red/blue/green component values, but are instead indices into the color table. For example, a 4bpp DIB section can have up to sixteen colors in its color table.
Although displays that use 8bpp or lower are considered woefully outdated nowadays, bitmaps in that format are actually quite useful precisely due to the fact that you can manipulate colors in the bitmap, not by manipulating the bits themselves, but instead by manipulating the color table.
Let’s demonstrate this by taking the “Gone Fishing” bitmap and converting it to grayscale. Start with our scratch program and make these changes:
HBITMAP g_hbm; 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) { HDC hdc = CreateCompatibleDC(NULL); if (hdc) { HBITMAP hbmPrev = SelectBitmap(hdc, g_hbm); RGBQUAD rgbColors[256]; UINT cColors = GetDIBColorTable(hdc, 0, 256, rgbColors); for (UINT iColor = 0; iColor < cColors; iColor++) { BYTE b = (BYTE)((30 * rgbColors[iColor].rgbRed + 59 * rgbColors[iColor].rgbGreen + 11 * rgbColors[iColor].rgbBlue) / 100); rgbColors[iColor].rgbRed = b; rgbColors[iColor].rgbGreen = b; rgbColors[iColor].rgbBlue = b; } SetDIBColorTable(hdc, 0, cColors, rgbColors); SelectBitmap(hdc, hbmPrev); DeleteDC(hdc); } } return TRUE; } void OnDestroy(HWND hwnd) { if (g_hbm) DeleteObject(g_hbm); PostQuitMessage(0); } void PaintContent(HWND hwnd, PAINTSTRUCT *pps) { if (g_hbm) { HDC hdc = CreateCompatibleDC(NULL); if (hdc) { HBITMAP hbmPrev = SelectBitmap(hdc, g_hbm); BITMAP bm; if (GetObject(g_hbm, sizeof(bm), &bm) == sizeof(bm)) { BitBlt(pps->hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdc, 0, 0, SRCCOPY); } SelectBitmap(hdc, hbmPrev); DeleteDC(hdc); } } }
The OnDestroy
function merely cleans up, and the
PaintContent
function simply draws the bitmap to
the window’s client area.
All the work really happens in the OnCreate
function.
First, we load the bitmap as a DIB section by passing the
LR_CREATEDIBSECTION
flag.
This opens up the exciting world of DIB sections,
but all we care about is the color table.
That happens when we call GetDIBColorTable
.
Since color tables are supported only up to 8bpp,
a color table of size 256 is big enough to handle the worst case.
Once we get the color table, we go through each color in it
and convert it to grayscale,
then set the new color table into the DIB section.
That’s all.
Notice that we were able to change the color of every single
pixel in the bitmap by modifying just 1KB of data.
(Four bytes per RGBQUAD
times a worst-case of
256 colors.)
Even if the bitmap were 1024 × 768,
modifying just the color table is enough to change all the colors
in the bitmap.
Manipulating the DIB color table is how flags like
LR_LOADMAP3DCOLORS
and LR_LOADTRANSPARENT
do their work.
They don’t walk the bitmap updating every single pixel;
instead, they just load the color table,
look for the colors they are interested in, and change
that entry in the color table.
This technique of editing the color table is
what I was referring to
when I suggested you could use DIB sections to
avoid the pesky DSna raster operation.
And it’s faster, too.
But it only works on bitmaps that are 8bpp or lower.
You may also have noticed that LR_LOADTRANSPARENT
doesn’t actually load a transparent bitmap.
Rather, it loads a bitmap that appears to be
transparent provided that you draw it against a window whose color
is COLOR_WINDOW
.
Why this misleading name?
Because at the time this flag was invented,
GDI didn’t support transparent bitmaps.
(And even today, it still doesn’t really support then,
with the notable exception of functions like AlphaBlend
.)
The best you could do was fake it.
0 comments