for reference: Using SDL_ttf with OpenGL

Started by
24 comments, last by AlasdairDeacon 13 years, 1 month ago
Hm. Interesting...
C-Junkie, PM me if you are interested in seeing that added to my library...
HxRender | Cornerstone SDL TutorialsCurrently picking on: Hedos, Programmer One
Advertisement
Very useful. I'm using in my project now. :D Thanks man!!
(necro...?)

Yes, it's nice and simple solution, hovewer, rendering each frame the same text on SDL_Surface by TTF_RenderText (which allocates memory -> which is slow), creating new texture, blitting one to another etc. was (in case of my code) horribly slow.

First I decided to add special function that would render text only once, and that rendered texture could be reused. But (by coincidence) I found on SDL mailing list one mail from Bob Pendleton, who has written SDL_TFF + OpenGL text rendering engine, by caching glyphs that were previously used. I've built similiar system, and it's working amazingly fast - renders multiple lines of text (yup, multiline rendering) without losing one frame, while for previous one the ratio was 5 frames / one function call :-/

I could post it here if anyone was interested... but be warned, it's rather big... very big piece of code :-)
Don't interest. Post-it! Post-it! :D

I see here too that example above is a bit slow, but anyways, it's working...

Ok... you asked for it :-)
Remember though that you probably won't be able to use it after simple copy & paste, there are too many things and references to things that are specific to my engine. If you want to see UML diagram of my font manager, it's here:




Header of file fontMgr.h, contains interface of FontMgr, which is manager responsible for loading, managing, releasing etc. fonts and for passing them to Renderer.

#ifndef FONTMGR_H#define FONTMGR_H #include "SDL_ttf.h"#include <map>#include <vector>#include <string>#include "../../Others/Misc/singleton.h"#include "tFontObject.h"// ------------------------------------------------- /*! \file fontMgr.h     \brief File tha contains interface of font system.*///! Use this macro to get access to font system#define getFontMgr SC::FontMgr::get()namespace SC  {   // -------------------------------------------------         typedef std :: vector <usint> FontSizeList;       // -------------------------------------------------         //! Singleton class that internally manages loading and accessing fonts  class FontMgr : public Singleton<FontMgr>   {    public:         friend class Renderer;        void Init();     void Shutdown();    // -------------------------------------------------           //! Mod architecture prefix is added after loading, however you can access font     //! using name you've passed here (that is used only during loading);     void LoadFont(const char * _fontName, const FontSizeList & _sizes );          //! Releases only one font _fon     void ReleaseFont(const char * _fontName, const FontSizeList & _sizes);          //! Releases all actually loaded fonts     void ReleaseAllFonts();     // -------------------------------------------------      //! Returns true if specified font _fontName exists at all (if _size == -1)     //! If _size != -1 then it checks for font _fontName with specified _size     //bool FontExists(const char * _fontName, ssint _size = -1);      // -------------------------------------------------            private:             //! Used only by Renderer.     //! Returned value isn't modified but due to problems making one function const-correct rest had to adjust     //! itself (I forgot about mutable :-) )     TGlyphsContainer * GetGlyphsContainer(const char * _name, usint _size); // -------------------------------------------------       // map of fonts          typedef std :: map <std :: string, TFontObject> FontsMap;     typedef FontsMap :: iterator FontsMapItor;     typedef std :: pair <std :: string, TFontObject> FontPair;     // -------------------------------------------------                          FontsMap fonts;      };    }#endif


Implementation of FontMgr.

