| Tenouk C & C++ | MFC Home | Bitmaps 4 | Windows Message Processing & Multithreaded Programming 1 | Download |Site Index |


 

 

 

 

 

 

Module 21d: The Bitmaps 5

 

 

 

 

 

 

This is a continuation from the previous module... Program examples compiled using Visual C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this Tutorial are listed below:

  1. Going Further with DIBs

  2. The LoadImage() Function

  3. The DrawDibDraw() Function

  4. Putting Bitmaps on Pushbuttons

  5. The MYMFC26D Example

  6. Going Further with Bitmap Buttons

 

 

 

Going Further with DIBs

 

Each new version of Windows offers more DIB programming choices. Both Windows 95 and Microsoft Windows NT 4.0 provide the LoadImage() and DrawDibDraw() functions, which are useful alternatives to the DIB functions already described. Experiment with these functions to see if they work well in your applications.

 

The LoadImage() Function

 

The LoadImage() function can read a bitmap directly from a disk file, returning a DIB section handle. It can even process OS/2 (already discontinued OS) format DIBs. Suppose you wanted to add an ImageLoad() member function to CDib that would work like ReadSection(). This is the code you would add to cdib.cpp:

 

BOOL CDib::ImageLoad(const char* lpszPathName, CDC* pDC)

{

    Empty();

    m_hBitmap = (HBITMAP) ::LoadImage(NULL, lpszPathName, IMAGE_BITMAP, 0, 0,

        LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);

    DIBSECTION ds;

    VERIFY(::GetObject(m_hBitmap, sizeof(ds), &ds) == sizeof(ds));

    // Allocate memory for BITMAPINFOHEADER

    //  and biggest possible color table

    m_lpBMIH = (LPBITMAPINFOHEADER) new

        char[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];

    memcpy(m_lpBMIH, &ds.dsBmih, sizeof(BITMAPINFOHEADER));

    TRACE("CDib::LoadImage, biClrUsed = %d, biClrImportant = %d\n",

        m_lpBMIH->biClrUsed, m_lpBMIH->biClrImportant);

    ComputeMetrics(); // sets m_lpvColorTable

    m_nBmihAlloc = crtAlloc;

    m_lpImage = (LPBYTE) ds.dsBm.bmBits;

    m_nImageAlloc = noAlloc;

    // Retrieve the DIB section's color table and make a palette from it

    CDC memdc;

    memdc.CreateCompatibleDC(pDC);

    ::SelectObject(memdc.GetSafeHdc(), m_hBitmap);

    UINT nColors = ::GetDIBColorTable(memdc.GetSafeHdc(), 0, 256, (RGBQUAD*) m_lpvColorTable);

    if (nColors != 0) {

        ComputePaletteSize(m_lpBMIH->biBitCount);

        MakePalette();

    }

    // memdc deleted and bitmap deselected

    return TRUE;

}

 

Note that this function extracts and copies the BITMAPINFOHEADER structure and sets the values of the CDib pointer data members. You must do some work to extract the palette from the DIB section, but the Win32 GetDIBColorTable() function gets you started. It's interesting that GetDIBColorTable() can't tell you how many palette entries a particular DIB uses. If the DIB uses only 60 entries, for example, GetDIBColorTable() generates a 256-entry color table with the last 196 entries set to 0.

 

The DrawDibDraw() Function

 

Windows includes the Video for Windows (VFW) component, which is supported by Visual C++. The VFW DrawDibDraw() function is an alternative to StretchDIBits(). One advantage of DrawDibDraw() is its ability to use dithered colors. Another is its increased speed in drawing a DIB with a bpp value that does not match the current video mode. The main disadvantage is the need to link the VFW code into your process at runtime. Shown below is a DrawDib() member function for the CDib class that calls DrawDibDraw():

 

BOOL CDib::DrawDib(CDC* pDC, CPoint origin, CSize size)

{

    if (m_lpBMIH == NULL) return FALSE;

    if (m_hPalette != NULL) {

        ::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);

    }

    HDRAWDIB hdd = ::DrawDibOpen();

    CRect rect(origin, size);

    pDC->LPtoDP(rect); // Convert DIB's rectangle to MM_TEXT coordinates

    rect -= pDC->GetViewportOrg();

    int nMapModeOld = pDC->SetMapMode(MM_TEXT);

    ::DrawDibDraw(hdd, pDC->GetSafeHdc(), rect.left, rect.top, rect.Width(), rect.Height(), m_lpBMIH, m_lpImage, 0, 0, m_lpBMIH->biWidth, m_lpBMIH->biHeight, 0);

    pDC->SetMapMode(nMapModeOld);

    VERIFY(::DrawDibClose(hdd));

    return TRUE;

}

 

