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

object. This is necessary because when calling function ::GetDIBits(...), we need to provide a BITMAPINFOHEADER type pointer which contains useful information. Here, some unimportant members of BITMAPINFOHEADER are assigned 0s (biSizeImage, biXPelsPerMeter...). Then the size of the color table is calculated and a global memory that is big enough for holding bitmap information header and color table is allocated, and the bitmap information header is stored into the buffers. We will use these buffers to receive color table.

Although the memory size for storing bitmap data can be calculated from the information already known, usually it is not done at this point. Generally the color table and the bitmap data are retrieved separately, in the first step, only the memory that is big enough for storing structure BITMAPINFOHEADER and the color table is prepared. When color table is being retrieved, the bitmap information header will also be updated at the same time. Since it is more desirable to calculate the bitmap data size using the updated information, in the sample, the memory size is updated after the color table is obtained successfully, and the global memory is reallocated for retrieving the bitmap data.

We also need to select logical palette into the DC and realize it so that the bitmap pixels will be intepreted by its own color table.

Function ::GetDIBits(...) is called in the next step to recieve BITMAPINFOHEADER data and the color table. Because some device drivers do not fill member biImageSize (This member carries redunant information with members biWidth, biHeight, and biBitCount), we need to calculate it if necessary:

(Code omitted)

Now the size of DIB data is already known, we can reallocate the buffers, and call function ::GetDIBits(...) again to receive bitmap data. Finally we need to select the logical palette out of the DC, and return the handle of the global memory before function exits:

(Code omitted)

Using New Functions

Using function CGDIDoc::ConvertDDBtoDIB(...), it is farily easy to save the bitmap into a file. All we need is to call this function to convert DDB to DIB, add a BITMAPFILEHEADER structure to it, and write the whole data into a file. In the sample, file saving is implement in function CGDIDoc::Serialize(...):

(Code omitted)

In the sample, command File | Save is disabled so that the user can only save the image through File | Save As command by specifying a new file name. To implement this, UPDATE_COMMAND_UI message handlers are added for both ID_FILE_SAVE and ID_FILE_SAVE_AS commands, and the corresponding member functions are implemented as follows:

(Code omitted)

It seems unnecessary to conver the DDB to DIB before saving the image to a disk file because its original format is DIB. However, if the DDB is changed after being loaded (This is possible for a graphic editor application), the new DDB is inconsistent with the original DIB data.

The DDB to DIB converting procedure is a little complex. If we are programming for Windows 95 or Windows NT 4.0, we can create DIB section (will be introduced in later sections) to let the format be converted automatically. If we are writing Win32 programs that will be run on Windows 3.1, we must use the method discussed in this section to implement the conversion.

5 Drawing DIB Directly

The counterpart function of ::GetDIBits(...) is ::SetDIBits(...), it can be used to convert DIB data to device dependent bitmap. The two functions provide us a way of implementing image editing: whenever we want to make change to the image, we can first retrieve DIB data from the DDB, edit the DIB data, and set it back to DDB.

New Functions

Sometimes it is easier to edit DDB directly instead of using DIB data. For example, if we want to reverse every pixel of the image, we can just call one API funciton to let this be handled by lower level driver instead of editting every single pixel by ourselves. This is why we need to handle both DIB and DDB in the applications.

If our application is restricted on edittng only DIB data, we can call an API function directly to draw DIB in the client window. By doing so, we eleminte the complexity of converting DIB to DDB back and forth. This function is ::SetDIBitsToDevice(...), which has the following format:

(Code omitted)

There are altogether 12 parameters, whose meanings are listed in the following table:

(Table omitted)

This function can output image at 1:1 ratio with respect to the source image. Similar to CDC::BitBlt(...) and CDC::StretchBlt(...), there is another function ::StretchDIBits(...), which allows us to enlarge or reduce the original image and output it to the target device:

(Code omitted)

The ratio between source and target image can be set through the following four parameters: nDestWidth, nDestHeight, nSrcWidth, nSrcHeight.

Modifications Made to Document

Sample 5\GDI is based on sample 4\GDI, it demonstrates how to use the above two functions.

With the new functions, many implementations in the previouse sample can be eleminated. First, on the document side, we no longer need CBitmap type variable (CGDDoc::m_bmpDraw) any more. We will use CGDIDoc::m_hDIB to store DIB data, which can be used directly to draw the image. Also we do not need function CGDIDoc::ConvertDDBtoDIB(...). Function CGDIDoc::GetColorTableSize(...) is still needed because we can use it to calculate the size of BITMAPINFO structure (BITMAPINFOHEADER and color table). In the sample, variable CGDIDoc::m_bmpDraw and function CGDIDoc::ConvertDDBtoDIB() are removed. The following is the modified class:

