diff --git a/src/openrct2/core/String.cpp b/src/openrct2/core/String.cpp index 8cc4e44548..3b2b9d7764 100644 --- a/src/openrct2/core/String.cpp +++ b/src/openrct2/core/String.cpp @@ -784,3 +784,8 @@ namespace String return false; } } // namespace String + +char32_t CodepointView::iterator::GetNextCodepoint(const char* ch, const char** next) +{ + return utf8_get_next(ch, next); +} diff --git a/src/openrct2/core/String.hpp b/src/openrct2/core/String.hpp index 866a453de0..69d237469b 100644 --- a/src/openrct2/core/String.hpp +++ b/src/openrct2/core/String.hpp @@ -118,3 +118,80 @@ namespace String bool ContainsColourCode(const std::string& string); } // namespace String + +class CodepointView +{ +private: + std::string_view _str; + +public: + class iterator + { + private: + std::string_view _str; + size_t _index; + + public: + iterator(std::string_view str, size_t index) + : _str(str) + , _index(index) + { + } + + bool operator==(const iterator& rhs) const + { + return _index == rhs._index; + } + bool operator!=(const iterator& rhs) const + { + return _index != rhs._index; + } + const char32_t operator*() const + { + return GetNextCodepoint(&_str[_index], nullptr); + } + iterator& operator++() + { + if (_index < _str.size()) + { + const utf8* nextch; + GetNextCodepoint(&_str[_index], &nextch); + _index = nextch - _str.data(); + } + return *this; + } + iterator operator++(int) + { + auto result = *this; + if (_index < _str.size()) + { + const utf8* nextch; + GetNextCodepoint(&_str[_index], &nextch); + _index = nextch - _str.data(); + } + return result; + } + + size_t GetIndex() const + { + return _index; + } + + static char32_t GetNextCodepoint(const char* ch, const char** next); + }; + + CodepointView(std::string_view str) + : _str(str) + { + } + + iterator begin() const + { + return iterator(_str, 0); + } + + iterator end() const + { + return iterator(_str, _str.size()); + } +}; diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index 9d5c046c51..732699a8a4 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -9,6 +9,7 @@ #include "../common.h" #include "../config/Config.h" +#include "../core/String.hpp" #include "../drawing/Drawing.h" #include "../interface/Viewport.h" #include "../localisation/Formatting.h" @@ -23,81 +24,6 @@ using namespace OpenRCT2; -class CodepointView -{ -private: - std::string_view _str; - -public: - class iterator - { - private: - std::string_view _str; - size_t _index; - - public: - iterator(std::string_view str, size_t index) - : _str(str) - , _index(index) - { - } - - bool operator==(const iterator& rhs) const - { - return _index == rhs._index; - } - bool operator!=(const iterator& rhs) const - { - return _index != rhs._index; - } - const char32_t operator*() const - { - return utf8_get_next(&_str[_index], nullptr); - } - iterator& operator++() - { - if (_index < _str.size()) - { - const utf8* nextch; - utf8_get_next(&_str[_index], &nextch); - _index = nextch - _str.data(); - } - return *this; - } - iterator operator++(int) - { - auto result = *this; - if (_index < _str.size()) - { - const utf8* nextch; - utf8_get_next(&_str[_index], &nextch); - _index = nextch - _str.data(); - } - return result; - } - - size_t GetIndex() const - { - return _index; - } - }; - - CodepointView(std::string_view str) - : _str(str) - { - } - - iterator begin() const - { - return iterator(_str, 0); - } - - iterator end() const - { - return iterator(_str, _str.size()); - } -}; - enum : uint32_t { TEXT_DRAW_FLAG_INSET = 1 << 0, diff --git a/src/openrct2/drawing/ScrollingText.cpp b/src/openrct2/drawing/ScrollingText.cpp index 257324428e..e47eec2919 100644 --- a/src/openrct2/drawing/ScrollingText.cpp +++ b/src/openrct2/drawing/ScrollingText.cpp @@ -8,7 +8,9 @@ *****************************************************************************/ #include "../config/Config.h" +#include "../core/String.hpp" #include "../interface/Colour.h" +#include "../localisation/Formatting.h" #include "../localisation/Localisation.h" #include "../localisation/LocalisationService.h" #include "../paint/Paint.h" @@ -19,6 +21,8 @@ #include #include +using namespace OpenRCT2; + struct rct_draw_scroll_text { rct_string_id string_id; @@ -38,9 +42,9 @@ static uint32_t _drawSCrollNextIndex = 0; static std::mutex _scrollingTextMutex; static void scrolling_text_set_bitmap_for_sprite( - utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour); + std::string_view text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour); static void scrolling_text_set_bitmap_for_ttf( - utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour); + std::string_view text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour); void scrolling_text_initialise_bitmaps() { @@ -1490,105 +1494,98 @@ int32_t scrolling_text_setup( } static void scrolling_text_set_bitmap_for_sprite( - utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour) + std::string_view text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour) { auto characterColour = colour; + auto fmt = FmtString(text); - utf8* ch = text; - while (true) + // Repeat string a maximum of four times (eliminates possibility of infinite loop) + for (auto i = 0; i < 4; i++) { - uint32_t codepoint = utf8_get_next(ch, const_cast(&ch)); - - // If at the end of the string loop back to the start - if (codepoint == 0) + for (const auto& token : fmt) { - ch = text; - continue; - } - - // Set any change in colour - if (codepoint <= FORMAT_COLOUR_CODE_END && codepoint >= FORMAT_COLOUR_CODE_START) - { - codepoint -= FORMAT_COLOUR_CODE_START; - const rct_g1_element* g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); - if (g1 != nullptr) + if (token.IsLiteral()) { - characterColour = g1->offset[codepoint * 4]; - } - continue; - } - - // If another type of control character ignore - if (codepoint < 32) - continue; - - int32_t characterWidth = font_sprite_get_codepoint_width(FONT_SPRITE_BASE_TINY, codepoint); - uint8_t* characterBitmap = font_sprite_get_codepoint_bitmap(codepoint); - for (; characterWidth != 0; characterWidth--, characterBitmap++) - { - // Skip any non-displayed columns - if (scroll != 0) - { - scroll--; - continue; - } - - int16_t scrollPosition = *scrollPositionOffsets; - if (scrollPosition == -1) - return; - if (scrollPosition > -1) - { - uint8_t* dst = &bitmap[scrollPosition]; - for (uint8_t char_bitmap = *characterBitmap; char_bitmap != 0; char_bitmap >>= 1) + CodepointView codepoints(token.text); + for (auto codepoint : codepoints) { - if (char_bitmap & 1) - *dst = characterColour; + auto characterWidth = font_sprite_get_codepoint_width(FONT_SPRITE_BASE_TINY, codepoint); + auto characterBitmap = font_sprite_get_codepoint_bitmap(codepoint); + for (; characterWidth != 0; characterWidth--, characterBitmap++) + { + // Skip any non-displayed columns + if (scroll != 0) + { + scroll--; + continue; + } - // Jump to next row - dst += 64; + int16_t scrollPosition = *scrollPositionOffsets; + if (scrollPosition == -1) + return; + + if (scrollPosition > -1) + { + auto dst = &bitmap[scrollPosition]; + for (uint8_t char_bitmap = *characterBitmap; char_bitmap != 0; char_bitmap >>= 1) + { + if (char_bitmap & 1) + *dst = characterColour; + + // Jump to next row + dst += 64; + } + } + scrollPositionOffsets++; + } + } + } + else if (token.kind <= FORMAT_COLOUR_CODE_END && token.kind >= FORMAT_COLOUR_CODE_START) + { + auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); + if (g1 != nullptr) + { + auto colourIndex = token.kind - FORMAT_COLOUR_CODE_START; + characterColour = g1->offset[colourIndex * 4]; } } - scrollPositionOffsets++; } } } static void scrolling_text_set_bitmap_for_ttf( - utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour) + std::string_view text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour) { #ifndef NO_TTF - TTFFontDescriptor* fontDesc = ttf_get_font_from_sprite_base(FONT_SPRITE_BASE_TINY); + auto fontDesc = ttf_get_font_from_sprite_base(FONT_SPRITE_BASE_TINY); if (fontDesc->font == nullptr) { scrolling_text_set_bitmap_for_sprite(text, scroll, bitmap, scrollPositionOffsets, colour); return; } - utf8* dstCh = text; - utf8* ch = text; - int32_t codepoint; - while ((codepoint = utf8_get_next(ch, const_cast(&ch))) != 0) + thread_local std::string ttfBuffer; + ttfBuffer.clear(); + + auto fmt = FmtString(text); + for (const auto& token : fmt) { - if (utf8_is_format_code(codepoint)) + if (token.IsLiteral()) { - if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) + ttfBuffer.append(token.text); + } + else if (token.kind >= FORMAT_COLOUR_CODE_START && token.kind <= FORMAT_COLOUR_CODE_END) + { + auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); + if (g1 != nullptr) { - codepoint -= FORMAT_COLOUR_CODE_START; - auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); - if (g1 != nullptr) - { - colour = g1->offset[codepoint * 4]; - } + auto colourIndex = token.kind - FORMAT_COLOUR_CODE_START; + colour = g1->offset[colourIndex * 4]; } } - else - { - dstCh = utf8_write_codepoint(dstCh, codepoint); - } } - *dstCh = 0; - TTFSurface* surface = ttf_surface_cache_get_or_add(fontDesc->font, text); + auto surface = ttf_surface_cache_get_or_add(fontDesc->font, ttfBuffer.c_str()); if (surface == nullptr) { return;