WPF vs. GDI+ Some additional notes
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 😉
[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);
Then before creating a bitmap, let’s create a memory mapped file as a source of bitmaps:
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);
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:
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);
Now you can in OnRender you can use the following code:
protected override void OnRender(DrawingContext dc) { // Ensure that bitmap is initialized and has the 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)); }
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
.