Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines Starting to add additional timers for different stages of the process of updating in order to get more insight into what is slowing it down. The update takes 9ms, which is much longer than it used to. Engine-specific timers are coming later.
File last commit:
Show/Diff file:
Action:
SpriteFontPlus/deps/BMFontToSpriteFont/Cyotek.Drawing.Bitmap/BitmapFont.cs
756 lines | 23.9 KiB | text/x-csharp | CSharpLexer
/* AngelCode bitmap font parsing using C#
* http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp
*
* Copyright © 2012-2015 Cyotek Ltd.
*
* Licensed under the MIT License. See license.txt for the full text.
*/
// Some documentation derived from the BMFont file format specification
// http://www.angelcode.com/products/bmfont/doc/file_format.html
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Xml;
#if !XENKO
using Point = Microsoft.Xna.Framework.Point;
using Rectangle = Microsoft.Xna.Framework.Rectangle;
#else
using Point = Xenko.Core.Mathematics.Point;
using Rectangle = Xenko.Core.Mathematics.Rectangle;
#endif
namespace Cyotek.Drawing.BitmapFont
{
/// <summary>
/// A bitmap font.
/// </summary>
/// <seealso cref="T:System.Collections.Generic.IEnumerable{Cyotek.Drawing.BitmapFont.Character}" />
internal class BitmapFont : IEnumerable<Character>
{
#region Constants
/// <summary>
/// When used with <see cref="MeasureFont(string,double)" />, specifies that no wrapping should occur.
/// </summary>
public const int NoMaxWidth = -1;
#endregion
#region Properties
/// <summary>
/// Gets or sets the alpha channel.
/// </summary>
/// <value>
/// The alpha channel.
/// </value>
/// <remarks>
/// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the
/// outline, 3 if its set to zero, and 4 if its set to one.
/// </remarks>
public int AlphaChannel { get; set; }
/// <summary>
/// Gets or sets the number of pixels from the absolute top of the line to the base of the characters.
/// </summary>
/// <value>
/// The number of pixels from the absolute top of the line to the base of the characters.
/// </value>
public int BaseHeight { get; set; }
/// <summary>
/// Gets or sets the blue channel.
/// </summary>
/// <value>
/// The blue channel.
/// </value>
/// <remarks>
/// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the
/// outline, 3 if its set to zero, and 4 if its set to one.
/// </remarks>
public int BlueChannel { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the font is bold.
/// </summary>
/// <value>
/// <c>true</c> if the font is bold, otherwise <c>false</c>.
/// </value>
public bool Bold { get; set; }
/// <summary>
/// Gets or sets the characters that comprise the font.
/// </summary>
/// <value>
/// The characters that comprise the font.
/// </value>
public IDictionary<char, Character> Characters { get; set; }
/// <summary>
/// Gets or sets the name of the OEM charset used.
/// </summary>
/// <value>
/// The name of the OEM charset used (when not unicode).
/// </value>
public string Charset { get; set; }
/// <summary>
/// Gets or sets the name of the true type font.
/// </summary>
/// <value>
/// The font family name.
/// </value>
public string FamilyName { get; set; }
/// <summary>
/// Gets or sets the size of the font.
/// </summary>
/// <value>
/// The size of the font.
/// </value>
public int FontSize { get; set; }
/// <summary>
/// Gets or sets the green channel.
/// </summary>
/// <value>
/// The green channel.
/// </value>
/// <remarks>
/// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the
/// outline, 3 if its set to zero, and 4 if its set to one.
/// </remarks>
public int GreenChannel { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the font is italic.
/// </summary>
/// <value>
/// <c>true</c> if the font is italic, otherwise <c>false</c>.
/// </value>
public bool Italic { get; set; }
/// <summary>
/// Indexer to get items within this collection using array index syntax.
/// </summary>
/// <param name="character">The character.</param>
/// <returns>
/// The indexed item.
/// </returns>
public Character this[char character] => Characters[character];
/// <summary>
/// Gets or sets the character kernings for the font.
/// </summary>
/// <value>
/// The character kernings for the font.
/// </value>
public IDictionary<Kerning, int> Kernings { get; set; }
/// <summary>
/// Gets or sets the distance in pixels between each line of text.
/// </summary>
/// <value>
/// The distance in pixels between each line of text.
/// </value>
public int LineHeight { get; set; }
/// <summary>
/// Gets or sets the outline thickness for the characters.
/// </summary>
/// <value>
/// The outline thickness for the characters.
/// </value>
public int OutlineSize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the monochrome characters have been packed into each of the texture
/// channels.
/// </summary>
/// <value>
/// <c>true</c> if the characters are packed, otherwise <c>false</c>.
/// </value>
/// <remarks>
/// When packed, the <see cref="AlphaChannel" /> property describes what is stored in each channel.
/// </remarks>
public bool Packed { get; set; }
/// <summary>
/// Gets or sets the padding for each character.
/// </summary>
/// <value>
/// The padding for each character.
/// </value>
public Padding Padding { get; set; }
/// <summary>
/// Gets or sets the texture pages for the font.
/// </summary>
/// <value>
/// The pages.
/// </value>
public Page[] Pages { get; set; }
/// <summary>
/// Gets or sets the red channel.
/// </summary>
/// <value>
/// The red channel.
/// </value>
/// <remarks>
/// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the
/// outline, 3 if its set to zero, and 4 if its set to one.
/// </remarks>
public int RedChannel { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the font is smoothed.
/// </summary>
/// <value>
/// <c>true</c> if the font is smoothed, otherwise <c>false</c>.
/// </value>
public bool Smoothed { get; set; }
/// <summary>
/// Gets or sets the spacing for each character.
/// </summary>
/// <value>
/// The spacing for each character.
/// </value>
public Point Spacing { get; set; }
/// <summary>
/// Gets or sets the font height stretch.
/// </summary>
/// <value>
/// The font height stretch.
/// </value>
/// <remarks>100% means no stretch.</remarks>
public int StretchedHeight { get; set; }
/// <summary>
/// Gets or sets the level of super sampling used by the font.
/// </summary>
/// <value>
/// The super sampling level of the font.
/// </value>
/// <remarks>A value of 1 indicates no super sampling is in use.</remarks>
public int SuperSampling { get; set; }
/// <summary>
/// Gets or sets the size of the texture images used by the font.
/// </summary>
/// <value>
/// The size of the texture.
/// </value>
public Size TextureSize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the font is unicode.
/// </summary>
/// <value>
/// <c>true</c> if the font is unicode, otherwise <c>false</c>.
/// </value>
public bool Unicode { get; set; }
#endregion
#region Methods
/// <summary>
/// Gets the kerning for the specified character combination.
/// </summary>
/// <param name="previous">The previous character.</param>
/// <param name="current">The current character.</param>
/// <returns>
/// The spacing between the specified characters.
/// </returns>
public int GetKerning(char previous, char current)
{
Kerning key;
int result;
key = new Kerning(previous, current, 0);
if (!Kernings.TryGetValue(key, out result)) result = 0;
return result;
}
/// <summary>
/// Load font information from the specified <see cref="Stream" />.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
/// <exception cref="ArgumentException">
/// Thrown when one or more arguments have unsupported or
/// illegal values.
/// </exception>
/// <exception cref="InvalidDataException">Thrown when an Invalid Data error condition occurs.</exception>
/// <param name="stream">The stream to load.</param>
public virtual void Load(Stream stream)
{
byte[] buffer;
string header;
if (stream == null) throw new ArgumentNullException("stream");
if (!stream.CanSeek)
throw new ArgumentException("Stream must be seekable in order to determine file format.", "stream");
// read the first five bytes so we can try and work out what the format is
// then reset the position so the format loaders can work
buffer = new byte[5];
stream.Read(buffer, 0, 5);
stream.Seek(0, SeekOrigin.Begin);
header = Encoding.ASCII.GetString(buffer);
switch (header)
{
case "info ":
LoadText(stream);
break;
case "<?xml":
LoadXml(stream);
break;
default:
throw new InvalidDataException("Unknown file format.");
}
}
/// <summary>
/// Load font information from the specified file.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
/// <exception cref="FileNotFoundException">Thrown when the requested file is not present.</exception>
/// <param name="fileName">The file name to load.</param>
public void Load(string fileName)
{
if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName");
if (!File.Exists(fileName))
throw new FileNotFoundException(string.Format("Cannot find file '{0}'.", fileName), fileName);
using (Stream stream = File.OpenRead(fileName))
{
Load(stream);
}
BitmapFontLoader.QualifyResourcePaths(this, Path.GetDirectoryName(fileName));
}
/// <summary>
/// Loads font information from the specified string.
/// </summary>
/// <param name="text">String containing the font to load.</param>
/// <remarks>The source data must be in BMFont text format.</remarks>
public void LoadText(string text)
{
using (var reader = new StringReader(text))
{
LoadText(reader);
}
}
/// <summary>
/// Loads font information from the specified stream.
/// </summary>
/// <remarks>
/// The source data must be in BMFont text format.
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
/// <param name="stream">The stream containing the font to load.</param>
public void LoadText(Stream stream)
{
if (stream == null) throw new ArgumentNullException("stream");
using (TextReader reader = new StreamReader(stream))
{
LoadText(reader);
}
}
/// <summary>
/// Loads font information from the specified <see cref="TextReader" />.
/// </summary>
/// <remarks>
/// The source data must be in BMFont text format.
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
/// <param name="reader">The <strong>TextReader</strong> used to feed the data into the font.</param>
public virtual void LoadText(TextReader reader)
{
IDictionary<int, Page> pageData;
IDictionary<Kerning, int> kerningDictionary;
IDictionary<char, Character> charDictionary;
string line;
if (reader == null) throw new ArgumentNullException("reader");
pageData = new SortedDictionary<int, Page>();
kerningDictionary = new Dictionary<Kerning, int>();
charDictionary = new Dictionary<char, Character>();
do
{
line = reader.ReadLine();
if (line != null)
{
string[] parts;
parts = BitmapFontLoader.Split(line, ' ');
if (parts.Length != 0)
switch (parts[0])
{
case "info":
FamilyName = BitmapFontLoader.GetNamedString(parts, "face");
FontSize = BitmapFontLoader.GetNamedInt(parts, "size");
Bold = BitmapFontLoader.GetNamedBool(parts, "bold");
Italic = BitmapFontLoader.GetNamedBool(parts, "italic");
Charset = BitmapFontLoader.GetNamedString(parts, "charset");
Unicode = BitmapFontLoader.GetNamedBool(parts, "unicode");
StretchedHeight = BitmapFontLoader.GetNamedInt(parts, "stretchH");
Smoothed = BitmapFontLoader.GetNamedBool(parts, "smooth");
SuperSampling = BitmapFontLoader.GetNamedInt(parts, "aa");
Padding = BitmapFontLoader.ParsePadding(
BitmapFontLoader.GetNamedString(parts, "padding"));
Spacing = BitmapFontLoader.ParsePoint(
BitmapFontLoader.GetNamedString(parts, "spacing"));
OutlineSize = BitmapFontLoader.GetNamedInt(parts, "outline");
break;
case "common":
LineHeight = BitmapFontLoader.GetNamedInt(parts, "lineHeight");
BaseHeight = BitmapFontLoader.GetNamedInt(parts, "base");
TextureSize = new Size(BitmapFontLoader.GetNamedInt(parts, "scaleW"),
BitmapFontLoader.GetNamedInt(parts, "scaleH"));
Packed = BitmapFontLoader.GetNamedBool(parts, "packed");
AlphaChannel = BitmapFontLoader.GetNamedInt(parts, "alphaChnl");
RedChannel = BitmapFontLoader.GetNamedInt(parts, "redChnl");
GreenChannel = BitmapFontLoader.GetNamedInt(parts, "greenChnl");
BlueChannel = BitmapFontLoader.GetNamedInt(parts, "blueChnl");
break;
case "page":
int id;
string name;
id = BitmapFontLoader.GetNamedInt(parts, "id");
name = BitmapFontLoader.GetNamedString(parts, "file");
pageData.Add(id, new Page(id, name));
break;
case "char":
Character charData;
charData = new Character
{
Char = (char)BitmapFontLoader.GetNamedInt(parts, "id"),
Bounds =
new Rectangle(BitmapFontLoader.GetNamedInt(parts, "x"),
BitmapFontLoader.GetNamedInt(parts, "y"),
BitmapFontLoader.GetNamedInt(parts, "width"),
BitmapFontLoader.GetNamedInt(parts, "height")),
Offset =
new Point(BitmapFontLoader.GetNamedInt(parts, "xoffset"),
BitmapFontLoader.GetNamedInt(parts, "yoffset")),
XAdvance = BitmapFontLoader.GetNamedInt(parts, "xadvance"),
TexturePage = BitmapFontLoader.GetNamedInt(parts, "page"),
Channel = BitmapFontLoader.GetNamedInt(parts, "chnl")
};
charDictionary.Add(charData.Char, charData);
break;
case "kerning":
Kerning key;
key = new Kerning((char)BitmapFontLoader.GetNamedInt(parts, "first"),
(char)BitmapFontLoader.GetNamedInt(parts, "second"),
BitmapFontLoader.GetNamedInt(parts, "amount"));
if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount);
break;
}
}
} while (line != null);
Pages = BitmapFontLoader.ToArray(pageData.Values);
Characters = charDictionary;
Kernings = kerningDictionary;
}
/// <summary>
/// Loads font information from the specified string.
/// </summary>
/// <param name="xml">String containing the font to load.</param>
/// <remarks>The source data must be in BMFont XML format.</remarks>
public void LoadXml(string xml)
{
using (var reader = new StringReader(xml))
{
LoadXml(reader);
}
}
/// <summary>
/// Loads font information from the specified <see cref="TextReader" />.
/// </summary>
/// <remarks>
/// The source data must be in BMFont XML format.
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
/// <param name="reader">The <strong>TextReader</strong> used to feed the data into the font.</param>
public virtual void LoadXml(TextReader reader)
{
XmlDocument document;
IDictionary<int, Page> pageData;
IDictionary<Kerning, int> kerningDictionary;
IDictionary<char, Character> charDictionary;
XmlNode root;
XmlNode properties;
if (reader == null) throw new ArgumentNullException("reader");
document = new XmlDocument();
pageData = new SortedDictionary<int, Page>();
kerningDictionary = new Dictionary<Kerning, int>();
charDictionary = new Dictionary<char, Character>();
document.Load(reader);
root = document.DocumentElement;
// load the basic attributes
properties = root.SelectSingleNode("info");
FamilyName = properties.Attributes["face"].Value;
FontSize = Convert.ToInt32(properties.Attributes["size"].Value);
Bold = Convert.ToInt32(properties.Attributes["bold"].Value) != 0;
Italic = Convert.ToInt32(properties.Attributes["italic"].Value) != 0;
Unicode = Convert.ToInt32(properties.Attributes["unicode"].Value) != 0;
StretchedHeight = Convert.ToInt32(properties.Attributes["stretchH"].Value);
Charset = properties.Attributes["charset"].Value;
Smoothed = Convert.ToInt32(properties.Attributes["smooth"].Value) != 0;
SuperSampling = Convert.ToInt32(properties.Attributes["aa"].Value);
Padding = BitmapFontLoader.ParsePadding(properties.Attributes["padding"].Value);
Spacing = BitmapFontLoader.ParsePoint(properties.Attributes["spacing"].Value);
OutlineSize = Convert.ToInt32(properties.Attributes["outline"].Value);
// common attributes
properties = root.SelectSingleNode("common");
BaseHeight = Convert.ToInt32(properties.Attributes["base"].Value);
LineHeight = Convert.ToInt32(properties.Attributes["lineHeight"].Value);
TextureSize = new Size(Convert.ToInt32(properties.Attributes["scaleW"].Value),
Convert.ToInt32(properties.Attributes["scaleH"].Value));
Packed = Convert.ToInt32(properties.Attributes["packed"].Value) != 0;
AlphaChannel = Convert.ToInt32(properties.Attributes["alphaChnl"].Value);
RedChannel = Convert.ToInt32(properties.Attributes["redChnl"].Value);
GreenChannel = Convert.ToInt32(properties.Attributes["greenChnl"].Value);
BlueChannel = Convert.ToInt32(properties.Attributes["blueChnl"].Value);
// load texture information
foreach (XmlNode node in root.SelectNodes("pages/page"))
{
Page page;
page = new Page();
page.Id = Convert.ToInt32(node.Attributes["id"].Value);
page.FileName = node.Attributes["file"].Value;
pageData.Add(page.Id, page);
}
Pages = BitmapFontLoader.ToArray(pageData.Values);
// load character information
foreach (XmlNode node in root.SelectNodes("chars/char"))
{
Character character;
character = new Character();
character.Char = (char)Convert.ToInt32(node.Attributes["id"].Value);
character.Bounds = new Rectangle(Convert.ToInt32(node.Attributes["x"].Value),
Convert.ToInt32(node.Attributes["y"].Value),
Convert.ToInt32(node.Attributes["width"].Value),
Convert.ToInt32(node.Attributes["height"].Value));
character.Offset = new Point(Convert.ToInt32(node.Attributes["xoffset"].Value),
Convert.ToInt32(node.Attributes["yoffset"].Value));
character.XAdvance = Convert.ToInt32(node.Attributes["xadvance"].Value);
character.TexturePage = Convert.ToInt32(node.Attributes["page"].Value);
character.Channel = Convert.ToInt32(node.Attributes["chnl"].Value);
charDictionary.Add(character.Char, character);
}
Characters = charDictionary;
// loading kerning information
foreach (XmlNode node in root.SelectNodes("kernings/kerning"))
{
Kerning key;
key = new Kerning((char)Convert.ToInt32(node.Attributes["first"].Value),
(char)Convert.ToInt32(node.Attributes["second"].Value),
Convert.ToInt32(node.Attributes["amount"].Value));
if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount);
}
Kernings = kerningDictionary;
}
/// <summary>
/// Loads font information from the specified stream.
/// </summary>
/// <remarks>
/// The source data must be in BMFont XML format.
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
/// <param name="stream">The stream containing the font to load.</param>
public void LoadXml(Stream stream)
{
if (stream == null) throw new ArgumentNullException("stream");
using (TextReader reader = new StreamReader(stream))
{
LoadXml(reader);
}
}
/// <summary>
/// Provides the size, in pixels, of the specified text when drawn with this font.
/// </summary>
/// <param name="text">The text to measure.</param>
/// <returns>
/// The <see cref="Size" />, in pixels, of <paramref name="text" /> drawn with this font.
/// </returns>
public Size MeasureFont(string text)
{
return MeasureFont(text, NoMaxWidth);
}
/// <summary>
/// Provides the size, in pixels, of the specified text when drawn with this font, automatically wrapping to keep
/// within the specified with.
/// </summary>
/// <param name="text">The text to measure.</param>
/// <param name="maxWidth">The maximum width.</param>
/// <returns>
/// The <see cref="Size" />, in pixels, of <paramref name="text" /> drawn with this font.
/// </returns>
/// <remarks>
/// The MeasureText method uses the <paramref name="maxWidth" /> parameter to automatically wrap when determining
/// text size.
/// </remarks>
public Size MeasureFont(string text, double maxWidth)
{
Size result;
if (!string.IsNullOrEmpty(text))
{
char previousCharacter;
int currentLineWidth;
int currentLineHeight;
int blockWidth;
int blockHeight;
int length;
List<int> lineHeights;
length = text.Length;
previousCharacter = ' ';
currentLineWidth = 0;
currentLineHeight = LineHeight;
blockWidth = 0;
blockHeight = 0;
lineHeights = new List<int>();
for (var i = 0; i < length; i++)
{
char character;
character = text[i];
if (character == '\n' || character == '\r')
{
if (character == '\n' || i + 1 == length || text[i + 1] != '\n')
{
lineHeights.Add(currentLineHeight);
blockWidth = Math.Max(blockWidth, currentLineWidth);
currentLineWidth = 0;
currentLineHeight = LineHeight;
}
}
else
{
Character data;
int width;
data = this[character];
width = data.XAdvance + GetKerning(previousCharacter, character);
if (maxWidth != NoMaxWidth && currentLineWidth + width >= maxWidth)
{
lineHeights.Add(currentLineHeight);
blockWidth = Math.Max(blockWidth, currentLineWidth);
currentLineWidth = 0;
currentLineHeight = LineHeight;
}
currentLineWidth += width;
currentLineHeight = Math.Max(currentLineHeight, data.Bounds.Height + data.Offset.Y);
previousCharacter = character;
}
}
// finish off the current line if required
if (currentLineHeight != 0) lineHeights.Add(currentLineHeight);
// reduce any lines other than the last back to the base
for (var i = 0; i < lineHeights.Count - 1; i++) lineHeights[i] = LineHeight;
// calculate the final block height
foreach (var lineHeight in lineHeights) blockHeight += lineHeight;
result = new Size(Math.Max(currentLineWidth, blockWidth), blockHeight);
}
else
{
result = Size.Empty;
}
return result;
}
#endregion
#region IEnumerable<Character> Interface
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through
/// the collection.
/// </returns>
/// <seealso cref="M:System.Collections.Generic.IEnumerable{Cyotek.Drawing.BitmapFont.Character}.GetEnumerator()" />
public IEnumerator<Character> GetEnumerator()
{
foreach (var pair in Characters) yield return pair.Value;
}
/// <summary>
/// Gets the enumerator.
/// </summary>
/// <returns>
/// The enumerator.
/// </returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}