简体   繁体   中英

c++ Get raw pixel data from hbitmap

I am fairly new to using p/invoke calls and am wondering if someone can guide me on how to retrieve the raw pixel data (unsigned char*) from an hbitmap.

This is my scenario:

I am loading a .NET Bitmap object on the C# side and sending it's IntPtr to my unmanaged c++ method. Once I receive the hbitmap ptr on the C++ side, I would like to access the Bitmaps' pixel data. I already made a method that accepts an unsigned char* which represents the raw pixel data from c# however I found extracting the byte[] from the c# is fairly slow. This is why I want to send in the Bitmap ptr instead of converting the Bitmap into a byte[] and sending that to my C++ method.

C# code for getting Bitmap IntPtr

Bitmap srcBitmap = new Bitmap(m_testImage);
IntPtr hbitmap = srcBitmap.GetHbitmap();

C# code for importing c++ method

[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int ResizeImage(IntPtr srcImg);

C++ method that will receive the Hbitmap handler

int Resize::ResizeImage(unsigned char* srcImg){
    //access srcImgs raw pixel data (preferably in unsigned char* format)
   //do work with that 
   return status;
}

Questions:

1) Since I am sending in an IntPrt, can my C++ method parameter be an unsigned char* ?

2) If not, how can I access the bitmap's raw data from c++?

The GetHbitmap method does not retrieve pixel data. It yields a GDI bitmap handle, of type HBITMAP . Your unmanaged code would receive that as a parameter of type HBITMAP . You can obtain the pixel data from that using GDI calls. But it is not, in itself, the raw pixels.

In fact, I'm pretty sure you are attacking this problem the wrong way. You are probably heading this way because GetPixel and SetPixel are slow. This quite true. Indeed, their GDI equivalents are too. What you need to do is to use LockBits . This will allow you to operate on the entire pixel data in C# in an efficient way. A good description of the subject can be found here: https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx . Note that, for efficiency, this is one type of C# code where unsafe code and pointers is often the best solution.

If, for whatever reason, you still wish to operate on the pixel data using C++ code, then you can still use LockBits as the simplest way to get a pointer to the pixel data. It's certainly much easier than the unmanaged GDI equivalents.

First, an HBITMAP shouldn't be a unsigned char* . If you are passing an HBITMAP to C++ then the parameter should be an HBITMAP :

int Resize::ResizeImage(HBITMAP hBmp)

Next to convert from HBITMAP to pixels:

std::vector<unsigned char> ToPixels(HBITMAP BitmapHandle, int &width, int &height)
{        
    BITMAP Bmp = {0};
    BITMAPINFO Info = {0};
    std::vector<unsigned char> Pixels = std::vector<unsigned char>();

    HDC DC = CreateCompatibleDC(NULL);
    std::memset(&Info, 0, sizeof(BITMAPINFO)); //not necessary really..
    HBITMAP OldBitmap = (HBITMAP)SelectObject(DC, BitmapHandle);
    GetObject(BitmapHandle, sizeof(Bmp), &Bmp);

    Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    Info.bmiHeader.biWidth = width = Bmp.bmWidth;
    Info.bmiHeader.biHeight = height = Bmp.bmHeight;
    Info.bmiHeader.biPlanes = 1;
    Info.bmiHeader.biBitCount = Bmp.bmBitsPixel;
    Info.bmiHeader.biCompression = BI_RGB;
    Info.bmiHeader.biSizeImage = ((width * Bmp.bmBitsPixel + 31) / 32) * 4 * height;

    Pixels.resize(Info.bmiHeader.biSizeImage);
    GetDIBits(DC, BitmapHandle, 0, height, &Pixels[0], &Info, DIB_RGB_COLORS);
    SelectObject(DC, OldBitmap);

    height = std::abs(height);
    DeleteDC(DC);
    return Pixels;
}

Apparently sending in the Pointer from Scan0 is equivalent to what I was searching for. I am able to manipulate the data as expected by sending in an IntPtr retrieved from the bitmapData.Scan0 method.

Bitmap srcBitmap = new Bitmap(m_testImage);
Rectangle rect = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData bmpData = srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, srcBitmap.PixelFormat);
//Get ptr to pixel data of image
IntPtr ptr = bmpData.Scan0;
//Call c++ method
int status = myDll.ResizeImage(ptr);
srcBitmap.UnlockBits(bmpData);

To further help clarify, the only code I changed from my initial post was the first block of code. All the rest remained the same. (C++ method still accepts unsigned char * as a param)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM