您的位置:首页 > 其它

Win32 Series - The Union of DIBs and DDBs

2013-12-29 09:29 344 查看
http://www-user.tu-chemnitz.de/~heha/petzold/

The Union of DIBs and DDBs

You can do a lot knowing the format of the DIB and by calling the two DIB-drawing functions,SetDIBitsToDevice and
StretchDIBits. You have direct access to every single bit, byte, and pixel in the DIB, and once you come up with a bunch of functions that let you examine and alter this data in a structured manner, there are no restrictions on what you can do.

Actually, we've found that there are some restrictions. In the last chapter, we saw how you can use GDI functions to draw images on a DDB. So far, there doesn't appear to be any way we can do that with DIBs. Another problem is that SetDIBitsToDevice
and StretchDIBits are not nearly as fast as BitBlt and StretchBlt, particularly under Windows NT and when many nearest-color searches have to be performed, such as when 24-bit DIBs are displayed on 8-bit video boards.

So, it might be advantageous to convert between DIBs and DDBs. For example, if we had a DIB that we needed to display to the screen and we might have to do this numerous times, then it would make more sense to convert the DIB into a DDB so that we could
use the faster BitBlt and StretchBlt functions with it.

Creating a DDB from a DIB

Is it possible to create a GDI bitmap object from a DIB? We basically already know how to do it: If you have a DIB, you can use CreateCompatibleBitmap to create a GDI bitmap object of the same size as the DIB and compatible with the video display.
You then select the bitmap object into a memory device context and call SetDIBitsToDevice to draw on that memory DC. The result is a DDB with the same image as the DIB but with a color organization that is compatible with the video display.

Or you can do the job with a fewer number of steps by using CreateDIBitmap. The function has the following syntax:

hBitmap = CreateDIBitmap (
hdc,        // device context handle
pInfoHdr,   // pointer to DIB information header
fInit,      // 0 or CBM_INIT
pBits,      // pointer to DIB pixel bits
pInfo,      // pointer to DIB information
fClrUse) ;  // color use flag


Notice the two arguments I've called pInfoHdr and pInfo. These are defined as pointers to a BITMAPINFOHEADER structure and a BITMAPINFO structure, respectively. As we know, the BITMAPINFO structure is a BITMAPINFOHEADER structure followed
by a color table. We'll see how this distinction works shortly. The last argument is either DIB_RGB_ COLORS (which equals 0) or DIB_PAL_COLORS, as with the SetDIBitsToDevice functions. I'll have more to say about this in the next chapter.

It is important in understanding the full array of bitmap functions in Windows to realize that, despite its name, the CreateDIBitmap function
does not create a device-independent bitmap. It creates a device-dependent bitmap from a device-independent specification. Notice that the function returns a handle to a GDI bitmap object, the same as CreateBitmap,
CreateBitmapIndirect, and CreateCompatibleBitmap.

The simplest way to call the CreateDIBitmap function is like so:

hBitmap = CreateDIBitmap (NULL, pbmih, 0, NULL, NULL, 0) ;


The only argument is a pointer to a BITMAPINFOHEADER structure (without the color table). In this form, the function creates a monochrome GDI bitmap object. The second simplest way to call the function is

hBitmap = CreateDIBitmap (hdc, pbmih, 0, NULL, NULL, 0) ;


In this form, the function creates a DDB that is compatible with the device context indicated by thehdc argument. So far, we've done nothing we couldn't have done using
CreateBitmap (to create a monochrome bitmap) or CreateCompatibleBitmap (to create one compatible with the video display).

In these two simplified forms of CreateDIBitmap, the pixel bits remain uninitialized. If the third argument to CreateDIBitmap is CBM_INIT, Windows creates the DDB and uses the last three arguments to initialize the bitmap bits. The pInfo
argument is a pointer to a BITMAPINFO structure that includes a color table. The pBits argument is a pointer to an array of bits in the color format indicated by the BITMAPINFO structure. Based on the color table, these bits are converted to the
color format of the device. This is identical to what happens inSetDIBitsToDevice. Indeed, the entire
CreateDIBitmap function could probably be implemented with the following code:

