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?

+3


source to share


3 answers


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.



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
        }
    }
}

      

+1


source


You need to select CBitmap

in CDC ( CDC :: SelectObject ). The device context contains the CDC :: GetPixel element .



+3


source


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>

0


source







All Articles