Click here to Skip to main content
15,861,125 members
Articles / Desktop Programming / Win32
Tip/Trick

Rendering AltNETType (= .NET FreeType port) with OpenGL

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
14 Oct 2013CPOL1 min read 33.8K   676   8   1
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)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNice Improvement Pin
jve7gm14-Apr-15 20:54
jve7gm14-Apr-15 20:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.