Initial work on new font rendering engine
This commit is contained in:
parent
9fd6fd2673
commit
05419cd555
@ -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>
|
||||
|
BIN
data/ttf/NotoSansCJK-Bold.ttc
Normal file
BIN
data/ttf/NotoSansCJK-Bold.ttc
Normal file
Binary file not shown.
BIN
data/ttf/NotoSansCJK-Regular.ttc
Normal file
BIN
data/ttf/NotoSansCJK-Regular.ttc
Normal file
Binary file not shown.
Binary file not shown.
@ -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");
|
||||
|
@ -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
59
src/font/bold_face.cpp
Normal 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
50
src/font/bold_face.hpp
Normal 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
60
src/font/font_manager.cpp
Normal 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
75
src/font/font_manager.hpp
Normal 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 */
|
79
src/font/font_settings.hpp
Normal file
79
src/font/font_settings.hpp
Normal 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
666
src/font/font_with_face.cpp
Normal 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
240
src/font/font_with_face.hpp
Normal 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
56
src/font/regular_face.cpp
Normal 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
50
src/font/regular_face.hpp
Normal 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 */
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user