Back to index

libsfml  1.6+dfsg2
FontLoader.cpp
Go to the documentation of this file.
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         // Add it to the sorted table of glyphs
00217         Glyphs.insert(std::make_pair(BitmapGlyph, Charset[i]));
00218     }
00219 
00220     // Copy the rendered glyphs into the texture
00221     unsigned int MaxHeight = 0;
00222     std::map<Uint32, IntRect> Coords;
00223     for (GlyphTable::const_iterator i = Glyphs.begin(); i != Glyphs.end(); ++i)
00224     {
00225         // Get the bitmap of the current glyph
00226         Glyph&         CurGlyph    = LoadedFont.myGlyphs[i->second];
00227         FT_BitmapGlyph BitmapGlyph = i->first;
00228         FT_Bitmap&     Bitmap      = BitmapGlyph->bitmap;
00229 
00230         // Make sure we don't go over the texture width
00231         if (Left + Bitmap.width + 1 >= TexWidth)
00232             Left = 0;
00233 
00234         // Compute the top coordinate
00235         Top = Tops[Left];
00236         for (int x = 0; x < Bitmap.width + 1; ++x)
00237             Top = std::max(Top, Tops[Left + x]);
00238         Top++;
00239 
00240         // Make sure we don't go over the texture height -- resize it if we need more space
00241         if (Top + Bitmap.rows + 1 >= TexHeight)
00242         {
00243             TexHeight *= 2;
00244             GlyphsBuffer.resize(TexWidth * TexHeight * 4);
00245         }
00246 
00247         // Store the character's position and size
00248         CurGlyph.Rectangle.Left   = BitmapGlyph->left;
00249         CurGlyph.Rectangle.Top    = -BitmapGlyph->top;
00250         CurGlyph.Rectangle.Right  = CurGlyph.Rectangle.Left + Bitmap.width;
00251         CurGlyph.Rectangle.Bottom = Bitmap.rows - BitmapGlyph->top;
00252         CurGlyph.Advance          = BitmapGlyph->root.advance.x >> 16;
00253 
00254         // Texture size may change, so let the texture coordinates be calculated later
00255         Coords[i->second] = IntRect(Left + 1, Top + 1, Left + Bitmap.width + 1, Top + Bitmap.rows + 1);
00256 
00257         // Draw the glyph into our bitmap font
00258         const Uint8* Pixels = Bitmap.buffer;
00259         for (int y = 0; y < Bitmap.rows; ++y)
00260         {
00261             for (int x = 0; x < Bitmap.width; ++x)
00262             {
00263                 std::size_t Index = x + Left + 1 + (y + Top + 1) * TexWidth;
00264                 GlyphsBuffer[Index * 4 + 0] = 255;
00265                 GlyphsBuffer[Index * 4 + 1] = 255;
00266                 GlyphsBuffer[Index * 4 + 2] = 255;
00267                 GlyphsBuffer[Index * 4 + 3] = Pixels[x];
00268             }
00269             Pixels += Bitmap.pitch;
00270         }
00271 
00272         // Update the rendering coordinates
00273         for (int x = 0; x < Bitmap.width + 1; ++x)
00274             Tops[Left + x] = Top + Bitmap.rows;
00275         Left += Bitmap.width + 1;
00276         if (Top + Bitmap.rows > MaxHeight)
00277             MaxHeight = Top + Bitmap.rows;
00278 
00279         // Delete the glyph
00280         FT_Done_Glyph((FT_Glyph)BitmapGlyph);
00281     }
00282 
00283     // Create the font's texture
00284     TexHeight = MaxHeight + 1;
00285     GlyphsBuffer.resize(TexWidth * TexHeight * 4);
00286     LoadedFont.myTexture.LoadFromPixels(TexWidth, TexHeight, &GlyphsBuffer[0]);
00287 
00288     // Now that the texture is created, we can precompute texture coordinates
00289     for (std::size_t i = 0; i < Charset.size(); ++i)
00290     {
00291         Uint32 CurChar = Charset[i];
00292         LoadedFont.myGlyphs[CurChar].TexCoords = LoadedFont.myTexture.GetTexCoords(Coords[CurChar]);
00293     }
00294 
00295     // Update the character size (it may have been changed by the function)
00296     LoadedFont.myCharSize = CharSize;
00297 
00298     return 0;
00299 }
00300 
00301 
00305 std::string FontLoader::GetErrorDesc(FT_Error Error)
00306 {
00307     switch (Error)
00308     {
00309         // Generic errors
00310         case FT_Err_Cannot_Open_Resource :      return "cannot open resource";
00311         case FT_Err_Unknown_File_Format :       return "unknown file format";
00312         case FT_Err_Invalid_File_Format :       return "broken file";
00313         case FT_Err_Invalid_Version :           return "invalid FreeType version";
00314         case FT_Err_Lower_Module_Version :      return "module version is too low";
00315         case FT_Err_Invalid_Argument :          return "invalid argument";
00316         case FT_Err_Unimplemented_Feature :     return "unimplemented feature";
00317         case FT_Err_Invalid_Table :             return "broken table";
00318         case FT_Err_Invalid_Offset :            return "broken offset within table";
00319 
00320         // Glyph / character errors
00321         case FT_Err_Invalid_Glyph_Index :       return "invalid glyph index";
00322         case FT_Err_Invalid_Character_Code :    return "invalid character code";
00323         case FT_Err_Invalid_Glyph_Format :      return "unsupported glyph image format";
00324         case FT_Err_Cannot_Render_Glyph :       return "cannot render this glyph format";
00325         case FT_Err_Invalid_Outline :           return "invalid outline";
00326         case FT_Err_Invalid_Composite :         return "invalid composite glyph";
00327         case FT_Err_Too_Many_Hints :            return "too many hints";
00328         case FT_Err_Invalid_Pixel_Size :        return "invalid pixel size";
00329 
00330         // Handle errors
00331         case FT_Err_Invalid_Handle :            return "invalid object handle";
00332         case FT_Err_Invalid_Library_Handle :    return "invalid library handle";
00333         case FT_Err_Invalid_Driver_Handle :     return "invalid module handle";
00334         case FT_Err_Invalid_Face_Handle :       return "invalid face handle";
00335         case FT_Err_Invalid_Size_Handle :       return "invalid size handle";
00336         case FT_Err_Invalid_Slot_Handle :       return "invalid glyph slot handle";
00337         case FT_Err_Invalid_CharMap_Handle :    return "invalid charmap handle";
00338         case FT_Err_Invalid_Cache_Handle :      return "invalid cache manager handle";
00339         case FT_Err_Invalid_Stream_Handle :     return "invalid stream handle";
00340 
00341         // Driver errors
00342         case FT_Err_Too_Many_Drivers :          return "too many modules";
00343         case FT_Err_Too_Many_Extensions :       return "too many extensions";
00344 
00345         // Memory errors
00346         case FT_Err_Out_Of_Memory :             return "out of memory";
00347         case FT_Err_Unlisted_Object :           return "unlisted object";
00348 
00349         // Stream errors
00350         case FT_Err_Cannot_Open_Stream :        return "cannot open stream";
00351         case FT_Err_Invalid_Stream_Seek :       return "invalid stream seek";
00352         case FT_Err_Invalid_Stream_Skip :       return "invalid stream skip";
00353         case FT_Err_Invalid_Stream_Read :       return "invalid stream read";
00354         case FT_Err_Invalid_Stream_Operation :  return "invalid stream operation";
00355         case FT_Err_Invalid_Frame_Operation :   return "invalid frame operation";
00356         case FT_Err_Nested_Frame_Access :       return "nested frame access";
00357         case FT_Err_Invalid_Frame_Read :        return "invalid frame read";
00358 
00359         // Raster errors
00360         case FT_Err_Raster_Uninitialized :      return "raster uninitialized";
00361         case FT_Err_Raster_Corrupted :          return "raster corrupted";
00362         case FT_Err_Raster_Overflow :           return "raster overflow";
00363         case FT_Err_Raster_Negative_Height :    return "negative height while rastering";
00364 
00365         // Cache errors
00366         case FT_Err_Too_Many_Caches :           return "too many registered caches";
00367 
00368         // TrueType and SFNT errors
00369         case FT_Err_Invalid_Opcode :            return "invalid opcode";
00370         case FT_Err_Too_Few_Arguments :         return "too few arguments";
00371         case FT_Err_Stack_Overflow :            return "stack overflow";
00372         case FT_Err_Code_Overflow :             return "code overflow";
00373         case FT_Err_Bad_Argument :              return "bad argument";
00374         case FT_Err_Divide_By_Zero :            return "division by zero";
00375         case FT_Err_Invalid_Reference :         return "invalid reference";
00376         case FT_Err_Debug_OpCode :              return "found debug opcode";
00377         case FT_Err_ENDF_In_Exec_Stream :       return "found ENDF opcode in execution stream";
00378         case FT_Err_Nested_DEFS :               return "nested DEFS";
00379         case FT_Err_Invalid_CodeRange :         return "invalid code range";
00380         case FT_Err_Execution_Too_Long :        return "execution context too long";
00381         case FT_Err_Too_Many_Function_Defs :    return "too many function definitions";
00382         case FT_Err_Too_Many_Instruction_Defs : return "too many instruction definitions";
00383         case FT_Err_Table_Missing :             return "SFNT font table missing";
00384         case FT_Err_Horiz_Header_Missing :      return "horizontal header (hhea) table missing";
00385         case FT_Err_Locations_Missing :         return "locations (loca) table missing";
00386         case FT_Err_Name_Table_Missing :        return "name table missing";
00387         case FT_Err_CMap_Table_Missing :        return "character map (cmap) table missing";
00388         case FT_Err_Hmtx_Table_Missing :        return "horizontal metrics (hmtx) table missing";
00389         case FT_Err_Post_Table_Missing :        return "PostScript (post) table missing";
00390         case FT_Err_Invalid_Horiz_Metrics :     return "invalid horizontal metrics";
00391         case FT_Err_Invalid_CharMap_Format :    return "invalid character map (cmap) format";
00392         case FT_Err_Invalid_PPem :              return "invalid ppem value";
00393         case FT_Err_Invalid_Vert_Metrics :      return "invalid vertical metrics";
00394         case FT_Err_Could_Not_Find_Context :    return "could not find context";
00395         case FT_Err_Invalid_Post_Table_Format : return "invalid PostScript (post) table format";
00396         case FT_Err_Invalid_Post_Table :        return "invalid PostScript (post) table";
00397 
00398         // CCF, CID and Type 1 errors
00399         case FT_Err_Syntax_Error :              return "opcode syntax error";
00400         case FT_Err_Stack_Underflow :           return "argument stack underflow";
00401         case FT_Err_Ignore :                    return "ignore";
00402 
00403         // BDF errors
00404         case FT_Err_Missing_Startfont_Field :   return "`STARTFONT' field missing";
00405         case FT_Err_Missing_Font_Field :        return "`FONT' field missing";
00406         case FT_Err_Missing_Size_Field :        return "`SIZE' field missing";
00407         case FT_Err_Missing_Chars_Field :       return "`CHARS' field missing";
00408         case FT_Err_Missing_Startchar_Field :   return "`STARTCHAR' field missing";
00409         case FT_Err_Missing_Encoding_Field :    return "`ENCODING' field missing";
00410         case FT_Err_Missing_Bbx_Field :         return "`BBX' field missing";
00411     }
00412 
00413     return "unknown error";
00414 }
00415 
00416 } // namespace priv
00417 
00418 } // namespace sf
00419