#include <string>#include "fontMgr.h"#include "../../Others/Logger/logger.h"#include "../../Others/Misc/misc.h"#include "../../Layer1/FileIO/genericFileIO.h"#include "../../Others/Misc/skinSystem.h"#include "../../Layer1/MemoryMgr/mmgr.h"using namespace std;// ------------------------------------------------- start Initvoid SC :: FontMgr :: Init() {    if ( TTF_Init() == -1)    {    string tmp("Couldn't init SDL_TTF subsystem, reason: ");    tmp += TTF_GetError();    logError2("FontMgr", tmp.c_str());    throw false;   }    log2("FontMgr", "Succesfully created.")  } // ------------------------------------------------- end Init// ------------------------------------------------- start Shutdown  void SC :: FontMgr :: Shutdown() {    if (!fonts.empty())   {    logError2("FontMgr", "There are unreleased fonts in the map:")            ReleaseAllFonts();                   }     TTF_Quit();  log2("FontMgr", "Succesfully destroyed.")  } // ------------------------------------------------- end Shutdown // ------------------------------------------------- start LoadFontvoid SC :: FontMgr :: LoadFont(const char * _fontName, const FontSizeList & _sizes ) {  string fullFontName(getSkinSystem.GetDirectoryPrefix());  fullFontName += _fontName;    if (!fileExists(fullFontName.c_str()))   {    string tmp("Couldn't find font file named: ");    tmp += fullFontName;    logError2("FontMgr", tmp.c_str());    return;   }         string msg("Loading font: ");  msg += _fontName;  log2("FontMgr", msg.c_str())    TFontObject * fontPtr = 0;    TFontObject font(fullFontName.c_str());      FontsMapItor fontsItor = fonts.find(_fontName);    if (fontsItor == fonts.end())   fontPtr = &font;  else    fontPtr = &fontsItor->second;      for (usint i = 0; i < _sizes.size(); ++i)   {    if (!fontPtr->LoadFont(_sizes))     {      string errMsg("Couldn't load font: ");      errMsg += fullFontName;      errMsg += ", using specified font size: ";      const char * _fontSize = usintToString(_sizes);      errMsg += _fontSize;            logError2("FontMgr", errMsg.c_str())      delete [] _fontSize;      continue;     }               }    if (fontsItor == fonts.end())   fonts.insert(FontPair(_fontName, font));    }// ------------------------------------------------- end LoadFont// ------------------------------------------------- start ReleaseFont                 void SC :: FontMgr :: ReleaseFont(const char * _fontName, const FontSizeList & _sizes) {  FontsMapItor fontsItor = fonts.find(_fontName);    if (fontsItor == fonts.end())   {    string tmp ("Couldn't find font to release it: ");    tmp += _fontName;    logError2("FontMgr", tmp.c_str())   }    else   {            for (usint i = 0; i < _sizes.size(); ++i)     {              fontsItor->second.ReleaseFont(_sizes);     }         if (fontsItor->second.CanBeReleased())      {      fonts.erase(fontsItor);            string tmp ("Released font: ");      tmp += _fontName;      log2("FontMgr", tmp.c_str())     }   }    }// ------------------------------------------------- end ReleaseFont// ------------------------------------------------- start ReleaseAllFonts     void SC :: FontMgr :: ReleaseAllFonts() {  FontsMapItor fontsItor;    beginG("Releasing fonts")    for (fontsItor = fonts.begin(); fontsItor != fonts.end(); ++fontsItor)   {    log2("Font", fontsItor->first.c_str());    fontsItor->second.ForceReleaseAll();       }     endG;     fonts.clear();     }     // ------------------------------------------------- end ReleaseAllFonts// ------------------------------------------------- start GetGlyphsContainer        SC :: TGlyphsContainer * SC :: FontMgr :: GetGlyphsContainer(const char * _name, usint _size) {  FontsMapItor fontsItor = fonts.find(_name);    if (fontsItor == fonts.end())   {    string tmp ("Couldn't find font: ");    tmp += _name;    logError2("FontMgr", tmp.c_str())    return 0;   }    else   {    return (fontsItor->second.GetGlyphsContainer(_size));   }    }// ------------------------------------------------- end GetGlyphsContainer


Header file tFontObject.h, interface of single "font".

#ifndef TFONTOBJECT_H#define TFONTOBJECT_H#include <map>#include <string>#include "tGlyphsContainer.h"// -------------------------------------------------namespace SC {// -------------------------------------------------  class TFontObject   {    public:                           TFontObject(const char * _fontName) : fontName(_fontName) { }                        bool LoadFont(usint _size);     // -------------------------------------------------               void ReleaseFont(usint _size);          void ForceReleaseAll();     bool CanBeReleased() const { return glyphContainers.empty(); }          // -------------------------------------------------              TGlyphsContainer * GetGlyphsContainer( usint _size);                               private:                typedef std :: map<usint, TGlyphsContainer> GlyphContainersMap;    typedef GlyphContainersMap :: iterator GlyphContainersMapItor;        GlyphContainersMap glyphContainers;    std :: string fontName;           }; // -------------------------------------------------  }#endif


Implementation of above.

#include "tFontObject.h"#include "../../Others/logger/logger.h"#include "../../Layer1/MemoryMgr/mmgr.h"// ------------------------------------------------- start LoadFontbool SC :: TFontObject :: LoadFont(usint _size) {  GlyphContainersMapItor itor;    itor = glyphContainers.find(_size);    if (itor == glyphContainers.end())   {        TGlyphsContainer newGlyph(fontName.c_str(), _size);        if (!newGlyph.Load())     return false;    else     {      glyphContainers.insert(std :: pair<usint, TGlyphsContainer>(_size, newGlyph) );      return true;     }   }    itor->second.Load();  return true; } // ------------------------------------------------- end LoadFont// ------------------------------------------------- start ReleaseFontvoid SC :: TFontObject :: ReleaseFont(usint _size) {  GlyphContainersMapItor itor = glyphContainers.find(_size);     if (itor == glyphContainers.end())   {    debug("Not found font that had to be released.")    return;   }     if (itor->second.Release())   glyphContainers.erase(itor);                                 }// ------------------------------------------------- end ReleaseFont// ------------------------------------------------- start ForceReleaseAllvoid SC :: TFontObject :: ForceReleaseAll() {  for (GlyphContainersMapItor itor = glyphContainers.begin(); itor != glyphContainers.end(); ++itor)   itor->second.ForceRelease();    glyphContainers.clear(); } // ------------------------------------------------- end ForceReleaseAll// ------------------------------------------------- start GetGlyphsContainerSC :: TGlyphsContainer * SC :: TFontObject :: GetGlyphsContainer( usint _size) {  GlyphContainersMapItor itor = glyphContainers.find(_size);    if (itor == glyphContainers.end())   return 0;  else return (&(itor->second));  }// ------------------------------------------------- end GetGlyphsContainer



