You Are Here:

Community: Wiki

This page was last modified 19:28, 19 August 2008.

Texture-mapped font for OpenGL ES

From Forum Nokia Wiki

An efficient approach to implement fonts in OpenGL is to use a single texture-mapped quad for each character. This technique has very good performance and presents nice results. At this link [1], there is more information on this subject.

glFont

The glFont tool [2] is a well-known Windows application and API to generate textures for fonts, and to render them using OpenGL. According to its licence, this tool is free to be used in any program, commercial or non-commercial [3]. The original author is Brad Fish (brad.fish@gmail.com).

Here is the font class converted to the Symbian OS API (The original author gently allowed the conversion to be published here):

//*******************************************************************
// glfont2.h -- Header for glfont2.cpp
// Copyright (c) 1998-2002 Brad Fish
// See glfont.html for terms of use
// May 14, 2002
//
// Symbian OS port - June 2007
// Luis Valente - lpvalente (http://wiki.forum.nokia.com/index.php/User:Lpvalente)
//
//*******************************************************************
 
#ifndef GLFONT2_H
#define GLFONT2_H
 
#include <e32base.h>
#include <GLES/gl.h>
 
//_____________________________________________________________________________
//
// Simple class to output text as texture-mapped triangles. Does not support
// unicode strings. Reference point when drawing: top-left.
//
 
class GLFont
{	
  public:
 
   /**
    * Factory-method.
    */
   static GLFont* NewL (const TDesC & aFilename);
 
  public:
 
   /**
    * Destructor.
    */		
   ~GLFont ();
 
  public:
 
 
   /**
    * Retrieves the texture width and height.
    */	
   void GetTexSize (TInt & aWidth, TInt & aHeight);
 
   /**
    * Retrieves the character interval.
    */	
   void GetCharInterval (TInt & aStart, TInt & aEnd);
 
   /**
    * Retrieves the character dimensions.
    */
   void GetCharSize (TText8 c, TInt & aWidth, TInt aHeight);
 
 
   /**
    * Calculates the dimensions of a string.
    */
   void GetStringSize (const TDesC8 & aText, TInt & aWidth, TInt & aHeight);
 
   /**
    * Renders a string.
    */
   void DrawString (const TDesC8 & aText, GLfixed aX, GLfixed aY);	
 
   /**
    * Sets required states for the font.
    */
   void BeginDraw ()
   {			
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable (GL_TEXTURE_2D);
    glEnableClientState (GL_TEXTURE_COORD_ARRAY);
   }
 
   /**
    * Turns off required states.
    */
   void EndDraw ()
   {			
    glDisable (GL_BLEND);			
    glDisable (GL_TEXTURE_2D);
    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
   }		
 
 private:
 
    /**
     * Default constructor.
     */
   GLFont ();
 
   /**
    * Final part of the two-phase constructor.
    */
   void ConstructL (const TDesC & aFilename);
 
   /**
    * Loads the font file.
    */
   void LoadFileL (RFs & aFs, const TDesC & aFilename);		
 
   /**
    * Destroys the font.
    */		
   void Destroy ();		
 
 
  private:	
 
   // single character
   struct GLFontChar
   {
      GLfixed dx, dy;
      GLfixed tx1, ty1;
      GLfixed tx2, ty2;
   };
 
   // font header
   struct GLFontHeader
   {
      GLuint tex;
      TInt   texWidth, texHeight;
      TInt   startChar, endChar;
      GLFontChar *chars;
   };			
 
 private:
 
   GLFontHeader iHeader;
};
 
//*******************************************************************
#endif

Here is the class implementation:

//*******************************************************************
// glfont2.cpp -- glFont Version 2.0 implementation
// Copyright (c) 1998-2002 Brad Fish
// See glfont.html for terms of use
// May 14, 2002
//
// Symbian OS port - June 2007
// Luis Valente - lpvalente@gmail.com
//
//*******************************************************************
 
// Symbian OS headers
#include <s32file.h>
#include <eikenv.h>
#include <eikappui.h>
#include <eikapp.h>
#include "glfont2.h"
#include "FixedMath.h"
 
 
// GLFontChar structure as stored in file
struct GLFontCharFile
{
   TReal32 dx, dy;
   TReal32 tx1, ty1;
   TReal32 tx2, ty2;
};
 
// GLFontHeaderFile structure as stored in file
struct GLFontHeaderFile
{
   TInt32  tex;
   TInt32  texWidth, texHeight;
   TInt32  startChar, endChar;
   TUint32 chars;
};
 
 
 
//_____________________________________________________________________________
//
// Default constructor.
//
 
