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


use color table and color indices to form a bitmap. The last format does not have a color table and all the pixels are represented by R, G, B combinations.

The following is the format of bitmap information header:

typedef struct tagBITMAPINFOHEADER{

DWORD biSize;

LONG biWidth;

LONG biHeight;

WORD biPlanes;

WORD biBitCount

DWORD biCompression;

DWORD biSizeImage;

LONG biXPelsPerMeter;

LONG biYPelsPerMeter;

DWORD biClrUsed;

DWORD biClrImportant;

} BITMAPINFOHEADER;


It has 11 members, the most important ones are biSize, biWidth, biHeight, biBitCount and biSizeImage.

Member biSize specifies the length of this structure, which can be specified by sizeof(BITMAPINFORHEADER). Members biWidth and biHeight specify the dimension of the bitmap image. Member biBitCount specifies how many bits are used to represent one pixel (Bit count per pixel). This factor determines the total number of colors that can be used by the bitmap and also, the size of the color table. For example, if this member is 1, the bitmap can use only two colors. In this case, the size of color table is 2. If it is 4, the bitmap can use up to 16 colors and the size of the color table is 16. The possible values of this member are 1, 4, 8, 16, 24, and 32. Member biSizeImage specifies the total number of bytes that must be allocated for storing image data. This is a very important member, because we must know its value before allocating memory.