Note that DrawDibDraw() needs MM_TEXT coordinates and the MM_TEXT mapping mode. Thus, logical coordinates must be converted not to device coordinates but to pixels with the origin at the top left of the scrolling window. To use DrawDibDraw(), your program needs an #include <vfw.h> statement, and you must add vfw32.lib to the list of linker input files. DrawDibDraw() might assume the bitmap it draws is in read/write memory, a fact to keep in mind if you map the memory to the BMP file.

 

Putting Bitmaps on Pushbuttons

 

The MFC library makes it easy to display a bitmap (instead of text) on a pushbutton. If you were to program this from scratch, you would set the Owner Draw property for your button and then write a message handler in your dialog class that would paint a bitmap on the button control's window. If you use the MFC CBitmapButton class instead, you end up doing a lot less work, but you have to follow a kind of "cookbook" procedure. Don't worry too much about how it all works (but be glad that you don't have to write much code!).

 

To make a long story short, you lay out your dialog resource as usual with unique text captions for the buttons you designate for bitmaps. Next you add some bitmap resources to your project, and you identify those resources by name rather than by numeric ID. Finally you add some CBitmapButton data members to your dialog class, and you call the AutoLoad() member function for each one, which matches a bitmap name to a button caption. If the button caption is "Copy", you add two bitmaps: "COPYU" for the up state and "COPYD" for the down state. By the way, you must still set the button's Owner Draw property. This will all make more sense when you write a program.

 

If you look at the MFC source code for the CBitmapButton class, you'll see that the bitmap is an ordinary GDI bitmap painted with a BitBlt() call. Thus, you can't expect any palette support. That's not often a problem because bitmaps for buttons are usually 16-color bitmaps that depend on standard VGA colors.

 

The MYMFC26D Project Example

 

Here are the steps for building MYMFC26D:

 

Run AppWizard to produce \mfcproject\mymfc26D. Accept all the defaults but three: select Single Document, deselect Printing And Print Preview, and select Context-Sensitive Help.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

----------------------------------------------------------------------------------------------------------------------

Step 4 of 6 AppWizard for MYMFC26D project, selecting the Context-sensitive Help.

 

Figure 23: Step 4 of 6 AppWizard for MYMFC26D project, selecting the Context-sensitive Help.

 

The options and the default class names are shown in the illustration below.

 

MYMFC26D project summary.

 

Figure 24: MYMFC26D project summary.

 

The Context-Sensitive Help option was selected for one reason only: it causes AppWizard to copy some bitmap files into your project's \hlp subdirectory. These bitmaps are supposed to be bound into your project's help file.

 

Modify the project's IDD_ABOUTBOX dialog resource. It's too much hassle to create a new dialog resource for a few buttons, so we'll use the About dialog that AppWizard generates for every project. Add three pushbuttons with captions, as shown below, accepting the default IDs IDC_BUTTON1, IDC_BUTTON2, and IDC_BUTTON3. The size of the buttons isn't important because the framework adjusts the button size at runtime to match the bitmap size.

 

Using the About dialog, adding button controls and modifying their properties.

 

Figure 25: Using the About dialog, adding button controls and modifying their properties.

 

Select the Owner Draw property for all three buttons.

 

Import three bitmaps from the project's \hlp subdirectory. Choose Resource from Visual C++'s Insert menu, select the Bitmap and then click the Import button. Start with EditCopy.bmp, as shown below.

 

Importing three bitmaps from the project's \hlp subdirectory.

 

Figure 26: Importing three bitmaps from the project's \hlp subdirectory.

 

Modifying the properties of the imported bitmaps.

 

Figure 27: Modifying the properties of the imported bitmaps.

 

Assign the name "COPYU" as shown.

 

Be sure to use quotes around the name in order to identify the resource by name rather than by ID. This is now the bitmap for the button's up state. Close the bitmap window and, from the ResourceView window, use the clipboard (or drag and drop or Edit, Copy/Paste menu) to make a copy of the bitmap. Rename the copy "COPYD" (down state), and then edit this bitmap. Choose Invert Colors from the Image menu. There are other ways of making a variation of the up image, but inversion is the quickest.

Repeat the steps listed above for the EditCut and EditPast bitmaps. When you're finished, you should have the following bitmap resources in your project.

 

Resource Name

Original File

Invert Colors

"COPYU"

EditCopy.bmp

no

"COPYD"

EditCopy.bmp

yes

"CUTU"

EditCut.bmp

no

"CUTD"

EditCut.bmp

yes

"PASTEU"

EditPast.bmp

no

"PASTED"

EditPast.bmp

yes

 

Table 18.

 

 

Using the image editor utility to invert color.

 

Figure 28: Using the image editor utility to invert color.

 

Inverting the bitmap color.

 

Figure 29: The inverted bitmap color.

 

Edit the code for the CAboutDlg class. Both the declaration and the implementation for this class are contained in the mymfc26D.cpp file. First add the three private data members shown here in the class declaration:

 

Adding three private data members to CAboutDlg class.

 

Figure 30: Adding three private data members to CAboutDlg class.

 

Entering the member variable type and name.

 

Figure 31: Entering the member variable type and name.

 

CBitmapButton m_editCopy;

CBitmapButton m_editCut;

CBitmapButton m_editPaste;

 

MFC code segment snapshot

 

Listing 21.

 

Then you use ClassWizard to map the WM_INITDIALOG message in the dialog class. Be sure that the CAboutDlg class is selected. Then click the Edit Code button.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Using ClassWizard to map the WM_INITDIALOG message in the dialog class.

 

Figure 32: Using ClassWizard to map the WM_INITDIALOG message in the dialog class.

 

The message handler (actually a virtual function) is coded as follows:

 

BOOL CAboutDlg::OnInitDialog()

{

    CDialog::OnInitDialog();

    VERIFY(m_editCopy.AutoLoad(IDC_BUTTON1, this));

    VERIFY(m_editCut.AutoLoad(IDC_BUTTON2, this));

    VERIFY(m_editPaste.AutoLoad(IDC_BUTTON3, this));

    return TRUE;  // return TRUE unless you set the focus to a control

                  // EXCEPTION: OCX Property Pages should return FALSE

}

 

MFC code segment snapshot

 

Listing 22.

 

The AutoLoad() function connects each button with the two matching resources. The VERIFY macro is an MFC diagnostic aid that displays a message box if you didn't code the bitmap names correctly.

 

Edit the OnDraw() function in mymfc26DView.cpp. Replace the AppWizard-generated code with the following line:

 

pDC->TextOut(30, 30, "Choose About from the Help menu.");

 

 

MFC code segment snapshot

 

Listing 23.

 

Build and test the application. When the program starts, choose About from the Help menu and observe the button behavior. The image below shows the CUT button in the down state.

 

MYMFC26D program output.

 

Figure 33: MYMFC26D program output.

 

MYMFC26D program output with bitmap.

 

Figure 34: MYMFC26D program output with bitmap.

 

Note that bitmap buttons send BN_CLICKED notification messages just as ordinary buttons do. ClassWizard can, of course, map those messages in your dialog class.

 

Going Further with Bitmap Buttons

 

You've seen bitmaps for the buttons' up and down states. The CBitmapButton class also supports bitmaps for the focused and disabled states. For the Copy button, the focused bitmap name would be "COPYF", and the disabled bitmap name would be "COPYX". If you want to test the disabled option, make a "COPYX" bitmap, possibly with a red line through it, and then add the following line to your program:

 

m_editCopy.EnableWindow(FALSE);

 

 

 

 

 

 

Further reading and digging:

  1. MSDN MFC 7.0 class library online documentation.

  2. MSDN MFC 9.0 class library online documentation - latest version.

  3. MSDN Library

  4. Windows data type.

  5. Win32 programming Tutorial.

  6. The best of C/C++, MFC, Windows and other related books.

  7. Unicode and Multibyte character set: Story and program examples.

 

 

 

 


 

| Tenouk C & C++ | MFC Home | Bitmaps 4 | Windows Message Processing & Multithreaded Programming 1 | Download |Site Index |