Refactor FaceTTF for later better usage

This commit is contained in:
Benau 2019-05-30 10:34:47 +08:00
parent dd9d5c89e1
commit 867cecc01e
14 changed files with 199 additions and 165 deletions

View File

@ -1,5 +1,5 @@
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
# This will then trigger a new cmake run automatically.
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")

View File

@ -22,7 +22,7 @@
/** Constructor of BoldFace.
* \param ttf \ref FaceTTF for BoldFace to use.
*/
BoldFace::BoldFace(FaceTTF* ttf) : FontWithFace("BoldFace", ttf)
BoldFace::BoldFace() : FontWithFace("BoldFace")
{
} // BoldFace

View File

@ -45,7 +45,7 @@ private:
public:
LEAK_CHECK()
// ------------------------------------------------------------------------
BoldFace(FaceTTF* ttf);
BoldFace();
// ------------------------------------------------------------------------
virtual void init() OVERRIDE;
// ------------------------------------------------------------------------

View File

@ -22,7 +22,7 @@
/** Constructor of DigitFace.
* \param ttf \ref FaceTTF for DigitFace to use.
*/
DigitFace::DigitFace(FaceTTF* ttf) : FontWithFace("DigitFace", ttf)
DigitFace::DigitFace() : FontWithFace("DigitFace")
{
} // DigitFace

View File

@ -40,7 +40,7 @@ private:
public:
LEAK_CHECK()
// ------------------------------------------------------------------------
DigitFace(FaceTTF* ttf);
DigitFace();
// ------------------------------------------------------------------------
virtual void init() OVERRIDE;
// ------------------------------------------------------------------------

View File

@ -1,61 +0,0 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "font/face_ttf.hpp"
#include "font/font_manager.hpp"
#include "io/file_manager.hpp"
#include "modes/profile_world.hpp"
// ----------------------------------------------------------------------------
/** Constructor. Load all TTFs from a list.
* \param ttf_list List of TTFs to be loaded.
*/
FaceTTF::FaceTTF(const std::vector<std::string>& ttf_list)
{
#ifndef SERVER_ONLY
if (ProfileWorld::isNoGraphics())
return;
for (const std::string& font : ttf_list)
{
FT_Face face = NULL;
const std::string loc = file_manager
->getAssetChecked(FileManager::TTF, font.c_str(), true);
font_manager->checkFTError(FT_New_Face(font_manager->getFTLibrary(),
loc.c_str(), 0, &face), loc + " is loaded");
m_faces.push_back(face);
}
#endif
} // FaceTTF
// ----------------------------------------------------------------------------
/** Destructor. Clears all TTFs.
*/
FaceTTF::~FaceTTF()
{
#ifndef SERVER_ONLY
if (ProfileWorld::isNoGraphics())
return;
for (unsigned int i = 0; i < m_faces.size(); i++)
{
font_manager->checkFTError(FT_Done_Face(m_faces[i]), "removing face");
}
#endif
} // ~FaceTTF

View File

