It’s been about two months since Windows 8 Consumer Preview is out and we, at Telerik, have been enjoying the new framework and all the features it offers. One of the aspects we've been exploring is the immediate mode rendering and what options the new runtime offers. XAML is in the core of WinRT (Windows Runtime) and it offers rich presentation capabilities but sometimes, especially on devices with limited hardware resources, we need to achieve even better performance to get up to the “fast and fluid” Metro applications' standard.

As I explained in this blog post, Direct2D is undoubtedly the best choice when it comes to presenting heavy visual scenes that are frequently updated. Another immediate-mode rendering option is the WriteableBitmap class where you can directly update pixels on the screen. One of the things that is surprisingly missing in WinRT is the ability to render a XAML UIElement on a WriteableBitmap instance. So, in case you need to use the WriteableBitmap class in your application, the only solution would be to have your own rendering engine that manipulates the pixels of the bitmap. Fortunately, the WriteableBitmapEx solution, by Rene Schulte, has already been ported to WinRT and it can serve your needs.

In this post I want to present yet another (a bit crazy) idea I have about outputting content within a WriteableBitmap instance. And that is – as the title states – integrating GDI+ within a C# Metro application and using it to present content.

 

Download the demo 

I am a big fan of Windows Forms and GDI+ since the initial release of .NET framework. Its only disadvantage is probably the fact that it is a software rendering engine which does not exploit Display Adapter’s capabilities. Luckily Native API interoperability is enabled in .NET for Metro Applications and we can call unmanaged methods through the DllImport attribute. So, the idea is to create managed wrappers over some GDI+ methods and use them to draw content in an off-screen bitmap, whose pixels will then be passed to a WriteableBitmap instance.

The wrappers

If you are familiar with Windows Forms and GDI+ then the wrappers in the demo application will look pretty much familiar – Brush, Pen, Font, Graphics, etc. Here is an example of how the wrapper of a Brush-object looks like:

public class SolidBrush : NativeObject
{
    private Color color;
 
    public SolidBrush(Color color)
    {
        this.color = color;
        IntPtr handle;
        int result = SafeNativeMethods.GdiPlus.GdipCreateSolidFill(this.color.ToArgb(), out handle);
        if (result != 0)
        {
            throw new InvalidOperationException();
        }
 
        this.NativePtr = handle;
    }
 
    public Color Color
    {
        get
        {
            return this.color;
        }
    }
 
    protected override void DestroyNativePtr()
    {
        base.DestroyNativePtr();
 
        SafeNativeMethods.GdiPlus.IntGdipDeleteBrush(this.NativePtr);
    }
}

Each GDI+ object is associated with a native pointer, which is unmanaged and we need to destroy it when the object is being disposed:

public abstract class NativeObject : IDisposable
{
    private IntPtr nativePtr;
 
    ~NativeObject()
    {
        this.Dispose(false);
    }
 
    protected IntPtr NativePtr
    {
        get
        {
            return this.nativePtr;
        }
        set
        {
            this.nativePtr = value;
        }
    }
 
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    protected void Dispose(bool disposing)
    {
        if (this.NativePtr != IntPtr.Zero)
        {
            try
            {
                this.DestroyNativePtr();
            }
            catch
            {
                throw;
            }
            finally
            {
                this.NativePtr = IntPtr.Zero;
            }
        }
    }
 
    protected virtual void DestroyNativePtr()
    {
    }
}

Drawing content

We will initialize a Graphics object from a Bitmap and will draw a string on it. First, let’s create the needed GDI resources:

private void InitializeGdiResources()
{
    this.gdiBitmap = new Bitmap(PixelWidth, PixelHeight);
    this.gdiGraphics = Graphics.FromBitmap(this.gdiBitmap);
    this.gdiFont = new Font("Segoe UI", 32, GdiPlusToMetro.FontStyle.Bold | GdiPlusToMetro.FontStyle.Italic);
    this.gdiBrush = new SolidBrush(Colors.White);
    this.gdiStringFormat = new StringFormat(StringFormatFlags.NoClip);
    this.gdiPen = new Pen(Colors.White, 1);
 
    this.writeableBitmap = new WriteableBitmap(PixelWidth, PixelHeight);
    this.img.Source = this.writeableBitmap;
}

Time for some pixels to be output on the screen:

private void RenderText()
{
    RectangleF rect = new RectangleF()
    {
        Width = PixelWidth,
        Height = PixelHeight
    };
 
    this.gdiGraphics.DrawString(DisplayText, this.gdiFont, this.gdiBrush, rect, this.gdiStringFormat);
}

Updating the WriteableBitmap’s pixels

Now that we have some content produced we need to update the pixels of our WriteableBitmap instance:

private void EndRender()
{
    byte[] pixels = this.gdiBitmap.GetPixels();
 
    Stream stream = this.writeableBitmap.PixelBuffer.AsStream();
    stream.Seek(0, SeekOrigin.Begin);
    stream.Write(pixels, 0, pixels.Length);
 
    this.writeableBitmap.Invalidate();
}

Points of interest

I also performed the same test as in the Direct2D blog post – rendering a polyline, consisting of 5000 points, within a 1024*768 rectangle. The results are pretty impressive:

 


FPS (frames-per-second)
GDI+ (antialias off)
 27
GDI+ (antialias on)
 3-4
XAML Polyline
 3

A software-rendering engine, that GDI+ is, produces the same results (sometimes even better) than a hardware-accelerated counterpart, which the XAML Polyline is. I definitely think that MS should revisit the Polyline implementation – I really can’t understand why it is that slow. 

Conclusion

I know that some (if not most) of you are wondering: “Why would I need GDI+ in my application when I have Direct2D”? Well, it is indeed up to you – this is just an experiment, which I (and hopefully you :)) find funny and interesting. With this experiment I learned that:

  • DllImport is supported in .NET for Metro Applications.
  • You cannot render XAML UI elements within a WriteableBitmap instance.
  • You can get the WriteableBitmap’s PixelBuffer as a stream and write some bytes within it.
  • Pixel format in WriteableBitmap is BGRA.
  • GDI+ is at least as fast as XAML regarding line(s) rendering.

UPDATE:
GDI+ is not in the list of supported graphics APIs for developing Metro Style applications, so this demo will fail the App Store verification tool. So, if you want to have your application published on the store, this approach is definitely not a solution :) Thanks to Pete_Brown and Morten Nielsen for pointing this out.

 

Well, that’s it, do not hesitate to grab the demo and give it a try. Please note that the set of wrapped GDI+ functionality is limited and its purpose is to fit the demo’s requirements. And have in mind that GDI+ will prevent you from publishing your app on the store.

Download the demo 


About the Author

Georgi Atanasov


Related Posts

Comments