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 ::