Content Pipeline:
- implemented FontDescriptionProcessor - began implementing FontDescriptionImporter
This commit is contained in:
parent
01cac2c358
commit
72ee0fd837
@ -106,6 +106,7 @@
|
|||||||
<Compile Include="Graphics\VertexContent.cs" />
|
<Compile Include="Graphics\VertexContent.cs" />
|
||||||
<Compile Include="IContentImporter.cs" />
|
<Compile Include="IContentImporter.cs" />
|
||||||
<Compile Include="IContentProcessor.cs" />
|
<Compile Include="IContentProcessor.cs" />
|
||||||
|
<Compile Include="Importer\FontDescriptionImporter.cs" />
|
||||||
<Compile Include="Importer\TextureImporter.cs" />
|
<Compile Include="Importer\TextureImporter.cs" />
|
||||||
<Compile Include="Importer\WavImporter.cs" />
|
<Compile Include="Importer\WavImporter.cs" />
|
||||||
<Compile Include="Importer\XmlImporter.cs" />
|
<Compile Include="Importer\XmlImporter.cs" />
|
||||||
|
@ -12,6 +12,7 @@ using System.Text;
|
|||||||
|
|
||||||
namespace ANX.Framework.Content.Pipeline.Graphics
|
namespace ANX.Framework.Content.Pipeline.Graphics
|
||||||
{
|
{
|
||||||
|
[Flags]
|
||||||
public enum FontDescriptionStyle
|
public enum FontDescriptionStyle
|
||||||
{
|
{
|
||||||
Bold,
|
Bold,
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using ANX.Framework.Content.Pipeline.Graphics;
|
||||||
|
using ANX.Framework.NonXNA.Development;
|
||||||
|
|
||||||
|
namespace ANX.Framework.Content.Pipeline.Importer
|
||||||
|
{
|
||||||
|
/// <summary>Provides methods for reading .spritefont files for use in the Content Pipeline.</summary>
|
||||||
|
[PercentageComplete(20)]
|
||||||
|
[Developer("SilentWarrior/Eagle Eye Studios")]
|
||||||
|
[TestState(TestStateAttribute.TestState.Untested)]
|
||||||
|
[ContentImporter("Spritefont", ".spritefont", DefaultProcessor = "FontDescriptionProcessor")]
|
||||||
|
public class FontDescriptionImporter : ContentImporter<FontDescription>
|
||||||
|
{
|
||||||
|
/// <summary>Called by the Framework when importing a .spritefont file to be used as a game asset. This is the method called by the Framework when an asset is to be imported into an object that can be recognized by the Content Pipeline.</summary>
|
||||||
|
/// <param name="filename">Name of a game asset file.</param>
|
||||||
|
/// <param name="context">Contains information for importing a game asset, such as a logger interface.</param>
|
||||||
|
public override FontDescription Import(string filename, ContentImporterContext context)
|
||||||
|
{
|
||||||
|
FontDescription fontDescription = null;
|
||||||
|
using (XmlReader xmlReader = XmlReader.Create(filename))
|
||||||
|
{
|
||||||
|
fontDescription = Deserialize(xmlReader, filename);
|
||||||
|
}
|
||||||
|
fontDescription.Identity = new ContentIdentity(new FileInfo(filename).FullName, "FontDescriptionImporter");
|
||||||
|
return fontDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FontDescription Deserialize(XmlReader reader, string filename)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,15 @@
|
|||||||
#region Using Statements
|
#region Using Statements
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Drawing.Text;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using ANX.Framework.Content.Pipeline.Graphics;
|
using ANX.Framework.Content.Pipeline.Graphics;
|
||||||
|
using ANX.Framework.NonXNA.Development;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -13,13 +19,285 @@ using ANX.Framework.Content.Pipeline.Graphics;
|
|||||||
|
|
||||||
namespace ANX.Framework.Content.Pipeline.Processors
|
namespace ANX.Framework.Content.Pipeline.Processors
|
||||||
{
|
{
|
||||||
|
[PercentageComplete(90)]
|
||||||
|
[Developer("SilentWarrior/Eagle Eye Studios")]
|
||||||
|
[TestState(TestStateAttribute.TestState.Untested)] //due to missing importer
|
||||||
[ContentProcessor]
|
[ContentProcessor]
|
||||||
public class FontDescriptionProcessor : ContentProcessor<FontDescription, SpriteFontContent>
|
public class FontDescriptionProcessor : ContentProcessor<FontDescription, SpriteFontContent>
|
||||||
{
|
{
|
||||||
|
|
||||||
public override SpriteFontContent Process(FontDescription input, ContentProcessorContext context)
|
public override SpriteFontContent Process(FontDescription input, ContentProcessorContext context)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("input");
|
||||||
|
}
|
||||||
|
var list = new List<char>(input.Characters);
|
||||||
|
if (input.DefaultCharacter.HasValue && !input.Characters.Contains(input.DefaultCharacter.Value))
|
||||||
|
{
|
||||||
|
list.Add(input.DefaultCharacter.Value);
|
||||||
|
}
|
||||||
|
list.Sort();
|
||||||
|
var spriteFontContent = new SpriteFontContent();
|
||||||
|
|
||||||
|
// Build up a list of all the glyphs to be output.
|
||||||
|
var bitmaps = new List<Bitmap>();
|
||||||
|
var xPositions = new List<int>();
|
||||||
|
var yPositions = new List<int>();
|
||||||
|
|
||||||
|
var globalBitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
|
||||||
|
System.Drawing.Graphics globalGraphics = System.Drawing.Graphics.FromImage(globalBitmap);
|
||||||
|
var font = new Font(input.FontName, input.Size, XnaFontStyleToGdiFontStyle(input.Style));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const int padding = 8;
|
||||||
|
|
||||||
|
int width = padding;
|
||||||
|
int height = padding;
|
||||||
|
int lineWidth = padding;
|
||||||
|
int lineHeight = padding;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// Rasterize each character in turn,
|
||||||
|
// and add it to the output list.
|
||||||
|
foreach (char ch in input.Characters)
|
||||||
|
{
|
||||||
|
Bitmap bitmap = RasterizeCharacter(globalGraphics, ch, true, font);
|
||||||
|
|
||||||
|
bitmaps.Add(bitmap);
|
||||||
|
|
||||||
|
xPositions.Add(lineWidth);
|
||||||
|
yPositions.Add(height);
|
||||||
|
|
||||||
|
lineWidth += bitmap.Width + padding;
|
||||||
|
lineHeight = Math.Max(lineHeight, bitmap.Height + padding);
|
||||||
|
|
||||||
|
// Output 16 glyphs per line, then wrap to the next line.
|
||||||
|
if ((++count == 16) || (ch == input.Characters.Count - 1))
|
||||||
|
{
|
||||||
|
width = Math.Max(width, lineWidth);
|
||||||
|
height += lineHeight;
|
||||||
|
lineWidth = padding;
|
||||||
|
lineHeight = padding;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var bitmap = new Bitmap(width, height,
|
||||||
|
PixelFormat.Format32bppArgb))
|
||||||
|
{
|
||||||
|
// Arrage all the glyphs onto a single larger bitmap.
|
||||||
|
using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
|
||||||
|
{
|
||||||
|
graphics.Clear(System.Drawing.Color.Magenta);
|
||||||
|
graphics.CompositingMode = CompositingMode.SourceCopy;
|
||||||
|
|
||||||
|
for (int i = 0; i < bitmaps.Count; i++)
|
||||||
|
{
|
||||||
|
spriteFontContent.Cropping.Add(new Rectangle(xPositions[i], yPositions[i], bitmaps[i].Width,
|
||||||
|
bitmaps[i].Height));
|
||||||
|
graphics.DrawImage(bitmaps[i], xPositions[i],
|
||||||
|
yPositions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.Flush();
|
||||||
|
}
|
||||||
|
spriteFontContent.Texture = ConvertBitmap(bitmap);
|
||||||
|
spriteFontContent.CharacterMap = input.Characters.ToList();
|
||||||
|
spriteFontContent.DefaultCharacter = input.DefaultCharacter;
|
||||||
|
spriteFontContent.Glyphs = CreateOutputGlyphs();
|
||||||
|
for (int i = 0; i < input.Characters.Count; i++)
|
||||||
|
{
|
||||||
|
Vector3 value = spriteFontContent.Kerning[i];
|
||||||
|
if (!input.UseKerning)
|
||||||
|
{
|
||||||
|
value.Y = spriteFontContent.Cropping[i].Width;
|
||||||
|
}
|
||||||
|
if (!input.UseKerning)
|
||||||
|
{
|
||||||
|
value.X = 0f;
|
||||||
|
value.Z = 0f;
|
||||||
|
}
|
||||||
|
spriteFontContent.Kerning[i] = value;
|
||||||
|
}
|
||||||
|
spriteFontContent.LineSpacing = (int) Math.Ceiling(font.GetHeight());
|
||||||
|
spriteFontContent.Spacing = input.Spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Clean up temporary objects.
|
||||||
|
foreach (Bitmap bitmap in bitmaps)
|
||||||
|
bitmap.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return spriteFontContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region RasterizeCharacter
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper for rendering out a single font character
|
||||||
|
/// into a System.Drawing bitmap.
|
||||||
|
/// </summary>
|
||||||
|
private static Bitmap RasterizeCharacter(System.Drawing.Graphics globalGraphics, char ch, bool antiAliasing,
|
||||||
|
Font font)
|
||||||
|
{
|
||||||
|
string text = ch.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
SizeF size = globalGraphics.MeasureString(text, font);
|
||||||
|
|
||||||
|
var width = (int) Math.Ceiling(size.Width);
|
||||||
|
var height = (int) Math.Ceiling(size.Height);
|
||||||
|
|
||||||
|
var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
||||||
|
|
||||||
|
using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
|
||||||
|
{
|
||||||
|
graphics.TextRenderingHint = antiAliasing
|
||||||
|
? TextRenderingHint.AntiAliasGridFit
|
||||||
|
: TextRenderingHint.SingleBitPerPixelGridFit;
|
||||||
|
|
||||||
|
graphics.Clear(System.Drawing.Color.Transparent);
|
||||||
|
|
||||||
|
using (Brush brush = new SolidBrush(System.Drawing.Color.White))
|
||||||
|
using (var format = new StringFormat())
|
||||||
|
{
|
||||||
|
format.Alignment = StringAlignment.Near;
|
||||||
|
format.LineAlignment = StringAlignment.Near;
|
||||||
|
|
||||||
|
graphics.DrawString(text, font, brush, 0, 0, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CropCharacter(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region CropCharacter
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper for cropping ununsed space from the sides of a bitmap.
|
||||||
|
/// </summary>
|
||||||
|
private static Bitmap CropCharacter(Bitmap bitmap)
|
||||||
|
{
|
||||||
|
int cropLeft = 0;
|
||||||
|
int cropRight = bitmap.Width - 1;
|
||||||
|
|
||||||
|
// Remove unused space from the left.
|
||||||
|
while ((cropLeft < cropRight) && (BitmapEmpty(bitmap, cropLeft)))
|
||||||
|
cropLeft++;
|
||||||
|
|
||||||
|
// Remove unused space from the right.
|
||||||
|
while ((cropRight > cropLeft) && (BitmapEmpty(bitmap, cropRight)))
|
||||||
|
cropRight--;
|
||||||
|
|
||||||
|
// Don't crop if that would reduce the glyph down to nothing at all!
|
||||||
|
if (cropLeft == cropRight)
|
||||||
|
return bitmap;
|
||||||
|
|
||||||
|
// Add some padding back in.
|
||||||
|
cropLeft = Math.Max(cropLeft - 1, 0);
|
||||||
|
cropRight = Math.Min(cropRight + 1, bitmap.Width - 1);
|
||||||
|
|
||||||
|
int width = cropRight - cropLeft + 1;
|
||||||
|
|
||||||
|
// Crop the glyph.
|
||||||
|
var croppedBitmap = new Bitmap(width, bitmap.Height, bitmap.PixelFormat);
|
||||||
|
|
||||||
|
using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(croppedBitmap))
|
||||||
|
{
|
||||||
|
graphics.CompositingMode = CompositingMode.SourceCopy;
|
||||||
|
|
||||||
|
graphics.DrawImage(bitmap, 0, 0,
|
||||||
|
new System.Drawing.Rectangle(cropLeft, 0, width, bitmap.Height),
|
||||||
|
GraphicsUnit.Pixel);
|
||||||
|
|
||||||
|
graphics.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap.Dispose();
|
||||||
|
|
||||||
|
return croppedBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region CreateOutputGlyphs
|
||||||
|
|
||||||
|
private static List<Rectangle> CreateOutputGlyphs()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ConvertBitmap
|
||||||
|
|
||||||
|
private Texture2DContent ConvertBitmap(Bitmap bitmap)
|
||||||
|
{
|
||||||
|
var bitmapContent = new PixelBitmapContent<Color>(bitmap.Width, bitmap.Height);
|
||||||
|
var destColor = new Color();
|
||||||
|
|
||||||
|
for (int x = bitmap.Width; x > 0; x--)
|
||||||
|
{
|
||||||
|
for (int y = bitmap.Height; y > 0; y--)
|
||||||
|
{
|
||||||
|
System.Drawing.Color sourceColor = bitmap.GetPixel(x, y);
|
||||||
|
|
||||||
|
destColor.R = sourceColor.R;
|
||||||
|
destColor.G = sourceColor.G;
|
||||||
|
destColor.B = sourceColor.B;
|
||||||
|
destColor.A = sourceColor.A;
|
||||||
|
|
||||||
|
bitmapContent.SetPixel(x, y, destColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureContent = new Texture2DContent();
|
||||||
|
textureContent.Faces[0] = new MipmapChain(bitmapContent);
|
||||||
|
|
||||||
|
return textureContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region FontDescriptionStyleConversion
|
||||||
|
|
||||||
|
private static FontStyle XnaFontStyleToGdiFontStyle(FontDescriptionStyle fontStyle)
|
||||||
|
{
|
||||||
|
var fontStyle2 = FontStyle.Regular;
|
||||||
|
if ((fontStyle & FontDescriptionStyle.Bold) == FontDescriptionStyle.Bold)
|
||||||
|
{
|
||||||
|
fontStyle2 |= FontStyle.Bold;
|
||||||
|
}
|
||||||
|
if ((fontStyle & FontDescriptionStyle.Italic) == FontDescriptionStyle.Italic)
|
||||||
|
{
|
||||||
|
fontStyle2 |= FontStyle.Italic;
|
||||||
|
}
|
||||||
|
return fontStyle2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region BitmapEmpty
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper for testing whether a column of a bitmap is entirely empty.
|
||||||
|
/// </summary>
|
||||||
|
private static bool BitmapEmpty(Bitmap bitmap, int x)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < bitmap.Height; y++)
|
||||||
|
{
|
||||||
|
if (bitmap.GetPixel(x, y).A != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,16 +11,16 @@ namespace ANX.Framework.Content.Pipeline.Processors
|
|||||||
public sealed class SpriteFontContent
|
public sealed class SpriteFontContent
|
||||||
{
|
{
|
||||||
[ContentSerializer(ElementName = "Texture", AllowNull = false)]
|
[ContentSerializer(ElementName = "Texture", AllowNull = false)]
|
||||||
internal Texture2DContent Texture { get; private set; }
|
internal Texture2DContent Texture { get; set; }
|
||||||
|
|
||||||
[ContentSerializer(ElementName = "Glyphs", AllowNull = false)]
|
[ContentSerializer(ElementName = "Glyphs", AllowNull = false)]
|
||||||
internal List<Rectangle> Glyphs { get; private set; }
|
internal List<Rectangle> Glyphs { get; set; }
|
||||||
|
|
||||||
[ContentSerializer(ElementName = "Cropping", AllowNull = false)]
|
[ContentSerializer(ElementName = "Cropping", AllowNull = false)]
|
||||||
internal List<Rectangle> Cropping { get; private set; }
|
internal List<Rectangle> Cropping { get; set; }
|
||||||
|
|
||||||
[ContentSerializer(ElementName = "CharacterMap", AllowNull = false)]
|
[ContentSerializer(ElementName = "CharacterMap", AllowNull = false)]
|
||||||
internal List<char> CharacterMap { get; private set; }
|
internal List<char> CharacterMap { get; set; }
|
||||||
|
|
||||||
[ContentSerializer(ElementName = "LineSpacing", AllowNull = false)]
|
[ContentSerializer(ElementName = "LineSpacing", AllowNull = false)]
|
||||||
internal int LineSpacing { get; set; }
|
internal int LineSpacing { get; set; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user