2007-07-19
| Table of Contents: |
| Rate This Article: | Add This Article To: |
( Page 3 of 3 )
Drawing CBitmaps to the Screen
Once you have a CBitmap object that contains a valid bitmap, you’re ready to draw it to the screen. Most of the time, you’ll rely on the BitBlt function. This function is fast. The BitBlt function is hard to beat when it comes to data transfer between memory and video. It’s evident that Microsoft placed a high priority on fast algorithms for blitting operations — and with the importance of these in such a graphically intensive environment, it’s a wise priority.
The first thing you must understand is that BitBlt technically copies data from one device context to another. That means a device context must be created for a bitmap before operations to and from it are carried out. Additionally, the bitmap must be selected into the device context before performing operations. The BitBlt function is a member of the CDC class and has the following syntax:
CDC:BitBlt( int x, int y, int nWidth, int nHeight,
CDC *pSrcDC, int xSrc, int ySrc, DWORD dwROP );
The x and y parameters are the destination coordinates to which the bitmap will be drawn. The nWidth and nHeight parameters are width and height of the destination rectangle and must match the source bitmap. The pSrcDC parameter is a pointer to the source device context — the source bitmap must have previously been selected into this device context for the operation to work correctly. The xSrc and ySrc parameters are the upper left corner of the source bitmap that’s to be copied. By changing the xSrc and ySrc, and the nWidth and nHeight parameters, you can draw partial bitmaps to the screen. The dwROP parameter specifies the raster operation that’s to be performed. A more detailed discussion of raster codes appears later in the article; see Raster Operations.
The example below loads a bitmap with the LoadBitmap function in an application’s view class constructor. The example then shows how to draw the bitmap to the screen using the OnDraw function. One very important item to note: When selecting the bitmap into the newly-created device context, you obtain a pointer to the old bitmap. This allows you to select the old bitmap back into the device context after the bitmap is drawn.
CmyView::CmyView()
{
m_Bitmap.LoadBitmap( IDC_BITMAP1 );
}
CmyView::OnDraw(CDC *pDC)
{
CBitmapDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CDC MemDC;
MemDC.CreateCompatibleDC( pDC );
CBitmap *pOldBitmap = MemDC.SelectObject( &m_Bitmap );
BITMAP bm;
m_Bitmap.GetObject( sizeof( BITMAP ), &bm );
pDC->BitBlt( 10, 10, bm.bmWidth, bm.bmHeight, &MemDC,
0, 0, SRCCOPY );
MemDC.SelectObject( pOldBitmap );
}
Drawing Bitmaps Summary
These steps sum up the process necessary to display a CBitmap object.
Create a device context for the bitmap that’s compatible with the video device context.
Select the CBitmap object into the newly-created device context and save a pointer to the old bitmap.
Fill in a BITMAP structure using GetObject so that you know what the bitmap’s dimensions are. (Skip this step if you already know the dimensions.)
Perform the BitBlt function.
Select the old bitmap back into the newly-created device context.
So far, the examples you've seen don't take into account mapping mode and the difference between device and logical coordinates. Most applications you write won't change the mapping mode, and most applications have device and logical coordinates that are the same. If you're not sure, though, of the situation in which you'll find yourself, you must add some extra code (and additional overhead). A DrawBitmap function follows in which these additional factors are accounted for:
void DrawBitmap( int x, int y, CDC *pDC, CBitmap *pBitmap )
{
BITMAP bm;
pBitmap->GetObject( sizeof( BITMAP ), &bm );
CPoint size( bm.bmWidth, bm.bmHeight );
pDC->DPtoLP( &size );
CPoint org( 0, 0 );
pDC->DPtoLP( &org );
CDC MemDC;
MemDC.CreateCompatibleDC( pDC );
CBitmap *pOldBitmap = MemDC.SelectObject( pBitmap );
MemDC.SetMapMode( pDC->GetMapMode() );
pDC->BitBlt( x, y, size.x, size.y, &MemDC,
org.x, org.y, SRCCOPY );
MemDC.SelectObject( pOldBitmap );
}
There are times when it's necessary to copy video memory into a bitmap. Since BitBlt doesn't care what the source and destination are as long as it gets two valid device contexts, it couldn't be easier. All you have to do in order to copy from the screen to a bitmap is reverse the roles of the two device contexts.
For instance, the previous examples all used the pDC device context pointer as the destination, and the function call was structured as pDC->BitBlt. In order to specify the second device context, the address of MemDC was passed in as a function argument to BitBlt. The destination will be MemDC and the function call will be structured MemDC.BitBlt. The pDC pointer will be passed in as an argument to BitBlt so that it knows what the second device context from which the source will be obtained is. A function named GetImage follows which copies from the screen into a bitmap:
void GetImage( int x, int y, CDC *pDC, CBitmap *pBitmap )
{
BITMAP bm;
pBitmap->GetObject( sizeof( BITMAP ), &bm );
CDC MemDC;
MemDC.CreateCompatibleDC( pDC );
CBitmap *pOldBitmap = MemDC.SelectObject( pBitmap );
MemDC.BitBlt( 0, 0, bm.bmWidth, bm.bmHeight, pDC,
x, y, SRCCOPY );
MemDC.SelectObject( pOldBitmap );
}
Raster Operations
The last argument in the BitBlt function determines how the source will combine with the destination. Most of the time, though, copying a bitmap to the screen to have it appear exactly as it does in the bitmap data is what you want. At other times, a simple data copy won't do. One example of this is when you want to draw a bitmap to the screen allowing a transparent color to be masked out. The transparent color, when it appeared in the source bitmap would be rejected. Whatever color was in the destination at any pixel location in which the source contained the transparent color would remain unchanged.
This masking technique must be carried out with two separate BitBlt calls. The first part of the process creates a mask based on the source bitmap. It finds the transparent pixels and is created based on them. It then masks the destination in such a way as to eliminate all destination pixels into which the non-transparent source pixels will be drawn. The same mask is then used to mask the source and remove the unwanted transparent-color pixels. The source is then ORed (or XORed) into the destination. The original bitmap must then be restored to its original state, otherwise the transparent color will no longer exist and the pair of transparent operations won't work correctly.
A faster way to accomplish this double masking process is to create a mask ahead of time. That way it doesn't have to be created every time you want to go through the process. If execution time is important, seriously consider this option.
Another example in which you might want to use another raster operation is in some animations. A common technique for moving bitmaps around on the screen is to XOR them before moving them to a new location. XORing one image over itself completely removes it. Here's why. When you first draw the image to the screen using an XOR raster operation, the image source combines with the destination. Anywhere the source has a bit that's set, toggles the corresponding bit in the destination to its opposite value. For instance, if the source bit is set (a one) and the destination bit is not set (a zero), the result will be a one. If the animation must be moved, XORing the same bitmap in the same location toggles those same bits, once again restoring them to their original state. The bitmap can then be drawn in a new location using an XOR raster operation. The following table defines the raster operation codes.
| Raster Operation Codes | Definition |
| BLACKNESS | Turns all output black. |
| DSTINVERT | Inverts the destination bitmap. |
| MERGECOPY | Combines the pattern and the source bitmap using the Boolean AND operator. |
| MERGEPAINT | Combines the inverted source bitmap with the destination bitmap using the Boolean OR operator. |
| NOTSRCCOPY | Copies the inverted source bitmap to the destination. |
| NOTSRCERASE | Inverts the result of combining the destination and source bitmaps using the Boolean OR operator. |
| PATCOPY | Copies the pattern to the destination bitmap. |
| PATINVERT | Combines the destination bitmap with the pattern using the Boolean XOR operator. |
| PATPAINT | Combines the inverted source bitmap with the pattern using the Boolean OR operator. Combines the result of this operation with the destination bitmap using the Boolean OR operator. |
| SRCAND | Combines pixels of the destination and source bitmaps using the Boolean AND operator. |
| SRCCOPY | Copies the source bitmap to the destination bitmap. |
| SRCERASE | Inverts the destination bitmap and combines the result with the source bitmap using the Boolean AND operator. |
| SRCINVERT | Combines pixels of the destination and source bitmaps using the Boolean XOR operator. |
| SRCPAINT | Combines pixels of the destination and source bitmaps using the Boolean OR operator. |
| WHITENESS | Turns all output white. |
Palettes and Color
The representation of color is done using RGB triplets. Each of the RGB triplets contains a value for the red, green, and blue components of a color. The combination of the three component values determines the color that’s seen on the screen.
RGB is a common color space (or color definition). The colors red, green, and blue are considered fundamental and undecomposable. Color systems can be separated into two categories: additive color systems and subtractive color systems. Colors in additive systems, such as the RGB system, are created by adding colors to black to create new colors. The more color that’s added, the more the resulting color tends towards white. The presence of all the primary colors in sufficient amounts creates pure white, while the absence of all the primary colors creates pure black.
We’ll use the common RGB macro in this section to describe RGB triplets. The RGB macro converts three byte values ranging from 0 to 255 into a COLORREF value. The macro’s first argument is the red value, the second green, and the third blue. Values range from 0 to 255. For instance, an RGB value with a red component of 244, a green component of 142, and a blue component of 34 can be notated as RGB( 244, 142, 34 ). The following table lists some of the common colors found in the default Window palette.
| RGB | Colors in the Default Windows Palette |
| RGB( 0, 0, 0 ) | Black |
| RGB( 255, 255, 255 ) | White |
| RGB( 255, 0, 0 ) | Red |
| RGB( 0, 255, 0 ) | Green |
| RGB( 0, 0, 255 ) | Blue |
| RGB( 255, 255, 0) | Yellow |
| RGB( 255, 0, 255 ) | Magenta |
| RGB( 0, 255, 255 ) | Cyan |
| RGB( 128, 128, 128 ) | Dark Gray |
| RGB( 192, 192, 192 ) | Light Gray |
Conclusion
Bitmaps and DIBs are powerful and flexible tools in your arsenal. In an age of visual applications, it would be difficult to overstate their importance. Now that you've read this article, it's a good time to roll up your sleeves and do you own experimentation. I hope to add articles on using DIBs, and even one on double buffering for flicker-free animation.
![]() |
|