HBITMAP CreateDIBitmap (HDC hdc, CONST BITMAPINFOHEADER * pbmih,
DWORD fInit, CONST VOID * pBits,
CONST BITMAPINFO * pbmi, UINT fUsage)
{
HBITMAP hBitmap ;
HDC     hdc ;
int     cx, cy, iBitCount ;

if (pbmih->biSize == sizeof (BITMAPCOREHEADER))
{
cx        = ((PBITMAPCOREHEADER) pbmih)->bcWidth ;
cy        = ((PBITMAPCOREHEADER) pbmih)->bcHeight ;
iBitCount = ((PBITMAPCOREHEADER) pbmih)->bcBitCount ;
}
else
{
cx        = pbmih->biWidth ;
cy        = pbmih->biHeight ;
iBitCount = pbmih->biBitCount ;
}
if (hdc)
hBitmap = CreateCompatibleBitmap (hdc, cx, cy) ;
else
hBitmap = CreateBitmap (cx, cy, 1, 1, NULL) ;

if (fInit == CBM_INIT)
{
hdcMem = CreateCompatibleDC (hdc) ;
SelectObject (hdcMem, hBitmap) ;
SetDIBitsToDevice (hdcMem, 0, 0, cx, cy, 0, 0, 0 cy,
pBits, pbmi, fUsage) ;
DeleteDC (hdcMem) ;
}

return hBitmap ;
}


If you're going to display a DIB only once and you're worried about the performance ofSetDIBitsToDevice, it doesn't make much sense to call
CreateDIBitmap and then display the DDB by using BitBlt or
StretchBlt. The two jobs will take the same length of time becauseSetDIBitsToDevice and
CreateDIBitmap both have to perform a color conversion. Only if you're displaying a DIB multiple times—which is very likely when processing WM_PAINT messages—does this conversion make sense.

The DIBCONV program shown in Figure 15-10 shows how you can use SetDIBitsToDevice to convert a DIB file to a DDB.

Figure 15-10. The DIBCONV program.

DIBCONV.C

/*----------------------------------------
DIBCONV.C -- Converts a DIB to a DDB
(c) Charles Petzold, 1998
----------------------------------------*/

#include <windows.h>
#include <commdlg.h>
#include "resource.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

TCHAR szAppName[] = TEXT ("DibConv") ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND     hwnd ;
MSG      msg ;
WNDCLASS wndclass ;

wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc   = WndProc ;
wndclass.cbClsExtra    = 0 ;
wndclass.cbWndExtra    = 0 ;
wndclass.hInstance     = hInstance ;
wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName  = szAppName ;
wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT ("DIB to DDB Conversion"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;

}
return msg.wParam ;
}
HBITMAP CreateBitmapObjectFromDibFile (HDC hdc, PTSTR szFileName)
{
BITMAPFILEHEADER * pbmfh ;
BOOL               bSuccess ;
DWORD              dwFileSize, dwHighSize, dwBytesRead ;
HANDLE             hFile ;
HBITMAP            hBitmap ;

// Open the file: read access, prohibit write access

hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL) ;

if (hFile == INVALID_HANDLE_VALUE)
return NULL ;

// Read in the whole file

dwFileSize = GetFileSize (hFile, &dwHighSize) ;

if (dwHighSize)
{
CloseHandle (hFile) ;
return NULL ;
}

pbmfh = malloc (dwFileSize) ;

if (!pbmfh)
{
CloseHandle (hFile) ;
return NULL ;
}

bSuccess = ReadFile (hFile, pbmfh, dwFileSize, &dwBytesRead, NULL) ;
CloseHandle (hFile) ;

// Verify the file

if (!bSuccess || (dwBytesRead != dwFileSize)
|| (pbmfh->bfType != * (WORD *) "BM")
|| (pbmfh->bfSize != dwFileSize))
{
free (pbmfh) ;
return NULL ;
}
// Create the DDB

hBitmap = CreateDIBitmap (hdc,
(BITMAPINFOHEADER *) (pbmfh + 1),
CBM_INIT,
(BYTE *) pbmfh + pbmfh->bfOffBits,
(BITMAPINFO *) (pbmfh + 1),
DIB_RGB_COLORS) ;
free (pbmfh) ;
return hBitmap ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP      hBitmap ;
static int          cxClient, cyClient ;
static OPENFILENAME ofn ;
static TCHAR        szFileName [MAX_PATH], szTitleName [MAX_PATH] ;
static TCHAR        szFilter[] = TEXT ("Bitmap Files (*.BMP)\0*.bmp\0")
TEXT ("All Files (*.*)\0*.*\0\0") ;
BITMAP              bitmap ;
HDC                 hdc, hdcMem ;
PAINTSTRUCT         ps ;

