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

destroy the current bitmap first.

Functions CBitmap::CreateBitmap(...) and CDC::CreateCompatibleDC(...) will fail if CBitmap and CDC type variables have already been initialized.

If a logical palette is implemented, we must select it into every DC before performing drawing operations. Before the application exits, all the objects selected by the DCs must be selected out, otherwise it may cause the system to crash.

Although we can prepare normal image and mask image separately, it is not the most convenient way to implement transparent background. The mask image can also be generated from the normal image so long as all the background pixels of the normal image are set to the same color (For example, white). In this situation, pixel in the mask image can be set by examing the corresponding pixel in the normal image: if it is the background color, the pixel in the mask image should be set to white, otherwise it should be set to black.

DIB Section

Both DIB and DDB are needed in order to implement transparent background drawing: we need DIB format to generate mask image, and need DDB to draw the image. Of course we can call ::GetDIBits(...) and ::SetDIBits(...) to convert between DIB and DDB format, however, there exists an easier way to let us handle DIB and DDB simultaneously.

A DIB section can be created to manage the image so that we can have both the DIB and DDB features without doing the conversion. A DIB section is a memory section that can be shared between the process and the system. When a change is made within the process, it is automatically updated to the system. By doing this, there is no need to update the data using functions ::GetDIBits(...) and ::SetDIBits(...).

We can call function ::CreateDIBSection(...) to create a DIB section. This function will return an HBITMAP handle, which can be attached to a CBitmap variable by calling function CBitmap::Attach(...).

Function ::CreateDIBSection(...) has six parameters:

HBITMAP ::CreateDIBSection


HDC hdc,

CONST BITMAPINFO *pbmi, UINT iUsage, VOID *ppvBits, HANDLE hSection,

DWORD dwOffset


(Table omitted)

After calling this function, we can access the buffers pointed by ppvBits and make change to the DIB bits directly, there is no need for us to do any DIB to DDB conversion or vice versa. After the change is made, we can draw the bitmap immediately by calling funciton CDC::BitBlt(...), this will draw the updated image to the window.

New Variables

The following new variables are declared in class CGDIView for drawing bitmap with transparancy:

(Code omitted)

Altogether there are four CBitmap type variables, three CBitmap type pointers, three CDC type variables, and three CPalette type pointers. Their meanings are explained in the following table:

(Table omitted)

To make the application more interesting, we will also draw the background of client window using bitmap. This bitmap will be loaded into m_bmpBkd variable.

The six pointers are initialized to NULL in the constructor:

(Code omitted)

Cleaning Up

A new function CGDIView::CleanUp() is added to the application for doing the clean up job. Within this function, all the objects selected by the DCs are selected out, then DCs and bitmaps are deleted:

(Code omitted)

If a pointer is not NULL, it means that there is an object being currently selected by the DC, so funciton CGDIObject::SelectObject(...) is called to select the object (palette or bitmap) out of the DC before destroying it.

We need to call this function just before the application exits. In the sample, a WM_DESTROY message handler is added to class CGDIView through using Class Wizard. The corresponding member function is implemented as follows:

(Code omitted)

Loading Bitmap & Creating Mask Bitmap

In the sample, funciton CGDIView::LoadBitmap(...) is changed. In the new function, a DIB section is created from the DIB data, and the mask bitmap image is generated from the normal image. The palette creation precedure is still the same. The handle retruned from the DIB section is attached to variable CGDIView::m_bmpDraw:

(Code omitted)

Please note that function CGDIView::CleanUp() is called before the bitmap is created. After the DIB section is created, we use the DIB data passed through hData parameter to initialize the image. The buffers that store DIB bit values are pointed by pointer pBits. We can use it to edit the image pixels directly, there is no need to convert between DDB and DIB foramts any more.

After the bitmap is loaded, we need to create the mask bitmap, memory bitmap, and theree memory DCs. We also need to select the bitmaps and the logical palette into the DCs if necessary:

(Code omitted)

The mask and memory bitmaps must be created by calling function CBitmap:: CreateCompatibleBitmap(...), this will allow the created bitmaps to be compatible with the device context.