GLFont::GLFont ()
{
   // Initialize iHeader to safe state
   iHeader.tex = 0;
   iHeader.texWidth = 0;
   iHeader.texHeight = 0;
   iHeader.startChar = 0;
   iHeader.endChar = 0;
   iHeader.chars = NULL;
 
   // OpenGL texture
   glGenTextures (1, &iHeader.tex);
}
 
//_____________________________________________________________________________
//
// Destructor.
//
 
GLFont::~GLFont ()
{
   // Destroy the font
   Destroy();
 
   // delete texture
   glDeleteTextures (1, &iHeader.tex);
}
 
//_____________________________________________________________________________
//
// Factory-method.
//
 
GLFont * GLFont::NewL (const TDesC & aFilename)
{
   GLFont* f = new (ELeave) GLFont();
   CleanupStack::PushL (f);
 
   f->ConstructL (aFilename);
 
   CleanupStack::Pop ();
   return f;
}
 
//_____________________________________________________________________________
//
// Second part of the two-phase construction.
//
 
void GLFont::ConstructL (const TDesC & aFilename)
{
   // Destroy the old font if there was one, just to be safe
   Destroy();
 
   // Open file session with server	
   RFs session;
   User::LeaveIfError (session.Connect());
   CleanupClosePushL (session);	
 
   // retrieve private application folder
   TFileName path;
   session.PrivatePath (path);
 
   // retrieve full application path on device
   #ifndef __WINS__
      TFileName appFullName =          
      CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
 
      TParse parse;
      parse.Set (appFullName, NULL, NULL);
      path.Insert (0, parse.Drive());
   #endif
 
   // update filename with full path
   TFileName fullFilename (path);
   fullFilename.Append (aFilename);
 
   // load file
   LoadFileL (session, fullFilename);
 
   // close server session
   CleanupStack::PopAndDestroy();
 
}
 
//_____________________________________________________________________________
//
// Loads the font file.
//
 
void GLFont::LoadFileL (RFs & aFs, const TDesC & aFilename)
{
   // Open input file
   RFileReadStream readStream;
 
   User::LeaveIfError (readStream.Open (aFs, aFilename, EFileRead));	
   readStream.PushL();
 
   // Read the iHeader from file
   GLFontHeaderFile headerFile;
 
   headerFile.tex       = readStream.ReadInt32L ();
   headerFile.texWidth  = readStream.ReadInt32L();
   headerFile.texHeight = readStream.ReadInt32L();
   headerFile.startChar = readStream.ReadInt32L();
   headerFile.endChar   = readStream.ReadInt32L();
   headerFile.chars     = readStream.ReadUint32L();
 
   // copy iHeader file to actual iHeader		
   iHeader.texWidth  = headerFile.texWidth;
   iHeader.texHeight = headerFile.texHeight;
   iHeader.startChar = headerFile.startChar;
   iHeader.endChar   = headerFile.endChar;
 
   // Allocate space for character array
   TInt numChars = iHeader.endChar - iHeader.startChar + 1;	
   iHeader.chars = new (ELeave) GLFontChar [numChars];
 
   // Read character array
   for (TInt i = 0; i < numChars; ++i)
   {
      iHeader.chars [i].dx  =  FloatToFixed (readStream.ReadReal32L () );		
      iHeader.chars [i].dy  =  FloatToFixed (readStream.ReadReal32L () );		
      iHeader.chars [i].tx1 =  FloatToFixed (readStream.ReadReal32L () );		
      iHeader.chars [i].ty1 =  FloatToFixed (readStream.ReadReal32L () );		
      iHeader.chars [i].tx2 =  FloatToFixed (readStream.ReadReal32L () );		
      iHeader.chars [i].ty2 =  FloatToFixed (readStream.ReadReal32L () );
   }
 
 
   // Read texture pixel data
   TInt numTexBytes = iHeader.texWidth * iHeader.texHeight * 2;	
   TUint8 * texBytes = new (ELeave) TUint8 [numTexBytes]; CleanupStack::PushL (texBytes);
 
   readStream.ReadL (texBytes, numTexBytes);	
 
   // Create OpenGL texture
   glBindTexture   (GL_TEXTURE_2D, iHeader.tex);  
   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
   glTexImage2D (GL_TEXTURE_2D,
                 0,
                 GL_LUMINANCE_ALPHA,
                 iHeader.texWidth,
                 iHeader.texHeight,
                 0,
                 GL_LUMINANCE_ALPHA,
                 GL_UNSIGNED_BYTE,
                 (GLvoid *)texBytes);
 
   // Free texture pixels memory
   CleanupStack::Pop ();
   delete [] texBytes;	
 
   // Close input file
   readStream.Close();
   readStream.Pop();
}
 