switch (message)
{
case WM_CREATE:
ofn.lStructSize       = sizeof (OPENFILENAME) ;
ofn.hwndOwner         = hwnd ;
ofn.hInstance         = NULL ;
ofn.lpstrFilter       = szFilter ;
ofn.lpstrCustomFilter = NULL ;
ofn.nMaxCustFilter    = 0 ;
ofn.nFilterIndex      = 0 ;
ofn.lpstrFile         = szFileName ;
ofn.nMaxFile          = MAX_PATH ;
ofn.lpstrFileTitle    = szTitleName ;
ofn.nMaxFileTitle     = MAX_PATH ;
ofn.lpstrInitialDir   = NULL ;
ofn.lpstrTitle        = NULL ;
ofn.Flags             = 0 ;
ofn.nFileOffset       = 0 ;
ofn.nFileExtension    = 0 ;
ofn.lpstrDefExt       = TEXT ("bmp") ;
ofn.lCustData         = 0 ;
ofn.lpfnHook          = NULL ;
ofn.lpTemplateName    = NULL ;

return 0 ;

case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_FILE_OPEN:

// Show the File Open dialog box

if (!GetOpenFileName (&ofn))
return 0 ;

// If there's an existing DIB, delete it

if (hBitmap)
{
DeleteObject (hBitmap) ;
hBitmap = NULL ;
}
// Create the DDB from the DIB

SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
ShowCursor (TRUE) ;

hdc = GetDC (hwnd) ;
hBitmap = CreateBitmapObjectFromDibFile (hdc, szFileName) ;
ReleaseDC (hwnd, hdc) ;

ShowCursor (FALSE) ;
SetCursor (LoadCursor (NULL, IDC_ARROW)) ;

// Invalidate the client area for later update

InvalidateRect (hwnd, NULL, TRUE) ;

if (hBitmap == NULL)
{
MessageBox (hwnd, TEXT ("Cannot load DIB file"),
szAppName, MB_OK | MB_ICONEXCLAMATION) ;
}
return 0 ;
}
break ;

case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;

if (hBitmap)
{
GetObject (hBitmap, sizeof (BITMAP), &bitmap) ;

hdcMem = CreateCompatibleDC (hdc) ;
SelectObject (hdcMem, hBitmap) ;

BitBlt (hdc,    0, 0, bitmap.bmWidth, bitmap.bmHeight,
hdcMem, 0, 0, SRCCOPY) ;

DeleteDC (hdcMem) ;
}

EndPaint (hwnd, &ps) ;
return 0 ;

case WM_DESTROY:
if (hBitmap)
DeleteObject (hBitmap) ;

PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}


DIBCONV.RC (excerpts)

//Microsoft Developer Studio generated resource script.

#include "resource.h"
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
// Menu

DIBCONV MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open",                       IDM_FILE_OPEN
END
END


RESOURCE.H (excerpts)

// Microsoft Developer Studio generated include file.
// Used by DibConv.rc

#define IDM_FILE_OPEN                   40001


DIBCONV.C is self-contained and requires no earlier files. In response to its only menu command (File Open),WndProc calls the program's
CreateBitmapObjectFromDibFile function. This function reads the entire file into memory and passes pointers to the memory block to theCreateDIBitmap function. The function returns a handle to the bitmap. The memory block containing the DIB
can then be freed. During the WM_PAINT message,WndProc selects the bitmap in a compatible memory device context and uses BitBlt rather than
SetDIBitsToDevice to display the bitmap on the client area. It obtains the width and height of the bitmap by calling GetObject with the BITMAP structure on the bitmap handle.

You do not need to initialize the DDB pixel bits while creating the bitmap from CreateDIBitmap. You can do it later by calling
SetDIBits. This function has the following syntax:

iLines = SetDIBits (
hdc,        // device context handle
hBitmap,    // bitmap handle
yScan,      // first scan line to convert
cyScans,    // number of scan lines to convert
pBits,      // pointer to pixel bits
pInfo,      // pointer to DIB information
fClrUse) ;  // color use flag


