How can I render text on a WriteableBitmap on a background thread, in Windows Phone 7? How can I render text on a WriteableBitmap on a background thread, in Windows Phone 7? multithreading multithreading

How can I render text on a WriteableBitmap on a background thread, in Windows Phone 7?


This method copies the letters from an pre-made image instead of using TextBlock, it's based on my answer to this question. The main limitation is requiring a different image for each font and size needed. A size 20 Font needed about 150kb.

Using SpriteFont2 export the font and the xml metrics file in the sizes you require. The code assumes they're named "FontName FontSize".png and "FontName FontSize".xml add them to your project and set the build action to content. The code also requires WriteableBitmapEx.

public static class BitmapFont{    private class FontInfo    {        public FontInfo(WriteableBitmap image, Dictionary<char, Rect> metrics, int size)        {            this.Image = image;            this.Metrics = metrics;            this.Size = size;        }        public WriteableBitmap Image { get; private set; }        public Dictionary<char, Rect> Metrics { get; private set; }        public int Size { get; private set; }    }    private static Dictionary<string, List<FontInfo>> fonts = new Dictionary<string, List<FontInfo>>();    public static void RegisterFont(string name,params int[] sizes)    {        foreach (var size in sizes)        {            string fontFile = name + " " + size + ".png";            string fontMetricsFile = name + " " + size + ".xml";            BitmapImage image = new BitmapImage();            image.SetSource(App.GetResourceStream(new Uri(fontFile, UriKind.Relative)).Stream);            var metrics = XDocument.Load(fontMetricsFile);            var dict = (from c in metrics.Root.Elements()                        let key = (char) ((int) c.Attribute("key"))                        let rect = new Rect((int) c.Element("x"), (int) c.Element("y"), (int) c.Element("width"), (int) c.Element("height"))                        select new {Char = key, Metrics = rect}).ToDictionary(x => x.Char, x => x.Metrics);            var fontInfo = new FontInfo(new WriteableBitmap(image), dict, size);            if(fonts.ContainsKey(name))                fonts[name].Add(fontInfo);            else                fonts.Add(name, new List<FontInfo> {fontInfo});        }    }    private static FontInfo GetNearestFont(string fontName,int size)    {        return fonts[fontName].OrderBy(x => Math.Abs(x.Size - size)).First();    }    public static Size MeasureString(string text,string fontName,int size)    {        var font = GetNearestFont(fontName, size);        double scale = (double) size / font.Size;        var letters = text.Select(x => font.Metrics[x]).ToArray();        return new Size(letters.Sum(x => x.Width * scale),letters.Max(x => x.Height * scale));    }    public static void DrawString(this WriteableBitmap bmp,string text,int x,int y, string fontName,int size,Color color)    {        var font = GetNearestFont(fontName, size);        var letters = text.Select(f => font.Metrics[f]).ToArray();        double scale = (double)size / font.Size;        double destX = x;        foreach (var letter in letters)        {            var destRect = new Rect(destX,y,letter.Width * scale,letter.Height * scale);            bmp.Blit(destRect, font.Image, letter, color, WriteableBitmapExtensions.BlendMode.Alpha);            destX += destRect.Width;        }    }}

You need to call RegisterFont once to load the files then you call DrawString. It uses WriteableBitmapEx.Blit so if your font file has white text and a transparent background alpha is handled correctly and you can recolour it. The code does scale the text if you draw at a size you didn't load but the results aren't good, a better interpolation method could be used.

I tried drawing from a different thread and this worked in the emulator, you still need to create the WriteableBitmap on the main thread. My understanding of your scenario is that you want to scroll through tiles similar to how mapping apps work, if this is the case reuse the old WriteableBitmaps instead of recreating them. If not the code could be changed to work with arrays instead.


I'm not sure if this will fully resolve your issues, but there are 2 tools that I use in my comic book reader (I won't shamelessly plug it here, but I'm tempted.. a hint if you are searching for it.. it is "Amazing"). There are times where I need to stitch together a bunch of images. I use Rene Schulte's (and a bunch of other contributors) WriteableBitmapExtensions (http://writeablebitmapex.codeplex.com/). I have been able to offload rendering/stitching of an image to a background thread and then set the resulting WriteableBitmap as the source of some image on the UI thread.

Another up and comer in this space is the .NET Image Tools (http://imagetools.codeplex.com/). They have a bunch of utilities for saving/reading various image formats. They also have a few of the low levels, and I wish there were an easy way to use both (but there isn't).

All of the above work in WP7.

I guess the major difference is with these tools you won't be using XAML you will be writing directly to your image (so you may need to do size detection of your text and stuff like that).


The very nature of UI elements requires interaction with them on the UI thread. Even if you could create them on a background thread, when you came to try to render them into the WriteableBitmap you'd get a similar exception, and even then if it allowed you to do that, the elements wouldn't actually have a visual representation until they were added into the visual tree. You might need to use a generic image manipulation library instead of using UI elements.

Perhaps you could describe your scenario on a wider basis, we might have a better solution for you :)