UPDATE: Today I use Cairo. Which is not only a font renderer but a full blown vector graphics library. They are now building support for OpenGL. Sweet!
Made me some nice wrapper functions. It's much more readable now. You can use it as a basis for your own font drawing routines.
TODO: Some better error handling.
FT_Face g_ftFace; FT_Library g_ftLib; FT_Matrix g_ftMatrix; FT_Vector g_ftPen; int g_ftBoxWidth; int g_ftBoxHeight; void ftBox( int width, int height ) { g_ftBoxWidth = width; g_ftBoxHeight = height; } void ftInit() { g_ftMatrix.xx = 0x10000; g_ftMatrix.xy = 0; g_ftMatrix.yx = 0; g_ftMatrix.yy = 0x10000; g_ftBoxWidth = 0; g_ftBoxHeight = 0; if( FT_Init_FreeType( &g_ftLib ) ) { printf( "Error: could not initialize FreeType library\n" ); } } void ftFace( char *face ) { FT_Error error = FT_New_Face( g_ftLib, face, 0, &g_ftFace ); if( error == FT_Err_Unknown_File_Format ) { printf( "Error: Unknown font file format\n" ); } else if( error ) { printf( "Error: Unknown error loading font\n" ); } } void ftMatrix( int xx, int xy, int yx, int yy ) { g_ftMatrix.xx = xx; g_ftMatrix.xy = xy; g_ftMatrix.yx = yx; g_ftMatrix.yy = yy; } void ftSize( int width, int height ) { if( FT_Set_Pixel_Sizes( g_ftFace, 80, 80 ) ) { printf( "Error: Could not set font pixel size\n" ); } } void ftText( char *image, char *text ) { int num_chars = strlen( text ); int x, y, i, j, p, q, x_max, y_max, pixel; char color; FT_Bitmap *bitmap; FT_GlyphSlot slot = g_ftFace->glyph; int n = 0; for( ; n < num_chars; n++ ) { FT_Set_Transform( g_ftFace, &g_ftMatrix, &g_ftPen ); if( FT_Load_Char( g_ftFace, text[n], FT_LOAD_RENDER ) ) { continue; } bitmap = &slot->bitmap; x = slot->bitmap_left; y = g_ftBoxHeight - slot->bitmap_top; x_max = x + bitmap->width; y_max = y + bitmap->rows; for( i = x, p = 0; i < x_max; i++, p++ ) { for( j = y, q = 0; j < y_max; j++, q++ ) { if( i < 0 || j < 0 || i >= g_ftBoxWidth || j >= g_ftBoxHeight ) { continue; } color = bitmap->buffer[q * bitmap->width + p]; pixel = ( j * g_ftBoxHeight + i ) * 4; image[pixel++] = 0xff; // R image[pixel++] = 0xff; // G image[pixel++] = 0xff; // B image[pixel] |= color; // A } } g_ftPen.x += slot->advance.x; g_ftPen.y += slot->advance.y; } } void ftTranslate( int x, int y ) { g_ftPen.x = x << 6; g_ftPen.y = ( g_ftBoxHeight - y ) << 6; }
This is how you use the new functions. Much nicer now!
#define IMG_WIDTH 512 #define IMG_HEIGHT 512 // create a bitmap unsigned char *image = malloc( IMG_WIDTH * IMG_HEIGHT * 4 ); if( image == NULL ) { printf( "Error: malloc()\n" ); return 0; } memset( image, 0, IMG_WIDTH * IMG_HEIGHT * 4 ); // draw our text using our awesome functions! ftInit(); ftFace( "c:/windows/fonts/verdana.ttf" ); ftSize( 80, 80 ); ftBox( IMG_WIDTH, IMG_HEIGHT ); double angle = ( -30.0 / 180.0 ) * 3.14159; ftMatrix( (FT_Fixed)( cos( angle ) * 0x10000L ), (FT_Fixed)(-sin( angle ) * 0x10000L ), (FT_Fixed)( sin( angle ) * 0x10000L ), (FT_Fixed)( cos( angle ) * 0x10000L ) ); ftTranslate( 50, 100 ); ftText( image, "Hello, world!" ); ftTranslate( 50, 200 ); ftText( image, "Hello, world!" ); ftTranslate( 50, 300 ); ftText( image, "Hello, world!" ); // ...and create a OpenGL texture of it GLint img; glGenTextures( 1, &img ); glBindTexture( GL_TEXTURE_2D, img ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, IMG_WIDTH, IMG_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, image );