The function uses the color table in the BITMAPINFO structure to convert the bits into the device-dependent format. The device context handle is required only if the last argument is set to DIB_PAL_COLORS.

From DDB to DIB

A function similar to the SetDIBits function is GetDIBits. You can use this function for converting a DDB to a DIB:

int WINAPI GetDIBits (
hdc,        // device context handle
hBitmap,    // bitmap handle
yScan,      // first scan line to convert
cyScans,    // number of scan lines to convert
pBits,      // pointer to pixel bits (out)
pInfo,      // pointer to DIB information (out)
fClrUse) ;  // color use flag


However, I'm afraid that this function is not simply the reverse of SetDIBits. In the general case, if you convert a DIB to a DDB using CreateDIBitmap and
SetDIBits and then convert back to a DIB usingGetDIBits, you won't get what you started out with. This is because some information is lost when a DIB is converted to a device-dependent format. How much information is lost depends on the
particular video mode you're running Windows under when you do the conversion.

You probably won't find a need to use GetDIBits much. Think about it: In what circumstances does your program find itself with a bitmap handle without having the data used to create the bitmap in the first place? The clipboard? But the clipboard
provides automatic conversion to DIBs. The one instance in which the GetDIBits functiondoes come in handy is when you're doing screen captures, such as what the BLOWUP program did inChapter
14. I won't be demonstrating this function, but some information is available in Knowledge Base article Q80080.

The DIB Section

Now, I hope, you have a good feel for the difference between device-dependent and device-independent bitmaps. A DIB can have one of several color organizations; a DDB must be either monochrome or the same format as a real-output device. A DIB is a file
or a block of memory; a DDB is a GDI bitmap object and is represented by a bitmap handle. A DIB can be displayed or converted to a DDB and back again, but this involves a process to convert between device-independent bits and device-specific bits.

Now you're about to encounter a function that seems to break these rules. This function was introduced in the 32-bit versions of Windows and is called CreateDIBSection. The syntax is

hBitmap = CreateDIBSection (
hdc,         // device context handle
pInfo,       // pointer to DIB information
fClrUse,     // color use flag
ppBits,      // pointer to pointer variable
hSection,    // file-mapping object handle
dwOffset) ;  // offset to bits in file-mapping object


