Jump to content
  • Advertisement
Sign in to follow this  
Funkapotamus

OpenGL For Reference: Using SDL_ttf with OpenGL #2

This topic is 4004 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

This post is a continuation of the wonderful thread started by C-Junkie found here: http://www.gamedev.net/community/forums/topic.asp?topic_id=284259 Thanks to that thread, I was able to get SDL_ttf up and running. However, I was disappointed by the method's speed. Creating, binding, blitting, and freeing a new OpenGL texture for each string of output was hurting framerate. So, I combined it with the good old .bmp font/display list method. This will load a .ttf font using SDL_ttf, and make a 512x512 pixel texture that contains every character from ascii value 32 to 128. It then builds display lists for each character so you can render text just like you would via the traditional .bmp font method. See a picture of it here. Code:
const int OFFSET = 32;
const int MAX_ASCII = 128;
TTF_Init();
TTF_Font* font = TTF_OpenFont("arial.ttf", 40);      

// it's best to set the width to the size of the font specified above
float charWidth        = 40;
// make the height a little bit higher since we'll be auto aligning letters that go above/below the baseline and they may trail over
float charHeight       = 50;
// characters are very small.  it's best to make the font large and scale it down rather than make it small and blow it up
float textureWidth     = 512;
float textureHeight    = 512;

/* create letters */
// set letter color to white
SDL_Color color = { 255, 255, 255, 255 };      
// create glyphs, from ' ' (space) to '~' (tilde)
std::vector<SDL_Surface*> glyphCache;         
for(int i = OFFSET; i < MAX_ASCII; i++)
   glyphCache.push_back(TTF_RenderGlyph_Blended(font, i, color));

/* create destination surface */
SDL_Surface* destination = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)textureWidth, (int)textureHeight, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);      

/* blit glyphs onto destination */      
SDL_Rect glyphRect;
int row = 0;   // 10 chars per row
int col = 0;   
for(int i = OFFSET; i < MAX_ASCII; i++) {
   int minx, maxx, miny, maxy, advance;
   TTF_GlyphMetrics(font, i, &minx, &maxx, &miny, &maxy, &advance);
   glyphRect.x = (int)(col * charWidth);
   glyphRect.y = (int)(row * charHeight + TTF_FontAscent(font) - maxy);                  

   SDL_BlitSurface(glyphCache[i - OFFSET], 0, destination, &glyphRect);

   if(col >= 10) {
      col = 0;
      row++;
   }
   else
      col++;      
}

/* create texture */
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);   
glTexImage2D(GL_TEXTURE_2D, 0, 4, (int)textureWidth, (int)textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, destination->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);      

/* create display lists */   
GLuint* chars = new GLuint[MAX_ASCII];
int* charWidths = new int[MAX_ASCII];

row = 0;
col = 0;
for(int i = 0; i < MAX_ASCII; i++) {
   int id = glGenLists(1);
   chars = id;

   int minx, maxx, miny, maxy, advance;
   TTF_GlyphMetrics(font, i + OFFSET, &minx, &maxx, &miny, &maxy, &advance);

   float minX = charWidth * col / textureWidth;
   float maxX = (advance) / textureWidth + minX;
   float minY = charHeight / textureHeight * row;
   float maxY = minY + charHeight / textureHeight;

   charWidths = advance;

   glNewList(id, GL_COMPILE); 
      glBegin(GL_QUADS);
         glTexCoord2f(minX, maxY);   glVertex2f(0.0f, 0.0f);       
         glTexCoord2f(maxX, maxY);   glVertex2f(advance, 0.0f);      
         glTexCoord2f(maxX, minY);   glVertex2f(advance, charHeight);   
         glTexCoord2f(minX, minY);   glVertex2f(0.0f, charHeight);
      glEnd();
      glTranslatef(advance + 1, 0.0f, 0.0f);
   glEndList();

   if(col >= 10) {
      col = 0;
      row++;
   }
   else
      col++;   
}

/* free/kill everything */
for(unsigned int i = 0; i < glyphCache.size(); i++)
   SDL_FreeSurface(glyphCache);
SDL_FreeSurface(destination); 
TTF_Quit();


/* .... */
/* Get the char's list */
GLuint getList(char c) {
   return chars[c - OFFSET];				
}

/* ... */
/* Usage */
string msg = "Do a barrel roll!";
for(unsigned int i = 0; i < msg.length(); i++)
   glCallList(getList(msg.c_str()));




You'll note I store the width of each character in "int* charWidths". Using this information, it'd be very easy to display text that's centered at a given position, or left/right justified.

Share this post


Link to post
Share on other sites
Advertisement
AFAIK, you will have some kerning problems with that approach. Also, for languages that are NOT English, this is impossible. I've been working on a project where Arabic text shaping and OpenGL rendering was required, and the whole system was limited to 192 MBs of RAM, so that approach was COMPLETELY impractical. So far, the best solution I could come up with is to transfer the texture asynchronously via PBOs. If anyone has a better idea, please jump in.

Oh, and kudos for picking "Do a barrel roll!" to be the string of choice ;)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!