Introduction
CDibData
is a class used to simplify loading, saving, converting (color-depth), and accessing bitmap image bits. It was originally designed as a utility class to allow direct access to the image bits of a bitmap object. Upon realizing that it would be a useful class, on its own, I decided to expand its abilities to include: loading, saving, and conversion of bitmaps. The class does not require that the bitmap be loaded as a DIBSECTION
in order to be useful, that was the original requirement when it was created.
I have also included a copy of Quantize.cpp by Jeff Prosise; Quantize.cpp can be found in the MFC library for those interested in reading about it.
Ok after reading this article and testing the code: blast me, find the bugs, tell me how to improve the code and/or Doxygen generated documentation. Do your best to break the code, even you guys in quality control (who specialize in the ridiculous). If there is something wrong, I want to know!
Background
The main references used to develop this were "Programming Windows Fifth Edition" by Charles Petzold, and the MFC library. Most of the references are listed in CDibData.cpp.
Using the Code
The DIB is stored internally in a single block of globally allocated memory suitable for transfer via the clipboard using the CF_DIB
format. Since CDibData
was not designed as a replacement for CBitmap
, it does not maintain a copy of the original bitmap handle.
If you have not installed the Windows SDK from Microsoft, you will get some compilation errors when compiling in debug mode. The reason for this is that BITMAPV5HEADER
was not defined in the SDK that came with Visual C++ 6.0. Since BITMAPV5HEADER
is only used for displaying debugging information, it should be easy to comment out the code.
Note: When an CDibData
object contains a compressed DIB, you no longer have direct access to the image bits. Therefore, you can save the DIB image to file but not convert it or use any function that requires direct access to the image bits.
Examples of how to load a bitmap file:
HBITMAP hBitmap = m_DibData.LoadDIB(lpszPathName);
if( !hBitmap )
return FALSE;
m_Bitmap.Attach(hBitmap);
HBITMAP hBitmap = (HBITMAP)::LoadImage(
NULL, lpszPathName, IMAGE_BITMAP,
0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
if( !hBitmap )
return FALSE;
if( m_Bitmap.Attach(hBitmap) )
m_DibData.CreateDIB(&m_Bitmap);
try
{
CFile cf(lpszPathName, CFile::modeRead);
BITMAPFILEHEADER bmfh;
cf.Read((void*)&bmfh, sizeof(BITMAPFILEHEADER));
if( bmfh.bfType != 0x4D42 )
{
cf.Close();
return NULL;
}
DWORD dwLen = cf.GetLength() - sizeof(BITMAPFILEHEADER);
hDib = DibAlloc(dwLen);
pDib = DibLock(hDib);
if( pDib )
cf.Read((void*)pDib, (UINT)dwLen);
cf.Close();
if( !pDib )
{
if( hDib )
DibFree(hDib);
return NULL;
}
DibUnlock(hDib);
}
catch( CFileException* )
{
if( pDib )
DibUnlock(hDib);
if( hDib )
DibFree(hDib);
return NULL;
}
if( !Attach(hDib) )
{
DibFree(hDib);
return NULL;
}
Examples of how to save a bitmap file:
m_DibData.CreateDIB.SaveDIB(lpszPathName);
m_DibData.CreateDIB.SaveDIB(lpszPathName, NULL, TRUE);
m_DibData.CreateDIB.SaveDIB(lpszPathName, &m_Bitmap);
m_DibData.CreateDIB.SaveDIB(lpszPathName, &m_Bitmap, TRUE);
Examples of converting from one format to another:
CDibData* pDibClass = m_DibData.GetConvertedDIB(wNewColorDepth);
CDibData* pDibClass = m_DibData.GetConvertedDIB(wNewColorDepth, TRUE);
CDibData ddObj;
ddObj.CreateDIB(&m_Bitmap);
CDibData* pDibClass = ddObj.GetConvertedDIB(wNewColorDepth, TRUE);
Example of 90 degree rotation:
CDibData dibSrc;
if( !dibSrc.CreateDIB(&bmpSrc) )
return FALSE;
int x, y, t;
int cx = dibSrc.GetWidth();
int cy = dibSrc.GetHeight();
BOOL bResult = FALSE;
CBitmap bmpDest;
{
CDC dcDest;
dcDest.CreateCompatableDC(NULL);
CBitmap* pOldBitmap = dcDest.SelectObject(&bmpSrc);
BOOL bRet = bmpDest.CreateCompatibleBitmap(&dcDest, cy, cx);
dcDest.SelectObject(pOldBitmap);
dcDest.DeleteDC();
if( !bRet )
return bRet;
}
CDibData dibDest;
if( !dibDest.CreateDIB(&bmpDest) )
return FALSE;
if( nRotations == 0 )
{
for( x = 0; x < cx; ++x )
{
t = cy - 1;
for( y = 0; y < cy; ++y, --t )
dibDest.CopyPixelValue( t, x, dibSrc, x, y);
}
}
else
{
for( y = 0; y < cy; ++y )
{
t = cx - 1;
for( x = 0; x < cx; ++x, --t )
dibDest.CopyPixelValue(y, t, dibSrc, x, y);
}
}
if( !dibDest.SetDIBits(&bmpDest) )
return FALSE;
Points of Interest
Some of you may be interested in the following:
- Question: What is with the style used to write the comments proceeding each function?
- Answer: I decided to use Doxygen to generate documentation for this object. I've used it before to help me understand other peoples code, but never to create accurate documentation of mine. You will find Doxygen.dat in the
CDibData
directory used by the Doxygen wizard to generate documentation for the CDibData
class.
- Question: Can
GetDIBits()
be used to convert bitmaps from one format to another? - Answer: Yes, but when converting to a lower color depth you loose important color information. The reason for this is that
GetDIBits()
does no color optimization.
- Question: Why use
CDibData::LoadDIB()
instead of LoadImage()
? - Answer:
CDibData::LoadDIB()
can load top-down bitmaps and 2 bits-per-pixel bitmaps, LoadImage()
cannot. It is my understanding that WinCE supports 2 bits-per-pixel (shades of gray) bitmaps therefore, I decided to support loading, saving, and manipulation of 2 bits-per-pixel bitmaps. Although CDibData
recognizes and can manipulate 2 bits-per-pixel bitmaps, it must convert it to 4 bits-per-pixel, when loading, in order to create an HBITMAP
handle that can be used by GDI on non-WinCE devices (a.k.a. your monitor).
- Question: Does
CDibData
support WinCE? - Answer: No, although
CDibData
supports 2 bits-per-pixel manipulation, it is designed to work on a PC. The main reason for supporting 2 bits-per-pixels is that some one has to create the bitmaps used by WinCE. I would also like to note that CDibData
does not remember the original format of the bitmap that was loaded therefore, you will have to convert it back to 2 bits-per-pixels when saving.
- Question: Does
CDibData
support the newest bitmap header types? - Answer: No,
CDibData
only supports DIBs with a BITMAPINFOHEADER
.
- Question: Why not use
GetBitmapBits()
to get access to image bits? - Answer: See Q131896 in the MFC library.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.