CreateDIBSection is one of the most important functions in the Windows API (well, at least if you're working with bitmaps a lot), yet it's burdened with such weirdness that you may find it inordinately esoteric and difficult to comprehend.

Let's begin with the very name of the function. We know what a DIB is, but what on earth is a "DIB section"? When you first began examining CreateDIBSection, you may have kept looking for some way that the function works with only part of the DIB.
That's almost right. WhatCreateDIBSection does is indeed create a section of the DIB—a memory block for the bitmap pixel bits.

Now let's look at the return value. It's a handle to a GDI bitmap object. That return value is probably the most deceptive aspect of the function call. The return value seems to imply thatCreateDIBSection is similar in functionality to
CreateDIBitmap. Yes, it's similar but also totally different. In fact, the bitmap handle returned from CreateDIBSection is intrinsically different from the bitmap handle returned from all the previous bitmap-creation functions we've encountered
in this chapter and the last chapter.

Once you understand the true nature of CreateDIBSection, you might wonder why the return value wasn't defined somewhat differently. You might also conclude that CreateDIBSection should have been called
CreateDIBitmap and that CreateDIBitmap should have been called, as I indicated earlier,
CreateDDBitmap.

To first approach CreateDIBSection, let's examine how we can simplify it and put it to use right away. First, you can set the last two arguments, hSection and
dwOffset, to NULL and 0, respectively. I'll discuss the use of these arguments towards the end of this chapter. Second, thehdc parameter is used only if the
fColorUse parameter is set to DIB_ PAL_COLORS. If fColorUse is DIB_RGB_COLORS (or 0),
hdc is ignored. (This is not the case with CreateDIBitmap, in which the
hdc parameter is used to get the color format of the device that the DDB is to be compatible with.)

So, in its simplest form, CreateDIBSection requires only the second and fourth arguments. The second argument is a pointer to a BITMAPINFO structure, something we've worked with before. I hope the pointer to a pointer definition of the fourth
argument doesn't upset you too much. It's actually quite simple when using the function.

Let's suppose you want to create a 384×256-bit DIB with 24 bits per pixel. The 24-bit format is simplest because it doesn't require a color table, so we can use a BITMAPINFOHEADER structure for the BITMAPINFO parameter.

You define three variables: a BITMAPINFOHEADER structure, a BYTE pointer, and a bitmap handle:

BITMAPINFOHEADER bmih ;
BYTE           * pBits ;
HBITMAP          hBitmap ;


Now initialize the fields of the BITMAPINFOHEADER structure:

bmih->biSize          = sizeof (BITMAPINFOHEADER) ;
bmih->biWidth         = 384 ;
bmih->biHeight        = 256 ;
bmih->biPlanes        = 1 ;
bmih->biBitCount      = 24 ;
bmih->biCompression   = BI_RGB ;
bmih->biSizeImage     = 0 ;
bmih->biXPelsPerMeter = 0 ;
bmih->biYPelsPerMeter = 0 ;
bmih->biClrUsed       = 0 ;
bmih->biClrImportant  = 0 ;


With this minimum amount of preparation, we are now ready to call the function:

hBitmap = CreateDIBSection (NULL, (BITMAPINFO *)  &bmih, 0, &pBits, NULL, 0) ;


Notice that we're taking the address of the BITMAPINFOHEADER structure for the second argument, as usual, but also the address of the BYTE pointer pBits, which is not usual. Thus, the fourth argument is a pointer to a pointer, as required by the
function.

Here's what the function call does: CreateDIBSection examines the BITMAPINFOHEADER structure and allocates a block of memory sufficient to hold the DIB pixel bits. (In this particular case, the block is 384×256×3 bytes in size.) It stores a pointer
to this memory block in the pBits parameter that you've supplied. The function also returns a handle to a bitmap, which, as I've said, is not quite the same as the handle returned fromCreateDIBitmap and other bitmap-creation functions.

We're not quite done yet, however. The bitmap pixel bits are uninitialized. If you're reading a DIB file, you can simply pass the pBits parameter to the
ReadFile function and read them in. Or you can set them "manually" with some program code.

The DIBSECT program shown in Figure 15-11 is similar to the DIBCONV program except that it calls CreateDIBSection rather than
CreateDIBitmap.

Figure 15-11. The DIBSECT program.

DIBSECT.C

/*--------------------------------------------------------
DIBSECT.C -- Displays a DIB Section in the client area
(c) Charles Petzold, 1998
--------------------------------------------------------*/
#include <windows.h>
#include <commdlg.h>
#include "resource.h"

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

TCHAR szAppName[] = TEXT ("DibSect") ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND     hwnd ;
MSG      msg ;
WNDCLASS wndclass ;
wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc   = WndProc ;
wndclass.cbClsExtra    = 0 ;
wndclass.cbWndExtra    = 0 ;
wndclass.hInstance     = hInstance ;
wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName  = szAppName ;
wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT ("DIB Section Display"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;

}
return msg.wParam ;
}
HBITMAP CreateDibSectionFromDibFile (PTSTR szFileName)
{
BITMAPFILEHEADER bmfh ;
BITMAPINFO     * pbmi ;
BYTE           * pBits ;
BOOL             bSuccess ;
DWORD            dwInfoSize, dwBytesRead ;
HANDLE           hFile ;
HBITMAP          hBitmap ;
// Open the file: read access, prohibit write access

hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL) ;

if (hFile == INVALID_HANDLE_VALUE)
return NULL ;

// Read in the BITMAPFILEHEADER

bSuccess = ReadFile (hFile, &bmfh, sizeof (BITMAPFILEHEADER),
&dwBytesRead, NULL) ;

if (!bSuccess || (dwBytesRead != sizeof (BITMAPFILEHEADER))
|| (bmfh.bfType != * (WORD *) "BM"))
{
CloseHandle (hFile) ;
return NULL ;
}

// Allocate memory for the BITMAPINFO structure & read it in

dwInfoSize = bmfh.bfOffBits - sizeof (BITMAPFILEHEADER) ;

pbmi = malloc (dwInfoSize) ;