Now where things are starting to be interesting... tGlyphsContainer.h

#ifndef TGLYPHSCONTAINER_H#define TGLYPHSCONTAINER_H#include <vector>#include <string>#include <gl/gl.h>#include "../../Layer1/Renderer/renderer.h"#include "SDL_ttf.h"// -------------------------------------------------namespace SC {         // -------------------------------------------------    struct GlyphTexture   {    GlyphTexture() : textureID(0) { }        GLuint textureID;    usint width, height;    int charWidth, charHeight;    int advance;   };  // -------------------------------------------------      struct TGlyph   {        void ReleaseTextures() const;             uchar character;        GlyphTexture glyphsSolid[8];    GlyphTexture glyphsBlended[8];   };// -------------------------------------------------  class TGlyphsContainer   {    public:         TGlyphsContainer(const char * _fontName, usint _size) : refCount(0), font(0), fontName(_fontName), size(_size) { }// -------------------------------------------------      //! returns false if loading TTF font wasn't successfull      bool Load();            //! returns true if this glyph container can be deleted      bool Release();            //! Releases font even if it's reference counter is != 0.      void ForceRelease();            GlyphTexture * GetGlyphTexture(uchar _character, ulint _style, eTextRenderMode _renderMode);            ssint GetFontDescent() const { return descent; }      usint GetFontLineSkip() const { return lineSkip; }      usint GetFontHeight() const { return TTF_FontHeight(font); }  // cache it?    private:         //!      void PrerenderGlyph( uchar _character, ulint _style, eTextRenderMode _renderMode, TGlyph & _glyph);               void CacheGlyph( SDL_Surface * tmp, GlyphTexture & _glyph) const;                  std :: vector <TGlyph> glyphs;     std :: string fontName;          uchar refCount;     TTF_Font * font;     usint size;     ssint descent;     usint lineSkip; // vertical distance between multi line text output        }; // -------------------------------------------------   }#endif


Probably most important file, contains implementation of clasess from tGlyphsContainer.h; they're responsible for aformentioned "glyph caching".

