In one of my previous posts, WPF vs. GDI+ I wrote about the performance of WPF and how to solve this problem. After some experiments in Kvinivel we’ve found more improvements to the solution described in the previous post. The main idea is that when you are converting GDI bitmap to WPF bitmap it requires memory allocation and decreases performance. And fortunately, there is a solution that allows mapping 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 descriptions in MSDN, but names of the functions are more than descriptive if you know Win API of course 😉

1[DllImport("kernel32.dll", SetLastError = true)]
2static extern IntPtr CreateFileMapping(
3IntPtr hFile,
4IntPtr lpFileMappingAttributes,
5uint flProtect,
6uint dwMaximumSizeHigh,
7uint dwMaximumSizeLow,
8string lpName);
9
10[DllImport("kernel32.dll", SetLastError = true)]
11static extern IntPtr MapViewOfFile(
12IntPtr hFileMappingObject,
13uint dwDesiredAccess,
14uint dwFileOffsetHigh,
15uint dwFileOffsetLow,
16uint dwNumberOfBytesToMap);
17
18[DllImport("kernel32.dll", SetLastError = true)]
19static extern bool UnmapViewOfFile(IntPtr hFileMappingObject);
20
21[DllImport("kernel32.dll", SetLastError = true)]
22static extern bool CloseHandle(IntPtr handle);

Then before creating a bitmap, let’s create a memory mapped file as a source of bitmaps:

1var format = PixelFormats.Bgr32;
2
3var pixelCount = (uint)(width * height * format.BitsPerPixel / 8);
4var rowWidth = width * (format.BitsPerPixel / 8);
5
6this.fileMapping = CreateFileMapping(
7 new IntPtr(-1),
8 IntPtr.Zero,
9 0x04,
10 0,
11 pixelCount,
12 null);
13
14this.mapView = MapViewOfFile(
15 fileMapping,
16 0xF001F,
17 0,
18 0,
19 pixelCount);

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

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

1this.bitmap = new System.Drawing.Bitmap(
2 width,
3 height,
4 rowWidth,
5 System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
6 this.mapView);
7
8this.image = (System.Windows.Interop.InteropBitmap)
9System.Windows.Interop.Imaging.CreateBitmapSourceFromMemorySection(
10 fileMapping,
11 width,
12 height,
13 format,
14 rowWidth,
15 0);

Now you can in OnRender you can use the following code:

1protected override void OnRender(DrawingContext dc)
2{
3 // Ensure that bitmap is initialized and has the correct size
4 // Recreate bitmap ONLY when size is changed
5 InitializeBitmap((int)this.width, (int)this.height);
6
7 //TODO: Put here your drawing code
8
9 // Invalidate and draw bitmap on WPF DrawingContext
10 this.image.Invalidate();
11 dc.DrawImage(
12 this.image,
13 new Rect(0, 0, this.bitmap.Width, this.bitmap.Height));
14}

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