bSuccess = ReadFile (hFile, pbmi, dwInfoSize, &dwBytesRead, NULL) ;

if (!bSuccess || (dwBytesRead != dwInfoSize))
{
free (pbmi) ;
CloseHandle (hFile) ;
return NULL ;
}
// Create the DIB Section

hBitmap = CreateDIBSection (NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0) ;

if (hBitmap == NULL)
{
free (pbmi) ;
CloseHandle (hFile) ;
return NULL ;
}

// Read in the bitmap bits

ReadFile (hFile, pBits, bmfh.bfSize - bmfh.bfOffBits, &dwBytesRead, NULL) ;

free (pbmi) ;
CloseHandle (hFile) ;

return hBitmap ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP      hBitmap ;
static int          cxClient, cyClient ;
static OPENFILENAME ofn ;
static TCHAR        szFileName [MAX_PATH], szTitleName [MAX_PATH] ;
static TCHAR        szFilter[] = TEXT ("Bitmap Files (*.BMP)\0*.bmp\0")
TEXT ("All Files (*.*)\0*.*\0\0") ;
BITMAP              bitmap ;
HDC                 hdc, hdcMem ;
PAINTSTRUCT         ps ;

switch (message)
{
case WM_CREATE:
ofn.lStructSize       = sizeof (OPENFILENAME) ;
ofn.hwndOwner         = hwnd ;
ofn.hInstance         = NULL ;
ofn.lpstrFilter       = szFilter ;
ofn.lpstrCustomFilter = NULL ;
ofn.nMaxCustFilter    = 0 ;
ofn.nFilterIndex      = 0 ;
ofn.lpstrFile         = szFileName ;
ofn.nMaxFile          = MAX_PATH ;
ofn.lpstrFileTitle    = szTitleName ;
ofn.nMaxFileTitle     = MAX_PATH ;
ofn.lpstrInitialDir   = NULL ;
ofn.lpstrTitle        = NULL ;
ofn.Flags             = 0 ;
ofn.nFileOffset       = 0 ;
ofn.nFileExtension    = 0 ;
ofn.lpstrDefExt       = TEXT ("bmp") ;
ofn.lCustData         = 0 ;
ofn.lpfnHook          = NULL ;
ofn.lpTemplateName    = NULL ;

return 0 ;

case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;

case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_FILE_OPEN:

// Show the File Open dialog box

if (!GetOpenFileName (&ofn))
return 0 ;

// If there's an existing bitmap, delete it

if (hBitmap)
{
DeleteObject (hBitmap)                 hBitmap = NULL ;
}
// Create the DIB Section from the DIB file

SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
ShowCursor (TRUE) ;

hBitmap = CreateDibSectionFromDibFile (szFileName) ;
ShowCursor (FALSE) ;
SetCursor (LoadCursor (NULL, IDC_ARROW)) ;

// Invalidate the client area for later update

InvalidateRect (hwnd, NULL, TRUE) ;

if (hBitmap == NULL)
{
MessageBox (hwnd, TEXT ("Cannot load DIB file"),
szAppName, MB_OK | MB_ICONEXCLAMATION) ;
}
return 0 ;
}
break ;

case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;

if (hBitmap)
{
GetObject (hBitmap, sizeof (BITMAP), &bitmap) ;

hdcMem = CreateCompatibleDC (hdc) ;
SelectObject (hdcMem, hBitmap) ;

BitBlt (hdc,    0, 0, bitmap.bmWidth, bitmap.bmHeight,
hdcMem, 0, 0, SRCCOPY) ;

DeleteDC (hdcMem) ;
}

EndPaint (hwnd, &ps) ;
return 0 ;

case WM_DESTROY:
if (hBitmap)
DeleteObject (hBitmap) ;

PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}


DIBSECT.RC (excerpts)

//Microsoft Developer Studio generated resource script.

#include "resource.h"
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////

// Menu

DIBSECT MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open",                       IDM_FILE_OPEN
END
END


RESOURCE.H (excerpts)

// Microsoft Developer Studio generated include file.
// Used by DibSect.rc

#define IDM_FILE_OPEN                   40001