@ -23,7 +23,9 @@
#include "utils/no_copy.hpp"
#include <cassert>
#include <map>
#include <string>
#include <utility>
#include <vector>
#ifndef SERVER_ONLY
@ -31,27 +33,71 @@
#include FT_FREETYPE_H
#endif
/** This class will load a list of TTF files from \ref STKConfig, and save
/** Glyph metrics for each glyph loaded. */
struct FontArea
{
FontArea() : advance_x(0), bearing_x(0), offset_y(0), offset_y_bt(0),
spriteno(0) {}
/** Advance width for horizontal layout. */
int advance_x;
/** Left side bearing for horizontal layout. */
int bearing_x;
/** Top side bearing for horizontal layout. */
int offset_y;
/** Top side bearing for horizontal layout used in billboard text. */
int offset_y_bt;
/** Index number in sprite bank of \ref FontWithFace. */
int spriteno;
};
/** This class will load a list of TTF files from \ref FontManager, and save
* them inside \ref m_faces for \ref FontWithFace to load glyph.
* Each FaceTTF can be used more than once in each instantiation of \ref
* FontWithFace, so it can render characters differently using the same TTF
* file to save memory, for example different outline size.
* \ingroup font
*/
class FaceTTF : public NoCopy
{
#ifndef SERVER_ONLY
private:
/** Contains all TTF files loaded. */
std::vector<FT_Face> m_faces;
/** Contains all FT_Face with a list of loaded glyph index with the
* \ref FontArea. */
std::vector<std::pair<FT_Face, std::map<unsigned, FontArea> > > m_faces;
#endif
public:
LEAK_CHECK()
// ------------------------------------------------------------------------
FaceTTF(const std::vector<std::string>& ttf_list);
FaceTTF() {}
// ------------------------------------------------------------------------
~FaceTTF();
~FaceTTF() {}
// ------------------------------------------------------------------------
void reset()
{
#ifndef SERVER_ONLY
for (unsigned int i = 0; i < m_faces.size(); i++)
m_faces[i].second.clear();
#endif
}
// ------------------------------------------------------------------------
/* Return a white-space font area, which is the first glyph in ttf. */
const FontArea* getFirstFontArea() const
{
#ifdef SERVER_ONLY
static FontArea area;
return &area;
#else
if (m_faces.empty())
return NULL;
if (m_faces.front().second.empty())
return NULL;
return &(m_faces.front().second.begin()->second);
#endif
}
#ifndef SERVER_ONLY
// ------------------------------------------------------------------------
void loadFaces(std::vector<FT_Face> faces)
{
for (unsigned int i = 0; i < faces.size(); i++)
m_faces.emplace_back(faces[i], std::map<unsigned, FontArea>());
}
// ------------------------------------------------------------------------
/** Return a TTF in \ref m_faces.
* \param i index of TTF file in \ref m_faces.
@ -59,11 +105,42 @@ public:
FT_Face getFace(unsigned int i) const
{
assert(i < m_faces.size());
return m_faces[i];
return m_faces[i].first;
}
// ------------------------------------------------------------------------
/** Return the total TTF files loaded. */
unsigned int getTotalFaces() const { return (unsigned int)m_faces.size(); }
// ------------------------------------------------------------------------
void insertFontArea(const FontArea& a, unsigned font_index,
unsigned glyph_index)
{
auto& ttf = m_faces.at(font_index).second;
ttf[glyph_index] = a;
}
// ------------------------------------------------------------------------
const FontArea* getFontArea(unsigned font_index, unsigned glyph_index)
{
auto& ttf = m_faces.at(font_index).second;
auto it = ttf.find(glyph_index);
if (it != ttf.end())
return &it->second;
return NULL;
}
// ------------------------------------------------------------------------
bool getFontAndGlyphFromChar(uint32_t c, unsigned* font, unsigned* glyph)
{
for (unsigned i = 0; i < m_faces.size(); i++)
{
unsigned glyph_index = FT_Get_Char_Index(m_faces[i].first, c);
if (glyph_index > 0)
{
*font = i;
*glyph = glyph_index;
return true;
}
}
return false;
}
#endif
}; // FaceTTF

View File

