Initial work on new font rendering engine

This commit is contained in:
Benau 2016-07-18 16:03:11 +08:00
parent 9fd6fd2673
commit 05419cd555
25 changed files with 1437 additions and 378 deletions

View File

@ -419,4 +419,8 @@
bold_fallback="FreeSansBold.ttf"
digit="SigmarOne.otf" />
<fonts-list regular-faces="Cantarell-Regular.otf FreeSans.ttf NotoSansCJK-Regular.ttc NotoNaskhArabicUI-Bold.ttf"
bold-faces="Cantarell-Bold.otf FreeSansBold.ttf NotoSansCJK-Bold.ttc"
digit-faces="SigmarOne.otf" />
</config>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -182,6 +182,9 @@ void STKConfig::init_defaults()
m_score_increase.clear();
m_leader_intervals.clear();
m_switch_items.clear();
m_regular_faces.clear();
m_bold_faces.clear();
m_digit_faces.clear();
} // init_defaults
//-----------------------------------------------------------------------------
@ -363,6 +366,13 @@ void STKConfig::getAllData(const XMLNode * root)
font_node->get("digit", &m_font_digit );
}
if (const XMLNode *fonts_list = root->getNode("fonts-list"))
{
fonts_list->get("regular-faces", &m_regular_faces);
fonts_list->get("bold-faces", &m_bold_faces );
fonts_list->get("digit-faces", &m_digit_faces );
}
// Get the default KartProperties
// ------------------------------
const XMLNode *node = root -> getNode("general-kart-defaults");

View File

@ -153,6 +153,10 @@ public:
std::string m_font_bold;
std::string m_font_bold_fallback;
std::string m_font_digit;
/** Lists of TTF files used in STK. */
std::vector<std::string> m_regular_faces;
std::vector<std::string> m_bold_faces;
std::vector<std::string> m_digit_faces;
private:
/** True if stk_config has been loaded. This is necessary if the

59
src/font/bold_face.cpp Normal file
View File

@ -0,0 +1,59 @@
//
// 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/bold_face.hpp"
#include "config/stk_config.hpp"
#include "font/regular_face.hpp"
// ----------------------------------------------------------------------------
BoldFace::BoldFace() : FontWithFace("BoldFace")
{
} // BoldFace
// ----------------------------------------------------------------------------
void BoldFace::init()
{
FontWithFace::init();
// Reserve some space for characters added later
m_font_max_height = m_glyph_max_height + 20;
setFallbackFont(font_manager->getFont<RegularFace>());
setFallbackFontScale(2.0f);
} // init
// ----------------------------------------------------------------------------
void BoldFace::reset()
{
FontWithFace::reset();
core::stringw preload_chars;
for (int i = 32; i < 128; i++)
{
// Include basic Latin
preload_chars.append((wchar_t)i);
}
insertCharacters(preload_chars.c_str());
updateCharactersList();
} // reset
// ----------------------------------------------------------------------------
std::vector<std::string> BoldFace::getFacesList() const
{
return stk_config->m_bold_faces;
} // getFacesList

50
src/font/bold_face.hpp Normal file
View File

@ -0,0 +1,50 @@
//
// 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.
#ifndef HEADER_BOLD_FACE_HPP
#define HEADER_BOLD_FACE_HPP
#include "font/font_with_face.hpp"
class BoldFace : public FontWithFace
{
public:
LEAK_CHECK();
// ------------------------------------------------------------------------
BoldFace();
// ------------------------------------------------------------------------
virtual ~BoldFace() {}
// ------------------------------------------------------------------------
virtual void init() OVERRIDE;
// ------------------------------------------------------------------------
virtual void reset() OVERRIDE;
// ------------------------------------------------------------------------
virtual std::vector<std::string> getFacesList() const;
// ------------------------------------------------------------------------
virtual bool supportLazyLoadChar() const { return true; }
// ------------------------------------------------------------------------
virtual unsigned int getGlyphPageSize() const { return 1024; }
// ------------------------------------------------------------------------
virtual float getScalingFactorOne() const { return 0.2f; }
// ------------------------------------------------------------------------
virtual unsigned int getScalingFactorTwo() const { return 120; }
}; // FontWithFace
#endif
/* EOF */

60
src/font/font_manager.cpp Normal file
View File

@ -0,0 +1,60 @@
//
// 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/font_manager.hpp"
#include "font/bold_face.hpp"
#include "font/regular_face.hpp"
#include "graphics/irr_driver.hpp"
#include "utils/leak_check.hpp"
FontManager *font_manager = NULL;
// ----------------------------------------------------------------------------
FontManager::FontManager()
{
checkFTError(FT_Init_FreeType(&m_ft_library), "loading freetype library");
} // FontManager
// ----------------------------------------------------------------------------
FontManager::~FontManager()
{
} // ~FontManager
// ----------------------------------------------------------------------------
void FontManager::loadFonts()
{
RegularFace* regular = new RegularFace();
regular->init();
m_fonts.push_back(regular);
BoldFace* bold = new BoldFace();
bold->init();
m_fonts.push_back(bold);
} // loadFonts
// ----------------------------------------------------------------------------
void FontManager::checkFTError(FT_Error err, const std::string& desc) const
{
if (err > 0)
{
Log::error("FontManager", "Something wrong when %s!", desc.c_str());
}
} // checkFTError
// ----------------------------------------------------------------------------

75
src/font/font_manager.hpp Normal file
View File

@ -0,0 +1,75 @@
//
// 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.
#ifndef HEADER_FONT_MANAGER_HPP
#define HEADER_FONT_MANAGER_HPP
#include "utils/leak_check.hpp"
#include "utils/log.hpp"
#include "utils/no_copy.hpp"
#include "utils/ptr_vector.hpp"
#include <string>
#include <ft2build.h>
#include FT_FREETYPE_H
class FontWithFace;
class FontManager : public NoCopy
{
private:
FT_Library m_ft_library;
PtrVector<FontWithFace> m_fonts;
public:
LEAK_CHECK();
// ------------------------------------------------------------------------
FontManager();
// ------------------------------------------------------------------------
~FontManager();
// ------------------------------------------------------------------------
template <typename T> T* getFont()
{
T* out = NULL;
for (unsigned int i = 0; i < m_fonts.size(); i++)
{
out = dynamic_cast<T*>(m_fonts.get(i));
if (out != NULL)
return out;
}
Log::fatal("FontManager", "Can't get a font!");
return out;
}
// ------------------------------------------------------------------------
/** Check for any error discovered in a freetype function that will return
* a FT_Error value.
* \param err The Freetype function.
* \param desc The description of what is the function doing.
*/
void checkFTError(FT_Error err, const std::string& desc) const;
// ------------------------------------------------------------------------
void loadFonts();
// ------------------------------------------------------------------------
FT_Library getFTLibrary() const { return m_ft_library; }
}; // FontManager
extern FontManager *font_manager;
#endif
/* EOF */

View File

@ -0,0 +1,79 @@
//
// 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.
#ifndef HEADER_FONT_SETTINGS_HPP
#define HEADER_FONT_SETTINGS_HPP
#include "utils/leak_check.hpp"
#include <irrlicht.h>
using namespace irr;
class FontSettings
{
private:
bool m_black_border;
bool m_rtl;
float m_scale;
bool m_shadow;
video::SColor m_shadow_color;
public:
LEAK_CHECK();
// ------------------------------------------------------------------------
FontSettings(bool black_border = false, bool rtl = false,
float scale = 1.0f, bool shadow = false,
const video::SColor& color = video::SColor(0, 0, 0, 0))
{
m_black_border = black_border;
m_rtl = rtl;
m_scale = scale;
m_shadow = shadow;
m_shadow_color = color;
}
// ------------------------------------------------------------------------
~FontSettings() {}
// ------------------------------------------------------------------------
void setScale(float scale) { m_scale = scale; }
// ------------------------------------------------------------------------
float getScale() const { return m_scale; }
// ------------------------------------------------------------------------
void setShadowColor(const video::SColor &col) { m_shadow_color = col; }
// ------------------------------------------------------------------------
const video::SColor& getShadowColor() const { return m_shadow_color; }
// ------------------------------------------------------------------------
bool useShadow() const { return m_shadow; }
// ------------------------------------------------------------------------
void setShadow(bool shadow) { m_shadow = shadow; }
// ------------------------------------------------------------------------
void setBlackBorder(bool border) { m_black_border = border; }
// ------------------------------------------------------------------------
bool useBlackBorder() const { return m_black_border; }
// ------------------------------------------------------------------------
void setRTL(bool rtl) { m_rtl = rtl; }
// ------------------------------------------------------------------------
bool isRTL() const { return m_rtl; }
// ------------------------------------------------------------------------
}; // FontSettings
#endif
/* EOF */

