DynamicSpriteFont.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. using FontStashSharp.Interfaces;
  2. using System;
  3. #if MONOGAME || FNA
  4. using Microsoft.Xna.Framework;
  5. using Microsoft.Xna.Framework.Graphics;
  6. #elif STRIDE
  7. using Stride.Core.Mathematics;
  8. using Stride.Graphics;
  9. #else
  10. using System.Drawing;
  11. using System.Numerics;
  12. #endif
  13. namespace FontStashSharp
  14. {
  15. public partial class DynamicSpriteFont : SpriteFontBase
  16. {
  17. internal readonly Int32Map<DynamicFontGlyph> Glyphs = new Int32Map<DynamicFontGlyph>();
  18. private readonly Int32Map<int> Kernings = new Int32Map<int>();
  19. private FontMetrics[] IndexedMetrics;
  20. public FontSystem FontSystem { get; private set; }
  21. internal DynamicSpriteFont(FontSystem system, float size, int lineHeight) : base(size, lineHeight)
  22. {
  23. if (system == null)
  24. {
  25. throw new ArgumentNullException(nameof(system));
  26. }
  27. FontSystem = system;
  28. RenderFontSizeMultiplicator = FontSystem.FontResolutionFactor;
  29. }
  30. private DynamicFontGlyph GetGlyphWithoutBitmap(int codepoint)
  31. {
  32. DynamicFontGlyph glyph;
  33. if (Glyphs.TryGetValue(codepoint, out glyph))
  34. {
  35. return glyph;
  36. }
  37. int fontSourceIndex;
  38. var g = FontSystem.GetCodepointIndex(codepoint, out fontSourceIndex);
  39. if (g == null)
  40. {
  41. Glyphs[codepoint] = null;
  42. return null;
  43. }
  44. var fontSize = FontSize * FontSystem.FontResolutionFactor;
  45. var font = FontSystem.FontSources[fontSourceIndex];
  46. double advance, x0, y0, x1, y1;
  47. font.GetGlyphMetrics(codepoint, fontSize, out advance, out x0, out y0, out x1, out y1);
  48. var effectPad = Math.Max(FontSystem.BlurAmount, FontSystem.StrokeAmount);
  49. var gw = x1 - x0 + effectPad * 2;
  50. var gh = y1 - y0 + effectPad * 2;
  51. glyph = new DynamicFontGlyph
  52. {
  53. Codepoint = codepoint,
  54. Id = g.Value,
  55. FontSize = fontSize,
  56. FontSourceIndex = fontSourceIndex,
  57. RenderOffset = new Point((int)x0, (int)y0),
  58. Size = new Point((int)gw, (int)gh),
  59. XAdvance = (int)advance,
  60. };
  61. Glyphs[codepoint] = glyph;
  62. return glyph;
  63. }
  64. #if MONOGAME || FNA || STRIDE
  65. private DynamicFontGlyph GetGlyphInternal(GraphicsDevice device, int codepoint)
  66. #else
  67. private DynamicFontGlyph GetGlyphInternal(ITexture2DManager device, int codepoint)
  68. #endif
  69. {
  70. var glyph = GetGlyphWithoutBitmap(codepoint);
  71. if (glyph == null)
  72. {
  73. return null;
  74. }
  75. if (device == null || glyph.Texture != null)
  76. return glyph;
  77. FontSystem.RenderGlyphOnAtlas(device, glyph);
  78. return glyph;
  79. }
  80. #if MONOGAME || FNA || STRIDE
  81. private DynamicFontGlyph GetDynamicGlyph(GraphicsDevice device, int codepoint)
  82. #else
  83. private DynamicFontGlyph GetDynamicGlyph(ITexture2DManager device, int codepoint)
  84. #endif
  85. {
  86. var result = GetGlyphInternal(device, codepoint);
  87. if (result == null && FontSystem.DefaultCharacter != null)
  88. {
  89. result = GetGlyphInternal(device, FontSystem.DefaultCharacter.Value);
  90. }
  91. return result;
  92. }
  93. #if MONOGAME || FNA || STRIDE
  94. protected internal override FontGlyph GetGlyph(GraphicsDevice device, int codepoint)
  95. #else
  96. protected internal override FontGlyph GetGlyph(ITexture2DManager device, int codepoint)
  97. #endif
  98. {
  99. return GetDynamicGlyph(device, codepoint);
  100. }
  101. private void GetMetrics(int fontSourceIndex, out FontMetrics result)
  102. {
  103. if (IndexedMetrics == null || IndexedMetrics.Length != FontSystem.FontSources.Count)
  104. {
  105. IndexedMetrics = new FontMetrics[FontSystem.FontSources.Count];
  106. for (var i = 0; i < IndexedMetrics.Length; ++i)
  107. {
  108. double ascent, descent, lineHeight;
  109. FontSystem.FontSources[i].GetMetricsForSize(FontSize * RenderFontSizeMultiplicator, out ascent, out descent, out lineHeight);
  110. IndexedMetrics[i] = new FontMetrics((int)ascent, (int)descent, (int)lineHeight);
  111. }
  112. }
  113. result = IndexedMetrics[fontSourceIndex];
  114. }
  115. internal override void PreDraw(TextSource source, out int ascent, out int lineHeight)
  116. {
  117. // Determine ascent and lineHeight from first character
  118. ascent = 0;
  119. lineHeight = 0;
  120. while (true)
  121. {
  122. int codepoint;
  123. if (!source.GetNextCodepoint(out codepoint))
  124. {
  125. break;
  126. }
  127. var glyph = GetDynamicGlyph(null, codepoint);
  128. if (glyph == null)
  129. {
  130. continue;
  131. }
  132. FontMetrics metrics;
  133. GetMetrics(glyph.FontSourceIndex, out metrics);
  134. ascent = metrics.Ascent;
  135. lineHeight = metrics.LineHeight;
  136. break;
  137. }
  138. source.Reset();
  139. }
  140. internal override Bounds InternalTextBounds(TextSource source, Vector2 position, float characterSpacing, float lineSpacing)
  141. {
  142. if (source.IsNull)
  143. return Bounds.Empty;
  144. var bounds = base.InternalTextBounds(source, position, characterSpacing, lineSpacing);
  145. bounds.X2 += FontSystem.StrokeAmount * 2;
  146. return bounds;
  147. }
  148. private static int GetKerningsKey(int glyph1, int glyph2)
  149. {
  150. return ((glyph1 << 16) | (glyph1 >> 16)) ^ glyph2;
  151. }
  152. internal override float GetKerning(FontGlyph glyph, FontGlyph prevGlyph)
  153. {
  154. if (!FontSystem.UseKernings)
  155. {
  156. return 0.0f;
  157. }
  158. var dynamicGlyph = (DynamicFontGlyph)glyph;
  159. var dynamicPrevGlyph = (DynamicFontGlyph)prevGlyph;
  160. if (dynamicGlyph.FontSourceIndex != dynamicPrevGlyph.FontSourceIndex)
  161. {
  162. return 0.0f;
  163. }
  164. var key = GetKerningsKey(prevGlyph.Codepoint, dynamicGlyph.Codepoint);
  165. var result = 0;
  166. if (!Kernings.TryGetValue(key, out result))
  167. {
  168. var fontSource = FontSystem.FontSources[dynamicGlyph.FontSourceIndex];
  169. result = fontSource.GetGlyphKernAdvance(prevGlyph.Id, dynamicGlyph.Id, dynamicGlyph.FontSize);
  170. Kernings[key] = result;
  171. }
  172. return result;
  173. }
  174. }
  175. }