//_____________________________________________________________________________
//
// Font destruction.
//
 
void GLFont::Destroy ()
{
   //Delete the character array if necessary
   if (iHeader.chars)
   {
      delete [] iHeader.chars;
      iHeader.chars = 0;
   }
}
 
//_____________________________________________________________________________
//
// Retrieves the texture dimensions.
//
 
void GLFont::GetTexSize (TInt & aWidth, TInt & aHeight)
{	
   aWidth  = iHeader.texWidth;
   aHeight = iHeader.texHeight;
}
 
//_____________________________________________________________________________
//
// Retrieves the character interval.
//
 
void GLFont::GetCharInterval (TInt & aStart, TInt & aEnd)
{	
   aStart = iHeader.startChar;
   aEnd   = iHeader.endChar;
}
 
//_____________________________________________________________________________
//
// Retrieves the dimensions of a character.
//
 
void GLFont::GetCharSize (TText8 aChar, TInt & aWidth, TInt aHeight)
{
   // Make sure character is in range
   if (aChar < iHeader.startChar || aChar > iHeader.endChar)
   {
      // Not a valid character, so it obviously has no size
      aWidth  = 0;
      aHeight = 0;
   }
   else
   {
      GLFontChar* fontChar;
 
      // Retrieve character size
      fontChar = & iHeader.chars [aChar - iHeader.startChar];		
      aWidth  = FixedToInt (MultiplyFixed (fontChar->dx, IntToFixed (iHeader.texWidth) ) );
      aHeight = FixedToInt (MultiplyFixed (fontChar->dy, IntToFixed (iHeader.texHeight) ) );		
   }
}
 
//_____________________________________________________________________________
//
// Retrieves the dimensions of a string.
//
 
void GLFont::GetStringSize (const TDesC8 & aText, TInt & aWidth, TInt & aHeight)
{	
   // Height is the same for now...might change in future
   aHeight = FixedToInt (MultiplyFixed (iHeader.chars [iHeader.startChar].dy, IntToFixed (iHeader.texHeight) ) ); 
 
   // texWidth as fixed
   const GLfixed texWidthx = IntToFixed (iHeader.texWidth);
 
   // Calculate width of string	
   GLfixed widthx = 0;
   for (TInt i = 0; i < aText.Length(); i++)
   {
      // Make sure character is in range
      const TText8 c = aText [i];
      if (c < iHeader.startChar || c > iHeader.endChar)
         continue;
 
      // Get pointer to glFont character
      const GLFontChar* fontChar = & iHeader.chars [c - iHeader.startChar];
 
      // Get width and height
      widthx += MultiplyFixed (fontChar->dx, texWidthx);		
   }
 
   // Save width
   aWidth = FixedToInt (widthx);
}
 
//_____________________________________________________________________________
//
// Renders a string. Reference point is top-left.
//
 
void GLFont::DrawString (const TDesC8 & aText, GLfixed aX, GLfixed aY)
{
   // vertex arrays to render the string
   GLfixed  vertices [4*2];
   GLfixed texCoords [4*2];	
   const GLubyte indices [] = {1, 2, 0, 3};
 
   glVertexPointer  (2, GL_FIXED, 0, vertices);
   glTexCoordPointer (2, GL_FIXED, 0, texCoords); 	
 
   // Bind texture
   glBindTexture (GL_TEXTURE_2D, iHeader.tex);
 
   // Loop through characters
   for (TInt i = 0; i < aText.Length(); i++)
   {
      // Make sure character is in range
      TText8 c = aText [i];
      if (c < iHeader.startChar || c > iHeader.endChar)
         continue;
 
      // Get pointer to glFont character
      GLFontChar* fontChar = &iHeader.chars [c - iHeader.startChar];
 
      // Get width and height
      GLfixed width =  MultiplyFixed (fontChar->dx, IntToFixed (iHeader.texWidth) );
      GLfixed height = MultiplyFixed (fontChar->dy, IntToFixed (iHeader.texHeight) );
 
      // Specify texture coordinates
      texCoords [0] = fontChar->tx1; texCoords [1] = fontChar->ty1;
      texCoords [2] = fontChar->tx1; texCoords [3] = fontChar->ty2;
 
      texCoords [4] = fontChar->tx2; texCoords [5] = fontChar->ty2;
      texCoords [6] = fontChar->tx2; texCoords [7] = fontChar->ty1;
 
      // and vertices
      vertices [0] = aX;         vertices [1] = aY;
      vertices [2] = aX;         vertices [3] = aY - height;
   
      vertices [4] = aX + width; vertices [5] = aY - height;
      vertices [6] = aX + width; vertices [7] = aY;
 
      // draw
      glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
 
      // Move to next character
      aX += width;
   }
 
}