666
src/font/font_with_face.cpp Normal file
View File

@ -0,0 +1,666 @@
//
// 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/font_with_face.hpp"
#include "font/bold_face.hpp"
#include "graphics/2dutils.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/skin.hpp"
#include "utils/string_utils.hpp"
// ----------------------------------------------------------------------------
FontWithFace::FontWithFace(const std::string& name)
{
m_spritebank = irr_driver->getGUI()->addEmptySpriteBank(name.c_str());
assert(m_spritebank != NULL);
m_spritebank->grab();
m_fallback_font = NULL;
m_fallback_font_scale = 1.0f;
m_glyph_max_height = 0;
} // FontWithFace
// ----------------------------------------------------------------------------
FontWithFace::~FontWithFace()
{
m_page->drop();
m_page = NULL;
m_spritebank->drop();
m_spritebank = NULL;
} // ~FontWithFace
// ----------------------------------------------------------------------------
void FontWithFace::init()
{
m_page = irr_driver->getVideoDriver()->createImage(video::ECF_A8R8G8B8,
core::dimension2du(getGlyphPageSize(), getGlyphPageSize()));
for (const std::string& font : getFacesList())
{
FT_Face face = NULL;
font_manager->checkFTError(FT_New_Face(font_manager->getFTLibrary(),
(file_manager->getAssetChecked(FileManager::TTF,
font.c_str(), true)).c_str(), 0, &face), "loading fonts");
font_manager->checkFTError(FT_Set_Pixel_Sizes(face, 0, getDPI()),
"setting DPI");
m_faces.push_back(face);
}
// Get the max height for this face
assert(m_faces.size() > 0);
FT_Face cur_face = m_faces[0];
for (int i = 32; i < 128; i++)
{
// Test all basic latin characters
const int idx = FT_Get_Char_Index(cur_face, (wchar_t)i);
if (idx == 0) continue;
font_manager->checkFTError(FT_Load_Glyph(cur_face, idx,
FT_LOAD_DEFAULT), "setting max height");
const int height = cur_face->glyph->metrics.height / BEARING;
if (height > m_glyph_max_height)
m_glyph_max_height = height;
}
reset();
} // init
// ----------------------------------------------------------------------------
void FontWithFace::reset()
{
m_new_char_holder.clear();
m_character_area_map.clear();
m_character_glyph_info_map.clear();
m_spritebank->clear();
createNewGlyphPage();
} // reset
// ----------------------------------------------------------------------------
void FontWithFace::createNewGlyphPage()
{
// Clean the current glyph page by filling it with transparent content
m_page->fill(video::SColor(0, 255, 255, 255));
m_temp_height = 0;
m_used_width = 0;
m_used_height = 0;
video::ITexture* page_texture = irr_driver->getVideoDriver()
->addTexture("Glyph_page", m_page);
m_spritebank->addTexture(NULL);
m_spritebank->setTexture(m_spritebank->getTextureCount() - 1,
page_texture);
// Doing so to make sure the texture inside the sprite bank has only 1
// reference, so they can be removed or updated on-the-fly
irr_driver->getVideoDriver()->removeTexture(page_texture);
assert(page_texture->getReferenceCount() == 1);
} // createNewGlyphPage
// ----------------------------------------------------------------------------
void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
{
assert(gi.glyph_index > 0);
assert(gi.font_number < m_faces.size());
FT_Face cur_face = m_faces[gi.font_number];
FT_GlyphSlot slot = cur_face->glyph;
font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index,
FT_LOAD_DEFAULT | FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL),
"loading a glyph");
// Convert to an anti-aliased bitmap
FT_Bitmap bits = slot->bitmap;
core::dimension2du d(bits.width + 1, bits.rows + 1);
core::dimension2du texture_size;
texture_size = d.getOptimalSize(!(irr_driver->getVideoDriver()
->queryFeature(video::EVDF_TEXTURE_NPOT)), !(irr_driver
->getVideoDriver()->queryFeature(video::EVDF_TEXTURE_NSQUARE)),
true, 0);
if ((m_used_width + texture_size.Width > getGlyphPageSize() &&
m_used_height + m_temp_height + texture_size.Height >
getGlyphPageSize()) ||
m_used_height + texture_size.Height > getGlyphPageSize())
{
// Current glyph page is full:
// Save the old glyph page
video::ITexture* page_texture = irr_driver->getVideoDriver()
->addTexture("Glyph_page", m_page);
m_spritebank->setTexture(m_spritebank->getTextureCount() - 1,
page_texture);
irr_driver->getVideoDriver()->removeTexture(page_texture);
assert(page_texture->getReferenceCount() == 1);
// Clear and add a new one
createNewGlyphPage();
}
video::IImage* glyph = NULL;
switch (bits.pixel_mode)
{
case FT_PIXEL_MODE_GRAY:
{
// Create our blank image.
glyph = irr_driver->getVideoDriver()
->createImage(video::ECF_A8R8G8B8, texture_size);
glyph->fill(video::SColor(0, 255, 255, 255));
// Load the grayscale data in.
const float gray_count = static_cast<float>(bits.num_grays);
const unsigned int image_pitch =
glyph->getPitch() / sizeof(unsigned int);
unsigned int* image_data = (unsigned int*)glyph->lock();
unsigned char* glyph_data = bits.buffer;
for (unsigned int y = 0; y < (unsigned int)bits.rows; y++)
{
unsigned char* row = glyph_data;
for (unsigned int x = 0; x < (unsigned)bits.width; x++)
{
image_data[y * image_pitch + x] |=
static_cast<unsigned int>(255.0f *
(static_cast<float>(*row++) / gray_count)) << 24;
}
glyph_data += bits.pitch;
}
glyph->unlock();
break;
}
default:
assert(false);
}
if (!glyph)
Log::fatal("FontWithFace", "Failed to load a glyph");
// Done creating a single glyph, now copy to the glyph page...
// Determine the linebreak location
if (m_used_width + texture_size.Width > getGlyphPageSize())
{
m_used_width = 0;
m_used_height += m_temp_height;
m_temp_height = 0;
}
// Copy to the full glyph page
glyph->copyTo(m_page, core::position2di(m_used_width, m_used_height));
// Store the rectangle of current glyph
gui::SGUISpriteFrame f;
gui::SGUISprite s;
core::rect<s32> rectangle(m_used_width, m_used_height,
m_used_width + bits.width, m_used_height + bits.rows);
f.rectNumber = m_spritebank->getPositions().size();
f.textureNumber = m_spritebank->getTextureCount() - 1;
// Add frame to sprite
s.Frames.push_back(f);
s.frameTime = 0;
m_spritebank->getPositions().push_back(rectangle);
m_spritebank->getSprites().push_back(s);
// Save glyph metrics
FontArea a;
a.advance_x = cur_face->glyph->advance.x / BEARING;
a.bearing_x = cur_face->glyph->metrics.horiBearingX / BEARING;
const int cur_height = (cur_face->glyph->metrics.height / BEARING);
const int cur_offset_y = cur_height -
(cur_face->glyph->metrics.horiBearingY / BEARING);
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;
// Clean the temporary glyph
glyph->drop();
glyph = NULL;
// Store used area
m_used_width += texture_size.Width;
if (m_temp_height < texture_size.Height)
m_temp_height = texture_size.Height;
} // insertGlyph
// ----------------------------------------------------------------------------
void FontWithFace::updateCharactersList()
{
if (m_fallback_font != NULL)
m_fallback_font->updateCharactersList();
if (m_new_char_holder.empty()) return;
for (const wchar_t& c : m_new_char_holder)
{
const GlyphInfo& gi = getGlyphInfo(c);
insertGlyph(c, gi);
}
m_new_char_holder.clear();
// Update last glyph page
video::ITexture* page_texture = irr_driver->getVideoDriver()
->addTexture("Glyph_page", m_page);
m_spritebank->setTexture(m_spritebank->getTextureCount() - 1,
page_texture);
irr_driver->getVideoDriver()->removeTexture(page_texture);
assert(page_texture->getReferenceCount() == 1);
} // updateCharactersList
// ----------------------------------------------------------------------------
void FontWithFace::dumpGlyphPage(const std::string& name)
{
for (unsigned int i = 0; i < m_spritebank->getTextureCount(); i++)
{
video::IImage* image = irr_driver->getVideoDriver()
->createImageFromData (m_spritebank->getTexture(i)
->getColorFormat(), m_spritebank->getTexture(i)->getSize(),
m_spritebank->getTexture(i)->lock(), false/*copy mem*/);
m_spritebank->getTexture(i)->unlock();
irr_driver->getVideoDriver()->writeImageToFile(image, std::string
(name + "_" + StringUtils::toString(i) + ".png").c_str());
image->drop();
}
} // dumpGlyphPage
// ----------------------------------------------------------------------------
void FontWithFace::dumpGlyphPage()
{
dumpGlyphPage("face");
} // dumpGlyphPage
// ----------------------------------------------------------------------------
unsigned int FontWithFace::getDPI() const
{
// Get face dpi:
// Font size is resolution-dependent.
// Normal text will range from 0.8, in 640x* resolutions (won't scale
// below that) to 1.0, in 1024x* resolutions, and linearly up
// Bold text will range from 0.2, in 640x* resolutions (won't scale
// below that) to 0.4, in 1024x* resolutions, and linearly up
const int screen_width = irr_driver->getFrameSize().Width;
const int screen_height = irr_driver->getFrameSize().Height;
float scale = std::max(0, screen_width - 640) / 564.0f;
// attempt to compensate for small screens
if (screen_width < 1200)
scale = std::max(0, screen_width - 640) / 750.0f;
if (screen_width < 900 || screen_height < 700)
scale = std::min(scale, 0.05f);
return unsigned((getScalingFactorOne() + 0.2f * scale) *
getScalingFactorTwo());
} // getDPI
// ----------------------------------------------------------------------------
const FontWithFace::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())
{
if (fallback_font != NULL)
*fallback_font = false;
return n->second;
}
else if (m_fallback_font != NULL && fallback_font != NULL)
{
*fallback_font = true;
return m_fallback_font->getAreaFromCharacter(c, NULL);
}
// 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;
} // getAreaFromCharacter
// ----------------------------------------------------------------------------
core::dimension2d<u32> FontWithFace::getDimension(const wchar_t* text,
FontSettings* font_settings)
{
const float scale = font_settings ? font_settings->getScale() : 1.0f;
// Test if lazy load char is needed
insertCharacters(text);
updateCharactersList();
assert(m_character_area_map.size() > 0);
core::dimension2d<u32> dim(0, 0);
core::dimension2d<u32> this_line(0, (int)(m_font_max_height * scale));
for (const wchar_t* p = text; *p; ++p)
{
if (*p == L'\r' || // Windows breaks
*p == L'\n' ) // Unix breaks
{
if (*p==L'\r' && p[1] == L'\n') // Windows breaks
++p;
dim.Height += this_line.Height;
if (dim.Width < this_line.Width)
dim.Width = this_line.Width;
this_line.Width = 0;
continue;
}
bool fallback = false;
const FontArea &area = getAreaFromCharacter(*p, &fallback);
this_line.Width += getCharWidth(area, fallback, scale);
}
dim.Height += this_line.Height;
if (dim.Width < this_line.Width)
dim.Width = this_line.Width;
dim.Width = (int)(dim.Width + 0.9f); // round up
dim.Height = (int)(dim.Height + 0.9f);
return dim;
} // getDimension
// ----------------------------------------------------------------------------
int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x,
FontSettings* font_settings) const
{
const float scale = font_settings ? font_settings->getScale() : 1.0f;
int x = 0;
int idx = 0;
while (text[idx])
{
bool use_fallback_font = false;
const FontArea &a = getAreaFromCharacter(text[idx], &use_fallback_font);
x += getCharWidth(a, use_fallback_font, scale);
if (x >= pixel_x)
return idx;
++idx;
}
return -1;
} // getCharacterFromPos
// ----------------------------------------------------------------------------
void FontWithFace::render(const core::stringw& text,
const core::rect<s32>& position,
const video::SColor& color, bool hcenter,
bool vcenter, const core::rect<s32>* clip,
FontSettings* font_settings,
FontCharCollector* char_collector)
{
const bool is_bold_face = dynamic_cast<BoldFace*>(this);
const bool black_border = font_settings ?
font_settings->useBlackBorder() : false;
const bool rtl = font_settings ? font_settings->isRTL() : false;
const float scale = font_settings ? font_settings->getScale() : 1.0f;
const float shadow = font_settings ? font_settings->useShadow() : false;
if (shadow)
{
assert(font_settings);
// Avoid infinite recursion
font_settings->setShadow(false);
core::rect<s32> shadowpos = position;
shadowpos.LowerRightCorner.X += 2;
shadowpos.LowerRightCorner.Y += 2;
render(text, shadowpos, font_settings->getShadowColor(), hcenter,
vcenter, clip);
// Set back
font_settings->setShadow(true);
}
core::position2d<s32> offset = position.UpperLeftCorner;
core::dimension2d<s32> text_dimension;
if (rtl || hcenter || vcenter || clip)
{
text_dimension = getDimension(text.c_str());
if (hcenter)
offset.X += (position.getWidth() - text_dimension.Width) / 2;
else if (rtl)
offset.X += (position.getWidth() - text_dimension.Width);
if (vcenter)
offset.Y += (position.getHeight() - text_dimension.Height) / 2;
if (clip)
{
core::rect<s32> clippedRect(offset, text_dimension);
clippedRect.clipAgainst(*clip);
if (!clippedRect.isValid()) return;
}
}
// Collect character locations
const unsigned int text_size = text.size();
core::array<s32> indices(text_size);
core::array<core::position2di> offsets(text_size);
std::vector<bool> fallback(text_size);
// Test again if lazy load char is needed,
// as some text isn't drawn with getDimension
insertCharacters(text.c_str());
updateCharactersList();
for (u32 i = 0; i < text_size; i++)
{
wchar_t c = text[i];
if (c == L'\r' || // Windows breaks
c == L'\n' ) // Unix breaks
{
if (c==L'\r' && text[i+1]==L'\n')
c = text[++i];
offset.Y += (int)(m_font_max_height * scale);
offset.X = position.UpperLeftCorner.X;
if (hcenter)
offset.X += (position.getWidth() - text_dimension.Width) >> 1;
continue;
} // if lineBreak
bool use_fallback_font = false;
const FontArea &area = getAreaFromCharacter(c, &use_fallback_font);
fallback[i] = use_fallback_font;
if (char_collector == NULL)
{
// Try to use ceil to make offset calculate correctly when scale
// is smaller than 1
s32 glyph_offset_x = (s32)ceil((float) area.bearing_x *
(fallback[i] ? m_fallback_font_scale : scale));
s32 glyph_offset_y = (s32)ceil((float) area.offset_y *
(fallback[i] ? m_fallback_font_scale : scale));
offset.X += glyph_offset_x;
offset.Y += glyph_offset_y;
offsets.push_back(offset);
offset.X -= glyph_offset_x;
offset.Y -= glyph_offset_y;
}
else
{
// Prevent overwriting texture used by billboard text when
// using lazying loading characters
if (supportLazyLoadChar() && fallback[i])
{
const int cur_texno = m_fallback_font->getSpriteBank()
->getSprites()[area.spriteno].Frames[0].textureNumber;
if (cur_texno == int(m_fallback_font->getSpriteBank()
->getTextureCount() - 1))
{
m_fallback_font->createNewGlyphPage();
}
}
else
{
const int cur_texno = m_spritebank
->getSprites()[area.spriteno].Frames[0].textureNumber;
if (cur_texno == int(m_spritebank->getTextureCount() - 1))
{
createNewGlyphPage();
}
}
// Billboard text specific, use offset_y_bt instead
s32 glyph_offset_x = (s32)ceil((float) area.bearing_x *
(fallback[i] ? m_fallback_font_scale : scale));
s32 glyph_offset_y = (s32)ceil((float) area.offset_y_bt *
(fallback[i] ? m_fallback_font_scale : scale));
offset.X += glyph_offset_x;
offset.Y += glyph_offset_y;
offsets.push_back(offset);
offset.X -= glyph_offset_x;
offset.Y -= glyph_offset_y;
}
indices.push_back(area.spriteno);
offset.X += getCharWidth(area, fallback[i], scale);
} // for i < text_size
// Do the actual rendering
const int indice_amount = indices.size();
core::array<gui::SGUISprite>& sprites = m_spritebank->getSprites();
core::array<core::rect<s32>>& positions = m_spritebank->getPositions();
core::array<gui::SGUISprite>* fallback_sprites;
core::array<core::rect<s32>>* fallback_positions;
if (m_fallback_font != NULL)
{
fallback_sprites = &m_fallback_font->m_spritebank->getSprites();
fallback_positions = &m_fallback_font->m_spritebank->getPositions();
}
else
{
fallback_sprites = NULL;
fallback_positions = NULL;
}
const int sprite_amount = sprites.size();
if ((black_border || is_bold_face) && char_collector == NULL)
{
// Draw black border first, to make it behind the real character
// which make script language display better
video::SColor black(color.getAlpha(),0,0,0);
for (int n = 0; n < indice_amount; n++)
{
const int sprite_id = indices[n];
if (!fallback[n] && (sprite_id < 0 || sprite_id >= sprite_amount))
continue;
if (indices[n] == -1) continue;
const int tex_id = (fallback[n] ?
(*fallback_sprites)[sprite_id].Frames[0].textureNumber :
sprites[sprite_id].Frames[0].textureNumber);
core::rect<s32> source = (fallback[n] ? (*fallback_positions)
[(*fallback_sprites)[sprite_id].Frames[0].rectNumber] :
positions[sprites[sprite_id].Frames[0].rectNumber]);
core::dimension2d<s32> size = source.getSize();
float cur_scale = (fallback[n] ? m_fallback_font_scale : scale);
size.Width = (int)(size.Width * cur_scale);
size.Height = (int)(size.Height * cur_scale);
core::rect<s32> dest(offsets[n], size);
video::ITexture* texture = (fallback[n] ?
m_fallback_font->m_spritebank->getTexture(tex_id) :
m_spritebank->getTexture(tex_id));
for (int x_delta = -2; x_delta <= 2; x_delta++)
{
for (int y_delta = -2; y_delta <= 2; y_delta++)
{
if (x_delta == 0 || y_delta == 0) continue;
draw2DImage(texture, dest + core::position2d<s32>
(x_delta, y_delta), source, clip, black, true);
}
}
}
}
for (int n = 0; n < indice_amount; n++)
{
const int sprite_id = indices[n];
if (!fallback[n] && (sprite_id < 0 || sprite_id >= sprite_amount))
continue;
if (indices[n] == -1) continue;
const int tex_id = (fallback[n] ?
(*fallback_sprites)[sprite_id].Frames[0].textureNumber :
sprites[sprite_id].Frames[0].textureNumber);
core::rect<s32> source = (fallback[n] ?
(*fallback_positions)[(*fallback_sprites)[sprite_id].Frames[0]
.rectNumber] : positions[sprites[sprite_id].Frames[0].rectNumber]);
core::dimension2d<s32> size = source.getSize();
float cur_scale = (fallback[n] ? m_fallback_font_scale : scale);
size.Width = (int)(size.Width * cur_scale);
size.Height = (int)(size.Height * cur_scale);
core::rect<s32> dest(offsets[n], size);
video::ITexture* texture = (fallback[n] ?
m_fallback_font->m_spritebank->getTexture(tex_id) :
m_spritebank->getTexture(tex_id));
if (fallback[n] || is_bold_face)
{
video::SColor top = GUIEngine::getSkin()->getColor("font::top");
video::SColor bottom = GUIEngine::getSkin()
->getColor("font::bottom");
top.setAlpha(color.getAlpha());
bottom.setAlpha(color.getAlpha());
video::SColor title_colors[] = {top, bottom, top, bottom};
if (char_collector != NULL)
{
char_collector->collectChar(texture, dest, source,
title_colors);
}
else
{
draw2DImage(texture, dest, source, clip, title_colors, true);
}
}
else
{
if (char_collector != NULL)
{
video::SColor colors[] = {color, color, color, color};
char_collector->collectChar(texture, dest, source, colors);
}
else
{
draw2DImage(texture, dest, source, clip, color, true);
}
}
}
} // render

