Topic : Bitmaps in MFC
Author : Unknown
Page : << Previous 3  Next >>
Go to page :


place to do this is in WM_DESTROY message handler. In the sample, a WM_DESTROY message handler is added to class CGDIView through using Class Wizard, and the corresponding function CGDIView::OnDestroy() is implemented as follows:

(Code omitted)

We must modify function CGDIView::OnDraw(...). Since everything is prepared at the initialization stage (the memory DC, the palette, the bitmap), the only thing we need to do in this function is calling CDC:: StrethBlt(...) or CDC::BitBlt(...) to copy the bitmap from memory DC to target DC:

(Code omitted)

With the above implementations, the bitmap will become more vivid.

3 Loading DIB from File

Now that we understand how to convert a DIB to DDB, it is fairly easy for us to load a device independent bitmap into memory and convert it to DDB. Sample 3\GDI demonstrates how to read DIB file and display the image in the clinet window. It is based on sample 2\GDI.

File Format

All bitmap images stored on the hard disk are in the format of DIB, and therefore can be any of the following formats: monochrome, 16 color, 256 color and 24-bit. The difference between DIB stored in a file and DIB stored as a resource is that there is an extra bitmap file header for DIB stored in file. This header has the following format:

typedef struct tagBITMAPFILEHEADER {

WORD bfType;

DWORD bfSize;

WORD bfReserved1;

WORD bfReserved2;

DWORD bfOffBits;

} BITMAPFILEHEADER;


This header specifies three factors: the file type, the bitmap file size, and the offset specifying where the DIB data really starts. So a real DIB file has the following format:

BITMAPFILEHEADER

BITMAPINFOHEADER

Color Table

Bitmap Bits

Member bfType must be set to 'BM', which indicates that the file is a bitmap file. Member bfSize specifies the whole length of the bitmap file, which is counted from the beginning of the file to its end. Member bfOffBits specifies the offset from BITMAPFILEHEADER to bitmap bits, which should be the size of BITMAPINFOHEADER plus the size of color table.

Supporting File Type

Using SDI or MDI model, it is easy to support certain type of files in "Open" or "Save As" common dialog box (activated when the user executes File | Open or File | Save As command). This feature can be added by changing IDR_MAINFRAME string resource. By default, this string resource contains the following sub-strings (Sub-strings are separated by '\n'):

GDI\n\nGDI\n\n\nGDI.Document\nGDI Document

The meaning of each sub string is listed in the following table:

(Table omitted)

By default, no special file types are supported. If we want the file dialog box to contain certain filters, we need to change the fourth and fifth items of the above string. In the sample, string resource IDR_MAFRAME is changed to the following:

GDI\n\nGDI\nBitmap Files(*.bmp)\n.bmp\nGDI.Document\nGDI Document

The fourth item (Bitmap Files(*.bmp)) is a descriptive name for bitmap files, and the fifth item (.bmp) is the filter.

Loading DIB through Serialization

Since we are going to store data read from the file in globally allocated memory, first we must add an HGLOBAL type varible (along with a member function) to class CGDIDoc. Because DIB data will be changed to DDB data before drawing, a CBitmap type variable and its associated funciton are also declared in class CGDIDoc:

(Code omitted)

Variable m_hDIB is initialized in the constructor:

CGDIDoc::CGDIDoc()

{

m_hDIB=NULL;

}


We will allocate global memory each time a new bitmap file is opened. So when the application exits, we need to check variable m_hDIB to see if the memory has been allocated. If so, we need to release it:

(Code omitted)

We need to modify function CGDIDoc::Serialize(...) to load data from DIB file. First, when a file is being opened, variable m_hDIB may be currently in use. In this case, we need to release the global memory:

(Code omitted)

Reading data from a DIB file needs the following steps:

1) Read bitmap file header.

2) Verify that the the file format is correct.

3) From the bitmap file header, calculate the total memory needed, then allocate enough buffers.

4) Read the DIB data into the allocated buffers.

We can call CArchive::Read(...) to read bytes from the file into the memory. In the sample, first bitmap file header (data contained in structure BITMAPFILEHEADER) is read:

(Code omitted)

Then the file type is checked. If it is DIB format, global memory is allocated, which will be used to store DIB data:

(Code omitted)

Next, the DIB data is read into the global memory:

(Code omitted)