And here is the fixed-point math operations required by this code:

//------------------------------
#ifndef FIXED_MATH_H_
#define FIXED_MATH_H_
//------------------------------
 
// INCLUDES
#include <e32base.h>
#include <e32std.h>
#include <e32math.h>
#include <GLES/gl.h>   
 
// FUNCTIONS
 
inline GLfixed IntToFixed (GLint aValue)
{ return aValue << 16; }
 
 
inline GLfixed FloatToFixed (GLfloat aValue)
{ return (GLfixed) (aValue * 65536.0f); }
 
 
inline GLint FixedToInt (GLfixed aValue)
{ return aValue >> 16; }
 
 
inline GLfloat FixedToFloat (GLfixed aValue)
{ return (GLfloat) (aValue * (1 / 65536.0f)); }
 
 
inline GLfixed MultiplyFixed (GLfixed op1, GLfixed op2) 
{
  TInt64 r = (TInt64)op1 * (TInt64)op2;
  return (GLfixed) (r >> 16);
}
 
 
//------------------------------
#endif

Notes and limitations

This font class is intended to render 2D text. So, it is important to set up the required orthographic projection before rendering. The following code is an example of how to do this:

// Rect() is a method that returns the current drawing rectangle.
// The (0,0) point will be the on bottom-left corner.
 
glViewport (0, 0, Rect().Width(), Rect().Height());
	
glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  glOrthox (0, IntToFixed (Rect().Width()), 0, IntToFixed (Rect().Height()), -1, 1);
	
 glMatrixMode (GL_MODELVIEW);
   glLoadIdentity ();

Also, the BeginDraw() and EndDraw() methods (or equivalent code) should be called to set up required states for rendering. For example, if alpha blending is not enabled, the quad corresponding to the character becomes noticeable.

Currently, this class supports 8 bit descriptors only. The class should be extended to support 16 bits descriptors and resource strings.