@ -19,6 +19,7 @@
#include "font/font_manager.hpp"
#include "config/stk_config.hpp"
#include "io/file_manager.hpp"
#include "font/bold_face.hpp"
#include "font/digit_face.hpp"
#include "font/face_ttf.hpp"
@ -51,44 +52,82 @@ FontManager::~FontManager()
delete m_fonts[i];
m_fonts.clear();
delete m_normal_ttf;
m_normal_ttf = NULL;
delete m_digit_ttf;
m_digit_ttf = NULL;
#ifndef SERVER_ONLY
if (ProfileWorld::isNoGraphics())
return;
for (unsigned int i = 0; i < m_faces.size(); i++)
checkFTError(FT_Done_Face(m_faces[i]), "removing faces");
checkFTError(FT_Done_FreeType(m_ft_library), "removing freetype library");
#endif
} // ~FontManager
#ifndef SERVER_ONLY
// ----------------------------------------------------------------------------
/** Load all TTFs from a list to m_faces.
* \param ttf_list List of TTFs to be loaded.
*/
std::vector<FT_Face>
FontManager::loadTTF(const std::vector<std::string>& ttf_list)
{
std::vector <FT_Face> ret;
if (ProfileWorld::isNoGraphics())
return ret;
for (const std::string& font : ttf_list)
{
FT_Face face = NULL;
const std::string loc = file_manager
->getAssetChecked(FileManager::TTF, font.c_str(), true);
font_manager->checkFTError(FT_New_Face(
m_ft_library, loc.c_str(), 0, &face), loc + " is loaded");
ret.push_back(face);
}
return ret;
} // loadTTF
#endif
// ----------------------------------------------------------------------------
/** Initialize all \ref FaceTTF and \ref FontWithFace members.
*/
void FontManager::loadFonts()
{
#ifndef SERVER_ONLY
// First load the TTF files required by each font
m_normal_ttf = new FaceTTF(stk_config->m_normal_ttf);
m_digit_ttf = new FaceTTF(stk_config->m_digit_ttf);
std::vector<FT_Face> normal_ttf = loadTTF(stk_config->m_normal_ttf);
std::vector<FT_Face> digit_ttf = loadTTF(stk_config->m_digit_ttf);
#endif
// Now load fonts with settings of ttf file
unsigned int font_loaded = 0;
RegularFace* regular = new RegularFace(m_normal_ttf);
RegularFace* regular = new RegularFace();
#ifndef SERVER_ONLY
regular->getFaceTTF()->loadFaces(normal_ttf);
#endif
regular->init();
m_fonts.push_back(regular);
m_font_type_map[std::type_index(typeid(RegularFace))] = font_loaded++;
BoldFace* bold = new BoldFace(m_normal_ttf);
BoldFace* bold = new BoldFace();
#ifndef SERVER_ONLY
bold->getFaceTTF()->loadFaces(normal_ttf);
#endif
bold->init();
m_fonts.push_back(bold);
m_font_type_map[std::type_index(typeid(BoldFace))] = font_loaded++;
DigitFace* digit = new DigitFace(m_digit_ttf);
DigitFace* digit = new DigitFace();
#ifndef SERVER_ONLY
digit->getFaceTTF()->loadFaces(digit_ttf);
#endif
digit->init();
m_fonts.push_back(digit);
m_font_type_map[std::type_index(typeid(DigitFace))] = font_loaded++;
#ifndef SERVER_ONLY
m_faces.insert(m_faces.end(), normal_ttf.begin(), normal_ttf.end());
m_faces.insert(m_faces.end(), digit_ttf.begin(), digit_ttf.end());
#endif
} // loadFonts
// ----------------------------------------------------------------------------
@ -115,6 +154,8 @@ void FontManager::unitTesting()
translations = new Translations();
Log::setLogLevel(cur_log_level);
std::set<wchar_t> used_chars = translations->getCurrentAllChar();
// First FontWithFace is RegularFace
FaceTTF* ttf = m_fonts.front()->getFaceTTF();
for (const wchar_t& c : used_chars)
{
// Skip non-printing characters
@ -122,20 +163,13 @@ void FontManager::unitTesting()
unsigned int font_number = 0;
unsigned int glyph_index = 0;
while (font_number < m_normal_ttf->getTotalFaces())
{
glyph_index =
FT_Get_Char_Index(m_normal_ttf->getFace(font_number), c);
if (glyph_index > 0) break;
font_number++;
}
if (glyph_index > 0)
if (ttf->getFontAndGlyphFromChar(c, &font_number, &glyph_index))
{
Log::debug("UnitTest", "Character %s in language %s"
" use face %s",
StringUtils::wideToUtf8(core::stringw(&c, 1)).c_str(),
lang.c_str(),
m_normal_ttf->getFace(font_number)->family_name);
ttf->getFace(font_number)->family_name);
}
else
{

View File

@ -37,7 +37,6 @@
#include FT_FREETYPE_H
#endif
class FaceTTF;
class FontWithFace;
/** This class stores all font files required in STK.
@ -52,14 +51,11 @@ private:
#ifndef SERVER_ONLY
/** A FreeType library, it holds the FT_Face internally inside freetype. */
FT_Library m_ft_library;
/** List of ttf files loaded. */
std::vector<FT_Face> m_faces;
#endif
/** TTF files used in \ref BoldFace and \ref RegularFace. */
FaceTTF* m_normal_ttf;
/** TTF files used in \ref DigitFace. */
FaceTTF* m_digit_ttf;
/** Map type for each \ref FontWithFace with a index, save getting time in
* \ref getFont. */
std::unordered_map<std::type_index, int> m_font_type_map;
@ -101,6 +97,8 @@ public:
// ------------------------------------------------------------------------
/** Return the \ref m_ft_library. */
FT_Library getFTLibrary() const { return m_ft_library; }
// ------------------------------------------------------------------------
std::vector<FT_Face> loadTTF(const std::vector<std::string>& ttf_list);
#endif
// ------------------------------------------------------------------------
void loadFonts();

View File

@ -39,7 +39,7 @@
* \param name The name of face, used by irrlicht to distinguish spritebank.
* \param ttf \ref FaceTTF for this face to use.
*/
FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf)
FontWithFace::FontWithFace(const std::string& name)
{
m_spritebank = irr_driver->getGUI()->addEmptySpriteBank(name.c_str());
@ -49,7 +49,7 @@ FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf)
m_fallback_font = NULL;
m_fallback_font_scale = 1.0f;
m_glyph_max_height = 0;
m_face_ttf = ttf;
m_face_ttf = new FaceTTF();
} // FontWithFace
// ----------------------------------------------------------------------------
@ -65,9 +65,7 @@ FontWithFace::~FontWithFace()
m_spritebank->drop();
m_spritebank = NULL;
// To be deleted by font_manager
m_face_ttf = NULL;
delete m_face_ttf;
} // ~FontWithFace
// ----------------------------------------------------------------------------
@ -112,7 +110,6 @@ void FontWithFace::init()
void FontWithFace::reset()
{
m_new_char_holder.clear();
m_character_area_map.clear();
m_character_glyph_info_map.clear();
for (unsigned int i = 0; i < m_spritebank->getTextureCount(); i++)
{
@ -120,6 +117,7 @@ void FontWithFace::reset()
static_cast<STKTexture*>(m_spritebank->getTexture(i)));
}
m_spritebank->clear();
m_face_ttf->reset();
createNewGlyphPage();
} // reset
@ -138,12 +136,7 @@ void FontWithFace::loadGlyphInfo(wchar_t c)
unsigned int font_number = 0;
unsigned int glyph_index = 0;
while (font_number < m_face_ttf->getTotalFaces())
{
glyph_index = FT_Get_Char_Index(m_face_ttf->getFace(font_number), c);
if (glyph_index > 0) break;
font_number++;
}
m_face_ttf->getFontAndGlyphFromChar(c, &font_number, &glyph_index);
m_character_glyph_info_map[c] = GlyphInfo(font_number, glyph_index);
#endif
} // loadGlyphInfo
@ -179,10 +172,9 @@ void FontWithFace::createNewGlyphPage()
// ----------------------------------------------------------------------------
/** Render a glyph for a character into bitmap and save it into the glyph page.
* \param c The character to be loaded.
* \param c \ref GlyphInfo for the character.
*/
void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
void FontWithFace::insertGlyph(const GlyphInfo& gi)
{
#ifndef SERVER_ONLY
if (ProfileWorld::isNoGraphics())
@ -280,7 +272,7 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
a.offset_y = m_glyph_max_height - cur_height + cur_offset_y;
a.offset_y_bt = -cur_offset_y;
a.spriteno = f.rectNumber;
m_character_area_map[c] = a;
m_face_ttf->insertFontArea(a, gi.font_number, gi.glyph_index);
// Store used area
m_used_width += texture_size.Width;
@ -301,7 +293,7 @@ void FontWithFace::updateCharactersList()
for (const wchar_t& c : m_new_char_holder)
{
const GlyphInfo& gi = getGlyphInfo(c);
insertGlyph(c, gi);
insertGlyph(gi);
}
m_new_char_holder.clear();
@ -373,17 +365,23 @@ void FontWithFace::setDPI()
* \param c The character to get.
* \param[out] fallback_font Whether fallback font is used.
*/
const FontWithFace::FontArea&
FontWithFace::getAreaFromCharacter(const wchar_t c,
bool* fallback_font) const
const FontArea& FontWithFace::getAreaFromCharacter(const wchar_t c,
bool* fallback_font) const
{
std::map<wchar_t, FontArea>::const_iterator n =
m_character_area_map.find(c);
if (n != m_character_area_map.end())
std::map<wchar_t, GlyphInfo>::const_iterator n =
m_character_glyph_info_map.find(c);
// Not found, return the first font area, which is a white-space
if (n == m_character_glyph_info_map.end())
return *(m_face_ttf->getFirstFontArea());
#ifndef SERVER_ONLY
const FontArea* area = m_face_ttf->getFontArea(n->second.font_number,
n->second.glyph_index);
if (area != NULL)
{
if (fallback_font != NULL)
*fallback_font = false;
return n->second;
return *area;
}
else if (m_fallback_font != NULL && fallback_font != NULL)
{
@ -394,8 +392,9 @@ const FontWithFace::FontArea&
// Not found, return the first font area, which is a white-space
if (fallback_font != NULL)
*fallback_font = false;
return m_character_area_map.begin()->second;
#endif
return *(m_face_ttf->getFirstFontArea());
} // getAreaFromCharacter
// ----------------------------------------------------------------------------
@ -419,7 +418,6 @@ core::dimension2d<u32> FontWithFace::getDimension(const wchar_t* text,
insertCharacters(text);
updateCharactersList();
assert(m_character_area_map.size() > 0);
core::dimension2d<float> dim(0.0f, 0.0f);
core::dimension2d<float> this_line(0.0f, m_font_max_height * scale);
@ -465,6 +463,7 @@ core::dimension2d<u32> FontWithFace::getDimension(const wchar_t* text,
int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x,
FontSettings* font_settings) const
{
#ifndef SERVER_ONLY
const float scale = font_settings ? font_settings->getScale() : 1.0f;
float x = 0;
int idx = 0;
@ -482,7 +481,7 @@ int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x,
++idx;
}
#endif
return -1;
} // getCharacterFromPos
@ -759,3 +758,18 @@ void FontWithFace::render(const core::stringw& text,
}
#endif
} // render
// ----------------------------------------------------------------------------
/** Return a character width.
* \param area \ref FontArea to get glyph metrics.
* \param fallback If fallback font is used.
* \param scale The scaling of the character.
* \return The calculated width with suitable scaling. */
float FontWithFace::getCharWidth(const FontArea& area, bool fallback,
float scale) const
{
if (fallback)
return area.advance_x * m_fallback_font_scale;
else
return area.advance_x * scale;
} // getCharWidth

View File

@ -43,6 +43,7 @@ const int BEARING = 64;
class FaceTTF;
class FontSettings;
struct FontArea;
/** An abstract class which contains functions which convert vector fonts into
* bitmap and render them in STK. To make STK draw characters with different
@ -70,23 +71,6 @@ public:
const video::SColor* const colors) = 0;
};
/** Glyph metrics for each glyph loaded. */
struct FontArea
{
FontArea() : advance_x(0), bearing_x(0) ,offset_y(0), offset_y_bt(0),
spriteno(0) {}
/** Advance width for horizontal layout. */
int advance_x;
/** Left side bearing for horizontal layout. */
int bearing_x;
/** Top side bearing for horizontal layout. */
int offset_y;
/** Top side bearing for horizontal layout used in billboard text. */
int offset_y_bt;
/** Index number in sprite bank. */
int spriteno;
};
protected:
/** Used in vertical dimension calculation. */
int m_font_max_height;
@ -176,25 +160,11 @@ private:
/** The dpi of this font. */
unsigned int m_face_dpi;
/** Store a list of supported character to a \ref FontArea. */
std::map<wchar_t, FontArea> m_character_area_map;
/** Store a list of loaded and tested character to a \ref GlyphInfo. */
std::map<wchar_t, GlyphInfo> m_character_glyph_info_map;
// ------------------------------------------------------------------------
/** Return a character width.
* \param area \ref FontArea to get glyph metrics.
* \param fallback If fallback font is used.
* \param scale The scaling of the character.
* \return The calculated width with suitable scaling. */
float getCharWidth(const FontArea& area, bool fallback, float scale) const
{
if (fallback)
return area.advance_x * m_fallback_font_scale;
else
return area.advance_x * scale;
}
float getCharWidth(const FontArea& area, bool fallback, float scale) const;
// ------------------------------------------------------------------------
/** Test if a character has already been tried to be loaded.
* \param c Character to test.
@ -243,7 +213,7 @@ private:
/** Add a character into \ref m_new_char_holder for lazy loading later. */
void addLazyLoadChar(wchar_t c) { m_new_char_holder.insert(c); }
// ------------------------------------------------------------------------
void insertGlyph(wchar_t c, const GlyphInfo& gi);
void insertGlyph(const GlyphInfo& gi);
// ------------------------------------------------------------------------
void setDPI();
// ------------------------------------------------------------------------
@ -273,7 +243,7 @@ private:
public:
LEAK_CHECK()
// ------------------------------------------------------------------------
FontWithFace(const std::string& name, FaceTTF* ttf);
FontWithFace(const std::string& name);
// ------------------------------------------------------------------------
virtual ~FontWithFace();
// ------------------------------------------------------------------------
@ -305,7 +275,8 @@ public:
// ------------------------------------------------------------------------
/** Return the dpi of this face. */
unsigned int getDPI() const { return m_face_dpi; }
// ------------------------------------------------------------------------
FaceTTF* getFaceTTF() const { return m_face_ttf; }
}; // FontWithFace
#endif

