Back to articles

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.

It also could be interesting to You

How do we achieve the highest efficiency in software development in Kvinivel

As many of our clients already know, we are doing fixed-price projects. Starting from requirements preparation and finishing with delivery and initial support. We have a lot of success stories, and all our customers are…

Hello World in Visual Studio Code on Linux

This post adds a sample in an addition to my post about VS Code previous post Setup Install the latest mono as described at http://www.mono-project.com/docs/getting-started/install/linux/#debian-ubuntu-and-derivatives. Install Visual Studio Code from https://code.visualstudio.com Just unpack and start.…