C# 在Windows Phone 7中,如何在背景线程上的可写位图上渲染文本?
我正在尝试在WindowsPhone7应用程序中渲染位图上的文本 在主线程上运行时,看起来或多或少类似于以下内容的代码可以正常工作:C# 在Windows Phone 7中,如何在背景线程上的可写位图上渲染文本?,c#,multithreading,silverlight,windows-phone-7,writeablebitmap,C#,Multithreading,Silverlight,Windows Phone 7,Writeablebitmap,我正在尝试在WindowsPhone7应用程序中渲染位图上的文本 在主线程上运行时,看起来或多或少类似于以下内容的代码可以正常工作: public ImageSource RenderText(string text, double x, double y) { var canvas = new Canvas(); var textBlock = new TextBlock { Text = text }; canvas.Children.Add(textBloxk);
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;
}
现在,由于我必须使用更复杂的内容渲染多个图像,因此我希望在后台线程上渲染位图,以避免出现无响应的UI
当我使用BackgroundWorker
执行此操作时,TextBlock
的构造函数抛出一个UnauthorizedAccessException
声明这是一个无效的跨线程访问
我的问题是:如何在不阻塞UI的情况下在位图上呈现文本
- 请不要建议使用web服务进行渲染。我需要渲染大量图像,带宽成本不能满足我的需要,离线工作能力是一项主要要求
- 如果有其他方式呈现文本,则解决方案不一定要使用
或WriteableBitmap
UIElements
后台工作人员
)
编辑2
考虑“代码> > Wraveabl位图< /代码>的替代品,我需要的特性是:
- 绘制背景图像
- 在给定字体系列和大小(最好是样式)的情况下,测量一行字符串的宽度和高度。不需要字包装
- 在给定坐标处绘制具有给定字体系列、大小和样式的单线字符串
- 文本呈现应支持透明背景。也就是说,你应该看到字符之间的背景图像
画布如何
我需要渲染大量图像
我有一种感觉,这将扼杀手机的性能。通常,对于位图维护,最好的方法是使用XNA。XNA框架的某些部分在Silverlight项目中做得非常好。(顺便说一句,Silverlight和XNA将在同一项目中共存)
我会退一步想想这个功能。像这样发展一个星期,然后以不可接受的表现结束,会让我成为一只悲伤的熊猫
编辑
据我所知,你需要一些弹出式的图像作为背景和信息
使用TextBlock制作画布,但将其隐藏
<Canvas x:Name="userInfoCanvas" Height="200" Width="200" Visibility="Collapsed">
<Image x:Name="backgroundImage"> </Image>
<TextBlock x:Name="messageTextBlock" Canvas.ZIndex="3> </TextBlock> <!--ZIndex set the order of elements -->
</Canvas>
很明显,这里有一个更新问题。UIelements只能从UI线程进行更新,因此更新必须使用调度程序进行排队
Dispatcher.BeginInvoke(DispatcherPriority.Background, messageUpdate); //messageUpdate is an Action or anthing that can be infered to Delegate
PS.没有编译,这是更多的伪代码。UI元素的本质要求在UI线程上与它们交互。即使您可以在后台线程上创建它们,当您尝试将它们渲染到WriteableBitmap中时,您也会得到类似的异常,即使它允许您这样做,元素在添加到可视化树中之前实际上不会有可视化表示。您可能需要使用通用图像处理库,而不是使用UI元素
也许您可以更广泛地描述您的场景,我们可能会为您提供更好的解决方案:)我同意Derek的回答:您试图在没有UI的情况下使用UI控件
如果要渲染位图,则需要使用用于在位图上绘制文本的类
我认为WindowsPhone7具有.NETCompact框架
psudeo代码:
public Bitmap RenderText(string text, double x, double y)
{
Bitmap bitmap = new Bitmap(400, 400);
using (Graphics g = new Graphics(bitmap))
{
using (Font font = SystemFonts....)
{
using (Brush brush = new SolidColorBrush(...))
{
g.DrawString(text, font, brush, new Point(x, y));
}
}
}
return bitmap;
}
我不确定这是否能完全解决您的问题,但我在我的漫画书阅读器中使用了两种工具(我不会无耻地把它插在这里,但我很受诱惑……如果您正在搜索它,我会给您一个提示……这是“惊人的”)。有时我需要将一组图像缝合在一起。我使用Rene Schulte(和其他一些贡献者)的WriteableBitmapExtensions()。我已经能够将图像的渲染/缝合卸载到背景线程,然后将生成的WriteableBitmap设置为UI线程上某些图像的源
这个领域的另一个后起之秀是.NET映像工具()。它们有一系列用于保存/读取各种图像格式的实用程序。它们也有一些低级别,我希望有一个简单的方法来使用它们(但现在没有)
以上所有工作都在WP7中完成
我想主要的区别在于,使用这些工具,您将不会使用XAML,而是直接向图像中写入内容(因此您可能需要对文本等进行大小检测)。此方法从预先制作的图像中复制字母,而不是使用TextBlock,它基于我对此的回答。主要限制是需要为所需的每种字体和大小使用不同的图像。大小为20的字体需要大约150kb
使用导出所需大小的字体和xml度量文件。代码假定它们的名称为“FontName FontSize”.png和“FontName FontSize”.xml。将它们添加到项目中,并将构建操作设置为content。该准则还要求:
公共静态类位图字体
{
私有类FontInfo
{
public FontInfo(WriteableBitmap图像、字典度量、整数大小)
{
这个。图像=图像;
这个。度量=度量;
这个。大小=大小;
}
公共可写位图映像{get;private set;}
公共字典度量{get;private set;}
公共整数大小{get;私有集;}
}
私有静态字典字体=新建字典();
公共静态无效注册表项(字符串名称,参数int[]大小)
{
foreach(变量大小,以大小为单位)
{
字符串fontFile=name+“”+size+“.png”;
字符串fontMetricsFile=name+“”+size+“.xml”;
public Bitmap RenderText(string text, double x, double y)
{
Bitmap bitmap = new Bitmap(400, 400);
using (Graphics g = new Graphics(bitmap))
{
using (Font font = SystemFonts....)
{
using (Brush brush = new SolidColorBrush(...))
{
g.DrawString(text, font, brush, new Point(x, y));
}
}
}
return bitmap;
}
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;
}
}
}