Introduction
This tip will explain how to render true type text to a OpenGL surface by using the AltSketch.AltNETType library; using open source tools and technology; these include the Mono, SDL, TAO and free for non commercial AltSketch package.
Background
This tip just explains the modified example of jve7gm Rendering FreeType/2 with OpenGL. Recently, I found AltSketch library implemented in pure C# for MS .NET / Mono (Silverlight / Moonlight), that includes AltNETType subsystem. AltNETType is a pure C# CLS compliant 100% managed, without unsafe blocks port of wonderful font rendering library Freetype. I modified jve7gm example to play with this library. And it is working.
Using the Code
Unlike the use of Freetype, in AltNETType uses "ANT_" prefixes at code elements instead of native Freetype "FT_" prefixes; the core class named "ANT". Instead of FT_Init_FreeType
and FT_Done_FreeType
, we need to use ANT_Init_AltNETType
and ANT_Done_AltNETType
.
So code of Rendering FreeType/2 with OpenGL example modified according to these differences.
In the code example, you can see all operations with AltNETType.
public Font3D(string font, int size)
{
font_size = size;
ANT_Library library;
ANT_Error ret = ANT.ANT_Init_AltNETType(out library);
if (ret != ANT_Error.ANT_Err_Ok)
{
return;
}
ANT_Face face;
ret = ANT.ANT_New_Face(library, font, 0, out face);
if (ret != ANT_Error.ANT_Err_Ok)
{
return;
}
ANT.ANT_Set_Char_Size(face, size << 6, size << 6, 96, 96);
ANT.ANT_Set_Pixel_Sizes(face, size, size);
textures = new int[128];
extent_x = new int[128];
list_base = Gl.glGenLists(128);
Gl.glGenTextures(128, textures);
for (int c = 0; c < 128; c++)
{
Compile_Character(face, c);
}
ANT.ANT_Done_Face(ref face);
ANT.ANT_Done_AltNETType(ref library);
}
public void Compile_Character(ANT_Face face, int c)
{
int index = ANT.ANT_Get_Char_Index(face, Convert.ToChar(c));
ANT_Error ret = ANT.ANT_Load_Glyph(face, index, ANT_LOAD.ANT_LOAD_DEFAULT);
if (ret != 0) return;
ANT_Glyph glyph;
ret = ANT.ANT_Get_Glyph(face.glyph, out glyph);
if (ret != ANT_Error.ANT_Err_Ok)
{
return;
}
ANT.ANT_Glyph_To_Bitmap(ref glyph, ANT_Render_Mode.ANT_RENDER_MODE_NORMAL, null, true);
ANT_BitmapGlyph glyph_bmp = (ANT_BitmapGlyph) glyph;
int size = (glyph_bmp.bitmap.width * glyph_bmp.bitmap.rows);
if (size <= 0)
{
extent_x[c] = 0;
if (c == 32)
{
Gl.glNewList((uint)(list_base + c), Gl.GL_COMPILE);
Gl.glTranslatef(font_size >> 1, 0, 0);
extent_x[c] = font_size >> 1;
Gl.glEndList();
}
return;
}
byte[] bmp = new byte[size];
Array.Copy(glyph_bmp.bitmap.buffer, bmp, bmp.Length);
int width = next_po2(glyph_bmp.bitmap.width);
int height = next_po2(glyph_bmp.bitmap.rows);
byte[] expanded = new byte[2 * width * height];
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
expanded[2 * (i + j * width)] = expanded[2 * (i + j * width) + 1] =
(i >= glyph_bmp.bitmap.width || j >= glyph_bmp.bitmap.rows) ?
(byte)0 : bmp[i + glyph_bmp.bitmap.width * j];
}
}
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[c]);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, width, height,
0, Gl.GL_LUMINANCE_ALPHA, Gl.GL_UNSIGNED_BYTE, expanded);
expanded = null;
bmp = null;
Gl.glNewList((uint)(list_base + c), Gl.GL_COMPILE);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[c]);
Gl.glTranslatef(glyph_bmp.left, 0, 0);
Gl.glPushMatrix();
Gl.glTranslatef(0, glyph_bmp.top - glyph_bmp.bitmap.rows, 0);
float x = (float)glyph_bmp.bitmap.width / (float)width;
float y = (float)glyph_bmp.bitmap.rows / (float)height;
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2d(0, 0); Gl.glVertex2f(0, glyph_bmp.bitmap.rows);
Gl.glTexCoord2d(0, y); Gl.glVertex2f(0, 0);
Gl.glTexCoord2d(x, y); Gl.glVertex2f(glyph_bmp.bitmap.width, 0);
Gl.glTexCoord2d(x, 0); Gl.glVertex2f(glyph_bmp.bitmap.width, glyph_bmp.bitmap.rows);
Gl.glEnd();
Gl.glPopMatrix();
Gl.glTranslatef(glyph_bmp.bitmap.width, 0, 0);
extent_x[c] = glyph_bmp.left + glyph_bmp.bitmap.width;
Gl.glEndList();
}
Points of Interest
As AltSketch is implemented in pure C#, so you do not need to use any unsafe blocks of code or managed/unmanaged conversations. Also as AltNETType is fully managed code, so you don't need to have different FreeType versions of OS-dependent DLLs.
History
- 13th October, 2013: First release
- 22nd June, 2014: Executable zip archive removed (it's better to get platform dependent SDL & OpenGL tools by yourself)