#include "tGlyphsContainer.h"#include "../../Others/logger/logger.h"#include "../../Layer1/MemoryMgr/mmgr.h"// ------------------------------------------------- start ReleaseTexturesvoid SC :: TGlyph :: ReleaseTextures() const {  GLuint textures[16];       for (usint i = 0; i < 8; ++i)   textures = glyphsBlended.textureID;                  for (usint i = 8; i < 16; ++i)   textures = glyphsSolid.textureID;     glDeleteTextures(16, textures); }// ------------------------------------------------- end ReleaseTextures// ------------------------------------------------- // TGlyphsContainer// ------------------------------------------------- // ------------------------------------------------- start Loadbool SC :: TGlyphsContainer :: Load() {  if (font)   {    ++refCount;    return true;   }    else   {    font = TTF_OpenFont(fontName.c_str(), size);        if (!font)     {      return false;     }                descent = TTF_FontDescent(font);    lineSkip = TTF_FontLineSkip(font);    refCount = 1;        return true;   }  } // ------------------------------------------------- end Load// ------------------------------------------------- start Releasebool SC :: TGlyphsContainer :: Release() {  --refCount;    if (refCount == 0)   {    SC_ASSERT(font)    TTF_CloseFont(font);    return true;   }    else return false;   }// ------------------------------------------------- end Release// ------------------------------------------------- void SC :: TGlyphsContainer :: ForceRelease() {  TTF_CloseFont(font);    std :: vector <TGlyph> :: iterator itor;    for ( itor = glyphs.begin(); itor != glyphs.end(); ++itor)   (*itor).ReleaseTextures();   }// ------------------------------------------------- // ------------------------------------------------- start GetGlyphTextureSC :: GlyphTexture * SC :: TGlyphsContainer :: GetGlyphTexture(uchar _character, ulint _style, eTextRenderMode _renderMode) {  std :: vector <TGlyph> :: iterator itor;    for ( itor = glyphs.begin(); itor != glyphs.end(); ++itor)   { // -------------------------------------------------             if (itor->character == _character)     {            if (_renderMode == trm_Blend)       {                if ((*itor).glyphsBlended[_style].textureID)         return (&(*itor).glyphsBlended[_style]);                 else         {          PrerenderGlyph(_character, _style, _renderMode, (*itor));          return (&(*itor).glyphsBlended[_style]);         }                        }         else if (_renderMode == trm_Solid)       {               if ((*itor).glyphsSolid[_style].textureID)         return (&(*itor).glyphsSolid[_style]);                 else         {                              PrerenderGlyph(_character, _style, _renderMode, (*itor));          return (&(*itor).glyphsSolid[_style]);         }                        }              SC_ASSERT(!"What, you added new enum value and forgot to update TGlyphsContainer?!")                          }// -------------------------------------------------         }     // we didn't found _character, so it's new glyph, first insert new to vector, then render and finally return       TGlyph newGlyph;    PrerenderGlyph(_character, _style, _renderMode, newGlyph);  newGlyph.character = _character;  glyphs.push_back(newGlyph);        if (_renderMode == trm_Blend)   {    return (&newGlyph.glyphsBlended[_style]);   }    else     if (_renderMode == trm_Solid)   {                                                  return (&newGlyph.glyphsSolid[_style]); // how this can work?! we're returning adress of local variable... lol   }          SC_ASSERT(!"What am I doing here?") }// ------------------------------------------------- end GetGlyphTexture// ------------------------------------------------- start PrerenderGlyphvoid SC :: TGlyphsContainer :: PrerenderGlyph( uchar _character, ulint _style, eTextRenderMode _renderMode, TGlyph & _glyph) {   TTF_SetFontStyle(font, _style);                // it doesn't matter, real color will be later set by Renderer  SDL_Color color = {255, 255, 255, 0};      char letter[2] = { _character, 0};    SDL_Surface * tmp;    if (_renderMode == trm_Solid)   {    tmp = TTF_RenderText_Solid( font, letter, color);    SC_ASSERT(tmp)    CacheGlyph(tmp, _glyph.glyphsSolid[_style]);    TTF_GlyphMetrics(font, (Uint16)_character, 0, 0, 0, 0, &_glyph.glyphsSolid[_style].advance);   }  else    {    tmp = TTF_RenderText_Blended( font, letter, color);    SC_ASSERT(tmp)    CacheGlyph(tmp, _glyph.glyphsBlended[_style]);    TTF_GlyphMetrics(font, (Uint16)_character, 0, 0, 0, 0, &(_glyph.glyphsBlended[_style].advance));   } }// ------------------------------------------------- end PrerenderGlyph// ------------------------------------------------- start RenderGlyphSolidvoid SC :: TGlyphsContainer :: CacheGlyph(SDL_Surface * tmp, GlyphTexture & _glyph) const {  usint w = tmp->w;    usint h = tmp->h;  SDL_Surface * image = tmp;    _glyph.charWidth = w;  _glyph.charHeight = h;        bool IsWidth = IsPowerOfTwo(w);  bool IsHeight = IsPowerOfTwo(h);           if ((!IsWidth) || (!IsHeight))   {       if (!IsWidth) w = PowerOfTwo(w);	if (!IsHeight) h = PowerOfTwo(h);		image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);			if ( image == 0 ) 	 {      logError2("FontMgr", "Couldn't allocate memory for expanding glyph texture to power of 2")      SDL_FreeSurface(tmp);	  SC_ASSERT(!"Couldn't allocate memory for expanding texture to power of 2")	  return;	 }  	         SDL_Rect area;	area.x = 0;	area.y = 0;	area.w = tmp->w;	area.h = tmp->h;	// copy the tmp into the GL texture image	SDL_BlitSurface(tmp, &area, image, &area);				SDL_FreeSurface(tmp);		tmp = 0;   }     glGenTextures(1, &_glyph.textureID);    glBindTexture(GL_TEXTURE_2D, _glyph.textureID);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);             if (tmp) SDL_FreeSurface(tmp);   else SDL_FreeSurface(image);     _glyph.width = w;  _glyph.height = h;     }// ------------------------------------------------- end RenderGlyphSolid


And the last (but not least one), some functions from Renderer, they are doing actual rendering to the screen. I'm doing 2d game, so they don't need to change view matrices all the time - I've assumed that when you call them, you have ortographic projection enabled etc.

