Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

# 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
    00044     //-----------------------------------------------------------------------
    00045     CFontManager::~CFontManager()
    00046     {
    00047         UnloadFonts();
    00048     }
    00049
    00050     //-----------------------------------------------------------------------
    00051     void CFontManager::Initialize()
    00052     {
    00053         // Vertex declaration
    00054         TDeclarationElement Decl[] =
    00055         {
    00056             {0, ELT_USAGE_POSITION,  ELT_TYPE_FLOAT3},
    00057             {0, ELT_USAGE_DIFFUSE,   ELT_TYPE_COLOR},
    00058             {0, ELT_USAGE_TEXCOORD0, ELT_TYPE_FLOAT2}
    00059         };
    00060         m_Declaration = Renderer->CreateVertexDeclaration(Decl);
    00061
    00062         // Vertex buffer
    00063         m_VertexBuffer = Renderer->CreateVertexBuffer<TVertex>(NbCharMax * 4, BUF_DYNAMIC);
    00064
    00065         // Index buffer
    00066         std::vector<TIndex> Indices;
    00067         for (TIndex i = 0; i < NbCharMax; ++i)
    00068         {
    00069             Indices.push_back(i * 4 + 0);
    00070             Indices.push_back(i * 4 + 1);
    00071             Indices.push_back(i * 4 + 2);
    00072             Indices.push_back(i * 4 + 2);
    00073             Indices.push_back(i * 4 + 1);
    00074             Indices.push_back(i * 4 + 3);
    00075         }
    00076         m_IndexBuffer = Renderer->CreateIndexBuffer((int)Indices.size(), 0, &Indices[0]);
    00077     }
    00078
    00079     //-----------------------------------------------------------------------
    00080     void CFontManager::LoadFont(const String& strFontFile, const String& strFontName, int nQuality)
    00081     {
    00082         // Log
    00083         ILogger::Log("Load of the font (size=%d): %s", nQuality, strFontFile.c_str());
    00084
    00085         // Search the file in the search paths of the MediaManager
    00086         CFile FontFile = CMediaManager::Instance().FindMedia(CFile(strFontFile));
    00087
    00088         uint nFontSize = nQuality;
    00089
    00090         // These are the characters that get stored. The last one ('\xFF')
    00091         // indicates the picture used to draw 'unknown' characters.
    00092         const String strChars(  "abcdefghijklmnopqrstuvwxyz"
    00093                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    00094                                 "1234567890~!@#$%^&*()-=+;:"
    00095                                 "'\",./?[]|\\ <>`\xFF");
    00096         // Margins around characters to prevent them from 'bleeding' into
    00097         // each other.
    00098         const uint nMargin = 3;
    00099         uint nImageHeight = 0, nImageWidth = 256;
    00100
    00101         // This initializes FreeType
    00102         FT_Library library;
    00103         if (FT_Init_FreeType(&library) != 0)
    00104             LOADINGFAILED_EXCEPT(strFontFile, "FreeType2 error : Could not initialize FreeType2 library","CFontManager::LoadFont");
    00105
    00106         // Load the font
    00107         FT_Face face;
    00108         if (FT_New_Face(library, FontFile.Fullname().c_str(), 0, &face) != 0)
    00109             LOADINGFAILED_EXCEPT(strFontFile, "FreeType2 error : Could not load font file","CFontManager::LoadFont");
    00110
    00111         // Abort if this is not a 'true type', scalable font.
    00112         if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) ||
    00113             !(face->face_flags & FT_FACE_FLAG_HORIZONTAL))
    00114             LOADINGFAILED_EXCEPT(strFontFile, "FreeType2 error : Error setting font size","CFontManager::LoadFont");
    00115
    00116         // Set the font size
    00117         FT_Set_Pixel_Sizes(face, (FT_UInt)nFontSize, 0);
    00118
    00119         // First we go over all the characters to find the max descent
    00120         // and ascent (space required above and below the base of a
    00121         // line of text) and needed image size. There are simpler methods
    00122         // to obtain these with FreeType but they are unreliable.
    00123         int nMaxDescent = 0, nMaxAscent = 0;
    00124         uint nSpaceOnLine = nImageWidth - nMargin, nLines = 1;
    00125
    00126         for (uint i = 0; i < strChars.size(); ++i)
    00127         {
    00128             // Look up the character in the font file.
    00129             uint nCharIndex = FT_Get_Char_Index(face, static_cast<unsigned int>(strChars[i]));
    00130             if (strChars[i] == '\xFF')
    00131                 nCharIndex = 0;
    00132
    00133             // Render the current glyph.
    00134             FT_Load_Glyph(face, (FT_UInt)nCharIndex, FT_LOAD_DEFAULT);
    00135             FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
    00136
    00137             uint nAdvance = (face->glyph->metrics.horiAdvance >> 6) + nMargin;
    00138             // If the line is full go to the next line
    00139             if (nAdvance > nSpaceOnLine){
    00140                 nSpaceOnLine = nImageWidth - nMargin;
    00141                 ++nLines;
    00142             }
    00143             nSpaceOnLine -= nAdvance;
    00144
    00145             nMaxAscent = std::max(face->glyph->bitmap_top, nMaxAscent);
    00146             nMaxDescent = std::max(face->glyph->bitmap.rows -
    00147                            face->glyph->bitmap_top, nMaxDescent);
    00148         }
    00149
    00150         m_Fonts[strFontName].LineHeight = nMaxAscent + nMaxDescent;
    00151
    00152         // Compute how high the texture has to be.
    00153         uint nNeededImageHeight = (nMaxAscent + nMaxDescent + nMargin) * nLines + nMargin;
    00154         // Get the first power of two in which it fits.
    00155         nImageHeight = 16;
    00156         while (nImageHeight < nNeededImageHeight)
    00157             nImageHeight *= 2;
    00158
    00159         // Allocate memory for the texture, and set it to 0
    00160         unsigned char* pImage = new unsigned char[nImageHeight * nImageWidth];
    00161         for (uint i = 0; i < nImageHeight * nImageWidth; ++i)
    00162             pImage[i] = 0;
    00163
    00164         m_Fonts[strFontName].TextureLineHeight = static_cast<float>(m_Fonts[strFontName].LineHeight) / nImageHeight;
    00165
    00166         // Make the glyph table.
    00167         m_Fonts[strFontName].Glyphs = new Glyph[strChars.size()];
    00168         for (uint i = 0; i != 256; ++i)
    00169             m_Fonts[strFontName].GlyphsTable[i] = NULL;
    00170
    00171         // These are the position at which to draw the next glyph
    00172         uint x = nMargin, y = nMargin + nMaxAscent;
    00173
    00174         // Drawing loop
    00175         for (uint i = 0; i < strChars.size(); ++i)
    00176         {
    00177             uint nCharIndex = FT_Get_Char_Index(face, static_cast<unsigned int>(strChars[i]));
    00178             if (strChars[i] == '\xFF')
    00179                 nCharIndex = 0;
    00180
    00181             // Render the glyph
    00182             FT_Load_Glyph(face, (FT_UInt)nCharIndex, FT_LOAD_DEFAULT);
    00183             FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
    00184
    00185             // See whether the character fits on the current line
    00186             uint nAdvance = (face->glyph->metrics.horiAdvance >> 6) + nMargin;
    00187             if (nAdvance > nImageWidth - x)
    00188             {
    00189                 x = nMargin;
    00190                 y += (nMaxAscent + nMaxDescent + nMargin);
    00191             }
    00192
    00193             m_Fonts[strFontName].Glyphs[i].Tex_x1 = static_cast<float>(x) / nImageWidth;
    00194             m_Fonts[strFontName].Glyphs[i].Tex_x2 = static_cast<float>(x + (nAdvance - nMargin)) / nImageWidth;
    00195             m_Fonts[strFontName].Glyphs[i].Tex_y1 = static_cast<float>(y - nMaxAscent) / nImageHeight;
    00196             m_Fonts[strFontName].Glyphs[i].Advance = (nAdvance - nMargin);
    00197
    00198             m_Fonts[strFontName].GlyphsTable[(unsigned char)strChars[i]] = m_Fonts[strFontName].Glyphs + i;
    00199
    00200             // Copy the image gotten from FreeType onto the texture
    00201             // at the correct position
    00202             for (int nRow = 0; nRow < face->glyph->bitmap.rows; ++nRow)
    00203             {
    00204                 for (int nPixel = 0; nPixel < face->glyph->bitmap.width; ++nPixel)
    00205                 {
    00206                     pImage[(x + face->glyph->bitmap_left + nPixel) +
    00207                         (y - face->glyph->bitmap_top + nRow) * nImageWidth] =
    00208                     face->glyph->bitmap.buffer[nPixel + nRow * face->glyph->bitmap.pitch];
    00209                 }
    00210             }
    00211
    00212             x += nAdvance;
    00213         }
    00214
    00215         // All chars that do not have their own glyph are set to point to
    00216         // the default glyph.
    00217         Glyph* pDefaultGlyph = m_Fonts[strFontName].GlyphsTable[(unsigned char)'\xFF'];
    00218         // We must have the default character (stored under '\xFF')
    00219         if (pDefaultGlyph == NULL)
    00220             throw std::runtime_error("Font file contains no default glyph");
    00221         for (uint i = 0; i != 256; ++i){
    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.GetTexture());
    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