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


that the menu command will be enabled only when the current DIB format is 256-color:

void CGDIDoc::OnUpdateConvert256toRGB(CCmdUI* pCmdUI)
{

pCmdUI->Enable(m_nBmpFormat == BMP_FORMAT_256COLOR);

}


In function CGDIDoc::OnConvert256toRGB(), first we need to lock the current DIB data, calculate the size of new DIB data (after format conversion) and allocate enough buffers:

(Code omitted)

The new DIB size is stored in local variable dwSize. Here macro WIDTHBYTES is used to calculate the actual bytes needed for one raster line (We use 24 instead of member biBitCount when using this macro to implement calculation for the new format). The size of new DIB data is the size of BITMAPINFOHEADER structure plus the size of bitmap data (Equal to bytes needed for one raster line multiplied by the height of bitmap, there is no color table any more). Then we allocate buffers from global memory and lock them, whose address is stored in pointer lpBi24.

Then we need to fill structure BITMAPINFOHEADER. Most of the members are the same for two different formats, such as biHeight, biWidth. There are three members we need to change: biBitCount must be set to 24, biImageSize should be recalculated, and biClrUsed needs to be 0:

(Code omitted)

Then we need to fill the DIB bit values. The image is converted pixel by pixel using two loops (one is embedded within another): the outer loop converts one raster line, and the inner loop converts one single pixel. As we move to a new raster line, we need to calculate the starting buffer address so that it can be used as the origin of the pixels for the whole raster line (For each single pixel, we can obtain its address by adding an offset to the origin address). The starting address of each raster line can be calculated through multiplying the current line index (0 based) by total number of bytes needed for one raster line. As we move from one pixel to the next of the same raster line, we can just move to the neighboring buffer (for RGB format, next three buffers). However, the final pixel of one raster line and the first pixel of next raster line may not use neighboring buffers, this is because there may exist some unused bits between them (Since each raster line must use a multiple of 4-byte buffers). The following portion of function CGDIDoc::OnConvert256toRGB() shows how to convert bitmap pixels from one format to another:

(Code omitted)

Finally, we must unlock the global memory, release the previous DIB data and assign the new memory handle to CGDIDoc::m_hDIB. We also need to inform the view to reload the image because the bitmap format has changed. For this purpose, a new function CGDIView::LoadBitmap(...) is implemented, it will be called from CGDIDoc::OnConvert256toRGB() and CGDIView::OnInitialUpdate() (The original portion of this funciton that loads the bitmap is replaced by calling the new function). The following is the portion of funciton CGDIDoc::OnConvert256toRGB() which shows what should be done after the format is converted:

(Code omitted)

Function CGDIView::LoadBitmap(...) should implement the following: delete the old palette, check if the DIB contains color table. If so, create a new palette. The function is declared in class CGDIView as follows:

(Code omitted)

The function is implemented as follows (Most part of this function is copied from function CGDIView::OnInitialUpdate()):

(Code omitted)

With this function, CGDIView::OnInitialUpdate() can be simplified to the following:

(Code omitted)

With the above implementation, the application is able to convert a bitmap from 256-color format format to 24-bit format.

7 Converting 24-bit Format to 256-color Format

Sample 5.7\GDI is based on sample 5.6\GDI, it demonstrates how to convert 24-bit bitmap format to 256-color format.

Two Cases

To convert a 24-bit format bitmap to 256-color format bitmap, we must extract a color table from the explicit RGB values. There are two cases that must be handled differently: the bitmap uses less than 256 colors, and the bitmap uses more than 256 colors.

If the bitmap uses less than 256 colors, the conversion is relatively simple: we just examine every pixel of the bitmap, and extract a color table from all the colors contained in the bitmap.

The following is the conversion procedure for this situation: At the beginning, the color table contains no color. Then for each pixel in the bitmap, we examine if the color is contained in the color table. If so, we move to the next pixel. If not, we add the color used by this pixel to the color table. After we go over all the pixels contained in the bitmap, the color table should contain all the colors that are used by the bitmap image.