// ------------------------------------------------- start RenderTextvoid SC :: Renderer :: RenderText(const char * _text, const char * _fontName, usint _size, const TScrPoint & _position,                                  const TColor4 & _color, ulint _style, eTextPosition _horizontal,                                  eTextPosition _vertical, eTextRenderMode _renderMode) {  SC_ASSERT(_text)  SC_ASSERT(_fontName)  SC_ASSERT(_size > 0)    TGlyphsContainer * glyphs = getFontMgr.GetGlyphsContainer(_fontName, _size);    if (!glyphs)   {    logError2("Renderer", "Couldn't find font to output text.")    return;   }   // -------------------------------------------------    glColor4ub(_color.r, _color.g, _color.b, _color.a);      usint xPos = _position.x;  usint yPos = _position.y;     const char * txtPtr = _text;      // this precaching of glyphs is needed to determine full size of rendered text, it's needed to  // render them properly with given eTextPosition stuff  std :: vector <GlyphTexture*> cachedGlyphs;  TScrPoint renderedText;  TScrPntSet(renderedText, 0, 0);  while (*txtPtr)   {    GlyphTexture * glyph = glyphs->GetGlyphTexture(*txtPtr, _style, _renderMode);    TScrPntAddX(renderedText, renderedText, glyph->advance );        renderedText.y = Max(glyph->charHeight, (int) renderedText.y);        cachedGlyphs.push_back(glyph);    ++txtPtr;   }   // ------------------------------------------------- HORIZONTAL JUSTIFICTION      if (_horizontal == tp_Centered)  // center text    xPos -= (renderedText.x)/2;   else if (_horizontal == tp_Right) // justify to right         xPos -= renderedText.x;    // ------------------------------------------------- VERTICAL JUSTIFICTION      yPos += glyphs->GetFontDescent();      if (_vertical == tp_Centered)  // center text    yPos -= (renderedText.y)/4;   else if (_vertical == tp_Right) // justify to right         yPos -= renderedText.y/2;     // -------------------------------------------------      std :: vector <GlyphTexture*> :: iterator itor = cachedGlyphs.begin();     while (itor != cachedGlyphs.end())   {    GlyphTexture * glyph = (*itor);    SC_ASSERT(glyph)        glBindTexture(GL_TEXTURE_2D, glyph->textureID);        float rightMargin = (float) glyph->charWidth /(float)glyph->width;    float upMargin = (float) glyph->charHeight / (float)glyph->height;    // -------------------------------------------------   glBegin(GL_QUADS);        glTexCoord2f(0.0f, 0.0f);	glVertex3i( xPos, yPos + glyph->charHeight, 1);    	glTexCoord2f(0.0f, upMargin);	glVertex3i( xPos, yPos, 1);		glTexCoord2f(rightMargin, upMargin);	glVertex3i( xPos + glyph->charWidth, yPos, 1);		          glTexCoord2f(rightMargin, 0.0f);	glVertex3i( xPos + glyph->charWidth, yPos + glyph->charHeight, 1);	   glEnd(); // -------------------------------------------------            xPos += glyph->advance;        ++itor;   }      glBindTexture(GL_TEXTURE_2D, 0);  boundTexture.id = 0;  glColor4ub(255, 255, 255, 255); }// ------------------------------------------------- end RenderText// ------------------------------------------------- start RenderTextMultivoid SC :: Renderer :: RenderTextMulti(const char * _text, const char * _fontName, usint _size,                                       const TScrPoint & _position, const TColor4 & _color, ulint _style,                                        eTextPosition _horizontal, eTextRenderMode _renderMode) {  SC_ASSERT(_text)  SC_ASSERT(_fontName)  SC_ASSERT(_size > 0)    TGlyphsContainer * glyphs = getFontMgr.GetGlyphsContainer(_fontName, _size);    if (!glyphs)   {    logError2("Renderer", "Couldn't find font to output text.")    return;   }     usint lineSkip = glyphs->GetFontLineSkip();// ------------------------------------------------- std :: vector < std :: string> vLines; // these are the individual lines of text std :: string text(_text); int n = 0; // Break the string into its lines, remember that it's different from std C++ language: end of line // is determined by "/n" not "\n" existance! while( n != -1 )  {   // Get until either '/n' or '\0'   std :: string strSub;   n = text.find( "/n", 0 ); // Find the next "/n"   strSub = text.substr( 0, n);   if( n != -1 )    text = text.substr( n+2, std::string::npos );       vLines.push_back(strSub);  }        // -------------------------------------------------    glColor4ub(_color.r, _color.g, _color.b, _color.a);      usint xPos = _position.x;  usint yPos = _position.y;    yPos += glyphs->GetFontDescent();    for( usint i = 0; i < vLines.size(); i++ )   {       const char * txtPtr = vLines.c_str();    xPos = _position.x;        // this precaching of glyphs is needed to determine full size of rendered text, it's needed to    // render them properly with given eTextPosition stuff    std :: vector <GlyphTexture*> cachedGlyphs;    TScrPoint renderedText;    TScrPntSet(renderedText, 0, 0);    while (*txtPtr)     {      GlyphTexture * glyph = glyphs->GetGlyphTexture(*txtPtr, _style, _renderMode);      TScrPntAddX(renderedText, renderedText, glyph->advance );          //renderedText.y = Max(glyph->height, (usint) renderedText.y);      renderedText.y = Max(glyph->charHeight, (int) renderedText.y);          cachedGlyphs.push_back(glyph);      ++txtPtr;     }   // ------------------------------------------------- HORIZONTAL JUSTIFICTION       if (_horizontal == tp_Centered)  // center text     xPos -= (renderedText.x)/2;    else if (_horizontal == tp_Right) // justify to right          xPos -= renderedText.x;     // -------------------------------------------------       std :: vector <GlyphTexture*> :: iterator itor = cachedGlyphs.begin();      while (itor != cachedGlyphs.end())    {     GlyphTexture * glyph = (*itor);     SC_ASSERT(glyph)         glBindTexture(GL_TEXTURE_2D, glyph->textureID);         float rightMargin = (float) glyph->charWidth /(float)glyph->width;     float upMargin = (float) glyph->charHeight / (float)glyph->height;    // -------------------------------------------------    glBegin(GL_QUADS);         glTexCoord2f(0.0f, 0.0f);	 glVertex3i( xPos, yPos + glyph->charHeight, 1);    	 glTexCoord2f(0.0f, upMargin);	 glVertex3i( xPos, yPos, 1);		 glTexCoord2f(rightMargin, upMargin);	 glVertex3i( xPos + glyph->charWidth, yPos, 1);		           glTexCoord2f(rightMargin, 0.0f);	 glVertex3i( xPos + glyph->charWidth, yPos + glyph->charHeight, 1);	    glEnd(); // -------------------------------------------------            xPos += glyph->advance;        ++itor;   }     yPos -= lineSkip;       }      glBindTexture(GL_TEXTURE_2D, 0);  boundTexture.id = 0;  glColor4ub(255, 255, 255, 255);                                          }// ------------------------------------------------- end RenderTextMulti// ------------------------------------------------- start TextWidthSC :: ulint SC :: Renderer :: TextWidth(const char * _text, const char * _fontName, usint _size, ulint _style,                                        eTextRenderMode _renderMode) {  SC_ASSERT(_text)  SC_ASSERT(_fontName)  SC_ASSERT(_size > 0)    TGlyphsContainer * glyphs = getFontMgr.GetGlyphsContainer(_fontName, _size);    if (!glyphs)   {    logError2("Renderer", "Couldn't find font to count text width.")    return 0;   }   // -------------------------------------------------        std :: vector < std :: string> vLines; // these are the individual lines of text   std :: string text(_text);   int n = 0;   // Break the string into its lines, remember that it's different from std C++ language: end of line   // is determined by "/n" not "\n" existance!   while( n != -1 )    {     // Get until either '/n' or '\0'     std :: string strSub;     n = text.find( "/n", 0 ); // Find the next "/n"     strSub = text.substr( 0, n);     if( n != -1 )      text = text.substr( n+2, std::string::npos );     vLines.push_back(strSub);    }  ulint maxRenderedTextWidth = 0;    for (std :: vector<std :: string> :: iterator itor = vLines.begin(); itor != vLines.end(); ++itor)   {    const char * txtPtr = (*itor).c_str();    ulint renderedTextWidth = 0;    // sum width of all letters from _text    while (*txtPtr)    {     GlyphTexture * glyph = glyphs->GetGlyphTexture(*txtPtr, _style, _renderMode);     renderedTextWidth += glyph->advance;     ++txtPtr;    }        maxRenderedTextWidth = Max(maxRenderedTextWidth, renderedTextWidth);   }    return maxRenderedTextWidth; }// ------------------------------------------------- end TextWidth// ------------------------------------------------- start TextHeightSC :: ulint SC :: Renderer :: TextHeight(const char * _text, const char * _fontName, usint _size, ulint _style,                                        eTextRenderMode _renderMode) {  SC_ASSERT(_text)  SC_ASSERT(_fontName)  SC_ASSERT(_size > 0)    TGlyphsContainer * glyphs = getFontMgr.GetGlyphsContainer(_fontName, _size);    if (!glyphs)   {    logError2("Renderer", "Couldn't find font to count text width.")    return 0;   }   // -------------------------------------------------  std :: string text(_text); usint lineHeight = glyphs->GetFontHeight(); int n = 0; usint found = 1; // 1 not 0 because every string constists at least of one line of text while( n != -1 )  {   std :: string strSub;   n = text.find( "/n", 0 );   if( n != -1 )    {     text = text.substr( n+1, std::string::npos );     ++found;    }  }   return (found * lineHeight);    }// ------------------------------------------------- end TextHeight


