Tag Archives: WPF

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 😉

[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);

Than before creating bitmap let’s create memory mapped file as 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 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:

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 following code:

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));
}

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.

WPF vs. GDI+

The problem

In one of our projects in Tesseris Pro we need to draw huge table of results of some physical experiment. The table can be 100×100 and can be 1000×1000. The application is usual Windows desktop application built with WPF. And as usual we’ve tried to use some 3rd party grid. And as you can imagine, we’ve got unacceptable performance even with visualization. The most problematic thing is that user needs to zoom-out the table to see every cell as single pixel in this case visualization gives us nothing.

Solution #1

One of the first idea was to create our own control derived from FrameworkElement or UIElement and implement drawing of the table inside overridden OnRender method. As we expected this should give us maximum performance. And keeping in mind that WPF is based on DirectX and we will get performance as in 3D games I’ve started implementation of proof of concept with following OnRender:

protected override void OnRender(DrawingContext dc)
{
    Debug.WriteLine("OnRender...");

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            int x = width * i;
            int y = height * j;

            // Draw cell rect
            dc.DrawRectangle(
                      Brushes.Green, 
                      pen, 
                      new Rect(x, y, width, height));

            // Draw some text in cell
            dc.DrawText(
                new FormattedText(string.Format("{0},{1}", i, j),
                    CultureInfo.InvariantCulture,
                    FlowDirection.LeftToRight,
                    typeface,
                    10,
                    Brushes.Black),
                new Point(x, y));
        }
    }

    Debug.WriteLine("OnRender finish");
}

But performance was still unacceptable even for table of 100×100 cells. UI was refreshed in about 10 seconds. And when I measured time elapsed by OnRender I have got strange result 800ms. UI stuck for 10 seconds but OnRender takes only 800ms. I’ve got this results because WPF never draw everything immediately inside OnRender. With dc.Draw*** you just told infrastructure to draw something. Than WPF draws all required things in some other moment. So real drawing of 100×100 table requires about 10 seconds.

Solution #2

After fail with first solution I’ve tried to get DirectDraw surface and draw everything by myself. And it is not so easy with WPF. I have not found any built functionality for this. In blogs I found that the only way to use DirectDraw is to call it through COM interop. Some nightmare for as for me!
After that I’ve decided to check GDI+ (System.Drawing namespace) and try to draw the table with System.Drawing.Bitmap and than just draw bitmap with WPF:

protected override void OnRender(DrawingContext dc)
{
    Debug.WriteLine("OnRender...");
    using (var bmp = new System.Drawing.Bitmap(
                           columns * width, 
                           rows * height))
    {
        using (var g = System.Drawing.Graphics.FromImage(bmp))
        {
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    int x = width * i;
                    int y = height * j;

                    g.FillRectangle(
                                 System.Drawing.Brushes.Green, 
                                 x, 
                                 y, 
                                 width, 
                                 height);

                    g.DrawRectangle(
                                System.Drawing.Pens.DarkGray, 
                                x, 
                                y, 
                                width, 
                                height);

                    g.DrawString(
                                string.Format("{0},{1}", i, j), 
                                font, 
                                System.Drawing.Brushes.Black, 
                                new System.Drawing.PointF(x, y));
                }
            }
        }

        // Create Image from bitmap and draw it
        var options = BitmapSizeOptions.FromEmptyOptions();
        var img = Imaging.CreateBitmapSourceFromHBitmap(
                                    bmp.GetHbitmap(), 
                                    IntPtr.Zero, 
                                    Int32Rect.Empty, 
                                    options);

        dc.DrawImage(img, new Rect(0, 0, bmp.Width, bmp.Height));
    }
    Debug.WriteLine("OnRender finish");
}

When I started the app I decided that something is going wrong. UI was refreshed in less then one second. More that 10 times faster that with WPF drawing!

Conclusion

WPF gives us complex layout and device independence and other sweet things. But old GDI+ sometimes give much more – performance and simplicity!