KeosFontManager.cpp

Go to the documentation of this file.
00001 /*
00002  * This source file is part of KEOS (Free 3D Engine)
00003  * For the latest info, see http://www.keosengine.org/
00004  * E-mails : thierry.vouriot@keosengine.org, yeri@keosengine.org
00005  *
00006  * This program is free software; you can redistribute it and/or modify it under
00007  * the terms of the GNU Lesser General Public License as published by the Free Software
00008  * Foundation; either version 2 of the License, or (at your option) any later
00009  * version.
00010  *
00011  * This program is distributed in the hope that it will be useful, but WITHOUT
00012  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00013  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License along with
00016  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
00017  * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
00018  * http://www.gnu.org/copyleft/lesser.txt.
00019  *
00020  */
00021 
00022 #include "KeosFontManager.h"
00023 #include "KeosLogger.h"
00024 #include "KeosRenderSystem.h"
00025 #include "KeosColor.h"
00026 #include "KeosGraphicString.h"
00027 #include "KeosMediaManager.h"
00028 #include <ft2build.h>
00029 #include FT_FREETYPE_H
00030 
00031 namespace Keos
00032 {
00033 
00034   //=======================================================================
00035   // CFontManager implementation
00036   //=======================================================================
00037 
00038   //-----------------------------------------------------------------------
00039   CFontManager::CFontManager()
00040   {}
00041 
00042   //-----------------------------------------------------------------------
00043   CFontManager::~CFontManager()
00044   {
00045     UnloadFonts();
00046   }
00047 
00048   //-----------------------------------------------------------------------
00049   void CFontManager::Initialize()
00050   {
00051     // Vertex declaration
00052     TDeclarationElement Decl[] =
00053       {
00054         {0, ELT_USAGE_POSITION,  ELT_TYPE_FLOAT3},
00055         {0, ELT_USAGE_DIFFUSE,   ELT_TYPE_COLOR},
00056         {0, ELT_USAGE_TEXCOORD0, ELT_TYPE_FLOAT2}
00057       };
00058     m_Declaration = Renderer->CreateVertexDeclaration(Decl);
00059 
00060     // Vertex buffer
00061     m_VertexBuffer = Renderer->CreateVertexBuffer<TVertex>(NbCharMax * 4, BUF_DYNAMIC);
00062 
00063     // Index buffer
00064     std::vector<TIndex> Indices;
00065     for (TIndex i = 0; i < NbCharMax; ++i)
00066     {
00067       Indices.push_back(i * 4 + 0);
00068       Indices.push_back(i * 4 + 1);
00069       Indices.push_back(i * 4 + 2);
00070       Indices.push_back(i * 4 + 2);
00071       Indices.push_back(i * 4 + 1);
00072       Indices.push_back(i * 4 + 3);
00073     }
00074     m_IndexBuffer = Renderer->CreateIndexBuffer((int)Indices.size(), 0, &Indices[0]);
00075   }
00076 
00077   //-----------------------------------------------------------------------
00078   void CFontManager::LoadFont(const String& strFontFile, const String& strFontName, int nQuality)
00079   {
00080     // Log
00081     ILogger::Log("Load of the font (size=%d): %s", nQuality, strFontFile.c_str());
00082 
00083     // Search the file in the search paths of the MediaManager
00084     CFile FontFile = CMediaManager::Instance().FindMedia(CFile(strFontFile));
00085 
00086     uint nFontSize = nQuality;
00087 
00088     // These are the characters that get stored. The last one ('\xFF')
00089     // indicates the picture used to draw 'unknown' characters.
00090     const String strChars(  "abcdefghijklmnopqrstuvwxyz"
00091                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00092                             "1234567890~!@#$%^&*()-=+;:"
00093                             "'\",./?[]|\\ <>`\xFF");
00094     // Margins around characters to prevent them from 'bleeding' into
00095     // each other.
00096     const uint nMargin = 3;
00097     uint nImageHeight = 0, nImageWidth = 256;
00098 
00099     // This initializes FreeType
00100     FT_Library library;
00101     if (FT_Init_FreeType(&library) != 0)
00102       LOADINGFAILED_EXCEPT(strFontFile, "FreeType2 error : Could not initialize FreeType2 library", "CFontManager::LoadFont");
00103 
00104     // Load the font
00105     FT_Face face;
00106     if (FT_New_Face(library, FontFile.Fullname().c_str(), 0, &face) != 0)
00107       LOADINGFAILED_EXCEPT(strFontFile, "FreeType2 error : Could not load font file", "CFontManager::LoadFont");
00108 
00109     // Abort if this is not a 'true type', scalable font.
00110     if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) ||
00111         !(face->face_flags & FT_FACE_FLAG_HORIZONTAL))
00112       LOADINGFAILED_EXCEPT(strFontFile, "FreeType2 error : Error setting font size", "CFontManager::LoadFont");
00113 
00114     // Set the font size
00115     FT_Set_Pixel_Sizes(face, (FT_UInt)nFontSize, 0);
00116 
00117     // First we go over all the characters to find the max descent
00118     // and ascent (space required above and below the base of a
00119     // line of text) and needed image size. There are simpler methods
00120     // to obtain these with FreeType but they are unreliable.
00121     int nMaxDescent = 0, nMaxAscent = 0;
00122     uint nSpaceOnLine = nImageWidth - nMargin, nLines = 1;
00123 
00124     for (uint i = 0; i < strChars.size(); ++i)
00125     {
00126       // Look up the character in the font file.
00127       uint nCharIndex = FT_Get_Char_Index(face, static_cast<unsigned int>(strChars[i]));
00128       if (strChars[i] == '\xFF')
00129         nCharIndex = 0;
00130 
00131       // Render the current glyph.
00132       FT_Load_Glyph(face, (FT_UInt)nCharIndex, FT_LOAD_DEFAULT);
00133       FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
00134 
00135       uint nAdvance = (face->glyph->metrics.horiAdvance >> 6) + nMargin;
00136       // If the line is full go to the next line
00137       if (nAdvance > nSpaceOnLine)
00138       {
00139         nSpaceOnLine = nImageWidth - nMargin;
00140         ++nLines;
00141       }
00142       nSpaceOnLine -= nAdvance;
00143 
00144       nMaxAscent = std::max(face->glyph->bitmap_top, nMaxAscent);
00145       nMaxDescent = std::max(face->glyph->bitmap.rows -
00146                              face->glyph->bitmap_top, nMaxDescent);
00147     }
00148 
00149     m_Fonts[strFontName].LineHeight = nMaxAscent + nMaxDescent;
00150 
00151     // Compute how high the texture has to be.
00152     uint nNeededImageHeight = (nMaxAscent + nMaxDescent + nMargin) * nLines + nMargin;
00153     // Get the first power of two in which it fits.
00154     nImageHeight = 16;
00155     while (nImageHeight < nNeededImageHeight)
00156       nImageHeight *= 2;
00157 
00158     // Allocate memory for the texture, and set it to 0
00159     unsigned char* pImage = new unsigned char[nImageHeight * nImageWidth];
00160     for (uint i = 0; i < nImageHeight * nImageWidth; ++i)
00161       pImage[i] = 0;
00162 
00163     m_Fonts[strFontName].TextureLineHeight = static_cast<float>(m_Fonts[strFontName].LineHeight) / nImageHeight;
00164 
00165     // Make the glyph table.
00166     m_Fonts[strFontName].Glyphs = new Glyph[strChars.size()];
00167     for (uint i = 0; i != 256; ++i)
00168       m_Fonts[strFontName].GlyphsTable[i] = NULL;
00169 
00170     // These are the position at which to draw the next glyph
00171     uint x = nMargin, y = nMargin + nMaxAscent;
00172 
00173     // Drawing loop
00174     for (uint i = 0; i < strChars.size(); ++i)
00175     {
00176       uint nCharIndex = FT_Get_Char_Index(face, static_cast<unsigned int>(strChars[i]));
00177       if (strChars[i] == '\xFF')
00178         nCharIndex = 0;
00179 
00180       // Render the glyph
00181       FT_Load_Glyph(face, (FT_UInt)nCharIndex, FT_LOAD_DEFAULT);
00182       FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
00183 
00184       // See whether the character fits on the current line
00185       uint nAdvance = (face->glyph->metrics.horiAdvance >> 6) + nMargin;
00186       if (nAdvance > nImageWidth - x)
00187       {
00188         x = nMargin;
00189         y += (nMaxAscent + nMaxDescent + nMargin);
00190       }
00191 
00192       m_Fonts[strFontName].Glyphs[i].Tex_x1 = static_cast<float>(x) / nImageWidth;
00193       m_Fonts[strFontName].Glyphs[i].Tex_x2 = static_cast<float>(x + (nAdvance - nMargin)) / nImageWidth;
00194       m_Fonts[strFontName].Glyphs[i].Tex_y1 = static_cast<float>(y - nMaxAscent) / nImageHeight;
00195       m_Fonts[strFontName].Glyphs[i].Advance = (nAdvance - nMargin);
00196 
00197       m_Fonts[strFontName].GlyphsTable[(unsigned char)strChars[i]] = m_Fonts[strFontName].Glyphs + i;
00198 
00199       // Copy the image gotten from FreeType onto the texture
00200       // at the correct position
00201       for (int nRow = 0; nRow < face->glyph->bitmap.rows; ++nRow)
00202       {
00203         for (int nPixel = 0; nPixel < face->glyph->bitmap.width; ++nPixel)
00204         {
00205           pImage[(x + face->glyph->bitmap_left + nPixel) +
00206                  (y - face->glyph->bitmap_top + nRow) * nImageWidth] =
00207                    face->glyph->bitmap.buffer[nPixel + nRow * face->glyph->bitmap.pitch];
00208         }
00209       }
00210 
00211       x += nAdvance;
00212     }
00213 
00214     // All chars that do not have their own glyph are set to point to
00215     // the default glyph.
00216     Glyph* pDefaultGlyph = m_Fonts[strFontName].GlyphsTable[(unsigned char)'\xFF'];
00217     // We must have the default character (stored under '\xFF')
00218     if (pDefaultGlyph == NULL)
00219       throw std::runtime_error("Font file contains no default glyph");
00220     for (uint i = 0; i != 256; ++i)
00221     {
00222       if (m_Fonts[strFontName].GlyphsTable[i] == NULL)
00223         m_Fonts[strFontName].GlyphsTable[i] = pDefaultGlyph;
00224     }
00225 
00226     CImage ImgFont(TVector2I(nImageWidth, nImageHeight), PXF_L8, pImage);
00227     delete[] pImage;
00228 
00229     //CMediaManager::Instance().SaveMediaToFile(&ImgFont, "test.bmp");
00230 
00231     // Create image in PXF_A8L8 format
00232     CImage ImgFont2(TVector2I(nImageWidth, nImageHeight), PXF_A8L8);
00233     for (uint i = 0; i < nImageWidth; ++i)
00234       for (uint j = 0; j < nImageHeight; ++j)
00235       {
00236         unsigned char CurPixel[1];
00237         ImgFont.GetPixel(i, j, CurPixel);
00238         ImgFont2.SetPixel(i, j, CColor(CurPixel[0], CurPixel[0], CurPixel[0], CurPixel[0]));
00239       }
00240 
00241     // Texture creation
00242     m_Fonts[strFontName].Texture.CreateFromImage(ImgFont2, PXF_A8L8, TEX_NOMIPMAP, strFontName);
00243 
00244     FT_Done_FreeType(library);
00245   }
00246 
00247   //-----------------------------------------------------------------------
00248   void CFontManager::UnloadFonts()
00249   {
00250     for (
00251       TFontsMap::iterator it = m_Fonts.begin();
00252       it != m_Fonts.end();
00253       ++it )
00254     {
00255       delete[] it->second.Glyphs;
00256     }
00257     m_Fonts.clear();
00258   }
00259 
00260   //-----------------------------------------------------------------------
00261   void CFontManager::DrawString(const CGraphicString& String)
00262   {
00263     // Empty text
00264     if (String.Text == "")
00265       return;
00266 
00267     // Initilialize render part
00268     if (!m_Declaration)
00269       Initialize();
00270 
00271     if (m_Fonts.find(String.Font) == m_Fonts.end())
00272       LOADINGFAILED_EXCEPT(String.Font, "This font has not been loaded with LoadFont function", "CFontManager::GetFont");
00273 
00274     // Get the font
00275     const TFont& CurFont = m_Fonts[String.Font];
00276 
00277     // Variables
00278     float         x       = static_cast<float>(String.Position.x);
00279     float         y       = static_cast<float>(String.Position.y);
00280     unsigned long Color   = Renderer->ConvertColor(String.Color);
00281 
00282     // Lock vertex buffer
00283     TVertex* Vertices = m_VertexBuffer.Lock(0, 0, LOCK_WRITEONLY);
00284 
00285     // For each character
00286     int nbChars = 0;
00287     for (String::const_iterator i = String.Text.begin(); (i != String.Text.end()) && (nbChars < NbCharMax); ++i)
00288     {
00289       unsigned char c = *i;
00290       Glyph* pGlyph = CurFont.GlyphsTable[c];
00291 
00292       // Specials characters
00293       switch (c)
00294       {
00295         case '\n' :
00296           x  = static_cast<float>(String.Position.x);
00297           y += CurFont.LineHeight;
00298           continue;
00299 
00300         case '\r' :
00301           x = static_cast<float>(String.Position.x);
00302           continue;
00303 
00304         case '\t' :
00305           x += 4 * CurFont.GlyphsTable[' ']->Advance;
00306           continue;
00307 
00308           // Vertical tabulation
00309         case '\v' :
00310           y += 4 * CurFont.LineHeight;
00311           continue;
00312 
00313         case ' ' :
00314           x += CurFont.GlyphsTable[' ']->Advance;
00315           continue;
00316       }
00317 
00318       // Position
00319       Vertices[nbChars * 4 + 0].Position.Set(x, y, 0);
00320       Vertices[nbChars * 4 + 1].Position.Set(x + pGlyph->Advance, y, 0);
00321       Vertices[nbChars * 4 + 2].Position.Set(x, y + CurFont.LineHeight, 0);
00322       Vertices[nbChars * 4 + 3].Position.Set(x + pGlyph->Advance, y + CurFont.LineHeight, 0);
00323 
00324       // Color
00325       Vertices[nbChars * 4 + 0].Diffuse = Color;
00326       Vertices[nbChars * 4 + 1].Diffuse = Color;
00327       Vertices[nbChars * 4 + 2].Diffuse = Color;
00328       Vertices[nbChars * 4 + 3].Diffuse = Color;
00329 
00330       // Texture coordinates
00331       Vertices[nbChars * 4 + 0].TexCoords.Set(pGlyph->Tex_x1, pGlyph->Tex_y1);
00332       Vertices[nbChars * 4 + 1].TexCoords.Set(pGlyph->Tex_x2, pGlyph->Tex_y1);
00333       Vertices[nbChars * 4 + 2].TexCoords.Set(pGlyph->Tex_x1, pGlyph->Tex_y1 + CurFont.TextureLineHeight);
00334       Vertices[nbChars * 4 + 3].TexCoords.Set(pGlyph->Tex_x2, pGlyph->Tex_y1 + CurFont.TextureLineHeight);
00335 
00336       x += pGlyph->Advance;
00337       ++nbChars;
00338     }
00339 
00340     // Unlock vertex buffer
00341     m_VertexBuffer.Unlock();
00342 
00343     // Render setup
00344     //Renderer->SetupAlphaBlending(BLEND_SRCCOLOR, BLEND_INVSRCCOLOR);
00345     Renderer->SetupAlphaBlending(BLEND_SRCALPHA, BLEND_INVSRCALPHA);
00346     Renderer->SetupTextureUnit(0, TXO_COLOR_MODULATE, TXA_TEXTURE, TXA_DIFFUSE);
00347     Renderer->SetupTextureUnit(0, TXO_ALPHA_MODULATE, TXA_TEXTURE, TXA_DIFFUSE);
00348     Renderer->Enable(RENDER_ALPHABLEND, true);
00349     Renderer->Enable(RENDER_ZWRITE, false);
00350 
00351     // Display text
00352     Renderer->SetDeclaration(m_Declaration);
00353     Renderer->SetTexture(0, CurFont.Texture.GetTextureBase());
00354     Renderer->SetVertexBuffer(0, m_VertexBuffer);
00355     Renderer->SetIndexBuffer(m_IndexBuffer);
00356     Renderer->DrawIndexedPrimitives(PT_TRIANGLELIST, 0, nbChars * 2);
00357     Renderer->SetTexture(0, NULL);
00358 
00359     // Restoring render options
00360     Renderer->Enable(RENDER_ZWRITE, true);
00361     Renderer->Enable(RENDER_ALPHABLEND, false);
00362   }
00363 
00364   //-----------------------------------------------------------------------
00365   TVector2I CFontManager::GetStringPixelSize(const CGraphicString& String)
00366   {
00367     // Empty text
00368     if (String.Text == "")
00369       return TVector2I(0, 0);
00370 
00371     if (m_Fonts.find(String.Font) == m_Fonts.end())
00372       LOADINGFAILED_EXCEPT(String.Font, "This font has not been loaded with LoadFont function", "CFontManager::GetFont");
00373 
00374     // Get the font
00375     const TFont& CurFont = m_Fonts[String.Font];
00376 
00377     // Vector containing the length of every line
00378     std::vector<int> Lengths;
00379 
00380     // Return value
00381     TVector2I Size(0, 0);
00382 
00383     unsigned char c;
00384 
00385     // For each character
00386     for (std::string::const_iterator i = String.Text.begin(); i != String.Text.end(); ++i)
00387     {
00388       c = *i;
00389 
00390       switch (c)
00391       {
00392         case '\n' :
00393           Lengths.push_back(Size.x);
00394           Size.y += (int)CurFont.LineHeight;
00395           Size.x = 0;
00396           break;
00397 
00398         case '\r' :
00399           Lengths.push_back(Size.x);
00400           Size.x = 0;
00401           break;
00402 
00403         case '\t' :
00404           Size.x += (int)(4 * CurFont.GlyphsTable[' ']->Advance);
00405           break;
00406 
00407           // Vertical tabulation
00408         case '\v' :
00409           Size.y += (int)(4 * CurFont.LineHeight);
00410           break;
00411 
00412         default :
00413           Size.x += (int)CurFont.GlyphsTable[c]->Advance;
00414           break;
00415       }
00416     }
00417 
00418     if (c != '\n' && c != '\v' && c != '\r')
00419       Size.y += (int)CurFont.LineHeight;
00420 
00421     Lengths.push_back(Size.x);
00422     Size.x = *std::max_element(Lengths.begin(), Lengths.end());
00423 
00424     return Size;
00425   }
00426 
00427   //-----------------------------------------------------------------------
00428   size_t CFontManager::GetFontHeight(const String& strFontName)
00429   {
00430     if (m_Fonts.find(strFontName) == m_Fonts.end())
00431       LOADINGFAILED_EXCEPT(strFontName, "This font has not been loaded with LoadFont function", "CFontManager::GetFont");
00432 
00433     // Get the font
00434     return m_Fonts[strFontName].LineHeight;
00435   }
00436 
00437 } // namespace Keos

Generated on Fri Mar 9 14:29:02 2007 for Keos by  doxygen 1.5.1-p1