This completes reading the bitmap file. The DIB data is stored in m_hDIB now.

Creating DDB

On the view side, we need to display the bitmap stored in memory (whose handle is m_hDIB) instead of the bitmap stored as resource. So we need to modify function CGDIView::OnInitialUpdate(), which will be called whenever a new file is opened successfully. First, instead of obtaining data from the resource, function CGDIDoc::GetHDib() is called to get the handle of DIB data:

(Code omitted)

Remember in the previous sample, after the DDB and palette are created, we will select them into the memory DC so that the image can be drawn directly later. In this sample, when a new DIB file is opened, there may be a bitmap (also a palette) that is being currently selected by the memory DC. If so, we need to select it out of the memory DC, then lock the global memory and obtain its address that can be used to access the DIB (In order to create DDB, we need the information contained in the DIB):

(Code omitted)

The rest portion of this function calculates the size of the color table, creates the palette (If there is an existing palette, delete it first), and creates DDB from DIB data. Then the newly created bitmap and palette are selected into the memory DC, and m_bmInfo is updated with the new bitmap information. Finally, the global memory is unlocked.

Other functions remain unchanged. With the above implementation, we can open any DIB file with our application and display the image in the client window.

4 Saving DDB to File

We cannot save DDB data directly to a file. Saving bitmap to a file is a reverse procedure of loading it from the disk: we must first convert the DDB back to DIB then write the data to file.

Converting DDB to DIB

To convert a DDB to DIB, we need to call API function ::GetDIBits(...) to receive the bitmap data in DIB format. When calling this function, we must allocate enough buffers for receiving data. As we know, DIB data contains three parts: header BITMAPINFOHEADER, whose size is fixed; color table, whose size depends on the data format; bitmap bit values, whose size depends upon both the bitmap format and bitmap dimension.

We need to calculate the size of color table and bitmap bit values before allocating memory for storing DIB. The attributes of a DDB can be obtained from function CBitmap::GetBitmap(...). The information will be stuffed into a BITMAP type object, from which we know the properties of a bitmap such as its height and width, bit count per pixel. The size of the color table can be calculated from bit count per pixel information.

Lets further take a look at function ::GetDIBits(...):

(Code omitted)

Its parameters are similar to that of funciton ::CreateDIBitmap(). Since DDB is device dependent, we must know the attribute of device context in order to convert DDB to DIB. So we must pass the handle of the target DC to the first parameter of this function. Parameter uStartScan and uScanLines specify the starting raster line and total number of raster lines whose data is to be retrieved. Parameter lpBits specifies the buffer address that can be used to recieve bitmap data. Pointer lpbi provides a BITMAPINFO structure specifying the desired format of DIB data.

When calling this function, we can pass NULL to pointer lpvBits. This will cause the function to fill the bitmap information into a BITMAPINFO object. By doing this, we can get the color table that is being used by the DDB.

So the conversion takes three steps: 1) Call function CBitmap::GetBitmap(...) to obtain the information of the bitmap, calculate the color table size, allocate enough buffers for storing bitmap information header and color table. 2) Call function ::GetDIBits(...) and pass NULL to parameter lpvBits to receive bitmap information header and color table. 3) Reallocate buffers for storing bitmap data and call ::GetDIBits(...) again to get the DIB data.

New Functions

Sample 4\GDI demonstrates how to convert DDB to DIB and save the data to hard disk.

First some functions are added to class CGDIDoc, they will be used for converting DDB to DIB:

(Code omitted)

Function CGDIDoc::ConvertDDBtoDIB(...) converts DDB to DIB, its input parameter is a CBitmap type pointer and its return value is a global memory handle. Function CGDIDoc::GetColorTableSize(...) is used to calculate the size of color table from bit count per pixel information (In the previouse samples, color table size calculation is implemented within function CGDIView::OnInitialUpdate(). Since we need color table size information more frequently now, this calculation is implemented as a single member function):

(Code omitted)

In function CGDIDoc::ConvertDDBtoDIB(...), first we must obtain a handle to the client window that can be used to create a DC:

(Code omitted)

Then function CBitmap::GetBitmap(...) is called to retrieve the information of bitmap and allocate enough buffers for storing structure BITMAPINFOHEADER and color table:

(Code omitted)

We first fill the information obtained previously into a BITMAPINFOHEADER

Page : << Previous 3  Next >>