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

Font.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/Font.hpp>
00029 #include <SFML/System/Err.hpp>
00030 #include <ft2build.h>
00031 #include FT_FREETYPE_H
00032 #include FT_GLYPH_H
00033 #include FT_OUTLINE_H
00034 #include FT_BITMAP_H
00035 
00036 
00037 namespace sf
00038 {
00040 Font::Font() :
00041 myLibrary    (NULL),
00042 myFace       (NULL),
00043 myRefCount   (NULL),
00044 myCurrentSize(0)
00045 {
00046 
00047 }
00048 
00049 
00051 Font::Font(const Font& copy) :
00052 myLibrary    (copy.myLibrary),
00053 myFace       (copy.myFace),
00054 myRefCount   (copy.myRefCount),
00055 myPages      (copy.myPages),
00056 myPixelBuffer(copy.myPixelBuffer),
00057 myCurrentSize(copy.myCurrentSize)
00058 {
00059     // Note: as FreeType doesn't provide functions for copying/cloning,
00060     // we must share all the FreeType pointers
00061 
00062     if (myRefCount)
00063         (*myRefCount)++;
00064 }
00065 
00066 
00068 Font::~Font()
00069 {
00070     Cleanup();
00071 }
00072 
00073 
00075 bool Font::LoadFromFile(const std::string& filename)
00076 {
00077     // Cleanup the previous resources
00078     Cleanup();
00079     myRefCount = new int(1);
00080 
00081     // Initialize FreeType
00082     // Note: we initialize FreeType for every font instance in order to avoid having a single
00083     // global manager that would create a lot of issues regarding creation and destruction order.
00084     FT_Library library;
00085     if (FT_Init_FreeType(&library) != 0)
00086     {
00087         Err() << "Failed to load font \"" << filename << "\" (failed to initialize FreeType)" << std::endl;
00088         return false;
00089     }
00090     myLibrary = library;
00091 
00092     // Load the new font face from the specified file
00093     FT_Face face;
00094     if (FT_New_Face(static_cast<FT_Library>(myLibrary), filename.c_str(), 0, &face) != 0)
00095     {
00096         Err() << "Failed to load font \"" << filename << "\" (failed to create the font face)" << std::endl;
00097         return false;
00098     }
00099 
00100     // Select the unicode character map
00101     if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
00102     {
00103         Err() << "Failed to load font \"" << filename << "\" (failed to set the Unicode character set)" << std::endl;
00104         return false;
00105     }
00106 
00107     // Store the loaded font in our ugly void* :)
00108     myFace = face;
00109 
00110     return true;
00111 }
00112 
00113 
00115 bool Font::LoadFromMemory(const void* data, std::size_t sizeInBytes)
00116 {
00117     // Cleanup the previous resources
00118     Cleanup();
00119     myRefCount = new int(1);
00120 
00121     // Initialize FreeType
00122     // Note: we initialize FreeType for every font instance in order to avoid having a single
00123     // global manager that would create a lot of issues regarding creation and destruction order.
00124     FT_Library library;
00125     if (FT_Init_FreeType(&library) != 0)
00126     {
00127         Err() << "Failed to load font from memory (failed to initialize FreeType)" << std::endl;
00128         return false;
00129     }
00130     myLibrary = library;
00131 
00132     // Load the new font face from the specified file
00133     FT_Face face;
00134     if (FT_New_Memory_Face(static_cast<FT_Library>(myLibrary), reinterpret_cast<const FT_Byte*>(data), static_cast<FT_Long>(sizeInBytes), 0, &face) != 0)
00135     {
00136         Err() << "Failed to load font from memory (failed to create the font face)" << std::endl;
00137         return false;
00138     }
00139 
00140     // Select the unicode character map
00141     if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
00142     {
00143         Err() << "Failed to load font from memory (failed to set the Unicode character set)" << std::endl;
00144         return false;
00145     }
00146 
00147     // Store the loaded font in our ugly void* :)
00148     myFace = face;
00149 
00150     return true;
00151 }
00152 
00153 
00155 const Glyph& Font::GetGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const
00156 {
00157     // Get the page corresponding to the character size
00158     GlyphTable& glyphs = myPages[characterSize].Glyphs;
00159 
00160     // Build the key by combining the code point and the bold flag
00161     Uint32 key = ((bold ? 1 : 0) << 31) | codePoint;
00162 
00163     // Search the glyph into the cache
00164     GlyphTable::const_iterator it = glyphs.find(key);
00165     if (it != glyphs.end())
00166     {
00167         // Found: just return it
00168         return it->second.GlyphDesc;
00169     }
00170     else
00171     {
00172         // Not found: we have to load it
00173         GlyphInfo glyph = LoadGlyph(codePoint, characterSize, bold);
00174         return glyphs.insert(std::make_pair(key, glyph)).first->second.GlyphDesc;
00175     }
00176 }
00177 
00178 
00180 int Font::GetKerning(Uint32 first, Uint32 second, unsigned int characterSize) const
00181 {
00182     // Special case where first or second is 0 (null character)
00183     if (first == 0 || second == 0)
00184         return 0;
00185 
00186     FT_Face face = static_cast<FT_Face>(myFace);
00187 
00188     if (face && FT_HAS_KERNING(face) && SetCurrentSize(characterSize))
00189     {
00190         // Convert the characters to indices
00191         FT_UInt index1 = FT_Get_Char_Index(face, first);
00192         FT_UInt index2 = FT_Get_Char_Index(face, second);
00193 
00194         // Get the kerning vector
00195         FT_Vector kerning;
00196         FT_Get_Kerning(face, index1, index2, FT_KERNING_DEFAULT, &kerning);
00197 
00198         // Return the X advance
00199         return kerning.x >> 6;
00200     }
00201     else
00202     {
00203         // Invalid font, or no kerning
00204         return 0;
00205     }
00206 }
00207 
00208 
00210 int Font::GetLineSpacing(unsigned int characterSize) const
00211 {
00212     FT_Face face = static_cast<FT_Face>(myFace);
00213 
00214     if (face && SetCurrentSize(characterSize))
00215     {
00216         return (face->size->metrics.height >> 6);
00217     }
00218     else
00219     {
00220         return 0;
00221     }
00222 }
00223 
00224 
00226 const Image& Font::GetImage(unsigned int characterSize) const
00227 {
00228     return myPages[characterSize].Texture;
00229 }
00230 
00231 
00233 Font& Font::operator =(const Font& right)
00234 {
00235     Font temp(right);
00236 
00237     std::swap(myLibrary,     temp.myLibrary);
00238     std::swap(myFace,        temp.myFace);
00239     std::swap(myPages,       temp.myPages);
00240     std::swap(myPixelBuffer, temp.myPixelBuffer);
00241     std::swap(myCurrentSize, temp.myCurrentSize);
00242     std::swap(myRefCount,    temp.myRefCount);
00243 
00244     return *this;
00245 }
00246 
00247 
00249 const Font& Font::GetDefaultFont()
00250 {
00251     static Font font;
00252     static bool loaded = false;
00253 
00254     // Load the default font on first call
00255     if (!loaded)
00256     {
00257         static const char data[] =
00258         {
00259             #include <SFML/Graphics/Arial.hpp>
00260         };
00261 
00262         font.LoadFromMemory(data, sizeof(data));
00263         loaded = true;
00264     }
00265 
00266     return font;
00267 }
00268 
00269 
00271 void Font::Cleanup()
00272 {
00273     // Check if we must destroy the FreeType pointers
00274     if (myRefCount)
00275     {
00276         // Decrease the reference counter
00277         (*myRefCount)--;
00278 
00279         // Free the resources only if we are the last owner
00280         if (*myRefCount == 0)
00281         {
00282             // Delete the reference counter
00283             delete myRefCount;
00284 
00285             // Destroy the font face
00286             if (myFace)
00287                 FT_Done_Face(static_cast<FT_Face>(myFace));
00288 
00289             // Close the library
00290             if (myLibrary)
00291                 FT_Done_FreeType(static_cast<FT_Library>(myLibrary));
00292         }
00293     }
00294 
00295     // Reset members
00296     myLibrary     = NULL;
00297     myFace        = NULL;
00298     myRefCount    = NULL;
00299     myCurrentSize = 0;
00300     myPages.clear();
00301     myPixelBuffer.clear();
00302 }
00303 
00304 
00306 Font::GlyphInfo Font::LoadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const
00307 {
00308     // The glyph to return
00309     GlyphInfo glyphInfo;
00310 
00311     // First, transform our ugly void* to a FT_Face
00312     FT_Face face = static_cast<FT_Face>(myFace);
00313     if (!face)
00314         return glyphInfo;
00315 
00316     // Set the character size
00317     if (!SetCurrentSize(characterSize))
00318         return glyphInfo;
00319 
00320     // Load the glyph corresponding to the code point
00321     if (FT_Load_Char(face, codePoint, FT_LOAD_TARGET_NORMAL) != 0)
00322         return glyphInfo;
00323 
00324     // Retrieve the glyph
00325     FT_Glyph glyphDesc;
00326     if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0)
00327         return glyphInfo;
00328 
00329     // Apply bold if necessary -- first technique using outline (highest quality)
00330     FT_Pos weight = 1 << 6;
00331     bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE);
00332     if (bold && outline)
00333     {
00334         FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc;
00335         FT_Outline_Embolden(&outlineGlyph->outline, weight);
00336     }
00337 
00338     // Convert the glyph to a bitmap (i.e. rasterize it)
00339     FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1);
00340     FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyphDesc;
00341     FT_Bitmap& bitmap = bitmapGlyph->bitmap;
00342 
00343     // Apply bold if necessary -- fallback technique using bitmap (lower quality)
00344     if (bold && !outline)
00345     {
00346         FT_Bitmap_Embolden(static_cast<FT_Library>(myLibrary), &bitmap, weight, weight);
00347     }
00348 
00349     // Compute the glyph's advance offset
00350     glyphInfo.GlyphDesc.Advance = glyphDesc->advance.x >> 16;
00351     if (bold)
00352         glyphInfo.GlyphDesc.Advance += weight >> 6;
00353 
00354     int width  = bitmap.width;
00355     int height = bitmap.rows;
00356     if ((width > 0) && (height > 0))
00357     {
00358         // Leave a small padding around characters, so that filtering doesn't
00359         // pollute them with pixels from neighbours
00360         const unsigned int padding = 1;
00361 
00362         // Get the glyphs page corresponding to the character size
00363         Page& page = myPages[characterSize];
00364 
00365         // Find a good position for the new glyph into the texture
00366         glyphInfo.TextureRect = FindGlyphRect(page, width + 2 * padding, height + 2 * padding);
00367 
00368         // Compute the glyph's texture coordinates and bounding box
00369         glyphInfo.GlyphDesc.TexCoords        = page.Texture.GetTexCoords(glyphInfo.TextureRect);
00370         glyphInfo.GlyphDesc.Rectangle.Left   = bitmapGlyph->left - padding;
00371         glyphInfo.GlyphDesc.Rectangle.Top    = -bitmapGlyph->top - padding;
00372         glyphInfo.GlyphDesc.Rectangle.Right  = bitmapGlyph->left + width + padding;
00373         glyphInfo.GlyphDesc.Rectangle.Bottom = -bitmapGlyph->top + height + padding;
00374 
00375         // Extract the glyph's pixels from the bitmap
00376         myPixelBuffer.resize(width * height * 4, 255);
00377         const Uint8* pixels = bitmap.buffer;
00378         for (int y = 0; y < height; ++y)
00379         {
00380             for (int x = 0; x < width; ++x)
00381             {
00382                 // The color channels remain white, just fill the alpha channel
00383                 std::size_t index = (x + y * width) * 4 + 3;
00384                 myPixelBuffer[index] = pixels[x];
00385 
00386                 // Formula for FT_RENDER_MODE_MONO
00387                 //myPixelBuffer[index] = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0;
00388             }
00389             pixels += bitmap.pitch;
00390         }
00391 
00392         // Write the pixels to the texture
00393         IntRect subrect = glyphInfo.TextureRect;
00394         subrect.Left   += padding;
00395         subrect.Top    += padding;
00396         subrect.Right  -= padding;
00397         subrect.Bottom -= padding;
00398         page.Texture.UpdatePixels(&myPixelBuffer[0], subrect);
00399     }
00400 
00401     // Delete the FT glyph
00402     FT_Done_Glyph(glyphDesc);
00403 
00404     // Done :)
00405     return glyphInfo;
00406 }
00407 
00408 
00410 IntRect Font::FindGlyphRect(Page& page, unsigned int width, unsigned int height) const
00411 {
00412     // Find the line that fits well the glyph
00413     Row* row = NULL;
00414     float bestRatio = 0;
00415     for (std::vector<Row>::iterator it = page.Rows.begin(); it != page.Rows.end() && !row; ++it)
00416     {
00417         float ratio = static_cast<float>(height) / it->Height;
00418 
00419         // Ignore rows that are either too small or too high
00420         if ((ratio < 0.7f) || (ratio > 1.f))
00421             continue;
00422 
00423         // Check if there's enough horizontal space left in the row
00424         if (width > page.Texture.GetWidth() - it->Width)
00425             continue;
00426 
00427         // Make sure that this new row is the best found so far
00428         if (ratio < bestRatio)
00429             continue;
00430 
00431         // The current row passed all the tests: we can select it
00432         row = &*it;
00433         bestRatio = ratio;
00434     }
00435 
00436     // If we didn't find a matching row, create a new one (10% taller than the glyph)
00437     if (!row)
00438     {
00439         int rowHeight = height + height / 10;
00440         if (page.NextRow + rowHeight >= page.Texture.GetHeight())
00441         {
00442             // Not enough space: resize the texture if possible
00443             unsigned int textureWidth  = page.Texture.GetWidth();
00444             unsigned int textureHeight = page.Texture.GetHeight();
00445             if ((textureWidth * 2 <= Image::GetMaximumSize()) && (textureHeight * 2 <= Image::GetMaximumSize()))
00446             {
00447                 // Make the texture 2 times bigger
00448                 std::size_t size = textureWidth * textureHeight * 4;
00449                 std::vector<Uint8> pixels(size);
00450                 memcpy(&pixels[0], page.Texture.GetPixelsPtr(), size);
00451                 page.Texture.Create(textureWidth * 2, textureHeight * 2, Color(255, 255, 255, 0));
00452                 page.Texture.UpdatePixels(&pixels[0], IntRect(0, 0, textureWidth, textureHeight));
00453 
00454                 // Adjust the texture coordinates of all the glyphs that are stored in this page
00455                 for (GlyphTable::iterator it = page.Glyphs.begin(); it != page.Glyphs.end(); ++it)
00456                 {
00457                     it->second.GlyphDesc.TexCoords = page.Texture.GetTexCoords(it->second.TextureRect);
00458                 }
00459             }
00460             else
00461             {
00462                 // Oops, we've reached the maximum texture size...
00463                 Err() << "Failed to add a new character to the font: the maximum image size has been reached" << std::endl;
00464                 return IntRect(0, 0, 2, 2);
00465             }
00466         }
00467 
00468         // We can now create the new row
00469         page.Rows.push_back(Row(page.NextRow, rowHeight));
00470         page.NextRow += rowHeight;
00471         row = &page.Rows.back();
00472     }
00473 
00474     // Find the glyph's rectangle on the selected row
00475     IntRect rect(row->Width, row->Top, row->Width + width, row->Top + height);
00476 
00477     // Update the row informations
00478     row->Width += width;
00479 
00480     return rect;
00481 }
00482 
00483 
00485 bool Font::SetCurrentSize(unsigned int characterSize) const
00486 {
00487     // FT_Set_Pixel_Sizes is an expensive function, so we must call it
00488     // only when necessary to avoid killing performances
00489 
00490     if (myCurrentSize != characterSize)
00491     {
00492         myCurrentSize = characterSize;
00493         return FT_Set_Pixel_Sizes(static_cast<FT_Face>(myFace), 0, characterSize) == 0;
00494     }
00495     else
00496     {
00497         return true;
00498     }
00499 }
00500 
00501 
00503 Font::Page::Page() :
00504 NextRow(2)
00505 {
00506     // Make sure that the texture is initialized by default
00507     Texture.Create(128, 128, Color(255, 255, 255, 0));
00508 
00509     // Reserve a 2x2 white square for texturing underlines
00510     for (int x = 0; x < 2; ++x)
00511         for (int y = 0; y < 2; ++y)
00512             Texture.SetPixel(x, y, Color(255, 255, 255, 255));
00513 }
00514 
00515 } // namespace sf

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