1. Trang chủ
  2. » Công Nghệ Thông Tin

programming windows phần 6 pdf

128 290 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Programming Windows Phần 6
Trường học University of Programming
Chuyên ngành Computer Science
Thể loại Bài luận
Năm xuất bản 2025
Thành phố Hanoi
Định dạng
Số trang 128
Dung lượng 542,1 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 1

void 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 2

GetObject (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 3

hMenu = 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 4

MENUFILE 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 5

RESOURCE.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 6

BIGHELP.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 7

hFont = (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 8

The 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 9

hMenuPopup = 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 10

AppendMenu (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 11

LRESULT 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 12

LRESULT 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 14

MATTHEW 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 15

This 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 16

The 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 17

LRESULT 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 18

LRESULT 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 20

BOUNCE 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 21

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

SelectObject (hdcMem, hBitmap) ;

srand ((int) GetCurrentTime ()) ;

Trang 22

SCRAMBLE 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 23

The 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 24

LRESULT 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 25

hdc = GetDCEx (hwndScr, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE) ;

GetObject (hBitmapSrc, sizeof (BITMAP), &bitmap) ;

hBitmapDst = CreateBitmapIndirect (&bitmap) ;

static BOOL bCapturing, bBlocking ;

static HBITMAP hBitmap ;

Trang 26

case 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 27

iEnable = 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 29

BLOWUP 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 30

Figure 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 31

I 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 32

Chapter 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 33

The 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 35

The 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 36

BITMAPCOREINFO, * 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 37

The 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 38

The 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 39

BITMAPINFO, * 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 40

than 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

Ngày đăng: 13/08/2014, 08:20

TỪ KHÓA LIÊN QUAN