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="IContentImporter.cs" />
|
||||
<Compile Include="IContentProcessor.cs" />
|
||||
<Compile Include="Importer\FontDescriptionImporter.cs" />
|
||||
<Compile Include="Importer\TextureImporter.cs" />
|
||||
<Compile Include="Importer\WavImporter.cs" />
|
||||
<Compile Include="Importer\XmlImporter.cs" />
|
||||
|
@ -12,6 +12,7 @@ using System.Text;
|
||||
|
||||
namespace ANX.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
[Flags]
|
||||
public enum FontDescriptionStyle
|
||||
{
|
||||
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
|
||||
|
||||
using System;
|
||||
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.Text;
|
||||
using ANX.Framework.Content.Pipeline.Graphics;
|
||||
using ANX.Framework.NonXNA.Development;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -13,13 +19,285 @@ using ANX.Framework.Content.Pipeline.Graphics;
|
||||
|
||||
namespace ANX.Framework.Content.Pipeline.Processors
|
||||
{
|
||||
[PercentageComplete(90)]
|
||||
[Developer("SilentWarrior/Eagle Eye Studios")]
|
||||
[TestState(TestStateAttribute.TestState.Untested)] //due to missing importer
|
||||
[ContentProcessor]
|
||||
public class FontDescriptionProcessor : ContentProcessor<FontDescription, SpriteFontContent>
|
||||
{
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
[ContentSerializer(ElementName = "Texture", AllowNull = false)]
|
||||
internal Texture2DContent Texture { get; private set; }
|
||||
internal Texture2DContent Texture { get; set; }
|
||||
|
||||
[ContentSerializer(ElementName = "Glyphs", AllowNull = false)]
|
||||
internal List<Rectangle> Glyphs { get; private set; }
|
||||
internal List<Rectangle> Glyphs { get; set; }
|
||||
|
||||
[ContentSerializer(ElementName = "Cropping", AllowNull = false)]
|
||||
internal List<Rectangle> Cropping { get; private set; }
|
||||
internal List<Rectangle> Cropping { get; set; }
|
||||
|
||||
[ContentSerializer(ElementName = "CharacterMap", AllowNull = false)]
|
||||
internal List<char> CharacterMap { get; private set; }
|
||||
internal List<char> CharacterMap { get; set; }
|
||||
|
||||
[ContentSerializer(ElementName = "LineSpacing", AllowNull = false)]
|
||||
internal int LineSpacing { get; set; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user