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:
parent
839c80556d
commit
3a34f81561
32 changed files with 1016 additions and 3016 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue