The first steps are to determine the size of the system font by using the TEXTMETRIC structure and to create a memory device context compatible with the screen: hdc = CreateIC TEXT "DIS
Trang 1void AddHelpToSys (HINSTANCE hInstance, HWND hwnd)
{
HBITMAP hBitmap ;
HMENU hMenu ;
hMenu = GetSystemMenu (hwnd, FALSE);
hBitmap = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapHelp"))) ; AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ;
AppendMenu (hMenu, MF_BITMAP, IDM_HELP, (PTSTR) (LONG) hBitmap) ;
hMenuPopup = LoadMenu (hInstance, TEXT ("MenuFile")) ;
hBitmap = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapFile"))) ; AppendMenu (hMenu, MF_BITMAP | MF_POPUP, (int) hMenuPopup,
(PTSTR) (LONG) hBitmap) ;
hMenuPopup = LoadMenu (hInstance, TEXT ("MenuEdit")) ;
hBitmap = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapEdit"))) ; AppendMenu (hMenu, MF_BITMAP | MF_POPUP, (int) hMenuPopup,
hBitmap = GetBitmapFont (i) ;
AppendMenu (hMenuPopup, MF_BITMAP, IDM_FONT_COUR + i,
int cxChar, cyChar ;
// Get the width and height of a system font character
cxChar = LOWORD (GetDialogBaseUnits ()) ;
cyChar = HIWORD (GetDialogBaseUnits ()) ;
// Create 2 memory DCs compatible with the display
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 2GetObject (hBitmap1, sizeof (BITMAP), (PTSTR) &bm1) ;
// Scale these dimensions based on the system font size
bm2 = bm1 ;
bm2.bmWidth = (cxChar * bm2.bmWidth) / 4 ;
bm2.bmHeight = (cyChar * bm2.bmHeight) / 8 ;
bm2.bmWidthBytes = ((bm2.bmWidth + 15) / 16) * 2 ;
// Create a new bitmap of larger size
hBitmap2 = CreateBitmapIndirect (&bm2) ;
// Select the bitmaps in the memory DCs and do a StretchBlt
SelectObject (hdcMem1, hBitmap1) ;
SelectObject (hdcMem2, hBitmap2) ;
StretchBlt (hdcMem2, 0, 0, bm2.bmWidth, bm2.bmHeight,
hdcMem1, 0, 0, bm1.bmWidth, bm1.bmHeight, SRCCOPY) ;
hFont = (HFONT) SelectObject (hdcMem, hFont) ;
GetTextExtentPoint32 (hdcMem, szFaceName[i],
lstrlen (szFaceName[i]), &size);
hBitmap = CreateBitmap (size.cx, size.cy, 1, 1, NULL) ;
SelectObject (hdcMem, hBitmap) ;
Trang 3hMenu = GetSystemMenu (hwnd, FALSE);
GetMenuItemInfo (hMenu, IDM_HELP, FALSE, &mii) ;
DeleteObject ((HBITMAP) mii.dwTypeData) ;
// Delete top-level menu bitmaps
hMenu = GetMenu (hwnd) ;
for (i = 0 ; i < 3 ; i++)
{
GetMenuItemInfo (hMenu, i, TRUE, &mii) ;
DeleteObject ((HBITMAP) mii.dwTypeData) ;
GetMenuItemInfo (hMenu, i, TRUE, &mii) ;
DeleteObject ((HBITMAP) mii.dwTypeData) ;
}
}
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 4MENUFILE MENU DISCARDABLE
BEGIN
MENUITEM "&New", IDM_FILE_NEW
MENUITEM "&Open ", IDM_FILE_OPEN
MENUITEM "&Save", IDM_FILE_SAVE
MENUITEM "Save &As ", IDM_FILE_SAVE_AS
MENUITEM "Cu&t", IDM_EDIT_CUT
MENUITEM "&Copy", IDM_EDIT_COPY
MENUITEM "&Paste", IDM_EDIT_PASTE
MENUITEM "De&lete", IDM_EDIT_CLEAR
END
/////////////////////////////////////////////////////////////////////////////// Bitmap
BITMAPFONT BITMAP DISCARDABLE "Fontlabl.bmp"
BITMAPHELP BITMAP DISCARDABLE "Bighelp.bmp"
BITMAPEDIT BITMAP DISCARDABLE "Editlabl.bmp"
BITMAPFILE BITMAP DISCARDABLE "Filelabl.bmp"
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 5RESOURCE.H (excerpts
// Microsoft Developer Studio generated include file
// Used by GrafMenu.rc
#define IDM_FONT_COUR 101
#define IDM_FONT_ARIAL 102
#define IDM_FONT_TIMES 103
#define IDM_HELP 104
#define IDM_EDIT_UNDO 40005
#define IDM_EDIT_CUT 40006
#define IDM_EDIT_COPY 40007
#define IDM_EDIT_PASTE 40008
#define IDM_EDIT_CLEAR 40009
#define IDM_FILE_NEW 40010
#define IDM_FILE_OPEN 40011
#define IDM_FILE_SAVE 40012
#define IDM_FILE_SAVE_AS 40013
EDITLABL.BMP
FILELABL.BMP
FONTLABL.BMP
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 6BIGHELP.BMP
To insert a bitmap into a menu, you use AppendMenu or InsertMenu The bitmap can come from one of two
places You can create a bitmap in Visual C++ Developer Studio, include the bitmap file in your resource script, and
within the program use LoadBitmap to load the bitmap resource into memory You then call AppendMenu or
InsertMenu to attach it to the menu There's a problem with this approach, however The bitmap might not be
suitable for all types of video resolutions and aspect ratios; you probably want to stretch the loaded bitmap to
account for this Alternatively, you can create the bitmap right in the program, select it into a memory device context, draw on it, and then attach it to the menu
The GetBitmapFont function in GRAFMENU takes a parameter of 0, 1, or 2 and returns a handle to a bitmap This
bitmap contains the string "Courier New," "Arial," or "Times New Roman" in the appropriate font and about twice
the size of the normal system font Let's see how GetBitmapFont does it (The code that follows is not the same as that in the GRAFMENU.C file For purposes of clarity, I've replaced references to the szFaceName array with the
values appropriate for the Arial font.)
The first steps are to determine the size of the system font by using the TEXTMETRIC structure and to create a memory device context compatible with the screen:
hdc = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;
This font is selected in the memory device context and the default font handle is saved:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 7hFont = (HFONT) SelectObject (hdcMem, hFont) ;
Now when we write some text to the memory device context, Windows will use the TrueType Arial font selected into the device context
But this memory device context initially has a one-pixel monochrome device surface We have to create a bitmap large enough for the text we want to display on it You can obtain the dimensions of the text through
GetTextExtentPoint32 and create a bitmap based on these dimensions with CreateBitmap:
GetTextExtentPoint32 (hdcMem, TEXT ("Arial"), 5, &size) ;
hBitmap = CreateBitmap (size.cx, size.cy, 1, 1, NULL) ;
SelectObject (hdcMem, hBitmap) ;
This device context now has a monochrome display surface exactly the size of the text Now all we have to do is write the text to it:
TextOut (hdcMem, 0, 0, TEXT ("Arial"), 5) ;
We're finished, except for cleaning up To do so, we select the system font (with handle hFont) back into the device context by using SelectObject, and we delete the previous font handle that SelectObject returns, which is the handle
to the Arial font:
DeleteObject (SelectObject (hdcMem, hFont)) ;
Now we can also delete the two device contexts:
DeleteDC (hdcMem) ;
DeleteDC (hdc) ;
We're left with a bitmap that has the text "Arial" in an Arial font
The memory device context also comes to the rescue when we need to scale fonts to a different display resolution or aspect ratio I created the four bitmaps used in GRAFMENU to be the correct size for a display that has a system font height of 8 pixels and width of 4 pixels For other system font dimensions, the bitmap has to be stretched This is
done in GRAFMENU's StretchBitmap function
The first step is to get the device context for the screen, obtain the text metrics for the system font, and create two memory device contexts:
hdc = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;
Trang 8The bitmap handle passed to the function is hBitmap1 The program can obtain the dimensions of this bitmap using
GetObject:
GetObject (hBitmap1, sizeof (BITMAP), (PSTR) &bm1) ;
This copies the dimensions into a structure bm1 of type BITMAP The structure bm2 is set equal to bm1, and then
certain fields are modified based on the system font dimensions:
bm2 = bm1 ;
bm2.bmWidth = (tm.tmAveCharWidth * bm2.bmWidth) / 4 ;
bm2.bmHeight = (tm.tmHeight * bm2.bmHeight) / 8 ;
bm2.bmWidthBytes = ((bm2.bmWidth + 15) / 16) * 2 ;
Next a new bitmap with handle hBitmap2 can be created that is based on the altered dimensions:
hBitmap2 = CreateBitmapIndirect (&bm2) ;
You can then select these two bitmaps into the two memory device contexts:
SelectObject (hdcMem1, hBitmap1) ;
SelectObject (hdcMem2, hBitmap2) ;
We want to copy the first bitmap to the second bitmap and stretch it in the process This involves the StretchBlt call:
StretchBlt (hdcMem2, 0, 0, bm2.bmWidth, bm2.bmHeight,
hdcMem1, 0, 0, bm1.bmWidth, bm1.bmHeight, SRCCOPY) ;
Now the second bitmap has the properly scaled bitmap We'll use that one in the menu As you can see below, cleanup is simple
DeleteDC (hdcMem1) ;
DeleteDC (hdcMem2) ;
DeleteObject (hBitmap1) ;
The CreateMyMenu function in GRAFMENU uses the StretchBitmap and GetBitmapFont functions when
constructing the menu GRAFMENU has two menus already defined in the resource script These will become popups for the File and Edit options The function begins by obtaining a handle to an empty menu:
Trang 9hMenuPopup = LoadMenu (hInstance, TEXT ("MenuFile")) ;
The bitmap containing the word "FILE" is also loaded from the resource script and stretched using StretchBitmap:
hBitmapFile = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapFile"))) ;
The bitmap handle and popup menu handle become arguments to the AppendMenu call:
AppendMenu (hMenu, MF_BITMAP MF_POPUP, hMenuPopup, (PTSTR) (LONG) hBitmapFile) ;
The same procedure is followed for the Edit menu:
hMenuPopup = LoadMenu (hInstance, TEXT ("MenuEdit")) ;
hBitmapEdit = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapEdit"))) ;
AppendMenu (hMenu, MF_BITMAP MF_POPUP, hMenuPopup, (PTSTR) (LONG) hBitmapEdit) ;
The popup menu for the three fonts is constructed from calls to the GetBitmapFont function:
hMenuPopup = CreateMenu () ;
for (i = 0 ; i < 3 ; i++)
{
hBitmapPopFont [i] = GetBitmapFont (i) ;
AppendMenu (hMenuPopup, MF_BITMAP, IDM_FONT_COUR + i,
(PTSTR) (LONG) hMenuPopupFont [i]) ;
}
The popup is then added to the menu:
hBitmapFont = StretchBitmap (LoadBitmap (hInstance, "BitmapFont")) ;
AppendMenu (hMenu, MF_BITMAP MF_POPUP, hMenuPopup, (PTSTR) (LONG) hBitmapFont) ;
The window menu is now complete, and WndProc makes it the window's menu by a call to SetMenu
GRAFMENU also alters the system menu in the AddHelpToSys function The function first obtains a handle to the
system menu:
hMenu = GetSystemMenu (hwnd, FALSE) ;
This loads the "Help" bitmap and stretches it to an appropriate size:
hBitmapHelp = StretchBitmap (LoadBitmap (hInstance, TEXT ("BitmapHelp"))) ;
This adds a separator bar and the stretched bitmap to the system menu:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 10AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ;
AppendMenu (hMenu, MF_BITMAP, IDM_HELP, (PTSTR)(LONG) hBitmapHelp) ;
GRAFMENU devotes a whole function to cleaning up and deleting all the bitmaps before the program terminates
A couple of miscellaneous notes on using bitmap in menus now follow
In a top-level menu, Windows adjusts the menu bar height to accommodate the tallest bitmap Other bitmaps (or
character strings) are aligned at the top of the menu bar The size of the menu bar obtained from GetSystemMetrics
with the SM_CYMENU constant is no longer valid after you put bitmaps in a top-level menu
As you can see from playing with GRAFMENU, you can use check marks with bitmapped menu items in popups, but the check mark is of normal size If that bothers you, you can create a customized check mark and use
SetMenuItemBitmaps
Another approach to using nontext (or text in a font other than the system font) on a menu is the "owner-draw" menu
The keyboard interface to menus is another problem When the menu contains text, Windows automatically adds a keyboard interface You can select a menu item using the Alt key in combination with a letter of the character string But once you put a bitmap in a menu, you've eliminated that keyboard interface Even if the bitmap says something, Windows doesn't know about it
This is where the WM_MENUCHAR message comes in handy Windows sends a WM_MENUCHAR message to your window procedure when you press Alt with a character key that does not correspond to a menu item.
GRAFMENU would need to intercept WM_MENUCHAR messages and check the value of wParam (the ASCII
character of the pressed key) If this corresponds to a menu item, it would have to return a double word to
Windows, where the high word is set to 2 and the low word is set to the index of the menu item we want associated with that key Windows does the rest
Nonrectangular Bitmap Images
Bitmaps are always rectangular, but they needn't be displayed like that For example, suppose you have a
rectangular bitmap image that you want to be displayed as an ellipse
At first, this sounds simple You just load the image into Visual C++ Developer Studio or the Windows Paint
program (or a more expensive application) and you start drawing around the outside of the image with a white pen You're left with an elliptical image with everything outside the ellipse painted white This will work but only when you display the bitmap on a white background If you display it on any other color background, you'll have an elliptical image on top of a white rectangle on top of a colored background That's no good
There's a very common technique to solve problems like this The technique involves a "mask" bitmap and some raster operations A mask is a monochrome bitmap of the same dimensions as the rectangular bitmap image you want
to display Each mask pixel corresponds with a pixel of the bitmap image The mask pixels are 1 (white) wherever the original bitmap pixel is to be displayed, and 0 (black) wherever you want to preserve the destination background (Or the mask bitmap can be opposite this, with some corresponding changes to the raster operations you use.) Let's see how this works in real life in the BITMASK program shown in Figure 14-17
Figure 14-17 The BITMASK program
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 11LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH) ; wndclass.lpszMenuName = NULL ;
Trang 12LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
static HBITMAP hBitmapImag, hBitmapMask ;
static HINSTANCE hInstance ;
static int cxClient, cyClient, cxBitmap, cyBitmap ;
hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
// Load the original image and get its size
hBitmapImag = LoadBitmap (hInstance, TEXT ("Matthew")) ;
GetObject (hBitmapImag, sizeof (BITMAP), &bitmap) ;
cxBitmap = bitmap.bmWidth ;
cyBitmap = bitmap.bmHeight ;
// Select the original image into a memory DC
hdcMemImag = CreateCompatibleDC (NULL) ;
SelectObject (hdcMemImag, hBitmapImag) ;
// Create the monochrome mask bitmap and memory DC
hBitmapMask = CreateBitmap (cxBitmap, cyBitmap, 1, 1, NULL) ;
hdcMemMask = CreateCompatibleDC (NULL) ;
SelectObject (hdcMemMask, hBitmapMask) ;
// Color the mask bitmap black with a white ellipse
SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ;
Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;
SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ;
Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;
// Mask the original image
BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap,
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
Trang 14MATTHEW BITMAP DISCARDABLE "matthew.bmp"
The MATTHEW.BMP file referred to in the resource script is a digitized black-and-white photograph of a nephew
of mine It's 200 pixels wide, 320 pixels high, and has 8 bits per pixel However, BITMASK is written so that this file can be just about anything
Notice that BITMASK colors its window background with a light gray brush This is to assure ourselves that we're properly masking the bitmap and not just coloring part of it white
Let's look at WM_CREATE processing BITMASK uses the LoadBitmap function to obtain a handle to the original image in the variable hBitmapImag The GetObject function obtains the bitmap width and height The bitmap handle
is then selected in a memory device context whose handle is hdcMemImag
Next the program creates a monochrome bitmap the same size as the original image The handle is stored in
hBitmapMask and selected into a memory device context whose handle is hdcMemMask The mask bitmap is
colored with a black background and a white ellipse by using GDI functions on the memory device context:
SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ;
Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;
SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ;
Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;
Because this is a monochrome bitmap, the black area is really 0 bits and the white area is really 1 bits
Then a BitBlt call alters the original image by using this mask:
BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap,
hdcMemMask, 0, 0, SRCAND) ;
The SRCAND raster operation performs a bitwise AND operation between the bits of the source (the mask bitmap) and the bits of the destination (the original image) Wherever the mask bitmap is white, the destination is preserved Wherever the mask bitmap is black, the destination becomes black as well An elliptical area in the original image is now surrounded by black
Now let's look at WM_PAINT processing Both the altered image bitmap and the mask bitmap are selected into
memory device contexts Two BitBlt calls perform the magic The first does a BitBlt of the mask bitmap on the
window:
BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326) ;
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 15This uses a raster operation for which there is no name The logical operation is D & ~S Recall that the source the mask bitmap is a white ellipse (1 bits) surrounded by black (0 bits) The raster operation inverts the source so that it's
a black ellipse surrounded by white The raster operation then performs a bitwise AND of this inverted source with the destination the surface of the window When the destination is ANDed with 1 bits, it remains unchanged When
ANDed with 0 bits, the destination becomes black Thus, this BitBlt operation draws a black ellipse in the window The second BitBlt call draws the image bitmap on the window:
BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT) ;
The raster operation performs a bitwise OR operation between the source and the destination The outside of the source bitmap is black, so it leaves the destination unchanged Within the ellipse, the destination is black, so the image is copied unchanged The result is shown in Figure 14-18
A few notes:
You may need a mask that is quite complex for example, one that blots out the whole background of the original image You'll probably need to create this manually in a paint program and save it to a file
Figure 14-18 The BITMASK display
If you're writing applications specifically for Windows NT, you can use the MaskBlt function to do something similar
to the MASKBIT program with fewer function calls Windows NT also includes another BitBlt-like function not supported under Windows 98 This is the PlgBlt ("parallelogram blt") function that lets you rotate or skew bitmap
Some Simple Animation
Because the display of small bitmaps is quite fast, you can use bitmaps in combination with the Windows timer for some rudimentary animation
Yes, it's time for the bouncing ball program
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 16The BOUNCE program, shown in Figure 14-19 below, constructs a ball that bounces around in the window's client area The program uses the timer to pace the ball The ball itself is a bitmap The program first creates the ball by creating the bitmap, selecting it into a memory device context, and then making simple GDI function calls The
program draws the bitmapped ball on the display using a BitBlt from a memory device context
Figure 14-19 The BOUNCE program
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 17LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ;
Trang 18LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam){
static HBITMAP hBitmap ;
static int cxClient, cyClient, xCenter, yCenter, cxTotal, cyTotal, cxRadius, cyRadius, cxMove, cyMove, xPixel, yPixel ;
xPixel = GetDeviceCaps (hdc, ASPECTX) ;
yPixel = GetDeviceCaps (hdc, ASPECTY) ;
xCenter = (cxClient = LOWORD (lParam)) / 2 ;
yCenter = (cyClient = HIWORD (lParam)) / 2 ;
iScale = min (cxClient * xPixel, cyClient * yPixel) / 16 ;
cxRadius = iScale / xPixel ;
cyRadius = iScale / yPixel ;
cxMove = max (1, cxRadius / 2) ;
cyMove = max (1, cyRadius / 2) ;
cxTotal = 2 * (cxRadius + cxMove) ;
cyTotal = 2 * (cyRadius + cyMove) ;
SelectObject (hdcMem, hBitmap) ;
Rectangle (hdcMem, -1, -1, cxTotal + 1, cyTotal + 1) ;
hBrush = CreateHatchBrush (HS_DIAGCROSS, 0L) ;
SelectObject (hdcMem, hBrush) ;
BitBlt (hdc, xCenter - cxTotal / 2,
yCenter - cyTotal / 2, cxTotal, cyTotal,
Trang 20BOUNCE reconstructs the ball whenever the program gets a WM_SIZE message This requires a memory device context compatible with the video display:
hdcMem = CreateCompatibleDC (hdc) ;
The diameter of the ball is set at one-sixteenth of either the height or the width of the client area, whichever is shorter However, the program constructs a bitmap that is larger than the ball: On each of its four sides, the bitmap extends beyond the ball's dimensions by one-half of the ball's radius:
hBitmap = CreateCompatibleBitmap (hdc, cxTotal, cyTotal) ;
After the bitmap is selected into a memory device context, the entire bitmap is colored white for the background:
Rectangle (hdcMem, -1, -1, xTotal + 1, yTotal + 1) ;
Those odd coordinates cause the rectangle boundary to be painted outside the bitmap A diagonally hatched brush is selected into the memory device context, and the ball is drawn in the center of the bitmap:
Ellipse (hdcMem, xMove, yMove, xTotal - xMove, yTotal - yMove) ;
The margins around the edges of the ball effectively erase the previous image of the ball when the ball is moved.
Redrawing the ball at another position requires only a simple BitBlt call using the ROP code of SRCCOPY:
BitBlt (hdc, xCenter - cxTotal / 2, yCenter - cyTotal / 2, cxTotal, cyTotal,
hdcMem, 0, 0, SRCCOPY) ;
BOUNCE demonstrates the simplest way to move an image around the display, but this approach isn't satisfactory for general purposes If you're interested in animation, you'll want to explore some of the other ROP codes (such as SRCINVERT) that perform an exclusive OR operation on the source and destination Other techniques for animation
involve the Windows palette (and the AnimatePalette function) and the CreateDIBSection function For more
sophisticated animation, you may need to abandon GDI and explore the DirectX interface
Bitmaps Outside the Window
The SCRAMBLE program, shown in Figure 14-20 beginning below, is very rude and I probably shouldn't be showing it to you But it demonstrates some interesting techniques and uses a memory device context as a temporary
holding space for BitBlt operations that swap the contents of pairs of display rectangles
Figure 14-20 The SCRAMBLE program
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 21LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
SelectObject (hdcMem, hBitmap) ;
srand ((int) GetCurrentTime ()) ;
Trang 22SCRAMBLE doesn't have a window procedure In WinMain, it first calls LockWindowUpdate with the desktop
window handle This function temporarily prevents any other program from updating the screen SCRAMBLE then
obtains a device context for the entire screen by calling GetDCEx with a DCX_LOCKWINDOWUPDATE
argument This lets SCRAMBLE write on the screen when no other program can
SCRAMBLE then determines the dimensions of the full screen and divides them by 10 The program uses these
dimensions (named cx and cy) to create a bitmap and then selects the bitmap into the memory device context
Using the C rand function, SCRAMBLE calculates four random values (two coordinate points) that are multiples of the cx and cy values The program swaps two rectangular blocks of the display through the use of three BitBlt
functions The first copies the rectangle beginning at the first coordinate point to the memory device context The
second BitBlt copies the rectangle beginning at the second point to the location beginning at the first point The third
copies the rectangle in the memory device context to the area beginning at second coordinate point
This process effectively swaps the contents of the two rectangles on the display SCRAMBLE does this 300 times, after which the screen should be thoroughly scrambled But do not fear, because SCRAMBLE keeps track of this mess and then unscrambles the screen, returning it to normal (and unlocking the screen) before exiting
You can also use memory device contexts to copy the contents of one bitmap to another For instance, suppose you want to create a bitmap that contains only the upper left quadrant of another bitmap If the original bitmap has the
handle hBitmap, you can copy the dimensions into a structure of type BITMAP,
GetObject (hBitmap, sizeof (BITMAP), &bm) ;
and create a new uninitialized bitmap of one-quarter the size:
hBitmap2 = CreateBitmap (bm.bmWidth / 2, bm.bmHeight / 2,
bm.bmPlanes, bm.bmBitsPixel, NULL) ;
Now create two memory device contexts and select the original bitmap and the new bitmap into them:
hdcMem1 = CreateCompatibleDC (hdc) ;
hdcMem2 = CreateCompatibleDC (hdc) ;
SelectObject (hdcMem1, hBitmap) ;
SelectObject (hdcMem2, hBitmap2) ;
Finally, copy the upper left quadrant of the first bitmap to the second:
BitBlt (hdcMem2, 0, 0, bm.bmWidth / 2, bm.bmHeight / 2,
Trang 23The BLOWUP.C program, shown in Figure 14-21, also uses window update locking to display a capture rectangle outside the border of the program's window This program lets you use the mouse to block out any rectangular area
of the screen BLOWUP then copies the contents of that rectangular area to a bitmap During the WM_PAINT message, the bitmap is copied to the program's client area and stretched or compressed, if necessary (See Figure 14-22)
Figure 14-21 The BLOWUP program
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 24LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ;
hAccel = LoadAccelerators (hInstance, szAppName) ;
while (GetMessage (&msg, NULL, 0, 0))
Trang 25hdc = GetDCEx (hwndScr, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE) ;
GetObject (hBitmapSrc, sizeof (BITMAP), &bitmap) ;
hBitmapDst = CreateBitmapIndirect (&bitmap) ;
static BOOL bCapturing, bBlocking ;
static HBITMAP hBitmap ;
Trang 26case WM_RBUTTONDOWN:
if (bCapturing)
{
bBlocking = TRUE ;
ptBeg.x = LOWORD (lParam) ;
ptBeg.y = HIWORD (lParam) ;
InvertBlock (hwndScr, hwnd, ptBeg, ptEnd) ;
ptEnd.x = LOWORD (lParam) ;
ptEnd.y = HIWORD (lParam) ;
InvertBlock (hwndScr, hwnd, ptBeg, ptEnd) ;
InvertBlock (hwndScr, hwnd, ptBeg, ptEnd) ;
ptEnd.x = LOWORD (lParam) ;
ptEnd.y = HIWORD (lParam) ;
abs (ptEnd.x - ptBeg.x),
abs (ptEnd.y - ptBeg.y)) ;
SelectObject (hdcMem, hBitmap) ;
StretchBlt (hdcMem, 0, 0, abs (ptEnd.x - ptBeg.x),
abs (ptEnd.y - ptBeg.y),
hdc, ptBeg.x, ptBeg.y, ptEnd.x - ptBeg.x,
ptEnd.y - ptBeg.y, SRCCOPY) ; DeleteDC (hdcMem) ;
bBlocking = bCapturing = FALSE ;
SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
Trang 27iEnable = hBitmap ? MF_ENABLED : MF_GRAYED ;
EnableMenuItem ((HMENU) wParam, IDM_EDIT_CUT, iEnable) ;
EnableMenuItem ((HMENU) wParam, IDM_EDIT_COPY, iEnable) ;
EnableMenuItem ((HMENU) wParam, IDM_EDIT_DELETE, iEnable) ;
SelectObject (hdcMem, hBitmap) ;
GetObject (hBitmap, sizeof (BITMAP), (PSTR) &bm) ;
SetStretchBltMode (hdc, COLORONCOLOR) ;
StretchBlt (hdc, 0, 0, rect.right, rect.bottom,
hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY) ; DeleteDC (hdcMem) ;
}
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 29BLOWUP MENU DISCARDABLE
BEGIN
POPUP "&Edit"
BEGIN
MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT
MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY
MENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTE
MENUITEM "De&lete\tDelete", IDM_EDIT_DELETE
END
END
/////////////////////////////////////////////////////////////////////////////// Accelerator
BLOWUP ACCELERATORS DISCARDABLE
BEGIN
"C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT
"V", IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
VK_DELETE, IDM_EDIT_DELETE, VIRTKEY, NOINVERT
"X", IDM_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT
Trang 30Figure 14-22 A sample BLOWUP display
Because of restrictions on mouse capturing, using BLOWUP is a little complicated at first and takes some getting used to Here's how to use the program:
1 Press the left mouse button in BLOWUP's client area, and keep the left button held down The mouse cursor changes to a crosshair
2 Still holding the left button down, move the mouse cursor anywhere on the screen Position the mouse cursor
at the upper left corner of the rectangular area you want to capture
3 Still holding the left button down, press the right mouse button and drag the mouse to the lower right corner
of the rectangular area you want to capture Release the left and right mouse buttons (The order in which you release the buttons doesn't matter.)
The mouse cursor changes back to an arrow, and the area that you blocked out is copied to BLOWUP's client area and compressed or expanded appropriately
If you block out a rectangle by moving from the upper right corner to the lower left corner, BLOWUP displays a mirror image If you move from the lower left to the upper right, BLOWUP displays an upside-down image And if you move from the upper right to the upper left, the program combines the two effects
BLOWUP also contains logic to copy the bitmap to the clipboard, and to copy any bitmap in the clipboard to the program BLOWUP processes the WM_INITMENUPOPUP message to enable or disable the various items on its Edit menu and the WM_COMMAND message to process these menu items The structure of this code should look familiar because it is essentially the same as that shown in Chapter 12 to copy and paste text items
For bitmaps, however, the clipboard items are not global handles but bitmap handles When you use the
CF_BITMAP, the GetClipboardData function returns an HBITMAP object and the SetClipboardData function
accepts an HBITMAP object If you want to transfer a bitmap to the clipboard but still have a copy of it for use by the program itself, you must make a copy of the bitmap Similarly, if you paste a bitmap from the clipboard, you
should also make a copy The CopyBitmap function in BLOWUP does this by obtaining a BITMAP structure of the existing bitmap and using this structure in the CreateBitmapIndirect function to create a new bitmap (The Src and
Dst suffixes on the variable names stand for "source" and "destination.") Both bitmaps are selected into memory
device contexts and the bitmap bits transferred with a call to BitBlt (Alternatively, to copy the bits, you can allocate
a block of memory the size of the bitmap and call GetBitmapBits for the source bitmap and SetBitmapBits for the
destination bitmap.)
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 31I find BLOWUP to be very useful for examining the multitude of little bitmaps and pictures that are scattered throughout Windows and its applications Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 32Chapter 15
The Device-Independent Bitmap
In the last chapter, we saw how the Windows GDI bitmap object (also known as the device-dependent bitmap, or
DDB) is useful for a variety of programming chores However, I did not demonstrate how to save these bitmaps to disk files or load them back into memory This is something that was done back in the old days of Windows but is never done today The DDB is inadequate for the purpose of image interchange because the format of the bitmap bits
is highly device-dependent There is no color table in a DDB that specifies a correspondence between the bitmap bits and color The DDB makes sense only when it is created and destroyed within the lifetime of a Windows session
The device-independent bitmap (or DIB) was introduced in Windows 3.0 to provide a sorely needed image file format suitable for interchange As you may know, other image file formats, such as GIF or JPEG, are much more common than DIB files on the Internet This is mostly because the GIF and JPEG formats implement compression schemes that significantly reduce downloading time Although there is a compression scheme defined for DIBs, it is rarely used The bitmap bits in most DIBs are almost always uncompressed This is actually a major advantage if you want to manipulate the bitmap bits in your program Unlike GIF and JPEG files, the DIB is directly supported by the Windows API If you have a DIB in memory, you can supply pointers to that DIB as arguments to several functions that let you display the DIB or convert it into a DDB
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 33The DIB File Format
Interestingly enough, the DIB format did not originate in Windows It was first defined in version 1.1 of OS/2, the operating system originally developed by IBM and Microsoft beginning in the mid-1980s OS/2 1.1 was released in
1988 and was the first version of OS/2 to include a Windows-like graphical user interface, known as the
Presentation Manager (PM) The Presentation Manager included the Graphics Programming Interface (GPI), which defined the bitmap format
That OS/2 bitmap format was then used in Windows 3.0 (released in 1990), where it came to be known as the DIB Windows 3.0 also included a variation of the original DIB format that under Windows has come to be the standard Additional enhancements were defined in Windows 95 (and Windows NT 4.0) and Windows 98 (and Windows NT 5.0), as I'll discuss in this chapter
The DIB is best examined first as a file format DIB files have the filename extension BMP or, more rarely, DIB Bitmap images used by Windows applications (for example, on the surfaces of buttons) are created as DIB files and generally stored as read-only resources in the program's executable file Icons and mouse cursors are also DIB files
in a slightly different form
A program can load a DIB file, minus the first 14 bytes, into a contiguous block of memory It is then sometimes
referred to as "a bitmap in the packed-DIB format." Applications running under Windows can use the packed-DIB
format for exchanging images through the Windows clipboard or for creating brushes Programs also have complete access to the contents of the DIB and can modify the DIB in whatever way they choose
Programs can also create their own DIBs in memory and later save them in files The images in these DIBs can be
"painted" by a program using GDI function calls Or the program can set and manipulate the pixel bits directly, perhaps using other memory-based DIBs in the process
When a DIB is loaded into memory, programs can also use the DIB data with several Windows API function calls, that I'll discuss in this chapter The DIB-related API calls are few in number and are mainly concerned with displaying display DIBs on the video display or a printer page and with converting them to and from GDI bitmap objects
When all is said and done, however, there remain many, many, many DIB tasks that application programs might need
to perform for which there is no support in the Windows operating system For example, a program might have access to a 24-bit DIB and might wish to convert it into an 8-bit DIB with an optimal 256-color palette Windows will not do this for you But this chapter and the next will show you how to work with DIBs beyond what the
Windows API provides
The OS/2-Style DIB
So that we don't get bogged down in too many details just yet, let's take a look at the format of the Windows DIB that is compatible with the bitmap format first introduced in OS/2 1.1
A DIB file has four main sections:
• A file header
• An information header
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 34• An RGB color table (but not always)
• The bitmap pixel bits
You can think of the first two parts as C data structures and the third part as an array of data structures These structures are documented in the Windows header file WINGDI.H A memory-based DIB in the packed-DIB format has three sections:
• An information header
• An RGB color table (but not always)
• The bitmap pixel bits
It's exactly the same as a DIB stored in a file except that it doesn't have the file header
The DIB file, but not the memory-based packed DIB, begins with a 14-byte file header defined as a structure like so:
typedef struct tagBITMAPFILEHEADER // bmfh
{
WORD bfType ; // signature word "BM" or 0x4D42
DWORD bfSize ; // entire size of file
WORD bfReserved1 ; // must be zero
WORD bfReserved2 ; // must be zero
DWORD bfOffsetBits ; // offset in file of DIB pixel bits
}
BITMAPFILEHEADER, * PBITMAPFILEHEADER ;
This may not be exactly the way the structure is defined in WINGDI.H (for example, the comments are mine), but it
is functionally the same The first comment (that is, the text "bmfh") shows the recommended abbreviation when
naming a structure variable of this data type If you see a variable in one of my programs named pbmfh, that will be a
pointer to a structure of type BITMAPFILEHEADER or a variable of type PBITMAPFILEHEADER
The structure is 14 bytes in length It begins with the two letters "BM" to indicate a bitmap file This is the WORD value 0x4D42 The "BM" indicator is followed by a DWORD indicating the entire size of the file, including the file header, in bytes The next two WORD fields are set to zero (In a mouse cursor file, which is similar in format to a DIB file, these two fields are used to indicate the "hot spot" of the cursor.) The structure concludes with a DWORD indicating the byte offset within the file where the pixel bits begin This number can be derived from information in the DIB information header, but it is provided here for convenience
In the OS/2-style DIB, the BITMAPFILEHEADER structure is followed immediately by a
BITMAPCOREHEADER structure, which provides the basic information about the DIB image A packed DIB begins with the BITMAPCOREHEADER:
typedef struct tagBITMAPCOREHEADER // bmch
{
DWORD bcSize ; // size of the structure = 12
WORD bcWidth ; // width of image in pixels
WORD bcHeight ; // height of image in pixels
Trang 35The word "core" sounds a little odd in this context, and it is It means that this format is the basis (thus the core) of other bitmap formats derived from it
The bcSize field in the BITMAPCOREHEADER structure indicates the size of the data structure, in this case 12
bytes
The bcWidth and bcHeight fields contain the size of the bitmap in pixels Although the use of a WORD for these
fields implies that a DIB may be 65,535 pixels high and wide, you'll rarely encounter anything quite that large
The bcPlanes field is always 1 Always, always, always from the time it was defined until this very second The field
is a remnant of the earlier Windows GDI bitmap object that we encountered in the last chapter
The bcBitCount field indicates the number of bits per pixel For OS/2-style DIBs, this can be either 1, 4, 8, or 24.
The number of colors in the DIB image is equal to 2bmch.bcBitCount or, in C syntax, to
1 << bmch.bcBitCount
Thus, the bcBitCount field is equal to:
• 1 for a 2-color DIB
• 4 for a 16-color DIB
• 8 for a 256-color DIB
• 24 for a full-color DIB
When I refer to "an 8-bit DIB," I'll mean a DIB that has 8 bits per pixel
For the first three cases (that is, for bit counts of 1, 4, and 8), the BITMAPCOREHEADER is followed by the color table The color table does not exist for 24-bit DIBs The color table is an array of 3-byte RGBTRIPLE structures, one for each color in the image:
typedef struct tagRGBTRIPLE // rgbt
{
BYTE rgbtBlue ; // blue level
BYTE rgbtGreen ; // green level
BYTE rgbtRed ; // red level
}
RGBTRIPLE ;
It is recommended that the color table be arranged so that the most important colors in the DIB appear first We'll see why in the next chapter
The WINGDI.H header file also defines the following structure:
typedef struct tagBITMAPCOREINFO // bmci
{
BITMAPCOREHEADER bmciHeader ; // core-header structure
RGBTRIPLE bmciColors[1] ; // color table array
}
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 36BITMAPCOREINFO, * PBITMAPCOREINFO ;
This structure combines the information header with the color table Although the number of RGBTRIPLE structures
is seemingly equal to 1 in this structure, you'll never find just one RGBTRIPLE in a DIB file The size of the color table is always 2, 16, or 256 RGBTRIPLE structures, depending on the number of bits per pixel If you need to allocate a structure of PBITMAPCOREINFO for an 8-bit DIB, you can do it like so:
pbmci = malloc (sizeof (BITMAPCOREINFO) + 255 * sizeof (RGBTRIPLE)) ;
Then you can access whatever RGBTRIPLE structure you need like so:
pbmci->bmciColors[i]
Because the RGBTRIPLE structure is 3 bytes in length, some of the RGBTRIPLE structures might begin at odd addresses within the DIB However, because there are always an even number of RGBTRIPLE structures in the DIB file, the data block that follows the color table array always begins at a WORD address boundary
The data that follow the color table (and what follows the information header for DIBs with a bit count of 24) are the pixel bits themselves
Bottoms Up!
Like most bitmap formats, the pixel bits in the DIB are organized in horizontal rows, sometimes also called "scan
lines" from the terminology of video display hardware The number of rows is equal to the bcHeight field of the
BITMAPCOREHEADER structure However, unlike most bitmap formats, the DIB begins with the bottom row of the image and proceeds up through the image
Let's establish some terminology here When I say "top row" and "bottom row," I mean the top and bottom of the visual image as it appears when correctly displayed on the monitor or printer page The top row of a portrait is hair; the bottom row of a portrait is chin When I say "first row," I mean the row of pixels that is found directly after the color table in the DIB file And when I say "last row," I mean the row of pixels at the very end of the file
So, in DIBs, the bottom row of the image is the first row of the file, and the top row of the image is the last row in the file This is called a bottom-up organization Because this organization is counterintuitive, you may ask why it's done this way
Well, it all goes back to the OS/2 Presentation Manager Someone at IBM decided that all coordinate systems in
PM including those for windows, graphics, and bitmaps should be consistent This provoked a debate: Most people, including programmers who have worked with full-screen text programming or windowing environments, think in terms of vertical coordinates that increase going down the screen However, hardcore computer graphics
programmers approach the video display from a perspective that originates in the mathematics of analytic geometry This involves a rectangular (or Cartesian) coordinate system where increasing vertical coordinates go up in space
In short, the mathematicians won Everything in PM was saddled with a bottom-left origin, including window
coordinates And that's how DIBs came to be this way
The DIB Pixel Bits
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 37The last section of the DIB file in most cases the great bulk of the DIB file consists of the actual DIB pixel bits The pixel bits are organized in horizontal rows beginning with the bottom row of the image and proceeding up through the image
The number of rows in a DIB is equal to the bcHeight field of the BITMAPCOREHEADER structure Each row encodes a number of pixels equal to the bcWidth field of the structure Each row begins with the leftmost pixels and proceeds to the right of the image The number of bits per pixel is obtained from the bcBitCount field, which is either
1, 4, 8, or 24
The length of each row in bytes is always a multiple of 4 The row length can be calculated like so:
RowLength = 4 * ((bmch.bcWidth * bmch.bcBitCount + 31) / 32) ;
Or, slightly more efficiently in C, like this:
RowLength = ((bmch.bcWidth * bmch.bcBitCount + 31) & ~31) >> 3 ;
The row is padded at the right (customarily with zeros), if necessary, to achieve this length The total number of bytes
of pixel data is equal to the product of RowLength and bmch.bcHeight
To see how the pixels are encoded, let's examine the four cases separately In the diagrams shown below, the bits of each byte are shown in boxes and are numbered with 7 indicating the most-significant bit and 0 indicating the
least-significant bit Pixels are also numbered beginning with 0 for the leftmost pixel in the row
For DIBs with 1 bit per pixel, each byte corresponds to 8 pixels The leftmost pixel is the most-significant bit of the first byte:
Each pixel can be either a 0 or a 1 A 0 bit means that the color of that pixel is given by the first RGBTRIPLE entry
in the color table A 1 bit is a pixel whose color is the second entry of the color table
For DIBs with 4 bits per pixel, each byte corresponds to 2 pixels The leftmost pixel is the high 4 bits of the first byte, and so on:
The value of each 4-bit pixel ranges from 0 to 15 This value is an index into the 16 entries in the color table
For a DIB with 8 bits per pixel, each byte is 1 pixel:
The value of the byte is 0 through 255 Again, this is an index into the 256 entries in the color table
For DIBs with 24 bits-per-pixel, each pixel requires 3 bytes for the red, green, and blue color values Each row of pixel bits is basically an array of RGBTRIPLE structures, possibly padded with 0 bytes at the end of each row so that the row has a multiple of 4 bytes:
Again, the 24-bit-per-pixel DIB has no color table
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 38The Expanded Windows DIB
Now that we've mastered the OS/2-compatible DIB introduced in Windows 3.0, we can take a look at the
expanded version of the DIB introduced in Windows at the same time
This form of the DIB begins with a BITMAPFILEHEADER structure just like the earlier format but then continues with a BITMAPINFOHEADER structure rather than a BITMAPCOREHEADER structure:
typedef struct tagBITMAPINFOHEADER // bmih
{
DWORD biSize ; // size of the structure = 40
LONG biWidth ; // width of the image in pixels
LONG biHeight ; // height of the image in pixels
WORD biPlanes ; // = 1
WORD biBitCount ; // bits per pixel (1, 4, 8, 16, 24, or 32)
DWORD biCompression ; // compression code
DWORD biSizeImage ; // number of bytes in image
LONG biXPelsPerMeter ; // horizontal resolution
LONG biYPelsPerMeter ; // vertical resolution
DWORD biClrUsed ; // number of colors used
DWORD biClrImportant ; // number of important colors
}
BITMAPINFOHEADER, * PBITMAPINFOHEADER ;
You can distinguish an OS/2-compatible DIB from a Windows DIB by checking the first field of the structure, which
is 12 in the former case and 40 in the latter case
As you'll note, there are six additional fields in this structure, but the BITMAPINFOHEADER structure is not simply
a BITMAPCOREHEADER with some new stuff tacked on to the end Take a closer look: In the
BITMAPCOREHEADER structure, the bcWidth and bcHeight fields are 16-bit WORD values In this structure,
they are 32-bit LONG values This is an annoying little change that is guaranteed to drive you nuts
Another change: For 1-bit, 4-bit, and 8-bit DIBs using the BITMAPINFOHEADER structure, the color table is not
an array of RGBTRIPLE structures Instead, the BITMAPINFOHEADER structure is followed by an array of RGBQUAD structures:
typedef struct tagRGBQUAD // rgb
{
BYTE rgbBlue ; // blue level
BYTE rgbGreen ; // green level
BYTE rgbRed ; // red level
BYTE rgbReserved ; // = 0
}
RGBQUAD ;
This is the same as the RGBTRIPLE structure except that it includes a fourth field that is always set to 0 The
WINGDI.H header file also defines the following structure:
typedef struct tagBITMAPINFO // bmi
{
BITMAPINFOHEADER bmiHeader ; // info-header structure
RGBQUAD bmiColors[1] ; // color table array
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 39BITMAPINFO, * PBITMAPINFO ;
Note that if the BITMAPINFO structure begins at a 32-bit address boundary, each entry in the RGBQUAD array also begins at a 32-bit address boundary because the BITMAPINFOHEADER structure is 40 bytes in length This assures more efficient addressing of the color table data by 32-bit microprocessors
Although the BITMAPINFOHEADER was originally defined for Windows 3.0, some of the fields were redefined in Windows 95 and Windows NT 4.0, and these have been carried over into Windows 98 and Windows NT 5.0 For
example, the current documentation states: "If biHeight is negative, the bitmap is a top-down DIB and its origin is the
upper left corner." That's good to know It would be even better if somebody had made this decision in 1990 when this DIB format was originally defined My advice is to avoid creating top-down DIBs You're almost begging that
some program written without awareness of this new "feature" will crash upon encountering a negative biHeight field.
Or that programs such as the Microsoft Photo Editor included with Microsoft Word 97 will report "Illegal image height" upon encountering a top-down DIB (although Word 97 itself does fine with them)
The biPlanes field is still always 1, but the biBitCount field can now be 16 or 32 as well as 1, 4, 8, or 24 This was
also a new feature in Windows 95 and Windows NT 4.0 I'll discuss how these additional formats work shortly
Let me skip the biCompression and biSizeImage fields for now I'll also discuss them shortly
The biXPelsPerMeter and biYPelsPerMeter fields indicate a suggested real-world size of the image in the ungainly
units of pixels per meter (The "pel" picture element is what IBM liked to call the pixel.) Internally, Windows does not use this information However, an application could use it to display a DIB in an accurate size These fields are also useful if the DIB originated from a device that does not have square pixels In most DIBs, these fields are set to 0, which indicates no suggested real-world size A resolution of 72 dots per inch (which is sometimes used for video displays, although the actual resolution depends on the size of the monitor) is approximately equivalent to 2,835 pixels per meter, and a common printer resolution of 300-dpi is 11,811 pixels per meter
The biClrUsed field is a very important field because it affects the number of entries in the color table For 4-bit and
8-bit DIBs, it can indicate that the color table contains fewer than 16 or 256 entries, respectively This is one method
to shrink down the size of the DIB, although not by very much For example, suppose a DIB image contains only 64
gray shades The biClrUsed field is set to 64, and the color table contains 64 RGBQUAD structures for a total color
table size of 256 bytes The pixel values then range from 0x00 through 0x3F The DIB still requires 1 byte per pixel,
but the high 2 bits of each pixel byte are zero If the biClrUsed field is set to 0, it means that the color table contains the full number of entries implied by the biBitCount field
Beginning with Windows 95, the biClrUsed field can be nonzero for 16-bit, 24-bit, or 32-bit DIBs In these cases,
the color table is not used by Windows to interpret the pixel bits Instead, it indicates the size of a color table in the DIB that could be used by programs to set a palette to display the DIB on 256-color video displays You'll recall that in the OS/2-compatible format, a 24-bit DIB had no color table This was also true of the extended format introduced in Windows 3.0 The change in Windows 95 means that a 24-bit DIB can have a color table the size of
which is indicated by the biClrUsed field
To summarize:
• For 1-bit DIBs, biClrUsed is always 0 or 2 The color table always has 2 entries
• For 4-bit DIBs, if the biClrUsed field is 0 or 16, the color table has 16 entries If it's a number from 2
through 15, it indicates the number of entries in the color table The maximum value of each pixel is 1 less than this number
• For 8-bit DIBs, if the biClrUsed field is 0 or 256, the color table has 256 entries If it's a number from 2
through 255, it indicates the number of entries in the color table The maximum value of each pixel is 1 less
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 40than this number
• For 16-bit, 24-bit, and 32-bit DIBs, the biClrUsed field is usually 0 If it's not 0, it indicates the number of
entries in the color table These entries could be used by an application running with a 256-color video adapter to set a palette for the DIB
Another warning: Programs originally written using the earlier DIB documentation do not expect to see a color table
in 24-bit DIBs You put one in at your own risk
Despite its name, the biClrImportant field is actually much less important than the biClrUsed field It's usually set to
0 to indicate that all colors in the color table are important, or it could be set to the same value as biClrUsed Both mean the same thing If it's set somewhere in between 0 and biClrUsed, it means that the DIB image can be
reasonably rendered using only the first biClrImportant entries in the color table This could be useful when
displaying two or more 8-bit DIBs side by side on a 256-color video adapter
For 1-bit, 4-bit, 8-bit, and 24-bit DIBs, the organization of the pixel bits is the same as in the OS/2-compatible DIB I'll discuss the 16-bit and 32-bit DIBs shortly
Reality Check
What can you expect to find when you encounter a DIB that was created by some other program or person?
Although OS/2-style DIBs were common when Windows 3.0 was first released, they have become quite scarce in recent years Some programmers writing quickie DIB routines virtually ignore them Any 4-bit DIBs you'll encounter will probably have been created in the Windows Paint program using a 16-color video display The color table will have the standard 16 colors on these displays
Probably the most common DIBs you'll find will have a bit count of 8 The 8-bit DIBs will fall into two categories: gray-shade DIBs and palletized color DIBs Unfortunately, nothing in the header indicates what type of 8-bit DIB you're dealing with
Some gray-shade DIBs will have a biClrUsed field equal to 64, indicating 64 entries in the color table These entries
will usually be in order of ascending levels of gray That is, the color table will begin with RGB values of 00-00-00, 04-04-04, 08-08-08, 0C-0C-0C, and conclude with RGB values of F0-F0-F0, F4-F4-F4, F8-F8-F8, and
FC-FC-FC Such a color table is calculated using a formula something like
rgb[i].rgbRed = rgb[i].rgbGreen = rgb[i].rgbBlue = i * 256 / 64 ;
where rgb is an array of RGBQUAD structures and i ranges from 0 through 63 Or the gray-shade color table will
have been calculated with a formula that looks like
rgb[i].rgbRed = rgb[i].rgbGreen = rgb[i].rgbBlue = i * 255 / 63 ;
so that the table ends with FF-FF-FF
It really doesn't matter which formula is used Many video display adapters and monitors don't have a color precision greater than 6 bits anyway The first formula recognizes that fact; the second formula, however, is more appropriate when generating fewer than 64 gray shades, perhaps 16 or 32 (in which case the divisor at the end of the formula is
15 or 31, respectively), because it ensures that the last entry in the color table is FF-FF-FF, which is white
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com