In the last two posts I have showed how to create a BitmapSource image and access its underlying WIC image, for manipulating its pixels. In this post I will explain how does the WICBitmap* works.
Download the code from here.
Looking inside the BitmapSource class using Reflector, it figures out that it encapsulates a WIC image (_wicSource). The WICBitmap class uses reflection to retrieve this private field:
public WICBitmap(BitmapSource source)
{
Type bitmapSource = typeof(BitmapSource);
FieldInfo wicSourceField = bitmapSource.GetField(
"_wicSource", BindingFlags.NonPublic | BindingFlags.Instance);
_wicBitmapSafeHandle = (SafeHandle)wicSourceField.GetValue(source);
}
Now that we have the underlying WIC image safe handle, we can do anything with it. In order to have an access to the pixels buffer, one must call the WIC lock API. If you look further inside the BitmapSource class you will find that it uses WIC Proxy Functions. These kinds of functions provide an easy interop to the WIC API since it's based on COM.
To have a lock, WICBitmap class exposes Lock and TryLock methods. These methods create a WICBitmapLock instance which actually provides the lock:
private static readonly MethodInfo GetDataPointerMethod;
private static readonly MethodInfo LockMethod;
static WICBitmapLock()
{
Type internalWICBitmapType = Type.GetType(
"MS.Win32.PresentationCore.UnsafeNativeMethods+WICBitmap, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
LockMethod = internalWICBitmapType.GetMethod(
"Lock", BindingFlags.NonPublic | BindingFlags.Static);
Type internalWICBitmapLockType = Type.GetType(
"MS.Win32.PresentationCore.UnsafeNativeMethods+WICBitmapLock, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
GetDataPointerMethod = internalWICBitmapLockType.GetMethod(
"GetDataPointer", BindingFlags.NonPublic | BindingFlags.Static);
}
internal bool Lock(SafeHandle wicBitmapSafeHandle, LockFlags flags, out int errorCode)
{
object[] args = new object[] { wicBitmapSafeHandle, _region, flags, null };
errorCode = (int)LockMethod.Invoke(null, args);
if (errorCode != 0)
{
return false;
}
_wicBitmapLockSafeHandle = (SafeHandle)args[3];
args = new object[] { _wicBitmapLockSafeHandle, null, null };
errorCode = (int)GetDataPointerMethod.Invoke(null, args);
if (errorCode != 0)
{
return false;
}
_wicBitmapBuffer = new WICBitmapBuffer((IntPtr)args[2], (uint)args[1]);
return true;
}
To create a lock, WICBitmapLock calls the internal "Lock" method using reflection. To get the size and pointer to the pixel buffer WICBitmapLock calls the internal "GetDataPointer" method using reflection.
Now that we have a pointer to the pixels memory, we can directly manipulate it using native or unsafe code.
Download the code from here.
WARNING: This code calls WPF internal methods using reflection and it is preliminary to changed. Use it on your own risk.