| Tenouk C & C++ | MFC Home | Win32 Memory Management | Bitmaps 2 | Download | Site Index |


 

 

 

 

 

Module 21: The Bitmaps 1

 

 

 

 

 

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

  2. GDI Bitmaps and Device-Independent Bitmaps

  3. Color Bitmaps and Monochrome Bitmaps

  4. Using GDI Bitmaps

  5. Loading a GDI Bitmap from a Resource

  6. The Effect of the Display Mapping Mode

  7. Stretching the Bits

  8. The MYMFC26A Example

  9. Using Bitmaps to Improve the Screen Display

 

 

 

Bitmaps

 

Without graphics images, Microsoft Windows-based applications would be pretty dull. Some applications depend on images for their usefulness, but any application can be spruced up with the addition of decorative clip art from a variety of sources. Windows bitmaps are arrays of bits mapped to display pixels. That might sound simple, but you have to learn a lot about bitmaps before you can use them to create professional applications for Windows. This module starts with the "old" way of programming bitmaps, creating the device-dependent GDI bitmaps that work with a memory device context. You need to know these techniques because many programmers are still using them and you'll also need to use them on occasion.

Next you'll graduate to the modern way of programming bitmaps, creating device-independent bitmaps (DIBs). If you use DIBs, you'll have an easier time with colors and with the printer. In some cases you'll get better performance. The Win32 function CreateDIBSection() gives you the benefits of DIBs combined with all the features of GDI bitmaps. Finally, you'll learn how to use the MFC CBitmapButton class to put bitmaps on pushbuttons. Using CBitmapButton to put bitmaps on pushbuttons has nothing to do with DIBs, but it's a useful technique that would be difficult to master without an example.

 

GDI Bitmaps and Device-Independent Bitmaps

 

There are two kinds of Windows bitmaps: GDI bitmaps and DIBs. GDI bitmap objects are represented by the MFC Library version 6.0 CBitmap class. The GDI bitmap object has an associated Windows data structure, maintained inside the Windows GDI module, which is device-dependent. Your program can get a copy of the bitmap data, but the bit arrangement depends on the display hardware. GDI bitmaps can be freely transferred among programs on a single computer, but because of their device dependency, transferring bitmaps by disk or modem doesn't make sense. In Win32, you're allowed to put a GDI bitmap handle on the clipboard for transfer to another process, but behind the scenes Windows converts the device-dependent bitmap to a DIB and copies the DIB to shared memory. That's a good reason to consider using DIBs from the start.

DIBs offer many programming advantages over GDI bitmaps. Because a DIB carries its own color information, color palette management is easier. DIBs also make it easy to control gray shades when printing. Any computer running Windows can process DIBs, which are usually stored in BMP disk files or as a resource in your program's EXE or DLL file. The wallpaper background on your monitor is read from a BMP file when you start Windows. The primary storage format for Microsoft Paint is the BMP file, and Visual C++ uses BMP files for toolbar buttons and other images. Other graphic interchange formats are available, such as TIFF, GIF, PNG and JPEG, but only the DIB format is directly supported by the Win32 API.

 

Color Bitmaps and Monochrome Bitmaps

 

Now might be a good time to reread the "Windows Color Mapping" section in Module 4. As you'll see in this module, Windows deals with color bitmaps a little differently from the way it deals with brush colors. Many color bitmaps are 16-color. A standard VGA board has four contiguous color planes, with 1 corresponding bit from each plane combining to represent a pixel. The 4-bit color values are set when the bitmap is created. With a standard VGA board, bitmap colors are limited to the standard 16 colors. Windows does not use dithered colors in bitmaps. A monochrome bitmap has only one plane. Each pixel is represented by a single bit that is either off (0) or on (1). The CDC::SetTextColor function sets the "off" display color, and SetBkColor() sets the "on" color. You can specify these pure colors individually with the Windows RGB macro.

 

Using GDI Bitmaps

 

A GDI bitmap is simply another GDI object, such as a pen or a font. You must somehow create a bitmap, and then you must select it into a device context. When you're finished with the object, you must deselect it and delete it. You know the drill.

There's a catch, though, because the "bitmap" of the display or printer device is effectively the display surface or the printed page itself. Therefore, you can't select a bitmap into a display device context or a printer device context. You have to create a special memory device context for your bitmaps, using the CDC::CreateCompatibleDC function. You must then use the CDC member function StretchBlt() or BitBlt() to copy the bits from the memory device context to the "real" device context. These "bit-blitting" functions are generally called in your view class's OnDraw() function. Of course, you mustn't forget to clean up the memory device context when you're finished.

 

Loading a GDI Bitmap from a Resource

 

The easiest way to use a bitmap is to load it from a resource. If you look in ResourceView in the Workspace window, you'll find a list of the project's bitmap resources. If you select a bitmap and examine its properties, you'll see a filename as shown below.

 

 

Figure 1: Bitmap properties dialog.

 

Here's an example entry in an RC (resource script) file, when viewed by a text editor:

 

...

// Icon with lowest ID value placed first to ensure application icon

// remains consistent on all systems.

