WPF vs. GDI+ Some additional notes.

In one of my previous posts WPF vs. GDI+ I wrote about performance of WPF and how to solve this problem. After some experiments in Tesseris Pro we’ve found more improvements to solution described in previous post. The main idea is that when you are converting GDI bitmap to WPF bitmap it requires memory allocation and deceases performance. And fortunately there is solution that allows to map WPF bitmap to GDI bitmap so when we are drawing on one bitmap other bitmap is changing too, because they are located in the same memory.

First we will need some API calls. You will be able to read all description in MSDN but names of the functions are more than descriptive, if you know Win API of course 😉

[code lang=csharp]
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFileMapping(
IntPtr hFile,
IntPtr lpFileMappingAttributes,
uint flProtect,
uint dwMaximumSizeHigh,
uint dwMaximumSizeLow,
string lpName);

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr MapViewOfFile(
IntPtr hFileMappingObject,
uint dwDesiredAccess,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
uint dwNumberOfBytesToMap);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool UnmapViewOfFile(IntPtr hFileMappingObject);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
[/code]

Than before creating bitmap let’s create memory mapped file as source of bitmaps:

[code lang=csharp]
var format = PixelFormats.Bgr32;

var pixelCount = (uint)(width * height * format.BitsPerPixel / 8);
var rowWidth = width * (format.BitsPerPixel / 8);

this.fileMapping = CreateFileMapping(
new IntPtr(-1),
IntPtr.Zero,
0x04,
0,
pixelCount,
null);

this.mapView = MapViewOfFile(
fileMapping,
0xF001F,
0,
0,
pixelCount);
[/code]

When we are calling CreateFileMapping with new IntPtr(-1) as first parameter windows doesn’t map some file to memory but will use system page file as source of mapping. And of course in this case we should specify size of file with width * height * format.BitsPerPixel / 8.

Now let’s create two bitmaps mapped to this mapped file:

[code lang=csharp]
this.bitmap = new System.Drawing.Bitmap(
width,
height,
rowWidth,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
this.mapView);

this.image = (System.Windows.Interop.InteropBitmap)
System.Windows.Interop.Imaging.CreateBitmapSourceFromMemorySection(
fileMapping,
width,
height,
format,
rowWidth,
0);
[/code]

Now you can in OnRender you can use following code:

[code lang=csharp]
protected override void OnRender(DrawingContext dc)
{
// Ensure that bitmap is initialized and has correct size
// Recreate bitmap ONLY when size is changed
InitializeBitmap((int)this.width, (int)this.height);

//TODO: Put here your drawing code

// Invalidate and draw bitmap on WPF DrawingContext
this.image.Invalidate();
dc.DrawImage(
this.image,
new Rect(0, 0, this.bitmap.Width, this.bitmap.Height));
}
[/code]

Please note than this.image should be of type System.Windows.Interop.InteropBitmap to call Invalidate method. And don’t forget to call UnmapViewOfFile and CloseHandle.