I'm using a wrapped C-library in C# and need to convert an image from that library to a Bitmap and back, but without copying the pixel buffer.
Converting to a Bitmap was simple:
Bitmap WrapAsBitmap(CImage image)
{
return new Bitmap(image.Width, image.Height, image.BytesPerLine, PixelFormat.Format24bppRgb, image.Data);
}
By just passing in the original pixel buffer (image.Data
) to the Bitmap
constructor, no copy is needed. For doing the opposite, I need to call LockBits
and UnlockBits
to get access to the IntPtr
of the original pixel buffer:
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;
}
Now, as you can see, if data.Scan0
is in managed memory, this is actually a dangerous approach as the bits are 'unlocked' before the newly constructed image
will be used. If bitmap
was constructed from the WrapAsBitmap
method, then there is no problem as data.Scan0
points to unmanaged memory which will not be moved by .NET. Therefore, I want to build in a safety check that checks the location (managed/unmanaged) of the data.Scan0
pointer.
An other solution I was thinking of was to inherit from the Bitmap
class and keep track of the original pointer image.Data
along with the Bitmap
, but the Bitmap
class is sealed, so I'm out of luck.
In short: any ideas how to detect the location (managed/unmanaged) of an IntPtr
?
There is no scenario where that IntPtr is ever going to point to managed memory. Not when the image data was generated by C code of course. Not when it was generated by Bitmap either, GDI+ is unmanaged code. You can reason this through by yourself, you can only ever obtain an IntPtr for managed memory by pinning it. GCHandle.AddrOfPinnedObject(). A hard requirement to prevent the garbage collector from invalidating the pointer when it compacts the heap. Nowhere do you see a documented need to pin.
There is no point in implementing the test.
You do have the rock-hard requirement to keep that IntPtr valid for the lifetime of the Bitmap wrapper. Up to the point where you call its Dispose() method. You cannot make this automagic, other than by encapsulating the Bitmap object in a class that implements IDisposable itself. Not doing so causes very ugly mis-behavior, you only get an AccessViolationException when you're lucky. A corrupted bitmap with random pixel content is the normal failure mode.
If you can't implement that guarantee then you should not do this and copy the bitmap data instead. It is not hard to implement, just not pretty.