If the bitmap uses more than 256 colors, we must find 256 colors that best represent all the colors used by the image. There are many algorithms for doing this, a relatively simple one is to omit some lower bits of RGB values so that maximum number of colors used by a bitmap does not exceed 256. For example, 24-bit bitmap format uses 8 bit to represent a basic color, it can result in 256(256(256 different colors. If we use only 3 bits to represent red and green color, and use 2 bits to represent blue color, the total number of possible combinations are 8(8(4=256.

In this situation, when we examine a pixel, we use the 3 most significant bits of red and green colors, along with 2 most significant bits of blue color to form a new color that will be used to create color table (Other bits will be filled with 0s). By doing this, the colors contained in the color table will not exceed 256. Although this algorithm may result in color distortion, it is relatively fast and less image dependent.

Sample

In the sample, a new command Convert | RGB to 256 is added to mainframe menu IDR_MAINFRAME, whose command ID is ID_CONVERT_RGBTO256. Also, WM_COMMAND and UPDATE_COMMAND_UI message handlers are added through using Class Wizard. The new corresponding functions are CGDIDoc:: OnConvertRGBto256() and CGDIDoc::OnUpdateConvertRGBto256(...) respectively.

Function CGDIDoc::OnUpdateConvertRGBto256(...) is implemented as follows:

void CGDIDoc::OnUpdateConvertRGBto256(CCmdUI* pCmdUI)
{

pCmdUI->Enable(m_nBmpFormat == BMP_FORMAT_24BIT);

}


If the current bitmap format is 24-bit, command Convert | RGB to 256 will be enabled.

The implementation of function CGDIDoc::OnConvertRGBto256() is somehow similar to that of CGDIDoc::OnConvert256toRGB(): we must first lock the global memory where the current 24-bit bitmap image is stored, then calculate the size of new bitmap image (256-color format), allocate enough buffers from global memory, and fill the new bitmap bit values one by one.

The first thing we need to do is creating the color table for the new bitmap image. The following portion of function CGDIDoc::OnConvertRGBto256() shows how to extract the color table from explicit RGB colors contained in a 24-bit bitmap image:

(Code omitted)

We examine from the first pixel. The color table will be stored in array arRgbQuad, which is empty at the beginning. For each pixel, we compare the color with every color contained in the color table, if there is a hit, we move on to next pixel, otherwise, we add this color to the color table.

The size of color table obtained this way may be less or greater than 256. In the first case, the conversion is done after the above operation. If the color table size is greater than 256, we must create a new color table using the alogrithsm discussed above:

(Code omitted)

If the size of color table is greater than 256, we first delete the color table, then create a new color table that contains only 256 colors. This color table comprises 256 colors that are evenly distributed in a 8(8(4 3-D space, which has the following contents:

(Table omitted)

For a 24-bit color, if we use only 3 most significant bits of red and green colors, and 2 most significant bits of blue color, and set rest bits to 0. Every possible RGB combination (8 bits for each color) has a corresponding entry in this table.

We use a flag bStandardPal to indicate which algorithm was used to generate the color table. This is important because for the two situations the procedure of converting explicit RGB values to indices of color table is different. If the color table is generated directly from the colors contained in the bitmap (first case), each pixel can be mapped to an index in the color table by comparing it with every color in the color table (there must be a hit). Otherwise, we must omit some bits before looking up the color table (second case).

Following is a portion of function CGDIDoc::OnConvertRGBto256() that allocates buffers from global memory, fill the buffers with bitmap information header and color table:

(Code omitted)

The differences between the new and old bitmap information headers are member bitBitCount (8 for 256-color format), biSizeImage, and biClrUsed (Member biClrUsed can be used to indicate the color usage. For simplicity, it is set to zero).

Next we need to convert explicit RGB values to color table indices. As mentioned before, there are two situations. If the color table is extracted directly from the bitmap, we must compare each pixel with every entry of the color table, find the index, and use it as the bitmap bit value. Otherwise the index can be formed by omitting the lower 5

Page : << Previous 5  Next >>