As an exercise, I am attempting to write a piece of code which can sample a single pixel from an MFC CBitmap object, at a particular x/y location.
The class does not have any GetPixel
type interface, and most of the information I have seen indicates copying the entire contents of the CBitmap bits via CBitmap::GetBitMapBits
, which seems extremely inefficient.
Is there no way to gain access to the byte array via pointer and access it as an array?
If the CBitmap
object is associated with a device-independent bitmap (DIB, created by CreateDIBSection()
for instance), you can get a pointer to directly access the bitmap pixels (without copying) by calling GetObject()
. Make sure to call GdiFlush()
if you have accessed the bitmap pixels by any other GDI functions before using direct access.
If the CBitmap
is associated with a device-dependent bitmap (DDB, also known as compatible bitmap), which method to use depends on how many pixels you want to access.
CDC::SelectObject()
, CDC::GetPixel()
route. This will be very slow if you want to read a bigger number of pixel. CBitmap::GetBitMapBits()
or GetDIBits()
. The latter may be more efficient when you only need to access part of the bitmap pixels because it has parameters to define a range of scanlines to copy. In either case, a DDB will always be slower than a DIB when you need to access it pixel-by-pixel.
The following example detects if a CBitmap
is associated with either a DIB or a DDB and branches to use the most efficient access method for each case.
void DoAwesomeStuff( CBitmap& bitmap )
{
DIBSECTION dib{ 0 };
if( ::GetObject( bitmap, sizeof( dib ), &dib ) )
{
// GetObject() succeeded so we know that bmp is associated with a DIB.
// Evaluate the information in dib thoroughly, to determine if you can handle
// the bitmap format. You will propably restrict yourself to a few uncompressed
// formats.
// In the following example I accept only uncompressed top-down bitmaps
// with 32bpp.
if( dib.dsBmih.biCompression == BI_RGB &&
dib.dsBmih.biHeight < 0 && // negative height indicates top-down bitmap
dib.dsBmih.biPlanes == 1 &&
dib.dsBmih.biBitCount == 32 )
{
DWORD* pPixels = reinterpret_cast<DWORD*>( dib.dsBm.bmBits );
// TODO: Access the bitmap directly through the pPixels pointer.
// Make sure to check bounds to avoid segfault.
}
}
else
{
// GetObject() failed because bmp is not a DIB or for some other reason.
BITMAP bmp{ 0 };
if( ::GetObject( bitmap, sizeof( bmp ), &bmp ) )
{
// GetObject() succeeded so we know that bmp is associated with a DDB.
CDC dc;
// Create a memory DC.
dc.CreateCompatibleDC( nullptr );
if( CBitmap* pOldBmp = dc.SelectObject( &bitmap ) )
{
// Get the bitmap pixel at given coordinates.
// For accessing a large number of pixels, CBitmap::GetBitMapBits()
// or GetDIBits() will be more efficient.
COLORREF pixel = dc.GetPixel( 42, 24 );
// Standard cleanup: restore the bitmap that was originally
// selected into the DC.
dc.SelectObject( pOldBmp );
}
else
{
// TODO: handle error
}
}
else
{
// TODO: handle error
}
}
}