How to draw text in WPF without WriteableBitmap constructor that takes UIElement parameter?

Feb 14, 2014 at 2:51 AM
The samples and other discussions show a 3rd constructor that takes 2 parameters (UIElement, Transform) and it seems this is the only way recommended to achieve writing text to the bitmap.

Most samples and referred examples are in Silverlight, but I'm restricted to WPF client app environment. MSDN seems to show the WriteableBitmap constructor that takes the UIElement input is only supported on Silverlight.

Is there another recommendation to draw text that will work in WPF??

Thanks in advance.
Coordinator
Feb 14, 2014 at 6:56 AM
Feb 14, 2014 at 6:37 PM
Edited Feb 14, 2014 at 7:07 PM
Thanks for the quick response. It works using this approach, then blitting with my base WriteableBitmap.

On performance, I'm seeing ~600uS to create each label and cache it in a WriteableBitmap object. For my use case of ~100K items (1 item = rectangle and label), the time to create each and cache for subsequent blitting would be >55s which is too much. Is this the most performant text approach that I should expect? Would the Silverlight UIElement conversion take about the same time? Maybe this is just my wake up call that drawing text is expensive.

As for the blitting of a cached text bitmap into the base, the performance is quite reasonable averaging 720nS per-blit; thus 72ms for my 100K case, although I plan to only conditionally add the text if determined it will be readable.

Benchmark system: Core i7 Q740, 1.73Ghz, 8GB RAM, W7 x64

//-----------------------------------

To summarize my approach for others, given the performance seen:
  1. Create a WriteableBitmap for the text (created on-demand and cached for future repeated use)
    a) DrawingVisual's DrawingContext.DrawText (<FormattedText>, new Point(0,0))
    b) Use the bounds of the FormattedText object as the 'sourceRect'
    c) Convert the DrawingVisual to a RenderTargetBitmap using sourceRect from (b)
    d) Create a WriteableBitmap object dimensioned the same size as sourceRect
            FormattedText text = new FormattedText(displayLabel,
                new System.Globalization.CultureInfo("en-us"),
                FlowDirection.LeftToRight,
                new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Normal, new FontStretch()),
                12,
                Brushes.Black);

            DrawingVisual drawingVisual = new DrawingVisual();
            DrawingContext drawingContext = drawingVisual.RenderOpen();
            drawingContext.DrawText(text, new Point(0, 0));
            drawingContext.Close();


            Rect sourceRect = new Rect(0, 0, (int)text.Width, (int)text.Height);

            RenderTargetBitmap rtb = new RenderTargetBitmap((int)sourceRect.Width, (int)sourceRect.Height, 96, 96, PixelFormats.Pbgra32);
            rtb.Render(drawingVisual);

            LabelBitmapCache = BitmapFactory.New((int)sourceRect.Width, (int)sourceRect.Height);
            rtb.CopyPixels(new Int32Rect(0, 0, rtb.PixelWidth, rtb.PixelHeight), 
                            LabelBitmapCache.BackBuffer, 
                            LabelBitmapCache.BackBufferStride * LabelBitmapCache.PixelHeight, 
                            LabelBitmapCache.BackBufferStride);
  1. Conditionally blit the text cached WriteableBitmap with my base (if determined it will be readable - not shown)
_bmp.Blit(new Rect(left, 5, i.LabelBitmapCache.PixelWidth, i.LabelBitmapCache.PixelHeight), i.LabelBitmapCache, 
                                  new Rect(0,0,i.LabelVisual.ContentBounds.Width, i.LabelVisual.ContentBounds.Height));
Feb 14, 2014 at 9:32 PM
Continued experiments with the RenderTargetBitmap lead down a rat-hole of a seemingly well know memory issue that leads to crashes. I've not found a way out.

Are there any other ways to draw text using this library in WPF?
Aug 20, 2014 at 9:20 PM
(After giving up then coming back) I found some references that provided 2 key points regarding the RenderTargetBitmap object to avoid the memory leak and subsequent crash:
  1. Freeze() the object after Rendering the Visual (RenderTargetBitmap.Freeze())
  2. Explicitly Clear (RenderTargetBitmap.Clear()) and null the object after blitting it
I seem to be back in business for cases that require text rendering.
Feb 20, 2015 at 6:52 PM
Hello... looking into this for my solution. Doesn't RenderTargetBitmap.Clear() clear out the actual bitmap? Documentation says it replaces everything with transparent black.