Rendering AltNETType (= .NET FreeType port) with OpenGL





5.00/5 (3 votes)
Example of AltNETType use in OpenGL
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)
{
// Save the size we need it later on when printing
font_size = size;
// We begin by creating a library pointer
ANT_Library library;
ANT_Error ret = ANT.ANT_Init_AltNETType(out library);
if (ret != ANT_Error.ANT_Err_Ok)
{
return;
}
// Once we have the library we create and load the font face
ANT_Face face;
ret = ANT.ANT_New_Face(library, font, 0, out face);
if (ret != ANT_Error.ANT_Err_Ok)
{
return;
}
// AltNETType (as Freetype) measures the font size in 1/64th of pixels for accuracy
// so we need to request characters in size*64
ANT.ANT_Set_Char_Size(face, size << 6, size << 6, 96, 96);
// Provide a reasonably accurate estimate for expected pixel sizes
// when we later on create the bitmaps for the font
ANT.ANT_Set_Pixel_Sizes(face, size, size);
// Once we have the face loaded and sized we generate opengl textures
// from the glyphs for each printable character
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);
}
// Dispose of these as we don't need
ANT.ANT_Done_Face(ref face);
ANT.ANT_Done_AltNETType(ref library);
}
public void Compile_Character(ANT_Face face, int c)
{
// We first convert the number index to a character index
int index = ANT.ANT_Get_Char_Index(face, Convert.ToChar(c));
// Here we load the actual glyph for the character
ANT_Error ret = ANT.ANT_Load_Glyph(face, index, ANT_LOAD.ANT_LOAD_DEFAULT);
if (ret != 0) return;
// Convert the glyph to a bitmap
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)
{
// space is a special `blank` character
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);
// Next we expand the bitmap into an opengl texture
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];
}
}
// Set up some texture parameters for opengl
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);
// Create the texture
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;
// Create a display list and bind a texture to it
Gl.glNewList((uint)(list_base + c), Gl.GL_COMPILE);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[c]);
// Account for freetype spacing rules
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;
// Draw the quad
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();
// Advance for the next character
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)