Accessing Individual Pixels in a CBitmap Object
As an exercise, I'm trying to write a piece of code that can map a single pixel from an MFC CBitmap object at a specific x / y location.
There is no type interface in the class GetPixel
, and most of the information I have seen indicates copying the entire contents of the CBitmap bits through CBitmap::GetBitMapBits
, which seems extremely inefficient.
Is there no way to access the byte array through a pointer and access it as an array?
source to share
If the object CBitmap
is associated with a device independent bitmap (DIB created CreateDIBSection()
), you can get a pointer to directly access the bitmap pixels (no copying) by calling . Be sure to call if you have accessed the bitmap pixels by any other GDI functions before using direct access. GetObject()
GdiFlush()
If CBitmap
associated with a device-dependent bitmap (DDB, also known as a compatible bitmap), which method to use depends on how many pixels you want.
- If you need access only to the number of pixels , you can go
CDC::SelectObject()
, . It will be very slow if you want to read more pixels.CDC::GetPixel()
- To access a large number of pixels , you can use
CBitmap::GetBitMapBits()
or . The latter can be more efficient when you only need to access a subset of the raster pixels, because it has options to define the range of scan lines to copy.GetDIBits()
Anyway, DDB will always be slower than DIB when you need to get pixel by pixel from it.
The following example determines if CBitmap
DIB or DDB and branches are linked to use the most efficient accessor 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
}
}
}
source to share
You need to select CBitmap
in CDC ( CDC :: SelectObject ). The device context contains the CDC :: GetPixel element .
source to share
My answer is BAD because the approach is GetPixel
slow. Didn't delete it, just stroked the text, only for people coming here to see that this is something they MUST NOT do.
<s> Something like
CDC mem_dc;
mem_dc.CreateCompatibleDC(dc);
CBitmap* old_bitmap=(CBitmap*)mem_dc.SelectObject(&bmp);
COLORREF cr_xy=mem_dc.GetPixel(x,y);
mem_dc.SelectObject(old_bitmap);
DeleteDC(mem_dc);
should do the job. C>
source to share