Win32 Series - The Union of DIBs and DDBs

 

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.

 

你可能感兴趣的:(Win32 Series - The Union of DIBs and DDBs)