SFML logo
  • Main Page
  • Namespaces
  • Classes
  • Files
  • File List

FontLoader.cpp

00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
00005 //
00006 // This software is provided 'as-is', without any express or implied warranty.
00007 // In no event will the authors be held liable for any damages arising from the use of this software.
00008 //
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it freely,
00011 // subject to the following restrictions:
00012 //
00013 // 1. The origin of this software must not be misrepresented;
00014 //    you must not claim that you wrote the original software.
00015 //    If you use this software in a product, an acknowledgment
00016 //    in the product documentation would be appreciated but is not required.
00017 //
00018 // 2. Altered source versions must be plainly marked as such,
00019 //    and must not be misrepresented as being the original software.
00020 //
00021 // 3. This notice may not be removed or altered from any source distribution.
00022 //
00024 
00026 // Headers
00028 #include <SFML/Graphics/FontLoader.hpp>
00029 #include <SFML/Graphics/Color.hpp>
00030 #include <SFML/Graphics/Font.hpp>
00031 #include <SFML/Graphics/Image.hpp>
00032 #include <SFML/Graphics/GraphicsContext.hpp>
00033 #include FT_GLYPH_H
00034 #include <iostream>
00035 #include <map>
00036 #include <vector>
00037 #include <math.h>
00038 
00039 
00040 namespace
00041 {
00043     // Functor to sort glyphs by size
00045     struct SizeCompare
00046     {
00047         bool operator ()(FT_BitmapGlyph Glyph1, FT_BitmapGlyph Glyph2) const
00048         {
00049             return Glyph2->bitmap.rows < Glyph1->bitmap.rows;
00050         }
00051     };
00052 }
00053 
00054 namespace sf
00055 {
00056 namespace priv
00057 {
00061 FontLoader& FontLoader::GetInstance()
00062 {
00063     static FontLoader Instance;
00064 
00065     return Instance;
00066 }
00067 
00068 
00072 FontLoader::FontLoader()
00073 {
00074     // Initialize FreeType library
00075     FT_Error Error = FT_Init_FreeType(&myLibrary);
00076     if (Error)
00077     {
00078         std::cerr << "Failed to initialize FreeType library (error code : " << Error << ")" << std::endl;
00079         return;
00080     }
00081 }
00082 
00083 
00087 FontLoader::~FontLoader()
00088 {
00089     // Shutdown FreeType library
00090     if (myLibrary)
00091         FT_Done_FreeType(myLibrary);
00092 }
00093 
00094 
00098 bool FontLoader::LoadFontFromFile(const std::string& Filename, unsigned int CharSize, const Unicode::UTF32String& Charset, Font& LoadedFont)
00099 {
00100     // Check if Freetype is correctly initialized
00101     if (!myLibrary)
00102     {
00103         std::cerr << "Failed to load font \"" << Filename << "\", FreeType has not been initialized" << std::endl;
00104         return false;
00105     }
00106 
00107     // Create a new font face from the specified file
00108     FT_Face FontFace;
00109     FT_Error Error = FT_New_Face(myLibrary, Filename.c_str(), 0, &FontFace);
00110     if (Error)
00111     {
00112         std::cerr << "Failed to load font \"" << Filename << "\" (" << GetErrorDesc(Error) << ")" << std::endl;
00113         return false;
00114     }
00115 
00116     // Create the bitmap font
00117     Error = CreateBitmapFont(FontFace, CharSize, Charset, LoadedFont);
00118     if (Error)
00119         std::cerr << "Failed to load font \"" << Filename << "\" (" << GetErrorDesc(Error) << ")" << std::endl;
00120 
00121     // Delete the font
00122     FT_Done_Face(FontFace);
00123 
00124     return Error == 0;
00125 }
00126 
00127 
00131 bool FontLoader::LoadFontFromMemory(const char* Data, std::size_t SizeInBytes, unsigned int CharSize, const Unicode::UTF32String& Charset, Font& LoadedFont)
00132 {
00133     // Check if Freetype is correctly initialized
00134     if (!myLibrary)
00135     {
00136         std::cerr << "Failed to load font from memory, FreeType has not been initialized" << std::endl;
00137         return false;
00138     }
00139 
00140     // Create a new font face from the specified memory data
00141     FT_Face FontFace;
00142     FT_Error Error = FT_New_Memory_Face(myLibrary, reinterpret_cast<const FT_Byte*>(Data), static_cast<FT_Long>(SizeInBytes), 0, &FontFace);
00143     if (Error)
00144     {
00145         std::cerr << "Failed to load font from memory (" << GetErrorDesc(Error) << ")" << std::endl;
00146         return false;
00147     }
00148 
00149     // Create the bitmap font
00150     Error = CreateBitmapFont(FontFace, CharSize, Charset, LoadedFont);
00151     if (Error)
00152         std::cerr << "Failed to load font from memory (" << GetErrorDesc(Error) << ")" << std::endl;
00153 
00154     // Delete the font
00155     FT_Done_Face(FontFace);
00156 
00157     return Error == 0;
00158 }
00159 
00160 
00164 FT_Error FontLoader::CreateBitmapFont(FT_Face FontFace, unsigned int CharSize, const Unicode::UTF32String& Charset, Font& LoadedFont)
00165 {
00166     // Make sure we have a valid context
00167     priv::GraphicsContext Ctx;
00168 
00169     // Let's find how many characters to put in each row to make them fit into a squared texture
00170     GLint MaxSize;
00171     GLCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxSize));
00172     int NbChars = static_cast<int>(sqrt(static_cast<double>(Charset.length())) * 0.75);
00173 
00174     // Clamp the character size to make sure we won't create a texture too big
00175     if (NbChars * CharSize >= static_cast<unsigned int>(MaxSize))
00176         CharSize = MaxSize / NbChars;
00177 
00178     // Initialize the dimensions
00179     unsigned int Left      = 0;
00180     unsigned int Top       = 0;
00181     unsigned int TexWidth  = Image::GetValidTextureSize(CharSize * NbChars);
00182     unsigned int TexHeight = CharSize * NbChars;
00183     std::vector<unsigned int> Tops(TexWidth, 0);
00184 
00185     // Create a pixel buffer for rendering every glyph
00186     std::vector<Uint8> GlyphsBuffer(TexWidth * TexHeight * 4);
00187 
00188     // Setup the font size
00189     FT_Error Error = FT_Set_Pixel_Sizes(FontFace, CharSize, CharSize);
00190     if (Error)
00191         return Error;
00192 
00193     // Select the unicode character map
00194     Error = FT_Select_Charmap(FontFace, FT_ENCODING_UNICODE);
00195     if (Error)
00196         return Error;
00197 
00198     // Render all glyphs and sort them by size to optimize texture space
00199     typedef std::multimap<FT_BitmapGlyph, Uint32, SizeCompare> GlyphTable;
00200     GlyphTable Glyphs;
00201     for (std::size_t i = 0; i < Charset.length(); ++i)
00202     {
00203         // Load the glyph corresponding to the current character
00204         Error = FT_Load_Char(FontFace, Charset[i], FT_LOAD_TARGET_NORMAL);
00205         if (Error)
00206             return Error;
00207 
00208         // Convert the glyph to a bitmap (ie. rasterize it)
00209         FT_Glyph Glyph;
00210         Error = FT_Get_Glyph(FontFace->glyph, &Glyph);
00211         if (Error)
00212             return Error;
00213         FT_Glyph_To_Bitmap(&Glyph, FT_RENDER_MODE_NORMAL, 0, 1);
00214         FT_BitmapGlyph BitmapGlyph = (FT_BitmapGlyph)Glyph;
00215 
00216         // Should we handle other pixel modes ?
00217         if (BitmapGlyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
00218             return FT_Err_Cannot_Render_Glyph;
00219 
00220         // Add it to the sorted table of glyphs
00221         Glyphs.insert(std::make_pair(BitmapGlyph, Charset[i]));
00222     }
00223 
00224     // Copy the rendered glyphs into the texture
00225     unsigned int MaxHeight = 0;
00226     std::map<Uint32, IntRect> Coords;
00227     for (GlyphTable::const_iterator i = Glyphs.begin(); i != Glyphs.end(); ++i)
00228     {
00229         // Get the bitmap of the current glyph
00230         Glyph&         CurGlyph    = LoadedFont.myGlyphs[i->second];
00231         FT_BitmapGlyph BitmapGlyph = i->first;
00232         FT_Bitmap&     Bitmap      = BitmapGlyph->bitmap;
00233 
00234         // Make sure we don't go over the texture width
00235         if (Left + Bitmap.width + 1 >= TexWidth)
00236             Left = 0;
00237 
00238         // Compute the top coordinate
00239         Top = Tops[Left];
00240         for (int x = 0; x < Bitmap.width + 1; ++x)
00241             Top = std::max(Top, Tops[Left + x]);
00242         Top++;
00243 
00244         // Make sure we don't go over the texture height -- resize it if we need more space
00245         if (Top + Bitmap.rows + 1 >= TexHeight)
00246         {
00247             TexHeight *= 2;
00248             GlyphsBuffer.resize(TexWidth * TexHeight * 4);
00249         }
00250 
00251         // Store the character's position and size
00252         CurGlyph.Rectangle.Left   = BitmapGlyph->left;
00253         CurGlyph.Rectangle.Top    = -BitmapGlyph->top;
00254         CurGlyph.Rectangle.Right  = CurGlyph.Rectangle.Left + Bitmap.width;
00255         CurGlyph.Rectangle.Bottom = Bitmap.rows - BitmapGlyph->top;
00256         CurGlyph.Advance          = BitmapGlyph->root.advance.x >> 16;
00257 
00258         // Texture size may change, so let the texture coordinates be calculated later
00259         Coords[i->second] = IntRect(Left + 1, Top + 1, Left + Bitmap.width + 1, Top + Bitmap.rows + 1);
00260 
00261         // Draw the glyph into our bitmap font
00262         const Uint8* Pixels = Bitmap.buffer;
00263         for (int y = 0; y < Bitmap.rows; ++y)
00264         {
00265             for (int x = 0; x < Bitmap.width; ++x)
00266             {
00267                 std::size_t Index = x + Left + 1 + (y + Top + 1) * TexWidth;
00268                 GlyphsBuffer[Index * 4 + 0] = 255;
00269                 GlyphsBuffer[Index * 4 + 1] = 255;
00270                 GlyphsBuffer[Index * 4 + 2] = 255;
00271                 GlyphsBuffer[Index * 4 + 3] = Pixels[x];
00272             }
00273             Pixels += Bitmap.pitch;
00274         }
00275 
00276         // Update the rendering coordinates
00277         for (int x = 0; x < Bitmap.width + 1; ++x)
00278             Tops[Left + x] = Top + Bitmap.rows;
00279         Left += Bitmap.width + 1;
00280         if (Top + Bitmap.rows > MaxHeight)
00281             MaxHeight = Top + Bitmap.rows;
00282 
00283         // Delete the glyph
00284         FT_Done_Glyph((FT_Glyph)BitmapGlyph);
00285     }
00286 
00287     // Create the font's texture
00288     TexHeight = MaxHeight + 1;
00289     GlyphsBuffer.resize(TexWidth * TexHeight * 4);
00290     LoadedFont.myTexture.LoadFromPixels(TexWidth, TexHeight, &GlyphsBuffer[0]);
00291 
00292     // Now that the texture is created, we can precompute texture coordinates
00293     for (std::size_t i = 0; i < Charset.size(); ++i)
00294     {
00295         Uint32 CurChar = Charset[i];
00296         LoadedFont.myGlyphs[CurChar].TexCoords = LoadedFont.myTexture.GetTexCoords(Coords[CurChar], false);
00297     }
00298 
00299     // Update the character size (it may have been changed by the function)
00300     LoadedFont.myCharSize = CharSize;
00301 
00302     return 0;
00303 }
00304 
00305 
00309 std::string FontLoader::GetErrorDesc(FT_Error Error)
00310 {
00311     switch (Error)
00312     {
00313         // Generic errors
00314         case FT_Err_Cannot_Open_Resource :      return "cannot open resource";
00315         case FT_Err_Unknown_File_Format :       return "unknown file format";
00316         case FT_Err_Invalid_File_Format :       return "broken file";
00317         case FT_Err_Invalid_Version :           return "invalid FreeType version";
00318         case FT_Err_Lower_Module_Version :      return "module version is too low";
00319         case FT_Err_Invalid_Argument :          return "invalid argument";
00320         case FT_Err_Unimplemented_Feature :     return "unimplemented feature";
00321         case FT_Err_Invalid_Table :             return "broken table";
00322         case FT_Err_Invalid_Offset :            return "broken offset within table";
00323 
00324         // Glyph / character errors
00325         case FT_Err_Invalid_Glyph_Index :       return "invalid glyph index";
00326         case FT_Err_Invalid_Character_Code :    return "invalid character code";
00327         case FT_Err_Invalid_Glyph_Format :      return "unsupported glyph image format";
00328         case FT_Err_Cannot_Render_Glyph :       return "cannot render this glyph format";
00329         case FT_Err_Invalid_Outline :           return "invalid outline";
00330         case FT_Err_Invalid_Composite :         return "invalid composite glyph";
00331         case FT_Err_Too_Many_Hints :            return "too many hints";
00332         case FT_Err_Invalid_Pixel_Size :        return "invalid pixel size";
00333 
00334         // Handle errors
00335         case FT_Err_Invalid_Handle :            return "invalid object handle";
00336         case FT_Err_Invalid_Library_Handle :    return "invalid library handle";
00337         case FT_Err_Invalid_Driver_Handle :     return "invalid module handle";
00338         case FT_Err_Invalid_Face_Handle :       return "invalid face handle";
00339         case FT_Err_Invalid_Size_Handle :       return "invalid size handle";
00340         case FT_Err_Invalid_Slot_Handle :       return "invalid glyph slot handle";
00341         case FT_Err_Invalid_CharMap_Handle :    return "invalid charmap handle";
00342         case FT_Err_Invalid_Cache_Handle :      return "invalid cache manager handle";
00343         case FT_Err_Invalid_Stream_Handle :     return "invalid stream handle";
00344 
00345         // Driver errors
00346         case FT_Err_Too_Many_Drivers :          return "too many modules";
00347         case FT_Err_Too_Many_Extensions :       return "too many extensions";
00348 
00349         // Memory errors
00350         case FT_Err_Out_Of_Memory :             return "out of memory";
00351         case FT_Err_Unlisted_Object :           return "unlisted object";
00352 
00353         // Stream errors
00354         case FT_Err_Cannot_Open_Stream :        return "cannot open stream";
00355         case FT_Err_Invalid_Stream_Seek :       return "invalid stream seek";
00356         case FT_Err_Invalid_Stream_Skip :       return "invalid stream skip";
00357         case FT_Err_Invalid_Stream_Read :       return "invalid stream read";
00358         case FT_Err_Invalid_Stream_Operation :  return "invalid stream operation";
00359         case FT_Err_Invalid_Frame_Operation :   return "invalid frame operation";
00360         case FT_Err_Nested_Frame_Access :       return "nested frame access";
00361         case FT_Err_Invalid_Frame_Read :        return "invalid frame read";
00362 
00363         // Raster errors
00364         case FT_Err_Raster_Uninitialized :      return "raster uninitialized";
00365         case FT_Err_Raster_Corrupted :          return "raster corrupted";
00366         case FT_Err_Raster_Overflow :           return "raster overflow";
00367         case FT_Err_Raster_Negative_Height :    return "negative height while rastering";
00368 
00369         // Cache errors
00370         case FT_Err_Too_Many_Caches :           return "too many registered caches";
00371 
00372         // TrueType and SFNT errors
00373         case FT_Err_Invalid_Opcode :            return "invalid opcode";
00374         case FT_Err_Too_Few_Arguments :         return "too few arguments";
00375         case FT_Err_Stack_Overflow :            return "stack overflow";
00376         case FT_Err_Code_Overflow :             return "code overflow";
00377         case FT_Err_Bad_Argument :              return "bad argument";
00378         case FT_Err_Divide_By_Zero :            return "division by zero";
00379         case FT_Err_Invalid_Reference :         return "invalid reference";
00380         case FT_Err_Debug_OpCode :              return "found debug opcode";
00381         case FT_Err_ENDF_In_Exec_Stream :       return "found ENDF opcode in execution stream";
00382         case FT_Err_Nested_DEFS :               return "nested DEFS";
00383         case FT_Err_Invalid_CodeRange :         return "invalid code range";
00384         case FT_Err_Execution_Too_Long :        return "execution context too long";
00385         case FT_Err_Too_Many_Function_Defs :    return "too many function definitions";
00386         case FT_Err_Too_Many_Instruction_Defs : return "too many instruction definitions";
00387         case FT_Err_Table_Missing :             return "SFNT font table missing";
00388         case FT_Err_Horiz_Header_Missing :      return "horizontal header (hhea) table missing";
00389         case FT_Err_Locations_Missing :         return "locations (loca) table missing";
00390         case FT_Err_Name_Table_Missing :        return "name table missing";
00391         case FT_Err_CMap_Table_Missing :        return "character map (cmap) table missing";
00392         case FT_Err_Hmtx_Table_Missing :        return "horizontal metrics (hmtx) table missing";
00393         case FT_Err_Post_Table_Missing :        return "PostScript (post) table missing";
00394         case FT_Err_Invalid_Horiz_Metrics :     return "invalid horizontal metrics";
00395         case FT_Err_Invalid_CharMap_Format :    return "invalid character map (cmap) format";
00396         case FT_Err_Invalid_PPem :              return "invalid ppem value";
00397         case FT_Err_Invalid_Vert_Metrics :      return "invalid vertical metrics";
00398         case FT_Err_Could_Not_Find_Context :    return "could not find context";
00399         case FT_Err_Invalid_Post_Table_Format : return "invalid PostScript (post) table format";
00400         case FT_Err_Invalid_Post_Table :        return "invalid PostScript (post) table";
00401 
00402         // CCF, CID and Type 1 errors
00403         case FT_Err_Syntax_Error :              return "opcode syntax error";
00404         case FT_Err_Stack_Underflow :           return "argument stack underflow";
00405         case FT_Err_Ignore :                    return "ignore";
00406 
00407         // BDF errors
00408         case FT_Err_Missing_Startfont_Field :   return "`STARTFONT' field missing";
00409         case FT_Err_Missing_Font_Field :        return "`FONT' field missing";
00410         case FT_Err_Missing_Size_Field :        return "`SIZE' field missing";
00411         case FT_Err_Missing_Chars_Field :       return "`CHARS' field missing";
00412         case FT_Err_Missing_Startchar_Field :   return "`STARTCHAR' field missing";
00413         case FT_Err_Missing_Encoding_Field :    return "`ENCODING' field missing";
00414         case FT_Err_Missing_Bbx_Field :         return "`BBX' field missing";
00415     }
00416 
00417     return "unknown error";
00418 }
00419 
00420 } // namespace priv
00421 
00422 } // namespace sf
00423 

 ::  Copyright © 2007-2008 Laurent Gomila, all rights reserved  ::  Documentation generated by doxygen 1.5.2  ::