Complete rewrite of sf::Font to make it more flexible (no more fixed charset and size)

FS#125 - Fix tab character not working in sf::String

git-svn-id: https://sfml.svn.sourceforge.net/svnroot/sfml/branches/sfml2@1309 4e206d99-4929-0410-ac5d-dfc041789085
This commit is contained in:
LaurentGom 2009-12-13 15:49:30 +00:00
parent 839c80556d
commit 3a34f81561
32 changed files with 1016 additions and 3016 deletions

View file

@ -26,133 +26,213 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/FontLoader.hpp>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include <iostream>
namespace sf
{
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
Uint32 Font::ourDefaultCharset[] =
{
// Printable characters in ASCII range
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E,
// Printable characters in extended ASCII range
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0x2A, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,
// To make it a valid string
0x00
};
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Font::Font() :
myCharSize(0)
myLibrary (NULL),
myFace (NULL),
myRefCount (NULL),
myCurrentSize(0)
{
}
////////////////////////////////////////////////////////////
/// Load the font from a file
////////////////////////////////////////////////////////////
bool Font::LoadFromFile(const std::string& filename, unsigned int charSize, String charset)
Font::Font(const Font& copy) :
myLibrary (copy.myLibrary),
myFace (copy.myFace),
myRefCount (copy.myRefCount),
myPages (copy.myPages),
myPixelBuffer(copy.myPixelBuffer),
myCurrentSize(copy.myCurrentSize)
{
// Clear the previous character map
myGlyphs.clear();
// Note: as FreeType doesn't provide functions for copying/cloning,
// we must share all the FreeType pointers
// Always add these special characters
if (std::find(charset.Begin(), charset.End(), L' ') == charset.End()) charset += L' ';
if (std::find(charset.Begin(), charset.End(), L'\n') == charset.End()) charset += L'\n';
if (std::find(charset.Begin(), charset.End(), L'\v') == charset.End()) charset += L'\v';
if (std::find(charset.Begin(), charset.End(), L'\t') == charset.End()) charset += L'\t';
return priv::FontLoader::GetInstance().LoadFontFromFile(filename, charSize, charset, *this);
if (myRefCount)
(*myRefCount)++;
}
////////////////////////////////////////////////////////////
/// Load the font from a file in memory
////////////////////////////////////////////////////////////
bool Font::LoadFromMemory(const char* data, std::size_t sizeInBytes, unsigned int charSize, String charset)
Font::~Font()
{
// Clear the previous character map
myGlyphs.clear();
Cleanup();
}
// Check parameters
if (!data || (sizeInBytes == 0))
////////////////////////////////////////////////////////////
bool Font::LoadFromFile(const std::string& filename)
{
// Cleanup the previous resources
Cleanup();
// Initialize FreeType
// Note: we initialize FreeType for every font instance in order to avoid having a single
// global manager that would create a lot of issues regarding creation and destruction order.
FT_Library library;
if (FT_Init_FreeType(&library) != 0)
{
std::cerr << "Failed to load font from memory, no data provided" << std::endl;
std::cerr << "Failed to load font \"" << filename << "\" (failed to initialize FreeType)" << std::endl;
return false;
}
myLibrary = library;
// Load the new font face from the specified file
FT_Face face;
if (FT_New_Face(static_cast<FT_Library>(myLibrary), filename.c_str(), 0, &face) != 0)
{
std::cerr << "Failed to load font \"" << filename << "\" (failed to create the font face)" << std::endl;
return false;
}
// Always add these special characters
if (std::find(charset.Begin(), charset.End(), L' ') == charset.End()) charset += L' ';
if (std::find(charset.Begin(), charset.End(), L'\n') == charset.End()) charset += L'\n';
if (std::find(charset.Begin(), charset.End(), L'\v') == charset.End()) charset += L'\v';
if (std::find(charset.Begin(), charset.End(), L'\t') == charset.End()) charset += L'\t';
return priv::FontLoader::GetInstance().LoadFontFromMemory(data, sizeInBytes, charSize, charset, *this);
}
////////////////////////////////////////////////////////////
/// Get the base size of characters in the font;
/// All glyphs dimensions are based on this value
////////////////////////////////////////////////////////////
unsigned int Font::GetCharacterSize() const
{
return myCharSize;
}
////////////////////////////////////////////////////////////
/// Get the description of a glyph (character)
/// given by its unicode value
////////////////////////////////////////////////////////////
const Glyph& Font::GetGlyph(Uint32 codePoint) const
{
std::map<Uint32, Glyph>::const_iterator it = myGlyphs.find(codePoint);
if (it != myGlyphs.end())
// Select the unicode character map
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
{
// Valid glyph
return it->second;
std::cerr << "Failed to load font \"" << filename << "\" (failed to set the Unicode character set)" << std::endl;
return false;
}
// Store the loaded font in our ugly void* :)
myFace = face;
return true;
}
////////////////////////////////////////////////////////////
bool Font::LoadFromMemory(const char* data, std::size_t sizeInBytes)
{
// Cleanup the previous resources
Cleanup();
// Initialize FreeType
// Note: we initialize FreeType for every font instance in order to avoid having a single
// global manager that would create a lot of issues regarding creation and destruction order.
FT_Library library;
if (FT_Init_FreeType(&library) != 0)
{
std::cerr << "Failed to load font from memory (failed to initialize FreeType)" << std::endl;
return false;
}
myLibrary = library;
// Load the new font face from the specified file
FT_Face face;
if (FT_New_Memory_Face(static_cast<FT_Library>(myLibrary), reinterpret_cast<const FT_Byte*>(data), static_cast<FT_Long>(sizeInBytes), 0, &face) != 0)
{
std::cerr << "Failed to load font from memory (failed to create the font face)" << std::endl;
return false;
}
// Select the unicode character map
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
{
std::cerr << "Failed to load font from memory (failed to set the Unicode character set)" << std::endl;
return false;
}
// Store the loaded font in our ugly void* :)
myFace = face;
return true;
}
////////////////////////////////////////////////////////////
const Glyph& Font::GetGlyph(Uint32 codePoint, unsigned int characterSize) const
{
// Get the page corresponding to the character size
GlyphTable& glyphs = myPages[characterSize].Glyphs;
// Search the glyph into the cache
GlyphTable::const_iterator it = glyphs.find(codePoint);
if (it != glyphs.end())
{
// Found: just return it
return it->second.GlyphDesc;
}
else
{
// Invalid glyph -- return an invalid glyph
static const Glyph invalid;
return invalid;
// Not found: we have to load it
GlyphInfo glyph = LoadGlyph(codePoint, characterSize);
return glyphs.insert(std::make_pair(codePoint, glyph)).first->second.GlyphDesc;
}
}
////////////////////////////////////////////////////////////
/// Get the image containing the rendered characters (glyphs)
////////////////////////////////////////////////////////////
const Image& Font::GetImage() const
int Font::GetKerning(Uint32 first, Uint32 second, unsigned int characterSize) const
{
return myTexture;
FT_Face face = static_cast<FT_Face>(myFace);
if (face && FT_HAS_KERNING(face) && SetCurrentSize(characterSize))
{
// Convert the characters to indices
FT_UInt index1 = FT_Get_Char_Index(face, first);
FT_UInt index2 = FT_Get_Char_Index(face, second);
// Get the kerning vector
FT_Vector kerning;
FT_Get_Kerning(face, index1, index2, FT_KERNING_DEFAULT, &kerning);
// Return the X advance
return kerning.x >> 6;
}
else
{
// Invalid font, or no kerning
return 0;
}
}
////////////////////////////////////////////////////////////
/// Get the SFML default built-in font (Arial)
int Font::GetLineSpacing(unsigned int characterSize) const
{
FT_Face face = static_cast<FT_Face>(myFace);
if (face && SetCurrentSize(characterSize))
{
return (face->size->metrics.height >> 6);
}
else
{
return 0;
}
}
////////////////////////////////////////////////////////////
const Image& Font::GetImage(unsigned int characterSize) const
{
return myPages[characterSize].Texture;
}
////////////////////////////////////////////////////////////
Font& Font::operator =(const Font& right)
{
Font temp(right);
std::swap(myLibrary, temp.myLibrary);
std::swap(myFace, temp.myFace);
std::swap(myPages, temp.myPages);
std::swap(myPixelBuffer, temp.myPixelBuffer);
std::swap(myCurrentSize, temp.myCurrentSize);
return *this;
}
////////////////////////////////////////////////////////////
const Font& Font::GetDefaultFont()
{
@ -167,11 +247,231 @@ const Font& Font::GetDefaultFont()
#include <SFML/Graphics/Arial.hpp>
};
font.LoadFromMemory(data, sizeof(data), 30);
font.LoadFromMemory(data, sizeof(data));
loaded = true;
}
return font;
}
////////////////////////////////////////////////////////////
void Font::Cleanup()
{
// Check if we must destroy the FreeType pointers
if (myRefCount)
{
// Decrease the reference counter
(*myRefCount)--;
// Free the resources only if we are the last owner
if (*myRefCount == 0)
{
// Delete the reference counter
delete myRefCount;
// Destroy the font face
if (myFace)
FT_Done_Face(static_cast<FT_Face>(myFace));
// Close the library
if (myLibrary)
FT_Done_FreeType(static_cast<FT_Library>(myLibrary));
}
}
// Reset members
myLibrary = NULL;
myFace = NULL;
myRefCount = NULL;
myCurrentSize = 0;
myPages.clear();
myPixelBuffer.clear();
}
////////////////////////////////////////////////////////////
Font::GlyphInfo Font::LoadGlyph(Uint32 codePoint, unsigned int characterSize) const
{
// The glyph to return
GlyphInfo glyphInfo;
// First, transform our ugly void* to a FT_Face
FT_Face face = static_cast<FT_Face>(myFace);
if (!face)
return glyphInfo;
// Set the character size
if (!SetCurrentSize(characterSize))
return glyphInfo;
// Load the glyph corresponding to the code point
if (FT_Load_Char(face, codePoint, FT_LOAD_TARGET_NORMAL) != 0)
return glyphInfo;
// Convert the glyph to a bitmap (i.e. rasterize it)
FT_Glyph glyphDesc;
if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0)
return glyphInfo;
FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1);
FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyphDesc;
FT_Bitmap& bitmap = bitmapGlyph->bitmap;
// Compute the glyph's advance offset
glyphInfo.GlyphDesc.Advance = bitmapGlyph->root.advance.x >> 16;
if ((bitmap.width > 0) && (bitmap.rows > 0))
{
// Leave a small padding around characters, so that filtering doesn't
// pollute them with pixels from neighbours
const unsigned int padding = 1;
// Get the glyphs page corresponding to the character size
Page& page = myPages[characterSize];
// Find a good position for the new glyph into the texture
glyphInfo.TextureRect = FindGlyphRect(page, bitmap.width + 2 * padding, bitmap.rows + 2 * padding);
// Compute the glyph's texture coordinates and bounding box
glyphInfo.GlyphDesc.TexCoords = page.Texture.GetTexCoords(glyphInfo.TextureRect);
glyphInfo.GlyphDesc.Rectangle.Left = bitmapGlyph->left - padding;
glyphInfo.GlyphDesc.Rectangle.Top = -bitmapGlyph->top - padding;
glyphInfo.GlyphDesc.Rectangle.Right = bitmapGlyph->left + bitmap.width + padding;
glyphInfo.GlyphDesc.Rectangle.Bottom = -bitmapGlyph->top + bitmap.rows + padding;
// Extract the glyph's pixels from the bitmap
myPixelBuffer.resize(bitmap.width * bitmap.rows * 4, 255);
const Uint8* pixels = bitmap.buffer;
for (int y = 0; y < bitmap.rows; ++y)
{
for (int x = 0; x < bitmap.width; ++x)
{
// The color channels remain white, just fill the alpha channel
std::size_t index = (x + y * bitmap.width) * 4 + 3;
myPixelBuffer[index] = pixels[x];
// Formula for FT_RENDER_MODE_MONO
// myPixelBuffer[index] = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0;
}
pixels += bitmap.pitch;
}
// Write the pixels to the texture
IntRect subrect = glyphInfo.TextureRect;
subrect.Left += padding;
subrect.Top += padding;
subrect.Right -= padding;
subrect.Bottom -= padding;
page.Texture.UpdatePixels(&myPixelBuffer[0], subrect);
}
// Delete the FT glyph
FT_Done_Glyph(glyphDesc);
// Done :)
return glyphInfo;
}
////////////////////////////////////////////////////////////
IntRect Font::FindGlyphRect(Page& page, unsigned int width, unsigned int height) const
{
// Find the line that fits well the glyph
Row* row = NULL;
float bestRatio = 0;
for (std::vector<Row>::iterator it = page.Rows.begin(); it != page.Rows.end() && !row; ++it)
{
float ratio = static_cast<float>(height) / it->Height;
// Ignore rows that are either too small or too high
if ((ratio < 0.7f) || (ratio > 1.f))
continue;
// Check if there's enough horizontal space left in the row
if (width > page.Texture.GetWidth() - it->Width)
continue;
// Make sure that this new row is the best found so far
if (ratio < bestRatio)
continue;
// The current row passed all the tests: we can select it
row = &*it;
bestRatio = ratio;
}
// If we didn't find a matching row, create a new one (10% taller than the glyph)
if (!row)
{
int rowHeight = height + height / 10;
if (page.NextRow + rowHeight >= page.Texture.GetHeight())
{
// Not enough space: resize the texture if possible
unsigned int textureWidth = page.Texture.GetWidth();
unsigned int textureHeight = page.Texture.GetHeight();
if ((textureWidth * 2 <= Image::GetMaximumSize()) && (textureHeight * 2 <= Image::GetMaximumSize()))
{
// Make the texture 2 times bigger
std::size_t size = textureWidth * textureHeight * 4;
std::vector<Uint8> pixels(size);
memcpy(&pixels[0], page.Texture.GetPixelsPtr(), size);
page.Texture.Create(textureWidth * 2, textureHeight * 2, sf::Color(255, 255, 255, 0));
page.Texture.UpdatePixels(&pixels[0], IntRect(0, 0, textureWidth, textureHeight));
// Adjust the texture coordinates of all the glyphs that are stored in this page
for (GlyphTable::iterator it = page.Glyphs.begin(); it != page.Glyphs.end(); ++it)
{
it->second.GlyphDesc.TexCoords = page.Texture.GetTexCoords(it->second.TextureRect);
}
}
else
{
// Oops, we've reached the maximum texture size...
std::cerr << "Failed to add a new character to the font: the maximum image size has been reached" << std::endl;
return IntRect(0, 0, 2, 2);
}
}
// We can now create the new row
page.Rows.push_back(Row(page.NextRow, rowHeight));
page.NextRow += rowHeight;
row = &page.Rows.back();
}
// Find the glyph's rectangle on the selected row
IntRect rect(row->Width, row->Top, row->Width + width, row->Top + height);
// Update the row informations
row->Width += width;
return rect;
}
////////////////////////////////////////////////////////////
bool Font::SetCurrentSize(unsigned int characterSize) const
{
// FT_Set_Pixel_Sizes is an expensive function, so we must call it
// only when necessary to avoid killing performances
if (myCurrentSize != characterSize)
{
myCurrentSize = characterSize;
return FT_Set_Pixel_Sizes(static_cast<FT_Face>(myFace), characterSize, characterSize) == 0;
}
else
{
return true;
}
}
////////////////////////////////////////////////////////////
Font::Page::Page() :
NextRow(0)
{
// Make sure that the texture is initialized by default
Texture.Create(128, 128, Color(255, 255, 255, 0));
}
} // namespace sf

