Show More
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:
FNA/src/Graphics/SpriteFont.cs
318 lines | 7.1 KiB | text/x-csharp | CSharpLexer
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2020 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
#endregion
namespace Microsoft.Xna.Framework.Graphics
{
// http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritefont.aspx
public sealed class SpriteFont
{
#region Public Properties
public ReadOnlyCollection<char> Characters
{
get;
private set;
}
public char? DefaultCharacter
{
get;
set;
}
public int LineSpacing
{
get
{
return lineSpacing;
}
set
{
lineSpacing = value;
}
}
public float Spacing
{
get
{
return spacing;
}
set
{
spacing = value;
}
}
#endregion
#region Internal Variables
/* I've had a bunch of games use reflection on SpriteFont to get
* this data. Keep these names as they are for XNA4 accuracy!
* -flibit
*/
internal Texture2D textureValue;
internal List<Rectangle> glyphData;
internal List<Rectangle> croppingData;
internal List<Vector3> kerning;
internal List<char> characterMap;
/* If, by chance, you're seeing this and thinking about using
* reflection to access the fields:
* Don't.
* To date, one (1) game is using the fields directly,
* even though the properties are publicly accessible.
* Not even FNA uses the fields directly.
* -ade
*/
internal int lineSpacing;
internal float spacing;
/* This is not a part of the spec as far as we know, but we
* added this because it's WAY faster than going to characterMap
* and calling IndexOf on each character.
*/
internal Dictionary<char, int> characterIndexMap;
#endregion
#region Internal Constructor
internal SpriteFont(
Texture2D texture,
List<Rectangle> glyphBounds,
List<Rectangle> cropping,
List<char> characters,
int lineSpacing,
float spacing,
List<Vector3> kerningData,
char? defaultCharacter
) {
Characters = new ReadOnlyCollection<char>(characters.ToArray());
DefaultCharacter = defaultCharacter;
LineSpacing = lineSpacing;
Spacing = spacing;
textureValue = texture;
glyphData = glyphBounds;
croppingData = cropping;
kerning = kerningData;
characterMap = characters;
characterIndexMap = new Dictionary<char, int>(characters.Count);
for (int i = 0; i < characters.Count; i += 1)
{
characterIndexMap[characters[i]] = i;
}
}
#endregion
#region Public MeasureString Methods
public Vector2 MeasureString(string text)
{
/* FIXME: This method is a duplicate of MeasureString(StringBuilder)!
* The only difference is how we iterate through the string.
* -flibit
*/
if (text == null)
{
throw new ArgumentNullException("text");
}
if (text.Length == 0)
{
return Vector2.Zero;
}
// FIXME: This needs an accuracy check! -flibit
Vector2 result = Vector2.Zero;
float curLineWidth = 0.0f;
float finalLineHeight = LineSpacing;
bool firstInLine = true;
foreach (char c in text)
{
// Special characters
if (c == '\r')
{
continue;
}
if (c == '\n')
{
result.X = Math.Max(result.X, curLineWidth);
result.Y += LineSpacing;
curLineWidth = 0.0f;
finalLineHeight = LineSpacing;
firstInLine = true;
continue;
}
/* Get the List index from the character map, defaulting to the
* DefaultCharacter if it's set.
*/
int index;
if (!characterIndexMap.TryGetValue(c, out index))
{
if (!DefaultCharacter.HasValue)
{
throw new ArgumentException(
"Text contains characters that cannot be" +
" resolved by this SpriteFont.",
"text"
);
}
index = characterIndexMap[DefaultCharacter.Value];
}
/* For the first character in a line, always push the width
* rightward, even if the kerning pushes the character to the
* left.
*/
Vector3 cKern = kerning[index];
if (firstInLine)
{
curLineWidth += Math.Abs(cKern.X);
firstInLine = false;
}
else
{
curLineWidth += Spacing + cKern.X;
}
/* Add the character width and right-side bearing to the line
* width.
*/
curLineWidth += cKern.Y + cKern.Z;
/* If a character is taller than the default line height,
* increase the height to that of the line's tallest character.
*/
int cCropHeight = croppingData[index].Height;
if (cCropHeight > finalLineHeight)
{
finalLineHeight = cCropHeight;
}
}
// Calculate the final width/height of the text box
result.X = Math.Max(result.X, curLineWidth);
result.Y += finalLineHeight;
return result;
}
public Vector2 MeasureString(StringBuilder text)
{
/* FIXME: This method is a duplicate of MeasureString(string)!
* The only difference is how we iterate through the StringBuilder.
* We don't use ToString() since it generates garbage.
* -flibit
*/
if (text == null)
{
throw new ArgumentNullException("text");
}
if (text.Length == 0)
{
return Vector2.Zero;
}
// FIXME: This needs an accuracy check! -flibit
Vector2 result = Vector2.Zero;
float curLineWidth = 0.0f;
float finalLineHeight = LineSpacing;
bool firstInLine = true;
for (int i = 0; i < text.Length; i += 1)
{
char c = text[i];
// Special characters
if (c == '\r')
{
continue;
}
if (c == '\n')
{
result.X = Math.Max(result.X, curLineWidth);
result.Y += LineSpacing;
curLineWidth = 0.0f;
finalLineHeight = LineSpacing;
firstInLine = true;
continue;
}
/* Get the List index from the character map, defaulting to the
* DefaultCharacter if it's set.
*/
int index;
if (!characterIndexMap.TryGetValue(c, out index))
{
if (!DefaultCharacter.HasValue)
{
throw new ArgumentException(
"Text contains characters that cannot be" +
" resolved by this SpriteFont.",
"text"
);
}
index = characterIndexMap[DefaultCharacter.Value];
}
/* For the first character in a line, always push the width
* rightward, even if the kerning pushes the character to the
* left.
*/
Vector3 cKern = kerning[index];
if (firstInLine)
{
curLineWidth += Math.Abs(cKern.X);
firstInLine = false;
}
else
{
curLineWidth += Spacing + cKern.X;
}
/* Add the character width and right-side bearing to the line
* width.
*/
curLineWidth += cKern.Y + cKern.Z;
/* If a character is taller than the default line height,
* increase the height to that of the line's tallest character.
*/
int cCropHeight = croppingData[index].Height;
if (cCropHeight > finalLineHeight)
{
finalLineHeight = cCropHeight;
}
}
// Calculate the final width/height of the text box
result.X = Math.Max(result.X, curLineWidth);
result.Y += finalLineHeight;
return result;
}
#endregion
}
}