/// <summary>
/// Create a bitmap font from the specified dynamic font.
/// </summary>
static public bool CreateFont (Font ttf, int size, int faceIndex, bool kerning, string characters, int padding, out BMFont font, out Texture2D tex)
{
font = null;
tex = null;
if (ttf == null || !isPresent) return false;
IntPtr lib = IntPtr.Zero;
IntPtr face = IntPtr.Zero;
if (FT_Init_FreeType(out lib) != 0)
{
Debug.LogError("Failed to initialize FreeType");
return false;
}
string fileName = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length) +
UnityEditor.AssetDatabase.GetAssetPath(ttf);
if (!File.Exists(fileName))
{
Debug.LogError("Unable to use the chosen font.");
}
else if (FT_New_Face(lib, fileName, faceIndex, out face) != 0)
{
Debug.LogError("Unable to use the chosen font (FT_New_Face).");
}
else
{
font = new BMFont();
font.charSize = size;
Color32 white = Color.white;
List<int> entries = new List<int>();
List<Texture2D> textures = new List<Texture2D>();
FT_FaceRec faceRec = (FT_FaceRec)Marshal.PtrToStructure(face, typeof(FT_FaceRec));
FT_Set_Pixel_Sizes(face, 0, (uint)size);
// Calculate the baseline value that would let the printed font be centered vertically
//int ascender = (faceRec.met.ascender >> 6);
//int descender = (faceRec.descender >> 6);
//int baseline = ((ascender - descender) >> 1);
//if ((baseline & 1) == 1) --baseline;
//Debug.Log(ascender + " " + descender + " " + baseline);
// Space character is not renderable
FT_Load_Glyph(face, FT_Get_Char_Index(face, 32), FT_LOAD_DEFAULT);
FT_GlyphSlotRec space = (FT_GlyphSlotRec)Marshal.PtrToStructure(faceRec.glyph, typeof(FT_GlyphSlotRec));
// Space is not visible and doesn't have a texture
BMGlyph spaceGlyph = font.GetGlyph(32, true);
spaceGlyph.offsetX = 0;
spaceGlyph.offsetY = 0;
spaceGlyph.advance = (space.metrics.horiAdvance >> 6);
spaceGlyph.channel = 15;
spaceGlyph.x = 0;
spaceGlyph.y = 0;
spaceGlyph.width = 0;
spaceGlyph.height = 0;
// Save kerning information
if (kerning)
{
for (int b = 0; b < characters.Length; ++b)
{
uint ch2 = characters[b];
if (ch2 == 32) continue;
FT_Vector vec;
if (FT_Get_Kerning(face, ch2, 32, 0, out vec) != 0) continue;
int offset = (vec.x >> 6);
if (offset != 0) spaceGlyph.SetKerning((int)ch2, offset);
}
}
// Run through all requested characters
foreach (char ch in characters)
{
uint charIndex = FT_Get_Char_Index(face, (uint)ch);
FT_Load_Glyph(face, charIndex, FT_LOAD_DEFAULT);
FT_GlyphSlotRec glyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(faceRec.glyph, typeof(FT_GlyphSlotRec));
FT_Render_Glyph(ref glyph, FT_Render_Mode.FT_RENDER_MODE_NORMAL);
if (glyph.bitmap.width > 0 && glyph.bitmap.rows > 0)
{
byte[] buffer = new byte[glyph.bitmap.width * glyph.bitmap.rows];
Marshal.Copy(glyph.bitmap.buffer, buffer, 0, buffer.Length);
Texture2D texture = new Texture2D(glyph.bitmap.width, glyph.bitmap.rows, UnityEngine.TextureFormat.ARGB32, false);
Color32[] colors = new Color32[buffer.Length];
for (int i = 0, y = 0; y < glyph.bitmap.rows; ++y)
{
for (int x = 0; x < glyph.bitmap.width; ++x)
{
white.a = buffer[i++];
colors[x + glyph.bitmap.width * (glyph.bitmap.rows - y - 1)] = white;
}
}
// Save the texture
texture.SetPixels32(colors);
texture.Apply();
textures.Add(texture);
entries.Add(ch);
// Record the metrics
BMGlyph bmg = font.GetGlyph(ch, true);
bmg.offsetX = (glyph.metrics.horiBearingX >> 6);
bmg.offsetY = -(glyph.metrics.horiBearingY >> 6);
bmg.advance = (glyph.metrics.horiAdvance >> 6);
bmg.channel = 15;
// Save kerning information
if (kerning)
{
for (int b = 0; b < characters.Length; ++b)
{
uint ch2 = characters[b];
if (ch2 == ch) continue;
FT_Vector vec;
if (FT_Get_Kerning(face, ch2, ch, 0, out vec) != 0) continue;
int offset = (vec.x >> 6);
if (offset != 0) bmg.SetKerning((int)ch2, offset);
}
}
}
}
// Create a packed texture with all the characters
tex = new Texture2D(32, 32, TextureFormat.ARGB32, false);
Rect[] rects = tex.PackTextures(textures.ToArray(), padding);
// Make the RGB channel pure white
Color32[] cols = tex.GetPixels32();
for (int i = 0, imax = cols.Length; i < imax; ++i)
{
Color32 c = cols[i];
c.r = 255;
c.g = 255;
c.b = 255;
cols[i] = c;
}
tex.SetPixels32(cols);
tex.Apply();
font.texWidth = tex.width;
font.texHeight = tex.height;
int min = int.MaxValue;
int max = int.MinValue;
// Other glyphs are visible and need to be added
for (int i = 0, imax = entries.Count; i < imax; ++i)
{
// Destroy the texture now that it's a part of an atlas
UnityEngine.Object.DestroyImmediate(textures[i]);
textures[i] = null;
Rect rect = rects[i];
// Set the texture coordinates
BMGlyph glyph = font.GetGlyph(entries[i], true);
glyph.x = Mathf.RoundToInt(rect.x * font.texWidth);
glyph.y = Mathf.RoundToInt(rect.y * font.texHeight);
glyph.width = Mathf.RoundToInt(rect.width * font.texWidth);
glyph.height = Mathf.RoundToInt(rect.height * font.texHeight);
// Flip the Y since the UV coordinate system is different
glyph.y = font.texHeight - glyph.y - glyph.height;
max = Mathf.Max(max, -glyph.offsetY);
min = Mathf.Min(min, -glyph.offsetY - glyph.height);
}
int baseline = size + min;
baseline += ((max - min - size) >> 1);
// Offset all glyphs so that they are not using the baseline
for (int i = 0, imax = entries.Count; i < imax; ++i)
{
BMGlyph glyph = font.GetGlyph(entries[i], true);
glyph.offsetY += baseline;
}
}
if (face != IntPtr.Zero) FT_Done_Face(face);
#if !UNITY_3_5
if (lib != IntPtr.Zero) FT_Done_FreeType(lib);
#endif
return (tex != null);
}