IDR_MAINFRAME           ICON    DISCARDABLE     "res\\mymfc25A.ico"

IDR_MYMFC2TYPE          ICON    DISCARDABLE     "res\\mymfc25ADoc.ico"

IDI_BLACK               ICON    DISCARDABLE     "res\\icon1.ico"

IDI_BLUE                ICON    DISCARDABLE     "res\\ico00001.ico"

IDI_CYAN                ICON    DISCARDABLE     "res\\ico00002.ico"

...

 

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

//

// Bitmap

//

 

IDR_MAINFRAME           BITMAP  MOVEABLE PURE   "res\\Toolbar.bmp"

IDB_BMBIRD              BITMAP  DISCARDABLE     "res\\bitmap1.bmp"

IDB_BMBIRDSELECTED      BITMAP  DISCARDABLE     "res\\bmp00001.bmp"

IDB_BMDOG               BITMAP  DISCARDABLE     "res\\bmdog.bmp"

IDB_BMDOGSELECTED       BITMAP  DISCARDABLE     "res\\bmp00002.bmp"

...

 

IDB_BMBIRD is the resource ID, and the file is bitmap1.bmp in the project's \res subdirectory. The resource compiler reads the DIB from disk and stores it in the project's RES file. The linker copies the DIB into the program's EXE file. You know that the bitmap1 bitmap must be in device-independent format because the EXE can be run with any display board that Windows supports.

The CDC::LoadBitmap function converts a resource-based DIB to a GDI bitmap. Below is the simplest possible self-contained OnDraw() function that displays the bitmap1 bitmap:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

CMyView::OnDraw(CDC* pDC)

{

    CBitmap bitmap; // Sequence is important

    CDC dcMemory;

    bitmap.LoadBitmap(IDB_BMBIRD);

    dcMemory.CreateCompatibleDC(pDC);

    dcMemory.SelectObject(&bitmap);

    pDC->BitBlt(100, 100, 54, 96, &dcMemory, 0, 0, SRCCOPY);

    // CDC destructor deletes dcMemory; bitmap is deselected

    // CBitmap destructor deletes bitmap

}

 

The BitBlt() function copies the bitmap1 pixels from the memory device context to the display (or printer) device context. The bitmap is 54 bits wide by 96 bits high, and on a VGA display it occupies a rectangle of 54-by-96 logical units, offset 100 units down and to the right of the upper-left corner of the window's client area. The code above works fine for the display. The application framework calls the OnDraw() function for printing, in which case pDC points to a printer device context. The bitmap here, unfortunately, is configured specifically for the display and thus cannot be selected into the printer-compatible memory device context. If you want to print a bitmap, you should look at the CDib class described later in this module.

 

The Effect of the Display Mapping Mode

 

If the display mapping mode in the bitmap1 example is MM_TEXT, each bitmap pixel maps to a display pixel and the bitmap fits perfectly. If the mapping mode is MM_LOENGLISH, the bitmap size is 0.54-by-0.96 inch, or 52-by-92 pixels for Windows 95 and the GDI must do some bit crunching to make the bitmap fit. Consequently, the bitmap might not look as good with the MM_LOENGLISH mapping mode. Calling CDC::SetStretchBltMode with a parameter value of COLORONCOLOR will make shrunken bitmaps look nicer.

 

Stretching the Bits

 

What if we want bitmap1 to occupy a rectangle of exactly 54-by-96 pixels, even though the mapping mode is not MM_TEXT? The StretchBlt() function is the solution. If we replace the BitBlt() call with the following three statements, bitmap1 is displayed cleanly, whatever the mapping mode:

 

CSize size(54, 96);

pDC->DPtoLP(&size);

pDC->StretchBlt(0, 0, size.cx, -size.cy, &dcMemory, 0, 0, 54, 96, SRCCOPY);

 

With either BitBlt() or StretchBlt(), the display update is slow if the GDI has to actually stretch or compress bits. If, as in the case above, the GDI determines that no conversion is necessary, the update is fast.

 

The MYMFC26A Example

 

The MYMFC26A example displays a resource-based bitmap in a scrolling view with mapping mode set to MM_LOENGLISH. The program uses the StretchBlt() logic described above, except that the memory device context and the bitmap are created in the view's OnInitialUpdate() member function and last for the life of the program. Also, the program reads the bitmap size through a call to the CGdiObject member function GetObject(), so it's not using hard-coded values as in the preceding examples.

 

Here are the steps for building the example:

 

Run AppWizard to produce \mfcproject\mymfc26A. Accept all the default settings but two: select Single Document, and in step 6, select the CScrollView view base class for CMymfc26AView. The options and the default class names are shown here.

 

MYMFC26A step 6 of 6 AppWizard, using CScrollView as view base class.

 

Figure 1: MYMFC26A step 6 of 6 AppWizard, using CScrollView as view base class.

 

MYMFC26A project summary.

 

Figure 2: MYMFC26A project summary.

 

Import the Soap Bubbles.bmp bitmap. Choose Resource from Visual C++'s Insert menu. Import the bitmap Soap Bubbles.bmp from the \WINDOWS directory (or other bmp file from the \WINDOWS directory or your own bmp).

 

