Cosmos/source2/Users/Orvid/Orvid.Graphics/FontSupport/fnt/FntLoader.cs
blah38621_cp 92dcac4a51 Made a few improvements to the speed of the ILScanner, including caching the resolved plugs.
Added the possibility for an optimization step in the IL2CPU MSBuild task. Currently commented out, but should work fine with a few modifications to the optimizer.
Moved classes that are dependent on Cosmos from the Orvid.Graphics, into the Orvid.Graphics.Cosmos assembly. 
Re-factored the font mechanism, added partially working .fnt Font support. 
Added a Rectangle class, and a Vec2d class, which is the same as Vec2, except with doubles for the X and Y values.
Made a small aesthetic improvement to the output of the File2ByteArray Converter.
2011-09-03 21:05:46 +00:00

573 lines
21 KiB
C#

// Reference: http://support.microsoft.com/kb/65123
using System;
using System.IO;
using System.Collections.Generic;
namespace Orvid.Graphics.FontSupport.fnt
{
internal class FntLoader
{
#region Fields
/// <summary>
/// 2 bytes specifying the version (0200H or 0300H) of
/// the file.
/// </summary>
public ushort Version;
/// <summary>
/// 4 bytes specifying the total size of the file in
/// bytes.
/// </summary>
public uint FileSize;
/// <summary>
/// 60 bytes specifying copyright information.
/// </summary>
public string Copyright;
/// <summary>
/// 2 bytes specifying the type of font file.
///
/// The low-order byte is exclusively for GDI use. If the
/// low-order bit of the WORD is zero, it is a bitmap
/// (raster) font file. If the low-order bit is 1, it is a
/// vector font file. The second bit is reserved and must
/// be zero. If no bits follow in the file and the bits are
/// located in memory at a fixed address specified in
/// dfBitsOffset, the third bit is set to 1; otherwise, the
/// bit is set to 0 (zero). The high-order bit of the low
/// byte is set if the font was realized by a device. The
/// remaining bits in the low byte are reserved and set to
/// zero.
///
/// The high byte is reserved for device use and will
/// always be set to zero for GDI-realized standard fonts
/// Physical fonts with the high-order bit of the low byte
/// set may use this byte to describe themselves. GDI will
/// never inspect the high byte.
/// </summary>
public ushort Type;
/// <summary>
/// 2 bytes specifying the nominal point size at which
/// this character set looks best.
/// </summary>
public ushort PointSize;
/// <summary>
/// 2 bytes specifying the nominal vertical resolution
/// (dots-per-inch) at which this character set was
/// digitized.
/// </summary>
public ushort VertDpi;
/// <summary>
/// 2 bytes specifying the nominal horizontal resolution
/// (dots-per-inch) at which this character set was
/// digitized.
/// </summary>
public ushort HorizDpi;
/// <summary>
/// 2 bytes specifying the distance from the top of a
/// character definition cell to the baseline of the
/// typographical font. It is useful for aligning the
/// baselines of fonts of different heights.
/// </summary>
public ushort Ascent;
/// <summary>
/// Specifies the amount of leading inside the bounds set
/// by dfPixHeight. Accent marks may occur in this area.
/// This may be zero at the designer's option.
/// </summary>
public ushort InternalLeading;
/// <summary>
/// Specifies the amount of extra leading that the designer
/// requests the application add between rows. Since this
/// area is outside of the font proper, it contains no
/// marks and will not be altered by text output calls in
/// either the OPAQUE or TRANSPARENT mode. This may be zero
/// at the designer's option.
/// </summary>
public ushort ExternalLeading;
public bool IsItalic;
public bool IsUnderline;
public bool IsStrikeOut;
/// <summary>
/// 2 bytes specifying the weight of the characters in the
/// character definition data, on a scale of 1 to 1000. A
/// dfWeight of 400 specifies a regular weight.
/// </summary>
public ushort Weight;
/// <summary>
/// 1 byte specifying the character set defined by this
/// font.
/// </summary>
public byte CharSet;
/// <summary>
/// 2 bytes specifing the width.
/// </summary>
public ushort PixWidth;
/// <summary>
/// 2 bytes specifying the height of the character bitmap
/// (raster fonts), or the height of the grid on which a
/// vector font was digitized.
/// </summary>
public ushort PixHeight;
/// <summary>
/// Specifies the pitch and font family. The low bit is set
/// if the font is variable pitch. The high four bits give
/// the family name of the font. Font families describe in
/// a general way the look of a font. They are intended for
/// specifying fonts when the exact face name desired is
/// not available. The families are as follows:
///
/// Family Description
/// ------ -----------
/// FF_DONTCARE (0<<4) Don't care or don't know.
/// FF_ROMAN (1<<4) Proportionally spaced fonts with serifs.
/// FF_SWISS (2<<4) Proportionally spaced fonts without serifs.
/// FF_MODERN (3<<4) Fixed-pitch fonts.
/// FF_SCRIPT (4<<4)
/// FF_DECORATIVE (5<<4)
/// </summary>
public byte PitchAndFamily;
/// <summary>
/// 2 bytes specifying the width of characters in the font.
/// For fixed-pitch fonts, this is the same as dfPixWidth.
/// For variable-pitch fonts, this is the width of the
/// character "X."
/// </summary>
public ushort AvgWidth;
/// <summary>
/// 2 bytes specifying the maximum pixel width of any
/// character in the font. For fixed-pitch fonts, this is
/// simply dfPixWidth.
/// </summary>
public ushort MaxWidth;
/// <summary>
/// 1 byte specifying the first character code defined by
/// this font. Character definitions are stored only for
/// the characters actually present in a font. Therefore,
/// use this field when calculating indexes into either
/// dfBits or dfCharOffset.
/// </summary>
public byte FirstChar;
/// <summary>
/// 1 byte specifying the last character code defined by
/// this font. Note that all the characters with codes
/// between dfFirstChar and dfLastChar must be present in
/// the font character definitions.
/// </summary>
public byte LastChar;
/// <summary>
/// 1 byte specifying the character to substitute
/// whenever a string contains a character out of the
/// range. The character is given relative to dfFirstChar
/// so that dfDefaultChar is the actual value of the
/// character, less dfFirstChar. The dfDefaultChar should
/// indicate a special character that is not a space.
/// </summary>
public byte DefaultChar;
/// <summary>
/// 1 byte specifying the character that will define word
/// breaks. This character defines word breaks for word
/// wrapping and word spacing justification. The character
/// is given relative to dfFirstChar so that dfBreakChar is
/// the actual value of the character, less that of
/// dfFirstChar. The dfBreakChar is normally (32 -
/// dfFirstChar), which is an ASCII space.
/// </summary>
public byte BreakChar;
/// <summary>
/// 2 bytes specifying the number of bytes in each row of
/// the bitmap. This is always even, so that the rows start
/// on WORD boundaries. For vector fonts, this field has no
/// meaning.
/// </summary>
public ushort WidthBytes;
/// <summary>
/// 4 bytes specifying the offset in the file to the
/// null-terminated string giving the device name.
/// For a generic font, this value is zero.
/// </summary>
public uint Device;
/// <summary>
/// 4 bytes specifying the offset in the file to the
/// null-terminated string that names the face.
/// </summary>
public uint Face;
/// <summary>
/// 4 bytes specifying the absolute machine address of
/// the bitmap. This is set by GDI at load time. The
/// dfBitsPointer is guaranteed to be even.
/// </summary>
public uint BitsPointer;
/// <summary>
/// 4 bytes specifying the offset in the file to the
/// beginning of the bitmap information. If the 04H bit in
/// the dfType is set, then dfBitsOffset is an absolute
/// address of the bitmap (probably in ROM).
///
/// For raster fonts, dfBitsOffset points to a sequence of
/// bytes that make up the bitmap of the font, whose height
/// is the height of the font, and whose width is the sum
/// of the widths of the characters in the font rounded up
/// to the next WORD boundary.
///
/// For vector fonts, it points to a string of bytes or
/// words (depending on the size of the grid on which the
/// font was digitized) that specify the strokes for each
/// character of the font. The dfBitsOffset field must be
/// even.
/// </summary>
public uint BitsOffset;
/// <summary>
/// 4 bytes specifying the bits flags, which are additional
/// flags that define the format of the Glyph bitmap, as
/// follows:
///
/// DFF_FIXED equ 0001h ; font is fixed pitch
/// DFF_PROPORTIONAL equ 0002h ; font is proportional pitch
/// DFF_ABCFIXED equ 0004h ; font is an ABC fixed font
/// DFF_ABCPROPORTIONAL equ 0008h ; font is an ABC pro-portional font
/// DFF_1COLOR equ 0010h ; font is one color
/// DFF_16COLOR equ 0020h ; font is 16 color
/// DFF_256COLOR equ 0040h ; font is 256 color
/// DFF_RGBCOLOR equ 0080h ; font is RGB color
/// </summary>
public uint Flags;
/// <summary>
/// 2 bytes specifying the global A space, if any. The
/// dfAspace is the distance from the current position to
/// the left edge of the bitmap.
/// </summary>
public ushort ASpace;
/// <summary>
/// 2 bytes specifying the global B space, if any. The
/// dfBspace is the width of the character.
/// </summary>
public ushort BSpace;
/// <summary>
/// 2 bytes specifying the global C space, if any. The
/// dfCspace is the distance from the right edge of the
/// bitmap to the new current position. The increment of a
/// character is the sum of the three spaces. These apply
/// to all glyphs and is the case for DFF_ABCFIXED.
/// </summary>
public ushort CSpace;
/// <summary>
/// 4 bytes specifying the offset to the color table for
/// color fonts, if any. The format of the bits is similar
/// to a DIB, but without the header. That is, the
/// characters are not split up into disjoint bytes.
/// Instead, they are left intact. If no color table is
/// needed, this entry is NULL.
/// </summary>
public uint ColorPointer;
public FntGlyph[] Glyphs = new FntGlyph[255];
public string DeviceName;
public string FaceName;
public FntGlyphType GlyphType;
public FntGlyph NotDefChar;
private const byte FixedFlagMask = 1;
private const byte ProportionalFlagMask = 2;
private const byte ABCFixedFlagMask = 4;
private const byte ABCProportionalFlagMask = 8;
private const byte Color1FlagMask = 16;
private const byte Color16FlagMask = 32;
private const byte Color256FlagMask = 64;
private const byte ColorRGBFlagMask = 128;
#endregion
public FntLoader()
{
}
public string GetFamily()
{
if (PitchAndFamily == 0)
{
return "None";
}
else if (PitchAndFamily == (1<<4))
{
return "Roman";
}
else if (PitchAndFamily == (2<<4))
{
return "Swiss";
}
else if (PitchAndFamily == (3<<4))
{
return "Modern";
}
else if (PitchAndFamily == (4<<4))
{
return "Script";
}
else if (PitchAndFamily == (5<<4))
{
return "Decorative";
}
else
{
return "Unknown";
}
}
public Rectangle GetBounds()
{
Rectangle r = new Rectangle();
r.SetBounds(this.CSpace, this.Ascent, this.MaxWidth, this.PixHeight);
return r;
}
public int GetDepth()
{
switch (GlyphType)
{
case FntGlyphType.Version2:
case FntGlyphType.Color1:
return 1;
default:
return 900;
}
}
public FontStyle GetStyle()
{
FontStyle f = FontStyle.Normal;
if (IsItalic)
f |= FontStyle.Italic;
if (IsStrikeOut)
f |= FontStyle.Strikeout;
if (IsUnderline)
f |= FontStyle.Underline;
if (Weight >= 800)
f |= FontStyle.Bold;
return f;
}
public FntGlyph GetGlyph(char c)
{
if (((int)c) > 255)
return NotDefChar;
FntGlyph g = Glyphs[((int)c) - 1];
if (g == null)
return NotDefChar;
return g;
}
public int GetSize()
{
return PointSize;
}
public void Load(Stream s)
{
BinaryReader br = new BinaryReader(s);
byte[] buf;
Version = br.ReadUInt16();
if (Version != 0x0300 && Version != 0x0200)
{
throw new Exception("Unknown format version!");
}
FileSize = br.ReadUInt32();
if (FileSize != s.Length)
{
throw new Exception("FileSize incorrect! Possible corrupt file?");
}
buf = br.ReadBytes(60);
Copyright = System.Text.ASCIIEncoding.ASCII.GetString(buf).Replace("\0", "");
buf = null;
Type = br.ReadUInt16();
if ((Type & 1) == 1)
{
throw new Exception("Can't handle Vector FNT files.");
}
PointSize = br.ReadUInt16();
VertDpi = br.ReadUInt16();
HorizDpi = br.ReadUInt16();
Ascent = br.ReadUInt16();
InternalLeading = br.ReadUInt16();
ExternalLeading = br.ReadUInt16();
if (br.ReadByte() != 0)
{
IsItalic = true;
}
else
{
IsItalic = false;
}
if (br.ReadByte() != 0)
{
IsUnderline = true;
}
else
{
IsUnderline = false;
}
if (br.ReadByte() != 0)
{
IsStrikeOut = true;
}
else
{
IsStrikeOut = false;
}
Weight = br.ReadUInt16();
CharSet = br.ReadByte();
PixWidth = br.ReadUInt16();
PixHeight = br.ReadUInt16();
PitchAndFamily = br.ReadByte();
AvgWidth = br.ReadUInt16();
MaxWidth = br.ReadUInt16();
FirstChar = br.ReadByte();
LastChar = br.ReadByte();
DefaultChar = br.ReadByte();
BreakChar = br.ReadByte();
WidthBytes = br.ReadUInt16();
long position;
string deviceName = "";
char curChar = ' ';
#region Read Device Name
Device = br.ReadUInt32();
position = br.BaseStream.Position;
br.BaseStream.Position = Device;
br.BaseStream.Flush();
while (curChar != '\u0000')
{
deviceName += curChar;
curChar = (char)br.ReadByte();
}
br.BaseStream.Position = position;
br.BaseStream.Flush();
DeviceName = deviceName.Substring(1);
#endregion
#region Read Face Name
Face = br.ReadUInt32();
deviceName = ""; // re-use the variable.
curChar = ' ';
position = br.BaseStream.Position;
br.BaseStream.Position = Face;
br.BaseStream.Flush();
// loop while it's not the null terminator.
while (curChar != '\u0000')
{
deviceName += curChar;
curChar = (char)br.ReadByte();
}
br.BaseStream.Position = position;
br.BaseStream.Flush();
// account for the extra char we added.
FaceName = deviceName.Substring(1);
#endregion
BitsPointer = br.ReadUInt32();
BitsOffset = br.ReadUInt32();
br.ReadByte(); // Not used.
if (Version == 0x0300)
{
Flags = br.ReadUInt32();
ASpace = br.ReadUInt16();
BSpace = br.ReadUInt16();
CSpace = br.ReadUInt16();
ColorPointer = br.ReadUInt32();
br.ReadBytes(16); // Not used.
}
// Next is the character table.
List<FntGlyph> chars = new List<FntGlyph>();
FntGlyph g;
uint nChars = (uint)((LastChar - FirstChar) + 2);
position = 0;
long loc;
int bytesToRead;
if (Version == 0x0200)
{
this.GlyphType = FntGlyphType.Version2;
for (uint c = 0; c < nChars; c++)
{
g = new FntGlyph();
g.Width = br.ReadUInt16();
g.Type = FntGlyphType.Version2;
g.Height = PixHeight;
g.Font = this;
#region Read Data
loc = br.ReadUInt16();
position = br.BaseStream.Position;
br.BaseStream.Position = loc;
br.BaseStream.Flush();
bytesToRead = (int)(((g.Width + 7) >> 3) * (PixHeight));
g.RawData = br.ReadBytes(bytesToRead);
br.BaseStream.Position = position;
br.BaseStream.Flush();
#endregion
chars.Add(g);
}
}
for (int i = 0; i < chars.Count; i++)
{
chars[i].Initialize();
}
Array.Copy(chars.ToArray(), 0, Glyphs, FirstChar - 1, chars.Count - 1);
//Glyphs = chars;
NotDefChar = chars[chars.Count - 1];
//throw new Exception();
//else // version == 0x0300
//{
// if ((Flags & FixedFlagMask) == FixedFlagMask || (Flags & ProportionalFlagMask) == ProportionalFlagMask)
// {
// FntGlyphType t;
// if ((Flags & FixedFlagMask) == FixedFlagMask)
// {
// t = FntGlyphType.Fixed;
// }
// else
// {
// t = FntGlyphType.Proportional;
// }
// for (uint c = 0; c < nChars; c++)
// {
// g = new FntGlyph();
// g.Type = t;
// }
// }
// else if ((Flags & ABCFixedFlagMask) == ABCFixedFlagMask || (Flags & ABCProportionalFlagMask) == ABCProportionalFlagMask)
// {
// for (uint c = 0; c < nChars; c++)
// {
// }
// }
// else // A color flag.
// {
// for (uint c = 0; c < nChars; c++)
// {
// }
// }
//}
}
}
}