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

we can use white as the highlighted color, dark gray as the shadowed color, and light gray as the normal color.

Creating Binary Bitmap Image

So we need to generate monochrome black/white binary image from the original color bitmap. Of course we can examine every pixel one by one and compare its brightness with a threshold (Usually the threshold value is set to the middle between the brightest and darkest color, which are white and black respectively). If the brightness of a pixel is greater than the threshold, its color is set to white, otherwise it will be set to black.

However, we can implement this conversion in an easier way. Remember, when creating the bitmap by calling function CBitmap::CreateBitmap(...), we are allowed to specify the bitmap's attributes, which includes the dimension of the image, number of bytes in each raster line, and bit count per pixel. Here, the bit count per pixel indicates how many bits will be used for representing a single pixel. If we set it to 1, the image can have only 2 colors, and will be automatically converted to mono chrome fomat no matter what kind of data we use to initialize the bitmap.

Raster Operation Mode

Another issue needs to be discussed here is how to draw the outline portion using specified color without affecting rest part of the image. In order to implement this, we can treat the two outline bitmaps as masks, and draw only the unmasked pixels with specified colors. This procedure is similar to that of drawing bitmaps with transparency.

When calling function CDC::BitBlt(...) or CDC::StretchBlt(...) to paint the bitmap, we always need to specify the drawing mode, which specifies how to combine the pixels in the source bitmap with the corresponding destination pixels. This drawing mode is also called raster operation mode, because it is applicable only to raster devices (Contary to the raster devices are vector devices, for example, a plotter is a vector device). We have many choices such as bit-wise AND, OR, XOR etc. Actually, we can specify more complex combinations among the following three different objects when calling the above two functions: the pixel in the source bitmap, the corresponding pixel in the target device, and the brush currently being selected by the DC. We can specify up to three Boolean bit-wise operations among them.

For example, the following operation will draw the outline on the destination bitmap with the brush color:

(Brush Color) XOR (Destinaton Color) AND (Source Color) XOR (Brush Color)

The reason is simple: After the first operation (between the brush and destination pixels), the pixels in the target device will become the XOR combination between the original pixel colors and the brush color. Next, this XORed result will be ANDed with the source bitmap (Only the outlined part is black, rest part is white), the outlined part on the target device will become black and the rest part remains unchanged (still XORed result from the first operation). Then we do XOR again between the target device and the brush, for the outlined part, this operation will fill it with the brush color (A ^ 0 = A); for the rest part, this will resume the original color for every pixel (A ^ B ^ A = B).

Chiselled Effect

With the following steps, we can create chiselled effect:

1) Create a brush with shadowed or highlighted color.

2) Paint the destination with the brush using bit-wise XOR operation mode.

3) Draw the mask bitmap on the target device using bit-wise AND operation mode.

4) Paint the destination with the brush using bit-wise XOR operation mode again.

This will draw one of the outlines. Creating new brush and repeating the above steps using the other mask bitmap can result in the chiselled effect.

The first and fourth steps can be implemented by calling function CDC::PatBlt(...), which allows us to fill a bitmap with a brush using specified operation mode:

BOOL CDC::PatBlt(int x, int y, int nWidth, int nHeight, DWORDdwRop);

The last parameter allows us to specify how to combine the color of the brush with the destination pixels. To do bitwise XOR, we need to specify PATINVERT mode.

So we can call CDC::PatBlt(...), CDC::BitBlt(...) and CDC::PatBlt(...) again to draw the outline using the brush created by our own. However, there is a simpler way. When calling function CDC::BitBlt(...), we can pass it a custom operation code and let it do the above-mentioned operations in one stroke.

To find out the custom operation code, we need to enumerate all the possible results from the combinations among brush, destination and source bits for our raster operation:

(Brush Color) XOR (Destinaton Color) AND (Source Color) XOR (Brush Color)

The following table lists all the possible results from the above fomulae:

(Table omitted)

The sequence in the table must be arragned so that brush is in the first column, source pixel in the second column and destination pixel in the third column. The code resulted from the combination of the three bits must increment by one for adjacent rows (In the above sample, for the first row it is 000, second row it is 001, third row it is 0..). If we read the output from the bit contained in the last row to the bit in the first row (10111000), we will have 0xB8.

With this index, we can find a raster code that will implement this typical operation for either CDC::BitBlt(...) or CDC::StretchBlt(...) calling. The table is documented in Win32 programming, we can also find it in Appendix B.

By looking up the table, we know that the raster code needs to be use is 0x00B8074A.

Highlighted and Shadowed Colors

The standard highlighted and shadowed colors in the system can be retrieved by calling function ::GetSysColor(...), which has the following format (The standard system colors can be set by calling the counterpart function ::SetSysColor(...)):

DWORD ::GetSysColor(int nIndex);

The following is a list of some values of nIndex parameter that could be used to retrieve some standard colors in the system:

(Table omitted)

In the sample, we choose COLOR_BTNHIGHLIGHT as the highlighted color, and COLOR_BTNSHADOW as the shadowed color.

New Function

In the sample, a new function CreateGrayedBitmap(...) is declared in class CGDIView to create grayed image from a nomal bimap:

(Code omitted)

The only parameter to this function is a CBitmap type pointer. The function will return an HBITMAP handle, which is the grayed bitmap. Within the function, we must prepare three bitmaps: the bitmap that will be used to store the final grayed image, the mask image that stores the shadowed outline, and the mask image that stores the highlighted outline. The function starts with creating these bitmaps:

(Code omitted)

The final grayed image will be stored in variable bmpGray. We will refer image created by this variable as "grayed image", although the image may not be grayed in the interim.

The other two CBitmap type variables, bmpHilight and bmpShadow will be used to store the outline mask images. We need two memory DCs, one used to select the color bitmap (normal image passed through pointer pBmp, and grayed image bmpGray) and one for binary bitmaps (the outline mask bitmaps). Note that binary bitmaps are created with bit count per pixel set to 1 and the grayed bitmap (actually it is a color bitmap, but we use only monochrom colors) is created by calling function CBitmap:: CreateCompatibleBitmap(...). Since the DC supports color bitmap (If the program is being run on a system with a color monitor) , this will create a color bitmap compatible with the window DC.

Then we select the color bitmap and the binary bitmaps into the memory DCs and create the outline mask images:

(Code omitted)

First we fill the mask bitmap with white color. Then we copy the patterns from the original image to the mask bitmap image. When doing this copy, the first horizontal line (the upper-most line) and the first vertical line (the left-most vertical line) are eleminated from the original image (Pixels with coordinates (0, y) and (x, 0) are elemented, where x can be 0, 1, ... , up to width of image -1; y can be 0, 1, ..., up to height of image -1). The colors contained in the souce bitmap will be automatically converted to black and white colors when we call function CDC::BitBlt(...) because the target image is a binary bitmap. The souce image is copied to the mask bitmap at the position of (0, 0). Then the original bitmap is inverted, and merged with the mask image with bit-wise OR operation. Here flag MERGEPAINT allows the pixels in the souce image and pixels in the target image to be combined in this way. After these operations, the binary bitmap image will contain the outline that should be drawn with the shadowed color.

The following portion of the function generates the highlighted outline:

(Code omitted)

Next we create a brush with standard button face color and used it to fill the grayed image (By default, the standard button face color is light gray. It can also be customized to other colors):

(Code omitted)

The button face color is retrieved by calling ::GetSystColor(...) API function. Actually, all the standard colors defined in the system can be retrieved

Page : << Previous 8  Next >>