Rate This

 
Bookmark this page: DeliciousDiggFacebookGoogleYahooStumbleUponRedditFurlTechnocratiMagnoliaTwitter  Share this page Share this page Print this Page Print this page Invite a friend Invite a friend
Email Newsletters Press Terms & Conditions Privacy Policy Sitemap Contact Us © 2009 Nokia 
RDF Facets: qdcZdescriptionQSxEa0E20WikiE20javaE20symbianE5fosE20s60E20maemoE20cE2bE2bE20WikiE20HomeE20WikiE20HelpE20OverviewE20GlossaryE20CreateE20PageE20ProposeE20anE20ArticleE20SpotlightE20TopicE20E2dE20WE52TE20WidgetsE20ProgrammingE20E4canguageE20E2dE20SymbianE20CE2bE2bE20E2dE20OpenE20CE2fCE2bE2bE20E2dE20JavaE20E2dE20FlashE20E4citeE20E2dE20PythonE20WebE20TechnologiesE20E2dE20WE52TE20WidgetsE20E2dE20WidSetsE20ToolsE20andE20SE44KE20CodeE20E45E78amplesE20KnowledgeE20BaseE20TechnologyE20AreasE20SoftwareE20PlatformsE20E44evelopmentE20ProcessE20E3fE3fWikiE20ChineseE20E3fE3fE3fWikiE20JapaneseE20PortugueseE2fBrazilianE20E52ussianE20WhatE20linksE20hereE20UploadE20fileE20SpecialE20pagesE20PrintableE20versionE44ownloadE20asE20PE44FE20GoE20ToE20E2eE2eE2eX qdcZidentifierQSxhttpE3aE2fE2fwikiE2eforumE2enokiaE2ecomE2findeE78E2ephpE2fIntroductionE5ftoE5fOpenGE4cE5fE45SX qdcZpublisherQUxhttpE3aE2fE2fswE2enokiaE2ecomE2fidE2fc764fd1cE2d8b06E2d499aE2d9a6aE2d17c3903d5a65E2fforumE5fnokiaE5fcrawlerE5fagentX qdcZtitleQSxIntroductionE20toE20OpenGE4cE20E45SE20E2dE20ForumE20NokiaE20WikiX qdcZtypeQUqfnZE45E78cludedFromGeneralE4cistingsQ qdcZtypeQUqfnTypeZCommunityContentQ qdcZtypeQUqfnTypeZE52esourceQ qdcZtypeQUqfnTypeZWebpageQ qdcZtypeQUqfnTypeZWikiContentQ qdcZtypeQUqmarsZManagedE52esourceQ qdcZtypeQUqwebZInformationE52esourceQ qdcZtypeQUqwebZPageQ qdcZtypeQUqwebZE52esourceQ qdcZtypeQUqrdfsZE52esourceQ qrssZdescriptionQSxEa0E20WikiE20javaE20symbianE5fosE20s60E20maemoE20cE2bE2bE20WikiE20HomeE20WikiE20HelpE20OverviewE20GlossaryE20CreateE20PageE20ProposeE20anE20ArticleE20SpotlightE20TopicE20E2dE20WE52TE20WidgetsE20ProgrammingE20E4canguageE20E2dE20SymbianE20CE2bE2bE20E2dE20OpenE20CE2fCE2bE2bE20E2dE20JavaE20E2dE20FlashE20E4citeE20E2dE20PythonE20WebE20TechnologiesE20E2dE20WE52TE20WidgetsE20E2dE20WidSetsE20ToolsE20andE20SE44KE20CodeE20E45E78amplesE20KnowledgeE20BaseE20TechnologyE20AreasE20SoftwareE20PlatformsE20E44evelopmentE20ProcessE20E3fE3fWikiE20ChineseE20E3fE3fE3fWikiE20JapaneseE20PortugueseE2fBrazilianE20E52ussianE20WhatE20linksE20hereE20UploadE20fileE20SpecialE20pagesE20PrintableE20versionE44ownloadE20asE20PE44FE20GoE20ToE20E2eE2eE2eX qfnZdistributionQUxhttpE3aE2fE2fwikiE2eforumE2enokiaE2ecomE2fX qfnZtopicQUqfnTopicZ3dQRqdcZtypeQUqrdfsZE52esourceQRqmarsZrelevanceQNx100X qfnZtopicQUqfnTopicZentertainmentQRqmarsZrelevanceQNx100X qfnZtopicQUqfnTopicZgamesQRqdcZtypeQUqrdfsZE52esourceQRqmarsZrelevanceQNx100X qfnZtopicQUqfnTopicZgraphicsQRqmarsZrelevanceQNx100X qfnZtypeQUqfnTypeZCommunityContentQ qfnZtypeQUqfnTypeZE52esourceQ qfnZtypeQUqfnTypeZWebpageQ qfnZtypeQUqfnTypeZWikiContentQ qfnZupdatedQDx2008E2d10E2d02X qfnZuserE5ftagQSx3dX qfnZuserE5ftagQSxentertainmentX qfnZuserE5ftagQSxgamesX qfnZuserE5ftagQSxgraphicsX qmarsZdescriptionQSxEa0E20WikiE20javaE20symbianE5fosE20s60E20maemoE20cE2bE2bE20WikiE20HomeE20WikiE20HelpE20OverviewE20GlossaryE20CreateE20PageE20ProposeE20anE20ArticleE20SpotlightE20TopicE20E2dE20WE52TE20WidgetsE20ProgrammingE20E4canguageE20E2dE20SymbianE20CE2bE2bE20E2dE20OpenE20CE2fCE2bE2bE20E2dE20JavaE20E2dE20FlashE20E4citeE20E2dE20PythonE20WebE20TechnologiesE20E2dE20WE52TE20WidgetsE20E2dE20WidSetsE20ToolsE20andE20SE44KE20CodeE20E45E78amplesE20KnowledgeE20BaseE20TechnologyE20AreasE20SoftwareE20PlatformsE20E44evelopmentE20ProcessE20E3fE3fWikiE20ChineseE20E3fE3fE3fWikiE20JapaneseE20PortugueseE2fBrazilianE20E52ussianE20WhatE20linksE20hereE20UploadE20fileE20SpecialE20pagesE20PrintableE20versionE44ownloadE20asE20PE44FE20GoE20ToE20E2eE2eE2eX qmarsZlanguageQUxhttpE3aE2fE2fswE2enokiaE2ecomE2flanguageE2d1E2fenX qrdfZtypeQUqfnZE45E78cludedFromGeneralE4cistingsQ qrdfZtypeQUqfnTypeZCommunityContentQ qrdfZtypeQUqfnTypeZE52esourceQ qrdfZtypeQUqfnTypeZWebpageQ qrdfZtypeQUqfnTypeZWikiContentQ qrdfZtypeQUqmarsZManagedE52esourceQ qrdfZtypeQUqwebZInformationE52esourceQ qrdfZtypeQUqwebZPageQ qrdfZtypeQUqwebZE52esourceQ qrdfZtypeQUqrdfsZE52esourceQ
User Rating: qfnZuserE5FratingQNx5E2E0000X