在后台线程中将字体渲染并加载到FontCache中? [英] Rendering and loading fonts into the FontCache on a background thread?

查看:237
本文介绍了在后台线程中将字体渲染并加载到FontCache中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图展示一个类似于Blend的字体选择器列表:

Blend字体选择器http://img691.imageshack.us/img691/60/blendfontpicker.png



看来,我付出的代价是花费的时间实际上为给定的FontSize渲染FontFamily并将其保存到FontCache中。一旦呈现的字体在缓存中,问题就消失了。

我已经尝试在后台线程上迭代Fonts.SystemFontFamilies集合,并调用一个调用UI的原因一个隐藏的TextBlock来更新(这应该会导致字体呈现)。

当然,因为调度调用发生连续,它只是磅的UI,我得到相同的直到所有的字体都已经被渲染并加载到FontCache中,才会阻塞用户界面。



有没有人可以很好地解决这个问题?他们似乎没有在Blend中找到解决办法,所以我认为没有一个好的解决方案。 解决方案

给你一些建议

想法1:完全在后台线程中获取字体。

字体实际上被加载到系统字体缓存(进程间)中,然后部分字体信息被复制到线程专有字体缓存中。填充系统字体缓存可能会导致速度提高。这可以通过一个低优先级的后台线程来完成,该线程开始运行你的应用程序启动的瞬间。因此,当用户下拉字体列表时,系统字体缓存应该完全填充。

想法2:自己缓存渲染的字体几何体



不使用TextBlocks,而是将内容绑定到PriorityBinding的ComboBox的DataTemplate中使用ContentPresenter对象。较低的优先级将使用默认字体生成一个TextBlock,而较高的优先级将是一个IsAsync绑定,它将创建一个带有适当参数的GlyphRun,在其上调用BuildGeometry(),并返回一个Path对象中的Geometry。创建的Geometry对象可以被缓存并且再次被返回,以便将来访问相同的字体。

这样做的结果是项目最初将以默认字体出现,只要字体可以加载并创建它们的几何形状,就可以将其渲染为样式字体。请注意,这可以与在单独线程中预填充缓存的代码结合使用。



Idea 2的代码如下所示:

 < ComboBox ItemsSource ={Binding MyFontObjects}> 
< ComboBox.ItemTemplate>
< ContentPresenter>
< ContentPresenter.Content>
< PriorityBinding>
< Binding IsAsync =TruePath =BuildStyledFontName/>
< Binding Path =BuildTextBlock/>
< / PriorityBinding>
...关闭所有标签...