And the really last one :-) test which shows how to use this system:

#include "SC.h"using namespace SC;class MyTask : public ITask {     public:      MyTask() {}   ~MyTask() {}      bool Start();   void Update();   void Stop() { }     };TScrPoint v1, v2; const usint fontSize = 20;    bool MyTask :: Start()  {      TScrPntSet(v1, 1, 300);        return true;  }   usint i = 400; eTextPosition pos = tp_Normal;  TColor4 color = { 0, 255, 0, 255 };  #define fontName "font.ttf" #define fontStyle trm_Blend   // -------------------------------------------------  void MyTask :: Update()  {     getRenderer.ClearBuffer(bt_Color);         TScrPntSet(v1, 400, 300);   getRenderer.RenderTextMulti("Man got his women/nto get his seed/nhe got the power/nshe got the need", fontName, fontSize,  v1, color, ts_Underline, tp_Right, fontStyle);           ulint time = getTimer.GetFPSCount();      const char * temp = ulintToString(time);             SetWindowCaption(temp);      delete [] temp;     }  //-------------------------------------------------- MyTask * task;int main(int argc, char ** argv) {   InitLib();        getRenderer.SetColorDepth(32);  getRenderer.SetFullscreen(0);  getRenderer.SetScreenSize(800, 600);   getRenderer.EnterGraphicsMode();       InitSubsystems();         FontSizeList sizes;    sizes.push_back(fontSize);    getFontMgr.LoadFont(fontName, sizes);      task = new MyTask;     SC_ASSERT(task)  task -> SetPriority(501);        		     getKernel.AddTask(task);      getKernel.Execute();    delete task;          getFontMgr.ReleaseFont(fontName, sizes);      getRenderer.CloseGraphicsMode();     ShutdownLib(); return 0; }


