访问 CBitmap 对象中的单个像素

Access individual pixels in CBitmap object

作为练习,我正在尝试编写一段代码,它可以在特定 x/y 位置从 MFC CBitmap 对象中采样单个像素。

class没有任何GetPixel类型的接口,而且我看到的大部分资料都是通过CBitmap::GetBitMapBits复制CBitmap位的全部内容,这似乎非常效率低下。

有没有办法通过指针访问字节数组并将其作为数组访问?

我的回答是 不好 因为 GetPixel 方法很慢。没有删除它,只是抚摸了文本,只是为了让来这里的人看到它是他们不应该做的事情。

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);

应该完成这项工作。

您需要select CBitmap 成为 CDC (CDC::SelectObject) first. The device context has a CDC::GetPixel 成员。

如果 CBitmap 对象在使用直接访问之前与 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 关联。

如果CBitmapdevice-dependent bitmap(DDB,也称为兼容位图)关联,使用哪种方法取决于多少像素你想访问。

  • 如果只需要访问少数像素,您可以走CDC::SelectObject(), CDC::GetPixel()路线。如果您想读取更多像素,这将非常慢。
  • 要访问大量像素,您可以使用CBitmap::GetBitMapBits() or GetDIBits()。当您只需要访问部分位图像素时,后者可能更有效,因为它具有定义要复制的扫描线范围的参数。

无论哪种情况,当您需要逐像素访问时,DDB 总是比 DIB 慢。

以下示例检测 CBitmap 是否与 DIB 或 DDB 关联,并分支以针对每种情况使用最有效的访问方法。

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