我在C#中使用了一个包装的C库,需要将该库中的图像转换为Bitmap并返回,但是没有复制像素缓冲区。
转换为位图非常简单:
Bitmap WrapAsBitmap(CImage image)
{
return new Bitmap(image.Width, image.Height, image.BytesPerLine, PixelFormat.Format24bppRgb, image.Data);
}
只需将原始像素缓冲区(image.Data
)传递给Bitmap
构造函数,就不需要复制。为了做相反的事情,我需要调用LockBits
和UnlockBits
来访问原始像素缓冲区的IntPtr
:
CImage WrapAsCImage(Bitmap bitmap)
{
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
var image = new CImage(bitmap.Width, bitmap.Height, data.Stride * 3, data.Scan0);
bitmap.UnlockBits(data);
return image;
}
现在,正如您所看到的,如果data.Scan0
位于托管内存中,这实际上是一种危险的方法,因为这些位已被解锁。在使用新构造的image
之前。如果bitmap
是从WrapAsBitmap
方法构造的,则没有问题,因为data.Scan0
指向非移动内存,.NET不会移动它。因此,我想构建一个安全检查,检查data.Scan0
指针的位置(托管/非托管)。
我想到的另一个解决方案是继承Bitmap
类并跟踪原始指针image.Data
和Bitmap
,但Bitmap
类是密封的,所以我运气不好。
简而言之:如何检测IntPtr
的位置(托管/非托管)?
答案 0 :(得分:5)
没有任何情况,IntPtr 曾指向托管内存。当然,当图像数据由C代码生成时。不是由Bitmap生成的,GDI +也是非托管代码。您可以自己解释这一点,您只能通过固定它来获取托管内存的IntPtr。 GCHandle.AddrOfPinnedObject()。很难要求在压缩堆时防止垃圾收集器使指针无效。你没有看到记录在案的需要。
实施测试毫无意义。
你做有严格的要求,要求IntPtr在Bitmap包装器的生命周期内保持有效。直到你调用它的Dispose()方法。除了通过将Bitmap对象封装在实现IDisposable本身的类中之外,您无法实现这种自动化。不这样做会导致非常丑陋的错误行为,当你很幸运时,你只会得到一个AccessViolationException。带有随机像素内容的损坏位图是正常故障模式。
如果您无法实现该保证,则不执行此操作并复制位图数据。它并不难实现,只是不漂亮。
答案 1 :(得分:0)
Bitmap
类实际上只是GDI + Bitmap对象的托管包装器。由于该对象不在暴露给用户代码的固定缓冲区上运行,也不保证该缓冲区采用特定格式,因此您将无法执行您所描述的操作。
换句话说,在不限制问题空间的情况下,肯定会出现需要缓冲区复制的情况。