240
src/font/font_with_face.hpp Normal file
View File

@ -0,0 +1,240 @@
//
// 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.
#ifndef HEADER_FONT_WITH_FACE_HPP
#define HEADER_FONT_WITH_FACE_HPP
#include "font/font_manager.hpp"
#include "font/font_settings.hpp"
#include "graphics/irr_driver.hpp"
#include "utils/cpp2011.hpp"
#include "utils/leak_check.hpp"
#include <algorithm>
#include <map>
#include <set>
const int BEARING = 64;
class FontWithFace : public NoCopy
{
public:
class FontCharCollector
{
public:
virtual void collectChar(video::ITexture* texture,
const core::rect<s32>& destRect,
const core::rect<s32>& sourceRect,
const video::SColor* const colors) = 0;
};
protected:
std::vector<FT_Face> m_faces;
int m_font_max_height;
int m_glyph_max_height;
unsigned int getDPI() const;
private:
struct FontArea
{
FontArea() : advance_x(0), bearing_x(0) ,offset_y(0), offset_y_bt(0),
spriteno(0) {}
int advance_x;
int bearing_x;
int offset_y;
int offset_y_bt;
int spriteno;
};
struct GlyphInfo
{
unsigned int font_number;
unsigned int glyph_index;
GlyphInfo(unsigned int first = 0, unsigned int second = 0)
{
font_number = first;
glyph_index = second;
}
};
FontWithFace* m_fallback_font;
float m_fallback_font_scale;
/** A temporary holder stored new char to be inserted.
*/
std::set<wchar_t> m_new_char_holder;
gui::IGUISpriteBank* m_spritebank;
/** A full glyph page for this font.
*/
video::IImage* m_page;
unsigned int m_temp_height;
unsigned int m_used_width;
unsigned int m_used_height;
//std::vector<FontArea> m_areas;
std::map<wchar_t, FontArea> m_character_area_map;
std::map<wchar_t, GlyphInfo> m_character_glyph_info_map;
// ------------------------------------------------------------------------
int getCharWidth(const FontArea& area, bool fallback, float scale) const
{
if (fallback)
return (int)(area.advance_x * m_fallback_font_scale * scale);
else
return (int)(area.advance_x * scale);
}
public:
LEAK_CHECK();
// ------------------------------------------------------------------------
FontWithFace(const std::string& name);
// ------------------------------------------------------------------------
virtual ~FontWithFace();
// ------------------------------------------------------------------------
virtual void init();
// ------------------------------------------------------------------------
virtual void reset();
// ------------------------------------------------------------------------
void addLazyLoadChar(wchar_t c) { m_new_char_holder.insert(c); }
// ------------------------------------------------------------------------
void updateCharactersList();
// ------------------------------------------------------------------------
void insertGlyph(wchar_t c, const GlyphInfo& gi);
// ------------------------------------------------------------------------
virtual bool supportLazyLoadChar() const = 0;
// ------------------------------------------------------------------------
virtual unsigned int getGlyphPageSize() const = 0;
// ------------------------------------------------------------------------
virtual float getScalingFactorOne() const = 0;
// ------------------------------------------------------------------------
virtual unsigned int getScalingFactorTwo() const = 0;
// ------------------------------------------------------------------------
virtual std::vector<std::string> getFacesList() const = 0;
// ------------------------------------------------------------------------
bool loadedChar(wchar_t c) const
{
std::map<wchar_t, GlyphInfo>::const_iterator n =
m_character_glyph_info_map.find(c);
if (n != m_character_glyph_info_map.end())
return true;
return false;
}
// ------------------------------------------------------------------------
const GlyphInfo& getGlyphInfo(wchar_t c) const
{
std::map<wchar_t, GlyphInfo>::const_iterator n =
m_character_glyph_info_map.find(c);
// Make sure we always find GlyphInfo
assert(n != m_character_glyph_info_map.end());
return n->second;
}
// ------------------------------------------------------------------------
bool supportChar(wchar_t c)
{
std::map<wchar_t, GlyphInfo>::const_iterator n =
m_character_glyph_info_map.find(c);
if (n != m_character_glyph_info_map.end())
{
return n->second.glyph_index > 0;
}
return false;
}
// ------------------------------------------------------------------------
void loadGlyphInfo(wchar_t c)
{
unsigned int font_number = 0;
unsigned int glyph_index = 0;
while (font_number < m_faces.size())
{
glyph_index = FT_Get_Char_Index(m_faces[font_number], c);
if (glyph_index > 0) break;
font_number++;
}
m_character_glyph_info_map[c] = GlyphInfo(font_number, glyph_index);
}
// ------------------------------------------------------------------------
void insertCharacters(const wchar_t* in_ptr)
{
if (!supportLazyLoadChar()) return;
for (const wchar_t* p = in_ptr; *p; ++p)
{
if (*p == L'\r' || *p == L'\n' || *p < (wchar_t)32)
continue;
if (!loadedChar(*p))
{
loadGlyphInfo(*p);
if (supportChar(*p))
addLazyLoadChar(*p);
else if (m_fallback_font != NULL)
{
if (!m_fallback_font->loadedChar(*p))
{
m_fallback_font->loadGlyphInfo(*p);
if (m_fallback_font->supportChar(*p))
m_fallback_font->addLazyLoadChar(*p);
}
}
}
}
}
// ------------------------------------------------------------------------
const FontArea& getAreaFromCharacter(const wchar_t c,
bool* fallback_font) const;
// ------------------------------------------------------------------------
core::dimension2d<u32> getDimension(const wchar_t* text,
FontSettings* font_settings = NULL);
// ------------------------------------------------------------------------
int getCharacterFromPos(const wchar_t* text, int pixel_x,
FontSettings* font_settings = NULL) const;
// ------------------------------------------------------------------------
void render(const core::stringw& text, const core::rect<s32>& position,
const video::SColor& color, bool hcenter, bool vcenter,
const core::rect<s32>* clip,
FontSettings* font_settings = NULL,
FontCharCollector* char_collector = NULL);
// ------------------------------------------------------------------------
gui::IGUISpriteBank* getSpriteBank() const { return m_spritebank; }
// ------------------------------------------------------------------------
void createNewGlyphPage();
// ------------------------------------------------------------------------
void setFallbackFont(FontWithFace* face) { m_fallback_font = face; }
// ------------------------------------------------------------------------
void setFallbackFontScale(float scale) { m_fallback_font_scale = scale; }
// ------------------------------------------------------------------------
/** Write the current glyph page in png inside current running directory.
* Mainly for debug use.
* \param fn The file name.
*/
void dumpGlyphPage(const std::string& name);
// ------------------------------------------------------------------------
/** Write the current glyph page in png inside current running directory.
* Useful in gdb without parameter.
*/
void dumpGlyphPage();
}; // FontWithFace
#endif
/* EOF */

