在 Windows Phone 7 中,如何在后台线程的 WriteableBitmap 上呈现文本? [英] How can I render text on a WriteableBitmap on a background thread, in Windows Phone 7?

查看:15
本文介绍了在 Windows Phone 7 中,如何在后台线程的 WriteableBitmap 上呈现文本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 Windows Phone 7 应用程序中的位图上呈现文本.

I am trying to render text on a bitmap in a Windows Phone 7 application.

看起来或多或少类似于以下的代码在主线程上运行时可以正常工作:

Code that looks more or less like the following would work fine when it's running on the main thread:

public ImageSource RenderText(string text, double x, double y)
{
    var canvas = new Canvas();

    var textBlock = new TextBlock { Text = text };
    canvas.Children.Add(textBloxk);
    Canvas.SetLeft(textBlock, x);
    Canvas.SetTop(textBlock, y);

    var bitmap = new WriteableBitmap(400, 400);
    bitmap.Render(canvas, null);
    bitmap.Invalidate();
    return bitmap;
}

现在,由于我必须用更复杂的东西渲染多个图像,我想在后台线程上渲染位图以避免用户界面无响应.

Now, since I have to render several images with more complex stuff, I would like to render the bitmap on a background thread to avoid an unresponsive UI.

当我使用 BackgroundWorker 这样做时,TextBlock 的构造函数抛出一个 UnauthorizedAccessException 声称这是一个无效的跨线程访问.

When I use a BackgroundWorker to do so, the constructor for TextBlock throws an UnauthorizedAccessException claiming that this is an invalid cross-thread access.

我的问题是:如何在不阻塞 UI 的情况下在位图上渲染文本?

My question is: how can I render text on a bitmap without blocking the UI?

  • 请不要建议使用网络服务进行渲染.我需要渲染大量图像,带宽成本无法满足我的需求,离线工作的能力是一个主要要求.
  • 解决方案不一定必须使用 WriteableBitmapUIElements,如果有另一种呈现文本的方式.
  • Please don't suggest using a web service to do the rendering. I need to render a large number of images and the bandwidth cost is not acceptable for my needs, and the ability to work offline is a major requirement.
  • The solution doesn't necessarily has to use WriteableBitmap or UIElements, if there is another way to render text.

编辑

另一个想法:有谁知道是否应该可以在另一个线程中运行 UI 消息循环,然后让该线程完成工作?(而不是使用 BackgroundWorker)?

Another thought: does anyone know if it should be possible to run a UI message loop in another thread, and then have that thread do the work? (instead of using a BackgroundWorker)?

编辑 2

考虑替代WriteableBitmap,我需要的功能是:

To consider alternatives to WriteableBitmap, the features I need are:

  • 绘制背景图像.
  • 在给定字体系列和大小(最好是样式)的情况下,测量 1 行字符串的宽度和高度.无需换行.
  • 在给定坐标处绘制一个具有给定字体系列、大小、样式的 1 行字符串.
  • 文本渲染应支持透明背景.IE.您应该会看到字符之间的背景图像.

推荐答案

此方法从预制图像中复制字母而不是使用 TextBlock,它基于我对此的回答 问题.主要限制是需要为所需的每种字体和大小使用不同的图像.一个大小为 20 的字体需要大约 150kb.

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.

使用 SpriteFont2 以您需要的大小导出字体和 xml 度量文件.代码假定它们被命名为FontName FontSize".png 和FontName FontSize".xml 将它们添加到您的项目并将构建操作设置为内容.该代码还需要 WriteableBitmapEx.

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

您需要调用 RegisterFont 一次以加载文件,然后调用 DrawString.它使用 WriteableBitmapEx.Blit 所以如果你的字体文件有白色文本并且透明背景 alpha 被正确处理,你可以重新着色它.如果您以未加载的大小绘制但结果不佳,则代码会缩放文本,可以使用更好的插值方法.

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.

我尝试从不同的线程绘制,这在模拟器中有效,您仍然需要在主线程上创建 WriteableBitmap.我对您的方案的理解是,您希望滚动浏览类似于地图应用程序工作方式的图块,如果是这种情况,请重用旧的 WriteableBitmaps 而不是重新创建它们.如果不是,可以将代码更改为使用数组.

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.

这篇关于在 Windows Phone 7 中,如何在后台线程的 WriteableBitmap 上呈现文本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