如何解决在Windows Phone 7中,如何在后台线程的WriteableBitmap上呈现文本?
| 我正在尝试在Windows Phone 7应用程序中的位图上呈现文本。 在主线程上运行时,看起来或类似以下内容的代码可以正常工作: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的情况下在位图上呈现文本?
请不要建议使用网络服务进行渲染。我需要渲染大量图像,带宽成本无法满足我的需求,并且脱机工作能力是主要要求。
如果存在另一种呈现文本的方法,则该解决方案不一定必须使用WriteableBitmap
或UIElements
。
编辑
另一个想法:是否有人知道是否应该可以在另一个线程中运行UI消息循环,然后让该线程来工作? (而不是使用BackgroundWorker
)?
编辑2
为了考虑替代“ 4”,我需要的功能是:
绘制背景图像。
在给定字体和大小(最好是样式)的情况下,测量1行字符串的宽度和高度。无需自动换行。
在给定的坐标处绘制具有给定的字体系列,大小,样式的1行字符串。
文字渲染应支持透明背景。即您应该看到字符之间的背景图像。
解决方法
这种方法是从预制图像中复制字母,而不是使用TextBlock,它基于我对此问题的回答。主要的限制是要求每种字体和大小都需要不同的图像。大小为20的字体大约需要150kb。
使用SpriteFont2以所需的大小导出字体和xml度量文件。该代码假定它们的名称为\“ FontName FontSize \”。png和\“ FontName FontSize \”。xml将它们添加到您的项目中,并将构建操作设置为content。该代码还需要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,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,则可以为其重新着色。如果您以未加载的大小进行绘制,但是代码确实缩放了文本,但是结果不好,可以使用更好的插值方法。
我尝试从其他线程进行绘制,并且在仿真器中可以正常工作,您仍然需要在主线程上创建WriteableBitmap。我对您的方案的理解是,您希望像浏览贴图应用程序一样滚动浏览图块,如果是这种情况,请重用旧的WriteableBitmaps而不是重新创建它们。如果不是这样,则可以将代码更改为与数组一起使用。
,我不确定这是否可以完全解决您的问题,但是我在漫画阅读器中使用了2种工具(我不会毫不客气地将其插入此处,但是我很想..如果您有提示,正在搜索它。它是\“ Amazing \”)。有时我需要将一堆图像拼接在一起。我使用Rene Schulte(和其他一些贡献者)的WriteableBitmapExtensions(http://writeablebitmapex.codeplex.com/)。我已经能够将图像的渲染/拼接卸载到后台线程,然后将生成的WriteableBitmap设置为UI线程上某些图像的源。
.NET映像工具(http://imagetools.codeplex.com/)是此领域中另一个崭露头角的地方。他们有一堆实用程序,用于保存/读取各种图像格式。他们也有一些较低的水平,我希望有一种简单的方法可以同时使用这两种方法(但没有)。
以上所有工作均在WP7中进行。
我猜主要的区别是这些工具不会使用XAML,而是直接写到图像(因此,您可能需要对文本和类似内容进行大小检测)。
,UI元素的本质要求在UI线程上与它们进行交互。即使您可以在后台线程上创建它们,当您尝试将它们呈现到WriteableBitmap中时,您也会遇到类似的异常,即使这样它也允许您执行此操作,元素实际上并没有视觉表示,直到将它们添加到视觉树中。您可能需要使用通用的图像处理库,而不是使用UI元素。
也许您可以更广泛地描述您的情况,我们可能会为您提供更好的解决方案:)
,首先,您确定要将其渲染为位图吗?如何生成带有图像的Canvas
和TextBlock
?
我需要渲染大量图像
我觉得这种生成会破坏手机性能。通常,对于位图维护,最好的方法是使用XNA。 XNA框架的某些部分在Silverlight项目方面做得很好。 (顺便说一句,更新后的Windows Phone开发人员工具将允许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>
当您收到新消息时,请在后台线程上完成渲染时向用户显示“画布”(不透明动画会很好)。
messageTextBlock.Text = message;
backgroundImage.Source = new BitmapImage(renderedImage);
显然,这是更新问题。只能从UI线程更新UIelements,因此更新必须与Dispatcher一起排队
Dispatcher.BeginInvoke(DispatcherPriority.Background,messageUpdate); //messageUpdate is an Action or anthing that can be infered to Delegate
PS。没有编译,这是更多的伪代码。
,您可以在线程中绘制WriteableBitmap,但是您必须
在主UI线程中创建WriteableBitmap
在后台线程中绘制工作
在主UI线程中分配BitmapSource
,我会同意Derek的回答:您正在尝试使用没有UI的UI控件。
如果要渲染位图,则需要坚持使用类在位图上绘制文本。
我认为Windows Phone 7具有.NET Compact Framework。
伪代码:
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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。