c ++从hbitmap获取原始像素数据

时间:2014-02-26 18:45:58

标签: c# c++ bitmap pinvoke

我对使用p / invoke调用相当新,我想知道是否有人可以指导我如何从hbitmap检索原始像素数据(unsigned char *)。

这是我的情景:

我正在 C#端加载 .NET Bitmap 对象,并将其IntPtr发送到我的非托管c ++方法。一旦我在 C ++ 端收到hbitmap ptr,我想访问Bitmaps的像素数据。我已经创建了一个接受unsigned char *的方法,它表示来自c#的原始像素数据但是我发现从c#中提取byte []相当慢。这就是为什么我想发送Bitmap ptr而不是将Bitmap转换为byte []并将其发送到我的C ++方法。

用于获取Bitmap IntPtr的C#代码

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

用于导入c ++方法的C#代码

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

将接收Hbitmap处理程序的C ++方法

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

问题:

1)由于我发送的是IntPrt,我的C ++方法参数可以是unsigned char *吗?

2)如果没有,我如何从c ++访问位图的原始数据?

3 个答案:

答案 0 :(得分:4)

GetHbitmap方法不检索像素数据。它产生一个类型为HBITMAP的GDI位图句柄。您的非托管代码会将其作为HBITMAP类型的参数接收。您可以使用GDI调用从中获取像素数据。但它本身并不是原始像素。

事实上,我很确定你是以错误的方式攻击这个问题。您可能正朝这种方向发展,因为GetPixelSetPixel速度很慢。这是真的。实际上,他们的GDI等价物也是如此。您需要做的是使用LockBits。这将允许您以高效的方式操作C#中的整个像素数据。可以在此处找到关于该主题的良好描述:http://bobpowell.net/lockingbits.aspx。请注意,为了提高效率,这是一种C#代码,其中不安全的代码和指针通常是最佳解决方案。

如果由于某种原因,您仍然希望使用C ++代码对像素数据进行操作,那么您仍然可以使用LockBits作为获取指向像素数据的指针的最简单方法。它肯定比非托管GDI等价物容易得多。

答案 1 :(得分:3)

首先,HBITMAP不应该是unsigned char*。如果您将HBITMAP传递给C ++,则参数应为HBITMAP

int Resize::ResizeImage(HBITMAP hBmp)

接下来将HBITMAP转换为像素:

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

答案 2 :(得分:0)

显然从Scan0发送指针等同于我正在搜索的内容。我可以通过发送从bitmapData.Scan0方法检索到的IntPtr来按预期操作数据。

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

为了进一步帮助澄清,我在初始帖子中更改的唯一代码是第一个代码块。其余的都保持不变。 (C ++方法仍然接受unsigned char *作为参数)