From 09101e923cd1cdda03ad31331d3ed2536f0a992f Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 7 Nov 2015 16:24:31 +0800 Subject: [PATCH] More clean up freetype-related code 1. Remove useless get font properties class 2. Pre-set the face dpi 3. Make all freetype related code to have a better coding style --- src/guiengine/ft_environment.cpp | 97 +++-- src/guiengine/ft_environment.hpp | 32 +- src/guiengine/get_font_properties.cpp | 127 ------- src/guiengine/get_font_properties.hpp | 76 ---- src/guiengine/glyph_page_creator.cpp | 94 +++-- src/guiengine/glyph_page_creator.hpp | 29 +- src/guiengine/scalable_font.cpp | 516 +++++++++++++++----------- src/guiengine/scalable_font.hpp | 105 +++--- 8 files changed, 544 insertions(+), 532 deletions(-) delete mode 100644 src/guiengine/get_font_properties.cpp delete mode 100644 src/guiengine/get_font_properties.hpp diff --git a/src/guiengine/ft_environment.cpp b/src/guiengine/ft_environment.cpp index 82072bee0..c3f3eb96b 100644 --- a/src/guiengine/ft_environment.cpp +++ b/src/guiengine/ft_environment.cpp @@ -15,85 +15,130 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include "graphics/irr_driver.hpp" #include "guiengine/ft_environment.hpp" -#include "guiengine/get_font_properties.hpp" #include "io/file_manager.hpp" #include "utils/log.hpp" +#include + using namespace gui; namespace GUIEngine { +// ---------------------------------------------------------------------------- + FTEnvironment::FTEnvironment() { - FTEnvironment::ft_err += FT_Init_FreeType(&(FTEnvironment::ft_lib)); + m_ft_err += FT_Init_FreeType(&(m_ft_lib)); loadFont(); } +// ---------------------------------------------------------------------------- + FTEnvironment::~FTEnvironment() { for (int i = 0; i < F_COUNT; ++i) - FTEnvironment::ft_err += FT_Done_Face((FTEnvironment::ft_face[i])); + m_ft_err += FT_Done_Face(m_ft_face[i]); - FTEnvironment::ft_err += FT_Done_FreeType(FTEnvironment::ft_lib); + m_ft_err += FT_Done_FreeType(m_ft_lib); - if (FTEnvironment::ft_err > 0) + if (m_ft_err > 0) Log::error("Freetype Environment", "Can't destroy all fonts."); else Log::info("Freetype Environment", "Successfully destroy all fonts."); } +// ---------------------------------------------------------------------------- + +FT_Face FTEnvironment::getFace(const FontUse font) +{ + return m_ft_face[font]; +} + +// ---------------------------------------------------------------------------- + void FTEnvironment::loadFont() { - FTEnvironment::ft_err += FT_New_Face(FTEnvironment::ft_lib, (file_manager->getAssetChecked + m_ft_err += FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, "Ubuntu-R.ttf", true)).c_str(), - 0, &(FTEnvironment::ft_face[F_DEFAULT])); + 0, &m_ft_face[F_DEFAULT]); - FTEnvironment::ft_err += FT_New_Face(FTEnvironment::ft_lib, (file_manager->getAssetChecked + m_ft_err += FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, "FreeSans.ttf",true)).c_str(), - 0, &(FTEnvironment::ft_face[F_DEFAULT_FALLBACK])); + 0, &m_ft_face[F_DEFAULT_FALLBACK]); - FTEnvironment::ft_err += FT_New_Face(FTEnvironment::ft_lib, (file_manager->getAssetChecked + m_ft_err += FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, "wqy-microhei.ttf",true)).c_str(), - 0, &(FTEnvironment::ft_face[F_CJK])); + 0, &m_ft_face[F_CJK]); - FTEnvironment::ft_err += FT_New_Face(FTEnvironment::ft_lib, (file_manager->getAssetChecked + m_ft_err += FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, "NotoNaskhArabicUI-Bold.ttf",true)).c_str(), - 0, &(FTEnvironment::ft_face[F_AR])); + 0, &m_ft_face[F_AR]); - FTEnvironment::ft_err += FT_New_Face(FTEnvironment::ft_lib, (file_manager->getAssetChecked + m_ft_err += FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, "Ubuntu-B.ttf", true)).c_str(), - 0, &(FTEnvironment::ft_face[F_BOLD])); + 0, &m_ft_face[F_BOLD]); - FTEnvironment::ft_err += FT_New_Face(FTEnvironment::ft_lib, (file_manager->getAssetChecked + m_ft_err += FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, "FreeSansBold.ttf", true)).c_str(), - 0, &(FTEnvironment::ft_face[F_BOLD_FALLBACK])); + 0, &m_ft_face[F_BOLD_FALLBACK]); - FTEnvironment::ft_err += FT_New_Face(FTEnvironment::ft_lib, (file_manager->getAssetChecked + m_ft_err += FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, "SigmarOne.otf",true)).c_str(), - 0, &(FTEnvironment::ft_face[F_DIGIT])); + 0, &m_ft_face[F_DIGIT]); //Set charmap for (int h = 0; h < F_COUNT; ++h) { - for (int i = 0; i < FTEnvironment::ft_face[h]->num_charmaps; ++i) + for (int i = 0; i < m_ft_face[h]->num_charmaps; ++i) { - FT_UShort pid = FTEnvironment::ft_face[h]->charmaps[i]->platform_id; - FT_UShort eid = FTEnvironment::ft_face[h]->charmaps[i]->encoding_id; + FT_UShort pid = m_ft_face[h]->charmaps[i]->platform_id; + FT_UShort eid = m_ft_face[h]->charmaps[i]->encoding_id; if (((pid == 0) && (eid == 3)) || ((pid == 3) && (eid == 1))) - FTEnvironment::ft_err += FT_Set_Charmap(FTEnvironment::ft_face[h], FTEnvironment::ft_face[h]->charmaps[i]); + m_ft_err += FT_Set_Charmap(m_ft_face[h], m_ft_face[h]->charmaps[i]); } } - if (FTEnvironment::ft_err > 0) + // Set 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 + // normal text will range from 0.2, in 640x* resolutions (won't scale + // below that) to 0.4, in 1024x* resolutions, and linearly up + const s32 screen_width = irr_driver->getFrameSize().Width; + const s32 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); + + const u32 normal_dpi = ((0.7f + 0.2f*scale)*27); + const u32 title_dpi = ((0.2f + 0.2f*scale)*120); + const u32 digit_dpi = ((0.7f + 0.2f*scale)*40); + + Log::info("Freetype Environment", "DPI for Normal Font is %d.", normal_dpi); + Log::info("Freetype Environment", "DPI for Title Font is %d.", title_dpi); + Log::info("Freetype Environment", "DPI for Digit Font is %d.", digit_dpi); + + m_ft_err += FT_Set_Pixel_Sizes(m_ft_face[F_DEFAULT], 0, normal_dpi); + m_ft_err += FT_Set_Pixel_Sizes(m_ft_face[F_DEFAULT_FALLBACK], 0, normal_dpi); + m_ft_err += FT_Set_Pixel_Sizes(m_ft_face[F_CJK], 0, normal_dpi); + m_ft_err += FT_Set_Pixel_Sizes(m_ft_face[F_AR], 0, normal_dpi); + m_ft_err += FT_Set_Pixel_Sizes(m_ft_face[F_BOLD], 0, title_dpi); + m_ft_err += FT_Set_Pixel_Sizes(m_ft_face[F_BOLD_FALLBACK], 0, title_dpi); + m_ft_err += FT_Set_Pixel_Sizes(m_ft_face[F_DIGIT], 0, digit_dpi); + + if (m_ft_err > 0) Log::error("Freetype Environment", "Can't load all fonts."); else Log::info("Freetype Environment", "Successfully loaded all fonts."); } -FT_Library FTEnvironment::ft_lib = NULL; -FT_Error FTEnvironment::ft_err = 0; +FT_Library FTEnvironment::m_ft_lib = NULL; +FT_Error FTEnvironment::m_ft_err = 0; } // guiengine diff --git a/src/guiengine/ft_environment.hpp b/src/guiengine/ft_environment.hpp index cea22930b..fbcd5364d 100644 --- a/src/guiengine/ft_environment.hpp +++ b/src/guiengine/ft_environment.hpp @@ -15,8 +15,10 @@ // 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_FT_ENVIRONMENT_HPP +#define HEADER_FT_ENVIRONMENT_HPP + #include -#include "guiengine/get_font_properties.hpp" #include FT_FREETYPE_H #include "utils/leak_check.hpp" @@ -26,6 +28,23 @@ */ namespace GUIEngine { + +enum FontUse +{ + F_DEFAULT = 0, + F_DEFAULT_FALLBACK = 1, + F_CJK = 2, + F_AR = 3, + F_LAST_REGULAR_FONT = F_AR, + + F_BOLD = 4, + F_BOLD_FALLBACK = 5, + F_DIGIT = 6, + F_COUNT = 7 +}; + +enum TTFLoadingType {T_NORMAL, T_DIGIT, T_BOLD}; + /** * \brief Initialize a freetype environment with a single freetype library. */ @@ -37,15 +56,20 @@ namespace GUIEngine FTEnvironment(); ~FTEnvironment(); - FT_Face ft_face[irr::gui::F_COUNT]; + + /** Get a face with a suitable font type. + */ + FT_Face getFace(const FontUse font); private: /** Load font face into memory, but don't create glyph yet. */ void loadFont(); - static FT_Library ft_lib; - static FT_Error ft_err; + FT_Face m_ft_face[F_COUNT]; + static FT_Library m_ft_lib; + static FT_Error m_ft_err; }; } // guiengine +#endif // HEADER_FT_ENVIRONMENT_HPP diff --git a/src/guiengine/get_font_properties.cpp b/src/guiengine/get_font_properties.cpp deleted file mode 100644 index 8aa538a4d..000000000 --- a/src/guiengine/get_font_properties.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2015 Ben Au -// -// 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 "graphics/irr_driver.hpp" -#include "guiengine/get_font_properties.hpp" -#include "utils/translation.hpp" - -#include -#include -#include - -namespace irr -{ -namespace gui -{ - -getFontProperties::getFontProperties (const core::stringc &langname, TTFLoadingType type, FontUse &fu) -{ - findScale(); - - switch(type) - { - case T_NORMAL: - loadChar(langname, fu, normal_text_scale); - break; - case T_DIGIT: - fu = F_DIGIT; - loadNumber(normal_text_scale); - break; - case T_BOLD: - fu = F_BOLD; - loadBoldChar(title_text_scale); - break; - } -} - -void getFontProperties::findScale() -{ - //Borrowed from engine.cpp: - // 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 - // normal 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); - - normal_text_scale = 0.7f + 0.2f*scale; - title_text_scale = 0.2f + 0.2f*scale; -} - -void getFontProperties::loadChar(const core::stringc langname, FontUse& fu, float scale) -{ - fu = F_DEFAULT; //Default font file - - for (int i = 32; i < 128; ++i) - usedchar.insert((wchar_t)i); //Include basic Latin too - usedchar.insert((wchar_t)160); //Non-breaking space - usedchar.insert((wchar_t)215); //Used on resolution selection screen (X). - - //There's specific handling for some language, we may need more after more translation are added or problems found out. - //if (langname == "el" || langname == "fr" || langname == "gd") - size = (int)(27*scale); //Lower scale for them as they're space-consuming. - //else - // size = (int)(29*scale); //Set to default size -} - -void getFontProperties::loadNumber(float scale) -{ - size = (int)(40*scale); //Set default size for Big Digit Text - for (int i = 46; i < 59; ++i) //Include chars used by timer and laps count only - usedchar.insert((wchar_t)i); //FIXME have to load 46 " . " to make 47 " / " display correctly, why? -} - -void getFontProperties::loadBoldChar(float scale) -{ - size = (int)(120*scale); //Set default size for Bold Text - - usedchar = translations->getCurrentAllChar(); //Loading unique characters - for (int i = 65; i < 256; ++i) - usedchar.insert((wchar_t)i); //Include basic Latin too, starting from A (char code 65) - - setlocale(LC_ALL, "en_US.UTF8"); - std::set::iterator it = usedchar.begin(); - while (it != usedchar.end()) - { - //Only use all capital letter for bold char with latin (<640 of char code). - //Remove all characters (>char code 8191) not used by the title - if (((iswlower((wchar_t)*it) || !iswalpha((wchar_t)*it)) && *it < 640) || *it > 8191) - it = usedchar.erase(it); - else - ++it; - } - - //Final hack to make stk display title properly - for (int i = 32; i < 65; ++i) - usedchar.insert((wchar_t)i); //Include basic symbol (from space (char code 32) to @(char code 64)) - usedchar.insert((wchar_t)160); //Non-breaking space - - //Remove Ordinal indicator (char code 170 and 186) - usedchar.erase((wchar_t)170); - usedchar.erase((wchar_t)186); - - usedchar.erase((wchar_t)304); //Remove Capital I-dotted (char code 304) with using "I" altogether. -} - -} // end namespace gui -} // end namespace irr diff --git a/src/guiengine/get_font_properties.hpp b/src/guiengine/get_font_properties.hpp deleted file mode 100644 index 3f3b2d171..000000000 --- a/src/guiengine/get_font_properties.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2015 Ben Au -// -// 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_GET_FONT_PROPERTIES_HPP -#define HEADER_GET_FONT_PROPERTIES_HPP - -#include -#include - -namespace irr -{ -namespace gui -{ - -enum FontUse -{ - F_DEFAULT = 0, - F_DEFAULT_FALLBACK = 1, - F_CJK = 2, - F_AR = 3, - F_LAST_REGULAR_FONT = F_AR, - - F_BOLD = 4, - F_BOLD_FALLBACK = 5, - F_DIGIT = 6, - F_COUNT = 7 -}; - -enum TTFLoadingType {T_NORMAL, T_DIGIT, T_BOLD}; - -class getFontProperties -{ -public: - - /** Get properties used for load characters with ttf. - * \param langname The current gui language. - * \param type Current loaded font type (normal, bold or digit font). - * \param fu Give a suitable font file. - * \return Font dpi and characters required to preload for current gui language. - */ - getFontProperties (const core::stringc &langname, TTFLoadingType type, FontUse &fu); - - unsigned short size; - std::set usedchar; - -private: - void loadChar(core::stringc, FontUse&, float); - void loadNumber(float); - void loadBoldChar(float); - - /** Find a suitable font scale base on current resolution - */ - void findScale(); - - float normal_text_scale; - float title_text_scale; -}; - -} // end namespace gui -} // end namespace irr - -#endif diff --git a/src/guiengine/glyph_page_creator.cpp b/src/guiengine/glyph_page_creator.cpp index aeeadf3f8..bd5dba005 100644 --- a/src/guiengine/glyph_page_creator.cpp +++ b/src/guiengine/glyph_page_creator.cpp @@ -27,25 +27,33 @@ namespace GUIEngine { +// ---------------------------------------------------------------------------- + GlyphPageCreator::GlyphPageCreator() { - page = GUIEngine::getDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(512, 512)); - image = 0; - newchar.clear(); + m_page = GUIEngine::getDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(512, 512)); + m_image = 0; } +// ---------------------------------------------------------------------------- + GlyphPageCreator::~GlyphPageCreator() { clearGlyphPage(); - page->drop(); - page = 0; + clearNewCharHolder(); + m_page->drop(); + m_page = 0; } +// ---------------------------------------------------------------------------- + void GlyphPageCreator::dumpGlyphPage(const core::stringc fn) { - GUIEngine::getDriver()->writeImageToFile(page, fn + ".png"); + GUIEngine::getDriver()->writeImageToFile(m_page, fn + ".png"); } +// ---------------------------------------------------------------------------- + bool GlyphPageCreator::checkEnoughSpace(FT_Bitmap bits) { core::dimension2du d(bits.width + 1, bits.rows + 1); @@ -53,39 +61,63 @@ bool GlyphPageCreator::checkEnoughSpace(FT_Bitmap bits) texture_size = d.getOptimalSize(!(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NPOT)), !(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NSQUARE)), true, 0); - if ((used_width + texture_size.Width > 512 && used_height + temp_height + texture_size.Height > 512) - || used_height + texture_size.Height > 512) + if ((m_used_width + texture_size.Width > 512 && m_used_height + m_temp_height + texture_size.Height > 512) + || m_used_height + texture_size.Height > 512) return false; return true; } +// ---------------------------------------------------------------------------- + +void GlyphPageCreator::clearNewCharHolder() +{ + m_new_char_holder.clear(); +} + +// ---------------------------------------------------------------------------- + void GlyphPageCreator::clearGlyphPage() { - used_width = 0; - temp_height = 0; - used_height = 0; + m_used_width = 0; + m_temp_height = 0; + m_used_height = 0; } +// ---------------------------------------------------------------------------- + void GlyphPageCreator::createNewGlyphPage() { //Clean the current glyph page by filling it with transparent content - page->fill(video::SColor(0, 255, 255, 255)); + m_page->fill(video::SColor(0, 255, 255, 255)); } +// ---------------------------------------------------------------------------- + video::IImage* GlyphPageCreator::getPage() { - return page; + return m_page; } +// ---------------------------------------------------------------------------- + core::stringw GlyphPageCreator::getNewChar() { core::stringw c; - for (std::set::iterator it = newchar.begin(); it != newchar.end(); ++it) + for (std::set::iterator it = m_new_char_holder.begin(); it != m_new_char_holder.end(); ++it) c += *it; return c; } +// ---------------------------------------------------------------------------- + +void GlyphPageCreator::insertChar(const wchar_t c) +{ + m_new_char_holder.insert(c); +} + +// ---------------------------------------------------------------------------- + bool GlyphPageCreator::insertGlyph(FT_Bitmap bits, core::rect& rect) { core::dimension2du d(bits.width + 1, bits.rows + 1); @@ -98,13 +130,13 @@ bool GlyphPageCreator::insertGlyph(FT_Bitmap bits, core::rect& rect) // Create our blank image. texture_size = d.getOptimalSize(!(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NPOT)), !(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NSQUARE)), true, 0); - image = GUIEngine::getDriver()->createImage(video::ECF_A8R8G8B8, texture_size); - image->fill(video::SColor(0, 255, 255, 255)); + m_image = GUIEngine::getDriver()->createImage(video::ECF_A8R8G8B8, texture_size); + m_image->fill(video::SColor(0, 255, 255, 255)); // Load the grayscale data in. const float gray_count = static_cast(bits.num_grays); - const u32 image_pitch = image->getPitch() / sizeof(u32); - u32* image_data = (u32*)image->lock(); + const u32 image_pitch = m_image->getPitch() / sizeof(u32); + u32* image_data = (u32*)m_image->lock(); u8* glyph_data = bits.buffer; for (u32 y = 0; y < (unsigned)bits.rows; ++y) { @@ -116,37 +148,37 @@ bool GlyphPageCreator::insertGlyph(FT_Bitmap bits, core::rect& rect) } glyph_data += bits.pitch; } - image->unlock(); + m_image->unlock(); break; } default: return false; } - if (!image) + if (!m_image) return false; //Done creating a single glyph, now copy to the glyph page... //Determine the linebreak location - if (used_width + texture_size.Width > 512) + if (m_used_width + texture_size.Width > 512) { - used_width = 0; - used_height += temp_height; - temp_height = 0; + m_used_width = 0; + m_used_height += m_temp_height; + m_temp_height = 0; } //Copy now - image->copyTo(page, core::position2di(used_width, used_height)); + m_image->copyTo(m_page, core::position2di(m_used_width, m_used_height)); //Store the rectangle of current glyph - rect = core::rect (used_width, used_height, used_width + bits.width, used_height + bits.rows); + rect = core::rect (m_used_width, m_used_height, m_used_width + bits.width, m_used_height + bits.rows); - image->drop(); - image = 0; + m_image->drop(); + m_image = 0; //Store used area - used_width += texture_size.Width; - if (temp_height < texture_size.Height) - temp_height = texture_size.Height; + m_used_width += texture_size.Width; + if (m_temp_height < texture_size.Height) + m_temp_height = texture_size.Height; return true; } diff --git a/src/guiengine/glyph_page_creator.hpp b/src/guiengine/glyph_page_creator.hpp index 58a028987..a07ee67d1 100644 --- a/src/guiengine/glyph_page_creator.hpp +++ b/src/guiengine/glyph_page_creator.hpp @@ -58,6 +58,10 @@ namespace GUIEngine */ void clearGlyphPage(); + /** Reset characters holder for lazy loading char. + */ + void clearNewCharHolder(); + /** Clear (fill it with transparent content) the current glyph page. */ void createNewGlyphPage(); @@ -67,11 +71,16 @@ namespace GUIEngine */ video::IImage* getPage(); - /** Used to get the string of new characters inside set newchar. (Mainly for debug) + /** Used to get the string of new characters inside set m_new_char_holder for lazy char loading. * \return string of wild-character. */ core::stringw getNewChar(); + /** Used to insert a single new character into glyph page used for lazy char loading. + * \param c A new character. + */ + void insertChar(const wchar_t c); + /** Used to insert a single glyph bitmap into the glyph page * \param bits The Glyph bitmap inputted. * \param rect Give the rectangle of the glyph on the page. @@ -79,22 +88,22 @@ namespace GUIEngine */ bool insertGlyph(FT_Bitmap bits, core::rect& rect); - /** A temporary holder stored new char to be inserted. - */ - std::set newchar; - private: /** A temporary storage for a single glyph. */ - video::IImage* image; + video::IImage* m_image; + + /** A temporary holder stored new char to be inserted. + */ + std::set m_new_char_holder; /** A full glyph page. */ - video::IImage* page; + video::IImage* m_page; - u32 temp_height; - u32 used_width; - u32 used_height; + u32 m_temp_height; + u32 m_used_width; + u32 m_used_height; }; } // guiengine diff --git a/src/guiengine/scalable_font.cpp b/src/guiengine/scalable_font.cpp index 5ca3f0cfd..7132e79b2 100644 --- a/src/guiengine/scalable_font.cpp +++ b/src/guiengine/scalable_font.cpp @@ -1,6 +1,20 @@ -// Copyright (C) 2002-2015 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// Copyright (C) 2015 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 "guiengine/scalable_font.hpp" @@ -20,15 +34,18 @@ #include #include +using namespace GUIEngine; + namespace irr { namespace gui { -//! constructor +// ---------------------------------------------------------------------------- + ScalableFont::ScalableFont(IGUIEnvironment *env, TTFLoadingType type) - : Driver(0), SpriteBank(0), Environment(env), WrongCharacter(0), - MaxHeight(0), GlobalKerningWidth(0), GlobalKerningHeight(0) + : 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) { #ifdef _DEBUG setDebugName("ScalableFont"); @@ -42,95 +59,73 @@ ScalableFont::ScalableFont(IGUIEnvironment *env, TTFLoadingType type) m_black_border = false; m_type = type; m_font_use = (FontUse)0; - m_dpi = 0; m_shadow = false; m_mono_space_digits = false; m_rtl = translations->isRTLLanguage(); - if (Environment) + if (m_gui_env) { // don't grab environment, to avoid circular references - Driver = Environment->getVideoDriver(); + m_video_driver = m_gui_env->getVideoDriver(); - SpriteBank = Environment->addEmptySpriteBank((std::to_string(type)).c_str()); - if (SpriteBank) - SpriteBank->grab(); + m_spritebank = m_gui_env->addEmptySpriteBank((std::to_string(type)).c_str()); + if (m_spritebank) + m_spritebank->grab(); } - if (Driver) - Driver->grab(); + if (m_video_driver) + m_video_driver->grab(); - setInvisibleCharacters ( L" " ); + setInvisibleCharacters(L" "); if (!loadTTF()) { Log::fatal("ScalableFont", "Loading TTF font failed"); } - assert(Areas.size() > 0); + assert(m_areas.size() > 0); } -void ScalableFont::recreateFromLanguage() -{ - //Clean previous font data - SpriteBank->clear(); - Areas.clear(); - CharacterMap.clear(); - WrongCharacter = 0; - MaxHeight = 0; - GlobalKerningWidth = 0; - GlobalKerningHeight = 0; +// ---------------------------------------------------------------------------- - //Set to default scale to reload font - m_scale = 1; - //Reload - if (!loadTTF()) - { - Log::fatal("ScalableFont", "Recreation of TTF font failed"); - } -} - -//! destructor ScalableFont::~ScalableFont() { if (!m_is_hollow_copy) { - if (Driver) Driver->drop(); - if (SpriteBank) SpriteBank->drop(); + if (m_video_driver) m_video_driver->drop(); + if (m_spritebank) m_spritebank->drop(); } } -void ScalableFont::updateRTL() -{ - m_rtl = translations->isRTLLanguage(); -} +// ---------------------------------------------------------------------------- -void ScalableFont::setShadow(const irr::video::SColor &col) -{ - m_shadow = true; - m_shadow_color = col; -} - -//! loads a font from a TTF file bool ScalableFont::loadTTF() { - if (!SpriteBank) + if (!m_spritebank) { Log::error("ScalableFont::loadTTF", "SpriteBank is NULL!!"); return false; } + switch(m_type) + { + case T_NORMAL: + m_font_use = F_DEFAULT; + break; + case T_DIGIT: + m_font_use = F_DIGIT; + break; + case T_BOLD: + m_font_use = F_BOLD; + break; + } + GUIEngine::GlyphPageCreator* gp_creator = GUIEngine::getGlyphPageCreator(); //Initialize glyph slot FT_GlyphSlot slot; FT_Error err; - //Determine which font(face) and size to load, - //also get all used char base on current language settings - getFontProperties cur_prop(translations->getCurrentLanguageNameCode().c_str(), m_type, m_font_use); - m_dpi = cur_prop.size; - std::vector offset; std::vector bearingx; std::vector advance; @@ -140,14 +135,15 @@ bool ScalableFont::loadTTF() s32 curr_maxheight = 0; s32 t; u32 texno = 0; - SpriteBank->addTexture(NULL); + m_spritebank->addTexture(NULL); gp_creator->clearGlyphPage(); gp_creator->createNewGlyphPage(); GUIEngine::FTEnvironment* ft_env = GUIEngine::getFreetype(); + std::set preload_char = getPreloadCharacters(m_type); - it = cur_prop.usedchar.begin(); - while (it != cur_prop.usedchar.end()) + it = preload_char.begin(); + while (it != preload_char.end()) { SGUISpriteFrame f; SGUISprite s; @@ -157,14 +153,10 @@ bool ScalableFont::loadTTF() if (m_type == T_BOLD) //Lite-Fontconfig for stk, this one is specifically for bold title { int count = F_BOLD; - while (count < irr::gui::F_COUNT) + while (count < GUIEngine::F_COUNT) { m_font_use = (FontUse)count; - FT_Face curr_face = ft_env->ft_face[m_font_use]; - - err = FT_Set_Pixel_Sizes(curr_face, 0, m_dpi); - if (err) - Log::error("ScalableFont::loadTTF", "Can't set font size."); + FT_Face curr_face = ft_env->getFace(m_font_use); idx = FT_Get_Char_Index(curr_face, *it); if (idx > 0) break; @@ -173,15 +165,11 @@ bool ScalableFont::loadTTF() } else { - FT_Face curr_face = ft_env->ft_face[m_font_use]; - err = FT_Set_Pixel_Sizes(curr_face, 0, m_dpi); - if (err) - Log::error("ScalableFont::loadTTF", "Can't set font size."); - + FT_Face curr_face = ft_env->getFace(m_font_use); idx = FT_Get_Char_Index(curr_face, *it); } - FT_Face curr_face = ft_env->ft_face[m_font_use]; + FT_Face curr_face = ft_env->getFace(m_font_use); slot = curr_face->glyph; if (idx) @@ -193,7 +181,7 @@ bool ScalableFont::loadTTF() Log::error("ScalableFont::loadTTF", "Can't load a single glyph."); // Store vertical offset on line. - s32 curr_offset = (curr_face->glyph->metrics.height >> 6) - curr_face->glyph->bitmap_top; + s32 curr_offset = (curr_face->glyph->metrics.height >> 6) - (curr_face->glyph->metrics.horiBearingY >> 6); offset.push_back(curr_offset); // This is to be used later. @@ -203,7 +191,7 @@ bool ScalableFont::loadTTF() curr_maxheight = t; // Store horizontal padding (bearingX). - s32 curr_bearingx = curr_face->glyph->bitmap_left; + s32 curr_bearingx = curr_face->glyph->metrics.horiBearingX >> 6; bearingx.push_back(curr_bearingx); // Store total width on horizontal line. @@ -213,7 +201,7 @@ bool ScalableFont::loadTTF() // Convert to an anti-aliased bitmap FT_Bitmap bits = slot->bitmap; - CharacterMap[*it] = SpriteBank->getSprites().size(); + m_character_map[*it] = m_spritebank->getSprites().size(); if (!gp_creator->checkEnoughSpace(bits)) // Glyph page is full, save current one and reset the current page @@ -221,9 +209,9 @@ bool ScalableFont::loadTTF() #ifdef FONT_DEBUG gp_creator->dumpGlyphPage(((std::to_string(m_type)) + "_" + (std::to_string(texno))).c_str()); #endif - SpriteBank->setTexture(texno, Driver->addTexture("Glyph_page", gp_creator->getPage())); + m_spritebank->setTexture(texno, m_video_driver->addTexture("Glyph_page", gp_creator->getPage())); gp_creator->clearGlyphPage(); - SpriteBank->addTexture(NULL); + m_spritebank->addTexture(NULL); gp_creator->createNewGlyphPage(); texno++; } @@ -231,36 +219,36 @@ bool ScalableFont::loadTTF() // Inserting now if (gp_creator->insertGlyph(bits, rectangle)) { - f.rectNumber = SpriteBank->getPositions().size(); + f.rectNumber = m_spritebank->getPositions().size(); f.textureNumber = texno; // add frame to sprite s.Frames.push_back(f); s.frameTime = 0; - SpriteBank->getPositions().push_back(rectangle); - SpriteBank->getSprites().push_back(s); + m_spritebank->getPositions().push_back(rectangle); + m_spritebank->getSprites().push_back(s); } else return false; } // Check for glyph page which can fit all characters - if (it == --cur_prop.usedchar.end()) + if (it == --preload_char.end()) { #ifdef FONT_DEBUG gp_creator->dumpGlyphPage(((std::to_string(m_type)) + "_" + (std::to_string(texno))).c_str()); #endif if (m_type == T_NORMAL) { - LastNormalPage = Driver->addTexture("Glyph_page", gp_creator->getPage()); - SpriteBank->setTexture(texno, LastNormalPage); + m_last_normal_page = m_video_driver->addTexture("Glyph_page", gp_creator->getPage()); + m_spritebank->setTexture(texno, m_last_normal_page); } else - SpriteBank->setTexture(texno, Driver->addTexture("Glyph_page", gp_creator->getPage())); + m_spritebank->setTexture(texno, m_video_driver->addTexture("Glyph_page", gp_creator->getPage())); } - if (*it == (wchar_t)32 && SpriteBank->getPositions().size() == 1) + if (*it == (wchar_t)32 && m_spritebank->getPositions().size() == 1) continue; //Preventing getAreaIDFromCharacter of whitespace == 0, which make space disappear else ++it; } @@ -268,28 +256,28 @@ bool ScalableFont::loadTTF() //Fix unused glyphs.... if (m_type == T_NORMAL || T_BOLD) { - CharacterMap[(wchar_t)9] = getAreaIDFromCharacter((wchar_t)160, NULL); //Use non-breaking space glyph to tab. - CharacterMap[(wchar_t)173] = 0; //Don't need a glyph for the soft hypen, as it only print when not having enough space. - //And then it will convert to a "-". + m_character_map[(wchar_t)9] = getAreaIDFromCharacter((wchar_t)160, NULL); //Use non-breaking space glyph to tab. + m_character_map[(wchar_t)173] = 0; //Don't need a glyph for the soft hypen, as it only print when not having enough space. + //And then it will convert to a "-". if (m_type == T_NORMAL) { - CharacterMap[(wchar_t)8204] = 0; //They are zero width chars found in Arabic. - CharacterMap[(wchar_t)65279] = 0; + m_character_map[(wchar_t)8204] = 0; //They are zero width chars found in Arabic. + m_character_map[(wchar_t)65279] = 0; } } if (m_type == T_BOLD) { setlocale(LC_ALL, "en_US.UTF8"); - for (it = cur_prop.usedchar.begin(); it != cur_prop.usedchar.end(); ++it) + for (it = preload_char.begin(); it != preload_char.end(); ++it) { if (iswupper((wchar_t)*it) && *it < 640) - CharacterMap[towlower((wchar_t)*it)] = getAreaIDFromCharacter(*it, NULL); + m_character_map[towlower((wchar_t)*it)] = getAreaIDFromCharacter(*it, NULL); } } - for (unsigned int n = 0 ; n < SpriteBank->getSprites().size(); ++n) + for (unsigned int n = 0 ; n < m_spritebank->getSprites().size(); ++n) { //Storing now SFontArea a; @@ -312,17 +300,15 @@ bool ScalableFont::loadTTF() else a.width = advance.at(n); // add character to font - Areas.push_back(a); + m_areas.push_back(a); } - WrongCharacter = getAreaIDFromCharacter(L' ', NULL); - //Reserve 10 for normal font new characters added, 40 for digit font to display separately //Consider fallback font (bold) too - MaxHeight = (int)((curr_maxheight + (m_type == T_DIGIT ? 40 : 10) + + m_max_height = (int)((curr_maxheight + (m_type == T_DIGIT ? 40 : 10) + (m_type == T_BOLD ? 20 : 0))*m_scale); - GlyphMaxHeight = curr_maxheight; + m_glyph_max_height = curr_maxheight; for(wchar_t c='0'; c<='9'; c++) { @@ -336,25 +322,26 @@ bool ScalableFont::loadTTF() { case T_NORMAL: Log::info("ScalableFont::loadTTF", "Created %d glyphs " - "supporting %d characters for normal font %s at %d dpi using %d glyph page(s)." - , Areas.size(), CharacterMap.size(), GUIEngine::getFreetype()->ft_face[m_font_use]->family_name, m_dpi, SpriteBank->getTextureCount()); + "supporting %d characters for normal font %s using %d glyph page(s)." + , m_areas.size(), m_character_map.size(), ft_env->getFace(m_font_use)->family_name, m_spritebank->getTextureCount()); break; case T_DIGIT: Log::info("ScalableFont::loadTTF", "Created %d glyphs " - "supporting %d characters for high-res digits font %s at %d dpi using %d glyph page(s)." - , Areas.size(), CharacterMap.size(), GUIEngine::getFreetype()->ft_face[m_font_use]->family_name, m_dpi, SpriteBank->getTextureCount()); + "supporting %d characters for high-res digits font %s using %d glyph page(s)." + , m_areas.size(), m_character_map.size(), ft_env->getFace(m_font_use)->family_name, m_spritebank->getTextureCount()); break; case T_BOLD: Log::info("ScalableFont::loadTTF", "Created %d glyphs " - "supporting %d characters for bold title font %s at %d dpi using %d glyph page(s)." - , Areas.size(), CharacterMap.size(), GUIEngine::getFreetype()->ft_face[m_font_use]->family_name, m_dpi, SpriteBank->getTextureCount()); + "supporting %d characters for bold title font %s using %d glyph page(s)." + , m_areas.size(), m_character_map.size(), ft_env->getFace(m_font_use)->family_name, m_spritebank->getTextureCount()); break; } return true; } -//! lazy load new characters discovered in normal font +// ---------------------------------------------------------------------------- + bool ScalableFont::lazyLoadChar() { //Mainly copy from loadTTF(), so removing unnecessary comments @@ -370,10 +357,9 @@ bool ScalableFont::lazyLoadChar() s32 bearingx; s32 curr_offset; s32 width; - u32 texno = SpriteBank->getTextureCount() - 1; - std::set::iterator it; - it = gp_creator->newchar.begin(); - while (it != gp_creator->newchar.end()) + u32 texno = m_spritebank->getTextureCount() - 1; + core::stringw lazy_load = gp_creator->getNewChar(); + for (u32 i = 0; i < lazy_load.size(); ++i) { SGUISpriteFrame f; SGUISprite s; @@ -381,24 +367,18 @@ bool ScalableFont::lazyLoadChar() //Lite-Fontconfig for stk int idx; - int font = irr::gui::F_DEFAULT; - while (font <= irr::gui::F_LAST_REGULAR_FONT) + int font = F_DEFAULT; + while (font <= F_LAST_REGULAR_FONT) { m_font_use = (FontUse)font; - FT_Face face = ft_env->ft_face[font]; - - err = FT_Set_Pixel_Sizes(face, 0, m_dpi); - if (err) - Log::error("ScalableFont::loadTTF", "Can't set font size."); - - idx = FT_Get_Char_Index(face, *it); + idx = FT_Get_Char_Index(ft_env->getFace(m_font_use), lazy_load[i]); if (idx > 0) break; font++; } - FT_Face curr_face = ft_env->ft_face[m_font_use]; + FT_Face curr_face = ft_env->getFace(m_font_use); slot = curr_face->glyph; @@ -409,57 +389,56 @@ bool ScalableFont::lazyLoadChar() if (err) Log::error("ScalableFont::loadTTF", "Can't load a single glyph."); - curr_offset = (curr_face->glyph->metrics.height >> 6) - curr_face->glyph->bitmap_top; + curr_offset = (curr_face->glyph->metrics.height >> 6) - (curr_face->glyph->metrics.horiBearingY >> 6); height = curr_face->glyph->metrics.height >> 6; - bearingx = curr_face->glyph->bitmap_left; + bearingx = curr_face->glyph->metrics.horiBearingX >> 6; width = curr_face->glyph->advance.x >> 6; FT_Bitmap bits = slot->bitmap; - CharacterMap[*it] = SpriteBank->getSprites().size(); + m_character_map[lazy_load[i]] = m_spritebank->getSprites().size(); if (!gp_creator->checkEnoughSpace(bits)) { #ifdef FONT_DEBUG gp_creator->dumpGlyphPage(((std::to_string(m_type)) + "_" + (std::to_string(texno))).c_str()); #endif - SpriteBank->setTexture(texno, Driver->addTexture("Glyph_page", gp_creator->getPage())); + m_spritebank->setTexture(texno, m_video_driver->addTexture("Glyph_page", gp_creator->getPage())); gp_creator->clearGlyphPage(); - SpriteBank->addTexture(NULL); + m_spritebank->addTexture(NULL); gp_creator->createNewGlyphPage(); texno++; } if (gp_creator->insertGlyph(bits, rectangle)) { - f.rectNumber = SpriteBank->getPositions().size(); + f.rectNumber = m_spritebank->getPositions().size(); f.textureNumber = texno; s.Frames.push_back(f); s.frameTime = 0; - SpriteBank->getPositions().push_back(rectangle); - SpriteBank->getSprites().push_back(s); + m_spritebank->getPositions().push_back(rectangle); + m_spritebank->getSprites().push_back(s); } else return false; SFontArea a; - a.spriteno = SpriteBank->getSprites().size() - 1; - a.offsety = GlyphMaxHeight - height + curr_offset; + a.spriteno = m_spritebank->getSprites().size() - 1; + a.offsety = m_glyph_max_height - height + curr_offset; a.offsety_bt = -curr_offset; a.bearingx = bearingx; a.width = width; - Areas.push_back(a); + m_areas.push_back(a); } else - CharacterMap[*it] = 2; //Set wrong character to !, preventing it from loading again - ++it; + m_character_map[lazy_load[i]] = 1; //Set any wrong characters to 1 (space), preventing it from loading again } #ifdef FONT_DEBUG gp_creator->dumpGlyphPage(((std::to_string(m_type)) + "_" + (std::to_string(texno))).c_str()); #endif - gp_creator->newchar.clear(); //Clear the Newly characters in creator after they are loaded - Driver->removeTexture(LastNormalPage); //Remove old texture - LastNormalPage = Driver->addTexture("Glyph_page", gp_creator->getPage()); - SpriteBank->setTexture(texno, LastNormalPage); + gp_creator->clearNewCharHolder(); //Clear the Newly characters in creator after they are loaded + m_video_driver->removeTexture(m_last_normal_page); //Remove old texture + m_last_normal_page = m_video_driver->addTexture("Glyph_page", gp_creator->getPage()); + m_spritebank->setTexture(texno, m_last_normal_page); if (!m_is_hollow_copy) { @@ -468,73 +447,167 @@ bool ScalableFont::lazyLoadChar() } Log::debug("ScalableFont::lazyLoadChar", "New characters drawn by %s inserted, there are %d glyphs " - "supporting %d characters for normal font at %d dpi using %d glyph page(s) now." - , GUIEngine::getFreetype()->ft_face[m_font_use]->family_name, Areas.size(), CharacterMap.size(), m_dpi, SpriteBank->getTextureCount()); + "supporting %d characters for normal font using %d glyph page(s) now." + , ft_env->getFace(m_font_use)->family_name, m_areas.size(), m_character_map.size(), m_spritebank->getTextureCount()); return true; } -//! force create a new texture (glyph) page in a font +// ---------------------------------------------------------------------------- + void ScalableFont::forceNewPage() { GUIEngine::GlyphPageCreator* gp_creator = GUIEngine::getGlyphPageCreator(); - SpriteBank->setTexture(SpriteBank->getTextureCount() - 1, Driver->addTexture("Glyph_page", gp_creator->getPage())); + m_spritebank->setTexture(m_spritebank->getTextureCount() - 1, m_video_driver->addTexture("Glyph_page", gp_creator->getPage())); gp_creator->clearGlyphPage(); - SpriteBank->addTexture(NULL); + m_spritebank->addTexture(NULL); gp_creator->createNewGlyphPage(); } +// ---------------------------------------------------------------------------- + +std::set ScalableFont::getPreloadCharacters(const GUIEngine::TTFLoadingType type) +{ + std::set preload_char; + + switch (type) + { + case T_NORMAL: + for (u32 i = 32; i < 128; ++i) + preload_char.insert((wchar_t)i); //Include basic Latin + + preload_char.insert((wchar_t)160); //Non-breaking space + preload_char.insert((wchar_t)215); //Used on resolution selection screen (X). + break; + case T_DIGIT: + preload_char.insert((wchar_t)32); //Space + + for (u32 i = 47; i < 59; ++i) + preload_char.insert((wchar_t)i); //Include chars used by timer and laps count only + break; + case T_BOLD: + preload_char = translations->getCurrentAllChar(); //Loading unique characters + + for (u32 i = 65; i < 256; ++i) + preload_char.insert((wchar_t)i); //Include basic Latin too, starting from A (char code 65) + + setlocale(LC_ALL, "en_US.UTF8"); + std::set::iterator it = preload_char.begin(); + + while (it != preload_char.end()) + { + //Only use all capital letter for bold char with latin (<640 of char code). + //Remove all characters (>char code 8191) not used by the title + if (((iswlower((wchar_t)*it) || !iswalpha((wchar_t)*it)) && *it < 640) || *it > 8191) + it = preload_char.erase(it); + else + ++it; + } + + //Final hack to make stk display title properly + for (u32 i = 32; i < 65; ++i) + preload_char.insert((wchar_t)i); //Include basic symbol (from space (char code 32) to @(char code 64)) + + preload_char.insert((wchar_t)160); //Non-breaking space + + //Remove Ordinal indicator (char code 170 and 186) + preload_char.erase((wchar_t)170); + preload_char.erase((wchar_t)186); + + preload_char.erase((wchar_t)304); //Remove Capital I-dotted (char code 304) with using "I" altogether. + break; + } + + return preload_char; +} + +// ---------------------------------------------------------------------------- + +void ScalableFont::recreateFromLanguage() +{ + //Clean previous font data + m_spritebank->clear(); + m_areas.clear(); + m_character_map.clear(); + m_max_height = 0; + m_global_kerning_width = 0; + m_global_kerning_height = 0; + + //Set to default scale to reload font + m_scale = 1; + //Reload + if (!loadTTF()) + { + Log::fatal("ScalableFont", "Recreation of TTF font failed"); + } +} + +// ---------------------------------------------------------------------------- + +void ScalableFont::updateRTL() +{ + m_rtl = translations->isRTLLanguage(); +} + +// ---------------------------------------------------------------------------- + +void ScalableFont::setShadow(const irr::video::SColor &col) +{ + m_shadow = true; + m_shadow_color = col; +} + +// ---------------------------------------------------------------------------- void ScalableFont::setScale(const float scale) { m_scale = scale; } +// ---------------------------------------------------------------------------- - - -//! set an Pixel Offset on Drawing ( scale position on width ) void ScalableFont::setKerningWidth(s32 kerning) { - GlobalKerningWidth = kerning; + m_global_kerning_width = kerning; } +// ---------------------------------------------------------------------------- -//! set an Pixel Offset on Drawing ( scale position on width ) s32 ScalableFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const { - s32 ret = GlobalKerningWidth; + s32 ret = m_global_kerning_width; return ret; } +// ---------------------------------------------------------------------------- -//! set an Pixel Offset on Drawing ( scale position on height ) void ScalableFont::setKerningHeight(s32 kerning) { - GlobalKerningHeight = kerning; + m_global_kerning_height = kerning; } +// ---------------------------------------------------------------------------- -//! set an Pixel Offset on Drawing ( scale position on height ) s32 ScalableFont::getKerningHeight () const { - return GlobalKerningHeight; + return m_global_kerning_height; } +// ---------------------------------------------------------------------------- -//! returns the sprite number from a given character u32 ScalableFont::getSpriteNoFromChar(const wchar_t *c) const { - return Areas[getAreaIDFromCharacter(*c, NULL)].spriteno; + return m_areas[getAreaIDFromCharacter(*c, NULL)].spriteno; } +// ---------------------------------------------------------------------------- s32 ScalableFont::getAreaIDFromCharacter(const wchar_t c, bool* fallback_font) const { - std::map::const_iterator n = CharacterMap.find(c); - if (n != CharacterMap.end()) + std::map::const_iterator n = m_character_map.find(c); + if (n != m_character_map.end()) { if (fallback_font != NULL) *fallback_font = false; //Log::info("ScalableFont", "Character %d found in font", (int)c); @@ -550,10 +623,12 @@ s32 ScalableFont::getAreaIDFromCharacter(const wchar_t c, bool* fallback_font) c { //Log::warn("ScalableFont", "The font does not have this character: <%d>", (int)c); if (fallback_font != NULL) *fallback_font = false; - return WrongCharacter; + return 1; //The first preload character in all type of fonts is space, which is 1 } } +// ---------------------------------------------------------------------------- + const ScalableFont::SFontArea &ScalableFont::getAreaFromCharacter(const wchar_t c, bool* fallback_font) const { @@ -563,8 +638,8 @@ const ScalableFont::SFontArea &ScalableFont::getAreaFromCharacter(const wchar_t if(m_mono_space_digits && ( (c>=L'0' && c<=L'9') || c==L' ' ) ) { const SFontArea &area = (fallback_font && *fallback_font) - ? m_fallback_font->Areas[area_id] - : Areas[area_id]; + ? m_fallback_font->m_areas[area_id] + : m_areas[area_id]; m_max_digit_area.spriteno = area.spriteno; return m_max_digit_area; } @@ -573,25 +648,36 @@ const ScalableFont::SFontArea &ScalableFont::getAreaFromCharacter(const wchar_t if (use_fallback_font) { - assert(area_id < (int)m_fallback_font->Areas.size()); + assert(area_id < (int)m_fallback_font->m_areas.size()); } else { - assert(area_id < (int)Areas.size()); + assert(area_id < (int)m_areas.size()); } // Note: fallback_font can be NULL - return ( use_fallback_font ? m_fallback_font->Areas[area_id] : Areas[area_id]); + return ( use_fallback_font ? m_fallback_font->m_areas[area_id] : m_areas[area_id]); } // getAreaFromCharacter +// ---------------------------------------------------------------------------- + +bool ScalableFont::hasThisChar(const wchar_t c) const +{ + std::map::const_iterator n = m_character_map.find(c); + if (n != m_character_map.end()) + return true; + return false; +} + +// ---------------------------------------------------------------------------- void ScalableFont::setInvisibleCharacters( const wchar_t *s ) { - Invisible = s; + m_invisible = s; } +// ---------------------------------------------------------------------------- -//! returns the dimension of text core::dimension2d ScalableFont::getDimension(const wchar_t* text) const { GUIEngine::GlyphPageCreator* gp_creator = GUIEngine::getGlyphPageCreator(); @@ -601,11 +687,11 @@ core::dimension2d ScalableFont::getDimension(const wchar_t* text) const for (const wchar_t* p = text; *p; ++p) { if (*p == L'\r' || *p == L'\n' || *p == L' ' || *p < 32) continue; - if (GUIEngine::getFont()->getSpriteNoFromChar(p) == WrongCharacter) - gp_creator->newchar.insert(*p); + if (!GUIEngine::getFont()->hasThisChar(*p)) + gp_creator->insertChar(*p); } - if (gp_creator->newchar.size() > 0 && !m_is_hollow_copy && m_scale == 1) + if (gp_creator->getNewChar().size() > 0 && !m_is_hollow_copy && m_scale == 1) { Log::debug("ScalableFont::getDimension", "New character(s) %s discoverd, perform lazy loading", StringUtils::wide_to_utf8(gp_creator->getNewChar().c_str()).c_str()); @@ -615,10 +701,10 @@ core::dimension2d ScalableFont::getDimension(const wchar_t* text) const } } - assert(Areas.size() > 0); + assert(m_areas.size() > 0); core::dimension2d dim(0, 0); - core::dimension2d thisLine(0, (int)(MaxHeight*m_scale)); + core::dimension2d thisLine(0, (int)(m_max_height*m_scale)); for (const wchar_t* p = text; *p; ++p) { @@ -653,6 +739,8 @@ core::dimension2d ScalableFont::getDimension(const wchar_t* text) const return dim; } +// ---------------------------------------------------------------------------- + void ScalableFont::draw(const core::stringw& text, const core::rect& position, video::SColor color, bool hcenter, bool vcenter, @@ -661,6 +749,8 @@ void ScalableFont::draw(const core::stringw& text, doDraw(text, position, color, hcenter, vcenter, clip, NULL); } +// ---------------------------------------------------------------------------- + void ScalableFont::draw(const core::stringw& text, const core::rect& position, video::SColor color, bool hcenter, bool vcenter, @@ -674,14 +764,15 @@ void ScalableFont::draw(const core::stringw& text, if (ignoreRTL) m_rtl = previousRTL; } -//! draws some text and clips it to the specified rectangle if wanted +// ---------------------------------------------------------------------------- + void ScalableFont::doDraw(const core::stringw& text, const core::rect& position, video::SColor color, bool hcenter, bool vcenter, const core::rect* clip, FontCharCollector* charCollector) { - if (!Driver) return; + if (!m_video_driver) return; GUIEngine::GlyphPageCreator* gp_creator = GUIEngine::getGlyphPageCreator(); @@ -729,12 +820,12 @@ void ScalableFont::doDraw(const core::stringw& text, { wchar_t c = text[i]; if (c == L'\r' || c == L'\n' || c == L' ' || c < 32) continue; - if (GUIEngine::getFont()->getSpriteNoFromChar(&c) == WrongCharacter) - gp_creator->newchar.insert(c); + if (!GUIEngine::getFont()->hasThisChar(c)) + gp_creator->insertChar(c); - if (charCollector != NULL && m_type == T_NORMAL && SpriteBank->getSprites() + if (charCollector != NULL && m_type == T_NORMAL && m_spritebank->getSprites() [GUIEngine::getFont()->getSpriteNoFromChar(&c)].Frames[0].textureNumber - == SpriteBank->getTextureCount() - 1) //Prevent overwriting texture used by billboard text + == 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."); @@ -742,7 +833,7 @@ void ScalableFont::doDraw(const core::stringw& text, } } - if (gp_creator->newchar.size() > 0 && !m_is_hollow_copy && m_scale == 1) + 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::wide_to_utf8(gp_creator->getNewChar().c_str()).c_str()); @@ -760,7 +851,7 @@ void ScalableFont::doDraw(const core::stringw& text, c == L'\n' ) // Unix breaks { if(c==L'\r' && text[i+1]==L'\n') c = text[++i]; - offset.Y += (int)(MaxHeight*m_scale); + offset.Y += (int)(m_max_height*m_scale); offset.X = position.UpperLeftCorner.X; if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) >> 1; @@ -772,46 +863,45 @@ void ScalableFont::doDraw(const core::stringw& text, fallback[i] = use_fallback_font; if (charCollector == NULL) { - //floor is used to prevent negligible movement when m_scale changes with resolution - int Hpadding = floor((float) area.bearingx* - (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); - int Vpadding = floor((float) area.offsety* - (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); - offset.X += Hpadding; - offset.Y += Vpadding + floor(m_type == T_DIGIT ? 20*m_scale : 0); //Additional offset for digit text + //Try to use ceil to make offset calculate correctly when m_scale is smaller than 1 + s32 glyph_offset_x = ceil((float) area.bearingx* + (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); + s32 glyph_offset_y = ceil((float) area.offsety* + (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); + offset.X += glyph_offset_x; + offset.Y += glyph_offset_y + floor(m_type == T_DIGIT ? 20*m_scale : 0); //Additional offset for digit text offsets.push_back(offset); - offset.X -= Hpadding; - offset.Y -= Vpadding + floor(m_type == T_DIGIT ? 20*m_scale : 0); + offset.X -= glyph_offset_x; + offset.Y -= glyph_offset_y + floor(m_type == T_DIGIT ? 20*m_scale : 0); } else //Billboard text specific { - int Hpadding = floor((float) area.bearingx* - (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); - int Vpadding = floor((float) area.offsety_bt* - (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); - offset.X += Hpadding; - offset.Y += Vpadding + floor(m_type == T_DIGIT ? 20*m_scale : 0); //Additional offset for digit text + s32 glyph_offset_x = ceil((float) area.bearingx* + (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); + s32 glyph_offset_y = ceil((float) area.offsety_bt* + (fallback[i] ? m_scale*m_fallback_font_scale : m_scale)); + offset.X += glyph_offset_x; + offset.Y += glyph_offset_y + floor(m_type == T_DIGIT ? 20*m_scale : 0); //Additional offset for digit text offsets.push_back(offset); - offset.X -= Hpadding; - offset.Y -= Vpadding + floor(m_type == T_DIGIT ? 20*m_scale : 0); + offset.X -= glyph_offset_x; + offset.Y -= 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( Invisible.findFirst(c) < 0 ? area.spriteno - : -1 ); + indices.push_back(m_invisible.findFirst(c) < 0 ? area.spriteno : -1); offset.X += getCharWidth(area, fallback[i]); } // for i& sprites = SpriteBank->getSprites(); - core::array< core::rect >& positions = SpriteBank->getPositions(); + core::array< SGUISprite >& sprites = m_spritebank->getSprites(); + core::array< core::rect >& positions = m_spritebank->getPositions(); core::array< SGUISprite >* fallback_sprites; core::array< core::rect >* fallback_positions; - if(m_fallback_font!=NULL) + if (m_fallback_font != NULL) { - fallback_sprites = &m_fallback_font->SpriteBank->getSprites(); - fallback_positions = &m_fallback_font->SpriteBank->getPositions(); + fallback_sprites = &m_fallback_font->m_spritebank->getSprites(); + fallback_positions = &m_fallback_font->m_spritebank->getPositions(); } else { @@ -825,7 +915,7 @@ void ScalableFont::doDraw(const core::stringw& text, { //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= spriteAmount)) continue; @@ -848,12 +938,12 @@ void ScalableFont::doDraw(const core::stringw& text, core::rect dest(offsets[n], size); video::ITexture* texture = (fallback[n] ? - m_fallback_font->SpriteBank->getTexture(texID) : - SpriteBank->getTexture(texID) ); + m_fallback_font->m_spritebank->getTexture(texID) : + m_spritebank->getTexture(texID) ); - for (int x_delta=-2; x_delta<=2; x_delta++) + for (int x_delta = -2; x_delta <= 2; x_delta++) { - for (int y_delta=-2; y_delta<=2; y_delta++) + for (int y_delta = -2; y_delta <= 2; y_delta++) { if (x_delta == 0 || y_delta == 0) continue; draw2DImage(texture, @@ -866,7 +956,7 @@ void ScalableFont::doDraw(const core::stringw& text, } } - for (int n=0; n= spriteAmount)) continue; @@ -891,8 +981,8 @@ void ScalableFont::doDraw(const core::stringw& text, core::rect dest(offsets[n], size); video::ITexture* texture = (fallback[n] ? - m_fallback_font->SpriteBank->getTexture(texID) : - SpriteBank->getTexture(texID) ); + m_fallback_font->m_spritebank->getTexture(texID) : + m_spritebank->getTexture(texID) ); /* if (fallback[n]) @@ -951,15 +1041,16 @@ void ScalableFont::doDraw(const core::stringw& text, } } +// ---------------------------------------------------------------------------- -int ScalableFont::getCharWidth(const SFontArea& area, const bool fallback) const +s32 ScalableFont::getCharWidth(const SFontArea& area, const bool fallback) const { if (fallback) return (int)((area.width*m_fallback_font_scale + m_fallback_kerning_width) * m_scale); - else return (int)((area.width + GlobalKerningWidth) * m_scale); + else return (int)((area.width + m_global_kerning_width) * m_scale); } +// ---------------------------------------------------------------------------- -//! Calculates the index of the character in the text which is on a specific position. s32 ScalableFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const { s32 x = 0; @@ -970,7 +1061,7 @@ s32 ScalableFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const bool use_fallback_font = false; const SFontArea &a = getAreaFromCharacter(text[idx], &use_fallback_font); - x += getCharWidth(a, use_fallback_font) + GlobalKerningWidth; + x += getCharWidth(a, use_fallback_font) + m_global_kerning_width; if (x >= pixel_x) return idx; @@ -981,13 +1072,12 @@ s32 ScalableFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const return -1; } +// ---------------------------------------------------------------------------- IGUISpriteBank* ScalableFont::getSpriteBank() const { - return SpriteBank; + return m_spritebank; } } // end namespace gui } // end namespace irr - - diff --git a/src/guiengine/scalable_font.hpp b/src/guiengine/scalable_font.hpp index 24effc0a9..5f82e5319 100644 --- a/src/guiengine/scalable_font.hpp +++ b/src/guiengine/scalable_font.hpp @@ -1,14 +1,27 @@ -// Copyright (C) 2002-2015 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// Copyright (C) 2015 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 __C_GUI_FONT_H_INCLUDED__ -#define __C_GUI_FONT_H_INCLUDED__ +#ifndef HEADER_SCALABLE_FONT_HPP +#define HEADER_SCALABLE_FONT_HPP +#include "guiengine/ft_environment.hpp" #include "utils/leak_check.hpp" -#include "guiengine/get_font_properties.hpp" - #include "IrrCompileConfig.h" #include "IGUIFontBitmap.h" #include "irrString.h" @@ -20,6 +33,7 @@ #include #include +#include namespace irr { @@ -62,16 +76,12 @@ public: bool m_black_border; - TTFLoadingType m_type; - FontUse m_font_use; - u32 m_dpi; - ScalableFont* m_fallback_font; float m_fallback_font_scale; int m_fallback_kerning_width; - //! constructor - ScalableFont(IGUIEnvironment* env, TTFLoadingType type); + ScalableFont(IGUIEnvironment* env, GUIEngine::TTFLoadingType type); + virtual ~ScalableFont(); /** Creates a hollow copy of this font; i.e. the underlying font data is the *same* for * both fonts. The advantage of doing this is that you can change "view" parameters @@ -89,13 +99,13 @@ public: return out; } - //! destructor - virtual ~ScalableFont(); - - //! loads a font from a TTF file + /** loads a font from a TTF file */ bool loadTTF(); - //! draws an text and clips it to the specified rectangle if wanted + /** lazy load new characters discovered in normal font */ + bool lazyLoadChar(); + + /** draws an text and clips it to the specified rectangle if wanted */ virtual void draw(const core::stringw& text, const core::rect& position, video::SColor color, bool hcenter = false, bool vcenter = false, const core::rect* clip = 0); @@ -109,20 +119,20 @@ public: bool vcenter, const core::rect* clip, FontCharCollector* charCollector = NULL); - //! returns the dimension of a text + /** returns the dimension of a text */ virtual core::dimension2d getDimension(const wchar_t* text) const; - //! Calculates the index of the character in the text which is on a specific position. + /** Calculates the index of the character in the text which is on a specific position. */ virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const; - //! Returns the type of this font + /** Returns the type of this font */ virtual EGUI_FONT_TYPE getType() const { return EGFT_BITMAP; } - //! set an Pixel Offset on Drawing ( scale position on width ) + /** set an Pixel Offset on Drawing ( scale position on width ) */ virtual void setKerningWidth (s32 kerning); virtual void setKerningHeight (s32 kerning); - //! set an Pixel Offset on Drawing ( scale position on width ) + /** set an Pixel Offset on Drawing ( scale position on width ) */ virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const; virtual s32 getKerningHeight() const; @@ -132,26 +142,26 @@ public: void setShadow(const irr::video::SColor &col); void disableShadow() {m_shadow = false;} - //! gets the sprite bank + /** gets the sprite bank */ virtual IGUISpriteBank* getSpriteBank() const; - //! returns the sprite number from a given character + /** returns the sprite number from a given character */ virtual u32 getSpriteNoFromChar(const wchar_t *c) const; virtual void setInvisibleCharacters( const wchar_t *s ); + /** test whether current font has this character regardless of fallback font */ + virtual bool hasThisChar(const wchar_t c) const; + void setScale(const float scale); float getScale() const { return m_scale; } void updateRTL(); - //! re-create fonts when language is changed + /** re-create fonts when language is changed */ void recreateFromLanguage(); - //! lazy load new characters discovered in normal font - bool lazyLoadChar(); - - //! force create a new texture (glyph) page in a font + /** force create a new texture (glyph) page in a font */ void forceNewPage(); private: @@ -166,30 +176,35 @@ private: s32 bearingx; }; - int getCharWidth(const SFontArea& area, const bool fallback) const; + s32 getCharWidth(const SFontArea& area, const bool fallback) const; s32 getAreaIDFromCharacter(const wchar_t c, bool* fallback_font) const; const SFontArea &getAreaFromCharacter(const wchar_t c, bool* fallback_font) const; - void setMaxHeight(); + /** get characters to be pre-loaded base on font type */ + std::set getPreloadCharacters(const GUIEngine::TTFLoadingType); - core::array Areas; + GUIEngine::TTFLoadingType m_type; + GUIEngine::FontUse m_font_use; + video::IVideoDriver *m_video_driver; + IGUISpriteBank *m_spritebank; + IGUIEnvironment *m_gui_env; + video::ITexture *m_last_normal_page; + + core::array m_areas; /** The maximum values of all digits, used in monospace_digits. */ - mutable SFontArea m_max_digit_area; - std::map CharacterMap; - video::IVideoDriver* Driver; - IGUISpriteBank* SpriteBank; - IGUIEnvironment* Environment; - u32 WrongCharacter; - s32 MaxHeight; - s32 GlobalKerningWidth, GlobalKerningHeight; - s32 GlyphMaxHeight; - video::ITexture* LastNormalPage; + mutable SFontArea m_max_digit_area; + std::map m_character_map; - core::stringw Invisible; + s32 m_max_height; + s32 m_global_kerning_width; + s32 m_global_kerning_height; + s32 m_glyph_max_height; + + core::stringw m_invisible; }; } // end namespace gui } // end namespace irr -#endif // __C_GUI_FONT_H_INCLUDED__ +#endif // HEADER_SCALABLE_FONT_HPP