mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-24 00:03:11 +01:00
Start eradicating old format codes
This commit is contained in:
@@ -42,7 +42,7 @@ namespace String
|
||||
bool IsNullOrEmpty(const utf8* str);
|
||||
int32_t Compare(const std::string& a, const std::string& b, bool ignoreCase = false);
|
||||
int32_t Compare(const utf8* a, const utf8* b, bool ignoreCase = false);
|
||||
bool Equals(const std::string_view& a, const std::string_view& b, bool ignoreCase);
|
||||
bool Equals(const std::string_view& a, const std::string_view& b, bool ignoreCase = false);
|
||||
bool Equals(const std::string& a, const std::string& b, bool ignoreCase = false);
|
||||
bool Equals(const utf8* a, const utf8* b, bool ignoreCase = false);
|
||||
bool StartsWith(const utf8* str, const utf8* match, bool ignoreCase = false);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "../config/Config.h"
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../interface/Viewport.h"
|
||||
#include "../localisation/Formatting.h"
|
||||
#include "../localisation/Localisation.h"
|
||||
#include "../localisation/LocalisationService.h"
|
||||
#include "../platform/platform.h"
|
||||
@@ -20,6 +21,83 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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,
|
||||
@@ -31,7 +109,7 @@ enum : uint32_t
|
||||
TEXT_DRAW_FLAG_NO_DRAW = 1u << 31
|
||||
};
|
||||
|
||||
static int32_t ttf_get_string_width(const utf8* text);
|
||||
static int32_t ttf_get_string_width(std::string_view text);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -69,9 +147,9 @@ int32_t gfx_get_string_width_new_lined(utf8* text)
|
||||
* rct2: 0x006C2321
|
||||
* buffer (esi)
|
||||
*/
|
||||
int32_t gfx_get_string_width(const utf8* buffer)
|
||||
int32_t gfx_get_string_width(std::string_view text)
|
||||
{
|
||||
return ttf_get_string_width(buffer);
|
||||
return ttf_get_string_width(text);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,84 +239,84 @@ int32_t gfx_clip_string(utf8* text, int32_t width)
|
||||
*/
|
||||
int32_t gfx_wrap_string(utf8* text, int32_t width, int32_t* outNumLines, int32_t* outFontHeight)
|
||||
{
|
||||
int32_t lineWidth = 0;
|
||||
constexpr size_t NULL_INDEX = std::numeric_limits<size_t>::max();
|
||||
thread_local std::string buffer;
|
||||
buffer.resize(0);
|
||||
|
||||
size_t currentLineIndex = 0;
|
||||
size_t splitIndex = NULL_INDEX;
|
||||
size_t numLines = 0;
|
||||
int32_t maxWidth = 0;
|
||||
*outNumLines = 0;
|
||||
|
||||
// Pointer to the start of the current word
|
||||
utf8* currentWord = nullptr;
|
||||
|
||||
// Width of line up to current word
|
||||
int32_t currentWidth = 0;
|
||||
|
||||
utf8* ch = text;
|
||||
utf8* firstCh = text;
|
||||
utf8* nextCh;
|
||||
int32_t codepoint;
|
||||
int32_t numCharactersOnLine = 0;
|
||||
while ((codepoint = utf8_get_next(ch, const_cast<const utf8**>(&nextCh))) != 0)
|
||||
FmtString fmt = text;
|
||||
for (const auto& token : fmt)
|
||||
{
|
||||
if (codepoint == ' ')
|
||||
if (token.IsLiteral())
|
||||
{
|
||||
currentWord = ch;
|
||||
currentWidth = lineWidth;
|
||||
numCharactersOnLine++;
|
||||
}
|
||||
else if (codepoint == FORMAT_NEWLINE)
|
||||
{
|
||||
*ch++ = 0;
|
||||
maxWidth = std::max(maxWidth, lineWidth);
|
||||
(*outNumLines)++;
|
||||
lineWidth = 0;
|
||||
currentWord = nullptr;
|
||||
firstCh = ch;
|
||||
numCharactersOnLine = 0;
|
||||
continue;
|
||||
}
|
||||
else if (utf8_is_format_code(codepoint))
|
||||
{
|
||||
ch = nextCh;
|
||||
ch += utf8_get_format_code_arg_length(codepoint);
|
||||
continue;
|
||||
}
|
||||
CodepointView codepoints(token.text);
|
||||
for (auto codepoint : codepoints)
|
||||
{
|
||||
buffer.push_back(codepoint);
|
||||
|
||||
uint8_t saveCh = *nextCh;
|
||||
*nextCh = 0;
|
||||
lineWidth = gfx_get_string_width(firstCh);
|
||||
*nextCh = saveCh;
|
||||
auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]);
|
||||
if (lineWidth <= width || splitIndex == NULL_INDEX)
|
||||
{
|
||||
if (codepoint == ' ')
|
||||
{
|
||||
// Mark line split here
|
||||
splitIndex = buffer.size() - 1;
|
||||
}
|
||||
else if (splitIndex == NULL_INDEX)
|
||||
{
|
||||
// Mark line split here (this is after first character of line)
|
||||
splitIndex = buffer.size();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert new line before current word
|
||||
buffer.insert(buffer.begin() + splitIndex, '\0');
|
||||
|
||||
if (lineWidth <= width || numCharactersOnLine == 0)
|
||||
{
|
||||
ch = nextCh;
|
||||
numCharactersOnLine++;
|
||||
// Recalculate the line length after splitting
|
||||
lineWidth = gfx_get_string_width(&buffer[currentLineIndex]);
|
||||
maxWidth = std::max(maxWidth, lineWidth);
|
||||
numLines++;
|
||||
|
||||
currentLineIndex = splitIndex + 1;
|
||||
splitIndex = NULL_INDEX;
|
||||
|
||||
// Trim the beginning of the new line
|
||||
while (buffer[currentLineIndex] == ' ')
|
||||
{
|
||||
buffer.erase(buffer.begin() + currentLineIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (currentWord == nullptr)
|
||||
else if (token.kind == FORMAT_NEWLINE)
|
||||
{
|
||||
// Single word is longer than line, insert null terminator
|
||||
ch += utf8_insert_codepoint(ch, 0);
|
||||
buffer.push_back('\0');
|
||||
|
||||
auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]);
|
||||
maxWidth = std::max(maxWidth, lineWidth);
|
||||
(*outNumLines)++;
|
||||
lineWidth = 0;
|
||||
currentWord = nullptr;
|
||||
firstCh = ch;
|
||||
numCharactersOnLine = 0;
|
||||
numLines++;
|
||||
splitIndex = buffer.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = currentWord;
|
||||
*ch++ = 0;
|
||||
|
||||
maxWidth = std::max(maxWidth, currentWidth);
|
||||
(*outNumLines)++;
|
||||
lineWidth = 0;
|
||||
currentWord = nullptr;
|
||||
firstCh = ch;
|
||||
numCharactersOnLine = 0;
|
||||
buffer.append(token.text);
|
||||
}
|
||||
}
|
||||
maxWidth = std::max(maxWidth, lineWidth);
|
||||
{
|
||||
// Final line width calculation
|
||||
auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]);
|
||||
maxWidth = std::max(maxWidth, lineWidth);
|
||||
}
|
||||
|
||||
std::memcpy(text, buffer.data(), buffer.size() + 1);
|
||||
*outNumLines = static_cast<int32_t>(numLines);
|
||||
*outFontHeight = gCurrentFontSpriteBase;
|
||||
return maxWidth == 0 ? lineWidth : maxWidth;
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -509,20 +587,18 @@ static void ttf_draw_character_sprite(rct_drawpixelinfo* dpi, int32_t codepoint,
|
||||
info->x += characterWidth;
|
||||
}
|
||||
|
||||
static void ttf_draw_string_raw_sprite(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
|
||||
static void ttf_draw_string_raw_sprite(rct_drawpixelinfo* dpi, const std::string_view text, text_draw_info* info)
|
||||
{
|
||||
const utf8* ch = text;
|
||||
int32_t codepoint;
|
||||
|
||||
while (!utf8_is_format_code(codepoint = utf8_get_next(ch, &ch)))
|
||||
CodepointView codepoints(text);
|
||||
for (auto codepoint : codepoints)
|
||||
{
|
||||
ttf_draw_character_sprite(dpi, codepoint, info);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NO_TTF
|
||||
|
||||
static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
|
||||
static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info)
|
||||
{
|
||||
if (!ttf_initialise())
|
||||
return;
|
||||
@@ -670,58 +746,38 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, const utf8* text, te
|
||||
|
||||
#endif // NO_TTF
|
||||
|
||||
static void ttf_draw_string_raw(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
|
||||
static void ttf_process_format_code(rct_drawpixelinfo* dpi, const FmtString::token& token, text_draw_info* info)
|
||||
{
|
||||
#ifndef NO_TTF
|
||||
if (info->flags & TEXT_DRAW_FLAG_TTF)
|
||||
{
|
||||
ttf_draw_string_raw_ttf(dpi, text, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif // NO_TTF
|
||||
ttf_draw_string_raw_sprite(dpi, text, info);
|
||||
#ifndef NO_TTF
|
||||
}
|
||||
#endif // NO_TTF
|
||||
}
|
||||
|
||||
static const utf8* ttf_process_format_code(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
|
||||
{
|
||||
const utf8* nextCh;
|
||||
int32_t codepoint;
|
||||
|
||||
codepoint = utf8_get_next(text, &nextCh);
|
||||
switch (codepoint)
|
||||
switch (token.kind)
|
||||
{
|
||||
case FORMAT_MOVE_X:
|
||||
info->x = info->startX + static_cast<uint8_t>(*nextCh++);
|
||||
// info->x = info->startX + static_cast<uint8_t>(*nextCh++);
|
||||
break;
|
||||
case FORMAT_ADJUST_PALETTE:
|
||||
{
|
||||
auto paletteMapId = static_cast<colour_t>(*nextCh++);
|
||||
auto paletteMap = GetPaletteMapForColour(paletteMapId);
|
||||
if (paletteMap)
|
||||
{
|
||||
uint32_t c = (*paletteMap)[249] + 256;
|
||||
if (!(info->flags & TEXT_DRAW_FLAG_OUTLINE))
|
||||
{
|
||||
c &= 0xFF;
|
||||
}
|
||||
info->palette[1] = c & 0xFF;
|
||||
info->palette[2] = (c >> 8) & 0xFF;
|
||||
|
||||
// Adjust the text palette
|
||||
info->palette[3] = (*paletteMap)[247];
|
||||
info->palette[4] = (*paletteMap)[248];
|
||||
info->palette[5] = (*paletteMap)[250];
|
||||
info->palette[6] = (*paletteMap)[251];
|
||||
}
|
||||
// auto paletteMapId = static_cast<colour_t>(*nextCh++);
|
||||
// auto paletteMap = GetPaletteMapForColour(paletteMapId);
|
||||
// if (paletteMap)
|
||||
// {
|
||||
// uint32_t c = (*paletteMap)[249] + 256;
|
||||
// if (!(info->flags & TEXT_DRAW_FLAG_OUTLINE))
|
||||
// {
|
||||
// c &= 0xFF;
|
||||
// }
|
||||
// info->palette[1] = c & 0xFF;
|
||||
// info->palette[2] = (c >> 8) & 0xFF;
|
||||
//
|
||||
// // Adjust the text palette
|
||||
// info->palette[3] = (*paletteMap)[247];
|
||||
// info->palette[4] = (*paletteMap)[248];
|
||||
// info->palette[5] = (*paletteMap)[250];
|
||||
// info->palette[6] = (*paletteMap)[251];
|
||||
// }
|
||||
break;
|
||||
}
|
||||
case FORMAT_3:
|
||||
case FORMAT_4:
|
||||
nextCh++;
|
||||
// nextCh++;
|
||||
break;
|
||||
case FORMAT_NEWLINE:
|
||||
info->x = info->startX;
|
||||
@@ -766,102 +822,89 @@ static const utf8* ttf_process_format_code(rct_drawpixelinfo* dpi, const utf8* t
|
||||
}
|
||||
case FORMAT_16:
|
||||
break;
|
||||
case FORMAT_INLINE_SPRITE:
|
||||
{
|
||||
uint32_t imageId;
|
||||
std::memcpy(&imageId, nextCh, sizeof(uint32_t));
|
||||
const rct_g1_element* g1 = gfx_get_g1_element(imageId & 0x7FFFF);
|
||||
if (g1 != nullptr)
|
||||
{
|
||||
if (!(info->flags & TEXT_DRAW_FLAG_NO_DRAW))
|
||||
{
|
||||
gfx_draw_sprite(dpi, imageId, { info->x, info->y }, 0);
|
||||
}
|
||||
info->x += g1->width;
|
||||
}
|
||||
nextCh += 4;
|
||||
break;
|
||||
}
|
||||
// case FORMAT_INLINE_SPRITE:
|
||||
// {
|
||||
// uint32_t imageId;
|
||||
// std::memcpy(&imageId, nextCh, sizeof(uint32_t));
|
||||
// const rct_g1_element* g1 = gfx_get_g1_element(imageId & 0x7FFFF);
|
||||
// if (g1 != nullptr)
|
||||
// {
|
||||
// if (!(info->flags & TEXT_DRAW_FLAG_NO_DRAW))
|
||||
// {
|
||||
// gfx_draw_sprite(dpi, imageId, { info->x, info->y }, 0);
|
||||
// }
|
||||
// info->x += g1->width;
|
||||
// }
|
||||
// nextCh += 4;
|
||||
// break;
|
||||
// }
|
||||
default:
|
||||
if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END)
|
||||
if (token.kind >= FORMAT_COLOUR_CODE_START && token.kind <= FORMAT_COLOUR_CODE_END)
|
||||
{
|
||||
uint16_t flags = info->flags;
|
||||
colour_char(codepoint - FORMAT_COLOUR_CODE_START, &flags, info->palette);
|
||||
colour_char(token.kind - FORMAT_COLOUR_CODE_START, &flags, info->palette);
|
||||
}
|
||||
else if (codepoint <= 0x16)
|
||||
else if (token.kind <= 0x16)
|
||||
{ // case 0x11? FORMAT_NEW_LINE_X_Y
|
||||
nextCh += 2;
|
||||
// nextCh += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextCh += 4; // never happens?
|
||||
// nextCh += 4; // never happens?
|
||||
}
|
||||
break;
|
||||
}
|
||||
return nextCh;
|
||||
}
|
||||
|
||||
static const utf8* ttf_process_glyph_run(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
|
||||
static void ttf_process_string_literal(rct_drawpixelinfo* dpi, const std::string_view text, text_draw_info* info)
|
||||
{
|
||||
utf8 buffer[512];
|
||||
const utf8* ch = text;
|
||||
const utf8* lastCh;
|
||||
int32_t codepoint;
|
||||
|
||||
#ifndef NO_TTF
|
||||
bool isTTF = info->flags & TEXT_DRAW_FLAG_TTF;
|
||||
#else
|
||||
bool isTTF = false;
|
||||
#endif // NO_TTF
|
||||
while (!utf8_is_format_code(codepoint = utf8_get_next(ch, &lastCh)))
|
||||
|
||||
if (!isTTF)
|
||||
{
|
||||
if (isTTF && utf8_should_use_sprite_for_codepoint(codepoint))
|
||||
{
|
||||
break;
|
||||
}
|
||||
ch = lastCh;
|
||||
}
|
||||
if (codepoint == 0)
|
||||
{
|
||||
ttf_draw_string_raw(dpi, text, info);
|
||||
return ch;
|
||||
ttf_draw_string_raw_sprite(dpi, text, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t length = static_cast<size_t>(ch - text);
|
||||
std::memcpy(buffer, text, length);
|
||||
buffer[length] = 0;
|
||||
ttf_draw_string_raw(dpi, buffer, info);
|
||||
return ch;
|
||||
CodepointView codepoints(text);
|
||||
size_t ttfRunIndex = 0;
|
||||
for (auto it = codepoints.begin(); it != codepoints.end(); it++)
|
||||
{
|
||||
auto codepoint = *it;
|
||||
if (utf8_should_use_sprite_for_codepoint(codepoint))
|
||||
{
|
||||
auto index = it.GetIndex();
|
||||
auto ttfLen = index - ttfRunIndex;
|
||||
if (ttfLen > 0)
|
||||
{
|
||||
// Draw the TTF run
|
||||
ttf_draw_string_raw_ttf(dpi, text.substr(ttfRunIndex, ttfLen), info);
|
||||
}
|
||||
ttfRunIndex = index;
|
||||
|
||||
// Draw the sprite font glyph
|
||||
ttf_draw_character_sprite(dpi, codepoint, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ttf_process_string(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
|
||||
static void ttf_process_string(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info)
|
||||
{
|
||||
const utf8* ch = text;
|
||||
const utf8* nextCh;
|
||||
int32_t codepoint;
|
||||
|
||||
#ifndef NO_TTF
|
||||
bool isTTF = info->flags & TEXT_DRAW_FLAG_TTF;
|
||||
#else
|
||||
bool isTTF = false;
|
||||
#endif // NO_TTF
|
||||
|
||||
while ((codepoint = utf8_get_next(ch, &nextCh)) != 0)
|
||||
FmtString fmt(text);
|
||||
for (const auto& token : fmt)
|
||||
{
|
||||
if (utf8_is_format_code(codepoint))
|
||||
if (token.IsLiteral())
|
||||
{
|
||||
ch = ttf_process_format_code(dpi, ch, info);
|
||||
}
|
||||
else if (isTTF && utf8_should_use_sprite_for_codepoint(codepoint))
|
||||
{
|
||||
ttf_draw_character_sprite(dpi, codepoint, info);
|
||||
ch = nextCh;
|
||||
ttf_process_string_literal(dpi, token.text, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = ttf_process_glyph_run(dpi, ch, info);
|
||||
ttf_process_format_code(dpi, token, info);
|
||||
}
|
||||
info->maxX = std::max(info->maxX, info->x);
|
||||
info->maxY = std::max(info->maxY, info->y);
|
||||
@@ -963,7 +1006,7 @@ void ttf_draw_string(rct_drawpixelinfo* dpi, const_utf8string text, int32_t colo
|
||||
gLastDrawStringY = info.y;
|
||||
}
|
||||
|
||||
static int32_t ttf_get_string_width(const utf8* text)
|
||||
static int32_t ttf_get_string_width(std::string_view text)
|
||||
{
|
||||
text_draw_info info;
|
||||
info.font_sprite_base = gCurrentFontSpriteBase;
|
||||
|
||||
@@ -757,7 +757,7 @@ void gfx_draw_string_with_y_offsets(
|
||||
bool forceSpriteFont);
|
||||
|
||||
int32_t gfx_wrap_string(char* buffer, int32_t width, int32_t* num_lines, int32_t* font_height);
|
||||
int32_t gfx_get_string_width(const utf8* buffer);
|
||||
int32_t gfx_get_string_width(std::string_view text);
|
||||
int32_t gfx_get_string_width_new_lined(char* buffer);
|
||||
int32_t string_get_height_raw(char* buffer);
|
||||
int32_t gfx_clip_string(char* buffer, int32_t width);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
# include "../OpenRCT2.h"
|
||||
# include "../config/Config.h"
|
||||
# include "../core/String.hpp"
|
||||
# include "../localisation/Localisation.h"
|
||||
# include "../localisation/LocalisationService.h"
|
||||
# include "../platform/platform.h"
|
||||
@@ -63,9 +64,9 @@ static uint32_t ttf_surface_cache_hash(TTF_Font* font, const utf8* text);
|
||||
static void ttf_surface_cache_dispose(ttf_cache_entry* entry);
|
||||
static void ttf_surface_cache_dispose_all();
|
||||
static void ttf_getwidth_cache_dispose_all();
|
||||
static bool ttf_get_size(TTF_Font* font, const utf8* text, int32_t* width, int32_t* height);
|
||||
static bool ttf_get_size(TTF_Font* font, std::string_view text, int32_t* outWidth, int32_t* outHeight);
|
||||
static void ttf_toggle_hinting(bool);
|
||||
static TTFSurface* ttf_render(TTF_Font* font, const utf8* text);
|
||||
static TTFSurface* ttf_render(TTF_Font* font, std::string_view text);
|
||||
|
||||
template<typename T> class FontLockHelper
|
||||
{
|
||||
@@ -181,12 +182,12 @@ static void ttf_close_font(TTF_Font* font)
|
||||
TTF_CloseFont(font);
|
||||
}
|
||||
|
||||
static uint32_t ttf_surface_cache_hash(TTF_Font* font, const utf8* text)
|
||||
static uint32_t ttf_surface_cache_hash(TTF_Font* font, std::string_view text)
|
||||
{
|
||||
uint32_t hash = static_cast<uint32_t>(((reinterpret_cast<uintptr_t>(font) * 23) ^ 0xAAAAAAAA) & 0xFFFFFFFF);
|
||||
for (const utf8* ch = text; *ch != 0; ch++)
|
||||
for (size_t i = 0; i < text.size(); i++)
|
||||
{
|
||||
hash = ror32(hash, 3) ^ (*ch * 13);
|
||||
hash = ror32(hash, 3) ^ (text[i] * 13);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
@@ -219,7 +220,7 @@ void ttf_toggle_hinting()
|
||||
ttf_toggle_hinting(true);
|
||||
}
|
||||
|
||||
TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text)
|
||||
TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, std::string_view text)
|
||||
{
|
||||
ttf_cache_entry* entry;
|
||||
|
||||
@@ -235,7 +236,7 @@ TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text)
|
||||
// Check if entry is a hit
|
||||
if (entry->surface == nullptr)
|
||||
break;
|
||||
if (entry->font == font && strcmp(entry->text, text) == 0)
|
||||
if (entry->font == font && String::Equals(entry->text, text))
|
||||
{
|
||||
_ttfSurfaceCacheHitCount++;
|
||||
entry->lastUseTick = gCurrentDrawCount;
|
||||
@@ -269,7 +270,7 @@ TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text)
|
||||
_ttfSurfaceCacheCount++;
|
||||
entry->surface = surface;
|
||||
entry->font = font;
|
||||
entry->text = _strdup(text);
|
||||
entry->text = strndup(text.data(), text.size());
|
||||
entry->lastUseTick = gCurrentDrawCount;
|
||||
return entry->surface;
|
||||
}
|
||||
@@ -295,7 +296,7 @@ static void ttf_getwidth_cache_dispose_all()
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text)
|
||||
uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, std::string_view text)
|
||||
{
|
||||
ttf_getwidth_cache_entry* entry;
|
||||
|
||||
@@ -311,7 +312,7 @@ uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text)
|
||||
// Check if entry is a hit
|
||||
if (entry->text == nullptr)
|
||||
break;
|
||||
if (entry->font == font && strcmp(entry->text, text) == 0)
|
||||
if (entry->font == font && String::Equals(entry->text, text))
|
||||
{
|
||||
_ttfGetWidthCacheHitCount++;
|
||||
entry->lastUseTick = gCurrentDrawCount;
|
||||
@@ -341,7 +342,7 @@ uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text)
|
||||
_ttfGetWidthCacheCount++;
|
||||
entry->width = width;
|
||||
entry->font = font;
|
||||
entry->text = _strdup(text);
|
||||
entry->text = strndup(text.data(), text.size());
|
||||
entry->lastUseTick = gCurrentDrawCount;
|
||||
return entry->width;
|
||||
}
|
||||
@@ -357,20 +358,24 @@ bool ttf_provides_glyph(const TTF_Font* font, codepoint_t codepoint)
|
||||
return TTF_GlyphIsProvided(font, codepoint);
|
||||
}
|
||||
|
||||
static bool ttf_get_size(TTF_Font* font, const utf8* text, int32_t* outWidth, int32_t* outHeight)
|
||||
static bool ttf_get_size(TTF_Font* font, std::string_view text, int32_t* outWidth, int32_t* outHeight)
|
||||
{
|
||||
return TTF_SizeUTF8(font, text, outWidth, outHeight);
|
||||
thread_local std::string buffer;
|
||||
buffer.assign(text);
|
||||
return TTF_SizeUTF8(font, buffer.c_str(), outWidth, outHeight);
|
||||
}
|
||||
|
||||
static TTFSurface* ttf_render(TTF_Font* font, const utf8* text)
|
||||
static TTFSurface* ttf_render(TTF_Font* font, std::string_view text)
|
||||
{
|
||||
thread_local std::string buffer;
|
||||
buffer.assign(text);
|
||||
if (TTF_GetFontHinting(font) != 0)
|
||||
{
|
||||
return TTF_RenderUTF8_Shaded(font, text, 0x000000FF, 0x000000FF);
|
||||
return TTF_RenderUTF8_Shaded(font, buffer.c_str(), 0x000000FF, 0x000000FF);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TTF_RenderUTF8_Solid(font, text, 0x000000FF);
|
||||
return TTF_RenderUTF8_Solid(font, buffer.c_str(), 0x000000FF);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "Font.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
bool ttf_initialise();
|
||||
void ttf_dispose();
|
||||
|
||||
@@ -26,8 +28,8 @@ struct TTFSurface
|
||||
|
||||
TTFFontDescriptor* ttf_get_font_from_sprite_base(uint16_t spriteBase);
|
||||
void ttf_toggle_hinting();
|
||||
TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text);
|
||||
uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text);
|
||||
TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, std::string_view text);
|
||||
uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, std::string_view text);
|
||||
bool ttf_provides_glyph(const TTF_Font* font, codepoint_t codepoint);
|
||||
void ttf_free_surface(TTFSurface* surface);
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace OpenRCT2
|
||||
}
|
||||
|
||||
FmtString::FmtString(const char* s)
|
||||
: FmtString(std::string_view(s))
|
||||
: FmtString(s == nullptr ? std::string_view() : std::string_view(s))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -176,6 +176,18 @@ namespace OpenRCT2
|
||||
return sz != nullptr ? sz : std::string_view();
|
||||
}
|
||||
|
||||
void FormatRealName(std::stringstream& ss, rct_string_id id)
|
||||
{
|
||||
if (IsRealNameStringId(id))
|
||||
{
|
||||
auto realNameIndex = id - REAL_NAME_START;
|
||||
ss << real_names[realNameIndex % std::size(real_names)];
|
||||
ss << ' ';
|
||||
ss << real_name_initials[(realNameIndex >> 10) % std::size(real_name_initials)];
|
||||
ss << '.';
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t TSize, typename TIndex> static void AppendSeperator(char (&buffer)[TSize], TIndex& i, std::string_view sep)
|
||||
{
|
||||
if (i < TSize)
|
||||
@@ -468,13 +480,6 @@ namespace OpenRCT2
|
||||
ss << arg.c_str();
|
||||
}
|
||||
break;
|
||||
case FORMAT_STRINGID:
|
||||
case FORMAT_STRINGID2:
|
||||
if constexpr (std::is_integral<T>())
|
||||
{
|
||||
ss << language_get_string(arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,11 +493,15 @@ namespace OpenRCT2
|
||||
return t == FORMAT_COMMA1DP16 || (t >= FORMAT_COMMA32 && t <= FORMAT_LENGTH);
|
||||
}
|
||||
|
||||
bool IsRealNameStringId(rct_string_id id)
|
||||
{
|
||||
return id >= REAL_NAME_START && id <= REAL_NAME_END;
|
||||
}
|
||||
|
||||
FmtString GetFmtStringById(rct_string_id id)
|
||||
{
|
||||
auto lang = language_get_string(id);
|
||||
auto fmtc = language_convert_string_to_tokens(lang);
|
||||
return FmtString(std::move(fmtc));
|
||||
auto fmtc = language_get_string(id);
|
||||
return FmtString(fmtc);
|
||||
}
|
||||
|
||||
std::stringstream& GetThreadFormatStream()
|
||||
@@ -547,8 +556,15 @@ namespace OpenRCT2
|
||||
const auto& arg = args[argIndex++];
|
||||
if (auto stringid = std::get_if<uint16_t>(&arg))
|
||||
{
|
||||
auto subfmt = GetFmtStringById(*stringid);
|
||||
FormatStringAny(ss, subfmt, args, argIndex);
|
||||
if (IsRealNameStringId(*stringid))
|
||||
{
|
||||
FormatRealName(ss, *stringid);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto subfmt = GetFmtStringById(*stringid);
|
||||
FormatStringAny(ss, subfmt, args, argIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -630,7 +646,6 @@ namespace OpenRCT2
|
||||
{
|
||||
auto sz = ReadFromArgs<const char*>(args);
|
||||
anyArgs.push_back(sz);
|
||||
BuildAnyArgListFromLegacyArgBuffer(sz, anyArgs, args);
|
||||
break;
|
||||
}
|
||||
case FORMAT_POP16:
|
||||
@@ -651,3 +666,8 @@ namespace OpenRCT2
|
||||
return FormatStringAny(buffer, bufferLen, fmt, anyArgs);
|
||||
}
|
||||
} // namespace OpenRCT2
|
||||
|
||||
void format_string(utf8* dest, size_t size, rct_string_id format, const void* args)
|
||||
{
|
||||
OpenRCT2::FormatStringLegacy(dest, size, format, args);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace OpenRCT2
|
||||
bool eol() const;
|
||||
};
|
||||
|
||||
FmtString() = default;
|
||||
FmtString(std::string&& s);
|
||||
FmtString(std::string_view s);
|
||||
FmtString(const char* s);
|
||||
@@ -76,6 +77,8 @@ namespace OpenRCT2
|
||||
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg);
|
||||
|
||||
bool CanFormatToken(FormatToken t);
|
||||
bool IsRealNameStringId(rct_string_id id);
|
||||
void FormatRealName(std::stringstream& ss, rct_string_id id);
|
||||
FmtString GetFmtStringById(rct_string_id id);
|
||||
std::stringstream& GetThreadFormatStream();
|
||||
size_t CopyStringStreamToBuffer(char* buffer, size_t bufferLen, std::stringstream& ss);
|
||||
@@ -110,9 +113,17 @@ namespace OpenRCT2
|
||||
{
|
||||
if constexpr (std::is_integral<TArg0>())
|
||||
{
|
||||
auto subfmt = GetFmtStringById(static_cast<rct_string_id>(arg0));
|
||||
auto subit = subfmt.begin();
|
||||
stack.push(&subit);
|
||||
auto stringId = static_cast<rct_string_id>(arg0);
|
||||
if (IsRealNameStringId(stringId))
|
||||
{
|
||||
FormatRealName(ss, stringId);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto subfmt = GetFmtStringById(stringId);
|
||||
auto subit = subfmt.begin();
|
||||
stack.push(&subit);
|
||||
}
|
||||
FormatString(ss, stack, argN...);
|
||||
}
|
||||
}
|
||||
@@ -145,6 +156,14 @@ namespace OpenRCT2
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<typename... TArgs>
|
||||
size_t FormatStringToBuffer(char* buffer, size_t bufferLen, const FmtString& fmt, TArgs&&... argN)
|
||||
{
|
||||
auto& ss = GetThreadFormatStream();
|
||||
FormatString(ss, fmt, argN...);
|
||||
return CopyStringStreamToBuffer(buffer, bufferLen, ss);
|
||||
}
|
||||
|
||||
template<typename... TArgs> static void FormatStringId(std::stringstream& ss, rct_string_id id, TArgs&&... argN)
|
||||
{
|
||||
auto fmt = GetFmtStringById(id);
|
||||
|
||||
@@ -51,14 +51,6 @@ const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] =
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// clang-format off
|
||||
const utf8 BlackUpArrowString[] = { static_cast<utf8>(static_cast<uint8_t>(0xC2)), static_cast<utf8>(static_cast<uint8_t>(0x8E)), static_cast<utf8>(static_cast<uint8_t>(0xE2)), static_cast<utf8>(static_cast<uint8_t>(0x96)), static_cast<utf8>(static_cast<uint8_t>(0xB2)), static_cast<utf8>(static_cast<uint8_t>(0x00)) };
|
||||
const utf8 BlackDownArrowString[] = { static_cast<utf8>(static_cast<uint8_t>(0xC2)), static_cast<utf8>(static_cast<uint8_t>(0x8E)), static_cast<utf8>(static_cast<uint8_t>(0xE2)), static_cast<utf8>(static_cast<uint8_t>(0x96)), static_cast<utf8>(static_cast<uint8_t>(0xBC)), static_cast<utf8>(static_cast<uint8_t>(0x00)) };
|
||||
const utf8 BlackLeftArrowString[] = { static_cast<utf8>(static_cast<uint8_t>(0xC2)), static_cast<utf8>(static_cast<uint8_t>(0x8E)), static_cast<utf8>(static_cast<uint8_t>(0xE2)), static_cast<utf8>(static_cast<uint8_t>(0x97)), static_cast<utf8>(static_cast<uint8_t>(0x80)), static_cast<utf8>(static_cast<uint8_t>(0x00)) };
|
||||
const utf8 BlackRightArrowString[] = { static_cast<utf8>(static_cast<uint8_t>(0xC2)), static_cast<utf8>(static_cast<uint8_t>(0x8E)), static_cast<utf8>(static_cast<uint8_t>(0xE2)), static_cast<utf8>(static_cast<uint8_t>(0x96)), static_cast<utf8>(static_cast<uint8_t>(0xB6)), static_cast<utf8>(static_cast<uint8_t>(0x00)) };
|
||||
const utf8 CheckBoxMarkString[] = { static_cast<utf8>(static_cast<uint8_t>(0xE2)), static_cast<utf8>(static_cast<uint8_t>(0x9C)), static_cast<utf8>(static_cast<uint8_t>(0x93)), static_cast<utf8>(static_cast<uint8_t>(0x00)) };
|
||||
// clang-format on
|
||||
|
||||
void utf8_remove_format_codes(utf8* text, bool allowcolours)
|
||||
{
|
||||
const utf8* ch = text;
|
||||
|
||||
@@ -85,11 +85,11 @@ struct language_descriptor
|
||||
|
||||
extern const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT];
|
||||
|
||||
extern const utf8 BlackUpArrowString[];
|
||||
extern const utf8 BlackDownArrowString[];
|
||||
extern const utf8 BlackLeftArrowString[];
|
||||
extern const utf8 BlackRightArrowString[];
|
||||
extern const utf8 CheckBoxMarkString[];
|
||||
constexpr const char* BlackUpArrowString = u8"{BLACK}▲";
|
||||
constexpr const char* BlackDownArrowString = u8"{BLACK}▼";
|
||||
constexpr const char* BlackLeftArrowString = u8"{BLACK}◀";
|
||||
constexpr const char* BlackRightArrowString = u8"{BLACK}▶";
|
||||
constexpr const char* CheckBoxMarkString = u8"✓";
|
||||
|
||||
uint8_t language_get_id_from_locale(const char* locale);
|
||||
const char* language_get_string(rct_string_id id);
|
||||
|
||||
@@ -539,28 +539,28 @@ private:
|
||||
sb.Clear();
|
||||
while (reader->TryPeek(&codepoint) && !IsNewLine(codepoint))
|
||||
{
|
||||
if (codepoint == '{')
|
||||
{
|
||||
uint32_t token;
|
||||
bool isByte;
|
||||
if (ParseToken(reader, &token, &isByte))
|
||||
{
|
||||
if (isByte)
|
||||
{
|
||||
sb.Append(reinterpret_cast<const utf8*>(&token), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(static_cast<int32_t>(token));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Syntax error or unknown token, ignore line entirely
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
// if (codepoint == '{')
|
||||
// {
|
||||
// uint32_t token;
|
||||
// bool isByte;
|
||||
// if (ParseToken(reader, &token, &isByte))
|
||||
// {
|
||||
// if (isByte)
|
||||
// {
|
||||
// sb.Append(reinterpret_cast<const utf8*>(&token), 1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// sb.Append(static_cast<int32_t>(token));
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // Syntax error or unknown token, ignore line entirely
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
{
|
||||
reader->Skip();
|
||||
sb.Append(codepoint);
|
||||
|
||||
@@ -1299,7 +1299,7 @@ std::string format_string(rct_string_id format, const void* args)
|
||||
* format (ax)
|
||||
* args (ecx)
|
||||
*/
|
||||
void format_string(utf8* dest, size_t size, rct_string_id format, const void* args)
|
||||
void format_string_old(utf8* dest, size_t size, rct_string_id format, const void* args)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (gDebugStringFormatting)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "../interface/Chat.h"
|
||||
#include "../interface/InteractiveConsole.h"
|
||||
#include "../localisation/FormatCodes.h"
|
||||
#include "../localisation/Formatting.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../paint/Paint.h"
|
||||
#include "../title/TitleScreen.h"
|
||||
@@ -83,16 +84,10 @@ void Painter::PaintReplayNotice(rct_drawpixelinfo* dpi, const char* text)
|
||||
{
|
||||
ScreenCoordsXY screenCoords(_uiContext->GetWidth() / 2, _uiContext->GetHeight() - 44);
|
||||
|
||||
// Format string
|
||||
utf8 buffer[64] = { 0 };
|
||||
utf8* ch = buffer;
|
||||
ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT);
|
||||
ch = utf8_write_codepoint(ch, FORMAT_OUTLINE);
|
||||
ch = utf8_write_codepoint(ch, FORMAT_RED);
|
||||
char buffer[64]{};
|
||||
FormatStringToBuffer(buffer, sizeof(buffer), "{MEDIUMFONT}{OUTLINE}{RED}{STRING}", text);
|
||||
|
||||
snprintf(ch, 64 - (ch - buffer), "%s", text);
|
||||
|
||||
int32_t stringWidth = gfx_get_string_width(buffer);
|
||||
auto stringWidth = gfx_get_string_width(buffer);
|
||||
screenCoords.x = screenCoords.x - stringWidth;
|
||||
|
||||
if (((gCurrentTicks >> 1) & 0xF) > 4)
|
||||
@@ -106,17 +101,10 @@ void Painter::PaintFPS(rct_drawpixelinfo* dpi)
|
||||
{
|
||||
ScreenCoordsXY screenCoords(_uiContext->GetWidth() / 2, 2);
|
||||
|
||||
// Measure FPS
|
||||
MeasureFPS();
|
||||
|
||||
// Format string
|
||||
utf8 buffer[64] = { 0 };
|
||||
utf8* ch = buffer;
|
||||
ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT);
|
||||
ch = utf8_write_codepoint(ch, FORMAT_OUTLINE);
|
||||
ch = utf8_write_codepoint(ch, FORMAT_WHITE);
|
||||
|
||||
snprintf(ch, 64 - (ch - buffer), "%d", _currentFPS);
|
||||
char buffer[64]{};
|
||||
FormatStringToBuffer(buffer, sizeof(buffer), "{MEDIUMFONT}{OUTLINE}{WHITE}{INT32}", _currentFPS);
|
||||
|
||||
// Draw Text
|
||||
int32_t stringWidth = gfx_get_string_width(buffer);
|
||||
|
||||
Reference in New Issue
Block a user