As you can see, 80% of above code isn't responsible for the actual text rendering :-/ rather for loading, releasing, managing fonts, reference counting etc. Anyway, I hope someone will find it usefull.
Koshmaar, that looks like exactly what I've been looking for

Thanks!
MSN: stupidbackup@hotmail.com (not actual e-mail)ICQ: 26469254(my site)
Don't thank me, thank Bob Pendleton :-) without his idea I wouldn't write it.

I've thought that for the sake of completness, I would post here also original font manager written by Bob (it's covered under LGPL)

class Font{private:  static const int minGlyph = ' ';  static const int maxGlyph = 126;  static int initCounter;  typedef struct  {    int minx, maxx;    int miny, maxy;    int advance;    SDL_Surface *pic;    GLuint tex;    GLfloat texMinX, texMinY;    GLfloat texMaxX, texMaxY;  } glyph;  int height;  int ascent;  int descent;  int lineSkip;  glyph glyphs[maxGlyph + 1];  unsigned char *address;  int length;  int pointSize;  int style;  float fgRed, fgGreen, fgBlue;  float bgRed, bgGreen, bgBlue;  TTF_Font *ttfFont;  SDL_Color foreground;  SDL_Color background;  void loadChar(char c)  {    GLfloat texcoord[4];    char letter[2] = {0, 0};    if ((minGlyph <= c) &&         (c <= maxGlyph) &&        (NULL == glyphs[((int)c)].pic))    {      SDL_Surface *g0 = NULL;      SDL_Surface *g1 = NULL;      letter[0] = c;      TTF_GlyphMetrics(ttfFont,                        (Uint16)c,                        &glyphs[((int)c)].minx,                        &glyphs[((int)c)].maxx,                        &glyphs[((int)c)].miny,                        &glyphs[((int)c)].maxy,                        &glyphs[((int)c)].advance);      g0 = TTF_RenderText_Shaded(ttfFont,                                  letter,                                  foreground,                                  background);      if (NULL != g0)      {        g1 = SDL_DisplayFormat(g0);        SDL_FreeSurface(g0);      }      if (NULL != g1)      {        glyphs[((int)c)].pic = g1;        glyphs[((int)c)].tex = loadTextureColorKey(g1, texcoord, 0, 0,0);        glyphs[((int)c)].texMinX = texcoord[0];        glyphs[((int)c)].texMinY = texcoord[1];        glyphs[((int)c)].texMaxX = texcoord[2];        glyphs[((int)c)].texMaxY = texcoord[3];      }    }  }public:  Font(unsigned char *address,       int length,       int pointSize,        int style,       float fgRed, float fgGreen, float fgBlue,       float bgRed, float bgGreen, float bgBlue):    address(address), length(length),    pointSize(pointSize),    style(style),    fgRed(fgRed), fgGreen(fgGreen), fgBlue(fgBlue),    bgRed(bgRed), bgGreen(bgGreen), bgBlue(bgBlue),    ttfFont(NULL)  {    if (0 == initCounter)    {      if (TTF_Init() < 0)      {        errorExit("Can't init SDL_ttf");      }    }    initCounter++;    initFont();  }  ~Font()  {    initCounter--;    if (0 == initCounter)    {      TTF_Quit();    }  }  void initFont()  {    SDL_RWops *src = NULL;    int i;    src = SDL_RWFromMem(address, length);    ttfFont = TTF_OpenFontRW(src, 1, pointSize);    if (NULL == ttfFont)    {      errorExit("Can't open font file");    }    TTF_SetFontStyle(ttfFont, style);    foreground.r = (Uint8)(255 * fgRed);    foreground.g  = (Uint8)(255 * fgGreen);    foreground.b = (Uint8)(255 * fgBlue);    background.r = (Uint8)(255 * bgRed);    background.g = (Uint8)(255 * bgGreen);    background.b = (Uint8)(255 * bgBlue);    height = TTF_FontHeight(ttfFont);    ascent = TTF_FontAscent(ttfFont);    descent = TTF_FontDescent(ttfFont);    lineSkip = TTF_FontLineSkip(ttfFont);    for (i = minGlyph; i <= maxGlyph; i++)    {      glyphs.pic = NULL;      glyphs.tex = 0;    }  }  int getLineSkip()  {    return lineSkip;  }  int getHeight()  {    return height;  }  void textSize(char *text,                 SDL_Rect *r)  {    int maxx = 0;    int advance = 0;    r->x = 0;    r->y = 0;    r->w = 0;    r->h = height;    while (0 != *text)    {      if ((minGlyph <= *text) && (*text <= maxGlyph))      {        loadChar(*text);        maxx = glyphs[((int)*text)].maxx;        advance = glyphs[((int)*text)].advance;        r->w += advance;      }      text++;    }    r->w = r->w - advance + maxx;  }  void drawText(char *text, int x, int y)  {    GLfloat left, right;    GLfloat top, bottom;    GLfloat texMinX, texMinY;    GLfloat texMaxX, texMaxY;    glPushAttrib(GL_ALL_ATTRIB_BITS);    glEnable(GL_BLEND);    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);    glEnable(GL_TEXTURE_2D);    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);    while (0 != *text)    {      if ((minGlyph <= *text) && (*text <= maxGlyph))      {        loadChar(*text);        texMinX = glyphs[((int)*text)].texMinX;        texMinY = glyphs[((int)*text)].texMinY;        texMaxX = glyphs[((int)*text)].texMaxX;        texMaxY = glyphs[((int)*text)].texMaxY;        left   = x;        right  = x + glyphs[((int)*text)].pic->w;        top    = y;        bottom = y + glyphs[((int)*text)].pic->h;        glBindTexture(GL_TEXTURE_2D, glyphs[((int)*text)].tex);        glBegin(GL_TRIANGLE_STRIP);        glTexCoord2f(texMinX, texMinY); glVertex2f( left,    top);        glTexCoord2f(texMaxX, texMinY); glVertex2f(right,    top);        glTexCoord2f(texMinX, texMaxY); glVertex2f( left, bottom);        glTexCoord2f(texMaxX, texMaxY); glVertex2f(right, bottom);        glEnd();        x += glyphs[((int)*text)].advance;      }      text++;    }    glPopAttrib();  }};//-------------------------------------------------int Font::initCounter = 0;//-------------------------------------------------//  Create a texture from a surface. Set the alpha according//  to the color key. Pixels that match the color key get an//  alpha of zero while all other pixels get an alpha of//  one.GLuint loadTextureColorKey(SDL_Surface *surface,                            GLfloat *texcoord,                           int ckr,                            int ckg,                            int ckb){  GLuint texture;  int w, h;  SDL_Surface *image;  SDL_Rect area;  Uint32 colorkey;  // Use the surface width and height expanded to powers of 2   w = powerOfTwo(surface->w);  h = powerOfTwo(surface->h);  texcoord[0] = 0.0f;                    // Min X   texcoord[1] = 0.0f;                    // Min Y   texcoord[2] = (GLfloat)surface->w / w; // Max X   texcoord[3] = (GLfloat)surface->h / h; // Max Y   image = SDL_CreateRGBSurface(                               SDL_SWSURFACE,                               w, h,                               32,#if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks                                0x000000FF,                                0x0000FF00,                                0x00FF0000,                                0xFF000000#else                               0xFF000000,                               0x00FF0000,                                0x0000FF00,                                0x000000FF#endif                               );  if (image == NULL)  {    return 0;  }  // Set up so that colorkey pixels become transparent   colorkey = SDL_MapRGBA(image->format, ckr, ckg, ckb, 0);  SDL_FillRect(image, NULL, colorkey);  colorkey = SDL_MapRGBA(surface->format, ckr, ckg, ckb, 0);  SDL_SetColorKey(surface, SDL_SRCCOLORKEY, colorkey);  // Copy the surface into the GL texture image   area.x = 0;  area.y = 0;  area.w = surface->w;  area.h = surface->h;  SDL_BlitSurface(surface, &area, image, &area);  // Create an OpenGL texture for the image   glGenTextures(1, &texture);  glBindTexture(GL_TEXTURE_2D, texture);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  glTexImage2D(GL_TEXTURE_2D,               0,               GL_RGBA,               w, h,               0,               GL_RGBA,               GL_UNSIGNED_BYTE,               image->pixels);  SDL_FreeSurface(image); // No longer needed   return texture;}------------------------------------------------


It's much shorter, so it should be easier to follow the code :-)
::Sorry for bring topic back up
If GL_BGRA is unidentified put this line in
#include <GL/glext.h>
Hello,


How can I render OPENGL textures with SDL_TTF fonts and transparent background?
I compiled the first example and the textures have black background.
Also, I would like how to obtain a HDC (Handle Device Context) of a Main Window that is created with SDL library.


Thank's.
Quote:Original post by cyberpuk
How can I render OPENGL textures with SDL_TTF fonts and transparent background?


That's an OpenGL specific issue, I'm not sure if it's possible, I've tried it in the past using this and couln't get it to come out how I wanted it to, so maybe someone with more OpenGL knowledge can answer that one.

Quote:Also, I would like how to obtain a HDC (Handle Device Context) of a Main Window that is created with SDL library.

#include <SDL_syswm.h>#include <windows.h>...// global or a static local variableSDL_SysWMinfo pInfo;...// In init function just need to call onceSDL_VERSION(&pInfo.version);SDL_GetWMInfo(&pInfo); ...// Get's the DC based on the window's HWNDHWND myHwnd = pInfo.window;HDC myDC = GetDC(myHwnd);

This topic is closed to new replies.

Advertisement