Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
FontManager.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Threading;
6 using SharpFont;
7 
8 using SiliconStudio.Core.Diagnostics;
9 using SiliconStudio.Core.IO;
10 using SiliconStudio.Core.Mathematics;
11 using SiliconStudio.Core.Serialization.Assets;
12 
13 namespace SiliconStudio.Paradox.Graphics.Font
14 {
15  /// <summary>
16  /// A font manager is in charge of loading in memory the ttf files, looking for font informations, rendering and then caching the <see cref="CharacterBitmap"/>s on the CPU .
17  /// </summary>
18  internal class FontManager : IDisposable
19  {
20  /// <summary>
21  /// Lock both <see cref="generatedBitmaps"/> and <see cref="bitmapsToGenerate"/>.
22  /// </summary>
23  private readonly object dataStructuresLock = new object();
24 
25  /// <summary>
26  /// The font data that are currently cached in the registry
27  /// </summary>
28  private readonly Dictionary<string, Face> cachedFontFaces = new Dictionary<string, Face>();
29 
30  /// <summary>
31  /// The list of the bitmaps that have already been generated.
32  /// </summary>
33  private readonly List<CharacterSpecification> generatedBitmaps = new List<CharacterSpecification>();
34 
35  /// <summary>
36  /// The list of the bitmaps that are in generation or to generate
37  /// </summary>
38  private readonly Queue<CharacterSpecification> bitmapsToGenerate = new Queue<CharacterSpecification>();
39 
40  /// <summary>
41  /// The <see cref="AutoResetEvent"/> used to signal the bitmap build thread that a build operation is requested.
42  /// </summary>
43  private readonly AutoResetEvent bitmapBuildSignal = new AutoResetEvent(false);
44 
45 #if !SILICONSTUDIO_PLATFORM_WINDOWS_RUNTIME
46  /// <summary>
47  /// The thread in charge of building the characters bitmaps
48  /// </summary>
49  private readonly Thread bitmapBuilderThread;
50 #endif
51 
52  /// <summary>
53  /// Boolean specifying if we need to quit the bitmap building thread.
54  /// </summary>
55  private bool bitmapShouldEndThread;
56 
57  /// <summary>
58  /// A reference pointer to the freetype library.
59  /// </summary>
60  private Library freetypeLibrary;
61 
62  /// <summary>
63  /// The asset manager used to load the ttf fonts.
64  /// </summary>
65  private readonly AssetManager assetManager;
66 
67  /// <summary>
68  /// The size of the transparent border to add around the character bitmap.
69  /// <remarks>
70  /// If we don't do so artifacts appears around the character when scaling fonts
71  /// Note that we cannot just increase space taken in the bin packer because artifacts with old/previous characters may happen.
72  /// </remarks>
73  /// </summary>
74  private Int2 borderSize = Int2.One;
75 
76  /// <summary>
77  /// Create an empty register.
78  /// </summary>
79  public FontManager()
80  {
81  assetManager = new AssetManager();
82 
83  // Preload proper freetype native library (depending on CPU type)
84  Core.NativeLibrary.PreloadLibrary("freetype.dll");
85 
86  // create a freetype library used to generate the bitmaps
87  freetypeLibrary = new Library();
88 
89 #if SILICONSTUDIO_PLATFORM_WINDOWS_RUNTIME
90  Windows.System.Threading.ThreadPool.RunAsync(operation => SafeAction.Wrap(BuildBitmapThread)());
91 #else
92  // launch the thumbnail builder thread
93  bitmapBuilderThread = new Thread(SafeAction.Wrap(BuildBitmapThread)) { IsBackground = true, Name = "Bitmap Builder thread" };
94  bitmapBuilderThread.Start();
95 #endif
96  }
97 
98  /// <summary>
99  /// Start the generation of the specified character's bitmap.
100  /// </summary>
101  /// <remarks>Does nothing if the bitmap already exist or if the generation is currently running.</remarks>
102  /// <param name="characterSpecification">The character we want the bitmap of</param>
103  /// <param name="synchronously">Indicate if the generation of the bitmap must by done synchronously or asynchronously</param>
104  public void GenerateBitmap(CharacterSpecification characterSpecification, bool synchronously)
105  {
106  // generate the glyph info (and the bitmap if required) synchronously
107  GenerateCharacterGlyph(characterSpecification, synchronously);
108 
109  // add the bitmap rendering job to a request queue if rendering is asynchronous
110  if (!synchronously)
111  {
112  lock (dataStructuresLock)
113  {
114  if (characterSpecification.Bitmap == null && !bitmapsToGenerate.Contains(characterSpecification))
115  {
116  bitmapsToGenerate.Enqueue(characterSpecification);
117  bitmapBuildSignal.Set();
118  }
119  }
120  }
121  }
122 
123  private void GenerateCharacterGlyph(CharacterSpecification character, bool renderBitmap)
124  {
125  // first the possible current glyph info
126  ResetGlyph(character);
127 
128  // let the glyph info null if the size is not valid
129  if (character.Size.X < 1 || character.Size.Y < 1)
130  return;
131 
132  // get the face of the font
133  var fontFace = GetOrCreateFontFace(character.FontName, character.Style);
134  lock (freetypeLibrary)
135  {
136  // set the font size
137  SetFontFaceSize(fontFace, character.Size);
138 
139  // get the glyph and render the bitmap
140  var glyphIndex = fontFace.GetCharIndex(character.Character);
141 
142  // the character does not exit => let the glyph info null
143  if (glyphIndex == 0)
144  return;
145 
146  // load the character glyph
147  fontFace.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
148 
149  // set glyph information
150  character.Glyph.XAdvance = fontFace.Glyph.Advance.X / 64f;
151 
152  // render the bitmap
153  if (renderBitmap)
154  RenderBitmap(character, fontFace);
155  }
156  }
157 
158  private void RenderBitmap(CharacterSpecification character, Face fontFace)
159  {
160  // choose the rendering type and render the glyph
161  var renderingMode = character.AntiAlias == FontAntiAliasMode.Aliased ? RenderMode.Mono : RenderMode.Normal;
162  fontFace.Glyph.RenderGlyph(renderingMode);
163 
164  // create the bitmap
165  var bitmap = fontFace.Glyph.Bitmap;
166  if (bitmap.Width != 0 && bitmap.Rows != 0)
167  character.Bitmap = new CharacterBitmap(bitmap.Buffer, ref borderSize, bitmap.Width, bitmap.Rows, bitmap.Pitch, bitmap.GrayLevels, bitmap.PixelMode);
168  else
169  character.Bitmap = new CharacterBitmap();
170 
171  // set the glyph offsets
172  character.Glyph.Offset = new Vector2(fontFace.Glyph.BitmapLeft - borderSize.X, -fontFace.Glyph.BitmapTop - borderSize.Y);
173  }
174 
175  private static void ResetGlyph(CharacterSpecification character)
176  {
177  character.Glyph.Offset = Vector2.Zero;
178  character.Glyph.XAdvance = 0;
179  character.Glyph.BitmapIndex = 0;
180  character.Glyph.Subrect.X = 0;
181  character.Glyph.Subrect.Y = 0;
182  character.Glyph.Subrect.Width = 0;
183  character.Glyph.Subrect.Height = 0;
184  }
185 
186  private void SetFontFaceSize(Face fontFace, Vector2 size)
187  {
188  // calculate and set the size of the font
189  // size is in 26.6 factional points (that is in 1/64th of points)
190  // 72 => the sizes are in "points" (=1/72 inch), setting resolution to 72 dpi let us specify the size in pixels directly
191  fontFace.SetCharSize((int)Math.Ceiling(size.X * 64), (int)Math.Ceiling(size.Y * 64), 72, 72);
192  }
193 
194  /// <summary>
195  /// Get various information about a font of a given family, type, and size.
196  /// </summary>
197  /// <param name="fontFamily">The name of the family of the font</param>
198  /// <param name="fontStyle">The style of the font</param>
199  /// <param name="lineSpacing">The space between two lines for a font size of 1 pixel</param>
200  /// <param name="baseLine">The default base line for a font size of 1 pixel</param>
201  /// <param name="maxWidth">The width of the largest character for a font size of 1 pixel</param>
202  /// <param name="maxHeight">The height of the largest character for a font size of 1 pixel</param>
203  public void GetFontInfo(string fontFamily, FontStyle fontStyle, out float lineSpacing, out float baseLine, out float maxWidth, out float maxHeight)
204  {
205  // get the data of the font data
206  var fontData = GetOrCreateFontFace(fontFamily, fontStyle);
207 
208  lineSpacing = fontData.Height / (float)fontData.UnitsPerEM;
209  baseLine = (fontData.Height + fontData.Descender) / (float)fontData.UnitsPerEM;
210  maxWidth = (fontData.BBox.Right - fontData.BBox.Left) / (float)fontData.UnitsPerEM;
211  maxHeight = (fontData.BBox.Top - fontData.BBox.Bottom) / (float)fontData.UnitsPerEM;
212  }
213 
214  /// <summary>
215  /// Returns a boolean indicating if the specified font contains the provided character.
216  /// </summary>
217  /// <param name="fontStyle">The style in the font family</param>
218  /// <param name="character">The character to look for</param>
219  /// <param name="fontFamily">The family of the font</param>
220  /// <returns>boolean indicating if the font contains the character</returns>
221  public bool DoesFontContains(string fontFamily, FontStyle fontStyle, char character)
222  {
223  var fontFace = GetOrCreateFontFace(fontFamily, fontStyle);
224 
225  uint glyphIndex;
226  lock (fontFace)
227  glyphIndex = fontFace.GetCharIndex(character);
228 
229  // see if the index of the character is valid
230  return glyphIndex != 0;
231  }
232 
233  public void Dispose()
234  {
235  // terminate the build thread
236  bitmapShouldEndThread = true;
237  bitmapBuildSignal.Set();
238 #if !SILICONSTUDIO_PLATFORM_WINDOWS_RUNTIME
239  bitmapBuilderThread.Join();
240 #endif
241 
242  // free and clear the list of generated bitmaps
243  foreach (var character in generatedBitmaps)
244  {
245  if (character.Bitmap != null)
246  {
247  character.Bitmap.Dispose();
248  character.Bitmap = null;
249  }
250  }
251  generatedBitmaps.Clear();
252 
253  // free font faces
254  foreach (var font in cachedFontFaces.Values)
255  font.Dispose();
256  cachedFontFaces.Clear();
257 
258  // free freetype library
259  if (freetypeLibrary != null)
260  freetypeLibrary.Dispose();
261  freetypeLibrary = null;
262  }
263 
264  private Face GetOrCreateFontFace(string fontFamily, FontStyle fontStyle)
265  {
266  var fontPath = FontHelper.GetFontPath(fontFamily, fontStyle);
267 
268  // ensure that the font is load in memory
269  LoadFontInMemory(fontPath);
270 
271  return cachedFontFaces[fontPath];
272  }
273 
274  private void LoadFontInMemory(string fontPath)
275  {
276  // return if the font is already cached
277  if (cachedFontFaces.ContainsKey(fontPath))
278  return;
279 
280  // load the font from the data base
281  using (var fontStream = assetManager.OpenAsStream(fontPath, StreamFlags.None))
282  {
283  // create the font data from the stream
284  var newFontData = new byte[fontStream.Length];
285  fontStream.Read(newFontData, 0, newFontData.Length);
286 
287  lock (freetypeLibrary)
288  cachedFontFaces[fontPath] = freetypeLibrary.NewMemoryFace(newFontData, 0);
289  }
290  }
291 
292  /// <summary>
293  /// Thread function in charge of building the bitmap
294  /// </summary>
295  private void BuildBitmapThread()
296  {
297  while (!bitmapShouldEndThread)
298  {
299  bitmapBuildSignal.WaitOne();
300 
301  while (bitmapsToGenerate.Count > 0)
302  {
303  var character = bitmapsToGenerate.Peek();
304 
305  // let the glyph data null if the size is not valid
306  if (character.Size.X < 1 || character.Size.Y < 1)
307  goto DequeueRequest;
308 
309  // get the face of the font
310  var fontFace = GetOrCreateFontFace(character.FontName, character.Style);
311 
312  lock (freetypeLibrary)
313  {
314  // set the font to the correct size
315  SetFontFaceSize(fontFace, character.Size);
316 
317  // get the glyph and render the bitmap
318  var glyphIndex = fontFace.GetCharIndex(character.Character);
319 
320  // if the character does not exist in the face => continue
321  if(glyphIndex == 0)
322  goto DequeueRequest;
323 
324  // load the character glyph
325  fontFace.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
326 
327  // render the bitmap and set remaining info of the glyph
328  RenderBitmap(character, fontFace);
329  }
330 
331  DequeueRequest:
332 
333  // update the generated cached data
334  lock (dataStructuresLock)
335  bitmapsToGenerate.Dequeue();
336  }
337  }
338  }
339  }
340 }
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
Represents a two dimensional mathematical vector.
Definition: Vector2.cs:42
static ThreadStart Wrap(ThreadStart action, [CallerFilePath] string sourceFilePath="", [CallerMemberName] string memberName="", [CallerLineNumber] int sourceLineNumber=0)
Definition: SafeAction.cs:13
SharpDX.DirectWrite.Font Font
float Y
The Y component of the vector.
Definition: Vector2.cs:79
Represents a three dimensional mathematical vector.
Definition: Int2.cs:41
float X
The X component of the vector.
Definition: Vector2.cs:73
SiliconStudio.Paradox.Graphics.Font.FontStyle FontStyle
_In_ size_t _In_ size_t size
Definition: DirectXTexP.h:175