56
src/font/regular_face.cpp Normal file
View File

@ -0,0 +1,56 @@
//
// 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/regular_face.hpp"
#include "config/stk_config.hpp"
// ----------------------------------------------------------------------------
RegularFace::RegularFace() : FontWithFace("RegularFace")
{
} // RegularFace
// ----------------------------------------------------------------------------
void RegularFace::init()
{
FontWithFace::init();
// Reserve some space for characters added later
m_font_max_height = m_glyph_max_height + 10;
} // init
// ----------------------------------------------------------------------------
void RegularFace::reset()
{
FontWithFace::reset();
core::stringw preload_chars;
for (int i = 32; i < 128; i++)
{
// Include basic Latin
preload_chars.append((wchar_t)i);
}
insertCharacters(preload_chars.c_str());
updateCharactersList();
} // reset
// ----------------------------------------------------------------------------
std::vector<std::string> RegularFace::getFacesList() const
{
return stk_config->m_regular_faces;
} // getFacesList

50
src/font/regular_face.hpp Normal file
View File

@ -0,0 +1,50 @@
//
// 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.
#ifndef HEADER_REGULAR_FACE_HPP
#define HEADER_REGULAR_FACE_HPP
#include "font/font_with_face.hpp"
class RegularFace : public FontWithFace
{
public:
LEAK_CHECK();
// ------------------------------------------------------------------------
RegularFace();
// ------------------------------------------------------------------------
virtual ~RegularFace() {}
// ------------------------------------------------------------------------
virtual void init() OVERRIDE;
// ------------------------------------------------------------------------
virtual void reset() OVERRIDE;
// ------------------------------------------------------------------------
virtual std::vector<std::string> getFacesList() const;
// ------------------------------------------------------------------------
virtual bool supportLazyLoadChar() const { return true; }
// ------------------------------------------------------------------------
virtual unsigned int getGlyphPageSize() const { return 512; }
// ------------------------------------------------------------------------
virtual float getScalingFactorOne() const { return 0.7f; }
// ------------------------------------------------------------------------
virtual unsigned int getScalingFactorTwo() const { return 27; }
}; // FontWithFace
#endif
/* EOF */