Next, the mask bitmap is generated from the normal bitmap image:

(Code omitted)

Every pixel of the normal image is examined to generate the mask image. Here functions CDC::GetPixel(...) and CDC::SetPixel(...) are called for manipulating single pixels. Although the two functions hide the details of device context and bitmap format, they are very slow, and should not be used for fast bitmap drawing or image processing.

Drawing Bitmap with Transparancy

Function CGDIView::OnDraw(...) is modified as follows for drawing bitmap with transparency:

(Code omitted)

In the above function, the pattern on the target device is first copied to the memory bitmap. Then function CDC::BitBlt(...) is called three times to draw the normal image and mask image on the memory bitmap, with two XOR drawings of the normal image (first and thrid operations) and one AND mode drawing of the mask image (second operation). Finally, the new pattern in the memory bitmap is copied back to the targert device.

Adding Background

If the window's background is also white, it is difficult for us to see the transparency effect. To show this effect, in the sample, the background of the client window is also painted with a bitmap image.

The image that is used to paint the background is prepared as a resource, whose ID is IDB_BITMAPBKD. The bitmap is loaded to variable CGDIView::m_bmpBkd in function CGDIView:: OnInitialUpdate():

(Code omitted)

The bitmap can also be loaded in the constructor of CGDIView.

To paint the background of a window, we need to handle message WM_ERASEBKGND and implement background drawing by ourseleves in the message hanlder. In the sample, this message handler is added through using Class Wizard, and the corresponding function is implemented as follows:

(Code omitted)

This function simply draw the bitmap image repeatedly so that the whole client area is covered by the image.

To test this sample, we may use it to load any bitmap images with white background.

Figure 10-1 shows the result after 9\Rose.bmp is loaded into the application.

(Figure 10-1 omitted)

10 Creating Chiseled Effect

We will continue to show the power of DDB. Sample 10\GDI demonstrates how to convert a normal bitmap to a grayed image with chiseled effect. It is based on sample 9\GDI. We can see that with DDB, this effect can be easily implemented with just a few CDC::BitBlt(...) calls. If we use DIB, we will have to make very complex mathematical calculation, and the program will become very slow.

The chiseled effect can be implemented by drawing the outline of an object with two different colors: highlighted color and shadowed color. Usually white is used as the the highlighted color and dark gray is used as the shadowed color. For example: the rectangle in Figure 10-2 uses white and dark gray as the highlighted and shadowed colors respectivly, it creates a chiseled effect:


The chiseled effect can be implemented with the following algorithm: find out the object's outline, imagine some parallel lines with 135( angle are drawn from the upper left side to bottom right side (Figure 10-3). Think these lines as rays of light. If we draw the portion of the outline that first encounters these parallel lines (the portion facing the light) with shadowed color, and draw the rest part of the outline (the portion facing away fromt he light) with highlighted color, the object will have a chiseled effect. If we swap the shadowed and highlighted colors, it will result in an embossed effect.

If we have a 2-D binary image (an image that contains only two colors: white (255, 255, 255) and black (0, 0, 0)), the outline can be generated by combining the inverse image with the original image at an offset origin. For example, the outline that should be drawn using the shadowed color can be generated with the following steps:

1) Draw the original bitmap at position (0, 0).

2) Invert the bitmap image.

3) Combine the inverted image with the image drawn in step 1) with bit-wise OR operation, with the inverted image be put at position (1, 1) (Figure 10-4).

The highlighted outline can be obtained in the same way, but we need to combine the original bitmap and the inverted bitmap differently here:

1) Draw the original bitmap at position (1, 1).

2) Invert the bitmap image.

3) Combine the inverted image with the original image with bit-wise OR operation, with the inverted image be put at position (0, 0) (Figure 10-5).

If we combine the two outlines and paint them with highlighted and shadowed colors respectively, then fill the rest part with a normal color (A color between the highlighted and shadowed color), we will have a 3D effect. For example,

Page : << Previous 7  Next >>