Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
TrueTypeImporter.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 //
4 // Copyright (c) 2010-2013 SharpDX - Alexandre Mutel
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 // THE SOFTWARE.
23 
24 using System;
25 using System.Collections.Generic;
26 using System.IO;
27 using SharpDX;
28 using SharpDX.Direct2D1;
29 using SharpDX.DirectWrite;
30 
32 
34 
35 namespace SiliconStudio.Paradox.Assets.SpriteFont.Compiler
36 {
37  using System.Drawing;
38  using System.Drawing.Imaging;
39 
40  // This code was originally taken from DirectXTk but rewritten with DirectWrite
41  // for more accuracy in font rendering
42  internal class TrueTypeImporter : IFontImporter
43  {
44  // Properties hold the imported font data.
45  public IEnumerable<Glyph> Glyphs { get; private set; }
46 
47  public float LineSpacing { get; private set; }
48 
49  public float BaseLine { get; private set; }
50 
51  public void Import(SpriteFontAsset options, List<char> characters)
52  {
53  var factory = new Factory();
54 
55  // try to get the font face from the source file if not null
56  FontFace fontFace = options.Source != null ? GetFontFaceFromSource(factory, options) : GetFontFaceFromSystemFonts(factory, options);
57 
58  var fontMetrics = fontFace.Metrics;
59 
60  // Create a bunch of GDI+ objects.
61  var fontSize = FontHelper.PointsToPixels(options.Size);
62 
63  var glyphList = new List<Glyph>();
64 
65  // Remap the LineMap coming from the font with a user defined remapping
66  // Note:
67  // We are remapping the lineMap to allow to shrink the LineGap and to reposition it at the top and/or bottom of the
68  // font instead of using only the top
69  // According to http://stackoverflow.com/questions/13939264/how-to-determine-baseline-position-using-directwrite#comment27947684_14061348
70  // (The response is from a MSFT employee), the BaseLine should be = LineGap + Ascent but this is not what
71  // we are experiencing when comparing with MSWord (LineGap + Ascent seems to offset too much.)
72  //
73  // So we are first applying a factor to the line gap:
74  // NewLineGap = LineGap * LineGapFactor
75  var lineGap = fontMetrics.LineGap * options.LineGapFactor;
76 
77  // Store the font height.
78  LineSpacing = (float)(lineGap + fontMetrics.Ascent + fontMetrics.Descent) / fontMetrics.DesignUnitsPerEm * fontSize;
79 
80  // And then the baseline is also changed in order to allow the linegap to be distributed between the top and the
81  // bottom of the font:
82  // BaseLine = NewLineGap * LineGapBaseLineFactor
83  BaseLine = (float)(lineGap * options.LineGapBaseLineFactor + fontMetrics.Ascent) / fontMetrics.DesignUnitsPerEm * fontSize;
84 
85  // Rasterize each character in turn.
86  foreach (var character in characters)
87  glyphList.Add(ImportGlyph(factory, fontFace, character, fontMetrics, fontSize, options.AntiAlias));
88 
89  Glyphs = glyphList;
90 
91  factory.Dispose();
92  }
93 
94  private FontFace GetFontFaceFromSource(Factory factory, SpriteFontAsset options)
95  {
96  if (!File.Exists(options.Source))
97  {
98  // Font does not exist
99  throw new FontNotFoundException(options.Source);
100  }
101 
102  using (var fontFile = new FontFile(factory, options.Source))
103  {
104  FontSimulations fontSimulations;
105  switch (options.Style)
106  {
107  case Paradox.Graphics.Font.FontStyle.Regular:
108  fontSimulations = FontSimulations.None;
109  break;
110  case Paradox.Graphics.Font.FontStyle.Bold:
111  fontSimulations = FontSimulations.Bold;
112  break;
113  case Paradox.Graphics.Font.FontStyle.Italic:
114  fontSimulations = FontSimulations.Oblique;
115  break;
116  default:
117  throw new ArgumentOutOfRangeException();
118  }
119 
120  Bool isSupported;
121  FontFileType fontType;
122  FontFaceType faceType;
123  int numberFaces;
124 
125  fontFile.Analyze(out isSupported, out fontType, out faceType, out numberFaces);
126 
127  return new FontFace(factory, faceType, new[] { fontFile }, 0, fontSimulations);
128  }
129  }
130 
131  private FontFace GetFontFaceFromSystemFonts(Factory factory, SpriteFontAsset options)
132  {
133  SharpDX.DirectWrite.Font font;
134  using (var fontCollection = factory.GetSystemFontCollection(false))
135  {
136  int index;
137  if (!fontCollection.FindFamilyName(options.FontName, out index))
138  {
139  // Lets try to import System.Drawing for old system bitmap fonts (like MS Sans Serif)
140  throw new FontNotFoundException(options.FontName);
141  }
142 
143  using (var fontFamily = fontCollection.GetFontFamily(index))
144  {
145  var weight = FontWeight.Regular;
146  var style = SharpDX.DirectWrite.FontStyle.Normal;
147  switch (options.Style)
148  {
149  case Paradox.Graphics.Font.FontStyle.Bold:
150  weight = FontWeight.Bold;
151  break;
152  case Paradox.Graphics.Font.FontStyle.Italic:
153  weight = FontWeight.Regular;
154  style = SharpDX.DirectWrite.FontStyle.Italic;
155  break;
156  case Paradox.Graphics.Font.FontStyle.Regular:
157  weight = FontWeight.Regular;
158  break;
159  }
160  font = fontFamily.GetFirstMatchingFont(weight, FontStretch.Normal, style);
161  }
162  }
163 
164  return new FontFace(font);
165  }
166 
167  private Glyph ImportGlyph(Factory factory, FontFace fontFace, char character, FontMetrics fontMetrics, float fontSize, FontAntiAliasMode antiAliasMode)
168  {
169  var indices = fontFace.GetGlyphIndices(new int[] { character });
170 
171  var metrics = fontFace.GetDesignGlyphMetrics(indices, false);
172  var metric = metrics[0];
173 
174  var width = (float)(metric.AdvanceWidth - metric.LeftSideBearing - metric.RightSideBearing) / fontMetrics.DesignUnitsPerEm * fontSize;
175  var height = (float)(metric.AdvanceHeight - metric.TopSideBearing - metric.BottomSideBearing) / fontMetrics.DesignUnitsPerEm * fontSize;
176 
177  var xOffset = (float)metric.LeftSideBearing / fontMetrics.DesignUnitsPerEm * fontSize;
178  var yOffset = (float)(metric.TopSideBearing - metric.VerticalOriginY) / fontMetrics.DesignUnitsPerEm * fontSize;
179 
180  var advanceWidth = (float)metric.AdvanceWidth / fontMetrics.DesignUnitsPerEm * fontSize;
181  //var advanceHeight = (float)metric.AdvanceHeight / fontMetrics.DesignUnitsPerEm * fontSize;
182 
183  var pixelWidth = (int)Math.Ceiling(width + 4);
184  var pixelHeight = (int)Math.Ceiling(height + 4);
185 
186  var matrix = Matrix.Identity;
187  matrix.M41 = -(float)Math.Floor(xOffset) + 1;
188  matrix.M42 = -(float)Math.Floor(yOffset) + 1;
189 
190  Bitmap bitmap;
191  if (char.IsWhiteSpace(character))
192  {
193  bitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
194  }
195  else
196  {
197  var glyphRun = new GlyphRun
198  {
199  FontFace = fontFace,
200  Advances = new[] { (float)Math.Ceiling(advanceWidth) },
201  FontSize = fontSize,
202  BidiLevel = 0,
203  Indices = indices,
204  IsSideways = false,
205  Offsets = new[] { new GlyphOffset() }
206  };
207 
208 
209  RenderingMode renderingMode;
210  if (antiAliasMode != FontAntiAliasMode.Aliased)
211  {
212  var rtParams = new RenderingParams(factory);
213  renderingMode = fontFace.GetRecommendedRenderingMode(fontSize, 1.0f, MeasuringMode.Natural, rtParams);
214  rtParams.Dispose();
215  }
216  else
217  {
218  renderingMode = RenderingMode.Aliased;
219  }
220 
221  using (var runAnalysis = new GlyphRunAnalysis(factory,
222  glyphRun,
223  1.0f,
224  matrix,
225  renderingMode,
226  MeasuringMode.Natural,
227  0.0f,
228  0.0f))
229  {
230 
231  var bounds = new SharpDX.Rectangle(0, 0, pixelWidth, pixelHeight);
232  bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
233 
234  if (renderingMode == RenderingMode.Aliased)
235  {
236 
237  var texture = new byte[bounds.Width * bounds.Height];
238  runAnalysis.CreateAlphaTexture(TextureType.Aliased1x1, bounds, texture, texture.Length);
239  for (int y = 0; y < bounds.Height; y++)
240  {
241  for (int x = 0; x < bounds.Width; x++)
242  {
243  int pixelX = y * bounds.Width + x;
244  var grey = texture[pixelX];
245  var color = Color.FromArgb(grey, grey, grey);
246 
247  bitmap.SetPixel(x, y, color);
248  }
249  }
250  }
251  else
252  {
253  var texture = new byte[bounds.Width * bounds.Height * 3];
254  runAnalysis.CreateAlphaTexture(TextureType.Cleartype3x1, bounds, texture, texture.Length);
255  for (int y = 0; y < bounds.Height; y++)
256  {
257  for (int x = 0; x < bounds.Width; x++)
258  {
259  int pixelX = (y * bounds.Width + x) * 3;
260  var red = LinearToGamma(texture[pixelX]);
261  var green = LinearToGamma(texture[pixelX + 1]);
262  var blue = LinearToGamma(texture[pixelX + 2]);
263  var color = Color.FromArgb(red, green, blue);
264 
265  bitmap.SetPixel(x, y, color);
266  }
267  }
268  }
269  }
270  }
271 
272  var glyph = new Glyph(character, bitmap)
273  {
274  XOffset = -matrix.M41,
275  XAdvance = advanceWidth,
276  YOffset = -matrix.M42,
277  };
278  return glyph;
279  }
280 
281  private static byte LinearToGamma(byte color)
282  {
283  return (byte)(Math.Pow(color / 255.0f, 1 / 2.2f) * 255.0f);
284  }
285  }
286 }
SharpDX.DirectWrite.Factory Factory
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
SharpDX.DirectWrite.Font Font
System.IO.File File
Description of a glyph (a single character)
Definition: Glyph.cs:12
FontAntiAliasMode
Available antialias mode.
PixelFormat
Defines various types of pixel formats.
Definition: PixelFormat.cs:32