public void LoadResource(Resource res)
{
// TODO : Revisit after checking current Imaging support in Mono.
#if !(XBOX || XBOX360 || ANDROID || IPHONE || SILVERLIGHT)
var current = Environment.CurrentDirectory;
var ftLibrary = IntPtr.Zero;
if (FT.FT_Init_FreeType(out ftLibrary) != 0)
{
throw new AxiomException("Could not init FreeType library!");
}
var face = IntPtr.Zero;
// Add a gap between letters vert and horz
// prevents nasty artefacts when letters are too close together
var char_space = 5;
// Locate ttf file, load it pre-buffered into memory by wrapping the
// original DataStream in a MemoryDataStream
var fileStream = ResourceGroupManager.Instance.OpenResource(Source, Group, true, this);
var ttfchunk = new byte[fileStream.Length];
fileStream.Read(ttfchunk, 0, ttfchunk.Length);
//Load font
if (FT.FT_New_Memory_Face(ftLibrary, ttfchunk, ttfchunk.Length, 0, out face) != 0)
{
throw new AxiomException("Could not open font face!");
}
// Convert our point size to freetype 26.6 fixed point format
var ftSize = this._ttfSize * (1 << 6);
if (FT.FT_Set_Char_Size(face, ftSize, 0, (uint)this._ttfResolution, (uint)this._ttfResolution) != 0)
{
throw new AxiomException("Could not set char size!");
}
int max_height = 0, max_width = 0;
// Backwards compatibility - if codepoints not supplied, assume 33-166
if (this.codePointRange.Count == 0)
{
this.codePointRange.Add(new KeyValuePair <int, int>(33, 166));
}
// Calculate maximum width, height and bearing
var glyphCount = 0;
foreach (var r in this.codePointRange)
{
var range = r;
for (var cp = range.Key; cp <= range.Value; ++cp, ++glyphCount)
{
FT.FT_Load_Char(face, (uint)cp, 4); //4 == FT_LOAD_RENDER
var rec = face.PtrToStructure <FT_FaceRec>();
var glyp = rec.glyph.PtrToStructure <FT_GlyphSlotRec>();
if ((2 * (glyp.bitmap.rows << 6) - glyp.metrics.horiBearingY) > max_height)
{
max_height = (2 * (glyp.bitmap.rows << 6) - glyp.metrics.horiBearingY);
}
if (glyp.metrics.horiBearingY > this.maxBearingY)
{
this.maxBearingY = glyp.metrics.horiBearingY;
}
if ((glyp.advance.x >> 6) + (glyp.metrics.horiBearingX >> 6) > max_width)
{
max_width = (glyp.advance.x >> 6) + (glyp.metrics.horiBearingX >> 6);
}
}
}
// Now work out how big our texture needs to be
var rawSize = (max_width + char_space) * ((max_height >> 6) + char_space) * glyphCount;
var tex_side = (int)System.Math.Sqrt((Real)rawSize);
// just in case the size might chop a glyph in half, add another glyph width/height
tex_side += System.Math.Max(max_width, (max_height >> 6));
// Now round up to nearest power of two
var roundUpSize = (int)Bitwise.FirstPO2From((uint)tex_side);
// Would we benefit from using a non-square texture (2X width)
int finalWidth = 0, finalHeight = 0;
if (roundUpSize * roundUpSize * 0.5 >= rawSize)
{
finalHeight = (int)(roundUpSize * 0.5);
}
else
{
finalHeight = roundUpSize;
}
finalWidth = roundUpSize;
var textureAspec = (Real)finalWidth / (Real)finalHeight;
var pixelBytes = 2;
var dataWidth = finalWidth * pixelBytes;
var dataSize = finalWidth * finalHeight * pixelBytes;
LogManager.Instance.Write("Font {0} using texture size {1}x{2}", _name, finalWidth.ToString(),
finalHeight.ToString());
var imageData = new byte[dataSize];
// Reset content (White, transparent)
for (var i = 0; i < dataSize; i += pixelBytes)
{
imageData[i + 0] = 0xff; // luminance
imageData[i + 1] = 0x00; // alpha
}
int l = 0, m = 0;
foreach (var r in this.codePointRange)
{
var range = r;
for (var cp = range.Key; cp <= range.Value; ++cp)
{
// Load & render glyph
var ftResult = FT.FT_Load_Char(face, (uint)cp, 4); //4 == FT_LOAD_RENDER
if (ftResult != 0)
{
// problem loading this glyph, continue
LogManager.Instance.Write("Info: cannot load character '{0}' in font {1}.",
#if (SILVERLIGHT || WINDOWS_PHONE)
cp,
#else
char.ConvertFromUtf32(cp),
#endif
_name);
continue;
}
var rec = face.PtrToStructure <FT_FaceRec>();
var glyp = rec.glyph.PtrToStructure <FT_GlyphSlotRec>();
var advance = glyp.advance.x >> 6;
if (glyp.bitmap.buffer == IntPtr.Zero)
{
LogManager.Instance.Write("Info: Freetype returned null for character '{0} in font {1}.",
#if (SILVERLIGHT || WINDOWS_PHONE)
cp,
#else
char.ConvertFromUtf32(cp),
#endif
_name);
continue;
}
#if !AXIOM_SAFE_ONLY
unsafe
#endif
{
var buffer = BufferBase.Wrap(glyp.bitmap.buffer, glyp.bitmap.rows * glyp.bitmap.pitch);
var bufferPtr = buffer.ToBytePointer();
var idx = 0;
var imageDataBuffer = BufferBase.Wrap(imageData);
var imageDataPtr = imageDataBuffer.ToBytePointer();
var y_bearing = ((this.maxBearingY >> 6) - (glyp.metrics.horiBearingY >> 6));
var x_bearing = glyp.metrics.horiBearingX >> 6;
for (var j = 0; j < glyp.bitmap.rows; j++)
{
var row = j + m + y_bearing;
var pDest = (row * dataWidth) + (l + x_bearing) * pixelBytes;
for (var k = 0; k < glyp.bitmap.width; k++)
{
if (AntialiasColor)
{
// Use the same greyscale pixel for all components RGBA
imageDataPtr[pDest++] = bufferPtr[idx];
}
else
{
// Always white whether 'on' or 'off' pixel, since alpha
// will turn off
imageDataPtr[pDest++] = (byte)0xFF;
}
// Always use the greyscale value for alpha
imageDataPtr[pDest++] = bufferPtr[idx++];
} //end k
} //end j
buffer.Dispose();
imageDataBuffer.Dispose();
SetGlyphTexCoords((uint)cp, (Real)l / (Real)finalWidth, //u1
(Real)m / (Real)finalHeight, //v1
(Real)(l + (glyp.advance.x >> 6)) / (Real)finalWidth, //u2
(m + (max_height >> 6)) / (Real)finalHeight, //v2
textureAspec);
// Advance a column
l += (advance + char_space);
// If at end of row
if (finalWidth - 1 < l + (advance))
{
m += (max_height >> 6) + char_space;
l = 0;
}
}
}
} //end foreach
var memStream = new MemoryStream(imageData);
var img = Image.FromRawStream(memStream, finalWidth, finalHeight, PixelFormat.BYTE_LA);
var tex = (Texture)res;
// Call internal _loadImages, not loadImage since that's external and
// will determine load status etc again, and this is a manual loader inside load()
var images = new Image[1];
images[0] = img;
tex.LoadImages(images);
FT.FT_Done_FreeType(ftLibrary);
//img.Save( "C:" + Path.DirectorySeparatorChar + Name + ".png" );
//FileStream file = new FileStream( "C:" + Path.DirectorySeparatorChar + Name + ".fontdef", FileMode.Create );
//StreamWriter str = new StreamWriter( file );
//str.WriteLine( Name );
//str.WriteLine( "{" );
//str.WriteLine( "\ttype\timage" );
//str.WriteLine( "\tsource\t{0}.png\n", Name );
//for ( uint i = 0; i < (uint)( END_CHAR - START_CHAR ); i++ )
//{
// char c = (char)( i + START_CHAR );
// str.WriteLine( "\tglyph\t{0}\t{1:F6}\t{2:F6}\t{3:F6}\t{4:F6}", c, Glyphs[ c ].uvRect.Top, Glyphs[ c ].uvRect.Left, Glyphs[ c ].uvRect.Bottom, Glyphs[ c ].uvRect.Right );
//}
//str.WriteLine( "}" );
//str.Close();
//file.Close();
#endif
}