diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index 9e6d8c897..298cfbf2c 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -2,6 +2,7 @@ CGUITTFont FreeType class for Irrlicht Copyright (c) 2009-2010 John Norman Copyright (c) 2016 Nathanaƫlle Courant + Copyright (c) 2023 Caleb Butler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -31,6 +32,8 @@ #include #include +#include +#include #include "CGUITTFont.h" namespace irr @@ -313,7 +316,7 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia // Log. if (logger) - logger->log(L"CGUITTFont", core::stringw(core::stringw(L"Creating new font: ") + core::ustring(filename).toWCHAR_s() + L" " + core::stringc(size) + L"pt " + (antialias ? L"+antialias " : L"-antialias ") + (transparency ? L"+transparency" : L"-transparency")).c_str(), irr::ELL_INFORMATION); + logger->log(L"CGUITTFont", core::stringw(core::stringw(L"Creating new font: ") + core::stringw(filename) + L" " + core::stringc(size) + L"pt " + (antialias ? L"+antialias " : L"-antialias ") + (transparency ? L"+transparency" : L"-transparency")).c_str(), irr::ELL_INFORMATION); // Grab the face. SGUITTFace* face = 0; @@ -354,8 +357,7 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia } else { - core::ustring converter(filename); - if (FT_New_Face(c_library, reinterpret_cast(converter.toUTF8_s().c_str()), 0, &face->face)) + if (FT_New_Face(c_library, reinterpret_cast(filename.c_str()), 0, &face->face)) { if (logger) logger->log(L"CGUITTFont", L"FT_New_Face failed.", irr::ELL_INFORMATION); @@ -398,7 +400,7 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia // Cache the first 127 ascii characters. u32 old_size = batch_load_size; batch_load_size = 127; - getGlyphIndexByChar((uchar32_t)0); + getGlyphIndexByChar((char32_t)0); batch_load_size = old_size; return true; @@ -578,28 +580,28 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect& positio } // Convert to a unicode string. - core::ustring utext = text.getString(); + std::u32string utext = convertWCharToU32String(text.c_str()); // Set up our render map. std::map Render_Map; // Start parsing characters. u32 n; - uchar32_t previousChar = 0; - core::ustring::const_iterator iter(utext); - while (!iter.atEnd()) + char32_t previousChar = 0; + std::u32string::const_iterator iter = utext.begin(); + while (iter != utext.end()) { - uchar32_t currentChar = *iter; + char32_t currentChar = *iter; n = getGlyphIndexByChar(currentChar); - bool visible = (Invisible.findFirst(currentChar) == -1); + bool visible = (Invisible.find_first_of(currentChar) == std::u32string::npos); bool lineBreak=false; if (currentChar == L'\r') // Mac or Windows breaks { lineBreak = true; - if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks. + if (*(iter + 1) == (char32_t)'\n') // Windows line breaks. currentChar = *(++iter); } - else if (currentChar == (uchar32_t)'\n') // Unix breaks + else if (currentChar == (char32_t)'\n') // Unix breaks { lineBreak = true; } @@ -632,8 +634,9 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect& positio CGUITTGlyphPage* const page = Glyph_Pages[glyph.glyph_page]; page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy)); page->render_source_rects.push_back(glyph.source_rect); - if (iter.getPos() < colors.size()) - page->render_colors.push_back(colors[iter.getPos()]); + const size_t iterPos = iter - utext.begin(); + if (iterPos < colors.size()) + page->render_colors.push_back(colors[iterPos]); else page->render_colors.push_back(video::SColor(255,255,255,255)); Render_Map[glyph.glyph_page] = page; @@ -653,7 +656,7 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect& positio offset.X += fallback->getKerningWidth(l1, &l2); offset.Y += fallback->getKerningHeight(); - u32 current_color = iter.getPos(); + const u32 current_color = iter - utext.begin(); fallback->draw(core::stringw(l1), core::rect({offset.X-1, offset.Y-1}, position.LowerRightCorner), // ??? current_color < colors.size() ? colors[current_color] : video::SColor(255, 255, 255, 255), @@ -718,10 +721,10 @@ core::dimension2d CGUITTFont::getCharDimension(const wchar_t ch) const core::dimension2d CGUITTFont::getDimension(const wchar_t* text) const { - return getDimension(core::ustring(text)); + return getDimension(convertWCharToU32String(text)); } -core::dimension2d CGUITTFont::getDimension(const core::ustring& text) const +core::dimension2d CGUITTFont::getDimension(const std::u32string& text) const { // Get the maximum font height. Unfortunately, we have to do this hack as // Irrlicht will draw things wrong. In FreeType, the font size is the @@ -730,19 +733,19 @@ core::dimension2d CGUITTFont::getDimension(const core::ustring& text) const // Irrlicht does not understand this concept when drawing fonts. Also, I // add +1 to give it a 1 pixel blank border. This makes things like // tooltips look nicer. - s32 test1 = getHeightFromCharacter((uchar32_t)'g') + 1; - s32 test2 = getHeightFromCharacter((uchar32_t)'j') + 1; - s32 test3 = getHeightFromCharacter((uchar32_t)'_') + 1; + s32 test1 = getHeightFromCharacter((char32_t)'g') + 1; + s32 test2 = getHeightFromCharacter((char32_t)'j') + 1; + s32 test3 = getHeightFromCharacter((char32_t)'_') + 1; s32 max_font_height = core::max_(test1, core::max_(test2, test3)); core::dimension2d text_dimension(0, max_font_height); core::dimension2d line(0, max_font_height); - uchar32_t previousChar = 0; - core::ustring::const_iterator iter = text.begin(); - for (; !iter.atEnd(); ++iter) + char32_t previousChar = 0; + std::u32string::const_iterator iter = text.begin(); + for (; iter != text.end(); ++iter) { - uchar32_t p = *iter; + char32_t p = *iter; bool lineBreak = false; if (p == '\r') // Mac or Windows line breaks. { @@ -784,10 +787,10 @@ core::dimension2d CGUITTFont::getDimension(const core::ustring& text) const inline u32 CGUITTFont::getWidthFromCharacter(wchar_t c) const { - return getWidthFromCharacter((uchar32_t)c); + return getWidthFromCharacter((char32_t)c); } -inline u32 CGUITTFont::getWidthFromCharacter(uchar32_t c) const +inline u32 CGUITTFont::getWidthFromCharacter(char32_t c) const { // Set the size of the face. // This is because we cache faces and the face may have been set to a different size. @@ -812,10 +815,10 @@ inline u32 CGUITTFont::getWidthFromCharacter(uchar32_t c) const inline u32 CGUITTFont::getHeightFromCharacter(wchar_t c) const { - return getHeightFromCharacter((uchar32_t)c); + return getHeightFromCharacter((char32_t)c); } -inline u32 CGUITTFont::getHeightFromCharacter(uchar32_t c) const +inline u32 CGUITTFont::getHeightFromCharacter(char32_t c) const { // Set the size of the face. // This is because we cache faces and the face may have been set to a different size. @@ -841,10 +844,10 @@ inline u32 CGUITTFont::getHeightFromCharacter(uchar32_t c) const u32 CGUITTFont::getGlyphIndexByChar(wchar_t c) const { - return getGlyphIndexByChar((uchar32_t)c); + return getGlyphIndexByChar((char32_t)c); } -u32 CGUITTFont::getGlyphIndexByChar(uchar32_t c) const +u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const { // Get the glyph. u32 glyph = FT_Get_Char_Index(tt_face, c); @@ -888,20 +891,20 @@ u32 CGUITTFont::getGlyphIndexByChar(uchar32_t c) const s32 CGUITTFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const { - return getCharacterFromPos(core::ustring(text), pixel_x); + return getCharacterFromPos(convertWCharToU32String(text), pixel_x); } -s32 CGUITTFont::getCharacterFromPos(const core::ustring& text, s32 pixel_x) const +s32 CGUITTFont::getCharacterFromPos(const std::u32string& text, s32 pixel_x) const { s32 x = 0; //s32 idx = 0; u32 character = 0; - uchar32_t previousChar = 0; - core::ustring::const_iterator iter = text.begin(); - while (!iter.atEnd()) + char32_t previousChar = 0; + std::u32string::const_iterator iter = text.begin(); + while (iter != text.end()) { - uchar32_t c = *iter; + char32_t c = *iter; x += getWidthFromCharacter(c); // Kerning. @@ -936,10 +939,10 @@ s32 CGUITTFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previo if (thisLetter == 0 || previousLetter == 0) return 0; - return getKerningWidth((uchar32_t)*thisLetter, (uchar32_t)*previousLetter); + return getKerningWidth((char32_t)*thisLetter, (char32_t)*previousLetter); } -s32 CGUITTFont::getKerningWidth(const uchar32_t thisLetter, const uchar32_t previousLetter) const +s32 CGUITTFont::getKerningWidth(const char32_t thisLetter, const char32_t previousLetter) const { // Return only the kerning width. return getKerning(thisLetter, previousLetter).X; @@ -953,10 +956,10 @@ s32 CGUITTFont::getKerningHeight() const core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const { - return getKerning((uchar32_t)thisLetter, (uchar32_t)previousLetter); + return getKerning((char32_t)thisLetter, (char32_t)previousLetter); } -core::vector2di CGUITTFont::getKerning(const uchar32_t thisLetter, const uchar32_t previousLetter) const +core::vector2di CGUITTFont::getKerning(const char32_t thisLetter, const char32_t previousLetter) const { if (tt_face == 0 || thisLetter == 0 || previousLetter == 0) return core::vector2di(); @@ -1006,20 +1009,18 @@ core::vector2di CGUITTFont::getKerning(const uchar32_t thisLetter, const uchar32 void CGUITTFont::setInvisibleCharacters(const wchar_t *s) { - core::ustring us(s); - Invisible = us; + Invisible = convertWCharToU32String(s); } -void CGUITTFont::setInvisibleCharacters(const core::ustring& s) +video::IImage* CGUITTFont::createTextureFromChar(const char32_t& ch) { - Invisible = s; -} + // This character allows us to print something to the screen for unknown, unrecognizable, or + // unrepresentable characters. See Unicode spec. + const char32_t UTF_REPLACEMENT_CHARACTER = 0xFFFD; -video::IImage* CGUITTFont::createTextureFromChar(const uchar32_t& ch) -{ u32 n = getGlyphIndexByChar(ch); if (n == 0) - n = getGlyphIndexByChar((uchar32_t) core::unicode::UTF_REPLACEMENT_CHARACTER); + n = getGlyphIndexByChar(UTF_REPLACEMENT_CHARACTER); const SGUITTGlyph& glyph = Glyphs[n-1]; CGUITTGlyphPage* page = Glyph_Pages[glyph.glyph_page]; @@ -1246,5 +1247,27 @@ core::array CGUITTFont::addTextSceneNode(const wchar_t* text return container; } +std::u32string CGUITTFont::convertWCharToU32String(const wchar_t* const charArray) const +{ + static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "unexpected wchar size"); + + if (sizeof(wchar_t) == 4) // Systems where wchar_t is UTF-32 + return std::u32string(reinterpret_cast(charArray)); + + // Systems where wchar_t is UTF-16: + // First, convert to UTF-8 + std::u16string utf16String(reinterpret_cast(charArray)); + std::wstring_convert, char16_t> converter1; + std::string utf8String = converter1.to_bytes(utf16String); + + // Next, convert to UTF-32 + std::wstring_convert, char32_t> converter2; + return converter2.from_bytes(utf8String); + + // This is inefficient, but importantly it is _correct_, rather than a hand-rolled UTF-16 to + // UTF-32 converter which may or may not be correct. +} + + } // end namespace gui } // end namespace irr diff --git a/src/irrlicht_changes/CGUITTFont.h b/src/irrlicht_changes/CGUITTFont.h index ebbdf4cf4..6fa45678a 100644 --- a/src/irrlicht_changes/CGUITTFont.h +++ b/src/irrlicht_changes/CGUITTFont.h @@ -2,6 +2,7 @@ CGUITTFont FreeType class for Irrlicht Copyright (c) 2009-2010 John Norman Copyright (c) 2016 Nathanaƫlle Courant + Copyright (c) 2023 Caleb Butler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -35,7 +36,6 @@ #include #include #include -#include #include "util/enriched_string.h" #include "util/basic_macros.h" #include FT_FREETYPE_H @@ -289,11 +289,9 @@ namespace gui //! Returns the dimension of a text string. virtual core::dimension2d getDimension(const wchar_t* text) const; - virtual core::dimension2d getDimension(const core::ustring& text) const; //! 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; - virtual s32 getCharacterFromPos(const core::ustring& text, s32 pixel_x) const; //! Sets global kerning width for the font. virtual void setKerningWidth(s32 kerning); @@ -303,14 +301,13 @@ namespace gui //! Gets kerning values (distance between letters) for the font. If no parameters are provided, virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const; - virtual s32 getKerningWidth(const uchar32_t thisLetter=0, const uchar32_t previousLetter=0) const; + virtual s32 getKerningWidth(const char32_t thisLetter=0, const char32_t previousLetter=0) const; //! Returns the distance between letters virtual s32 getKerningHeight() const; //! Define which characters should not be drawn by the font. virtual void setInvisibleCharacters(const wchar_t *s); - virtual void setInvisibleCharacters(const core::ustring& s); //! Get the last glyph page if there's still available slots. //! If not, it will return zero. @@ -330,7 +327,7 @@ namespace gui //! Create corresponding character's software image copy from the font, //! so you can use this data just like any ordinary video::IImage. //! \param ch The character you need - virtual video::IImage* createTextureFromChar(const uchar32_t& ch); + virtual video::IImage* createTextureFromChar(const char32_t& ch); //! This function is for debugging mostly. If the page doesn't exist it returns zero. //! \param page_index Simply return the texture handle of a given page index. @@ -360,6 +357,14 @@ namespace gui static scene::IMesh* shared_plane_ptr_; static scene::SMesh shared_plane_; + // Helper functions for the same-named public member functions above + // (Since std::u32string is nicer to work with than wchar_t *) + core::dimension2d getDimension(const std::u32string& text) const; + s32 getCharacterFromPos(const std::u32string& text, s32 pixel_x) const; + + // Helper function for the above helper functions :P + std::u32string convertWCharToU32String(const wchar_t* const) const; + CGUITTFont(IGUIEnvironment *env); bool load(const io::path& filename, const u32 size, const bool antialias, const bool transparency); void reset_images(); @@ -374,13 +379,13 @@ namespace gui else load_flags |= FT_LOAD_TARGET_NORMAL; } u32 getWidthFromCharacter(wchar_t c) const; - u32 getWidthFromCharacter(uchar32_t c) const; + u32 getWidthFromCharacter(char32_t c) const; u32 getHeightFromCharacter(wchar_t c) const; - u32 getHeightFromCharacter(uchar32_t c) const; + u32 getHeightFromCharacter(char32_t c) const; u32 getGlyphIndexByChar(wchar_t c) const; - u32 getGlyphIndexByChar(uchar32_t c) const; + u32 getGlyphIndexByChar(char32_t c) const; core::vector2di getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const; - core::vector2di getKerning(const uchar32_t thisLetter, const uchar32_t previousLetter) const; + core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const; core::dimension2d getDimensionUntilEndOfLine(const wchar_t* p) const; void createSharedPlane(); @@ -398,7 +403,7 @@ namespace gui s32 GlobalKerningWidth; s32 GlobalKerningHeight; - core::ustring Invisible; + std::u32string Invisible; u32 shadow_offset; u32 shadow_alpha;