Notice the differences between the CreateBitmapObjectFromDibFile function in DIBCONV and the CreateDibSectionFromDibFile function in DIBSECT. DIBCONV reads the entire file in one shot and then passes pointers to the DIB memory block to
the CreateDIBitmap function. DIBSECT reads in the BITMAPFILEHEADER structure first and then determines how big the BITMAPINFO structure is. Memory is allocated for that, and it's read in on the secondReadFile call. The function then passes
pointers to the BITMAPINFO structure and to the pointer variable pBits to
CreateDIBSection. The function returns a bitmap handle and setspBits to point to a block of memory into which the function then reads the DIB pixel bits.

The memory block pointed to by pBits is owned by the system. The memory is automatically freed when you delete the bitmap by calling DeleteObject. However, programs can use the pointer to alter the DIB bits directly. That the system owns
this memory block makes it not subject to the speed penalty incurred under Windows NT when an application passes large memory blocks across the API.

As I noted above, when you display a DIB on a video display, at some point it must undergo a conversion from device-independent pixels to device-dependent pixels. Sometimes this format conversion can be lengthy. Let's look at the three approaches we've
used to display DIBs:

When you use SetDIBitsToDevice or StretchDIBits to display a DIB directly to the screen, the format conversion occurs during the SetDIBitsToDevice or
StretchDIBits call.

When you convert a DIB to a DDB using CreateDIBitmap and (possibly) SetDIBits and then use
BitBlt or StretchBlt to display it, the format conversion occurs duringCreateDIBitmap, if the CBM_INIT flag is set, or
SetDIBits.

When you create a DIB section using CreateDIBSection and then display it using BitBlt or
StretchBlt, the format conversion occurs during the BitBlt to
StretchBlt call.

Read that last sentence over again and make sure you didn't misread it. This is one way in which the bitmap handle returned from CreateDIBSection is different from the other bitmap handles we've encountered. This bitmap handle actually references
a DIB that is stored in memory maintained by the system but to which an application has access. This DIB is converted to a particular color format when necessary, which is usually when it's displayed using
BitBlt or StretchBlt.

You can also select the bitmap handle into a memory device context and use GDI functions to draw on it. The results will be reflected in the DIB pixel bits pointed to by thepBits variable. Because of batching of GDI calls under Windows NT, call GdiFlush
after drawing on the memory device context before accessing the bits "manually."

In DIBSECT we discarded the pBits variable because it was no longer required by the program. If you need to alter the bits directly, which is a major reason why you'll useCreateDIBSection, hold onto it. There seems to be no way to later
obtain the bits pointer after theCreateDIBSection call.

More DIB Section Differences

The bitmap handle returned from CreateDIBitmap has the same planes and bits-per-pixel organization as the device referenced by the hdc parameter to the function. You can verify this by calling
GetObject with the BITMAP structure.

CreateDIBSection is different. If you call GetObject with the BITMAP structure on the bitmap handle returned from the function, you'll find that the bitmap has the same color organization as indicated by the fields of the BITMAPINFOHEADER
structure. Yet you can select this handle into a memory device context compatible with the video display. This contradicts what I said in the last chapter about DDBs, of course, but that's why I contend that this DIB section bitmap handle is different.

Another oddity: As you'll recall, the byte length of the rows of pixel data in DIBs is always a multiple of 4. The byte length of rows in GDI bitmap objects, which you can get from thebmWidthBytes field of the BITMAP structure used with
GetObject, is always a multiple of 2. Well, if you set up the BITMAPINFOHEADER structure shown above with 24 bits per pixel and a width of 2 pixels (for example) and later call GetObject, you'll find that the
bmWidthBytes field is 8 rather than 6.

With the bitmap handle returned from CreateDIBSection, you can also call GetObject with a DIBSECTION structure, like so:

GetObject (hBitmap, sizeof (DIBSECTION), &dibsection) ;


This won't work with a bitmap handle returned from any of the other bitmap-creation functions. The DIBSECTION structure is defined like so:

typedef struct tagDIBSECTION  // ds
{
BITMAP           dsBm ;             // BITMAP structure
BITMAPINFOHEADER dsBmih ;           // DIB information header
DWORD            dsBitfields [3] ;  // color masks
HANDLE           dshSection ;       // file-mapping object handle
DWORD            dsOffset ;         // offset to bitmap bits
}
DIBSECTION, * PDIBSECTION ;


