PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Rendering/Basics/Fonts/Font.cs

#
C# | 1334 lines | 702 code | 120 blank | 512 comment | 59 complexity | cb2eaa4acbbacbc3d2ff30c85a1c138a MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.IO;
  3. using Delta.ContentSystem.Rendering;
  4. using Delta.ContentSystem.Rendering.Helpers;
  5. using Delta.Engine;
  6. using Delta.Engine.Dynamic;
  7. using Delta.Engine.SettingsNodes;
  8. using Delta.Graphics.Basics;
  9. using Delta.Rendering.Basics.Drawing;
  10. using Delta.Rendering.Basics.Materials;
  11. using Delta.Rendering.Enums;
  12. using Delta.Utilities;
  13. using Delta.Utilities.Datatypes;
  14. using Delta.Utilities.Datatypes.Advanced;
  15. using Delta.Utilities.Graphics;
  16. using Delta.Utilities.Helpers;
  17. namespace Delta.Rendering.Basics.Fonts
  18. {
  19. /// <summary>
  20. /// That class contains all the logic to render a font <see cref="FontData"/>
  21. /// and will try to request the specified font data (in the constructor). As
  22. /// an extra feature the font will automatically choose one of the available
  23. /// font sizes depending on the current resolution to support a more sharp
  24. /// down- or up-scaling of the originally wished font size. Most fonts you
  25. /// can load from the content system will already provide 4 resolution based
  26. /// fonts fine tuned to work in all possible resolutions (see constructor).
  27. /// <para />
  28. /// Note: Performance is heavily optimized for render speed. The main idea is
  29. /// to use cached glyphs to avoid recalculating them for each text. This also
  30. /// means all of the properties of this class are read-only. To change
  31. /// anything (including character distances, line spacing, the render color,
  32. /// etc.) you need to create a new Font instance. This way the Geometry
  33. /// can be cached and only calculated once for a text, which then can be
  34. /// rendered many times (even with different positioning, rotation, etc.).
  35. /// In release mode a font rendering can be done up to 1.5 million times/sec
  36. /// (when swapbuffer, clear, geometry rendering is turned off for measuring.)
  37. /// </summary>
  38. public class Font : IDisposable, ISaveLoadBinary
  39. {
  40. #region TextParameters Struct
  41. /// <summary>
  42. /// Helper for the second input value of glyphCache. These values should
  43. /// not change much, but we still need to check them for every Get.
  44. /// </summary>
  45. private struct TextParameters
  46. {
  47. #region Width (Public)
  48. /// <summary>
  49. /// The available width (in Quadratic Space) to draw the related text.
  50. /// </summary>
  51. public float Width;
  52. #endregion
  53. #region Height (Public)
  54. /// <summary>
  55. /// The available height (in Quadratic Space) to draw the related text.
  56. /// </summary>
  57. public float Height;
  58. #endregion
  59. #region HorizontalAlignment (Public)
  60. /// <summary>
  61. /// The horizontal alignment of the related text.
  62. /// </summary>
  63. public HorizontalAlignment HorizontalAlignment;
  64. #endregion
  65. #region LineSpacing (Public)
  66. /// <summary>
  67. /// The set spacing for the text lines of the related text (if there are
  68. /// some lines). This value is FontData.LineHeight * Font.LineHeight.
  69. /// </summary>
  70. public float LineSpacing;
  71. #endregion
  72. }
  73. #endregion
  74. #region TextDrawInfo Struct
  75. /// <summary>
  76. /// Helper struct for the cache result of glyphCache.
  77. /// </summary>
  78. private struct TextDrawInfo
  79. {
  80. #region GlyphInfos (Public)
  81. /// <summary>
  82. /// That array of glyph info represents the drawing area's of each
  83. /// character in relation to the text. In other words that are NOT the
  84. /// final areas where the text will be drawn on the screen !
  85. /// </summary>
  86. public GlyphDrawInfo[] GlyphInfos;
  87. #endregion
  88. #region TextSize (Public)
  89. /// <summary>
  90. /// The measured size of the glyphs (or in other words the text) that
  91. /// will be needed to show it fully on the screen.
  92. /// </summary>
  93. public Size TextSize;
  94. #endregion
  95. #region Material (Public)
  96. /// <summary>
  97. /// Material for rendering the Geometry.
  98. /// </summary>
  99. public Material2DColored Material;
  100. #endregion
  101. #region Geometry (Public)
  102. /// <summary>
  103. /// And finally the pre-calculated geometry to render out text quickly.
  104. /// Usually text does not change, thus we can calculate the geometry
  105. /// once and render it many times.
  106. /// </summary>
  107. public Geometry Geometry;
  108. #endregion
  109. }
  110. #endregion
  111. #region Constants
  112. /// <summary>
  113. /// The current version of the implementation of this font class.
  114. /// </summary>
  115. private const int VersionNumber = 1;
  116. /// <summary>
  117. /// The default fallback font name is "DefaultFont". Used in Font.Default!
  118. /// </summary>
  119. private const string DefaultFontName = "DefaultFont";
  120. #endregion
  121. #region Default (Static)
  122. /// <summary>
  123. /// Get the default fallback font, that is usually used for info and
  124. /// debug texts, the FPS counter, profiler and other stuff that does not
  125. /// have a valid font set (or does not want to handle own fonts).
  126. /// All text is displayed
  127. /// UI has its own fallback and is much more sophisticated because
  128. /// we usually need different font sizes depending on the resolution!
  129. /// </summary>
  130. public static Font Default
  131. {
  132. get
  133. {
  134. if (defaultFont == null)
  135. {
  136. defaultFont = new Font(DefaultFontName);
  137. } // if
  138. return defaultFont;
  139. } // get
  140. }
  141. #endregion
  142. #region DrawTopLeftInformation (Static)
  143. /// <summary>
  144. /// Draws information text on the top left corner of the screen via the
  145. /// default font. Should only be used to display profiling and debug
  146. /// information.
  147. /// </summary>
  148. /// <param name="text">Text to display</param>
  149. public static void DrawTopLeftInformation(string text)
  150. {
  151. if (informationFont == null)
  152. {
  153. informationFont = new Font(Default, HorizontalAlignment.Left,
  154. VerticalAlignment.Top);
  155. } // if
  156. informationFont.Draw(text, ScreenSpace.DrawArea);
  157. }
  158. #endregion
  159. #region FamilyName (Public)
  160. /// <summary>
  161. /// Get the family name of the font (e.g. "Verdana", "Arial", etc.)
  162. /// </summary>
  163. public string FamilyName
  164. {
  165. get
  166. {
  167. return activeFontData.FamilyName;
  168. }
  169. }
  170. #endregion
  171. #region CurrentFontSize (Public)
  172. /// <summary>
  173. /// Get the current selected font size based on the current resolution.
  174. /// <para/>
  175. /// Note: This will only change if different font data values were
  176. /// specified in the constructor, else the size will always be the same
  177. /// for the current font instance.
  178. /// </summary>
  179. public float CurrentFontSize
  180. {
  181. get
  182. {
  183. return activeFontData.SizeInPoints;
  184. }
  185. }
  186. #endregion
  187. #region LineSpacing (Public)
  188. /// <summary>
  189. /// The scale factor which finally defines the total line height based on
  190. /// the font content size (default is '1.0'). Must be set in constructor
  191. /// and is usually set from the font content data. Can be adjusted in the
  192. /// constructor (e.g. 1.5) and will be multiplied with fontData.LineHeight
  193. /// for rendering.
  194. /// <para />
  195. /// LineHeight = 1.0: Normal font height for the characters.
  196. /// <para />
  197. /// LineHeight = 1.5: Bigger font height for the characters with an extra
  198. /// space of 50% between lines.
  199. /// </summary>
  200. public float LineSpacingMultiplier
  201. {
  202. get
  203. {
  204. return lineSpacingMultiplier;
  205. } // get
  206. }
  207. #endregion
  208. #region CharacterSpacing (Public)
  209. /// <summary>
  210. /// Similar to LineSpacing you can also define how far each font
  211. /// character should be apart. The default value is '1.0' and it already
  212. /// uses the Tracking defined in each of the FontDatas used by this font.
  213. /// 2.0 means the same distance a space character holds is added between
  214. /// each letter, but you can use whatever value you like (1.1, 5.0, 0.8).
  215. /// </summary>
  216. public float CharacterSpacingMultiplier
  217. {
  218. get
  219. {
  220. return characterSpacingMultiplier;
  221. }
  222. }
  223. #endregion
  224. #region LineHeight (Public)
  225. /// <summary>
  226. /// The total height of a text line (in quadratic space) based on the set
  227. /// font height (in pixel) and the line spacing multiplier.
  228. /// <para />Note: The value is depending on the current resolution.
  229. /// </summary>
  230. public float LineHeight
  231. {
  232. get;
  233. private set;
  234. }
  235. #endregion
  236. #region Color (Public)
  237. /// <summary>
  238. /// Get the color of the displayed text (default is 'White'). Can only be
  239. /// set in the constructor like most of the important font render states.
  240. /// </summary>
  241. public Color Color
  242. {
  243. get
  244. {
  245. return renderColor;
  246. }
  247. }
  248. #endregion
  249. #region DrawLayer (Public)
  250. /// <summary>
  251. /// Get the render layer where a text will be drawn. Can only be set in the
  252. /// constructor.
  253. /// </summary>
  254. public RenderLayer DrawLayer
  255. {
  256. get
  257. {
  258. return drawLayer;
  259. } // get
  260. }
  261. #endregion
  262. #region IsWordWrappingOn (Public)
  263. /// <summary>
  264. /// Is word wrapping on (default is 'false').
  265. /// <para />
  266. /// Note: This feature is still not fully supported yet.
  267. /// </summary>
  268. public bool IsWordWrappingOn
  269. {
  270. get;
  271. private set;
  272. }
  273. #endregion
  274. #region HorizontalAlignment (Public)
  275. /// <summary>
  276. /// Horizontal text alignment (default is 'Centered').
  277. /// </summary>
  278. public HorizontalAlignment HorizontalAlignment
  279. {
  280. get;
  281. private set;
  282. }
  283. #endregion
  284. #region VerticalTextAlignment (Public)
  285. /// <summary>
  286. /// Vertical text alignment (default is 'Centered').
  287. /// </summary>
  288. public VerticalAlignment VerticalAlignment
  289. {
  290. get;
  291. private set;
  292. }
  293. #endregion
  294. #region Private
  295. #region defaultFont (Private)
  296. /// <summary>
  297. /// Default font to render text centered.
  298. /// </summary>
  299. private static Font defaultFont;
  300. #endregion
  301. #region informationFont (Private)
  302. /// <summary>
  303. /// Information font for the DrawTopLeftInformation method.
  304. /// </summary>
  305. private static Font informationFont;
  306. #endregion
  307. #region renderColor (Private)
  308. /// <summary>
  309. /// Text render color
  310. /// </summary>
  311. private Color renderColor;
  312. #endregion
  313. #region lineSpacing (Private)
  314. /// <summary>
  315. /// Line spacing multiplier
  316. /// </summary>
  317. private float lineSpacingMultiplier;
  318. #endregion
  319. #region characterSpacing (Private)
  320. /// <summary>
  321. /// Character spacing
  322. /// </summary>
  323. private float characterSpacingMultiplier;
  324. #endregion
  325. #region textDrawLayer (Private)
  326. /// <summary>
  327. /// Render layer for this font
  328. /// </summary>
  329. private RenderLayer drawLayer;
  330. #endregion
  331. #region fontDatas (Private)
  332. /// <summary>
  333. /// The available FontData's for the resolutions 480x320, 800x480,
  334. /// 1024x768 and 1920x1080, started with the smallest resolution. The
  335. /// correct one will be selected by the 'DetermineBestFont()'.
  336. /// <para/>
  337. /// Note: If only one entry is set, that "auto-selecting" feature is
  338. /// disabled. If more that one is set, every entry is valid (is made sure
  339. /// in the constructor).
  340. /// </summary>
  341. private FontData[] fontDatas;
  342. #endregion
  343. #region activeFontData (Private)
  344. /// <summary>
  345. /// The reference to the current selected 'FontData' (from the 'fontDatas'
  346. /// list and based on the current screen resolution) which is used for
  347. /// drawing the text on the screen.
  348. /// </summary>
  349. private FontData activeFontData;
  350. #endregion
  351. #region fontMaps (Private)
  352. /// <summary>
  353. /// Font maps, will be updated as the activeFontData changes (each font can
  354. /// have multiple font maps). Often just has one material that we use for
  355. /// rendering. Each Glyph links to this list and has the UV coordinates for
  356. /// rendering that glyph.
  357. /// </summary>
  358. private Material2DColored[] fontMaps;
  359. #endregion
  360. #region glyphCache (Private)
  361. /// <summary>
  362. /// The internal cache of drawing information for each text that needs to
  363. /// be handled in a "Draw(...)" or in the <see cref="Measure(String)"/>
  364. /// method by the current font instance.
  365. /// <para/>
  366. /// <b>Note:</b> The cache is initialized in the constructor. It is also
  367. /// cleared every time we change something important (resolution changed).
  368. /// </summary>
  369. private readonly Cache<string, TextParameters, TextDrawInfo> glyphCache;
  370. #endregion
  371. #endregion
  372. #region Constructors
  373. /// <summary>
  374. /// Create a new font for drawing a text from a font content name.
  375. /// Note: You can also search for fonts with the FontData.Get method,
  376. /// but you should use this method normally because it lets you setup
  377. /// and change font settings without having to change the code.
  378. /// </summary>
  379. /// <param name="setFontContentName">Set font content name</param>
  380. public Font(string setFontContentName)
  381. : this(FontData.Get(setFontContentName))
  382. {
  383. }
  384. /// <summary>
  385. /// Create a new font for drawing a text from FontData, which can be loaded
  386. /// from content via FontData.Get or created customly. Please note that
  387. /// each FontData can have children entries for additional font sizes (four
  388. /// children for the 4 default resolutions, see DetermineBestFont, which
  389. /// switches fonts at 480x320, 800x480, 1024x768 and 1920x1080).
  390. /// </summary>
  391. /// <param name="setFontData">
  392. /// Set font data, which can contain 4 children for the other resolutions
  393. /// (always in the same order). If there are no children just one font data
  394. /// will be set to one font size.
  395. /// </param>
  396. /// <param name="setHorizontalAlignment">
  397. /// Set horizontal alignment mode for font rendering, defaults to centered.
  398. /// </param>
  399. /// <param name="setVerticalAlignment">
  400. /// Set vertical alignment mode for font rendering, defaults to centered.
  401. /// </param>
  402. /// <param name="setWordWrapping">Word wrapping mode (usually off).</param>
  403. /// <param name="setLineSpacingMultiplier">
  404. /// Set line spacing multiplier (default to 1.0). Multiplied with the
  405. /// FontData.LineHeight.
  406. /// </param>
  407. /// <param name="setCharacterSpacingMultiplier">
  408. /// Set character spacing multiplier (defaults to 1.0). Multiplied with
  409. /// FontData character render distances.
  410. /// </param>
  411. /// <param name="setDrawLayer">Set draw layer to use for rendering.</param>
  412. public Font(FontData setFontData,
  413. HorizontalAlignment setHorizontalAlignment =
  414. HorizontalAlignment.Centered,
  415. VerticalAlignment setVerticalAlignment = VerticalAlignment.Centered,
  416. bool setWordWrapping = false,
  417. float setLineSpacingMultiplier = 1.0f,
  418. float setCharacterSpacingMultiplier = 1.0f,
  419. RenderLayer setDrawLayer = RenderLayer.Text)
  420. : this(setFontData, Color.White, setHorizontalAlignment,
  421. setVerticalAlignment, setWordWrapping, setLineSpacingMultiplier,
  422. setCharacterSpacingMultiplier, setDrawLayer)
  423. {
  424. }
  425. /// <summary>
  426. /// Create a new font for drawing a text from FontData, which can be loaded
  427. /// from content via FontData.Get or created customly. Please note that
  428. /// each FontData can have children entries for additional font sizes (four
  429. /// children for the 4 default resolutions, see DetermineBestFont, which
  430. /// switches fonts at 480x320, 800x480, 1024x768 and 1920x1080).
  431. /// </summary>
  432. /// <param name="setFontData">
  433. /// Set font data, which can contain 3 children for the other resolutions
  434. /// (always in the same order). If there are no children all 4 fontDatas
  435. /// will be set to the same font size.
  436. /// </param>
  437. /// <param name="newFontColor">New font text render color to use.</param>
  438. /// <param name="setHorizontalAlignment">
  439. /// Set horizontal alignment mode for font rendering, defaults to centered.
  440. /// </param>
  441. /// <param name="setVerticalAlignment">
  442. /// Set vertical alignment mode for font rendering, defaults to centered.
  443. /// </param>
  444. /// <param name="setWordWrapping">Word wrapping mode (usually off).</param>
  445. /// <param name="setLineSpacingMultiplier">
  446. /// Set line spacing multiplier (default to 1.0). Multiplied with the
  447. /// FontData.LineHeight.
  448. /// </param>
  449. /// <param name="setCharacterSpacingMultiplier">
  450. /// Set character spacing multiplier (defaults to 1.0). Multiplied with
  451. /// FontData character render distances.
  452. /// </param>
  453. /// <param name="setDrawLayer">Set draw layer to use for rendering.</param>
  454. public Font(FontData setFontData, Color newFontColor,
  455. HorizontalAlignment setHorizontalAlignment =
  456. HorizontalAlignment.Centered,
  457. VerticalAlignment setVerticalAlignment = VerticalAlignment.Centered,
  458. bool setWordWrapping = false,
  459. float setLineSpacingMultiplier = 1.0f,
  460. float setCharacterSpacingMultiplier = 1.0f,
  461. RenderLayer setDrawLayer = RenderLayer.Text)
  462. : this()
  463. {
  464. #region Validation
  465. // We always need a valid FontData
  466. if (setFontData == null)
  467. {
  468. Log.Warning(
  469. "You always have to specify a valid 'FontData' to make " +
  470. "a font work, will use now the 'DefaultFont' content instead.");
  471. setFontData = FontData.Get("DefaultFont");
  472. }
  473. // If we have more data because the font should be resolution-based
  474. // then we need to check if we have the correct amount of entries
  475. if (setFontData.ResolutionFonts != null)
  476. {
  477. fontDatas = setFontData.ResolutionFonts;
  478. // If we have less entries than allowed
  479. if (setFontData.ResolutionFonts.Length < 4)
  480. {
  481. // Then we fill it up with the default value (later) inclusive
  482. // keeping the already set entries
  483. fontDatas = new FontData[4];
  484. for (int num = 0; num < setFontData.ResolutionFonts.Length; num++)
  485. {
  486. fontDatas[num] = setFontData.ResolutionFonts[num];
  487. } // for
  488. } // if
  489. // If we have more than allowed
  490. else if (fontDatas.Length > 4)
  491. {
  492. Log.Warning(
  493. "The font data has not too many ResolutionFonts, we only " +
  494. "support exactly 4 font data resolutions, but we got " +
  495. setFontData.ResolutionFonts.Length);
  496. // Then we "clamp" them
  497. fontDatas = new FontData[4];
  498. for (int num = 0; num < fontDatas.Length; num++)
  499. {
  500. fontDatas[num] = setFontData.ResolutionFonts[num];
  501. } // for
  502. } // else if
  503. // Finally make sure that every entry is valid
  504. for (int index = 0; index < fontDatas.Length; index++)
  505. {
  506. if (fontDatas[index] == null)
  507. {
  508. // Copy from last, or if this is the first, use the default
  509. fontDatas[index] =
  510. index == 0
  511. ? FontData.Default
  512. : fontDatas[index - 1];
  513. } // if
  514. } // for
  515. } // if
  516. else
  517. {
  518. // Otherwise we have no resolution based fonts, just set one font!
  519. fontDatas = new[]
  520. {
  521. setFontData
  522. };
  523. }
  524. #endregion
  525. // Also set the render state to the given (mostly default) values
  526. renderColor = newFontColor;
  527. drawLayer = setDrawLayer;
  528. lineSpacingMultiplier = setLineSpacingMultiplier;
  529. characterSpacingMultiplier = setCharacterSpacingMultiplier;
  530. HorizontalAlignment = setHorizontalAlignment;
  531. VerticalAlignment = setVerticalAlignment;
  532. IsWordWrappingOn = setWordWrapping;
  533. // Finally determine the best matching font for the current resolution
  534. // (including updating/syncing our properties). This also sets the
  535. // important activeFontData and fontMaps values.
  536. DetermineBestFont();
  537. }
  538. /// <summary>
  539. /// Creates a new font based on an existing font, required for cloning
  540. /// fonts and allows us to change the font render parameters like Color,
  541. /// DrawLayer and Alignment.
  542. /// </summary>
  543. /// <param name="fontToCopy">Font to copy all data from.</param>
  544. /// <param name="newFontColor">New font text render color to use.</param>
  545. /// <param name="setHorizontalAlignment">
  546. /// Set horizontal alignment mode for font rendering, defaults to centered.
  547. /// </param>
  548. /// <param name="setVerticalAlignment">
  549. /// Set vertical alignment mode for font rendering, defaults to centered.
  550. /// </param>
  551. /// <param name="setWordWrapping">Word wrapping mode (usually off).</param>
  552. /// <param name="setLineSpacingMultiplier">
  553. /// Set line spacing multiplier (default to 1.0). Multiplied with the
  554. /// FontData.LineHeight.
  555. /// </param>
  556. /// <param name="setCharacterSpacingMultiplier">
  557. /// Set character spacing multiplier (defaults to 1.0). Multiplied with
  558. /// FontData character render distances.
  559. /// </param>
  560. /// <param name="setDrawLayer">Set draw layer to use for rendering.</param>
  561. public Font(Font fontToCopy, Color newFontColor,
  562. HorizontalAlignment setHorizontalAlignment =
  563. HorizontalAlignment.Centered,
  564. VerticalAlignment setVerticalAlignment = VerticalAlignment.Centered,
  565. bool setWordWrapping = false,
  566. float setLineSpacingMultiplier = 1.0f,
  567. float setCharacterSpacingMultiplier = 1.0f,
  568. RenderLayer setDrawLayer = RenderLayer.Text)
  569. : this()
  570. {
  571. // Copy all data over
  572. fontDatas = fontToCopy.fontDatas;
  573. fontMaps = fontToCopy.fontMaps;
  574. activeFontData = fontToCopy.activeFontData;
  575. // Set new values
  576. renderColor = newFontColor;
  577. HorizontalAlignment = setHorizontalAlignment;
  578. VerticalAlignment = setVerticalAlignment;
  579. drawLayer = setDrawLayer;
  580. lineSpacingMultiplier = setLineSpacingMultiplier;
  581. characterSpacingMultiplier = setCharacterSpacingMultiplier;
  582. IsWordWrappingOn = setWordWrapping;
  583. // Finally determine the best matching font for the current resolution
  584. // (including updating/syncing our properties). This also sets the
  585. // important activeFontData and fontMaps values.
  586. DetermineBestFont();
  587. }
  588. /// <summary>
  589. /// Creates a new font based on an existing font, required for cloning
  590. /// fonts and allows us to change the font Alignment parameters.
  591. /// </summary>
  592. /// <param name="fontToCopy">Font to copy all data from.</param>
  593. /// <param name="setHorizontalAlignment">
  594. /// Set horizontal alignment mode for font rendering, defaults to centered.
  595. /// </param>
  596. /// <param name="setVerticalAlignment">
  597. /// Set vertical alignment mode for font rendering, defaults to centered.
  598. /// </param>
  599. public Font(Font fontToCopy,
  600. HorizontalAlignment setHorizontalAlignment =
  601. HorizontalAlignment.Centered,
  602. VerticalAlignment setVerticalAlignment = VerticalAlignment.Centered)
  603. : this()
  604. {
  605. // Copy all data over
  606. fontDatas = fontToCopy.fontDatas;
  607. fontMaps = fontToCopy.fontMaps;
  608. activeFontData = fontToCopy.activeFontData;
  609. renderColor = fontToCopy.renderColor;
  610. drawLayer = fontToCopy.drawLayer;
  611. lineSpacingMultiplier = fontToCopy.lineSpacingMultiplier;
  612. characterSpacingMultiplier = fontToCopy.characterSpacingMultiplier;
  613. IsWordWrappingOn = fontToCopy.IsWordWrappingOn;
  614. // Set new values
  615. HorizontalAlignment = setHorizontalAlignment;
  616. VerticalAlignment = setVerticalAlignment;
  617. // Finally determine the best matching font for the current resolution
  618. // (including updating/syncing our properties). This also sets the
  619. // important activeFontData and fontMaps values.
  620. DetermineBestFont();
  621. }
  622. /// <summary>
  623. /// Creates an empty font, required for the <see cref="Factory"/> if the
  624. /// font will be loaded there by the <see cref="ISaveLoadBinary"/>
  625. /// interface (see StreamHelper.LoadWithLength). Note: All values need to
  626. /// be filled in via the Load method.
  627. /// </summary>
  628. private Font()
  629. {
  630. // Make to sure that the glyph cache is initialized.
  631. // Note: Usually it's not allowed to use instance members like the
  632. // 'activeFontData', but here it will be reset in 'DetermineBestFont()'
  633. // together with the glyph cache which allows us to ignore that rule.
  634. glyphCache = new Cache<string, TextParameters, TextDrawInfo>(
  635. delegate(string inputText, TextParameters param)
  636. {
  637. // The result of our glyph info request from the currently active
  638. // font based on the current line-spacing
  639. GlyphDrawInfo[] glyphInfos;
  640. // Clipping is only set if a useful clipping size is specified
  641. bool isClippingOn =
  642. param.Width != 0.0f &&
  643. param.Height != 0.0f;
  644. if (isClippingOn)
  645. {
  646. // In the case we have clipping we need to convert the available
  647. // drawing size convert to Pixel Space first, because that's the
  648. // space the FontData uses.
  649. Size availableSizeForText = ScreenSpace.ToPixelSpace(
  650. new Size(param.Width, param.Height));
  651. // Request glyph info with clipping and optionally word wrapping
  652. glyphInfos = activeFontData.GetGlyphDrawInfos(inputText,
  653. param.LineSpacing, param.HorizontalAlignment,
  654. true, IsWordWrappingOn, ref availableSizeForText);
  655. } // if
  656. else
  657. {
  658. // If we have no clipping we can just request glyph info directly
  659. glyphInfos = activeFontData.GetGlyphDrawInfos(inputText,
  660. param.LineSpacing, param.HorizontalAlignment);
  661. } // else
  662. for (int index = 0; index < glyphInfos.Length; index++)
  663. {
  664. // Convert now the font (pixel space) into our quadratic space but
  665. // we don't use the "ScreenSpace.ToQuadraticSpace()" method (which we
  666. // usually would use for that case, because we have here
  667. // "relative space" that have to start always at (0,0) and the
  668. // "Screen" methods would returns us the current screen pixel
  669. // (which are usually not quadratic) "mapped" centered into the
  670. // Quadratic Space
  671. // e.g. for Position (0,0) of a 1024x768 resolution we would get
  672. // the quadratic position (0, 0.125), but we want (0, 0),
  673. // so we call the method that don't adds the
  674. // "offset for centering"
  675. glyphInfos[index].DrawArea =
  676. ScreenSpace.ToQuadraticSpaceTopLeft(glyphInfos[index].DrawArea);
  677. } // for
  678. // Finally return the result now
  679. return new TextDrawInfo
  680. {
  681. GlyphInfos = glyphInfos,
  682. TextSize = Measure(glyphInfos),
  683. Material =
  684. fontMaps.Length > 0
  685. ? fontMaps[0]
  686. : null,
  687. Geometry = CreateGeometryFromGlyph(glyphInfos, inputText),
  688. };
  689. });
  690. // and we will determine the best font (including glyph cache clearing)
  691. // every time the resolution changes
  692. Application.Window.ResizeEvent += DetermineBestFont;
  693. // All other values will be either initialized by the other constructors
  694. // or set via the "Load" method which is initiated by "Factory.Create".
  695. }
  696. #endregion
  697. #region IDisposable Members
  698. /// <summary>
  699. /// Dispose
  700. /// </summary>
  701. public void Dispose()
  702. {
  703. // We need do remove this event, for GC to be able to release this object
  704. Application.Window.ResizeEvent -= DetermineBestFont;
  705. // The created geometry needs to be disposed again!
  706. foreach (TextDrawInfo drawInfo in glyphCache.GetAllEntries())
  707. {
  708. drawInfo.Geometry.Dispose();
  709. }
  710. glyphCache.Clear();
  711. // Also "unregister" from the current 'FontData.ContentChanged' event
  712. if (activeFontData != null)
  713. {
  714. activeFontData.ContentChanged = null;
  715. activeFontData = null;
  716. } // if
  717. }
  718. #endregion
  719. #region ISaveLoadBinary Members
  720. /// <summary>
  721. /// Loads all data of the object again which were previously saved.
  722. /// </summary>
  723. /// <param name="dataReader">The data reader.</param>
  724. public void Load(BinaryReader dataReader)
  725. {
  726. // We currently only support our version, if more versions are added,
  727. // we need to do different loading code depending on the version here.
  728. int version = dataReader.ReadInt32();
  729. switch (version)
  730. {
  731. // Version 1
  732. case VersionNumber:
  733. // Now load all previously saved data of the font
  734. int fontDataCount = dataReader.ReadInt32();
  735. fontDatas = new FontData[fontDataCount];
  736. for (int index = 0; index < fontDataCount; index++)
  737. {
  738. // Reconstruct the FontData that is required
  739. string fontName = dataReader.ReadString();
  740. int fontSize = dataReader.ReadInt32();
  741. FontStyle fontStyle = (FontStyle)dataReader.ReadByte();
  742. // for the FontData
  743. fontDatas[index] = FontData.Get(fontName, fontSize, fontStyle);
  744. } // for
  745. // and call the DetermineBestFont() the select the current
  746. // active font based on the current screen resolution
  747. DetermineBestFont();
  748. // Finally set the specified draw options
  749. lineSpacingMultiplier = dataReader.ReadSingle();
  750. renderColor = new Color(dataReader);
  751. IsWordWrappingOn = dataReader.ReadBoolean();
  752. characterSpacingMultiplier = dataReader.ReadSingle();
  753. HorizontalAlignment = (HorizontalAlignment)dataReader.ReadByte();
  754. VerticalAlignment = (VerticalAlignment)dataReader.ReadByte();
  755. break;
  756. default:
  757. Log.InvalidVersionWarning(GetType().Name, version, VersionNumber);
  758. break;
  759. } // switch
  760. }
  761. /// <summary>
  762. /// Saves all necessary data of the object to a byte array.
  763. /// </summary>
  764. /// <param name="dataWriter">The data writer.</param>
  765. public void Save(BinaryWriter dataWriter)
  766. {
  767. // First we write the current version number of the class data format
  768. dataWriter.Write(VersionNumber);
  769. // Now we save all internal FontData's (that we need to support
  770. // several resolutions)
  771. dataWriter.Write(fontDatas.Length);
  772. foreach (FontData data in fontDatas)
  773. {
  774. dataWriter.Write(data.FamilyName);
  775. dataWriter.Write(data.SizeInPoints);
  776. dataWriter.Write((byte)data.Style);
  777. } // foreach
  778. // And finally the specified draw options
  779. dataWriter.Write(LineSpacingMultiplier);
  780. renderColor.Save(dataWriter);
  781. dataWriter.Write(IsWordWrappingOn);
  782. dataWriter.Write(CharacterSpacingMultiplier);
  783. dataWriter.Write((byte)HorizontalAlignment);
  784. dataWriter.Write((byte)VerticalAlignment);
  785. }
  786. #endregion
  787. #region Draw (Public)
  788. /// <summary>
  789. /// Draws a text without any rotation and (scrolling) offset at the
  790. /// specified area.
  791. /// </summary>
  792. /// <param name="text">The text that should be drawn.</param>
  793. /// <param name="drawArea">
  794. /// The area (in Quadratic Space) where the text should be drawn.
  795. /// </param>
  796. public void Draw(string text, Rectangle drawArea)
  797. {
  798. Draw(text, drawArea, 0.0f, Point.Zero);
  799. }
  800. /// <summary>
  801. /// Draws a text with the specified rotation (and scrolling offset) at the
  802. /// given area whereby the area defines the available space for the
  803. /// clipping too. Note: Rotation and scroll offset is not supported yet.
  804. /// </summary>
  805. /// <param name="text">The text that should be drawn.</param>
  806. /// <param name="drawArea">
  807. /// The area (in Quadratic Space) where the text should be drawn.
  808. /// </param>
  809. /// <param name="rotation">The rotation the text should have.</param>
  810. /// <param name="scrollOffset">
  811. /// The offset where the text should begin inside the text area which is
  812. /// necessary for scrolled text elements that needs a partial clipping.
  813. /// </param>
  814. public void Draw(string text, Rectangle drawArea, float rotation,
  815. Point scrollOffset)
  816. {
  817. #region Validation
  818. if (String.IsNullOrEmpty(text))
  819. {
  820. Log.Warning("You shouldn't call Font.Draw without any text!");
  821. return;
  822. } // if
  823. #endregion
  824. #region Grab glyphs from cache
  825. // If glyph cache has grown too big, just kill it and restart
  826. if (glyphCache.Count > 100)
  827. {
  828. foreach (TextDrawInfo drawInfo in glyphCache.GetAllEntries())
  829. {
  830. // The created geometry needs to be disposed again!
  831. drawInfo.Geometry.Dispose();
  832. }
  833. glyphCache.Clear();
  834. }
  835. TextDrawInfo cache = glyphCache.Get(text,
  836. new TextParameters
  837. {
  838. Width = drawArea.Width,
  839. Height = drawArea.Height,
  840. LineSpacing = lineSpacingMultiplier,
  841. HorizontalAlignment = HorizontalAlignment
  842. });
  843. // Sanity check for the case that an error has occurred due loading the
  844. // font data
  845. if (cache.GlyphInfos.Length == 0)
  846. {
  847. // In that case a warning was already logged and there is no need to
  848. // log again
  849. return;
  850. } // if
  851. #endregion
  852. #region Compute alignment positions
  853. // Initialize the text start position for rendering
  854. Point startPos = drawArea.TopLeft;
  855. //unused: Point rotatedStartPos = drawArea.Center;
  856. // Now we need to know the size of the text that we want to show
  857. Size textSize = cache.TextSize;
  858. // then use that information to align it horizontally
  859. switch (HorizontalAlignment)
  860. {
  861. case HorizontalAlignment.Left:
  862. // Already initialized
  863. //unused: rotatedStartPos.X = drawArea.Left;
  864. break;
  865. case HorizontalAlignment.Centered:
  866. startPos.X = drawArea.Center.X - textSize.WidthHalf;
  867. //unused: rotatedStartPos.X = drawArea.Center.X - textSize.WidthHalf;
  868. break;
  869. case HorizontalAlignment.Right:
  870. startPos.X = drawArea.Right - textSize.Width;
  871. break;
  872. default:
  873. // Log and reuse the 'Left' mode
  874. Log.Warning(
  875. "The set HorizontalAlignment mode '" + HorizontalAlignment +
  876. "' isn't supported (yet).");
  877. break;
  878. } // switch
  879. // and align it vertically
  880. switch (VerticalAlignment)
  881. {
  882. case VerticalAlignment.Top:
  883. // Already initialized
  884. break;
  885. case VerticalAlignment.Centered:
  886. startPos.Y = drawArea.Center.Y - textSize.HeightHalf;
  887. break;
  888. case VerticalAlignment.Bottom:
  889. startPos.Y = drawArea.Bottom - textSize.Height;
  890. break;
  891. default:
  892. // Log
  893. Log.Warning(
  894. "The set VerticalTextAlignment mode '" + VerticalAlignment +
  895. "' isn't supported (yet).");
  896. // and reuse the 'Left' mode
  897. break;
  898. } // switch
  899. // finally step we still have to make sure that computed Quadratic Space
  900. // is pixel accurate else we can would get a blurry text
  901. startPos = ScreenSpace.MakePositionPixelAccurate(startPos);
  902. if (Settings.Debug.IsDrawDebugInfoModeOn(ProfilingMode.Text))
  903. {
  904. Rect.DrawOutline(drawArea, Color.Red, rotation);
  905. } // if
  906. #endregion
  907. // In the case we have a rotation
  908. if (rotation != 0.0f)
  909. {
  910. // Apply this aspect ratio, because we want the quadratic space
  911. // coordinates to work and not 0-1 for top-bottom, top is usually ~0.15
  912. Vector aspectRatioScale = Vector.One;
  913. if (ScreenSpace.AspectRatio < 1.0f)
  914. {
  915. aspectRatioScale.X = 1.0f / ScreenSpace.AspectRatio;
  916. } // if
  917. else if (ScreenSpace.AspectRatio > 1.0f)
  918. {
  919. aspectRatioScale.Y = ScreenSpace.AspectRatio;
  920. } // else if
  921. // Note: Slow way to calculate the full font position matrix, see
  922. // ScreenSpace.ViewProjection2D for details on the aspect ratio stuff.
  923. // This are 4 matrix multiplies, which are fairly slow, see the code
  924. // below which does it all in one swoop because most matrices are easy.
  925. //Matrix fontPositionMatrix =
  926. // Matrix.CreateScale(new Vector(2, -2, 1)) *
  927. // Matrix.CreateTranslation(-1, 1, 0) *
  928. // Matrix.CreateRotationZ(rotation) *
  929. // Directly calculate the matrix :)
  930. float cosValue = MathHelper.Cos(rotation);
  931. float sinValue = MathHelper.Sin(rotation);
  932. Matrix fontPositionMatrix = new Matrix(
  933. // Everything needs to be scaled by the aspect ratio!
  934. aspectRatioScale.X * 2 * cosValue,
  935. aspectRatioScale.Y * 2 * sinValue, 0f, 0f,
  936. aspectRatioScale.X * 2 * sinValue,
  937. aspectRatioScale.Y * (-2) * cosValue, 0f, 0f,
  938. 0f, 0f, 1f, 0f,
  939. // Calculate X offset directly including the rotation.
  940. aspectRatioScale.X *
  941. (1.0f * (-1) * cosValue +
  942. -1.0f * sinValue),
  943. // Same with Y offset (see CreateRotationZ for details)
  944. aspectRatioScale.Y *
  945. (1.0f * (-1) * sinValue +
  946. -1.0f * (-1) * cosValue),
  947. 0.0f, 1.0f);
  948. // Helper to quickly move the matrix by the specified 2d amount
  949. fontPositionMatrix.Move2D(startPos);
  950. cache.Material.Draw(cache.Geometry, ref fontPositionMatrix);
  951. }
  952. else
  953. {
  954. // We need to build a matrix for positioning the font.
  955. // This time there is no need for any complicated matrix calculations.
  956. Matrix fontPositionMatrix = ScreenSpace.InternalViewProjection2D;
  957. // Helper to quickly move the matrix by the specified 2d amount
  958. fontPositionMatrix.Move2D(startPos);
  959. cache.Material.Draw(cache.Geometry, ref fontPositionMatrix);
  960. } // else
  961. }
  962. #endregion
  963. #region Measure (Public)
  964. /// <summary>
  965. /// Measures the given text and returns the size that it would need for
  966. /// drawing it in the current resolution of the screen.
  967. /// </summary>
  968. /// <param name="text">Text</param>
  969. /// <returns>
  970. /// Size
  971. /// </returns>
  972. public Size Measure(string text)
  973. {
  974. #region Validation
  975. if (String.IsNullOrEmpty(text))
  976. {
  977. Log.Info("It doesn't make sense to measure an empty text");
  978. return Size.Zero;
  979. } // if
  980. #endregion
  981. return glyphCache.Get(text,
  982. new TextParameters
  983. {
  984. Width = 0,
  985. Height = 0,
  986. LineSpacing = lineSpacingMultiplier,
  987. HorizontalAlignment = HorizontalAlignment
  988. }).TextSize;
  989. }
  990. #endregion
  991. #region Methods (Private)
  992. #region Measure
  993. /// <summary>
  994. /// Measures the given text and returns the size that it would need for
  995. /// drawing it in the current resolution of the screen.
  996. /// </summary>
  997. /// <param name="glyphDrawInfos">Glyph draw info</param>
  998. /// <returns>Size</returns>
  999. private static Size Measure(GlyphDrawInfo[] glyphDrawInfos)
  1000. {
  1001. // Nothing to measure, then abort.
  1002. if (glyphDrawInfos.Length == 0)
  1003. {
  1004. return Size.Zero;
  1005. }
  1006. // Init the total measured size with the size of the first character
  1007. Size textSize = glyphDrawInfos[0].DrawArea.Size;
  1008. // then we iterate through all other characters to build the
  1009. // "bounding box" of the text block represented by the glyph info
  1010. // Note:
  1011. // The bounding box will here a little bigger than the real text size
  1012. // because a draw area of a glyph is as wide as in the font map to be
  1013. // pixel-accurate at rendering time, but we would need the so-called
  1014. // "AdvanceWidth" and would have to subtract the "RighSideBearing" of it
  1015. // for the most-right character
  1016. for (int index = 1; index < glyphDrawInfos.Length; index++)
  1017. {
  1018. // Horizontal dimension
  1019. if (glyphDrawInfos[index].DrawArea.Right >
  1020. textSize.Width)
  1021. {
  1022. textSize.Width = glyphDrawInfos[index].DrawArea.Right;
  1023. } // if
  1024. // Vertical dimension
  1025. if (glyphDrawInfos[index].DrawArea.Bottom >
  1026. textSize.Height)
  1027. {
  1028. textSize.Height = glyphDrawInfos[index].DrawArea.Bottom;
  1029. } // if
  1030. } // for
  1031. return textSize;
  1032. }
  1033. #endregion
  1034. #region CreateGeometryFromGlyph
  1035. /// <summary>
  1036. /// Helper method to create geometry per font request, called by the
  1037. /// glyphCache delegate inside InitializeGlyphCache.
  1038. /// </summary>
  1039. /// <param name="glyphs">Glyphs with all the letters</param>
  1040. /// <param name="text">Text, just for the geometry name</param>
  1041. /// <returns>Geometry that will be used for drawing.</returns>
  1042. private Geometry CreateGeometryFromGlyph(GlyphDrawInfo[] glyphs,
  1043. string text)
  1044. {
  1045. // Now create the geometry for the text we want to draw
  1046. GeometryData textGeomData = new GeometryData("<Font_" + text + ">",
  1047. // Where we need 4 vertices for a glyph quad
  1048. glyphs.Length * 4, VertexFormat.Position2DColorTextured,
  1049. // and 6 indices to describe 2 triangles by the 4 vertices
  1050. // Note: Using indices is ~7% faster instead of pure vertex rendering
  1051. glyphs.Length * 6, true, false);
  1052. // For easier geometry creation, we still cache here the elements of
  1053. // the vertex declaration
  1054. VertexElement positionElement = textGeomData.Format.Elements[0];
  1055. VertexElement colorElement = textGeomData.Format.Elements[1];
  1056. VertexElement uvElement = textGeomData.Format.Elements[2];
  1057. // before we iterate through all glyphs and the quads for it
  1058. for (int glyphId = 0; glyphId < glyphs.Length; glyphId++)
  1059. {
  1060. //GlyphDrawInfo glyphInfo = glyphs[glyphId];
  1061. // Grab the relative draw area (which starts at (0,0))
  1062. Rectangle glyphArea = glyphs[glyphId].DrawArea;
  1063. // Cache the UV coordinates too
  1064. Rectangle glyphUV = glyphs[glyphId].UV;
  1065. // We store now the information of the quad into the geometry
  1066. // (Counter-Clockwise). Please note that the character and line
  1067. // distances, all the UVs and glyph positions and also the render color
  1068. // are baked into the geometry for the best render speed.
  1069. // TopLeft
  1070. positionElement.SaveData(textGeomData.writer, glyphArea.TopLeft);
  1071. colorElement.SaveData(textGeomData.writer, renderColor);
  1072. uvElement.SaveData(textGeomData.writer, glyphUV.TopLeft);
  1073. // BottomLeft
  1074. positionElement.SaveData(textGeomData.writer, glyphArea.BottomLeft);
  1075. colorElement.SaveData(textGeomData.writer, renderColor);
  1076. uvElement.SaveData(textGeomData.writer, glyphUV.BottomLeft);
  1077. // BottomRight
  1078. positionElement.SaveData(textGeomData.writer, glyphArea.BottomRight);
  1079. colorElement.SaveData(textGeomData.writer, renderColor);
  1080. uvElement.SaveData(textGeomData.writer, glyphUV.BottomRight);
  1081. // TopRight
  1082. positionElement.SaveData(textGeomData.writer, glyphArea.TopRight);
  1083. colorElement.SaveData(textGeomData.writer, renderColor);
  1084. uvElement.SaveData(textGeomData.writer, glyphUV.TopRight);
  1085. // and last still define the triangle render order of the quad
  1086. textGeomData.Indices[glyphId * 6] = (ushort)(glyphId * 4);
  1087. textGeomData.Indices[glyphId * 6 + 1] = (ushort)(glyphId * 4 + 1);
  1088. textGeomData.Indices[glyphId * 6 + 2] = (ushort)(glyphId * 4 + 2);
  1089. textGeomData.Indices[glyphId * 6 + 3] = (ushort)(glyphId * 4);
  1090. textGeomData.Indices[glyphId * 6 + 4] = (ushort)(glyphId * 4 + 2);
  1091. textGeomData.Indices[glyphId * 6 + 5] = (ushort)(glyphId * 4 + 3);
  1092. } // for
  1093. // Finally return the geometry, which can be rendered with the FontMaps
  1094. return Geometry.Create(textGeomData);
  1095. }
  1096. #endregion
  1097. #region DetermineBestFont
  1098. /// <summary>
  1099. /// Determines the best matching font depending on the current screen
  1100. /// resolution and clears the current glyph cache too. Note: This supports
  1101. /// both landscape (4:3, 16:9, etc.) resolutions, but also portrait mode
  1102. /// </summary>
  1103. private void DetermineBestFont()
  1104. {
  1105. if (fontDatas == null)
  1106. {
  1107. if (activeFontData == null)
  1108. {
  1109. Log.Warning(
  1110. "Unable to determine best font, which is needed to set " +
  1111. "the active font. Error: No content font data is available!");
  1112. }
  1113. return;
  1114. }
  1115. #region Select the best matching font
  1116. // We are only interested in the resolution height, because only that
  1117. // important for the current font-scaling
  1118. int resHeight = Application.Window.ViewportPixelHeight;
  1119. FontData bestFontData;
  1120. if (fontDatas.Length > 1)
  1121. {
  1122. // 480x320
  1123. if (resHeight <= 320)
  1124. {
  1125. bestFontData = fontDatas[3];
  1126. } // if
  1127. // 800x480, 800x600, 800x480, 854x480 320x480
  1128. else if (resHeight <= 480)
  1129. {
  1130. bestFontData = fontDatas[2];
  1131. } // else if
  1132. // 1024x768, 1280x720, 960x640
  1133. else if (resHeight <= 856)//640)//720//768)
  1134. {
  1135. bestFontData = fontDatas[1];
  1136. } // else if
  1137. // everything bigger like 1920x1080, 1600x1200, 640x960, etc.
  1138. else
  1139. {
  1140. bestFontData = fontDatas[0];
  1141. } // else if
  1142. } // if
  1143. else
  1144. {
  1145. // Just use the only entry that we have. Note: That call is only
  1146. // triggered by the constructor and will happen once.
  1147. bestFontData = fontDatas[0];
  1148. } // else
  1149. #endregion
  1150. // Always update the new line height (because the selected FontData could
  1151. // be same as before but the resolution has changed which means that
  1152. // line height value (in Quadratic Space) has changed at least)
  1153. ComputeLineHeight(bestFontData);
  1154. // At least clear the cache in any case because it needs to be updated
  1155. // for the new resolution. This is important because fonts are pixel
  1156. // based. Normal geometry does not matter and scales nicely usually.
  1157. glyphCache.Clear();
  1158. // If the font data is still the same then there is nothing to do
  1159. if (activeFontData == bestFontData)
  1160. {
  1161. return;
  1162. } // if
  1163. // "Delete" the notify from the 'ContentChanged' event again
  1164. // (if the call doesn't come from constructor itself)
  1165. if (activeFontData != null)
  1166. {
  1167. activeFontData.ContentChanged = null;
  1168. } // if
  1169. // Before we can set the reference to the new one
  1170. activeFontData = bestFontData;
  1171. // rebuild the font maps from the new data
  1172. UpdateFontMaps();
  1173. // And finally register at the 'ContentChanged' event in case any
  1174. // font image gets automatically reloaded.
  1175. activeFontData.ContentChanged = UpdateFontMaps;
  1176. }
  1177. #endregion
  1178. #region ComputeLineHeight
  1179. /// <summary>
  1180. /// Computes the current line height in quadratic space, needs to be
  1181. /// updated once the active font instance or resolution changes.
  1182. /// </summary>
  1183. /// <param name="fontData">
  1184. /// The <see cref="FontData"/> to compute height from.
  1185. /// </param>
  1186. private void ComputeLineHeight(FontData fontData)
  1187. {
  1188. // At first we need to get the font data that is currently selected
  1189. LineHeight = ScreenSpace.ToQuadraticSpace(
  1190. // to get the font line height in pixel for computing the line height
  1191. // for rendering a text on the screen in the current resolution
  1192. new Size(0, fontData.PixelLineHeight * LineSpacingMultiplier)).Height;
  1193. }
  1194. #endregion
  1195. #region UpdateFontMaps
  1196. /// <summary>
  1197. /// Update font maps
  1198. /// </summary>
  1199. private void UpdateFontMaps()
  1200. {
  1201. fontMaps = new Material2DColored[activeFontData.FontMapNames.Length];
  1202. for (int index = 0; index < activeFontData.FontMapNames.Length; index++)
  1203. {
  1204. fontMaps[index] = new Material2DColored(
  1205. activeFontData.FontMapNames[index])
  1206. {
  1207. DrawLayer = DrawLayer,
  1208. };
  1209. } // for
  1210. }
  1211. #endregion
  1212. #endregion
  1213. }
  1214. }