View file

@ -1,671 +0,0 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
//#define SFML_USE_STBTT
#ifndef SFML_USE_STBTT
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/FontLoader.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include FT_GLYPH_H
#include <iostream>
#include <map>
#include <vector>
#include <math.h>
namespace
{
////////////////////////////////////////////////////////////
// Functor to sort glyphs by size
////////////////////////////////////////////////////////////
struct SizeCompare
{
bool operator ()(FT_BitmapGlyph left, FT_BitmapGlyph right) const
{
return left->bitmap.rows < right->bitmap.rows;
}
};
}
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// Get the unique instance of the class
////////////////////////////////////////////////////////////
FontLoader& FontLoader::GetInstance()
{
static FontLoader instance;
return instance;
}
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
FontLoader::FontLoader()
{
// Initialize FreeType library
FT_Error error = FT_Init_FreeType(&myLibrary);
if (error)
{
std::cerr << "Failed to initialize FreeType library (error code : " << error << ")" << std::endl;
return;
}
}
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
FontLoader::~FontLoader()
{
// Shutdown FreeType library
if (myLibrary)
FT_Done_FreeType(myLibrary);
}
////////////////////////////////////////////////////////////
/// Load a font from a file
////////////////////////////////////////////////////////////
bool FontLoader::LoadFontFromFile(const std::string& filename, unsigned int charSize, const String& charset, Font& font)
{
// Check if Freetype is correctly initialized
if (!myLibrary)
{
std::cerr << "Failed to load font \"" << filename << "\", FreeType has not been initialized" << std::endl;
return false;
}
// Create a new font face from the specified file
FT_Face face;
FT_Error error = FT_New_Face(myLibrary, filename.c_str(), 0, &face);
if (error)
{
std::cerr << "Failed to load font \"" << filename << "\" (" << GetErrorDesc(error) << ")" << std::endl;
return false;
}
// Create the bitmap font
error = CreateBitmapFont(face, charSize, charset, font);
if (error)
std::cerr << "Failed to load font \"" << filename << "\" (" << GetErrorDesc(error) << ")" << std::endl;
// Delete the font
FT_Done_Face(face);
return error == 0;
}
////////////////////////////////////////////////////////////
/// Load the font from a file in memory
////////////////////////////////////////////////////////////
bool FontLoader::LoadFontFromMemory(const char* data, std::size_t sizeInBytes, unsigned int charSize, const String& charset, Font& font)
{
// Check if Freetype is correctly initialized
if (!myLibrary)
{
std::cerr << "Failed to load font from memory, FreeType has not been initialized" << std::endl;
return false;
}
// Create a new font face from the specified memory data
FT_Face face;
FT_Error error = FT_New_Memory_Face(myLibrary, reinterpret_cast<const FT_Byte*>(data), static_cast<FT_Long>(sizeInBytes), 0, &face);
if (error)
{
std::cerr << "Failed to load font from memory (" << GetErrorDesc(error) << ")" << std::endl;
return false;
}
// Create the bitmap font
error = CreateBitmapFont(face, charSize, charset, font);
if (error)
std::cerr << "Failed to load font from memory (" << GetErrorDesc(error) << ")" << std::endl;
// Delete the font
FT_Done_Face(face);
return error == 0;
}
////////////////////////////////////////////////////////////
/// Create a bitmap font from a font face and a characters set
////////////////////////////////////////////////////////////
FT_Error FontLoader::CreateBitmapFont(FT_Face face, unsigned int charSize, const String& charset, Font& font)
{
// Let's find how many characters to put in each row to make them fit into a squared texture
unsigned int maxSize = Image::GetMaximumSize();
int nbChars = static_cast<int>(sqrt(static_cast<double>(charset.GetSize())) * 0.75);
// Clamp the character size to make sure we won't create a texture too big
if (nbChars * charSize >= maxSize)
charSize = maxSize / nbChars;
// Initialize the dimensions
unsigned int left = 0;
unsigned int top = 0;
unsigned int texWidth = Image::GetValidSize(charSize * nbChars);
unsigned int texHeight = charSize * nbChars;
std::vector<unsigned int> tops(texWidth, 0);
// Create a pixel buffer for rendering every glyph
std::vector<Uint8> glyphsBuffer(texWidth * texHeight * 4);
// Setup the font size
FT_Error error = FT_Set_Pixel_Sizes(face, charSize, charSize);
if (error)
return error;
// Select the unicode character map
error = FT_Select_Charmap(face, FT_ENCODING_UNICODE);
if (error)
return error;
// Render all glyphs and sort them by size to optimize texture space
typedef std::multimap<FT_BitmapGlyph, Uint32, SizeCompare> GlyphTable;
GlyphTable glyphs;
for (std::size_t i = 0; i < charset.GetSize(); ++i)
{
// Load the glyph corresponding to the current character
error = FT_Load_Char(face, charset[i], FT_LOAD_TARGET_NORMAL);
if (error)
return error;
// Convert the glyph to a bitmap (ie. rasterize it)
FT_Glyph glyph;
error = FT_Get_Glyph(face->glyph, &glyph);
if (error)
return error;
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
// Add it to the sorted table of glyphs
glyphs.insert(std::make_pair(bitmapGlyph, charset[i]));
}
// Leave a small margin around characters, so that filtering doesn't
// pollute them with pixels from neighbours
unsigned int padding = 1;
unsigned int margin = 1;
// Copy the rendered glyphs into the texture
unsigned int maxHeight = 0;
std::map<Uint32, IntRect> coords;
for (GlyphTable::const_iterator i = glyphs.begin(); i != glyphs.end(); ++i)
{
// Get the bitmap of the current glyph
Glyph& curGlyph = font.myGlyphs[i->second];
FT_BitmapGlyph bitmapGlyph = i->first;
FT_Bitmap& bitmap = bitmapGlyph->bitmap;
// Make sure we don't go over the texture width
if (left + bitmap.width + 2 * padding + margin >= texWidth)
left = 0;
// Compute the top coordinate
top = tops[left];
for (unsigned int x = 0; x < bitmap.width + 2 * padding + margin; ++x)
top = std::max(top, tops[left + x]);
// Make sure we don't go over the texture height -- resize it if we need more space
if (top + bitmap.rows + 2 * padding + margin >= texHeight)
{
texHeight *= 2;
glyphsBuffer.resize(texWidth * texHeight * 4);
}
// Store the character's position and size
curGlyph.Rectangle.Left = bitmapGlyph->left - padding;
curGlyph.Rectangle.Top = -bitmapGlyph->top - padding;
curGlyph.Rectangle.Right = bitmapGlyph->left + bitmap.width + padding;
curGlyph.Rectangle.Bottom = -bitmapGlyph->top + bitmap.rows + padding;
curGlyph.Advance = bitmapGlyph->root.advance.x >> 16;
// Texture size may change, so let the texture coordinates be calculated later
coords[i->second] = IntRect(left, top, left + bitmap.width + 2 * padding, top + bitmap.rows + 2 * padding);
// Draw the glyph into our bitmap font
const Uint8* pixels = bitmap.buffer;
for (int y = 0; y < bitmap.rows; ++y)
{
for (int x = 0; x < bitmap.width; ++x)
{
std::size_t index = x + left + padding + (y + top + padding) * texWidth;
glyphsBuffer[index * 4 + 0] = 255;
glyphsBuffer[index * 4 + 1] = 255;
glyphsBuffer[index * 4 + 2] = 255;
glyphsBuffer[index * 4 + 3] = pixels[x];
// Formula for FT_RENDER_MODE_MONO
// glyphsBuffer[index * 4 + 3] = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0;
}
pixels += bitmap.pitch;
}
// Update the rendering coordinates
for (unsigned int x = 0; x < bitmap.width + 2 * padding + margin; ++x)
tops[left + x] = top + bitmap.rows + 2 * padding + margin;
left += bitmap.width + 2 * padding + margin;
if (top + bitmap.rows + 2 * padding > maxHeight)
maxHeight = top + bitmap.rows + 2 * padding;
// Delete the glyph
FT_Done_Glyph((FT_Glyph)bitmapGlyph);
}
// Create the font's texture
texHeight = maxHeight;
glyphsBuffer.resize(texWidth * texHeight * 4);
font.myTexture.LoadFromPixels(texWidth, texHeight, &glyphsBuffer[0]);
// Now that the texture is created, we can precompute texture coordinates
for (std::size_t i = 0; i < charset.GetSize(); ++i)
{
Uint32 curChar = charset[i];
font.myGlyphs[curChar].TexCoords = font.myTexture.GetTexCoords(coords[curChar]);
}
// Update the character size (it may have been changed by the function)
font.myCharSize = charSize;
return 0;
}
////////////////////////////////////////////////////////////
/// Get a description from a FT error code
////////////////////////////////////////////////////////////
std::string FontLoader::GetErrorDesc(FT_Error error)
{
switch (error)
{
// Generic errors
case FT_Err_Cannot_Open_Resource : return "cannot open resource";
case FT_Err_Unknown_File_Format : return "unknown file format";
case FT_Err_Invalid_File_Format : return "broken file";
case FT_Err_Invalid_Version : return "invalid FreeType version";
case FT_Err_Lower_Module_Version : return "module version is too low";
case FT_Err_Invalid_Argument : return "invalid argument";
case FT_Err_Unimplemented_Feature : return "unimplemented feature";
case FT_Err_Invalid_Table : return "broken table";
case FT_Err_Invalid_Offset : return "broken offset within table";
// Glyph / character errors
case FT_Err_Invalid_Glyph_Index : return "invalid glyph index";
case FT_Err_Invalid_Character_Code : return "invalid character code";
case FT_Err_Invalid_Glyph_Format : return "unsupported glyph image format";
case FT_Err_Cannot_Render_Glyph : return "cannot render this glyph format";
case FT_Err_Invalid_Outline : return "invalid outline";
case FT_Err_Invalid_Composite : return "invalid composite glyph";
case FT_Err_Too_Many_Hints : return "too many hints";
case FT_Err_Invalid_Pixel_Size : return "invalid pixel size";
// Handle errors
case FT_Err_Invalid_Handle : return "invalid object handle";
case FT_Err_Invalid_Library_Handle : return "invalid library handle";
case FT_Err_Invalid_Driver_Handle : return "invalid module handle";
case FT_Err_Invalid_Face_Handle : return "invalid face handle";
case FT_Err_Invalid_Size_Handle : return "invalid size handle";
case FT_Err_Invalid_Slot_Handle : return "invalid glyph slot handle";
case FT_Err_Invalid_CharMap_Handle : return "invalid charmap handle";
case FT_Err_Invalid_Cache_Handle : return "invalid cache manager handle";
case FT_Err_Invalid_Stream_Handle : return "invalid stream handle";
// Driver errors
case FT_Err_Too_Many_Drivers : return "too many modules";
case FT_Err_Too_Many_Extensions : return "too many extensions";
// Memory errors
case FT_Err_Out_Of_Memory : return "out of memory";
case FT_Err_Unlisted_Object : return "unlisted object";
// Stream errors
case FT_Err_Cannot_Open_Stream : return "cannot open stream";
case FT_Err_Invalid_Stream_Seek : return "invalid stream seek";
case FT_Err_Invalid_Stream_Skip : return "invalid stream skip";
case FT_Err_Invalid_Stream_Read : return "invalid stream read";
case FT_Err_Invalid_Stream_Operation : return "invalid stream operation";
case FT_Err_Invalid_Frame_Operation : return "invalid frame operation";
case FT_Err_Nested_Frame_Access : return "nested frame access";
case FT_Err_Invalid_Frame_Read : return "invalid frame read";
// Raster errors
case FT_Err_Raster_Uninitialized : return "raster uninitialized";
case FT_Err_Raster_Corrupted : return "raster corrupted";
case FT_Err_Raster_Overflow : return "raster overflow";
case FT_Err_Raster_Negative_Height : return "negative height while rastering";
// Cache errors
case FT_Err_Too_Many_Caches : return "too many registered caches";
// TrueType and SFNT errors
case FT_Err_Invalid_Opcode : return "invalid opcode";
case FT_Err_Too_Few_Arguments : return "too few arguments";
case FT_Err_Stack_Overflow : return "stack overflow";
case FT_Err_Code_Overflow : return "code overflow";
case FT_Err_Bad_Argument : return "bad argument";
case FT_Err_Divide_By_Zero : return "division by zero";
case FT_Err_Invalid_Reference : return "invalid reference";
case FT_Err_Debug_OpCode : return "found debug opcode";
case FT_Err_ENDF_In_Exec_Stream : return "found ENDF opcode in execution stream";
case FT_Err_Nested_DEFS : return "nested DEFS";
case FT_Err_Invalid_CodeRange : return "invalid code range";
case FT_Err_Execution_Too_Long : return "execution context too long";
case FT_Err_Too_Many_Function_Defs : return "too many function definitions";
case FT_Err_Too_Many_Instruction_Defs : return "too many instruction definitions";
case FT_Err_Table_Missing : return "SFNT font table missing";
case FT_Err_Horiz_Header_Missing : return "horizontal header (hhea) table missing";
case FT_Err_Locations_Missing : return "locations (loca) table missing";
case FT_Err_Name_Table_Missing : return "name table missing";
case FT_Err_CMap_Table_Missing : return "character map (cmap) table missing";
case FT_Err_Hmtx_Table_Missing : return "horizontal metrics (hmtx) table missing";
case FT_Err_Post_Table_Missing : return "PostScript (post) table missing";
case FT_Err_Invalid_Horiz_Metrics : return "invalid horizontal metrics";
case FT_Err_Invalid_CharMap_Format : return "invalid character map (cmap) format";
case FT_Err_Invalid_PPem : return "invalid ppem value";
case FT_Err_Invalid_Vert_Metrics : return "invalid vertical metrics";
case FT_Err_Could_Not_Find_Context : return "could not find context";
case FT_Err_Invalid_Post_Table_Format : return "invalid PostScript (post) table format";
case FT_Err_Invalid_Post_Table : return "invalid PostScript (post) table";
// CCF, CID and Type 1 errors
case FT_Err_Syntax_Error : return "opcode syntax error";
case FT_Err_Stack_Underflow : return "argument stack underflow";
case FT_Err_Ignore : return "ignore";
// BDF errors
case FT_Err_Missing_Startfont_Field : return "`STARTFONT' field missing";
case FT_Err_Missing_Font_Field : return "`FONT' field missing";
case FT_Err_Missing_Size_Field : return "`SIZE' field missing";
case FT_Err_Missing_Chars_Field : return "`CHARS' field missing";
case FT_Err_Missing_Startchar_Field : return "`STARTCHAR' field missing";
case FT_Err_Missing_Encoding_Field : return "`ENCODING' field missing";
case FT_Err_Missing_Bbx_Field : return "`BBX' field missing";
}
return "unknown error";
}
} // namespace priv
} // namespace sf
#else
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/FontLoader.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#define STB_TRUETYPE_IMPLEMENTATION
#include <SFML/Graphics/stb_truetype/stb_truetype.h>
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <math.h>
namespace
{
////////////////////////////////////////////////////////////
// Functor to sort glyphs by size
////////////////////////////////////////////////////////////
struct SizeCompare
{
bool operator ()(const sf::Glyph& left, const sf::Glyph& right) const
{
return left.Rectangle.GetSize().y < right.Rectangle.GetSize().y;
}
};
}
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// Get the unique instance of the class
////////////////////////////////////////////////////////////
FontLoader& FontLoader::GetInstance()
{
static FontLoader instance;
return instance;
}
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
FontLoader::FontLoader()
{
}
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
FontLoader::~FontLoader()
{
}
////////////////////////////////////////////////////////////
/// Load a font from a file
////////////////////////////////////////////////////////////
bool FontLoader::LoadFontFromFile(const std::string& filename, unsigned int charSize, const String& charset, Font& font)
{
// Get the contents of the font file
std::ifstream file(filename.c_str(), std::ios_base::binary);
if (!file)
return false;
file.seekg(0, std::ios::end);
std::size_t length = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> data(length);
file.read(&data[0], static_cast<std::streamsize>(length));
// Load from memory
return LoadFontFromMemory(&data[0], data.size(), charSize, charset, font);
}
////////////////////////////////////////////////////////////
/// Load the font from a file in memory
////////////////////////////////////////////////////////////
bool FontLoader::LoadFontFromMemory(const char* data, std::size_t sizeInBytes, unsigned int charSize, const String& charset, Font& font)
{
// Let's find how many characters to put in each row to make them fit into a squared texture
unsigned int maxSize = Image::GetMaximumSize();
int nbChars = static_cast<int>(sqrt(static_cast<double>(charset.GetSize())) * 0.75);
// Clamp the character size to make sure we won't create a texture too big
if (nbChars * charSize >= maxSize)
charSize = maxSize / nbChars;
// Initialize the dimensions
unsigned int left = 0;
unsigned int top = 0;
unsigned int texWidth = Image::GetValidSize(charSize * nbChars);
unsigned int texHeight = charSize * nbChars;
std::vector<unsigned int> tops(texWidth, 0);
// Create a pixel buffer for rendering every glyph
std::vector<Uint8> glyphsBuffer(texWidth * texHeight * 4);
// Load the font
stbtt_fontinfo info;
int success = stbtt_InitFont(&info, reinterpret_cast<const unsigned char*>(data), 0);
if (!success)
return false;
// Compute the global scale to apply to match the character size
float scale = stbtt_ScaleForPixelHeight(&info, static_cast<float>(charSize));
// Render all glyphs and sort them by size to optimize texture space
typedef std::multimap<Glyph, Uint32, SizeCompare> GlyphTable;
GlyphTable glyphs;
for (std::size_t i = 0; i < charset.GetSize(); ++i)
{
// Load the glyph corresponding to the current character
int index = stbtt_FindGlyphIndex(&info, static_cast<int>(charset[i]));
// Extract the glyph parameters (bounding box, horizontal advance)
Glyph glyph;
stbtt_GetGlyphHMetrics(&info, index, &glyph.Advance, NULL);
stbtt_GetGlyphBitmapBox(&info, index, scale, scale, &glyph.Rectangle.Left, &glyph.Rectangle.Top, &glyph.Rectangle.Right, &glyph.Rectangle.Bottom);
// Apply the global scale to the horizontal advance
glyph.Advance = static_cast<int>(glyph.Advance * scale);
// Add it to the sorted table of glyphs
glyphs.insert(std::make_pair(glyph, charset[i]));
}
// Leave a small margin around characters, so that filtering doesn't
// pollute them with pixels from neighbours
unsigned int padding = 1;
unsigned int margin = 1;
// Copy the rendered glyphs into the texture
unsigned int maxHeight = 0;
std::map<Uint32, IntRect> coords;
for (GlyphTable::const_iterator i = glyphs.begin(); i != glyphs.end(); ++i)
{
// Get the bitmap of the current glyph
Glyph& curGlyph = font.myGlyphs[i->second];
const Glyph& bitmapGlyph = i->first;
const Vector2i& glyphSize = bitmapGlyph.Rectangle.GetSize();
// Make sure we don't go over the texture width
if (left + glyphSize.x + 2 * padding + margin >= texWidth)
left = 0;
// Compute the top coordinate
top = tops[left];
for (unsigned int x = 0; x < glyphSize.x + 2 * padding + margin; ++x)
top = std::max(top, tops[left + x]);
// Make sure we don't go over the texture height -- resize it if we need more space
if (top + glyphSize.x + 2 * padding + margin >= texHeight)
{
texHeight *= 2;
glyphsBuffer.resize(texWidth * texHeight * 4);
}
// Store the character's position and size
curGlyph.Rectangle.Left = bitmapGlyph.Rectangle.Left - padding;
curGlyph.Rectangle.Top = bitmapGlyph.Rectangle.Top - padding;
curGlyph.Rectangle.Right = bitmapGlyph.Rectangle.Right + padding;
curGlyph.Rectangle.Bottom = bitmapGlyph.Rectangle.Bottom + padding;
curGlyph.Advance = bitmapGlyph.Advance;
// Texture size may change, so let the texture coordinates be calculated later
coords[i->second] = IntRect(left, top, left + glyphSize.x + 2 * padding, top + glyphSize.y + 2 * padding);
// Draw the glyph into our bitmap font
int width, height;
unsigned char* bitmap = stbtt_GetCodepointBitmap(&info, scale, scale, i->second, &width, &height, NULL, NULL);
unsigned char* pixels = bitmap;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
std::size_t index = x + left + padding + (y + top + padding) * texWidth;
glyphsBuffer[index * 4 + 0] = 255;
glyphsBuffer[index * 4 + 1] = 255;
glyphsBuffer[index * 4 + 2] = 255;
glyphsBuffer[index * 4 + 3] = pixels[x];
}
pixels += width;
}
// Update the rendering coordinates
for (unsigned int x = 0; x < width + 2 * padding + margin; ++x)
tops[left + x] = top + height + 2 * padding + margin;
left += width + 2 * padding + margin;
if (top + height + 2 * padding > maxHeight)
maxHeight = top + height + 2 * padding;
// Delete the bitmap
stbtt_FreeBitmap(bitmap, NULL);
}
// Create the font's texture
texHeight = maxHeight;
glyphsBuffer.resize(texWidth * texHeight * 4);
font.myTexture.LoadFromPixels(texWidth, texHeight, &glyphsBuffer[0]);
// Now that the texture is created, we can precompute texture coordinates
for (std::size_t i = 0; i < charset.GetSize(); ++i)
{
Uint32 curChar = charset[i];
font.myGlyphs[curChar].TexCoords = font.myTexture.GetTexCoords(coords[curChar]);
}
// Update the character size (it may have been changed by the function)
font.myCharSize = charSize;
return 0;
}
////////////////////////////////////////////////////////////
/// Create a bitmap font from a font face and a characters set
////////////////////////////////////////////////////////////
FT_Error FontLoader::CreateBitmapFont(FT_Face face, unsigned int charSize, const String& charset, Font& font)
{
return 0;
}
////////////////////////////////////////////////////////////
/// Get a description from a FT error code
////////////////////////////////////////////////////////////
std::string FontLoader::GetErrorDesc(FT_Error error)
{
return "";
}
} // namespace priv
} // namespace sf
#endif

