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 );
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!
[u]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![/u]
I'm writing an OpenGL GUI interface. One thing that's very important is drawing some fonts. This means 2D, not 3D fonts. There are a numbers of ways to do this. Even some nice C++ library's that you could use. But I want to know the 'magic'. When you work with Linux you probebly heard of FreeType. Which is a cross-platform library created to load fonts. Just what we needed!
FreeType renders the text into his own grayscale bitmap. We could uses this bitmap as an alpha channel.
Steps for writing fonts with OpenGL:
1: Load the font
2: Draw some text onto a bitmap
3: Load this bitmap as an OpenGL texture
4: Draw the texture
I used the code snippet from documentation of FreeType self. And made some little changes to it.
http://www.freetype.org/freetype2/docs/tutorial/step1.html
The 'magic' code:
#define WIDTH 512 #define HEIGHT 512 char *text = "Hello, world!"; int num_chars = strlen( text ); double angle = ( -45.0 / 180.0 ) * 3.14159; // my RGBA bitmap unsigned char *data = malloc( WIDTH * HEIGHT * 4 ); if( data == NULL ) { printf( "Error: malloc()\n" ); return 0; } memset( data, 0, WIDTH * HEIGHT * 4 ); // init FreeType FT_Library library; if( FT_Init_FreeType( &library ) ) { printf( "Error: could not initialize FreeType library\n" ); } // loads a face FT_Face face; FT_Error error = FT_New_Face( library, "c:/windows/fonts/verdana.ttf", 0, &face ); if( error == FT_Err_Unknown_File_Format ) { printf( "Error: Unknown font file format\n" ); } else if( error ) { printf( "Error: Unknown error loading font\n" ); } // set font dimensions 80 x 80 pixels if( FT_Set_Pixel_Sizes( face, 80, 80 ) ) { printf( "Error: Could not set font pixel size\n" ); } FT_GlyphSlot slot = face->glyph; // transformation matrix FT_Matrix matrix; matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); // untransformed origin // the pen position in 26.6 cartesian space coordinates; // start at (50,100) relative to the upper left corner FT_Vector pen; pen.x = 50 << 6; pen.y = ( HEIGHT - 100 ) << 6; int n = 0; for( ; n < num_chars; n++ ) { FT_Set_Transform( face, &matrix, &pen ); // load glyph image into the slot (erase previous one) if( FT_Load_Char( face, text[n], FT_LOAD_RENDER ) ) { continue; // ignore errors } FT_Bitmap *bitmap = &slot->bitmap; FT_Int x = slot->bitmap_left; FT_Int y = HEIGHT - slot->bitmap_top; FT_Int i, j, p, q; FT_Int x_max = x + bitmap->width; FT_Int 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 >= WIDTH || j >= HEIGHT ) { continue; } char color = bitmap->buffer[q * bitmap->width + p]; int pixel = ( j * HEIGHT + i ) * 4; data[pixel++] = 0xff; // R data[pixel++] = 0xff; // G data[pixel++] = 0xff; // B data[pixel] |= color; // A } } // increment pen position pen.x += slot->advance.x; pen.y += slot->advance.y; } // Now load the bitmap into an OpenGL texture 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, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); // And now do some OpenGL drawing! glTranslatef( WIDTH / 2, HEIGHT / 2, 0.0f ); glScalef( WIDTH, HEIGHT, 1.0f ); glBindTexture( GL_TEXTURE_2D, img ); glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); glBegin( GL_QUADS ); glTexCoord2f( 0.0f, 0.0f ); glVertex2f( -0.5f, -0.5f ); glTexCoord2f( 1.0f, 0.0f ); glVertex2f( 0.5f, -0.5f ); glTexCoord2f( 1.0f, 1.0f ); glVertex2f( 0.5f, 0.5f ); glTexCoord2f( 0.0f, 1.0f ); glVertex2f( -0.5f, 0.5f ); glEnd();
That's it! Pretty easy huh? Thank FreeType for this great library!
I made it using CodeBlocks.
The project file and sources are here: http://vin.ingine.nl/posts/11/opengl_fonts.zip