- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Device Contexts in MFC
- Brushes and Pens
- MFC Classes for GDI Operations
- Working with Fonts
- Creating and Loading Bitmaps
- Drawing with Bitmaps
- Creating a Device-Independent Bitmap Class
- Summary
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Creating a Device-Independent Bitmap Class
The bitmaps discussed so far are called device-dependent bitmaps because they rely on the current display device context for their palette colors and are dependent on the current display resolution to determine the displayed size.
A device-independent bitmap (DIB) not only stores the bitmap image but also uses a BITMAPINFO structure to store the color depth, sizing information, and colors. The first part of the structure consists of one of the various header structures followed by an array of RGBQUAD structures that store the colors used in the bitmap. You can create a BITMAPINFO and associate it with a bitmap image to form a DIB.
After you create a DIB, you can transfer it onto different machines and display the same image, represented to the best capabilities of various devices while preserving the original size and colors.
This section shows some of the steps required to create, display, and store a DIB. The sample code builds a DIB class derived from CBitmap to extend the bitmap functionality into a DIB.
Creating a DIB
To create a DIB, you first must initialize a BITMAPINFOHEADER structure or one of its more modern counterparts, such as the BITMAPV5HEADER (which requires Windows NT5 or Windows 98).
You can initialize a number of member variables in the DIB header (BITMAPINFOHEADER structure) to specify the DIB width, height, number of bit planes, bits per pixel, compression technique, pixels per meter in the x and y dimensions, and the actual number of colors used. The more modern versions of the BITMAPINFOHEADER structure (such as BITMAPV5HEADER) offer even more members to specify advanced compression techniques and gamma corrections.
Specifying the pixels per meter provides an important piece of information that lets you render the DIB to the correct size of various devices. By using the information about the resolution of the device, or by using one of the device-context mapping modes (especially the MM_LOMETRIC or MM_HIMETRIC mode), you can render the image to the correct size.
After specifying the header details, you should initialize the RGBQUAD structures that specify each color used in the bitmap. You may want to use the colors from the palette selected in a specific device context by using the SetDIBColorTable() function. This function fills the RGBQUAD array for you from the specified device-context handle.
The following code fragment shows a class definition for a DIB manipulation class:
class CDIB : public CObject
{
public:
CDIB();
virtual ~CDIB();
BOOL CreateDIB(DWORD dwWidth,DWORD dwHeight,int nBits);
BOOL CreateDIBFromBitmap(CDC* pDC);
void InitializeColors();
int GetDIBCols() const;
VOID* GetDIBBitArray() const;
BOOL CopyDIB(CDC* pDestDC,int x,int y);
public:
CBitmap m_bmBitmap;
private:
LPBITMAPINFO m_pDIB;
};
You will notice that the class uses a BITMAPINFO pointer (m_pDIB) to the buffer containing the DIB and has an embedded device-dependent bitmap (m_bmBitmap) for transferring the image from an existing bitmap resource (discussed in the next section).
You then could implement the CreateDIB() and associated functions to allocate the memory for a DIB and initialize the BITMAPINFO structure, like this:
CDIB::CDIB() : m_pDIB(NULL)
{
}
CDIB::~CDIB()
{
if (m_pDIB) delete m_pDIB;
}
BOOL CDIB::CreateDIB(DWORD dwWidth,DWORD dwHeight,int nBits)
{
if (m_pDIB) return FALSE;
const DWORD dwcBihSize = sizeof(BITMAPINFOHEADER);
// Calculate the memory required for the DIB
DWORD dwSize = dwcBihSize +
(2>>nBits) * sizeof(RGBQUAD) +
((nBits * dwWidth) * dwHeight);
m_pDIB = (LPBITMAPINFO)new BYTE[dwSize];
if (!m_pDIB) return FALSE;
m_pDIB->bmiHeader.biSize = dwcBihSize;
m_pDIB->bmiHeader.biWidth = dwWidth;
m_pDIB->bmiHeader.biHeight = dwHeight;
m_pDIB->bmiHeader.biBitCount = nBits;
m_pDIB->bmiHeader.biPlanes = 1;
m_pDIB->bmiHeader.biCompression = BI_RGB;
m_pDIB->bmiHeader.biXPelsPerMeter = 1000;
m_pDIB->bmiHeader.biYPelsPerMeter = 1000;
m_pDIB->bmiHeader.biClrUsed = 0;
m_pDIB->bmiHeader.biClrImportant = 0;
InitializeColors();
return TRUE;
}
You will notice that dwSize is calculated from the size of the BITMAPINFOHEADER structure, the size of the RGBQUAD array required for the specified color depth, and the size of the resulting bitmap buffer.
The BITMAPINFOHEADER structure is initialized with dimensions of 1,000x1,000 pixels per meter and uses the specified width, height, and color depth. The compression flag biCompression lets you specify the type of compression used in the DIB image buffer. The BI_RGB flag shown in the code sample indicates that no compression is used.
The colors then could be initialized (all to black) like this:
void CDIB::InitializeColors()
{
if (!m_pDIB) return;
// This just initializes all colors to black
LPRGBQUAD lpColors =
(LPRGBQUAD)(m_pDIB+m_pDIB->bmiHeader.biSize);
for(int i=0;i<GetDIBCols();i++)
{
lpColors[i].rgbRed=0;
lpColors[i].rgbBlue=0;
lpColors[i].rgbGreen=0;
lpColors[i].rgbReserved=0;
}
}
You might want to initialize a specific set of RGBQUAD colors, depending on the requirements of your DIB.
Creating a DIB from a Device-Dependent Bitmap
After you set the color values for the DIB, you can set the pixels for the image directly into a buffer (usually stored immediately after the BITMAPINFO structure) from within your code, or from an existing device-dependent bitmap.
Although it is not necessary, it usually is desirable to keep the bitmap image information directly after the BITMAPINFO structure, as shown earlier in the CreateDIB() implementation.
The following helper functions can help you retrieve the number of colors calculated from the color depth in BITMAPINFOHEADER, a pointer to the bitmap image buffer calculated from the size of the header structure, and the following color value array:
int CDIB::GetDIBCols() const
{
if (!m_pDIB) return 0;
return (2>>m_pDIB->bmiHeader.biBitCount);
}
VOID* CDIB::GetDIBBitArray() const
{
if (!m_pDIB) return FALSE;
return (m_pDIB + m_pDIB->bmiHeader.biSize +
GetDIBCols() * sizeof(RGBQUAD));
}
You can find the size and color-depth information from a device-dependent bitmap by using the CBitmap class's GetBitmap() function. This function fills a BITMAP structure (passed by pointer) with the details about a specific GDI bitmap object.
You then can copy these details along with the extra DIB information into the DIB's BITMAPINFOHEADER to create a DIB with the same width, height, and color depth as the device-dependent bitmap.
The DIB color-value information then can be initialized with the SetDIBColorTable() from a reference device context.
After the DIB header and color values are initialized from the bitmap, it only remains to copy the image bits themselves. The GetDIBits() function can perform this task for you. You just need to pass the DIB information, a reference device context, and the GDI bitmap handle; GetDIBits() then copies the entire image bitmap into your supplied buffer.
If you want to perform the reverse operation of copying a DIB bitmap to a device- dependent bitmap, you can use the corresponding SetDIBits() function. You can even create an HBITMAP GDI object initialized from a DIB directly by using the global CreateDIBitmap() function.
The following implementation for the CDIB class's CreateDIBFromBitmap() function demonstrates these functions using the device-dependent m_bmBitmap member. The CreateDIBFromBitmap() function assumes that this CBitmap member was initialized previously through a CreateBitmap() or LoadBitmap():
BOOL CDIB::CreateDIBFromBitmap(CDC* pDC)
{
if (!pDC) return FALSE;
HDC hDC = pDC->GetSafeHdc();
BITMAP bimapInfo;
m_bmBitmap.GetBitmap(&bimapInfo);
if (!CreateDIB(bimapInfo.bmWidth,bimapInfo.bmHeight,
bimapInfo.bmBitsPixel)) return FALSE;
LPRGBQUAD lpColors =
(LPRGBQUAD)(m_pDIB+m_pDIB->bmiHeader.biSize);
SetDIBColorTable(hDC,0,GetDIBCols(),lpColors);
// This implicitly assumes that the source bitmap
// is at the 1 pixel per mm resolution
BOOL bSuccess = (GetDIBits(hDC,(HBITMAP)m_bmBitmap,
0,bimapInfo.bmHeight,GetDIBBitArray(),
m_pDIB,DIB_RGB_COLORS) > 0);
return bSuccess;
}
You will notice the DIB_RGB_COLORS flag also is passed to the GetDIBits() function. You can set this flag to either DIB_RGB_COLORS to indicate that the color values are literal RGB values, or DIB_RGB_PAL to specify color index positions in the device context's current palette.
You might call the CreateDIBFromBitmap() function after loading a resource-based bitmap like this:
#include "dib.h"
CDIB g_DIB;
void CBitmapdemoView::OnInitialUpdate()
{
CView::OnInitialUpdate();
CClientDC dc(this);
g_DIB.m_bmBitmap.LoadBitmap(IDB_TSTBITMAP);
g_DIB.CreateDIBFromBitmap(&dc);
}
After calling the CreateDIBFromBitmap() function, the embedded DIB will be initialized in the memory pointed at by the m_pDIB pointer.
Drawing with a DIB
You can use several GDI functions to draw from a DIB directly into a normal device context. These functions are similar to the device-dependent blit functions you saw earlier.
The global SetDIBitsToDevice() function copies the image from a DIB buffer directly to a specified device context at a specified position. You can specify the number of lines to copy, as well as the width, height, and position of the source DIB image. If the operation succeeds, SetDIBitsToDevice() returns the number of scan lines copied.
You can use StretchDIBits() in a way similar to StretchBlt() to stretch the DIB bitmap to the required size. This function often is useful when you are trying to preserve the original bitmap size. You can use the DIB's pixels-per-meter values to calculate the correct destination size, as well as a specific mapping mode to draw the image at a specific size.
The following example shows an implementation of the CopyDIB() function for the CDIB class defined previously. This function uses StretchDIBits() to render the DIB into a device context that is provided.
This method maintains the standard size of the DIB by setting the mapping mode to MM_LOMETRIC. In this mapping mode, the logical unit size is 0.1 mm, so coordinates specified by the GDI functions will be converted by the device context to represent the correct specified size on the destination device. Therefore the coordinates specified in the destination width and height should be multiplied by 10 so that each DIB pixel represents 1 mm to maintain the 1,000 pixel-per-meter specification:
BOOL CDIB::CopyDIB(CDC* pDestDC,int x,int y)
{
if (!m_pDIB || !pDestDC) return FALSE;
int nOldMapMode = pDestDC->SetMapMode(MM_LOMETRIC);
BOOL bOK = StretchDIBits(pDestDC->GetSafeHdc(),
x,y,
m_pDIB->bmiHeader.biWidth * 10, // Dest Width
m_pDIB->bmiHeader.biHeight * -10, // Dest Height
0,0,
m_pDIB->bmiHeader.biWidth, // Source Width
m_pDIB->bmiHeader.biHeight, // Source Height
GetDIBBitArray(),m_pDIB,DIB_RGB_COLORS,SRCCOPY) > 0;
pDestDC->SetMapMode(nOldMapMode);
return bOK;
}
You could invoke this code from your application's view class in the OnDraw() and OnPrint() functions, after previously creating the DIB from the loaded bitmap resource (as shown earlier), like this:
void CBitmapdemoView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
g_DIB.CopyDIB(pDC,50,-50);
}
void CBitmapdemoView::OnDraw(CDC* pDC)
{
g_DIB.CopyDIB(pDC,50,-50);
}
Regardless of the device context passed to CopyDIB() and the ultimate destination device, the DIB should be rendered to the same size and with the same colors to the best capabilities of the device.
Summary | Next Section

Account Sign In
View your cart