View file

@ -1,132 +0,0 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#ifndef SFML_FONTLOADER_HPP
#define SFML_FONTLOADER_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/NonCopyable.hpp>
#include <SFML/System/String.hpp>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <string>
namespace sf
{
class Font;
namespace priv
{
////////////////////////////////////////////////////////////
/// FontLoader loads and saves character fonts
////////////////////////////////////////////////////////////
class FontLoader : NonCopyable
{
public :
////////////////////////////////////////////////////////////
/// Get the unique instance of the class
///
/// \return Reference to the FontLoader instance
///
////////////////////////////////////////////////////////////
static FontLoader& GetInstance();
////////////////////////////////////////////////////////////
/// Load a font from a file
///
/// \param filename : Path of the font file to load
/// \param charSize : Size of characters in bitmap - the bigger, the higher quality
/// \param charset : Characters set to generate
/// \param font : Font object to fill up
///
/// \return True if loading was successful
///
////////////////////////////////////////////////////////////
bool LoadFontFromFile(const std::string& filename, unsigned int charSize, const String& charset, Font& font);
////////////////////////////////////////////////////////////
/// Load the font from a file in memory
///
/// \param data : Pointer to the data to load
/// \param sizeInBytes : Size of the data, in bytes
/// \param charSize : Size of characters in bitmap - the bigger, the higher quality
/// \param charset : Characters set to generate
/// \param font : Font object to fill up
///
/// \return True if loading was successful
///
////////////////////////////////////////////////////////////
bool LoadFontFromMemory(const char* data, std::size_t sizeInBytes, unsigned int charSize, const String& charset, Font& font);
private :
////////////////////////////////////////////////////////////
/// Default constructor
///
////////////////////////////////////////////////////////////
FontLoader();
////////////////////////////////////////////////////////////
/// Destructor
///
////////////////////////////////////////////////////////////
~FontLoader();
////////////////////////////////////////////////////////////
/// Create a bitmap font from a font face and a characters set
///
/// \param face : Font face containing the loaded font
/// \param charSize : Size of characters in bitmap
/// \param charset : Characters set to generate
/// \param font : Font object to fill up
///
////////////////////////////////////////////////////////////
FT_Error CreateBitmapFont(FT_Face face, unsigned int charSize, const String& charset, Font& font);
////////////////////////////////////////////////////////////
/// Get a description from a FT error code
///
/// \param error : FreeType error code
///
/// \return Error description
///
////////////////////////////////////////////////////////////
static std::string GetErrorDesc(FT_Error error);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
FT_Library myLibrary; ///< Handle to the Freetype library
};
} // namespace priv
} // namespace sf
#endif // SFML_FONTLOADER_HPP

