diff --git a/Core/Constants.cs b/Core/Constants.cs
new file mode 100644
index 0000000..003ea17
--- /dev/null
+++ b/Core/Constants.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using SharpFont;
+
+namespace Velentr.Font
+{
+ ///
+ /// Various constants defined for the font system.
+ ///
+ internal sealed class Constants
+ {
+ ///
+ /// The default hidef texture size
+ ///
+ public const int DEFAULT_HIDEF_TEXTURE_SIZE = 4096;
+
+ ///
+ /// The default reach texture size
+ ///
+ public const int DEFAULT_REACH_TEXTURE_SIZE = 2048;
+
+ ///
+ /// The default cache surface format
+ ///
+ public const SurfaceFormat DEFAULT_CACHE_SURFACE_FORMAT = SurfaceFormat.Bgra4444;
+
+ ///
+ /// The glyph bitmap origin
+ ///
+ public static FTVector26Dot6 GlyphBitmapOrigin = new FTVector26Dot6(0, 0);
+
+ ///
+ /// The default character list
+ ///
+ private char[] _defaultCharacterList;
+
+ ///
+ /// The default characters
+ ///
+ public string DefaultCharacters = " AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789~`!@#$%^&*()_+-=[]\\{}|;':\",./<>?。? 【】{}、|《》()…¥";
+
+ ///
+ /// The default load flags
+ ///
+ public LoadFlags DefaultLoadFlags = LoadFlags.Default;
+
+ ///
+ /// The default load target
+ ///
+ public LoadTarget DefaultLoadTarget = LoadTarget.Normal;
+
+ ///
+ /// The default render mode
+ ///
+ public RenderMode DefaultRenderMode = RenderMode.Normal;
+
+ ///
+ /// The default spaces in a tab
+ ///
+ public int DefaultSpacesInTab = 4;
+
+ ///
+ /// The kerning sanity multiplier
+ ///
+ public int KerningSanityMultiplier = 5;
+
+ ///
+ /// Initializes the class.
+ ///
+ static Constants() { }
+
+ ///
+ /// Prevents a default instance of the class from being created.
+ ///
+ private Constants() { }
+
+ ///
+ /// Gets the settings.
+ ///
+ ///
+ /// The settings.
+ ///
+ public static Constants Settings { get; } = new Constants();
+
+ ///
+ /// Gets or sets the default character list.
+ ///
+ ///
+ /// The default character list.
+ ///
+ public char[] DefaultCharacterList
+ {
+ get => _defaultCharacterList ?? (_defaultCharacterList = DefaultCharacters.ToCharArray());
+ set
+ {
+ _defaultCharacterList = value;
+ DefaultCharacters = new string(_defaultCharacterList);
+ }
+ }
+
+ ///
+ /// Whether to store the font's file data or not.
+ /// Increases memory usage, but allows the program to not have to re-read the font file when generating the same font at different sizes.
+ ///
+ ///
+ /// The default character list.
+ ///
+ public bool StoreFontFileData { get; set; } = true;
+
+ ///
+ /// A full arc of a circle
+ ///
+ public const float TWO_PI = (float)(2 * Math.PI);
+
+ ///
+ /// The maximum size of the TextCache object on a particular font
+ ///
+ public int MaxTextCacheSize = 16;
+
+ ///
+ /// The color mapping.
+ ///
+ private Dictionary _colorMapping = null;
+
+ ///
+ /// Gets the color mapping.
+ ///
+ ///
+ ///
+ /// The color mapping.
+ ///
+ public Dictionary ColorMapping
+ {
+ get
+ {
+ if (_colorMapping == null)
+ {
+ _colorMapping = new Dictionary();
+ var props = typeof(Color).GetProperties();
+
+ foreach (var color in props)
+ {
+ switch (color.Name)
+ {
+ case "PackedValue":
+ case "B":
+ case "G":
+ case "R":
+ case "A":
+ break;
+ default:
+ _colorMapping[color.Name.ToUpperInvariant()] = (Color)color.GetValue(color);
+ break;
+ }
+ }
+ }
+
+ return _colorMapping;
+ }
+ }
+
+ }
+}
diff --git a/Core/Core.projitems b/Core/Core.projitems
new file mode 100644
index 0000000..4bcc72c
--- /dev/null
+++ b/Core/Core.projitems
@@ -0,0 +1,29 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ bfaa5910-e8e8-4d47-b59b-9839992f2e81
+
+
+ Velentr.Font
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Core/Core.shproj b/Core/Core.shproj
new file mode 100644
index 0000000..456c70a
--- /dev/null
+++ b/Core/Core.shproj
@@ -0,0 +1,13 @@
+
+
+
+ bfaa5910-e8e8-4d47-b59b-9839992f2e81
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/Core/DrawStringExtensions.cs b/Core/DrawStringExtensions.cs
new file mode 100644
index 0000000..481c3e9
--- /dev/null
+++ b/Core/DrawStringExtensions.cs
@@ -0,0 +1,108 @@
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Velentr.Font
+{
+
+ ///
+ /// Extensions to SpriteBatch.Draw to handle Velentr.Font
+ ///
+// ReSharper disable once CheckNamespace
+// ReSharper disable once UnusedMember.Global
+ public static class DrawStringExtension
+ {
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The spritebatch
+ /// The Font to use when rendering the string
+ /// The string to render
+ /// Position at which to render the string
+ /// Color with which to render the string
+ public static void DrawString(this SpriteBatch spriteBatch, Font font, string text, Vector2 position, Color color)
+ {
+ font.Draw(spriteBatch, text, color, new Rectangle((int) position.X, (int) position.Y, 0, 0));
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The sprite batch.
+ /// The font.
+ /// The text.
+ /// The position.
+ /// The color.
+ /// A rotation of this string.
+ /// Center of the rotation. 0,0 by default.
+ /// A scaling of this string.
+ /// Modifications for drawing. Can be combined.
+ /// A depth of the layer of this string.
+ public static void DrawString(this SpriteBatch spriteBatch, Font font, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
+ {
+ font.Draw(spriteBatch, text, color, new Rectangle((int)position.X, (int)position.Y, 0, 0), rotation, origin, scale, effects, layerDepth);
+ }
+
+ ///
+ /// Draws text to the screen with the given font.
+ ///
+ /// The spritebatch
+ /// The Font to use when rendering the string
+ /// The string to render.
+ /// Position at which to render the string
+ /// Color with which to render the string
+ public static void DrawString(this SpriteBatch spriteBatch, Font font, StringBuilder text, Vector2 position, Color color)
+ {
+ font.Draw(spriteBatch, text.ToString(), color, new Rectangle((int)position.X, (int)position.Y, 0, 0));
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The sprite batch.
+ /// The font.
+ /// The text.
+ /// The position.
+ /// The color.
+ /// A rotation of this string.
+ /// Center of the rotation. 0,0 by default.
+ /// A scaling of this string.
+ /// Modifications for drawing. Can be combined.
+ /// A depth of the layer of this string.
+ public static void DrawString(this SpriteBatch spriteBatch, Font font, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
+ {
+ font.Draw(spriteBatch, text.ToString(), color, new Rectangle((int)position.X, (int)position.Y, 0, 0), rotation, origin, scale, effects, layerDepth);
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The spritebatch
+ /// The text to render.
+ /// Position at which to render the text
+ /// Color with which to render the text
+ public static void DrawString(this SpriteBatch spriteBatch, Text text, Vector2 position, Color color)
+ {
+ text.Draw(spriteBatch, position, color);
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The spritebatch
+ /// The text to render.
+ /// Position at which to render the text
+ /// Color with which to render the text
+ /// A rotation of this string.
+ /// Center of the rotation. 0,0 by default.
+ /// A scaling of this string.
+ /// Modifications for drawing. Can be combined.
+ /// A depth of the layer of this string.
+ public static void DrawString(this SpriteBatch spriteBatch, Text text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
+ {
+ text.Draw(spriteBatch, position, color, rotation, origin, scale, effects, layerDepth);
+ }
+
+ }
+
+}
diff --git a/Core/DrawStringMarkdownExtensions.cs b/Core/DrawStringMarkdownExtensions.cs
new file mode 100644
index 0000000..273211c
--- /dev/null
+++ b/Core/DrawStringMarkdownExtensions.cs
@@ -0,0 +1,108 @@
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Velentr.Font
+{
+
+ ///
+ /// Extensions to SpriteBatch.Draw to handle Velentr.Font
+ ///
+// ReSharper disable once CheckNamespace
+// ReSharper disable once UnusedMember.Global
+ public static class DrawStringMarkdownExtensions
+ {
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The spritebatch
+ /// The Font to use when rendering the string
+ /// The string to render
+ /// Position at which to render the string
+ /// Color with which to render the string
+ public static void DrawStringWithMarkdown(this SpriteBatch spriteBatch, Font font, string text, Vector2 position, Color color)
+ {
+ font.Draw(spriteBatch, text, color, new Rectangle((int) position.X, (int) position.Y, 0, 0), true);
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The sprite batch.
+ /// The font.
+ /// The text.
+ /// The position.
+ /// The color.
+ /// A rotation of this string.
+ /// Center of the rotation. 0,0 by default.
+ /// A scaling of this string.
+ /// Modifications for drawing. Can be combined.
+ /// A depth of the layer of this string.
+ public static void DrawStringWithMarkdown(this SpriteBatch spriteBatch, Font font, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
+ {
+ font.Draw(spriteBatch, text, color, new Rectangle((int)position.X, (int)position.Y, 0, 0), rotation, origin, scale, effects, layerDepth, true);
+ }
+
+ ///
+ /// Draws text to the screen with the given font.
+ ///
+ /// The spritebatch
+ /// The Font to use when rendering the string
+ /// The string to render.
+ /// Position at which to render the string
+ /// Color with which to render the string
+ public static void DrawStringWithMarkdown(this SpriteBatch spriteBatch, Font font, StringBuilder text, Vector2 position, Color color)
+ {
+ font.Draw(spriteBatch, text.ToString(), color, new Rectangle((int)position.X, (int)position.Y, 0, 0), true);
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The sprite batch.
+ /// The font.
+ /// The text.
+ /// The position.
+ /// The color.
+ /// A rotation of this string.
+ /// Center of the rotation. 0,0 by default.
+ /// A scaling of this string.
+ /// Modifications for drawing. Can be combined.
+ /// A depth of the layer of this string.
+ public static void DrawStringWithMarkdown(this SpriteBatch spriteBatch, Font font, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
+ {
+ font.Draw(spriteBatch, text.ToString(), color, new Rectangle((int)position.X, (int)position.Y, 0, 0), rotation, origin, scale, effects, layerDepth, true);
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The spritebatch
+ /// The text to render.
+ /// Position at which to render the text
+ /// Color with which to render the text
+ public static void DrawStringWithMarkdown(this SpriteBatch spriteBatch, Text text, Vector2 position, Color color)
+ {
+ text.Draw(spriteBatch, position, color, true);
+ }
+
+ ///
+ /// Draw text to the screen with the given Font.
+ ///
+ /// The spritebatch
+ /// The text to render.
+ /// Position at which to render the text
+ /// Color with which to render the text
+ /// A rotation of this string.
+ /// Center of the rotation. 0,0 by default.
+ /// A scaling of this string.
+ /// Modifications for drawing. Can be combined.
+ /// A depth of the layer of this string.
+ public static void DrawStringWithMarkdown(this SpriteBatch spriteBatch, Text text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
+ {
+ text.Draw(spriteBatch, position, color, rotation, origin, scale, effects, layerDepth, true);
+ }
+
+ }
+
+}
diff --git a/Core/Font.cs b/Core/Font.cs
new file mode 100644
index 0000000..62108a9
--- /dev/null
+++ b/Core/Font.cs
@@ -0,0 +1,815 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using SharpFont;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Velentr.Font.Internal;
+
+namespace Velentr.Font
+{
+
+ public abstract class Font : IEquatable, IDisposable
+ {
+
+ ///
+ /// The manager
+ ///
+ private FontManager _manager;
+
+ ///
+ /// Caches of glyphs
+ ///
+ private readonly List _glyphCaches = new List();
+
+ ///
+ /// The characters that we currently have generated glyphs for
+ ///
+ internal Dictionary CharacterGlyphs = new Dictionary();
+
+ ///
+ /// The text cache
+ ///
+ internal Cache TextCache = new Cache(Constants.Settings.MaxTextCacheSize);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Size of the font.
+ /// The font face.
+ /// The typefaceName.
+ /// The font manager.
+ protected Font(int size, Face face, string typefaceName, FontManager manager)
+ {
+ Size = size;
+ TypefaceName = typefaceName;
+ Face = face;
+ FontFamily = face.FamilyName;
+ _manager = manager;
+
+ GlyphHeight = face.Size.Metrics.Height.Ceiling();
+ }
+
+ ///
+ /// Gets the font face.
+ ///
+ ///
+ /// The face.
+ ///
+ public Face Face { get; }
+
+ ///
+ /// Gets the size of the font.
+ ///
+ ///
+ /// The size of the font.
+ ///
+ public string FontFamily { get; }
+
+ ///
+ /// Gets the height of glyphs in the Font.
+ ///
+ ///
+ /// The height of the glyph.
+ ///
+ public int GlyphHeight { get; }
+
+ ///
+ /// Gets the key representing the font.
+ ///
+ ///
+ /// The font's key.
+ ///
+ public Tuple Key => new Tuple(FontFamily, Size);
+
+ ///
+ /// Gets or sets the load flags.
+ ///
+ ///
+ /// The load flags.
+ ///
+ public LoadFlags LoadFlags { get; set; } = Constants.Settings.DefaultLoadFlags;
+
+ ///
+ /// Gets or sets the load target.
+ ///
+ ///
+ /// The load target.
+ ///
+ public LoadTarget LoadTarget { get; set; } = Constants.Settings.DefaultLoadTarget;
+
+ ///
+ /// Gets or sets the render mode.
+ ///
+ ///
+ /// The render mode.
+ ///
+ public RenderMode RenderMode { get; set; } = Constants.Settings.DefaultRenderMode;
+
+ ///
+ /// Gets the size of the Font.
+ ///
+ ///
+ /// The size.
+ ///
+ public int Size { get; }
+
+ ///
+ /// Gets or sets the number of spaces we'll use when a tab is requested.
+ ///
+ ///
+ /// The spaces in tab.
+ ///
+ public int SpacesInTab { get; set; } = Constants.Settings.DefaultSpacesInTab;
+
+ ///
+ /// Gets the typeface the font is associated with.
+ ///
+ ///
+ /// The typeface.
+ ///
+ public Typeface Typeface => _manager.GetStoredTypeface(TypefaceName);
+
+ ///
+ /// Gets the name of the typeface the font is associated with.
+ ///
+ ///
+ /// The typeface name.
+ ///
+ public string TypefaceName { get; }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ Face.Dispose();
+ Typeface.RemoveFont(Size);
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void DisposeFinal()
+ {
+ Face.Dispose();
+ }
+
+ ///
+ /// Draws the text to the screen at the specified position and with the specified color.
+ ///
+ /// The sprite batch.
+ /// The text.
+ /// The color.
+ /// The boundaries.
+ /// Whether to apply markdown commands or not. Defaults to false. If set to true, Color will be the default color.
+ public void Draw(SpriteBatch spriteBatch, string text, Color color, Rectangle boundaries, bool applyMarkdown = false)
+ {
+ var warpLine = boundaries.Width > 0;
+ var offsetX = 0;
+ var offsetY = 0;
+
+ var width = warpLine ? boundaries.Width : spriteBatch.GraphicsDevice.Viewport.Width;
+ var height = (boundaries.Height > 0 ? boundaries.Height : spriteBatch.GraphicsDevice.Viewport.Height) - boundaries.Y;
+
+ var countX = 0;
+ var underrun = 0;
+ var finalCharacterIndex = text.Length - 1;
+
+ var currentColor = color;
+ for (var i = 0; i < text.Length; i++)
+ {
+ TryGetGlyph(text[i], out var cachedCharacter);
+
+ if (warpLine && offsetX + cachedCharacter.Boundary.Width + countX > width || text[i] == '\n')
+ {
+ offsetX = 0;
+ underrun = 0;
+ offsetY += cachedCharacter.AdvanceY;
+ }
+
+ if (text[i] == '\r' || text[i] == '\n')
+ {
+ continue;
+ }
+
+ if (offsetY > height || !warpLine && offsetX > width)
+ {
+ return;
+ }
+
+ // Markdown rules
+ if (applyMarkdown && text[i] == '[' && (i > 0 && text[i - 1] != '\\'))
+ {
+ var results = ApplyMarkdownCommands(text, color, i);
+ i = results.Item1;
+ currentColor = results.Item2;
+ continue;
+ }
+
+ // calculate underrun
+ underrun += -cachedCharacter.BearingX;
+ if (offsetX == 0)
+ {
+ offsetX += underrun;
+ }
+
+ if (underrun <= 0)
+ {
+ underrun = 0;
+ }
+
+ spriteBatch.Draw(cachedCharacter.GlyphCache.Texture, new Vector2(boundaries.X + offsetX, boundaries.Y + offsetY), cachedCharacter.Boundary, currentColor);
+ offsetX += cachedCharacter.Boundary.Width;
+
+ // calculate kerning
+ if (i != finalCharacterIndex)
+ {
+ var nextCharacter = text[i + 1];
+ if (TryGetGlyph(nextCharacter, out var nextCachedCharacter))
+ {
+ var kerning = GetKerning(cachedCharacter, nextCachedCharacter);
+ var maxBounds = cachedCharacter.AdvanceX * Constants.Settings.KerningSanityMultiplier;
+ if (kerning <= maxBounds && kerning >= -maxBounds)
+ {
+ offsetX += kerning;
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Draws the text to the screen at the specified position and with the specified color.
+ ///
+ /// The sprite batch.
+ /// The text.
+ /// The color.
+ /// The boundaries.
+ /// The rotation.
+ /// The origin.
+ /// The scale.
+ /// The effects.
+ /// The layer depth.
+ /// Whether to apply markdown commands or not. Defaults to false. If set to true, Color will be the default color.
+ public void Draw(SpriteBatch spriteBatch, string text, Color color, Rectangle boundaries, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth, bool applyMarkdown = false)
+ {
+ // calculate our transformation matrix
+ var flipAdjustment = Vector2.Zero;
+ var flippedVertically = effects.HasFlag(SpriteEffects.FlipVertically);
+ var flippedHorizontally = effects.HasFlag(SpriteEffects.FlipHorizontally);
+
+ // if we've flipped, handle adjusting our location as required
+ if (flippedVertically || flippedHorizontally)
+ {
+ var size = MeasureText(text);
+
+ if (flippedHorizontally)
+ {
+ origin.X *= -1;
+ flipAdjustment.X -= size.X;
+ }
+
+ if (flippedVertically)
+ {
+ origin.Y *= -1;
+ flipAdjustment.Y = GlyphHeight - size.Y;
+ }
+ }
+
+ // Handle our rotation as required
+ var transformation = Matrix.Identity;
+ float cos, sin = 0;
+ var xScale = flippedHorizontally ? -scale.X : scale.X;
+ var yScale = flippedVertically ? -scale.Y : scale.Y;
+ var xOrigin = flipAdjustment.X - origin.X;
+ var yOrigin = flipAdjustment.Y - origin.Y;
+ if (Helpers.FloatsAreEqual(rotation, 0) || Helpers.FloatsAreEqual(rotation / Constants.TWO_PI, 1))
+ {
+ transformation.M11 = xScale;
+ transformation.M22 = yScale;
+ transformation.M41 = xOrigin * transformation.M11 + boundaries.X;
+ transformation.M42 = yOrigin * transformation.M22 + boundaries.Y;
+ }
+ else
+ {
+ cos = (float)Math.Cos(rotation);
+ sin = (float)Math.Sin(rotation);
+ transformation.M11 = xScale * cos;
+ transformation.M12 = xScale * sin;
+ transformation.M21 = yScale * -sin;
+ transformation.M22 = yScale * cos;
+ transformation.M41 = (xOrigin * transformation.M11 + yOrigin * transformation.M21) + boundaries.X;
+ transformation.M42 = (xOrigin * transformation.M12 + yOrigin * transformation.M22) + boundaries.Y;
+ }
+
+ // calculate the rest of the text position
+ var warpLine = boundaries.Width > 0;
+ var offsetX = 0;
+ var offsetY = 0;
+
+ var width = warpLine ? boundaries.Width : spriteBatch.GraphicsDevice.Viewport.Width;
+ var height = (boundaries.Height > 0 ? boundaries.Height : spriteBatch.GraphicsDevice.Viewport.Height) - boundaries.Y;
+
+ var countX = 0;
+ var underrun = 0;
+ var finalCharacterIndex = text.Length - 1;
+
+ var currentColor = color;
+ for (var i = 0; i < text.Length; i++)
+ {
+ TryGetGlyph(text[i], out var cachedCharacter);
+
+ if (warpLine && offsetX + cachedCharacter.Boundary.Width + countX > width || text[i] == '\n')
+ {
+ offsetX = 0;
+ underrun = 0;
+ offsetY += cachedCharacter.AdvanceY;
+ }
+
+ if (text[i] == '\r' || text[i] == '\n')
+ {
+ continue;
+ }
+
+ if (offsetY > height || !warpLine && offsetX > width)
+ {
+ return;
+ }
+
+ // Markdown rules
+ if (applyMarkdown && text[i] == '[' && (i > 0 && text[i - 1] != '\\'))
+ {
+ var results = ApplyMarkdownCommands(text, color, i);
+ i = results.Item1;
+ currentColor = results.Item2;
+ continue;
+ }
+
+ // calculate underrun
+ underrun += -cachedCharacter.BearingX;
+ if (offsetX == 0)
+ {
+ offsetX += underrun;
+ }
+
+ if (underrun <= 0)
+ {
+ underrun = 0;
+ }
+
+ var characterPosition = new Vector2(boundaries.X + offsetX, boundaries.Y + offsetY);
+ Vector2.Transform(ref characterPosition, ref transformation, out characterPosition);
+ spriteBatch.Draw(cachedCharacter.GlyphCache.Texture, characterPosition, cachedCharacter.Boundary, currentColor, rotation, origin, scale, effects, layerDepth);
+ offsetX += cachedCharacter.Boundary.Width;
+
+ // calculate kerning
+ if (i != finalCharacterIndex)
+ {
+ var nextCharacter = text[i + 1];
+ if (TryGetGlyph(nextCharacter, out var nextCachedCharacter))
+ {
+ var kerning = GetKerning(cachedCharacter, nextCachedCharacter);
+ var maxBounds = cachedCharacter.AdvanceX * Constants.Settings.KerningSanityMultiplier;
+ if (kerning <= maxBounds && kerning >= -maxBounds)
+ {
+ offsetX += kerning;
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Pre-generates a list of Glyphs to draw to the string.
+ ///
+ /// The text.
+ /// Whether to apply markdown commands or not. Defaults to false. If set to true, Color will be the default color.
+ /// A Text object representing the Glyphs we need to draw to the screen for the input string.
+ public Text MakeText(StringBuilder text, bool applyMarkdown = false)
+ {
+ return MakeText(text.ToString(), applyMarkdown);
+ }
+
+ ///
+ /// Pre-generates a list of Glyphs to draw to the string.
+ ///
+ /// The text.
+ /// Whether to apply markdown commands or not. Defaults to false. If set to true, Color will be the default color.
+ /// A Text object representing the Glyphs we need to draw to the screen for the input string.
+ public Text MakeText(string text, bool applyMarkdown = false)
+ {
+ Text textResult;
+ if (!TextCache.TryGetItem(text, out textResult))
+ {
+ var finalSize = new Vector2(0, 0);
+
+ textResult = new TextImplementation(text, this);
+
+ var offsetX = 0;
+ var offsetY = 0;
+
+ var underrun = 0;
+ var finalCharacterIndex = text.Length - 1;
+
+ var currentColor = Color.White;
+ for (var i = 0; i < text.Length; i++)
+ {
+ TryGetGlyph(text[i], out var cachedCharacter);
+ if (i == 0)
+ {
+ finalSize.Y += cachedCharacter.Boundary.Height;
+ }
+
+ if (text[i] == '\n')
+ {
+ finalSize.X = Math.Max(offsetX, finalSize.X);
+ offsetX = 0;
+ underrun = 0;
+ offsetY += cachedCharacter.AdvanceY;
+ if (i != finalCharacterIndex)
+ {
+ finalSize.Y += cachedCharacter.AdvanceY;
+ }
+ }
+
+ if (text[i] == '\r' || text[i] == '\n')
+ {
+ continue;
+ }
+
+ // Markdown rules
+ if (applyMarkdown && text[i] == '[' && (i > 0 && text[i - 1] != '\\'))
+ {
+ var results = ApplyMarkdownCommands(text, Color.White, i);
+ i = results.Item1;
+ currentColor = results.Item2;
+ continue;
+ }
+
+ // calculate underrun
+ underrun += -cachedCharacter.BearingX;
+ if (offsetX == 0)
+ {
+ offsetX += underrun;
+ }
+
+ if (underrun <= 0)
+ {
+ underrun = 0;
+ }
+
+ textResult.AddCharacter(new TextCharacter(cachedCharacter, new Vector2(offsetX, offsetY), applyMarkdown ? (Color?)currentColor : null));
+ offsetX += cachedCharacter.Boundary.Width;
+
+ // calculate kerning
+ if (i != finalCharacterIndex)
+ {
+ var nextCharacter = text[i + 1];
+ if (TryGetGlyph(nextCharacter, out var nextCachedCharacter))
+ {
+ var kerning = GetKerning(cachedCharacter, nextCachedCharacter);
+ var maxBounds = cachedCharacter.AdvanceX * Constants.Settings.KerningSanityMultiplier;
+ if (kerning <= maxBounds && kerning >= -maxBounds)
+ {
+ offsetX += kerning;
+ }
+ }
+ }
+ else
+ {
+ finalSize.X = Math.Max(offsetX, finalSize.X);
+ }
+ }
+
+ textResult.Width = finalSize.X;
+ textResult.Height = finalSize.Y;
+ textResult.Size = new Vector2(textResult.Width, textResult.Height);
+
+ TextCache.AddItemToCache(text, textResult);
+ }
+
+ return new TextImplementation(textResult);
+ }
+
+ ///
+ /// Measures the text.
+ ///
+ /// The text.
+ /// The size of the text.
+ public Vector2 MeasureText(StringBuilder text)
+ {
+ return MeasureText(text.ToString());
+ }
+
+ ///
+ /// Measures the text.
+ ///
+ /// The text.
+ /// The size of the text.
+ public Vector2 MeasureText(string text)
+ {
+ var finalSize = new Vector2(0, 0);
+
+ var offsetX = 0;
+
+ var underrun = 0;
+ var finalCharacterIndex = text.Length - 1;
+
+ for (var i = 0; i < text.Length; i++)
+ {
+ TryGetGlyph(text[i], out var cachedCharacter);
+ if (i == 0)
+ {
+ finalSize.Y += cachedCharacter.Boundary.Height;
+ }
+
+ if (text[i] == '\n')
+ {
+ finalSize.X = Math.Max(offsetX, finalSize.X);
+ offsetX = 0;
+ underrun = 0;
+ if (i != finalCharacterIndex)
+ {
+ finalSize.Y += cachedCharacter.AdvanceY;
+ }
+ }
+
+ if (text[i] == '\r' || text[i] == '\n')
+ {
+ continue;
+ }
+
+ // Markdown rules
+ if (text[i] == '[' && (i > 0 && text[i - 1] != '\\'))
+ {
+ var results = ApplyMarkdownCommands(text, Color.White, i);
+ i = results.Item1;
+ continue;
+ }
+
+ // calculate underrun
+ underrun += -cachedCharacter.BearingX;
+ if (offsetX == 0)
+ {
+ offsetX += underrun;
+ }
+
+ if (underrun <= 0)
+ {
+ underrun = 0;
+ }
+
+ offsetX += cachedCharacter.Boundary.Width;
+
+ // calculate kerning
+ if (i != finalCharacterIndex)
+ {
+ var nextCharacter = text[i + 1];
+ if (TryGetGlyph(nextCharacter, out var nextCachedCharacter))
+ {
+ var kerning = GetKerning(cachedCharacter, nextCachedCharacter);
+ var maxBounds = cachedCharacter.AdvanceX * Constants.Settings.KerningSanityMultiplier;
+ if (kerning <= maxBounds && kerning >= -maxBounds)
+ {
+ offsetX += kerning;
+ }
+ }
+ }
+ }
+
+ return finalSize;
+ }
+
+ ///
+ /// Indicates whether the current object is equal to another object of the same type.
+ ///
+ /// An object to compare with this object.
+ ///
+ /// true if the current object is equal to the other parameter; otherwise, false.
+ ///
+ public bool Equals(Font other)
+ {
+ return !(other is null) && Equals(Key, other.Key);
+ }
+
+ ///
+ /// Determines whether the specified , is equal to this instance.
+ ///
+ /// The to compare with this instance.
+ ///
+ /// true if the specified is equal to this instance; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+ return obj != null && (obj is Font || obj is FontImplementation) && Equals((Font)obj);
+ }
+
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ ///
+ public override int GetHashCode()
+ {
+ return (FontFamily.GetHashCode() * 397) ^ Size.GetHashCode();
+ }
+
+ ///
+ /// Resizes the text cache.
+ ///
+ /// New size of the cache.
+ public void ResizeTextCache(int newCacheSize)
+ {
+ TextCache.MaxCacheSize = newCacheSize;
+ }
+
+ ///
+ /// Converts the Font to a string to provide debug information on the font.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return $"font: [{FontFamily}], size: [{Size}]";
+ }
+
+ ///
+ /// Implements the operator ==.
+ ///
+ /// The left.
+ /// The right.
+ ///
+ /// The result of the operator.
+ ///
+ public static bool operator ==(Font left, Font right)
+ {
+ return !(left is null) && left.Equals(right);
+ }
+
+ ///
+ /// Implements the operator !=.
+ ///
+ /// The left.
+ /// The right.
+ ///
+ /// The result of the operator.
+ ///
+ public static bool operator !=(Font left, Font right)
+ {
+ return !(left is null) && !left.Equals(right);
+ }
+
+ ///
+ /// Pres the generate character glyphs.
+ ///
+ /// The characters.
+ internal void PreGenerateCharacterGlyphs(char[] characters)
+ {
+ if (characters == null)
+ {
+ characters = Constants.Settings.DefaultCharacterList;
+ }
+
+ // ReSharper disable once ForCanBeConvertedToForeach
+ for (var i = 0; i < characters.Length; i++)
+ {
+ GenerateGlyph(characters[i]);
+ }
+ }
+
+ ///
+ /// Gets the kerning between two characters.
+ ///
+ /// The left.
+ /// The right.
+ /// The kerning between the characters.
+ private int GetKerning(Internal.Glyph left, Internal.Glyph right)
+ {
+ if (left.Kerning.TryGetValue(right.Character, out var kerning))
+ {
+ left.Kerning[right.Character] = kerning =
+ (int)Face.GetKerning((uint)left.Index, (uint)right.Index, KerningMode.Default).X;
+ }
+
+ return kerning;
+ }
+
+ ///
+ /// Generates the glyph.
+ ///
+ /// The character.
+ ///
+ /// Could not generate character [{character}]!
+ private Internal.Glyph GenerateGlyph(char character)
+ {
+ var cache = _glyphCaches.FirstOrDefault(c => !c.Full);
+ if (cache == null)
+ {
+ cache = new GlyphCache(this, _manager);
+ _glyphCaches.Add(cache);
+ }
+
+ if (!cache.AddCharacterToCache(character, out var cachedGlyph))
+ {
+ cache = new GlyphCache(this, _manager);
+ _glyphCaches.Add(cache);
+ if (!cache.AddCharacterToCache(character, out cachedGlyph))
+ {
+ throw new Exception($"Could not generate character [{character}]!");
+ }
+ }
+
+ return cachedGlyph;
+ }
+
+ ///
+ /// Tries the get glyph.
+ ///
+ /// The character.
+ /// The glyph.
+ ///
+ private bool TryGetGlyph(char character, out Internal.Glyph glyph)
+ {
+ if (!CharacterGlyphs.TryGetValue(character, out glyph))
+ {
+ glyph = GenerateGlyph(character);
+ CharacterGlyphs.Add(character, glyph);
+ }
+
+ return true;
+ }
+
+ ///
+ /// Applies the markdown commands.
+ ///
+ /// The text.
+ /// The default color.
+ /// Index of the current.
+ /// (new index, new text color)
+ ///
+ /// Invalid markdown exception!
+ ///
+ private (int, Color) ApplyMarkdownCommands(string text, Color defaultColor, int currentIndex)
+ {
+ int finalIIndex;
+ int endIndex;
+ var finalColor = defaultColor;
+
+ // exit early if we're at the end of the string
+ if (text.Length <= currentIndex + 1)
+ {
+ return (currentIndex, finalColor);
+ }
+
+ switch (text[currentIndex + 1])
+ {
+ // invalid markdown, we'll skip this markdown...
+ case ']':
+ case '/':
+ endIndex = text.Substring(currentIndex).IndexOf(']');
+ finalIIndex = endIndex == -1 ? text.Length : endIndex + currentIndex;
+
+ if (text[currentIndex + 1] == '/')
+ {
+ finalColor = defaultColor;
+ }
+ break;
+ default:
+ endIndex = text.Substring(currentIndex).IndexOf(']');
+ finalIIndex = endIndex == -1 ? text.Length : endIndex + currentIndex;
+ var length = finalIIndex - (currentIndex + 1);
+ var rawMarkdown = text.Substring(currentIndex + 1, length);
+ var cmd = rawMarkdown.Split(':');
+ if (cmd.Length != 2)
+ {
+ throw new Exception("Invalid markdown exception!");
+ }
+
+ cmd[0] = cmd[0].Trim().ToUpperInvariant();
+ cmd[1] = cmd[1].Trim().ToUpperInvariant();
+
+ // Color command
+ if (cmd[0] == "C" || cmd[0] == "COLOR")
+ {
+ if (Constants.Settings.ColorMapping.TryGetValue(cmd[1], out var newColor))
+ {
+ finalColor = newColor;
+ }
+ else
+ {
+ throw new Exception("Invalid markdown exception!");
+ }
+ }
+
+ break;
+ }
+
+ return (finalIIndex, finalColor);
+ }
+ }
+}
diff --git a/Core/FontManager.cs b/Core/FontManager.cs
new file mode 100644
index 0000000..5258569
--- /dev/null
+++ b/Core/FontManager.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.Xna.Framework.Graphics;
+using SharpFont;
+using Velentr.Collections.Collections.Concurrent;
+using Velentr.Font.Internal;
+
+namespace Velentr.Font
+{
+
+ ///
+ ///
+ ///
+ ///
+ public class FontManager : IDisposable
+ {
+
+ ///
+ /// The libraries
+ ///
+ private ConcurrentPool _libraries;
+
+ ///
+ /// The fonts we have cached.
+ ///
+ private readonly Dictionary typefaces;
+
+ ///
+ /// Initializes the class.
+ ///
+ public FontManager(GraphicsDevice graphicsDevice)
+ {
+ _libraries = new ConcurrentPool(capacity: 1);
+ typefaces = new Dictionary();
+ GraphicsDevice = graphicsDevice;
+ }
+
+ ///
+ /// Gets the library.
+ ///
+ ///
+ internal Library GetLibrary()
+ {
+ return _libraries.Get();
+ }
+
+ ///
+ /// Returns the library.
+ ///
+ /// The library.
+ internal void ReturnLibrary(Library library)
+ {
+ _libraries.Return(library);
+ }
+
+ ///
+ /// Gets the graphics device.
+ ///
+ ///
+ /// The graphics device.
+ ///
+ internal GraphicsDevice GraphicsDevice { get; private set; }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ foreach (var font in typefaces.Values)
+ {
+ font.DisposeFinal();
+ }
+
+ _libraries.Dispose();
+ }
+
+ ///
+ /// Gets the font.
+ ///
+ /// The path.
+ /// The size.
+ /// if set to true [pre generate characters].
+ /// The characters to pregenerate.
+ /// The store typeface file data.
+ ///
+ public Font GetFont(string path, int size, bool preGenerateCharacters = false, char[] charactersToPregenerate = null, bool? storeTypefaceFileData = null)
+ {
+ var typeface = GetTypefaceInternal(path, File.ReadAllBytes(path), preGenerateCharacters, charactersToPregenerate, storeTypefaceFileData);
+ return typeface.GetFont(size, preGenerateCharacters, charactersToPregenerate);
+ }
+
+ ///
+ /// Gets the font.
+ ///
+ /// The name.
+ /// The file stream.
+ /// The size.
+ /// if set to true [pre generate characters].
+ /// The characters to pregenerate.
+ ///
+ public Font GetFont(string name, Stream fileStream, int size, bool preGenerateCharacters = false, char[] charactersToPregenerate = null)
+ {
+ var buffer = Helpers.ReadStream(fileStream);
+ var typeface = GetTypefaceInternal(name, buffer, preGenerateCharacters, charactersToPregenerate, true);
+ return typeface.GetFont(size, preGenerateCharacters, charactersToPregenerate);
+ }
+
+ ///
+ /// Gets the font.
+ ///
+ /// The name.
+ /// The file data.
+ /// The size.
+ /// if set to true [pre generate characters].
+ /// The characters to pregenerate.
+ ///
+ public Font GetFont(string name, byte[] fileData, int size, bool preGenerateCharacters = false, char[] charactersToPregenerate = null)
+ {
+ var typeface = GetTypefaceInternal(name, fileData, preGenerateCharacters, charactersToPregenerate, true);
+ return typeface.GetFont(size, preGenerateCharacters, charactersToPregenerate);
+ }
+
+ ///
+ /// Gets or loads the specified font with the specified size.
+ ///
+ /// The path to load the font from.
+ /// if set to true [pre generate characters].
+ /// The characters to pregenerate.
+ /// Whether to store typeface file data.
+ /// The Font that matches the specified parameters.
+ /// GraphicsDevice is not initialized! Please either initialize VelentrFont.Core or provide the GraphicsDevice when getting a new font.
+ public Typeface GetTypeface(string path, bool preGenerateCharacters = false, char[] charactersToPregenerate = null, bool? storeTypefaceFileData = null)
+ {
+ return GetTypefaceInternal(path, File.ReadAllBytes(path), preGenerateCharacters, charactersToPregenerate, storeTypefaceFileData);
+ }
+
+ ///
+ /// Gets or loads the specified font with the specified size.
+ ///
+ /// The name.
+ /// The file stream.
+ /// if set to true [pre generate characters].
+ /// The characters to pregenerate.
+ /// The Font that matches the specified parameters.
+ public Typeface GetTypeface(string name, Stream fileStream, bool preGenerateCharacters = false, char[] charactersToPregenerate = null)
+ {
+ var buffer = Helpers.ReadStream(fileStream);
+ return GetTypefaceInternal(name, buffer, preGenerateCharacters, charactersToPregenerate, true);
+ }
+
+ ///
+ /// Gets or loads the specified font with the specified size.
+ ///
+ /// The name.
+ /// The file data.
+ /// if set to true [pre generate characters].
+ /// The characters to pregenerate.
+ /// The Font that matches the specified parameters.
+ public Typeface GetTypeface(string name, byte[] fileData, bool preGenerateCharacters = false, char[] charactersToPregenerate = null)
+ {
+ return GetTypefaceInternal(name, fileData, preGenerateCharacters, charactersToPregenerate, true);
+ }
+
+ ///
+ /// Gets the typeface internal.
+ ///
+ /// The name.
+ /// The file data.
+ /// if set to true [pre generate characters].
+ /// The characters to pregenerate.
+ /// The store typeface file data.
+ ///
+ /// GraphicsDevice is not initialized! Please either initialize VelentrFont.Core or provide the GraphicsDevice when getting a new font.
+ private Typeface GetTypefaceInternal(string name, byte[] fileData, bool preGenerateCharacters, char[] charactersToPregenerate, bool? storeTypefaceFileData)
+ {
+ if (storeTypefaceFileData == null)
+ {
+ storeTypefaceFileData = Constants.Settings.StoreFontFileData;
+ }
+
+ if (!typefaces.TryGetValue(name, out var typeface))
+ {
+ typeface = new TypefaceImplementation(name, fileData, preGenerateCharacters, charactersToPregenerate, (bool)storeTypefaceFileData, this);
+
+ typefaces.Add(name, typeface);
+ }
+
+ return typeface;
+ }
+
+ ///
+ /// Gets the stored typeface.
+ ///
+ /// The name.
+ ///
+ internal Typeface GetStoredTypeface(string name)
+ {
+ return typefaces[name];
+ }
+
+ ///
+ /// Removes a font from the system.
+ ///
+ /// The key for the font we want to remove.
+ internal void RemoveTypeface(string name, bool dispose = true)
+ {
+ if (typefaces.ContainsKey(name))
+ {
+ if (dispose)
+ {
+ typefaces[name].DisposeFinal();
+ }
+
+ typefaces.Remove(name);
+ }
+ }
+ }
+}
diff --git a/Core/Internal/Cache.cs b/Core/Internal/Cache.cs
new file mode 100644
index 0000000..0372d31
--- /dev/null
+++ b/Core/Internal/Cache.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+
+namespace Velentr.Font.Internal
+{
+ internal class Cache
+ {
+ ///
+ /// The cached objects
+ ///
+ private Dictionary objects;
+
+ ///
+ /// The queue
+ ///
+ private Queue queue;
+
+ ///
+ /// The maximum cache size
+ ///
+ private int maxCacheSize;
+
+ ///
+ /// Gets or sets the maximum size of the cache.
+ ///
+ ///
+ /// The maximum size of the cache.
+ ///
+ /// Max Cache Size must be 0 or greater!
+ public int MaxCacheSize
+ {
+ get => maxCacheSize;
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("Max Cache Size must be 0 or greater!");
+ }
+
+ ResizeCache(value);
+ maxCacheSize = value;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Maximum size of the cache.
+ public Cache(int maxCacheSize)
+ {
+ objects = new Dictionary(maxCacheSize);
+ queue = new Queue(maxCacheSize);
+ this.maxCacheSize = maxCacheSize;
+ }
+
+ ///
+ /// Gets the cache item if it exists.
+ ///
+ /// The key.
+ /// The value to return.
+ /// Whether we were able to retrieve the value or not
+ public bool TryGetItem(K key, out V value)
+ {
+ if (objects.TryGetValue(key, out value))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Adds the item to cache.
+ ///
+ /// The key.
+ /// The value.
+ /// if set to true [override when key exists].
+ ///
+ /// Key already exists in cache!
+ public bool AddItemToCache(K key, V value, bool overrideWhenKeyExists = false)
+ {
+ // Validate we don't already have this object in the Cache
+ if (objects.ContainsKey(key) && !overrideWhenKeyExists)
+ {
+ if (!overrideWhenKeyExists)
+ {
+ throw new ArgumentException("Key already exists in cache!");
+ }
+ else
+ {
+ objects[key] = value;
+ return false;
+ }
+ }
+
+ // Add the item to the cache, if we can
+ if (maxCacheSize > 0)
+ {
+ objects.Add(key, value);
+ queue.Enqueue(key);
+ if (queue.Count > maxCacheSize)
+ {
+ var keyToRemove = queue.Dequeue();
+ objects.Remove(keyToRemove);
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Resizes the cache.
+ ///
+ /// The maximum size.
+ private void ResizeCache(int maxSize)
+ {
+ var tempCachedObjects = new Dictionary(objects);
+ objects = new Dictionary(maxSize);
+ foreach (var item in tempCachedObjects)
+ {
+ objects.Add(item.Key, item.Value);
+ }
+
+ var tempQueue = new Queue(queue);
+ queue = new Queue(maxSize);
+ while (queue.Count > 0)
+ {
+ queue.Enqueue(tempQueue.Dequeue());
+ }
+ }
+ }
+}
diff --git a/Core/Internal/FontImplementation.cs b/Core/Internal/FontImplementation.cs
new file mode 100644
index 0000000..2535d1e
--- /dev/null
+++ b/Core/Internal/FontImplementation.cs
@@ -0,0 +1,12 @@
+using SharpFont;
+
+namespace Velentr.Font.Internal
+{
+ internal class FontImplementation : Font
+ {
+ public FontImplementation(int size, Face face, string typefaceName, FontManager manager) : base(size, face, typefaceName, manager)
+ {
+
+ }
+ }
+}
diff --git a/Core/Internal/Glyph.cs b/Core/Internal/Glyph.cs
new file mode 100644
index 0000000..db76dcf
--- /dev/null
+++ b/Core/Internal/Glyph.cs
@@ -0,0 +1,90 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace Velentr.Font.Internal
+{
+ internal class Glyph
+ {
+ ///
+ /// The X Advance of the character.
+ ///
+ public int AdvanceX;
+
+ ///
+ /// The Y Advance of the character.
+ ///
+ public int AdvanceY;
+
+ ///
+ /// The X bearing of the character.
+ ///
+ public int BearingX;
+
+ ///
+ /// The Y bearing of the character.
+ ///
+ public int BearingY;
+
+ ///
+ /// The position and boundaries of the Character on the GlyphCache's Texture.
+ ///
+ public Rectangle Boundary;
+
+ ///
+ /// The Character.
+ ///
+ public char Character;
+
+ ///
+ /// The index of the Character.
+ ///
+ public int Index;
+
+ ///
+ /// Kerning for the character.
+ ///
+ public Dictionary Kerning = new Dictionary();
+
+ ///
+ /// The GlyphCache that the character is associated with.
+ ///
+ public GlyphCache GlyphCache;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The advance x.
+ /// The advance y.
+ /// The bearing x.
+ /// The bearing y.
+ /// The boundary.
+ /// The character.
+ /// The index.
+ /// The glyph cache.
+ public Glyph(int advanceX, int advanceY, int bearingX, int bearingY, Rectangle boundary, char character, int index, GlyphCache glyphCache)
+ {
+ AdvanceX = advanceX;
+ AdvanceY = advanceY;
+ BearingX = bearingX;
+ BearingY = bearingY;
+ Boundary = boundary;
+ Character = character;
+ Index = index;
+ GlyphCache = glyphCache;
+ }
+
+ ///
+ /// The Bearing of the Character with the Width added.
+ ///
+ public int BearingWithWidth => BearingX + Boundary.Width;
+
+ ///
+ /// Converts the character to a string for debug purposes.
+ ///
+ ///
+ public override string ToString()
+ {
+ return $"Char: {Character}; X: {Boundary.X}; Y: {Boundary.Y}; Width: {Boundary.Width}, Height: {Boundary.Height}";
+ }
+ }
+}
diff --git a/Core/Internal/GlyphCache.cs b/Core/Internal/GlyphCache.cs
new file mode 100644
index 0000000..4d19ff5
--- /dev/null
+++ b/Core/Internal/GlyphCache.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using SharpFont;
+
+namespace Velentr.Font.Internal
+{
+ ///
+ /// A cache of Glyphs.
+ ///
+ internal class GlyphCache
+ {
+ ///
+ /// The width of the GlyphCache.
+ ///
+ public static int Width = Constants.DEFAULT_REACH_TEXTURE_SIZE;
+
+ ///
+ /// The height of the GlyphCache.
+ ///
+ public static int Height = Constants.DEFAULT_HIDEF_TEXTURE_SIZE;
+
+ ///
+ /// The buffer.
+ ///
+ private static ushort[] _buffer;
+
+ ///
+ /// The font the GlyphCache is associated with.
+ ///
+ private readonly Font _font;
+
+ ///
+ /// The characters that are part of this GlyphCache.
+ ///
+ private readonly List _characters = new List();
+
+ ///
+ /// The current x position in the GlyphCache.
+ ///
+ private int _currentX;
+
+ ///
+ /// The current y position in the GlyphCache.
+ ///
+ private int _currentY;
+
+ ///
+ /// The manager
+ ///
+ private FontManager _manager;
+
+ ///
+ /// Whether the Cache is full (true) or not (false).
+ ///
+ public bool Full;
+
+ ///
+ /// The texture the characters are cached on.
+ ///
+ public Texture2D Texture;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The font.
+ /// The font manager.
+ public GlyphCache(Font font, FontManager manager)
+ {
+ _manager = manager;
+ _font = font;
+ switch (_manager.GraphicsDevice.GraphicsProfile)
+ {
+ case GraphicsProfile.HiDef:
+ Height = Constants.DEFAULT_HIDEF_TEXTURE_SIZE;
+ Width = Constants.DEFAULT_HIDEF_TEXTURE_SIZE;
+ break;
+ case GraphicsProfile.Reach:
+ Height = Constants.DEFAULT_REACH_TEXTURE_SIZE;
+ Width = Constants.DEFAULT_HIDEF_TEXTURE_SIZE;
+ break;
+ }
+
+ Texture = new Texture2D(_manager.GraphicsDevice, Width, Height, false, Constants.DEFAULT_CACHE_SURFACE_FORMAT);
+ }
+
+ ///
+ /// Adds the character to cache.
+ ///
+ /// The character.
+ /// The cached glyph.
+ /// Whether we could add the character to the cache or not.
+ public bool AddCharacterToCache(char character, out Glyph glyph)
+ {
+ var index = _font.Face.GetCharIndex(character);
+ _font.Face.LoadGlyph(index, _font.LoadFlags, _font.LoadTarget);
+ using (var faceGlyph = _font.Face.Glyph.GetGlyph())
+ {
+ faceGlyph.ToBitmap(_font.RenderMode, Constants.GlyphBitmapOrigin, true);
+
+ using (var bitmap = faceGlyph.ToBitmapGlyph())
+ {
+ if (_currentX + faceGlyph.Advance.X.Ceiling() >= Width)
+ {
+ _currentY += _font.GlyphHeight + _font.Face.Size.Metrics.NominalHeight;
+ _currentX = 0;
+ }
+
+ if (_currentY >= Height - _font.GlyphHeight)
+ {
+ Full = true;
+ glyph = null;
+ return false;
+ }
+
+ glyph = AddGlyph(character, faceGlyph, bitmap);
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Adds the character.
+ ///
+ /// The character.
+ /// The glyph.
+ /// The bitmap glyph.
+ /// The character that we added to the cache.
+ private Glyph AddGlyph(char character, SharpFont.Glyph glyph, BitmapGlyph bitmapGlyph)
+ {
+ if (!(bitmapGlyph.Bitmap.Width == 0 || bitmapGlyph.Bitmap.Rows == 0))
+ {
+ var cBox = glyph.GetCBox(GlyphBBoxMode.Pixels);
+ var bearingY = (int)_font.Face.Size.Metrics.NominalHeight;
+ var rectangle = new Rectangle(_currentX + cBox.Left, _currentY + (bearingY - cBox.Top), bitmapGlyph.Bitmap.Width, bitmapGlyph.Bitmap.Rows);
+ var dataLength = bitmapGlyph.Bitmap.BufferData.Length;
+ _buffer = new ushort[dataLength];
+
+ for (var i = 0; i < _buffer.Length; i++)
+ {
+ var c = bitmapGlyph.Bitmap.BufferData[i] >> 4;
+ _buffer[i] = (ushort)((c << 4) | (c << 8) | (c << 12) | c);
+ }
+
+ if (character < 255 && character != '_')
+ {
+ rectangle.Y += 1;
+ }
+
+ if (rectangle.X < 0)
+ {
+ rectangle.Offset(-rectangle.X, 0);
+ }
+
+ if (rectangle.Y < 0)
+ {
+ rectangle.Offset(0, -rectangle.Y);
+ }
+
+ if (glyph.Advance.X.Ceiling() != rectangle.Width)
+ {
+ rectangle.Offset(Math.Abs(rectangle.Width - glyph.Advance.X.Ceiling()) / 2, 0);
+ }
+
+ Texture.SetData(0, rectangle, _buffer, 0, dataLength);
+ }
+
+ _characters.Add(character);
+
+ var advanceX = glyph.Advance.X.Ceiling();
+ if (character == '\t')
+ {
+ advanceX = Math.Abs(_font.Face.Size.Metrics.NominalWidth * _font.SpacesInTab);
+ }
+
+ var finalCharacter = new Internal.Glyph(glyph.Advance.X.Ceiling(), _font.Face.Size.Metrics.NominalHeight, _font.Face.Glyph.Metrics.HorizontalBearingX.Ceiling(), _font.Face.Size.Metrics.Descender.Ceiling(), new Rectangle(_currentX, _currentY, advanceX, _font.GlyphHeight + _font.Face.Size.Metrics.NominalHeight), character, _characters.Count - 1, this);
+
+ _currentX += advanceX + _font.Face.Size.Metrics.NominalWidth;
+ return finalCharacter;
+ }
+ }
+}
diff --git a/Core/Internal/Helpers.cs b/Core/Internal/Helpers.cs
new file mode 100644
index 0000000..18b1d5c
--- /dev/null
+++ b/Core/Internal/Helpers.cs
@@ -0,0 +1,95 @@
+using System;
+using System.IO;
+
+namespace Velentr.Font.Internal
+{
+ ///
+ /// Various helpers for use with the Font System.
+ ///
+ public static class Helpers
+ {
+ ///
+ /// Converts a float to an int.
+ ///
+ /// The value.
+ /// The int representing the float value.
+ public static int ConvertFloatToInt(float value)
+ {
+ return Convert.ToInt32(value);
+ }
+
+ ///
+ /// Converts the 1D array to a 2D array.
+ ///
+ /// The type of the array.
+ /// The base 1D array.
+ /// The width.
+ /// The height.
+ /// The 2D array.
+ public static T[,] Convert1DArrayTo2DArray(T[] baseArray, int width, int height)
+ {
+ var array = new T[width, height];
+
+ for (var x = 0; x < width; x++)
+ for (var y = 0; y < height; y++)
+ {
+ array[x, y] = baseArray[x + y * width];
+ }
+
+ return array;
+ }
+
+ ///
+ /// Converts the 2D array to a 1D array.
+ ///
+ /// The type of the array.
+ /// The base array.
+ /// The width.
+ /// The height.
+ /// The 1D array.
+ public static T[] Convert2DArrayTo1DArray(T[,] baseArray, int width, int height)
+ {
+ var array = new T[width * height];
+
+ var i = 0;
+ var xMax = baseArray.GetUpperBound(0);
+ var yMax = baseArray.GetUpperBound(1);
+ for (var x = 0; x < xMax; x++)
+ for (var y = 0; y < yMax; y++)
+ {
+ array[i++] = baseArray[x, y];
+ }
+
+ return array;
+ }
+
+ ///
+ /// Checks whether two floats are approximately equivalent
+ ///
+ /// The value1.
+ /// The value2.
+ /// The maximum difference.
+ /// Whether the floats are equivalent
+ public static bool FloatsAreEqual(float value1, float value2, float maxDifference = 0.00001f)
+ {
+ return Math.Abs(value1 - value2) <= maxDifference;
+ }
+
+ ///
+ /// Reads the a filestream and returns it as a byte array.
+ ///
+ /// The stream.
+ /// The filestream as a byte array
+ public static byte[] ReadStream(Stream stream)
+ {
+ byte[] output;
+ using (var ms = new MemoryStream())
+ {
+ stream.CopyTo(ms);
+ output = ms.ToArray();
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/Core/Internal/TextCharacter.cs b/Core/Internal/TextCharacter.cs
new file mode 100644
index 0000000..7ddf24f
--- /dev/null
+++ b/Core/Internal/TextCharacter.cs
@@ -0,0 +1,38 @@
+using Microsoft.Xna.Framework;
+
+namespace Velentr.Font.Internal
+{
+ ///
+ /// A Character in the Text object.
+ ///
+ internal class TextCharacter
+ {
+ ///
+ /// The character.
+ ///
+ public Glyph Character;
+
+ ///
+ /// The position of the character in the Text object.
+ ///
+ public Vector2 Position;
+
+ ///
+ /// The color
+ ///
+ public Color? Color;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The character.
+ /// The position.
+ /// The color. Defaults to null.
+ public TextCharacter(Glyph character, Vector2 position, Color? color = null)
+ {
+ Character = character;
+ Position = position;
+ Color = color;
+ }
+ }
+}
diff --git a/Core/Internal/TextImplementation.cs b/Core/Internal/TextImplementation.cs
new file mode 100644
index 0000000..5b447a3
--- /dev/null
+++ b/Core/Internal/TextImplementation.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+
+namespace Velentr.Font.Internal
+{
+ ///
+ /// The implementation of the Text object.
+ ///
+ ///
+ public class TextImplementation : Text
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The text.
+ /// The font.
+ public TextImplementation(string text, Font font)
+ {
+ String = text;
+ Characters = new List();
+ Font = font;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The old text object to copy.
+ public TextImplementation(Text text) : base(text) { }
+ }
+
+}
diff --git a/Core/Internal/TypefaceImplementation.cs b/Core/Internal/TypefaceImplementation.cs
new file mode 100644
index 0000000..6138e84
--- /dev/null
+++ b/Core/Internal/TypefaceImplementation.cs
@@ -0,0 +1,10 @@
+namespace Velentr.Font.Internal
+{
+ internal class TypefaceImplementation : Typeface
+ {
+ public TypefaceImplementation(string name, byte[] typefaceData, bool preGenerateCharacters, char[] charactersToPreGenerate, bool storeTypefaceFileData, FontManager manager) : base(name, typefaceData, preGenerateCharacters, charactersToPreGenerate, storeTypefaceFileData, manager)
+ {
+
+ }
+ }
+}
diff --git a/Core/MarkdownMapping.cs b/Core/MarkdownMapping.cs
new file mode 100644
index 0000000..6541d05
--- /dev/null
+++ b/Core/MarkdownMapping.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Velentr.Font
+{
+ class MarkdownMapping
+ {
+ }
+}
diff --git a/Core/Text.cs b/Core/Text.cs
new file mode 100644
index 0000000..647eab0
--- /dev/null
+++ b/Core/Text.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Velentr.Collections.Collections;
+using Velentr.Font.Internal;
+
+namespace Velentr.Font
+{
+ ///
+ /// A pre-generated list of characters that can be rendered
+ ///
+ public abstract class Text
+ {
+ ///
+ /// The characters that are used by this Text object
+ ///
+ internal List Characters;
+
+ ///
+ /// The font that is used by this Text object
+ ///
+ protected Font Font;
+
+ ///
+ /// Gets the width.
+ ///
+ ///
+ /// The width.
+ ///
+ public float Width { get; internal set; }
+
+ ///
+ /// Gets the width as an int.
+ ///
+ ///
+ /// The width int.
+ ///
+ public int WidthInt => Helpers.ConvertFloatToInt(Width);
+
+ ///
+ /// Gets the height.
+ ///
+ ///
+ /// The height.
+ ///
+ public float Height { get; internal set; }
+
+ ///
+ /// Gets the height as an int.
+ ///
+ ///
+ /// The height int.
+ ///
+ public int HeightInt => Helpers.ConvertFloatToInt(Height);
+
+ ///
+ /// Gets the string that will be printed by this Text object.
+ ///
+ ///
+ /// The string.
+ ///
+ public string String { get; internal set; }
+
+ ///
+ /// Gets the size of the object.
+ ///
+ ///
+ /// The size.
+ ///
+ public Vector2 Size { get; internal set; }
+
+ ///
+ /// The calculation cache.
+ ///
+ private SizeLimitedOrderedDictionary<(Vector2, float, Vector2, Vector2, SpriteEffects), List> _calculationCache = new SizeLimitedOrderedDictionary<(Vector2, float, Vector2, Vector2, SpriteEffects), List>();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected Text()
+ {
+ _calculationCache.MaxSize = 8;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The old object.
+ protected Text(Text oldObject)
+ {
+ String = oldObject.String;
+ Size = oldObject.Size;
+ Height = oldObject.Height;
+ Width = oldObject.Width;
+ Font = oldObject.Font;
+ Characters = new List(oldObject.Characters);
+ _calculationCache.MaxSize = oldObject.MaxCalculationCacheSize;
+ }
+
+ ///
+ /// Gets or sets the maximum size of the calculation cache.
+ ///
+ ///
+ ///
+ /// The maximum size of the calculation cache.
+ ///
+ public long MaxCalculationCacheSize
+ {
+ get => _calculationCache.MaxSize;
+ set => _calculationCache.MaxSize = value;
+ }
+
+ ///
+ /// Adds a character to the Text object.
+ ///
+ /// The character to add.
+ internal void AddCharacter(TextCharacter character)
+ {
+ Characters.Add(character);
+ }
+
+ ///
+ /// Draws the text to the string.
+ ///
+ /// The sprite batch.
+ /// The position to draw the text at.
+ /// The color to draw the text with.
+ /// The color to draw the text with.
+ /// Whether to apply markdown commands or not. Defaults to false. If set to true, Color will be the default color.
+ public void Draw(SpriteBatch spriteBatch, Vector2 position, Color color, bool applyMarkdown = false)
+ {
+ // ReSharper disable once ForCanBeConvertedToForeach
+ for (var i = 0; i < Characters.Count; i++)
+ {
+ spriteBatch.Draw(Characters[i].Character.GlyphCache.Texture, Characters[i].Position + position, Characters[i].Character.Boundary, applyMarkdown ? Characters[i].Color ?? color : color);
+ }
+ }
+
+ ///
+ /// Draws the text to the string.
+ ///
+ /// The sprite batch.
+ /// The position to draw the text at.
+ /// The color to draw the text with.
+ /// A rotation of this string.
+ /// Center of the rotation. 0,0 by default.
+ /// A scaling of this string.
+ /// Modifications for drawing. Can be combined.
+ /// A depth of the layer of this string.
+ /// Whether to apply markdown commands or not. Defaults to false. If set to true, Color will be the default color.
+ public void Draw(SpriteBatch spriteBatch, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth, bool applyMarkdown = false)
+ {
+ var key = (position, rotation, origin, scale, effects);
+ if (_calculationCache.Exists(key))
+ {
+ var positions = _calculationCache.GetItem(key);
+ for (var i = 0; i < Characters.Count; i++)
+ {
+ spriteBatch.Draw(Characters[i].Character.GlyphCache.Texture, positions[i], Characters[i].Character.Boundary, applyMarkdown ? Characters[i].Color ?? color : color, rotation, origin, scale, effects, layerDepth);
+ }
+ }
+ else
+ {
+ var flipAdjustment = Vector2.Zero;
+ var flippedVertically = effects.HasFlag(SpriteEffects.FlipVertically);
+ var flippedHorizontally = effects.HasFlag(SpriteEffects.FlipHorizontally);
+
+ // if we've flipped, handle adjusting our location as required
+ if (flippedVertically || flippedHorizontally)
+ {
+ if (flippedHorizontally)
+ {
+ origin.X *= -1;
+ flipAdjustment.X -= Size.X;
+ }
+
+ if (flippedVertically)
+ {
+ origin.Y *= -1;
+ flipAdjustment.Y = Font.GlyphHeight - Size.Y;
+ }
+ }
+
+ // Handle our rotation as required
+ var transformation = Matrix.Identity;
+ float cos, sin = 0;
+ var xScale = flippedHorizontally ? -scale.X : scale.X;
+ var yScale = flippedVertically ? -scale.Y : scale.Y;
+ var xOrigin = flipAdjustment.X - origin.X;
+ var yOrigin = flipAdjustment.Y - origin.Y;
+ if (Helpers.FloatsAreEqual(rotation, 0) || Helpers.FloatsAreEqual(rotation / Constants.TWO_PI, 1))
+ {
+ transformation.M11 = xScale;
+ transformation.M22 = yScale;
+ transformation.M41 = xOrigin * transformation.M11 + position.X;
+ transformation.M42 = yOrigin * transformation.M22 + position.Y;
+ }
+ else
+ {
+ cos = (float)Math.Cos(rotation);
+ sin = (float)Math.Sin(rotation);
+ transformation.M11 = xScale * cos;
+ transformation.M12 = xScale * sin;
+ transformation.M21 = yScale * -sin;
+ transformation.M22 = yScale * cos;
+ transformation.M41 = (xOrigin * transformation.M11 + yOrigin * transformation.M21) + position.X;
+ transformation.M42 = (xOrigin * transformation.M12 + yOrigin * transformation.M22) + position.Y;
+ }
+
+ var positions = new List();
+ // ReSharper disable once ForCanBeConvertedToForeach
+ for (var i = 0; i < Characters.Count; i++)
+ {
+ var characterPosition = Characters[i].Position;
+ Vector2.Transform(ref characterPosition, ref transformation, out characterPosition);
+ positions.Add(characterPosition);
+ spriteBatch.Draw(Characters[i].Character.GlyphCache.Texture, characterPosition, Characters[i].Character.Boundary, applyMarkdown ? Characters[i].Color ?? color : color, rotation, origin, scale, effects, layerDepth);
+ }
+
+ _calculationCache.AddItem(key, positions);
+ }
+ }
+
+ }
+}
diff --git a/Core/Typeface.cs b/Core/Typeface.cs
new file mode 100644
index 0000000..9c7eb36
--- /dev/null
+++ b/Core/Typeface.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections.Generic;
+using SharpFont;
+using Velentr.Font.Internal;
+
+namespace Velentr.Font
+{
+ ///
+ /// A Font that can be used to draw text to the screen
+ ///
+ ///
+ ///
+ public abstract class Typeface : IEquatable, IDisposable
+ {
+
+ ///
+ /// The manager
+ ///
+ private FontManager _manager;
+
+ ///
+ /// The fonts
+ ///
+ private Dictionary Fonts = new Dictionary();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name.
+ /// The typeface data.
+ /// if set to true [pre generate characters].
+ /// The characters to pre generate.
+ /// if set to true [store typeface file data].
+ /// The font manager.
+ protected Typeface(string name, byte[] typefaceData, bool preGenerateCharacters, char[] charactersToPreGenerate, bool storeTypefaceFileData, FontManager manager)
+ {
+ Name = name;
+ _manager = manager;
+
+ if (storeTypefaceFileData)
+ {
+ TypefaceData = typefaceData;
+ }
+ else
+ {
+ TypefaceData = null;
+ }
+
+ PreGenerateCharacters = preGenerateCharacters;
+ CharactersToPreGenerate = charactersToPreGenerate;
+ }
+
+ ///
+ /// Gets the name of the typeface.
+ ///
+ ///
+ /// The name.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the typeface data.
+ ///
+ ///
+ /// The typeface data.
+ ///
+ public byte[] TypefaceData { get; }
+
+ ///
+ /// Gets or sets a value indicating whether [pre generate characters].
+ ///
+ ///
+ /// true if [pre generate characters]; otherwise, false.
+ ///
+ public bool PreGenerateCharacters { get; set; }
+
+ ///
+ /// Gets or sets the characters to pre generate.
+ ///
+ ///
+ /// The characters to pre generate.
+ ///
+ public char[] CharactersToPreGenerate { get; set; }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ DisposeFinal();
+ _manager.RemoveTypeface(Name);
+ }
+
+ ///
+ /// Disposes the final.
+ ///
+ public void DisposeFinal()
+ {
+ for (var i = 0; i < Fonts.Count; i++)
+ {
+ Fonts[i].DisposeFinal();
+ }
+ }
+
+ ///
+ /// Indicates whether the current object is equal to another object of the same type.
+ ///
+ /// An object to compare with this object.
+ ///
+ /// true if the current object is equal to the other parameter; otherwise, false.
+ ///
+ public bool Equals(Typeface other)
+ {
+ return !(other is null) && Equals(Name, other.Name);
+ }
+
+ ///
+ /// Determines whether the specified , is equal to this instance.
+ ///
+ /// The to compare with this instance.
+ ///
+ /// true if the specified is equal to this instance; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+ return obj != null && (obj is Typeface || obj is TypefaceImplementation) && Equals((Typeface)obj);
+ }
+
+ ///
+ /// Gets the font.
+ ///
+ /// The size.
+ /// The pre generate characters.
+ /// The characters to pre generate.
+ /// The requested font
+ public Font GetFont(int size, bool? preGenerateCharacters = null, char[] charactersToPreGenerate = null)
+ {
+ if (!Fonts.TryGetValue(size, out var font))
+ {
+ var library = _manager.GetLibrary();
+ Face face;
+ if (TypefaceData == null)
+ {
+ face = library.NewFace(Name, 0);
+ }
+ else
+ {
+ face = new Face(library, TypefaceData, 0);
+ }
+
+ face.SetCharSize(size, size, 0, 0);
+ face.SetTransform();
+ font = new FontImplementation(size, face, Name, _manager);
+
+ preGenerateCharacters = preGenerateCharacters ?? PreGenerateCharacters;
+ charactersToPreGenerate = charactersToPreGenerate ?? CharactersToPreGenerate;
+ if ((bool)preGenerateCharacters && charactersToPreGenerate != null)
+ {
+ font.PreGenerateCharacterGlyphs(charactersToPreGenerate);
+ }
+
+ Fonts.Add(size, font);
+ _manager.ReturnLibrary(library);
+ }
+
+ return font;
+ }
+
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ ///
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+
+ ///
+ /// Removes the font.
+ ///
+ /// The size.
+ public void RemoveFont(int size)
+ {
+ Fonts.Remove(size);
+ }
+ }
+}
diff --git a/Velentr.Font.DevEnv/Content/Content.mgcb b/Velentr.Font.DevEnv/Content/Content.mgcb
deleted file mode 100644
index ddc4c36..0000000
--- a/Velentr.Font.DevEnv/Content/Content.mgcb
+++ /dev/null
@@ -1,15 +0,0 @@
-
-#----------------------------- Global Properties ----------------------------#
-
-/outputDir:bin/$(Platform)
-/intermediateDir:obj/$(Platform)
-/platform:DesktopGL
-/config:
-/profile:Reach
-/compress:False
-
-#-------------------------------- References --------------------------------#
-
-
-#---------------------------------- Content ---------------------------------#
-
diff --git a/Velentr.Font.DevEnv/Content/PlayfairDisplayRegular-ywLOY.ttf b/Velentr.Font.DevEnv/Content/PlayfairDisplayRegular-ywLOY.ttf
deleted file mode 100644
index b034b57..0000000
Binary files a/Velentr.Font.DevEnv/Content/PlayfairDisplayRegular-ywLOY.ttf and /dev/null differ
diff --git a/Velentr.Font.DevEnv/Content/Trueno-wml2.otf b/Velentr.Font.DevEnv/Content/Trueno-wml2.otf
deleted file mode 100644
index 82a3952..0000000
Binary files a/Velentr.Font.DevEnv/Content/Trueno-wml2.otf and /dev/null differ
diff --git a/Velentr.Font.DevEnv/Icon.bmp b/Velentr.Font.DevEnv/Icon.bmp
deleted file mode 100644
index 2b48165..0000000
Binary files a/Velentr.Font.DevEnv/Icon.bmp and /dev/null differ
diff --git a/Velentr.Font.DevEnv/Icon.ico b/Velentr.Font.DevEnv/Icon.ico
deleted file mode 100644
index 7d9dec1..0000000
Binary files a/Velentr.Font.DevEnv/Icon.ico and /dev/null differ
diff --git a/Velentr.Font.DevEnv/Velentr.Font.DevEnv.csproj b/Velentr.Font.DevEnv/Velentr.Font.DevEnv.csproj
deleted file mode 100644
index d9bdcdc..0000000
--- a/Velentr.Font.DevEnv/Velentr.Font.DevEnv.csproj
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
- WinExe
- netcoreapp3.1
- false
- false
- AnyCPU;x64;x86
-
-
- app.manifest
- Icon.ico
- Velentr.Font.MonogameDevEnv
- Velentr.Font.MonogameDevEnv
- 1.1.5
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Velentr.Font.DevEnv/Velentr.Font.DevEnv.projitems b/Velentr.Font.DevEnv/Velentr.Font.DevEnv.projitems
new file mode 100644
index 0000000..1d4a010
--- /dev/null
+++ b/Velentr.Font.DevEnv/Velentr.Font.DevEnv.projitems
@@ -0,0 +1,15 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ 3a1a739f-4ff1-4a5f-ae05-c1c71269e471
+
+
+ Velentr.Font.DevEnv
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Velentr.Font.DevEnv/Velentr.Font.DevEnv.shproj b/Velentr.Font.DevEnv/Velentr.Font.DevEnv.shproj
new file mode 100644
index 0000000..f28526b
--- /dev/null
+++ b/Velentr.Font.DevEnv/Velentr.Font.DevEnv.shproj
@@ -0,0 +1,13 @@
+
+
+
+ 3a1a739f-4ff1-4a5f-ae05-c1c71269e471
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/Velentr.Font.DevEnv/app.manifest b/Velentr.Font.DevEnv/app.manifest
deleted file mode 100644
index 588488d..0000000
--- a/Velentr.Font.DevEnv/app.manifest
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true/pm
- permonitorv2,permonitor
-
-
-
-
diff --git a/Velentr.Font.FNA.DevEnv/Velentr.Font.FNA.DevEnv.csproj b/Velentr.Font.FNA.DevEnv/Velentr.Font.FNA.DevEnv.csproj
index 74df041..f02504c 100644
--- a/Velentr.Font.FNA.DevEnv/Velentr.Font.FNA.DevEnv.csproj
+++ b/Velentr.Font.FNA.DevEnv/Velentr.Font.FNA.DevEnv.csproj
@@ -25,6 +25,10 @@
+
+
+
+
PreserveNewest
@@ -47,10 +51,6 @@
-
-
-
-
diff --git a/Velentr.Font.FNA/Velentr.Font.FNA.csproj b/Velentr.Font.FNA/Velentr.Font.FNA.csproj
index a10bcc2..4f66f33 100644
--- a/Velentr.Font.FNA/Velentr.Font.FNA.csproj
+++ b/Velentr.Font.FNA/Velentr.Font.FNA.csproj
@@ -22,32 +22,12 @@
true
TRACE;FNA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
True
@@ -73,4 +53,5 @@
true
+
\ No newline at end of file
diff --git a/Velentr.Font.Monogame/Velentr.Font.Monogame.csproj b/Velentr.Font.Monogame/Velentr.Font.Monogame.csproj
index 0a58545..79122ef 100644
--- a/Velentr.Font.Monogame/Velentr.Font.Monogame.csproj
+++ b/Velentr.Font.Monogame/Velentr.Font.Monogame.csproj
@@ -20,33 +20,12 @@
TRACE;MONOGAME
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
True
@@ -64,4 +43,5 @@
true
+
\ No newline at end of file
diff --git a/Velentr.Font.sln b/Velentr.Font.sln
index 127a25f..dced0bb 100644
--- a/Velentr.Font.sln
+++ b/Velentr.Font.sln
@@ -28,15 +28,21 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNA", "FNA\FNA.csproj", "{3
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{0C917F86-4FB7-4B9F-ADA5-9E2FFEA1A630}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velentr.Font", "Velentr.Font\Velentr.Font.csproj", "{D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velentr.Font.DevEnv", "Velentr.Font.DevEnv\Velentr.Font.DevEnv.csproj", "{DAF5FD47-E521-4C3E-957A-39396087412C}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velentr.Font.FNA.DevEnv", "Velentr.Font.FNA.DevEnv\Velentr.Font.FNA.DevEnv.csproj", "{98FA26C8-9CD9-42D4-A9BD-1EF91F35FA96}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velentr.Font.Monogame.DevEnv", "Velentr.Font.Monogame.DevEnv\Velentr.Font.Monogame.DevEnv.csproj", "{A8C229E5-B7B0-4601-854B-386800ED3C04}"
EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Velentr.Font.DevEnv", "Velentr.Font.DevEnv\Velentr.Font.DevEnv.shproj", "{3A1A739F-4FF1-4A5F-AE05-C1C71269E471}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Velentr.Font", "Velentr.Font\Velentr.Font.shproj", "{2095BC84-2EF2-49E6-853A-5DDC228E3FC9}"
+EndProject
Global
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ Velentr.Font\Velentr.Font.projitems*{2095bc84-2ef2-49e6-853a-5ddc228e3fc9}*SharedItemsImports = 13
+ Velentr.Font.DevEnv\Velentr.Font.DevEnv.projitems*{3a1a739f-4ff1-4a5f-ae05-c1c71269e471}*SharedItemsImports = 13
+ Velentr.Font\Velentr.Font.projitems*{5c078eed-2110-441a-b035-b64448331acc}*SharedItemsImports = 5
+ Velentr.Font\Velentr.Font.projitems*{ff7b65da-eadd-48ef-a8a6-bc74f1c49f7a}*SharedItemsImports = 5
+ EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
@@ -90,30 +96,6 @@ Global
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|x64.Build.0 = Release|x64
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|x86.ActiveCfg = Release|x86
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|x86.Build.0 = Release|x86
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Debug|x64.Build.0 = Debug|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Debug|x86.Build.0 = Debug|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Release|Any CPU.Build.0 = Release|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Release|x64.ActiveCfg = Release|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Release|x64.Build.0 = Release|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Release|x86.ActiveCfg = Release|Any CPU
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE}.Release|x86.Build.0 = Release|Any CPU
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Debug|x64.ActiveCfg = Debug|x64
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Debug|x64.Build.0 = Debug|x64
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Debug|x86.ActiveCfg = Debug|x86
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Debug|x86.Build.0 = Debug|x86
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Release|Any CPU.Build.0 = Release|Any CPU
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Release|x64.ActiveCfg = Release|x64
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Release|x64.Build.0 = Release|x64
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Release|x86.ActiveCfg = Release|x86
- {DAF5FD47-E521-4C3E-957A-39396087412C}.Release|x86.Build.0 = Release|x86
{98FA26C8-9CD9-42D4-A9BD-1EF91F35FA96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{98FA26C8-9CD9-42D4-A9BD-1EF91F35FA96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98FA26C8-9CD9-42D4-A9BD-1EF91F35FA96}.Debug|x64.ActiveCfg = Debug|x64
@@ -147,10 +129,10 @@ Global
{FF7B65DA-EADD-48EF-A8A6-BC74F1C49F7A} = {4EF70DBD-06A1-4A99-BAC1-0A637986A23D}
{5C078EED-2110-441A-B035-B64448331ACC} = {63F57368-C601-4517-B2BF-E4A90AB50432}
{35253CE1-C864-4CD3-8249-4D1319748E8F} = {63F57368-C601-4517-B2BF-E4A90AB50432}
- {D4BA7BCC-6453-42D4-ADDC-F0B9E251B8AE} = {0C917F86-4FB7-4B9F-ADA5-9E2FFEA1A630}
- {DAF5FD47-E521-4C3E-957A-39396087412C} = {0C917F86-4FB7-4B9F-ADA5-9E2FFEA1A630}
{98FA26C8-9CD9-42D4-A9BD-1EF91F35FA96} = {63F57368-C601-4517-B2BF-E4A90AB50432}
{A8C229E5-B7B0-4601-854B-386800ED3C04} = {4EF70DBD-06A1-4A99-BAC1-0A637986A23D}
+ {3A1A739F-4FF1-4A5F-AE05-C1C71269E471} = {0C917F86-4FB7-4B9F-ADA5-9E2FFEA1A630}
+ {2095BC84-2EF2-49E6-853A-5DDC228E3FC9} = {0C917F86-4FB7-4B9F-ADA5-9E2FFEA1A630}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D55B5BF-F2FF-4F6A-852F-9D3EAB7B7A9A}
diff --git a/Velentr.Font/Velentr.Font.csproj b/Velentr.Font/Velentr.Font.csproj
deleted file mode 100644
index f761a8d..0000000
--- a/Velentr.Font/Velentr.Font.csproj
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
- netstandard2.0
- AnyCPU
- Velentr.Font
- 1.2.6
- 107679e3-f475-468e-aa29-5562efc68072
-
-
-
-
-
-
-
-
-
-
- Always
- true
-
-
- Always
- true
-
-
-
-
- Always
-
-
- Always
-
-
-
\ No newline at end of file
diff --git a/Velentr.Font/Velentr.Font.projitems b/Velentr.Font/Velentr.Font.projitems
new file mode 100644
index 0000000..f5f1c29
--- /dev/null
+++ b/Velentr.Font/Velentr.Font.projitems
@@ -0,0 +1,29 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ 2095bc84-2ef2-49e6-853a-5ddc228e3fc9
+
+
+ Velentr.Font
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Velentr.Font/Velentr.Font.shproj b/Velentr.Font/Velentr.Font.shproj
new file mode 100644
index 0000000..f115e58
--- /dev/null
+++ b/Velentr.Font/Velentr.Font.shproj
@@ -0,0 +1,13 @@
+
+
+
+ 2095bc84-2ef2-49e6-853a-5ddc228e3fc9
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/Velentr.Font/lib/x64/freetype6.dll b/Velentr.Font/lib/x64/freetype6.dll
deleted file mode 100644
index 18784d5..0000000
Binary files a/Velentr.Font/lib/x64/freetype6.dll and /dev/null differ
diff --git a/Velentr.Font/lib/x86/freetype6.dll b/Velentr.Font/lib/x86/freetype6.dll
deleted file mode 100644
index d91bebe..0000000
Binary files a/Velentr.Font/lib/x86/freetype6.dll and /dev/null differ