View File

@ -22,7 +22,7 @@
/** Constructor of RegularFace.
* \param ttf \ref FaceTTF for RegularFace to use.
*/
RegularFace::RegularFace(FaceTTF* ttf) : FontWithFace("RegularFace", ttf)
RegularFace::RegularFace() : FontWithFace("RegularFace")
{
} // RegularFace

View File

@ -38,7 +38,7 @@ private:
public:
LEAK_CHECK()
// ------------------------------------------------------------------------
RegularFace(FaceTTF* ttf);
RegularFace();
// ------------------------------------------------------------------------
virtual void init() OVERRIDE;
// ------------------------------------------------------------------------

View File

@ -18,6 +18,7 @@
#include "guiengine/scalable_font.hpp"
#include "font/face_ttf.hpp"
#include "font/font_settings.hpp"
#include "font/font_with_face.hpp"
#include "utils/translation.hpp"
@ -143,7 +144,7 @@ IGUISpriteBank* ScalableFont::getSpriteBank() const
// ------------------------------------------------------------------------
u32 ScalableFont::getSpriteNoFromChar(const wchar_t *c) const
{
const FontWithFace::FontArea& area =
const FontArea& area =
m_face->getAreaFromCharacter(*c, NULL/*fallback_font*/);
return area.spriteno;
} // getSpriteNoFromChar