(Code omitted)

When saving image to file, we do not need to convert DDB to DIB any more. Instead, CGDIDoc::m_hDIB can be used directly for storing data:

(Code omitted)

Note since biSizeImage member of BITMAPINFOHEADER structure may be zero, we need to calculate its value before saving the image to file. Also, the original statement for releasing global memory is deleted because CGDIDoc::m_hDIB is the only variable that is used for storing image in the application.

Function CGDIDoc::OnUpdateFileSaveAs(...) is changed to the following:

void CGDIDoc::OnUpdateFileSaveAs(CCmdUI* pCmdUI)

pCmdUI->Enable(m_hDIB != NULL);


Modifications Made to View

On the view side, we do not need memory DC any more, so three variables CGDIView::m_dcMem, CGDIView::m_pBmpMemOld and CGDIView::m_pPalMemOld are deleted. Since the image size can be obtained from DIB data, variable CGDIView::m_bmInfo can also be eleminated. The following code fragment shows the modified class CGDIView:

(Code omitted)

In function CGDIView::OnInitialUpdate(), there is no need to create DDB any more. So in the updated function, only the logical palette is created:

(Code omitted)

In this functon, DIB handle is obtained from the document and locked. From the global memory buffers, the color table contained in the DIB is obtained and is used for creating the logical palette. The a flag is set to indicate that the bitmap is loaded successfully.

In function CGDIView::OnDraw(...), the DIB is painted to the client window:

(Code omitted)

The procedure of selecting and realizing the logical palette is the same with the previous sample. The difference between them is that function CDC::BitBlt(...) is replaced by function ::SetDIBitsToDevice(...) here.

Message handler CGDI::OnDestroy() is removed through using Class Wizard in the sample. The reason for this is that we no longer need to select objects (palette, bitmap) out of memory DC any more. Also, the constructor of CGDIView is changed as follows:

(Code omitted)

With the above modification, the application is able to display any DIB image without doing DIB to DDB conversion.

6 Bitmap Format Conversion: 256-color to 24-bit

Now that we understand different DIB formats, we can easily implement conversion from one format to another. Sample 6\GDI demonstrates how to convert 256-color DIB format to 24-bit format, it is based on sample 5\GDI.


We need to delete the color table and expand the indices to explicit RGB combinations in order to implement this conversoin. Also in the bitmap information header, we need to change the value of member biBitCount to 24, and recalculate member biImageSize. There is also another difference in the bitmap header bwteen 256-color and 24-bit formats: for DIB that does not contain the color table, member biClrUsed is 0; for DIB that contains the color table, this member specifies the number of color indices in the color table that are actually used by the bitmap.

Current Format

In the sample, a new command Convert | 256 to RGB is added to the mainframe menu IDR_MAINFRAME, whose command ID is ID_CONVERT_256TORGB. Also, WM_COMMAND and UPDATE_COMMAND_UI message handlers are added for this command through using Class Wizard. The corresponding functions are CGDIDoc::OnConvert256toRGB() and CGDIDoc::OnUpdateConvert256toRGB(...) respectively. This command will be used to convert the image from 256-color format to 24-bit format. We want to disable this menu item if the current DIB is not 256 color format.

Before doing the conversion, we must know the current format of the image. So in the sample, a new variable is declared in class CGDIDoc for this purpose:

class CGDIDoc : public CDocument




int m_nBmpFormat;



The following macros are defined in the header file of class CGDIDoc:



#define BMP_FORMAT_16COLOR 2

#define BMP_FORMAT_256COLOR 3

#define BMP_FORMAT_24BIT 4

Variable CGDIDoc::m_nBmpFormat is initialized in the constructor:





Whenever a DIB is loaded, function CGDIDoc::GetColorTableSize(...) will be called by CGDIView:: OnInitialUpdate(), so it is convenient to set CGDIDoc::m_nBmpFormat to an appropriate value to indicate the image format in this function. The following code fragment shows the modified function CGDIDoc::GetColorTableSize():

(Code omitted)

Function Implementation

Function CGDIDoc::OnUpdateConvert256toRGB(...) is implemented as follows so

Page : << Previous 4  Next >>