An image is composed of multiple raster lines, each raster line is made up of an array of pixels. To speed up image loading, each raster line must use a multiple of four-byte buffers (This means if we have a 2-color (monochrom) 1(1 bitmap, we need four bytes instead of one byte to store only one pixel). Because of this, the value of biSizeImage can not be simply calculated by the following fomulae:

biHeight*biWidth*biBitCount/8

We will discuss how to calculate this value later.

For bitmaps under Windows(, member biPlanes is always set to 1. If the bitmap is not compressed, member biCompress should be set to BI_RGB.

The next part of the DIB data is color table, which comprises an array of RGBQUAD objects. The bitmap information header and color table together form a bitmap header, which can be described by a BITMAPINFO structure:

typedef struct tagBITMAPINFO {

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[1];

} BITMAPINFO;


DIB Example

Following the bitmap header are bitmap bit values. Image data is stored in the memory one pixel after another from left to right, and vertically from the bottom to the top. The following is an example of 3(4 image:

(Table omitted)

This image has only two colors: black and white. If we store it using monochrome DIB format (2 colors), we need only 3 bits to store one raster line. For 16-color DIB format, we need 12 bits to store one line. Since each raster line must use multiple of four-byte buffers (32 bits), if one raster line can not use up all the bits, the rest will simply be left unused.

The following table compares four different types of DIB formats by listing the following information: the necessary bits needed, the actual bits used, and number of bits wasted by one raster line:

(Table omitted)

We can define a macro that allows us to calculate the number of bytes needed for each raster line for different bitmap formats:

#define WIDTHBYTES(bits) ((((bits)+31)/32)*4)

Here bits represents the number of bits that are needed for one raster line, it can be obtained from BITMAPINFORHEADER by doing the following calculation:

biWidth(biBitCount

Now we know how to calculate the value of biSizeImage from other members of structure BITMAPINFOHEADER:

WIDTHBYTES(biWidth*biBitCount)*biHeight

The following is the image data for the above DIB example, assume all unused bits are set to 0:

2 color bitmap (assuming in the color table, index to white color = 0, index to black color = 1):

C0 00 00 00

40 00 00 00

C0 00 00 00

40 00 00 00


4 color bitmap (assuming in the color table, index to white color = 0, index to black color = 15):

F0 00 00 00

0F 00 00 00

F0 F0 00 00

0F 00 00 00


8 color bitmap (assuming in the color table, index to white color = 0, index to black color = 255):

FF 00 FF 00

00 FF 00 00

FF 00 FF 00

00 FF 00 00


24 bit color bitmap:

00 00 00 FF FF FF 00 00 00 00 00

FF FF FF 00 00 00 FF FF FF 00 00

00 00 00 FF FF FF 00 00 00 00 00

FF FF FF 00 00 00 FF FF FF 00 00


The bitmap resource is stored exactly in the format mentioned above. To avoid color distortion, we need to extract the color table contained in the DIB to create logic palette, and convert DIB to DDB before drawing it.

Creating DDB from DIB

To create a DDB from DIB data, we need to call function ::CreateDIBitmap(...), which has the following format:

HBITMAP ::CreateDIBitmap

(

HDC hdc,

CONST BITMAPINFOHEADER *lpbmih, DWORD fdwInit,

CONST VOID *lpbInit, CONST BITMAPINFO *lpbmi, UINT fuUsage

);


The first parameter of this function is a handle to target DC. Because DDB is device dependent, we must know the DC information in order to create the bitmap. The second parameter is a pointer to BITMAPINFORHEADER type object, it contains bitmap information. The third parameter is a flag, if we set it to CBM_INIT, the bitmap will be initialized with the data pointed by lpbInit and lpbmi; if this flag is 0, a blank bitmap will be created. The final parameter specifies how to use color table. If the color table is contained in the bitmap header, we can set its value to DIB_RGB_COLORS.

Loading Resource

To access data stored in the resource, we need to call the following three funcitons:

HRSRC ::FindResource(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType);

HGLOBAL ::LoadResource(HMODULE hModule, HRSRC hResInfo);

LPVOID ::LockResource(HGLOBAL hResData);


The first function will find the specified resource and return a resource handle. When calling this function, we need to provide module handle (which can be obtained by calling function AfxGetResourceHandle()), the resource ID, and the resource type. The second function loads the resource found by the first function. When calling this function, we need to provide the module handle, along with the resource handle returned from the first function. The third function locks the resource. By doing this, we can access the data contained in it. The input parameter to this function must be the global handle obtained from the second function.

Sample

Sample 2\GDI demonstrates how to extract color table from DIB and convert it to DDB. It is based on sample 1-2\GDI.

Because we need to implement logical palette, first a variable and a function are added to class CGDIDoc:

(Code omitted)

Variable m_palDraw is added for creating logical palette and function GetPalette() is used to access it outside class CGDIDoc.

In class CGDIView, some new variables are added for bitmap drawing:

(Code omitted)

Variable m_dcMem will be used to implement memory DC at the initialization stage of the client window. It will be used later for drawing bitmap. By implementing memory DC this way, we don't have to create it every time. Also, we will select the bitmap and palette into the memory DC after they are avialabe, and selet them out of the DC when the window is being destroyed. For this purpose, two pointers m_pPalMemOld and m_pBmpMemOld are declared, they will be used to select the palette and bitmap out of DC. Variable m_bmInfo is used to store the information of bitmap.

The best place to create bitmap and palette is in CGDIView::OnInitialUpdate(). First, we must locate and load the bitmap resource:

(Code omitted)

We call funcitons ::FindResource(...) and ::LoadResource(...) to load the bitmap resource. Function ::FindResource(...) will return a handle to the specified resource block, which can be passed to ::LoadResource(...) for loading the resource. This function returns a global memory handle. We can call ::LockResource(...) to lock the resource. This function will return an address that can be used to access the bitmap data. In the sample, we use pointer lpBi to store this address.

Next, we must calculate the size of color table and allocate enough memory for creating logical palette. The color table size can be calculated from "bit count per pixel" information of the bitmap as follows:

(Code omitted)

The color table size is stored in variable nSizeCT. Next, the logical palette is created from the color table stored in the DIB data:

(Code omitted)

The color table is obtained from member bmiColors of structure BITMAPINFO (pointed by lpBi). Since the palette is stored in the document, we first call CGDIDoc::GetPalette() to obtain the address of the palette (CGDIDoc::m_palDraw), then call CPalette::CreatePalette(...) to create the palette. After this we select the palette into the client DC, and call CDC::RealizePalette() to let the logical palette be mapped to the system palette.

Then, we create the DDB from DIB data:

(Code omitted)

Function ::CreateDIBitmap(...) returns an HBITMAP type handle, which must be associated with a CBitmap type varible by calling function CBitmap::Attach(...).

The rest part of this funciton fills variable CGDIView::m_bmInfo with bitmap information, sets the scroll sizes, create the memory DC, select bitmap and palette into it, then free the bitmap resource loaded before:

(Code omitted)

Because the bitmap and the palette are selected into the memory DC here, we must select them out before application exits. The best

Page : << Previous 2  Next >>