Inserting a new resource.

 

Figure 3: Inserting a new resource.

 

 

Importing a new bitmap into the project.

 

Figure 4: Importing a new bitmap into the project.

 

Selecting Soap Bubbles.bmp bitmap from Windows system directory.

 

Figure 5: Selecting Soap Bubbles.bmp bitmap file from Windows system directory.

 

Visual C++ will copy this bitmap file into your project's \res subdirectory. Assign the ID IDB_SOAPBUBBLE, and save the changes.

 

The imported bitmap properties dialog copied into Visual C++ \res directory.

 

Figure 6: The imported bitmap properties dialog copied into Visual C++ \res directory.

 

Add the following private data members to the class CMymfc26AView. Edit the file mymfc26AView.h or use ClassView. The bitmap and the memory device context last for the life of the view. The CSize objects are the source (bitmap) dimensions and the destination (display) dimensions.

 

Using ClassView to add a private data members to the class CMymfc26AView.

 

Figure 7: Using ClassView to add a private data members to the class CMymfc26AView..

 

CDC*     m_pdcMemory;

CBitmap* m_pBitmap;

CSize    m_sizeSource, m_sizeDest;

 

MFC code segment snapshot

 

Listing 1.

 

Edit the following member functions in the class CMymfc26AView. Edit the file mymfc26AView.cpp. The constructor and destructor do C++ housekeeping for the embedded objects. You want to keep the constructor as simple as possible because failing constructors cause problems. The OnInitialUpdate() function sets up the memory device context and the bitmap, and it computes output dimensions that map each bit to a pixel. The OnDraw() function calls StretchBlt() twice, once by using the special computed dimensions and once by mapping each bit to a 0.01-by-0.01-inch square. Add the following code:

 

CMymfc26AView::CMymfc26AView()

{

    m_pdcMemory = new CDC;

    m_pBitmap = new CBitmap;

}

 

MFC code segment snapshot

 

Listing 2.

 

CMymfc26AView::~CMymfc26AView()

{

    // cleans up the memory device context and the bitmap

    delete m_pdcMemory; // deselects bitmap

    delete m_pBitmap;

}

 

MFC code segment snapshot

 

Listing 3.

 

void CMymfc26AView::OnDraw(CDC* pDC)

{

    pDC->SetStretchBltMode(COLORONCOLOR);

    pDC->StretchBlt(20, -20, m_sizeDest.cx, -m_sizeDest.cy, m_pdcMemory, 0, 0,

        m_sizeSource.cx, m_sizeSource.cy, SRCCOPY);

 

    pDC->StretchBlt(350, -20, m_sizeSource.cx, -m_sizeSource.cy, m_pdcMemory, 0, 0,

        m_sizeSource.cx, m_sizeSource.cy, SRCCOPY);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

MFC code segment snapshot

 

Listing 4.

 

 

 

void CMymfc26AView::OnInitialUpdate()

{

    CScrollView::OnInitialUpdate();

    CSize sizeTotal(800, 1050); // 8-by-10.5 inches

    CSize sizeLine = CSize(sizeTotal.cx / 100, sizeTotal.cy / 100);

    SetScrollSizes(MM_LOENGLISH, sizeTotal, sizeTotal, sizeLine);

 

    BITMAP bm; // Windows BITMAP data structure; see Win32 help

    if (m_pdcMemory->GetSafeHdc() == NULL) {

        CClientDC dc(this);

        OnPrepareDC(&dc); // necessary

 //Change the ID to your own bitmap if any

        m_pBitmap->LoadBitmap(IDB_SOAPBUBBLE);

        m_pdcMemory->CreateCompatibleDC(&dc);

        m_pdcMemory->SelectObject(m_pBitmap);

        m_pBitmap->GetObject(sizeof(bm), &bm);

        m_sizeSource.cx = bm.bmWidth;

        m_sizeSource.cy = bm.bmHeight;

        m_sizeDest = m_sizeSource;

        dc.DPtoLP(&m_sizeDest);

    }

}

 

MFC code segment snapshot

 

Listing 5.

 

Build and test the MYMFC26A application. Your screen should look something like this.

 

MYMFC26A program output.

 

Figure 8: MYMFC26A program output.

 

Try the Print Preview and Print features.

 

MYMFC26A Print Preview view.

 

Figure 9: MYMFC26A Print Preview view.

 

The bitmap prints to scale because the application framework applies the MM_LOENGLISH mapping mode to the printer device context just as it does to the display device context. The output looks great in Print Preview mode, but (depending on your print drivers) the printed output will probably be either blank or microscopic! We'll fix that soon.

 

Using Bitmaps to Improve the Screen Display

 

You've seen an example program that displays a bitmap that originated outside the program. Now you'll see an example program that generates its own bitmap to support smooth motion on the screen. The principle is simple: you draw on a memory device context with a bitmap selected, and then you zap the bitmap onto the screen.

 

 

Continue on next module...part 2.

 

 

 

 

 

 

 

 

 

 

 

 

 

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 | Win32 Memory Management | Bitmaps 2 | Download | Site Index |