View File

@ -22,7 +22,6 @@
#include "graphics/stk_billboard.hpp"
#include "graphics/stk_mesh_scene_node.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/scalable_font.hpp"
#include "glwrap.hpp"
#include <SMesh.h>
#include <SMeshBuffer.h>
@ -31,7 +30,7 @@
using namespace irr;
STKTextBillboard::STKTextBillboard(core::stringw text, gui::ScalableFont* font,
STKTextBillboard::STKTextBillboard(core::stringw text, FontWithFace* font,
const video::SColor& color_top, const video::SColor& color_bottom,
irr::scene::ISceneNode* parent,
irr::scene::ISceneManager* mgr, irr::s32 id,
@ -61,11 +60,11 @@ void STKTextBillboard::updateAbsolutePosition()
AbsoluteTransformation = getRelativeTransformation();
}
scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, gui::ScalableFont* font)
scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, FontWithFace* font)
{
core::dimension2du size = font->getDimension(text.c_str());
font->doDraw(text, core::rect<s32>(0, 0, size.Width, size.Height), video::SColor(255,255,255,255),
false, false, NULL, this);
font->render(text, core::rect<s32>(0, 0, size.Width, size.Height), video::SColor(255,255,255,255),
false, false, NULL, NULL, this);
const float scale = 0.03f;

View File

@ -19,7 +19,7 @@
#define STK_TEXT_BILLBOARD_HPP
#include "graphics/stk_mesh_scene_node.hpp"
#include "guiengine/scalable_font.hpp"
#include "font/font_with_face.hpp"
#include "utils/cpp2011.hpp"
#include "../lib/irrlicht/source/Irrlicht/CBillboardSceneNode.h"
@ -57,16 +57,16 @@ public:
}
};
class STKTextBillboard : public STKMeshSceneNode, irr::gui::FontCharCollector
class STKTextBillboard : public STKMeshSceneNode, FontWithFace::FontCharCollector
{
std::vector<STKTextBillboardChar> m_chars;
irr::video::SColor m_color_top;
irr::video::SColor m_color_bottom;
irr::scene::IMesh* getTextMesh(irr::core::stringw text, gui::ScalableFont* font);
irr::scene::IMesh* getTextMesh(irr::core::stringw text, FontWithFace* font);
public:
STKTextBillboard(irr::core::stringw text, irr::gui::ScalableFont* font,
STKTextBillboard(irr::core::stringw text, FontWithFace* font,
const irr::video::SColor& color_top,
const irr::video::SColor& color_bottom,
irr::scene::ISceneNode* parent,

View File

@ -657,9 +657,11 @@ namespace GUIEngine
#include "guiengine/engine.hpp"
#include "config/user_config.hpp"
#include "graphics/2dutils.hpp"
#include "font/bold_face.hpp"
#include "font/regular_face.hpp"
#include "input/input_manager.hpp"
#include "io/file_manager.hpp"
#include "graphics/2dutils.hpp"
#include "guiengine/event_handler.hpp"
#include "guiengine/modaldialog.hpp"
#include "guiengine/message_queue.hpp"
@ -954,10 +956,10 @@ namespace GUIEngine
//if (g_skin != NULL) delete g_skin;
g_skin = NULL;
delete g_ft_env;
g_ft_env = NULL;
delete g_gp_creator;
g_gp_creator = NULL;
//delete g_ft_env;
//g_ft_env = NULL;
//delete g_gp_creator;
//g_gp_creator = NULL;
for (unsigned int i=0; i<g_loaded_screens.size(); i++)
{
@ -992,12 +994,7 @@ namespace GUIEngine
// -----------------------------------------------------------------------
void cleanHollowCopyFont()
{
g_small_font->drop();
g_small_font = NULL;
g_large_font->drop();
g_large_font = NULL;
g_outline_font->drop();
g_outline_font = NULL;
} // cleanHollowCopyFont
// -----------------------------------------------------------------------
@ -1027,8 +1024,8 @@ namespace GUIEngine
g_focus_for_player[n] = NULL;
}
g_ft_env = new FTEnvironment();
g_gp_creator = new GlyphPageCreator();
//g_ft_env = new FTEnvironment();
//g_gp_creator = new GlyphPageCreator();
/*
To make the g_font a little bit nicer, we load an external g_font
@ -1063,48 +1060,34 @@ namespace GUIEngine
}
}
ScalableFont* digit_font =new ScalableFont(g_env,T_DIGIT);
digit_font->setMonospaceDigits(true);
RegularFace* regular = font_manager->getFont<RegularFace>();
BoldFace* bold = font_manager->getFont<BoldFace>();
ScalableFont* digit_font = new ScalableFont(regular);
g_digit_font = digit_font;
ScalableFont* sfont2 =new ScalableFont(g_env,T_BOLD);
sfont2->setKerningWidth(0);
// Because the fallback font is much smaller than the title font:
sfont2->m_fallback_font_scale = 2.0f;
sfont2->m_fallback_kerning_width = 0;
ScalableFont* sfont =new ScalableFont(g_env,T_NORMAL);
sfont->setKerningHeight(0);
sfont->setScale(1);
ScalableFont* sfont = new ScalableFont(regular);
g_font = sfont;
Private::font_height = g_font->getDimension( L"X" ).Height;
ScalableFont* sfont_larger = sfont->getHollowCopy();
sfont_larger->setScale(1.4f);
sfont_larger->setKerningHeight(0);
ScalableFont* sfont_larger = new ScalableFont(regular);
sfont_larger->getFontSettings()->setScale(1.4f);
g_large_font = sfont_larger;
g_outline_font = sfont->getHollowCopy();
g_outline_font->m_black_border = true;
Private::large_font_height = g_large_font->getDimension( L"X" ).Height;
ScalableFont* sfont_smaller = sfont->getHollowCopy();
sfont_smaller->setScale(0.8f);
sfont_smaller->setKerningHeight(0);
g_outline_font = new ScalableFont(regular);
g_outline_font->getFontSettings()->setBlackBorder(true);
ScalableFont* sfont_smaller = new ScalableFont(regular);
sfont_smaller->getFontSettings()->setScale(0.8f);
g_small_font = sfont_smaller;
Private::small_font_height = g_small_font->getDimension( L"X" ).Height;
Private::small_font_height =
g_small_font->getDimension( L"X" ).Height;
sfont2->m_fallback_font = sfont;
sfont2->setScale(1);
sfont2->m_black_border = true;
ScalableFont* sfont2 = new ScalableFont(bold);
g_title_font = sfont2;
Private::title_font_height =
g_title_font->getDimension( L"X" ).Height;
if (g_font != NULL) g_skin->setFont(g_font);
// set event receiver
@ -1119,27 +1102,6 @@ namespace GUIEngine
// -----------------------------------------------------------------------
void reloadHollowCopyFont(irr::gui::ScalableFont* sfont)
{
//Base on the init function above
sfont->setScale(1);
sfont->setKerningHeight(0);
Private::font_height = sfont->getDimension( L"X" ).Height;
ScalableFont* sfont_larger = sfont->getHollowCopy();
sfont_larger->setScale(1.4f);
sfont_larger->setKerningHeight(0);
g_large_font = sfont_larger;
g_outline_font = sfont->getHollowCopy();
g_outline_font->m_black_border = true;
Private::large_font_height = g_large_font->getDimension( L"X" ).Height;
ScalableFont* sfont_smaller = sfont->getHollowCopy();
sfont_smaller->setScale(0.8f);
sfont_smaller->setKerningHeight(0);
g_small_font = sfont_smaller;
Private::small_font_height = g_small_font->getDimension( L"X" ).Height;
} // reloadHollowCopyFont
// -----------------------------------------------------------------------

View File

@ -18,6 +18,9 @@
#include "guiengine/scalable_font.hpp"
#include "font/font_manager.hpp"
#include "font/font_with_face.hpp"
#include "font/regular_face.hpp"
#include "graphics/2dutils.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/skin.hpp"
@ -37,11 +40,19 @@ namespace gui
{
// ----------------------------------------------------------------------------
ScalableFont::ScalableFont(FontWithFace* face): m_video_driver(0), m_spritebank(0),
m_max_height(0), m_global_kerning_width(0), m_global_kerning_height(0)
{
m_face = face;
m_font_settings = new FontSettings();
}
// ----------------------------------------------------------------------------
ScalableFont::ScalableFont(IGUIEnvironment *env, TTFLoadingType type)
: m_video_driver(0), m_spritebank(0), m_gui_env(env),
m_max_height(0), m_global_kerning_width(0), m_global_kerning_height(0)
{
m_font_settings = new FontSettings();
#ifdef _DEBUG
setDebugName("ScalableFont");
#endif
@ -86,19 +97,14 @@ ScalableFont::ScalableFont(IGUIEnvironment *env, TTFLoadingType type)
ScalableFont::~ScalableFont()
{
if (!m_is_hollow_copy)
{
if (m_video_driver)
m_video_driver->drop();
if (m_spritebank)
m_spritebank->drop();
}
delete m_font_settings;
}
// ----------------------------------------------------------------------------
bool ScalableFont::loadTTF()
{
if (!m_spritebank)
{
Log::error("ScalableFont::loadTTF", "SpriteBank is NULL!!");
@ -186,7 +192,13 @@ bool ScalableFont::loadTTF()
t = curr_face->glyph->metrics.height >> 6;
height.push_back(t);
if (t > curr_maxheight)
{
if (t > 20)
{
}
curr_maxheight = t;
}
// Store horizontal padding (bearingX).
s32 curr_bearingx = curr_face->glyph->metrics.horiBearingX >> 6;
@ -688,7 +700,8 @@ void ScalableFont::setInvisibleCharacters( const wchar_t *s )
core::dimension2d<u32> ScalableFont::getDimension(const wchar_t* text) const
{
GUIEngine::GlyphPageCreator* gp_creator = GUIEngine::getGlyphPageCreator();
return m_face->getDimension(text, m_font_settings);
/* GUIEngine::GlyphPageCreator* gp_creator = GUIEngine::getGlyphPageCreator();
if (m_type == T_NORMAL || T_BOLD) //lazy load char
{
@ -746,7 +759,7 @@ core::dimension2d<u32> ScalableFont::getDimension(const wchar_t* text) const
//Log::info("ScalableFont", "After: %d, %d", dim.Width, dim.Height);
return dim;
return dim;*/
}
// ----------------------------------------------------------------------------
@ -782,279 +795,7 @@ void ScalableFont::doDraw(const core::stringw& text,
const core::rect<s32>* clip,
FontCharCollector* charCollector)
{
if (!m_video_driver) return;
GUIEngine::GlyphPageCreator* gp_creator = GUIEngine::getGlyphPageCreator();
if (m_shadow)
{
m_shadow = false; // avoid infinite recursion
core::rect<s32> shadowpos = position;
shadowpos.LowerRightCorner.X += 2;
shadowpos.LowerRightCorner.Y += 2;
draw(text, shadowpos, m_shadow_color, hcenter, vcenter, clip);
m_shadow = true; // set back
}
core::position2d<s32> offset = position.UpperLeftCorner;
core::dimension2d<s32> text_dimension;
if (m_rtl || hcenter || vcenter || clip)
{
text_dimension = getDimension(text.c_str());
if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) / 2;
else if (m_rtl) offset.X += (position.getWidth() - text_dimension.Width);
if (vcenter) offset.Y += (position.getHeight() - text_dimension.Height) / 2;
if (clip)
{
core::rect<s32> clippedRect(offset, text_dimension);
clippedRect.clipAgainst(*clip);
if (!clippedRect.isValid()) return;
}
}
// ---- collect character locations
const unsigned int text_size = text.size();
core::array<s32> indices(text_size);
core::array<core::position2di> offsets(text_size);
std::vector<bool> fallback(text_size);
if (m_type == T_NORMAL || T_BOLD) //lazy load char, have to do this again
{ //because some text isn't drawn with getDimension
for (u32 i = 0; i < text_size; i++)
{
wchar_t c = text[i];
if (c == L'\r' || c == L'\n' || c == L' ' || c < 32) continue;
if (!GUIEngine::getFont()->hasThisChar(c))
gp_creator->insertChar(c);
if (charCollector != NULL && m_type == T_NORMAL && m_spritebank->getSprites()
[GUIEngine::getFont()->getSpriteNoFromChar(&c)].Frames[0].textureNumber
== m_spritebank->getTextureCount() - 1) //Prevent overwriting texture used by billboard text
{
Log::debug("ScalableFont::doDraw",
"Character used by billboard text is in the last "
"glyph page of normal font. Create a new glyph "
"page for new characters inserted later to prevent "
"it from being removed.");
GUIEngine::getFont()->forceNewPage();
}
}
if (gp_creator->getNewChar().size() > 0 && !m_is_hollow_copy && m_scale == 1)
{
Log::debug("ScalableFont::doDraw",
"New character(s) %s discoverd, perform lazy loading",
StringUtils::wideToUtf8(gp_creator->getNewChar()).c_str());
if (!GUIEngine::getFont()->lazyLoadChar())
Log::error("ScalableFont::lazyLoadChar",
"Can't insert new char into glyph pages.");
}
}
for (u32 i = 0; i < text_size; i++)
{
wchar_t c = text[i];
if (c == L'\r' || // Windows breaks
c == L'\n' ) // Unix breaks
{
if(c==L'\r' && text[i+1]==L'\n') c = text[++i];
offset.Y += (int)(m_max_height*m_scale);
offset.X = position.UpperLeftCorner.X;
if (hcenter)
offset.X += (position.getWidth() - text_dimension.Width) >> 1;
continue;
} // if lineBreak
bool use_fallback_font = false;
const SFontArea &area = getAreaFromCharacter(c, &use_fallback_font);
fallback[i] = use_fallback_font;
if (charCollector == NULL)
{
//Try to use ceil to make offset calculate correctly when m_scale is smaller than 1
s32 glyph_offset_x = (s32)((float) area.bearingx*
(fallback[i] ? m_scale*m_fallback_font_scale : m_scale));
s32 glyph_offset_y = (s32)ceil((float) area.offsety*
(fallback[i] ? m_scale*m_fallback_font_scale : m_scale));
offset.X += glyph_offset_x;
offset.Y += s32(glyph_offset_y + floor(m_type == T_DIGIT ? 20*m_scale : 0)); //Additional offset for digit text
offsets.push_back(offset);
offset.X -= glyph_offset_x;
offset.Y -= s32(glyph_offset_y + floor(m_type == T_DIGIT ? 20*m_scale : 0));
}
else //Billboard text specific
{
s32 glyph_offset_x = (s32)ceil((float) area.bearingx*
(fallback[i] ? m_scale*m_fallback_font_scale : m_scale));
s32 glyph_offset_y = (s32)ceil((float) area.offsety_bt*
(fallback[i] ? m_scale*m_fallback_font_scale : m_scale));
offset.X += glyph_offset_x;
offset.Y += s32(glyph_offset_y + floor(m_type == T_DIGIT ? 20*m_scale : 0)); //Additional offset for digit text
offsets.push_back(offset);
offset.X -= glyph_offset_x;
offset.Y -= s32(glyph_offset_y + floor(m_type == T_DIGIT ? 20*m_scale : 0));
}
// Invisible character. add something to the array anyway so that
// indices from the various arrays remain in sync
indices.push_back(m_invisible.findFirst(c) < 0 ? area.spriteno : -1);
offset.X += getCharWidth(area, fallback[i]);
} // for i<text_size
// ---- do the actual rendering
const int indiceAmount = indices.size();
core::array< SGUISprite >& sprites = m_spritebank->getSprites();
core::array< core::rect<s32> >& positions = m_spritebank->getPositions();
core::array< SGUISprite >* fallback_sprites;
core::array< core::rect<s32> >* fallback_positions;
if (m_fallback_font != NULL)
{
fallback_sprites = &m_fallback_font->m_spritebank->getSprites();
fallback_positions = &m_fallback_font->m_spritebank->getPositions();
}
else
{
fallback_sprites = NULL;
fallback_positions = NULL;
}
const int spriteAmount = sprites.size();
if (m_black_border && charCollector == NULL)
{ //Draw black border first, to make it behind the real character
//which make script language display better
video::SColor black(color.getAlpha(),0,0,0);
for (int n = 0; n < indiceAmount; n++)
{
const int spriteID = indices[n];
if (!fallback[n] && (spriteID < 0 || spriteID >= spriteAmount)) continue;
if (indices[n] == -1) continue;
const int texID = (fallback[n] ?
(*fallback_sprites)[spriteID].Frames[0].textureNumber :
sprites[spriteID].Frames[0].textureNumber);
core::rect<s32> source = (fallback[n] ?
(*fallback_positions)[(*fallback_sprites)[spriteID].Frames[0].rectNumber] :
positions[sprites[spriteID].Frames[0].rectNumber]);
core::dimension2d<s32> size = source.getSize();
float scale = (fallback[n] ? m_scale*m_fallback_font_scale : m_scale);
size.Width = (int)(size.Width * scale);
size.Height = (int)(size.Height * scale);
core::rect<s32> dest(offsets[n], size);
video::ITexture* texture = (fallback[n] ?
m_fallback_font->m_spritebank->getTexture(texID) :
m_spritebank->getTexture(texID) );
for (int x_delta = -2; x_delta <= 2; x_delta++)
{
for (int y_delta = -2; y_delta <= 2; y_delta++)
{
if (x_delta == 0 || y_delta == 0) continue;
draw2DImage(texture,
dest + core::position2d<s32>(x_delta, y_delta),
source,
clip,
black, true);
}
}
}
}
for (int n = 0; n < indiceAmount; n++)
{
const int spriteID = indices[n];
if (!fallback[n] && (spriteID < 0 || spriteID >= spriteAmount)) continue;
if (indices[n] == -1) continue;
//assert(sprites[spriteID].Frames.size() > 0);
const int texID = (fallback[n] ?
(*fallback_sprites)[spriteID].Frames[0].textureNumber :
sprites[spriteID].Frames[0].textureNumber);
core::rect<s32> source = (fallback[n] ?
(*fallback_positions)[(*fallback_sprites)[spriteID].Frames[0].rectNumber] :
positions[sprites[spriteID].Frames[0].rectNumber]);
core::dimension2d<s32> size = source.getSize();
float scale = (fallback[n] ? m_scale*m_fallback_font_scale : m_scale);
size.Width = (int)(size.Width * scale);
size.Height = (int)(size.Height * scale);
core::rect<s32> dest(offsets[n], size);
video::ITexture* texture = (fallback[n] ?
m_fallback_font->m_spritebank->getTexture(texID) :
m_spritebank->getTexture(texID) );
/*
if (fallback[n])
{
Log::info("ScalableFont", "Using fallback font %s; source area is %d, %d; size %d, %d; dest = %d, %d",
core::stringc(texture->getName()).c_str(), source.UpperLeftCorner.X, source.UpperLeftCorner.Y,
source.getWidth(), source.getHeight(), offsets[n].X, offsets[n].Y);
}
*/
#ifdef FONT_DEBUG
GL32_draw2DRectangle(video::SColor(255, 255,0,0), dest,clip);
#endif
if (fallback[n] || m_type == T_BOLD)
{
video::SColor top = GUIEngine::getSkin()->getColor("font::top");
video::SColor bottom = GUIEngine::getSkin()->getColor("font::bottom");
top.setAlpha(color.getAlpha());
bottom.setAlpha(color.getAlpha());
video::SColor title_colors[] = {top, bottom, top, bottom};
if (charCollector != NULL)
{
charCollector->collectChar(texture,
dest,
source,
title_colors);
}
else
{
draw2DImage(texture,
dest,
source,
clip,
title_colors, true);
}
}
else
{
if (charCollector != NULL)
{
video::SColor colors[] = { color, color, color, color };
charCollector->collectChar(texture,
dest,
source,
colors);
}
else
{
draw2DImage(texture,
dest,
source,
clip,
color, true);
}
}
}
m_face->render(text,position,color,hcenter,vcenter,clip,m_font_settings,NULL);
}
// ----------------------------------------------------------------------------

View File

@ -26,6 +26,9 @@
#include <string>
#include <set>
class FontSettings;
class FontWithFace;
namespace irr
{
@ -71,6 +74,11 @@ public:
float m_fallback_font_scale;
int m_fallback_kerning_width;
ScalableFont(FontWithFace* face);
FontWithFace* m_face;
FontSettings* m_font_settings;
FontSettings* getFontSettings() { return m_font_settings; }
const FontSettings* getFontSettings() const { return m_font_settings; }
ScalableFont(IGUIEnvironment* env, GUIEngine::TTFLoadingType type);
virtual ~ScalableFont();

View File

@ -15,6 +15,7 @@
// 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/regular_face.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/scalable_font.hpp"
#include "guiengine/widgets/dynamic_ribbon_widget.hpp"
@ -46,8 +47,7 @@ DynamicRibbonWidget::DynamicRibbonWidget(const bool combo, const bool multi_row)
m_supports_multiplayer = true;
m_scrolling_enabled = true;
m_animated_contents = false;
// Don't initialize m_font here to make lazy loading characters work
m_font = NULL;
m_font = new ScalableFont(font_manager->getFont<RegularFace>());
// by default, set all players to have no selection in this ribbon
for (unsigned int n=0; n<MAX_PLAYER_COUNT; n++)
@ -64,6 +64,7 @@ DynamicRibbonWidget::DynamicRibbonWidget(const bool combo, const bool multi_row)
DynamicRibbonWidget::~DynamicRibbonWidget()
{
m_font->drop();
m_font = NULL;
if (m_animated_contents)
{
GUIEngine::needsUpdate.remove(this);
@ -386,12 +387,11 @@ void DynamicRibbonWidget::buildInternalStructure()
ribbon->m_properties[PROP_ID] = name.str();
ribbon->m_event_handler = this;
m_font = GUIEngine::getFont()->getHollowCopy();
// calculate font size
if (m_col_amount > 0)
{
m_font->setScale(GUIEngine::getFont()->getScale() *
m_font->getFontSettings()->setScale(GUIEngine::getFont()
->getFontSettings()->getScale() *
getFontScale((ribbon->m_w / m_col_amount) - 30));
}

View File

@ -25,6 +25,7 @@
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
#include "font/regular_face.hpp"
#include "graphics/camera.hpp"
#include "graphics/explosion.hpp"
#include "graphics/irr_driver.hpp"
@ -2802,17 +2803,12 @@ btQuaternion Kart::getVisualRotation() const
*/
void Kart::setOnScreenText(const wchar_t *text)
{
core::dimension2d<u32> textsize = GUIEngine::getFont()->getDimension(text);
// FIXME: Titlefont is the only font guaranteed to be loaded if STK
// is started without splash screen (since "Loading" is shown even in this
// case). A smaller font would be better
RegularFace* regular_face = font_manager->getFont<RegularFace>();
core::dimension2d<u32> textsize = regular_face->getDimension(text);
if (CVS->isGLSL())
{
gui::ScalableFont* font = GUIEngine::getFont() ? GUIEngine::getFont()
: GUIEngine::getTitleFont();
new STKTextBillboard(text, font,
new STKTextBillboard(text, regular_face,
GUIEngine::getSkin()->getColor("font::bottom"),
GUIEngine::getSkin()->getColor("font::top"),
getNode(), irr_driver->getSceneManager(), -1,

View File

@ -150,6 +150,7 @@
#include "config/player_profile.hpp"
#include "config/stk_config.hpp"
#include "config/user_config.hpp"
#include "font/font_manager.hpp"
#include "graphics/camera.hpp"
#include "graphics/camera_debug.hpp"
#include "graphics/central_settings.hpp"
@ -1297,6 +1298,8 @@ void initRest()
exit(0);
}
font_manager = new FontManager();
font_manager->loadFonts();
GUIEngine::init(device, driver, StateManager::get());
// This only initialises the non-network part of the addons manager. The
@ -1767,6 +1770,7 @@ static void cleanSuperTuxKart()
if(unlock_manager) delete unlock_manager;
Online::ProfileManager::destroy();
GUIEngine::DialogQueue::deallocate();
if(font_manager) delete font_manager;
// Now finish shutting down objects which a separate thread. The
// RequestManager has been signaled to shut down as early as possible,

View File

@ -19,6 +19,7 @@
#include "script_track.hpp"
#include "animations/three_d_animation.hpp"
#include "font/regular_face.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/stk_text_billboard.hpp"
#include "guiengine/scalable_font.hpp"
@ -88,17 +89,17 @@ namespace Scripting
void createTextBillboard(std::string* text, SimpleVec3* location)
{
core::stringw wtext = StringUtils::utf8ToWide(*text);
core::dimension2d<u32> textsize = GUIEngine::getHighresDigitFont()
->getDimension(wtext.c_str());
assert(GUIEngine::getHighresDigitFont() != NULL);
//core::dimension2d<u32> textsize = GUIEngine::getHighresDigitFont()
//->getDimension(wtext.c_str());
RegularFace* regular_face = font_manager->getFont<RegularFace>();
core::dimension2d<u32> textsize = regular_face->getDimension(wtext.c_str());
//assert(GUIEngine::getHighresDigitFont() != NULL);
core::vector3df xyz(location->getX(), location->getY(), location->getZ());
if (CVS->isGLSL())
{
gui::ScalableFont* font = GUIEngine::getHighresDigitFont();
STKTextBillboard* tb = new STKTextBillboard(wtext.c_str(), font,
STKTextBillboard* tb = new STKTextBillboard(wtext.c_str(), regular_face,
GUIEngine::getSkin()->getColor("font::bottom"),
GUIEngine::getSkin()->getColor("font::top"),
irr_driver->getSceneManager()->getRootSceneNode(),

View File

@ -331,12 +331,7 @@ void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, con
translations = new Translations();
//Reload fonts for new translation
GUIEngine::cleanHollowCopyFont();
GUIEngine::getTitleFont()->recreateFromLanguage();
GUIEngine::getFont()->recreateFromLanguage();
GUIEngine::reloadHollowCopyFont(GUIEngine::getFont());
// Reload fonts for new translation
GUIEngine::getStateManager()->hardResetAndGoToScreen<MainMenuScreen>();
GUIEngine::getFont()->updateRTL();