Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Rendering AltNETType (= .NET FreeType port) with OpenGL

5.00/5 (3 votes)
14 Oct 2013CPOL1 min read 34.6K   686  
Example of AltNETType use in OpenGL

Image 1

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.

C#
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)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)