View file

@ -28,7 +28,6 @@
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <iostream>

View file

@ -37,7 +37,7 @@ namespace sf
////////////////////////////////////////////////////////////
Text::Text() :
myFont (&Font::GetDefaultFont()),
mySize (30.f),
myCharacterSize (30),
myStyle (Regular),
myNeedRectUpdate(true)
{
@ -48,9 +48,9 @@ myNeedRectUpdate(true)
////////////////////////////////////////////////////////////
/// Construct the string from any kind of text
////////////////////////////////////////////////////////////
Text::Text(const String& string, const Font& font, float size) :
Text::Text(const String& string, const Font& font, unsigned int characterSize) :
myFont (&font),
mySize (size),
myCharacterSize (characterSize),
myStyle (Regular),
myNeedRectUpdate(true)
{
@ -82,14 +82,14 @@ void Text::SetFont(const Font& font)
////////////////////////////////////////////////////////////
/// Set the size of the string
/// Set the base size for the characters.
////////////////////////////////////////////////////////////
void Text::SetSize(float size)
void Text::SetCharacterSize(unsigned int size)
{
if (mySize != size)
if (myCharacterSize != size)
{
myNeedRectUpdate = true;
mySize = size;
myCharacterSize = size;
}
}
@ -127,11 +127,11 @@ const Font& Text::GetFont() const
////////////////////////////////////////////////////////////
/// Get the size of the characters
/// Get the base size of characters
////////////////////////////////////////////////////////////
float Text::GetSize() const
unsigned int Text::GetCharacterSize() const
{
return mySize;
return myCharacterSize;
}
@ -149,36 +149,42 @@ unsigned long Text::GetStyle() const
/// in coordinates relative to the string
/// (note : translation, center, rotation and scale are not applied)
////////////////////////////////////////////////////////////
sf::Vector2f Text::GetCharacterPos(std::size_t index) const
Vector2f Text::GetCharacterPos(std::size_t index) const
{
// Make sure that we have a valid font
if (!myFont)
return Vector2f(0, 0);
// Adjust the index if it's out of range
if (index > myString.GetSize())
index = myString.GetSize();
// The final size is based on the text size
float factor = mySize / myFont->GetCharacterSize();
float advanceY = mySize;
// We'll need this a lot
float space = static_cast<float>(myFont->GetGlyph(L' ', myCharacterSize).Advance);
// Compute the position
sf::Vector2f position;
Uint32 prevChar = 0;
float lineSpacing = static_cast<float>(myFont->GetLineSpacing(myCharacterSize));
for (std::size_t i = 0; i < index; ++i)
{
// Get the current character and its corresponding glyph
Uint32 curChar = myString[i];
const Glyph& curGlyph = myFont->GetGlyph(curChar);
float advanceX = curGlyph.Advance * factor;
Uint32 curChar = myString[i];
// Apply the kerning offset
position.x += static_cast<float>(myFont->GetKerning(prevChar, curChar, myCharacterSize));
prevChar = curChar;
// Handle special characters
switch (curChar)
{
// Handle special characters
case L' ' : position.x += advanceX; break;
case L'\t' : position.x += advanceX * 4; break;
case L'\v' : position.y += advanceY * 4; break;
case L'\n' : position.y += advanceY; position.x = 0; break;
// Regular character : just add its advance value
default : position.x += advanceX; break;
case L' ' : position.x += space; continue;
case L'\t' : position.x += space * 4; continue;
case L'\v' : position.y += lineSpacing * 4; continue;
case L'\n' : position.y += lineSpacing; position.x = 0; continue;
}
// For regular characters, add the advance offset of the glyph
position.x += static_cast<float>(myFont->GetGlyph(curChar, myCharacterSize).Advance);
}
return position;
@ -204,24 +210,22 @@ FloatRect Text::GetRect() const
////////////////////////////////////////////////////////////
/// /see sfDrawable::Render
/// /see Drawable::Render
////////////////////////////////////////////////////////////
void Text::Render(RenderTarget&, RenderQueue& queue) const
{
// No text, no rendering :)
if (myString.IsEmpty())
// No text or not font: nothing to render
if (!myFont || myString.IsEmpty())
return;
// Set the scaling factor to get the actual size
float charSize = static_cast<float>(myFont->GetCharacterSize());
float factor = mySize / charSize;
// Bind the font texture
queue.SetTexture(&myFont->GetImage());
queue.SetTexture(&myFont->GetImage(myCharacterSize));
// Initialize the rendering coordinates
float space = static_cast<float>(myFont->GetGlyph(L' ', myCharacterSize).Advance);
float lineSpacing = static_cast<float>(myFont->GetLineSpacing(myCharacterSize));
float x = 0.f;
float y = charSize;
float y = static_cast<float>(myCharacterSize);
// Holds the lines to draw later, for underlined style
std::vector<float> underlineCoords;
@ -232,15 +236,15 @@ void Text::Render(RenderTarget&, RenderQueue& queue) const
// Draw one quad for each character
unsigned int index = 0;
Uint32 prevChar = 0;
queue.BeginBatch();
for (std::size_t i = 0; i < myString.GetSize(); ++i)
{
// Get the current character and its corresponding glyph
Uint32 curChar = myString[i];
const Glyph& curGlyph = myFont->GetGlyph(curChar);
int advance = curGlyph.Advance;
const IntRect& rect = curGlyph.Rectangle;
const FloatRect& coord = curGlyph.TexCoords;
Uint32 curChar = myString[i];
// Apply the kerning offset
x += static_cast<float>(myFont->GetKerning(prevChar, curChar, myCharacterSize));
prevChar = curChar;
// If we're using the underlined style and there's a new line,
// we keep track of the previous line to draw it later
@ -253,17 +257,23 @@ void Text::Render(RenderTarget&, RenderQueue& queue) const
// Handle special characters
switch (curChar)
{
case L' ' : x += advance; continue;
case L'\n' : y += charSize; x = 0; continue;
case L'\t' : x += advance * 4; continue;
case L'\v' : y += charSize * 4; continue;
case L' ' : x += space; continue;
case L'\t' : x += space * 4; continue;
case L'\n' : y += lineSpacing; x = 0; continue;
case L'\v' : y += lineSpacing * 4; continue;
}
// Extract the current glyph's description
const Glyph& curGlyph = myFont->GetGlyph(curChar, myCharacterSize);
int advance = curGlyph.Advance;
const IntRect& rect = curGlyph.Rectangle;
const FloatRect& coord = curGlyph.TexCoords;
// Draw a textured quad for the current character
queue.AddVertex(factor * (x + rect.Left - italicCoeff * rect.Top), factor * (y + rect.Top), coord.Left, coord.Top);
queue.AddVertex(factor * (x + rect.Left - italicCoeff * rect.Bottom), factor * (y + rect.Bottom), coord.Left, coord.Bottom);
queue.AddVertex(factor * (x + rect.Right - italicCoeff * rect.Bottom), factor * (y + rect.Bottom), coord.Right, coord.Bottom);
queue.AddVertex(factor * (x + rect.Right - italicCoeff * rect.Top), factor * (y + rect.Top), coord.Right, coord.Top);
queue.AddVertex(x + rect.Left - italicCoeff * rect.Top, y + rect.Top, coord.Left, coord.Top);
queue.AddVertex(x + rect.Left - italicCoeff * rect.Bottom, y + rect.Bottom, coord.Left, coord.Bottom);
queue.AddVertex(x + rect.Right - italicCoeff * rect.Bottom, y + rect.Bottom, coord.Right, coord.Bottom);
queue.AddVertex(x + rect.Right - italicCoeff * rect.Top, y + rect.Top, coord.Right, coord.Top);
queue.AddTriangle(index + 0, index + 1, index + 3);
queue.AddTriangle(index + 3, index + 1, index + 2);
@ -277,7 +287,7 @@ void Text::Render(RenderTarget&, RenderQueue& queue) const
// slightly offseted, to simulate a higher weight
if (myStyle & Bold)
{
float offset = mySize * 0.02f;
float offset = myCharacterSize * 0.02f;
static const float offsetsX[] = {-offset, offset, 0.f, 0.f};
static const float offsetsY[] = {0.f, 0.f, -offset, offset};
@ -285,32 +295,38 @@ void Text::Render(RenderTarget&, RenderQueue& queue) const
{
index = 0;
x = 0.f;
y = charSize;
y = static_cast<float>(myCharacterSize);
Uint32 prevChar = 0;
queue.BeginBatch();
for (std::size_t i = 0; i < myString.GetSize(); ++i)
{
// Get the current character and its corresponding glyph
Uint32 curChar = myString[i];
const Glyph& curGlyph = myFont->GetGlyph(curChar);
int advance = curGlyph.Advance;
const IntRect& rect = curGlyph.Rectangle;
const FloatRect& coord = curGlyph.TexCoords;
Uint32 curChar = myString[i];
// Apply the kerning offset
x += static_cast<float>(myFont->GetKerning(prevChar, curChar, myCharacterSize));
prevChar = curChar;
// Handle special characters
switch (curChar)
{
case L' ' : x += advance; continue;
case L'\n' : y += charSize; x = 0; continue;
case L'\t' : x += advance * 4; continue;
case L'\v' : y += charSize * 4; continue;
case L' ' : x += space; continue;
case L'\t' : x += space * 4; continue;
case L'\n' : y += lineSpacing; x = 0; continue;
case L'\v' : y += lineSpacing * 4; continue;
}
// Extract the current glyph's description
const Glyph& curGlyph = myFont->GetGlyph(curChar, myCharacterSize);
int advance = curGlyph.Advance;
const IntRect& rect = curGlyph.Rectangle;
const FloatRect& coord = curGlyph.TexCoords;
// Draw a textured quad for the current character
queue.AddVertex(factor * (x + offsetsX[j] + rect.Left - italicCoeff * rect.Top), factor * (y + offsetsY[j] + rect.Top), coord.Left, coord.Top);
queue.AddVertex(factor * (x + offsetsX[j] + rect.Left - italicCoeff * rect.Bottom), factor * (y + offsetsY[j] + rect.Bottom), coord.Left, coord.Bottom);
queue.AddVertex(factor * (x + offsetsX[j] + rect.Right - italicCoeff * rect.Bottom), factor * (y + offsetsY[j] + rect.Bottom), coord.Right, coord.Bottom);
queue.AddVertex(factor * (x + offsetsX[j] + rect.Right - italicCoeff * rect.Top), factor * (y + offsetsY[j] + rect.Top), coord.Right, coord.Top);
queue.AddVertex(x + offsetsX[j] + rect.Left - italicCoeff * rect.Top, y + offsetsY[j] + rect.Top, coord.Left, coord.Top);
queue.AddVertex(x + offsetsX[j] + rect.Left - italicCoeff * rect.Bottom, y + offsetsY[j] + rect.Bottom, coord.Left, coord.Bottom);
queue.AddVertex(x + offsetsX[j] + rect.Right - italicCoeff * rect.Bottom, y + offsetsY[j] + rect.Bottom, coord.Right, coord.Bottom);
queue.AddVertex(x + offsetsX[j] + rect.Right - italicCoeff * rect.Top, y + offsetsY[j] + rect.Top, coord.Right, coord.Top);
queue.AddTriangle(index + 0, index + 1, index + 3);
queue.AddTriangle(index + 3, index + 1, index + 2);
@ -338,10 +354,10 @@ void Text::Render(RenderTarget&, RenderQueue& queue) const
queue.BeginBatch();
for (std::size_t i = 0; i < underlineCoords.size(); i += 2)
{
queue.AddVertex(factor * (0), factor * (underlineCoords[i + 1]));
queue.AddVertex(factor * (0), factor * (underlineCoords[i + 1] + thickness));
queue.AddVertex(factor * (underlineCoords[i]), factor * (underlineCoords[i + 1] + thickness));
queue.AddVertex(factor * (underlineCoords[i]), factor * (underlineCoords[i + 1]));
queue.AddVertex(0, underlineCoords[i + 1]);
queue.AddVertex(0, underlineCoords[i + 1] + thickness);
queue.AddVertex(underlineCoords[i], underlineCoords[i + 1] + thickness);
queue.AddVertex(underlineCoords[i], underlineCoords[i + 1]);
queue.AddTriangle(index + 0, index + 1, index + 3);
queue.AddTriangle(index + 3, index + 1, index + 2);
@ -356,41 +372,51 @@ void Text::Render(RenderTarget&, RenderQueue& queue) const
////////////////////////////////////////////////////////////
void Text::RecomputeRect()
{
// Reset the "need update" state
// Reset the previous states
myNeedRectUpdate = false;
myBaseRect = FloatRect(0, 0, 0, 0);
// No text, empty box :)
if (myString.IsEmpty())
{
myBaseRect = FloatRect(0, 0, 0, 0);
// No text or not font: empty box
if (!myFont || myString.IsEmpty())
return;
}
// Initial values
float curWidth = 0;
float curHeight = 0;
float width = 0;
float height = 0;
float factor = mySize / myFont->GetCharacterSize();
float charSize = static_cast<float>(myCharacterSize);
float space = static_cast<float>(myFont->GetGlyph(L' ', myCharacterSize).Advance);
float lineSpacing = static_cast<float>(myFont->GetLineSpacing(myCharacterSize));
float curWidth = 0;
float curHeight = 0;
float width = 0;
float height = 0;
Uint32 prevChar = 0;
// Go through each character
for (std::size_t i = 0; i < myString.GetSize(); ++i)
{
// Get the current character and its corresponding glyph
Uint32 curChar = myString[i];
const Glyph& curGlyph = myFont->GetGlyph(curChar);
float advance = curGlyph.Advance * factor;
const IntRect& rect = curGlyph.Rectangle;
Uint32 curChar = myString[i];
// Apply the kerning offset
curWidth += static_cast<float>(myFont->GetKerning(prevChar, curChar, myCharacterSize));
prevChar = curChar;
// Handle special characters
switch (curChar)
{
case L' ' : curWidth += advance; continue;
case L'\t' : curWidth += advance * 4; continue;
case L'\v' : height += mySize * 4; curHeight = 0; continue;
case L' ' :
curWidth += space;
continue;
case L'\t' :
curWidth += space * 4;
continue;
case L'\v' :
height += lineSpacing * 4;
curHeight = 0;
continue;
case L'\n' :
height += mySize;
height += lineSpacing;
curHeight = 0;
if (curWidth > width)
width = curWidth;
@ -398,11 +424,14 @@ void Text::RecomputeRect()
continue;
}
// Extract the current glyph's description
const Glyph& curGlyph = myFont->GetGlyph(curChar, myCharacterSize);
// Advance to the next character
curWidth += advance;
curWidth += static_cast<float>(curGlyph.Advance);
// Update the maximum height
float charHeight = (myFont->GetCharacterSize() + rect.Bottom) * factor;
float charHeight = charSize + curGlyph.Rectangle.Bottom;
if (charHeight > curHeight)
curHeight = charHeight;
}
@ -415,21 +444,21 @@ void Text::RecomputeRect()
// Add a slight width / height if we're using the bold style
if (myStyle & Bold)
{
width += 1 * factor;
height += 1 * factor;
width += 1.f;
height += 1.f;
}
// Add a slight width if we're using the italic style
if (myStyle & Italic)
{
width += 0.208f * mySize;
width += 0.208f * charSize;
}
// Add a slight height if we're using the underlined style
if (myStyle & Underlined)
{
if (curHeight < mySize + 4 * factor)
height += 4 * factor;
if (curHeight < charSize + 4.f)
height += 4.f;
}
// Finally update the rectangle

File diff suppressed because it is too large Load diff

View file

@ -244,10 +244,6 @@ Socket::Status SocketUDP::Send(Packet& packet, const IPAddress& address, unsigne
////////////////////////////////////////////////////////////
Socket::Status SocketUDP::Receive(Packet& packet, IPAddress& address, unsigned short& port)
{
// This is not safe at all, as data can be lost, duplicated, or arrive in a different order.
// So if a packet is split into more than one chunk, nobody knows what could happen...
// Conclusion : we shouldn't use packets with UDP, unless we build a more complex protocol on top of it.
// We start by getting the size of the incoming packet
Uint32 packetSize = 0;
std::size_t received = 0;