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