This structure contains both a BITMAP structure and a BITMAPINFOHEADER structure. The last two fields are the last two arguments passed to CreateDIBSection, which I'll discuss shortly.

The DIBSECTION structure tells you much of what you need to know about the bitmap, except for the color table. When you select the DIB section bitmap handle into a memory device context, you can get the color table by calling GetDIBColorTable:

hdcMem = CreateCompatibleDC (NULL) ;
SelectObject (hdcMem, hBitmap) ;
GetDIBColorTable (hdcMem, uFirstIndex, uNumEntries, &rgb) ;
DeleteDC (hdcMem) ;


Similary, you can set entries in the color table by calling SetDIBColorTable.

The File-Mapping Option

I haven't yet discussed the last two arguments to CreateDIBSection, which are a handle to a file-mapping object and an offset within that file where the bitmap bits begin. A file-mapping object allows you to treat a file as if it were located
in memory. That is, you can access the file by using memory pointers, but the file needn't be entirely located in memory.

In the case of large DIBs, this technique can help reduce memory requirements. The DIB pixel bits can remain on disk but still be accessed as if they were in memory, albeit with a performance penalty. The problem is, while the pixel bits can indeed remain
stored on disk, they can't be part of an actual DIB file. They'd have to be in some other file.

To demonstrate, the function shown below is very similar to the function that creates the DIB section in DIBSECT except that it doesn't read the pixel bits into memory; instead, it supplies a file-mapping object and an offset to the CreateDIBSection
function:

HBITMAP CreateDibSectionMappingFromFile (PTSTR szFileName)
{
BITMAPFILEHEADER bmfh ;
BITMAPINFO     * pbmi ;
BYTE           * pBits ;
BOOL             bSuccess ;
DWORD            dwInfoSize, dwBytesRead ;
HANDLE           hFile, hFileMap ;
HBITMAP          hBitmap ;

hFile = CreateFile (szFileName, GENERIC_READ | GENERIC_WRITE,
0,                  // No sharing!
NULL, OPEN_EXISTING, 0, NULL) ;

if (hFile == INVALID_HANDLE_VALUE)
return NULL ;

bSuccess = ReadFile (hFile, &bmfh, sizeof (BITMAPFILEHEADER),
&dwBytesRead, NULL) ;

if (!bSuccess || (dwBytesRead != sizeof (BITMAPFILEHEADER))
|| (bmfh.bfType != * (WORD *) "BM"))
{
CloseHandle (hFile) ;
return NULL ;
}
dwInfoSize = bmfh.bfOffBits - sizeof (BITMAPFILEHEADER) ;
pbmi = malloc (dwInfoSize) ;
bSuccess = ReadFile (hFile, pbmi, dwInfoSize, &dwBytesRead, NULL) ;

if (!bSuccess || (dwBytesRead != dwInfoSize))
{
free (pbmi) ;
CloseHandle (hFile) ;
return NULL ;
}
hFileMap = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL) ;

hBitmap = CreateDIBSection (NULL, pbmi, DIB_RGB_COLORS, &pBits,
hFileMap, bmfh.bfOffBits) ;
free (pbmi) ;
return hBitmap ;
}


Alas, this does not work. The documentation of CreateDIBSection indicates that "dwOffset [the final argument to the function] must be a multiple of the size of a DWORD." Although the size of the information header is always a multiple
of 4 and the size of the color table is always a multiple of 4, the bitmap file header is not. It's 14 bytes. Sobmfh.bfOffBits is never a multiple of 4.

In Summary

If you have small DIBs and you need to frequently manipulate the pixel bits, you can display them usingSetDIBitsToDevice and
StretchDIBits. However, for larger DIBs, this technique will encounter performance problems, particularly on 8-bit video displays and under Windows NT.

You can convert a DIB to a DDB by using CreateDIBitmap and SetDIBits. Displaying the bitmap will now involve the speedy BitBlt and
StretchBlt functions. However, you no longer have access to the device-independent pixel bits.

CreateDIBSection is a good compromise. Using the bitmap handle with
BitBlt and StretchBlt gives you better performance under Windows NT than using SetDIBitsToDevice and
StretchDIBits but with none of the drawbacks of the DDB. You still have access to the DIB pixel bits.

In the next chapter, we'll wrap up our exploration of bitmaps after looking at the Windows Palette Manager.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