其中,MyFontObjets是一个IEnumerable对象像这样:

  public class MyFontObject 
{
public FontFamily Font {get;组; }

public object BuildTextBlock
{
get {return new TextBlock {Text = GetFamilyName(Font)}}
}

public object BuildStyledFontName
{
get
{
return new Path {Data = GetStyledFontGeometryUsingCache()};


$ b $ private Geometry GetStyledFontGeometryUsingCache()

Geometry geo;
lock(_fontGeometryCache)
if(_fontGeometryCache.TryGetValue(Font,out geo)return geo;
$ b $ lock(_fontGeometryBuildLock)

lock(_fontGeometryCache )
if(_fontGeometryCache.TryGetValue(Font,out geo)return geo;

geo = BuildStyledFontGeometry();

lock(_fontGeometryCache)
_fontGeometryCache ();


静态对象b
私人几何BuildStyledFontGeometry()
{
var run = new GlyphRun
{
Characters = GetFamilyName(Font),
GlyphTypeface = GetGlyphTypeface(Font) ,
}
return run.BuildGeometry();
}

... GetFamilyName ...

... GetGlyphTypeface。 ..

//从应用程序启动时产生的低优先级后台线程调用
pub lc static void PrefillCache()
{
foreach(Fonts.SystemFontFamilies中的FontFamily字体)
MyFontObject {Font = font} .GetStyledFontGeometryUsingCache();




$ b $ p
$ b

请注意,缓存中的几何对象可以保存通过将其转换为PathGeometry,然后将其转换为PathGeometry迷你语言中的字符串。这将允许字体几何缓存使用单个文件读取&解析,所以唯一一次你会看到任何延迟是当你第一次运行应用程序,或者当你用大量的新字体运行它。


I am trying to show a Font picker list similar to the one in Blend:

Blend Font Picker http://img691.imageshack.us/img691/60/blendfontpicker.png

Like Blend, I am seeing performance issues when the FontFamilies are not loaded in the FontCache.

It seems that the penalty I am paying is the time it takes to actually render the FontFamily for the given FontSize and save it into the FontCache. Once the rendered font is in the cache the problem goes away.

I have tried iterating the Fonts.SystemFontFamilies collection on a background thread and dispatching a call to the UI which causes a hidden TextBlock to update (which should cause the font to render).

Of course, since the dispatch calls happen in succession it just pounds the UI and I get the same net result of a blocking UI until all the fonts have been rendered and loaded into the FontCache.

Does anybody have a good solution to this issue? They didn't seem to find a fix for it in Blend so I am thinking there isn't a good solution.

解决方案

A couple of ideas for you

Idea 1: Do the font fetching entirely on the background thread.

The fonts are actually loaded into the system font cache (inter-process) and then part of the font information is copied into the thread-specific font cache. It is possible that filling the system font cache would result in a good enough increase in speed. This could be done by a low priority background thread that starts running the instant your app is started. So by the time the user drops down the font list the system font cache should be fully populated.

Idea 2: Cache the rendered font geometry yourself

Instead of using TextBlocks, use ContentPresenter objects in your ComboBox's DataTemplate with the content bound to a PriorityBinding. The lower priority would produce a TextBlock using the default font, and the higher priority would be an IsAsync binding that would create a GlyphRun with the appropriate parameters, call BuildGeometry() on it, and return the Geometry inside a Path object. The created Geometry objects can be cached and returned again for future accesses to the same font.

The result of this will be that items will initially appear in the default font, and render into the styled font as soon as the fonts can be loaded and their geometry created. Note that this can be combined with code that prefills your cache in a separate thread.

The code for Idea 2 would look something like this:

<ComboBox ItemsSource="{Binding MyFontObjects}">
  <ComboBox.ItemTemplate>
    <ContentPresenter>
      <ContentPresenter.Content>
        <PriorityBinding>
          <Binding IsAsync="True" Path="BuildStyledFontName" />
          <Binding Path="BuildTextBlock" />
        </PriorityBinding>
        ... close all tags ...

Where MyFontObjets would be a IEnumerable of objects something like this:

public class MyFontObject
{
  public FontFamily Font { get; set; }

  public object BuildTextBlock
  {
    get { return new TextBlock { Text = GetFamilyName(Font) } }
  }

  public object BuildStyledFontName
  {
    get
    {
      return new Path { Data = GetStyledFontGeometryUsingCache() };
    }
  }

  private Geometry GetStyledFontGeometryUsingCache()
  {      
    Geometry geo;
    lock(_fontGeometryCache)
      if(_fontGeometryCache.TryGetValue(Font, out geo) return geo;

    lock(_fontGeometryBuildLock)
    {
      lock(_fontGeometryCache)
        if(_fontGeometryCache.TryGetValue(Font, out geo) return geo;

      geo = BuildStyledFontGeometry();

      lock(_fontGeometryCache)
        _fontGeometryCache[Font] = geo;
    }
  }
  static object _fontGeometryCache = new Dictionary<FontFamily, Geometry>();
  static object _fontGeometryBuildLock = new object();

  private Geometry BuildStyledFontGeometry()
  {
    var run = new GlyphRun
    {
      Characters = GetFamilyName(Font),
      GlyphTypeface = GetGlyphTypeface(Font),
    }
    return run.BuildGeometry();
  }

  ... GetFamilyName ...

  ... GetGlyphTypeface ...

  // Call from low priority background thread spawned at app startup
  publc static void PrefillCache()
  {
    foreach(FontFamily font in Fonts.SystemFontFamilies)
      new MyFontObject { Font = font }.GetStyledFontGeometryUsingCache();
  }
}

Note that the Geometry objects in the cache could be saved to disk by converting them to PathGeometry and thence to strings in the PathGeometry mini-language. This would allow the font geometry cache to be filled using a single file read & parse, so the only time you would see any delay was when you first ran the app, or when you ran it with a large number of new fonts.

这篇关于在后台线程中将字体渲染并加载到FontCache中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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