SND\AstrorEnales_cp 7350ca3d3a - Implemented the SkinnedEffect class (not tested yet, sample will come soon)
- Optimizations in the SpriteBatch and SpriteFont classes
2012-09-11 13:51:20 +00:00

272 lines
8.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using ANX.Framework.NonXNA.Development;
// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license
namespace ANX.Framework.Graphics
{
[PercentageComplete(100)]
[TestState(TestStateAttribute.TestState.Untested)]
[Developer("Glatzemann, AstrorEnales")]
public sealed class SpriteFont
{
#region Private
private Texture2D texture;
private Rectangle[] glyphs;
private Rectangle[] cropping;
private List<char> characterMap;
private Vector3[] kernings;
private char? defaultCharacter;
Vector2 topLeft;
Vector2 position;
#endregion
#region Public
public ReadOnlyCollection<Char> Characters { get; private set; }
public int LineSpacing { get; set; }
public float Spacing { get; set; }
public char? DefaultCharacter
{
get { return defaultCharacter; }
set
{
if (value.HasValue && this.characterMap.Contains(value.Value) == false)
throw new NotSupportedException("Character is not in used font");
this.defaultCharacter = value;
}
}
#endregion
#region Constructor
internal SpriteFont(Texture2D texture, List<Rectangle> glyphs, List<Rectangle> cropping, List<char> charMap,
int lineSpacing, float horizontalSpacing, List<Vector3> kerning, char? defaultCharacter)
{
this.texture = texture;
this.glyphs = glyphs.ToArray();
this.cropping = cropping.ToArray();
this.characterMap = charMap;
this.LineSpacing = lineSpacing;
this.Spacing = horizontalSpacing;
this.kernings = kerning.ToArray();
this.defaultCharacter = defaultCharacter;
Characters = new ReadOnlyCollection<char>(characterMap);
}
#endregion
#region MeasureString
public Vector2 MeasureString(string text)
{
if (text == null)
throw new ArgumentNullException("text");
return InternalMeasure(text);
}
public Vector2 MeasureString(StringBuilder text)
{
if (text == null)
throw new ArgumentNullException("text");
return InternalMeasure(text.ToString());
}
#endregion
#region DrawString
internal void DrawString(string text, SpriteBatch spriteBatch, Vector2 textPos, Color color, Vector2 scale,
Vector2 origin, float rotation, float layerDepth, SpriteEffects effects)
{
Matrix rotationMatrix;
Matrix.CreateRotationZ(rotation, out rotationMatrix);
Matrix translationMatrix;
Matrix.CreateTranslation(-origin.X * scale.X, -origin.Y * scale.Y, 0f, out translationMatrix);
Matrix transformation;
Matrix.Multiply(ref rotationMatrix, ref translationMatrix, out transformation);
topLeft.X = topLeft.Y = 0f;
int horizontalFlipModifier = 1;
float width = 0f;
bool flipVertically = (effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically;
bool flipHorizontally = (effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally;
if (flipHorizontally)
{
topLeft.X = width = InternalMeasure(text).X * scale.X;
horizontalFlipModifier = -1;
}
if (flipVertically)
topLeft.Y = (InternalMeasure(text).Y - LineSpacing) * scale.Y;
bool firstCharacterInLine = true;
for (int i = 0; i < text.Length; i++)
{
char currentCharacter = text[i];
if (currentCharacter == '\r')
continue;
if (currentCharacter == '\n')
{
firstCharacterInLine = true;
topLeft.X = width;
float factor = LineSpacing * scale.Y;
topLeft.Y += flipVertically ? -factor : factor;
continue;
}
int characterIndex = GetIndexForCharacter(currentCharacter);
Vector3 kerning = kernings[characterIndex];
Rectangle croppingRect = cropping[characterIndex];
if (firstCharacterInLine)
kerning.X = Math.Max(kerning.X, 0f);
else
topLeft.X += (Spacing * scale.X) * horizontalFlipModifier;
topLeft.X += (kerning.X * scale.X) * horizontalFlipModifier;
if (flipVertically)
croppingRect.Y = (LineSpacing - glyphs[characterIndex].Height) - croppingRect.Y;
if (flipHorizontally)
croppingRect.X -= croppingRect.Width;
position.X = topLeft.X + (croppingRect.X * scale.X);
position.Y = topLeft.Y + (croppingRect.Y * scale.Y);
Vector2 result;
Vector2.Transform(ref position, ref transformation, out result);
result.X += textPos.X;
result.Y += textPos.Y;
spriteBatch.DrawOptimizedText(texture, result, ref glyphs[characterIndex], ref color, rotation, scale,
effects, layerDepth);
firstCharacterInLine = false;
topLeft.X += ((kerning.Y + kerning.Z) * scale.X) * horizontalFlipModifier;
}
}
#endregion
#region DrawString
internal void DrawString(string text, SpriteBatch spriteBatch, Vector2 textPos, Color color)
{
topLeft.X = topLeft.Y = 0f;
bool firstCharacterInLine = true;
for (int i = 0; i < text.Length; i++)
{
char currentCharacter = text[i];
if (currentCharacter == '\r')
continue;
if (currentCharacter == '\n')
{
firstCharacterInLine = true;
topLeft.X = 0f;
topLeft.Y += LineSpacing;
continue;
}
int characterIndex = GetIndexForCharacter(currentCharacter);
Vector3 kerning = kernings[characterIndex];
Rectangle croppingRect = cropping[characterIndex];
if (firstCharacterInLine)
kerning.X = Math.Max(kerning.X, 0f);
else
topLeft.X += Spacing;
topLeft.X += kerning.X;
position.X = topLeft.X + croppingRect.X + textPos.X;
position.Y = topLeft.Y + croppingRect.Y + textPos.Y;
spriteBatch.DrawOptimizedText(texture, position, ref glyphs[characterIndex], ref color);
firstCharacterInLine = false;
topLeft.X += kerning.Y + kerning.Z;
}
}
#endregion
#region InternalMeasure
private Vector2 InternalMeasure(string text)
{
if (text.Length < 1)
return Vector2.Zero;
Vector2 size = Vector2.Zero;
size.Y = this.LineSpacing;
float maxWidth = 0f;
int currentCharacter = 0;
float z = 0f;
bool firstCharacterInLine = true;
for (int i = 0; i < text.Length; i++)
{
char currentChar = text[i];
if (currentChar == '\r')
continue;
if (currentChar == '\n')
{
size.X += Math.Max(z, 0f);
z = 0f;
maxWidth = Math.Max(size.X, maxWidth);
size = Vector2.Zero;
size.Y = LineSpacing;
firstCharacterInLine = true;
currentCharacter++;
continue;
}
int currentCharIndex = GetIndexForCharacter(currentChar);
Vector3 kerning = kernings[currentCharIndex];
if (firstCharacterInLine)
kerning.X = Math.Max(kerning.X, 0f);
else
size.X += this.Spacing + z;
size.X += kerning.X + kerning.Y;
z = kerning.Z;
size.Y = Math.Max(size.Y, (float)cropping[currentCharIndex].Height);
firstCharacterInLine = false;
}
size.Y += currentCharacter * LineSpacing;
size.X = Math.Max(Math.Max(z, 0) + size.X, maxWidth);
return size;
}
#endregion
#region GetIndexForCharacter
private int GetIndexForCharacter(char character)
{
int currentIndex = 0;
int upperBound = this.characterMap.Count - 1;
char testChar;
int searchPos;
while (currentIndex <= upperBound)
{
searchPos = currentIndex + ((upperBound - currentIndex) >> 1);
testChar = characterMap[searchPos];
if (testChar == character)
return searchPos;
else if (testChar > character)
upperBound = searchPos - 1;
else
currentIndex = searchPos + 1;
}
if (defaultCharacter.HasValue)
return GetIndexForCharacter(defaultCharacter.Value);
throw new ArgumentException("character not found");
}
#endregion
}
}