2014-02-26 370 views
6

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

这是我的情景:

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

用于获取位图的IntPtr

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

C#代码用于导入C++方法将接收HBITMAP处理程序

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

问题

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

C++方法C#代码

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

2)如果不是,我如何从C++访问位图的原始数据?

回答

4

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

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

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

+0

感谢大卫,这正是我发现还!我发布我的代码作为答案,希望可以帮助其他人寻找相同的解决方案。 – Matthew

+0

你当然可以这样做。但它也应该可以生成高效的托管代码。使用不安全和指针。 –

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++方法仍接受无符号字符*作为参数)

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

感谢您的回复,我会尝试比较使用此方法和我发布的解决方案的性能差异。 – Matthew