2014-10-06 18:36:58 +02:00
|
|
|
/*****************************************************************************
|
2020-07-21 15:04:34 +02:00
|
|
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
2014-10-06 18:36:58 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
2014-10-06 18:36:58 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2014-10-06 18:36:58 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2018-04-24 00:15:51 +02:00
|
|
|
#include "../common.h"
|
2017-09-22 18:03:21 +02:00
|
|
|
#include "../config/Config.h"
|
2020-10-14 20:40:24 +02:00
|
|
|
#include "../core/String.hpp"
|
2018-03-19 23:28:40 +01:00
|
|
|
#include "../drawing/Drawing.h"
|
2018-01-06 00:45:53 +01:00
|
|
|
#include "../interface/Viewport.h"
|
2020-10-13 02:14:39 +02:00
|
|
|
#include "../localisation/Formatting.h"
|
2018-01-06 18:32:25 +01:00
|
|
|
#include "../localisation/Localisation.h"
|
2018-04-27 00:03:02 +02:00
|
|
|
#include "../localisation/LocalisationService.h"
|
2017-02-18 16:45:10 +01:00
|
|
|
#include "../platform/platform.h"
|
2014-10-06 18:36:58 +02:00
|
|
|
#include "../sprites.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "../util/Util.h"
|
2018-01-05 22:14:20 +01:00
|
|
|
#include "TTF.h"
|
2014-10-06 18:36:58 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
using namespace OpenRCT2;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
enum : uint32_t
|
2018-04-24 00:15:51 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
TEXT_DRAW_FLAG_INSET = 1 << 0,
|
|
|
|
TEXT_DRAW_FLAG_OUTLINE = 1 << 1,
|
|
|
|
TEXT_DRAW_FLAG_DARK = 1 << 2,
|
|
|
|
TEXT_DRAW_FLAG_EXTRA_DARK = 1 << 3,
|
2020-11-22 01:36:40 +01:00
|
|
|
TEXT_DRAW_FLAG_NO_FORMATTING = 1 << 28,
|
2017-06-06 23:24:18 +02:00
|
|
|
TEXT_DRAW_FLAG_Y_OFFSET_EFFECT = 1 << 29,
|
|
|
|
TEXT_DRAW_FLAG_TTF = 1 << 30,
|
|
|
|
TEXT_DRAW_FLAG_NO_DRAW = 1u << 31
|
2016-11-16 14:24:39 +01:00
|
|
|
};
|
|
|
|
|
2020-11-22 01:36:40 +01:00
|
|
|
static int32_t ttf_get_string_width(std::string_view text, bool noFormatting);
|
2015-07-26 01:55:17 +02:00
|
|
|
|
2015-07-27 19:58:12 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006C23B1
|
|
|
|
*/
|
2020-10-15 23:12:41 +02:00
|
|
|
int32_t gfx_get_string_width_new_lined(std::string_view text)
|
2015-07-27 19:58:12 +02:00
|
|
|
{
|
2020-10-15 23:12:41 +02:00
|
|
|
thread_local std::string buffer;
|
|
|
|
buffer.clear();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-15 23:12:41 +02:00
|
|
|
std::optional<int32_t> maxWidth;
|
|
|
|
FmtString fmt(text);
|
|
|
|
for (const auto& token : fmt)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-16 01:13:52 +02:00
|
|
|
if (token.kind == FormatToken::Newline || token.kind == FormatToken::NewlineSmall)
|
2020-10-15 23:12:41 +02:00
|
|
|
{
|
|
|
|
auto width = gfx_get_string_width(buffer);
|
|
|
|
if (!maxWidth || maxWidth > width)
|
|
|
|
{
|
|
|
|
maxWidth = width;
|
|
|
|
}
|
|
|
|
buffer.clear();
|
|
|
|
}
|
|
|
|
else
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-15 23:12:41 +02:00
|
|
|
buffer.append(token.text);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-15 23:12:41 +02:00
|
|
|
if (!maxWidth)
|
|
|
|
{
|
|
|
|
maxWidth = gfx_get_string_width(buffer);
|
|
|
|
}
|
|
|
|
return *maxWidth;
|
2014-11-01 14:10:14 +01:00
|
|
|
}
|
|
|
|
|
2014-10-06 18:36:58 +02:00
|
|
|
/**
|
2015-12-11 16:38:37 +01:00
|
|
|
* Return the width of the string in buffer
|
2014-10-06 18:36:58 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006C2321
|
|
|
|
* buffer (esi)
|
|
|
|
*/
|
2020-10-13 02:14:39 +02:00
|
|
|
int32_t gfx_get_string_width(std::string_view text)
|
2014-10-06 18:36:58 +02:00
|
|
|
{
|
2020-11-22 01:36:40 +01:00
|
|
|
return ttf_get_string_width(text, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t gfx_get_string_width_no_formatting(std::string_view text)
|
|
|
|
{
|
|
|
|
return ttf_get_string_width(text, true);
|
2014-10-06 18:36:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-12-11 16:38:37 +01:00
|
|
|
* Clip the text in buffer to width, add ellipsis and return the new width of the clipped string
|
2014-10-06 18:36:58 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006C2460
|
|
|
|
* buffer (esi)
|
|
|
|
* width (edi)
|
|
|
|
*/
|
2018-06-22 22:59:03 +02:00
|
|
|
int32_t gfx_clip_string(utf8* text, int32_t width)
|
2014-10-06 18:36:58 +02:00
|
|
|
{
|
2018-06-22 22:59:03 +02:00
|
|
|
if (width < 6)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
*text = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-15 23:12:41 +02:00
|
|
|
// If width of the full string is less than allowed width then we don't need to clip
|
|
|
|
auto clippedWidth = gfx_get_string_width(text);
|
2018-06-22 22:59:03 +02:00
|
|
|
if (clippedWidth <= width)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return clippedWidth;
|
|
|
|
}
|
|
|
|
|
2020-10-15 23:12:41 +02:00
|
|
|
// Append each character 1 by 1 with an ellipsis on the end until width is exceeded
|
|
|
|
thread_local std::string buffer;
|
|
|
|
buffer.clear();
|
|
|
|
|
|
|
|
size_t bestLength = 0;
|
|
|
|
int32_t bestWidth = 0;
|
|
|
|
|
|
|
|
FmtString fmt(text);
|
|
|
|
for (const auto& token : fmt)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-15 23:12:41 +02:00
|
|
|
CodepointView codepoints(token.text);
|
|
|
|
for (auto codepoint : codepoints)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-15 23:12:41 +02:00
|
|
|
// Add the ellipsis before checking the width
|
|
|
|
buffer.append("...");
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-22 22:01:58 +02:00
|
|
|
auto currentWidth = gfx_get_string_width(buffer);
|
2020-10-15 23:12:41 +02:00
|
|
|
if (currentWidth < width)
|
|
|
|
{
|
|
|
|
bestLength = buffer.size();
|
|
|
|
bestWidth = currentWidth;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-15 23:12:41 +02:00
|
|
|
// Trim the ellipsis
|
|
|
|
buffer.resize(bestLength - 3);
|
|
|
|
}
|
|
|
|
else
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-15 23:12:41 +02:00
|
|
|
// Width exceeded, rollback to best length and put ellipsis back
|
|
|
|
buffer.resize(bestLength);
|
2020-10-19 21:37:07 +02:00
|
|
|
for (auto i = static_cast<int32_t>(bestLength) - 1; i >= 0 && i >= static_cast<int32_t>(bestLength) - 3; i--)
|
2020-10-15 23:12:41 +02:00
|
|
|
{
|
2020-10-22 22:01:58 +02:00
|
|
|
buffer[i] = '.';
|
2020-10-15 23:12:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Copy buffer back to input text buffer
|
|
|
|
std::strcpy(text, buffer.c_str());
|
|
|
|
return bestWidth;
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-15 23:12:41 +02:00
|
|
|
char cb[8]{};
|
|
|
|
utf8_write_codepoint(cb, codepoint);
|
|
|
|
buffer.append(cb);
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
return gfx_get_string_width(text);
|
2014-10-06 18:36:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-12-11 16:38:37 +01:00
|
|
|
* Wrap the text in buffer to width, returns width of longest line.
|
2014-10-06 18:36:58 +02:00
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* Inserts NULL where line should break (as \n is used for something else),
|
|
|
|
* so the number of lines is returned in num_lines. font_height seems to be
|
|
|
|
* a control character for line height.
|
2014-10-06 18:36:58 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006C21E2
|
|
|
|
* buffer (esi)
|
|
|
|
* width (edi) - in
|
|
|
|
* num_lines (edi) - out
|
|
|
|
* font_height (ebx) - out
|
|
|
|
*/
|
2018-06-22 22:59:03 +02:00
|
|
|
int32_t gfx_wrap_string(utf8* text, int32_t width, int32_t* outNumLines, int32_t* outFontHeight)
|
2014-10-06 18:36:58 +02:00
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
constexpr size_t NULL_INDEX = std::numeric_limits<size_t>::max();
|
|
|
|
thread_local std::string buffer;
|
|
|
|
buffer.resize(0);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
size_t currentLineIndex = 0;
|
|
|
|
size_t splitIndex = NULL_INDEX;
|
2020-10-22 22:01:58 +02:00
|
|
|
size_t bestSplitIndex = NULL_INDEX;
|
2020-10-13 02:14:39 +02:00
|
|
|
size_t numLines = 0;
|
|
|
|
int32_t maxWidth = 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
FmtString fmt = text;
|
|
|
|
for (const auto& token : fmt)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
if (token.IsLiteral())
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
CodepointView codepoints(token.text);
|
|
|
|
for (auto codepoint : codepoints)
|
|
|
|
{
|
2020-10-15 23:12:41 +02:00
|
|
|
char cb[8]{};
|
|
|
|
utf8_write_codepoint(cb, codepoint);
|
|
|
|
buffer.append(cb);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]);
|
2020-10-22 22:01:58 +02:00
|
|
|
if (lineWidth <= width || (splitIndex == NULL_INDEX && bestSplitIndex == NULL_INDEX))
|
2020-10-13 02:14:39 +02:00
|
|
|
{
|
|
|
|
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)
|
2020-10-22 22:01:58 +02:00
|
|
|
bestSplitIndex = buffer.size();
|
2020-10-13 02:14:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Insert new line before current word
|
2020-10-22 22:01:58 +02:00
|
|
|
if (splitIndex == NULL_INDEX)
|
|
|
|
{
|
|
|
|
splitIndex = bestSplitIndex;
|
|
|
|
}
|
2020-10-13 02:14:39 +02:00
|
|
|
buffer.insert(buffer.begin() + splitIndex, '\0');
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
// 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;
|
2020-10-22 22:01:58 +02:00
|
|
|
bestSplitIndex = NULL_INDEX;
|
2020-10-13 02:14:39 +02:00
|
|
|
|
|
|
|
// Trim the beginning of the new line
|
|
|
|
while (buffer[currentLineIndex] == ' ')
|
|
|
|
{
|
|
|
|
buffer.erase(buffer.begin() + currentLineIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
2020-10-16 01:13:52 +02:00
|
|
|
else if (token.kind == FormatToken::Newline)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
buffer.push_back('\0');
|
|
|
|
|
|
|
|
auto lineWidth = gfx_get_string_width(&buffer[currentLineIndex]);
|
2018-01-05 19:21:57 +01:00
|
|
|
maxWidth = std::max(maxWidth, lineWidth);
|
2020-10-13 02:14:39 +02:00
|
|
|
numLines++;
|
2020-10-22 22:01:58 +02:00
|
|
|
|
|
|
|
currentLineIndex = buffer.size();
|
|
|
|
splitIndex = NULL_INDEX;
|
|
|
|
bestSplitIndex = NULL_INDEX;
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
buffer.append(token.text);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-13 02:14:39 +02:00
|
|
|
{
|
|
|
|
// 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);
|
2017-06-06 23:24:18 +02:00
|
|
|
*outFontHeight = gCurrentFontSpriteBase;
|
2020-10-13 02:14:39 +02:00
|
|
|
return maxWidth;
|
2014-10-06 18:36:58 +02:00
|
|
|
}
|
|
|
|
|
2015-06-17 17:21:50 +02:00
|
|
|
/**
|
|
|
|
* Draws text that is left aligned and vertically centred.
|
|
|
|
*/
|
2018-06-22 22:59:03 +02:00
|
|
|
void gfx_draw_string_left_centred(
|
2020-06-21 13:38:25 +02:00
|
|
|
rct_drawpixelinfo* dpi, rct_string_id format, void* args, int32_t colour, const ScreenCoordsXY& coords)
|
2015-06-17 17:21:50 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
|
2018-06-22 22:59:03 +02:00
|
|
|
char* buffer = gCommonStringFormatBuffer;
|
2017-06-06 23:24:18 +02:00
|
|
|
format_string(buffer, 256, format, args);
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t height = string_get_height_raw(buffer);
|
2020-06-21 13:38:25 +02:00
|
|
|
gfx_draw_string(dpi, buffer, colour, coords - ScreenCoordsXY{ 0, (height / 2) });
|
2015-06-17 17:21:50 +02:00
|
|
|
}
|
|
|
|
|
2014-10-06 18:36:58 +02:00
|
|
|
/**
|
|
|
|
* Changes the palette so that the next character changes colour
|
|
|
|
*/
|
2018-06-22 22:59:03 +02:00
|
|
|
static void colour_char(uint8_t colour, const uint16_t* current_font_flags, uint8_t* palette_pointer)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t colour32 = 0;
|
2018-06-22 22:59:03 +02:00
|
|
|
const rct_g1_element* g1 = gfx_get_g1_element(SPR_TEXT_PALETTE);
|
2018-01-07 21:43:07 +01:00
|
|
|
if (g1 != nullptr)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
2019-02-04 23:15:14 +01:00
|
|
|
uint32_t idx = (colour & 0xFF) * 4;
|
|
|
|
std::memcpy(&colour32, &g1->offset[idx], sizeof(colour32));
|
2017-10-26 14:14:37 +02:00
|
|
|
}
|
2014-10-06 18:36:58 +02:00
|
|
|
|
2018-09-16 15:07:32 +02:00
|
|
|
if (!(*current_font_flags & TEXT_DRAW_FLAG_OUTLINE))
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
|
|
|
colour32 = colour32 & 0x0FF0000FF;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
// Adjust text palette. Store current colour?
|
2017-10-26 14:14:37 +02:00
|
|
|
palette_pointer[1] = colour32 & 0xFF;
|
|
|
|
palette_pointer[2] = (colour32 >> 8) & 0xFF;
|
|
|
|
palette_pointer[3] = (colour32 >> 16) & 0xFF;
|
|
|
|
palette_pointer[4] = (colour32 >> 24) & 0xFF;
|
2014-10-06 18:36:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes the palette so that the next character changes colour
|
2015-12-11 16:38:37 +01:00
|
|
|
* This is specific to changing to a predefined window related colour
|
2014-10-06 18:36:58 +02:00
|
|
|
*/
|
2018-06-22 22:59:03 +02:00
|
|
|
static void colour_char_window(uint8_t colour, const uint16_t* current_font_flags, uint8_t* palette_pointer)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t eax;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
colour = NOT_TRANSLUCENT(colour);
|
|
|
|
eax = ColourMapA[colour].colour_11;
|
2018-06-22 22:59:03 +02:00
|
|
|
if (*current_font_flags & TEXT_DRAW_FLAG_OUTLINE)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
eax |= 0x0A0A00;
|
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
// Adjust text palette. Store current colour?
|
2017-06-06 23:24:18 +02:00
|
|
|
palette_pointer[1] = eax & 0xFF;
|
|
|
|
palette_pointer[2] = (eax >> 8) & 0xFF;
|
|
|
|
palette_pointer[3] = (eax >> 16) & 0xFF;
|
|
|
|
palette_pointer[4] = (eax >> 24) & 0xFF;
|
2014-10-06 18:36:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x006C1DB7
|
2014-10-06 18:36:58 +02:00
|
|
|
*
|
|
|
|
* left : cx
|
|
|
|
* top : dx
|
|
|
|
* numLines : bp
|
|
|
|
* text : esi
|
|
|
|
* dpi : edi
|
|
|
|
*/
|
2020-06-21 13:42:38 +02:00
|
|
|
void draw_string_centred_raw(rct_drawpixelinfo* dpi, const ScreenCoordsXY& coords, int32_t numLines, char* text)
|
2014-10-06 18:36:58 +02:00
|
|
|
{
|
2020-05-10 14:49:15 +02:00
|
|
|
ScreenCoordsXY screenCoords(dpi->x, dpi->y);
|
2017-06-06 23:24:18 +02:00
|
|
|
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
|
2020-06-07 23:18:11 +02:00
|
|
|
gfx_draw_string(dpi, "", COLOUR_BLACK, screenCoords);
|
2020-06-21 13:42:38 +02:00
|
|
|
screenCoords = coords;
|
2015-06-09 16:42:25 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
for (int32_t i = 0; i <= numLines; i++)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t width = gfx_get_string_width(text);
|
2020-05-10 14:49:15 +02:00
|
|
|
gfx_draw_string(dpi, text, TEXT_COLOUR_254, screenCoords - ScreenCoordsXY{ width / 2, 0 });
|
2015-06-09 16:42:25 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
const utf8* ch = text;
|
|
|
|
const utf8* nextCh = nullptr;
|
2016-03-01 12:23:48 +01:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
while ((utf8_get_next(ch, &nextCh)) != 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
ch = nextCh;
|
|
|
|
}
|
2020-04-22 17:09:29 +02:00
|
|
|
text = const_cast<char*>(ch + 1);
|
2015-06-09 16:42:25 +02:00
|
|
|
|
2020-05-10 14:49:15 +02:00
|
|
|
screenCoords.y += font_get_line_height(gCurrentFontSpriteBase);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-06-16 23:56:08 +02:00
|
|
|
}
|
|
|
|
|
2021-02-03 00:48:46 +01:00
|
|
|
int32_t string_get_height_raw(std::string_view text)
|
2015-06-16 23:56:08 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint16_t fontBase = gCurrentFontSpriteBase;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t height = 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
if (fontBase <= FONT_SPRITE_BASE_MEDIUM)
|
|
|
|
height += 10;
|
|
|
|
else if (fontBase == FONT_SPRITE_BASE_TINY)
|
|
|
|
height += 6;
|
|
|
|
|
2021-02-03 00:48:46 +01:00
|
|
|
FmtString fmt(text);
|
2020-10-16 01:13:52 +02:00
|
|
|
for (const auto& token : fmt)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-16 01:13:52 +02:00
|
|
|
switch (token.kind)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::Newline:
|
2018-06-22 22:59:03 +02:00
|
|
|
if (fontBase == FONT_SPRITE_BASE_SMALL || fontBase == FONT_SPRITE_BASE_MEDIUM)
|
|
|
|
{
|
|
|
|
height += 10;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (fontBase == FONT_SPRITE_BASE_TINY)
|
|
|
|
{
|
|
|
|
height += 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
height += 18;
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::NewlineSmall:
|
2018-06-22 22:59:03 +02:00
|
|
|
if (fontBase == FONT_SPRITE_BASE_SMALL || fontBase == FONT_SPRITE_BASE_MEDIUM)
|
|
|
|
{
|
|
|
|
height += 5;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (fontBase == FONT_SPRITE_BASE_TINY)
|
|
|
|
{
|
|
|
|
height += 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
height += 9;
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::FontTiny:
|
2018-06-22 22:59:03 +02:00
|
|
|
fontBase = FONT_SPRITE_BASE_TINY;
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::FontMedium:
|
2018-06-22 22:59:03 +02:00
|
|
|
fontBase = FONT_SPRITE_BASE_MEDIUM;
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::FontSmall:
|
2018-06-22 22:59:03 +02:00
|
|
|
fontBase = FONT_SPRITE_BASE_SMALL;
|
|
|
|
break;
|
2020-10-19 21:37:07 +02:00
|
|
|
default:
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return height;
|
2015-06-28 02:57:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x006C1F57
|
2015-06-28 02:57:50 +02:00
|
|
|
*
|
|
|
|
* colour : al
|
|
|
|
* format : bx
|
|
|
|
* x : cx
|
|
|
|
* y : dx
|
|
|
|
* text : esi
|
|
|
|
* dpi : edi
|
|
|
|
* width : bp
|
|
|
|
* ticks : ebp >> 16
|
|
|
|
*/
|
2018-06-22 22:59:03 +02:00
|
|
|
void gfx_draw_string_centred_wrapped_partial(
|
2020-06-21 13:46:51 +02:00
|
|
|
rct_drawpixelinfo* dpi, const ScreenCoordsXY& coords, int32_t width, int32_t colour, rct_string_id format, void* args,
|
2018-06-22 22:59:03 +02:00
|
|
|
int32_t ticks)
|
2015-06-28 02:57:50 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t numLines, fontSpriteBase, lineHeight, lineY;
|
2018-06-22 22:59:03 +02:00
|
|
|
utf8* buffer = gCommonStringFormatBuffer;
|
2020-05-10 14:49:15 +02:00
|
|
|
ScreenCoordsXY screenCoords(dpi->x, dpi->y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
|
2020-06-07 23:18:11 +02:00
|
|
|
gfx_draw_string(dpi, "", colour, screenCoords);
|
2017-06-06 23:24:18 +02:00
|
|
|
format_string(buffer, 256, format, args);
|
|
|
|
|
|
|
|
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
|
|
|
|
gfx_wrap_string(buffer, width, &numLines, &fontSpriteBase);
|
|
|
|
lineHeight = font_get_line_height(fontSpriteBase);
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t numCharactersDrawn = 0;
|
|
|
|
int32_t numCharactersToDraw = ticks;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-06-21 13:46:51 +02:00
|
|
|
lineY = coords.y - ((numLines * lineHeight) / 2);
|
2018-06-22 22:59:03 +02:00
|
|
|
for (int32_t line = 0; line <= numLines; line++)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t halfWidth = gfx_get_string_width(buffer) / 2;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-16 01:13:52 +02:00
|
|
|
FmtString fmt(buffer);
|
|
|
|
for (const auto& token : fmt)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-16 01:13:52 +02:00
|
|
|
bool doubleBreak = false;
|
|
|
|
if (token.IsLiteral())
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-16 01:13:52 +02:00
|
|
|
CodepointView codepoints(token.text);
|
|
|
|
for (auto it = codepoints.begin(); it != codepoints.end(); it++)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-16 01:13:52 +02:00
|
|
|
numCharactersDrawn++;
|
|
|
|
if (numCharactersDrawn > numCharactersToDraw)
|
|
|
|
{
|
|
|
|
auto ch = const_cast<char*>(&token.text[it.GetIndex()]);
|
|
|
|
*ch = '\0';
|
|
|
|
doubleBreak = true;
|
|
|
|
break;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-16 01:13:52 +02:00
|
|
|
if (doubleBreak)
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2020-06-21 13:46:51 +02:00
|
|
|
screenCoords = { coords.x - halfWidth, lineY };
|
2020-05-10 14:49:15 +02:00
|
|
|
gfx_draw_string(dpi, buffer, TEXT_COLOUR_254, screenCoords);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
if (numCharactersDrawn > numCharactersToDraw)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer = get_string_end(buffer) + 1;
|
|
|
|
lineY += lineHeight;
|
|
|
|
}
|
2015-07-26 01:55:17 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
struct text_draw_info
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t startX;
|
|
|
|
int32_t startY;
|
|
|
|
int32_t x;
|
|
|
|
int32_t y;
|
|
|
|
int32_t maxX;
|
|
|
|
int32_t maxY;
|
|
|
|
int32_t flags;
|
|
|
|
uint8_t palette[8];
|
|
|
|
uint16_t font_sprite_base;
|
2018-06-22 22:59:03 +02:00
|
|
|
const int8_t* y_offset;
|
2018-02-14 09:42:26 +01:00
|
|
|
};
|
2015-07-26 01:55:17 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
static void ttf_draw_character_sprite(rct_drawpixelinfo* dpi, int32_t codepoint, text_draw_info* info)
|
2015-07-27 02:09:24 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t characterWidth = font_sprite_get_codepoint_width(info->font_sprite_base, codepoint);
|
|
|
|
int32_t sprite = font_sprite_get_codepoint_sprite(info->font_sprite_base, codepoint);
|
2015-07-27 02:09:24 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
if (!(info->flags & TEXT_DRAW_FLAG_NO_DRAW))
|
|
|
|
{
|
2020-06-21 14:05:20 +02:00
|
|
|
auto screenCoords = ScreenCoordsXY{ info->x, info->y };
|
2018-06-22 22:59:03 +02:00
|
|
|
if (info->flags & TEXT_DRAW_FLAG_Y_OFFSET_EFFECT)
|
|
|
|
{
|
2020-06-21 14:05:20 +02:00
|
|
|
screenCoords.y += *info->y_offset++;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-05-28 20:52:20 +02:00
|
|
|
|
|
|
|
PaletteMap paletteMap(info->palette);
|
2020-06-21 14:05:20 +02:00
|
|
|
gfx_draw_glyph(dpi, sprite, screenCoords, paletteMap);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-27 02:09:24 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
info->x += characterWidth;
|
2015-07-27 02:09:24 +02:00
|
|
|
}
|
|
|
|
|
2021-01-06 17:22:22 +01:00
|
|
|
static void ttf_draw_string_raw_sprite(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info)
|
2015-07-26 14:58:53 +02:00
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
CodepointView codepoints(text);
|
|
|
|
for (auto codepoint : codepoints)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
ttf_draw_character_sprite(dpi, codepoint, info);
|
2020-10-13 02:14:39 +02:00
|
|
|
}
|
2015-07-26 14:58:53 +02:00
|
|
|
}
|
|
|
|
|
2017-06-25 01:18:08 +02:00
|
|
|
#ifndef NO_TTF
|
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info)
|
2015-07-26 01:55:17 +02:00
|
|
|
{
|
2017-06-13 21:50:14 +02:00
|
|
|
if (!ttf_initialise())
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
TTFFontDescriptor* fontDesc = ttf_get_font_from_sprite_base(info->font_sprite_base);
|
|
|
|
if (fontDesc->font == nullptr)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
ttf_draw_string_raw_sprite(dpi, text, info);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
if (info->flags & TEXT_DRAW_FLAG_NO_DRAW)
|
|
|
|
{
|
2017-06-13 21:50:14 +02:00
|
|
|
info->x += ttf_getwidth_cache_get_or_add(fontDesc->font, text);
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t colour = info->palette[1];
|
2018-06-22 22:59:03 +02:00
|
|
|
TTFSurface* surface = ttf_surface_cache_get_or_add(fontDesc->font, text);
|
2018-01-07 21:43:07 +01:00
|
|
|
if (surface == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t drawX = info->x + fontDesc->offset_x;
|
|
|
|
int32_t drawY = info->y + fontDesc->offset_y;
|
|
|
|
int32_t width = surface->w;
|
|
|
|
int32_t height = surface->h;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t overflowX = (dpi->x + dpi->width) - (drawX + width);
|
|
|
|
int32_t overflowY = (dpi->y + dpi->height) - (drawY + height);
|
2018-06-22 22:59:03 +02:00
|
|
|
if (overflowX < 0)
|
|
|
|
width += overflowX;
|
|
|
|
if (overflowY < 0)
|
|
|
|
height += overflowY;
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t skipX = drawX - dpi->x;
|
|
|
|
int32_t skipY = drawY - dpi->y;
|
2017-06-06 23:24:18 +02:00
|
|
|
info->x += width;
|
|
|
|
|
2020-04-22 17:09:29 +02:00
|
|
|
auto src = static_cast<const uint8_t*>(surface->pixels);
|
2018-06-22 22:59:03 +02:00
|
|
|
uint8_t* dst = dpi->bits;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
if (skipX < 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
width += skipX;
|
|
|
|
src += -skipX;
|
|
|
|
skipX = 0;
|
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
if (skipY < 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
height += skipY;
|
|
|
|
src += (-skipY * surface->pitch);
|
|
|
|
skipY = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dst += skipX;
|
|
|
|
dst += skipY * (dpi->width + dpi->pitch);
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t srcScanSkip = surface->pitch - width;
|
|
|
|
int32_t dstScanSkip = dpi->width + dpi->pitch - width;
|
2018-06-22 22:59:03 +02:00
|
|
|
uint8_t* dst_orig = dst;
|
|
|
|
const uint8_t* src_orig = src;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Draw shadow/outline
|
2018-06-22 22:59:03 +02:00
|
|
|
if (info->flags & TEXT_DRAW_FLAG_OUTLINE)
|
|
|
|
{
|
|
|
|
for (int32_t yy = 0; yy < height - 0; yy++)
|
|
|
|
{
|
|
|
|
for (int32_t xx = 0; xx < width - 0; xx++)
|
|
|
|
{
|
|
|
|
if (*src != 0)
|
|
|
|
{
|
2018-12-28 20:47:08 +01:00
|
|
|
// right
|
|
|
|
if (xx + skipX < dpi->width + dpi->pitch - 1)
|
|
|
|
{
|
|
|
|
*(dst + 1) = info->palette[3];
|
|
|
|
}
|
|
|
|
// left
|
|
|
|
if (xx + skipX > 1)
|
|
|
|
{
|
|
|
|
*(dst - 1) = info->palette[3];
|
|
|
|
}
|
|
|
|
// top
|
|
|
|
if (yy + skipY > 1)
|
|
|
|
{
|
|
|
|
*(dst - width - dstScanSkip) = info->palette[3];
|
|
|
|
}
|
|
|
|
// bottom
|
|
|
|
if (yy + skipY < dpi->height - 1)
|
|
|
|
{
|
|
|
|
*(dst + width + dstScanSkip) = info->palette[3];
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
src++;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
// Skip any remaining bits
|
|
|
|
src += srcScanSkip;
|
|
|
|
dst += dstScanSkip;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
dst = dst_orig;
|
|
|
|
src = src_orig;
|
2018-02-12 21:32:47 +01:00
|
|
|
bool use_hinting = gConfigFonts.enable_hinting && fontDesc->hinting_threshold > 0;
|
2018-06-22 22:59:03 +02:00
|
|
|
for (int32_t yy = 0; yy < height; yy++)
|
|
|
|
{
|
|
|
|
for (int32_t xx = 0; xx < width; xx++)
|
|
|
|
{
|
|
|
|
if (*src != 0)
|
|
|
|
{
|
|
|
|
if (info->flags & TEXT_DRAW_FLAG_INSET)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
*(dst + width + dstScanSkip + 1) = info->palette[3];
|
|
|
|
}
|
2017-09-22 12:42:22 +02:00
|
|
|
|
2018-02-12 21:32:47 +01:00
|
|
|
if (*src > 180 || !use_hinting)
|
2017-09-22 12:42:22 +02:00
|
|
|
{
|
|
|
|
// Centre of the glyph: use full colour.
|
|
|
|
*dst = colour;
|
|
|
|
}
|
2018-02-12 21:32:47 +01:00
|
|
|
else if (use_hinting && *src > fontDesc->hinting_threshold)
|
2017-09-22 12:42:22 +02:00
|
|
|
{
|
|
|
|
// Simulate font hinting by shading the background colour instead.
|
2017-09-24 01:34:21 +02:00
|
|
|
if (info->flags & TEXT_DRAW_FLAG_OUTLINE)
|
|
|
|
{
|
|
|
|
// As outlines are black, these texts should always use a darker shade
|
|
|
|
// of the foreground colour for font hinting.
|
2017-10-15 17:27:51 +02:00
|
|
|
*dst = blendColours(colour, PALETTE_INDEX_0);
|
2017-10-05 14:11:50 +02:00
|
|
|
}
|
2017-09-24 01:34:21 +02:00
|
|
|
else
|
|
|
|
{
|
2017-10-15 17:27:51 +02:00
|
|
|
*dst = blendColours(colour, *dst);
|
2017-09-24 01:34:21 +02:00
|
|
|
}
|
2017-09-22 12:42:22 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
src++;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
src += srcScanSkip;
|
|
|
|
dst += dstScanSkip;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-26 01:55:17 +02:00
|
|
|
}
|
2017-06-13 21:50:14 +02:00
|
|
|
|
2017-01-03 22:57:15 +01:00
|
|
|
#endif // NO_TTF
|
2015-07-26 01:55:17 +02:00
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
static void ttf_process_format_code(rct_drawpixelinfo* dpi, const FmtString::token& token, text_draw_info* info)
|
2015-07-26 01:55:17 +02:00
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
switch (token.kind)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::Move:
|
2020-10-15 00:46:09 +02:00
|
|
|
info->x = info->startX + token.parameter;
|
2018-06-22 22:59:03 +02:00
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::Newline:
|
2018-06-22 22:59:03 +02:00
|
|
|
info->x = info->startX;
|
|
|
|
info->y += font_get_line_height(info->font_sprite_base);
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::NewlineSmall:
|
2018-06-22 22:59:03 +02:00
|
|
|
info->x = info->startX;
|
|
|
|
info->y += font_get_line_height_small(info->font_sprite_base);
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::FontTiny:
|
2018-06-22 22:59:03 +02:00
|
|
|
info->font_sprite_base = FONT_SPRITE_BASE_TINY;
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::FontSmall:
|
2018-06-22 22:59:03 +02:00
|
|
|
info->font_sprite_base = FONT_SPRITE_BASE_SMALL;
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::FontMedium:
|
2018-06-22 22:59:03 +02:00
|
|
|
info->font_sprite_base = FONT_SPRITE_BASE_MEDIUM;
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::OutlineEnable:
|
2018-06-22 22:59:03 +02:00
|
|
|
info->flags |= TEXT_DRAW_FLAG_OUTLINE;
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::OutlineDisable:
|
2018-06-22 22:59:03 +02:00
|
|
|
info->flags &= ~TEXT_DRAW_FLAG_OUTLINE;
|
|
|
|
break;
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::ColourWindow1:
|
2017-10-28 14:33:02 +02:00
|
|
|
{
|
2018-06-22 22:59:03 +02:00
|
|
|
uint16_t flags = info->flags;
|
|
|
|
colour_char_window(gCurrentWindowColours[0], &flags, info->palette);
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::ColourWindow2:
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
|
|
|
uint16_t flags = info->flags;
|
|
|
|
colour_char_window(gCurrentWindowColours[1], &flags, info->palette);
|
|
|
|
break;
|
|
|
|
}
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::ColourWindow3:
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint16_t flags = info->flags;
|
2018-06-22 22:59:03 +02:00
|
|
|
colour_char_window(gCurrentWindowColours[2], &flags, info->palette);
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-10-16 01:13:52 +02:00
|
|
|
case FormatToken::InlineSprite:
|
2020-10-15 00:46:09 +02:00
|
|
|
{
|
|
|
|
auto g1 = gfx_get_g1_element(token.parameter & 0x7FFFF);
|
|
|
|
if (g1 != nullptr)
|
|
|
|
{
|
|
|
|
if (!(info->flags & TEXT_DRAW_FLAG_NO_DRAW))
|
|
|
|
{
|
|
|
|
gfx_draw_sprite(dpi, token.parameter, { info->x, info->y }, 0);
|
|
|
|
}
|
|
|
|
info->x += g1->width;
|
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
break;
|
2020-10-15 00:46:09 +02:00
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
default:
|
2020-10-16 01:13:52 +02:00
|
|
|
if (FormatTokenIsColour(token.kind))
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
|
|
|
uint16_t flags = info->flags;
|
2020-10-16 01:13:52 +02:00
|
|
|
auto colourIndex = FormatTokenGetTextColourIndex(token.kind);
|
|
|
|
colour_char(static_cast<uint8_t>(colourIndex), &flags, info->palette);
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-26 01:55:17 +02:00
|
|
|
}
|
|
|
|
|
2020-10-19 21:37:07 +02:00
|
|
|
static bool ShouldUseSpriteForCodepoint(char32_t codepoint)
|
|
|
|
{
|
|
|
|
switch (codepoint)
|
|
|
|
{
|
|
|
|
case UnicodeChar::up:
|
|
|
|
case UnicodeChar::down:
|
|
|
|
case UnicodeChar::leftguillemet:
|
|
|
|
case UnicodeChar::tick:
|
|
|
|
case UnicodeChar::cross:
|
|
|
|
case UnicodeChar::right:
|
|
|
|
case UnicodeChar::rightguillemet:
|
|
|
|
case UnicodeChar::small_up:
|
|
|
|
case UnicodeChar::small_down:
|
|
|
|
case UnicodeChar::left:
|
|
|
|
case UnicodeChar::quote_open:
|
|
|
|
case UnicodeChar::quote_close:
|
|
|
|
case UnicodeChar::german_quote_open:
|
|
|
|
case UnicodeChar::plus:
|
|
|
|
case UnicodeChar::minus:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-06 17:22:22 +01:00
|
|
|
static void ttf_process_string_literal(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info)
|
2015-07-26 01:55:17 +02:00
|
|
|
{
|
2017-01-04 13:51:12 +01:00
|
|
|
#ifndef NO_TTF
|
2017-06-06 23:24:18 +02:00
|
|
|
bool isTTF = info->flags & TEXT_DRAW_FLAG_TTF;
|
2017-01-04 13:51:12 +01:00
|
|
|
#else
|
2017-06-06 23:24:18 +02:00
|
|
|
bool isTTF = false;
|
2017-01-04 13:51:12 +01:00
|
|
|
#endif // NO_TTF
|
2020-10-13 02:14:39 +02:00
|
|
|
|
|
|
|
if (!isTTF)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
ttf_draw_string_raw_sprite(dpi, text, info);
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
2020-10-19 21:37:07 +02:00
|
|
|
#ifndef NO_TTF
|
2018-06-22 22:59:03 +02:00
|
|
|
else
|
|
|
|
{
|
2020-10-13 02:14:39 +02:00
|
|
|
CodepointView codepoints(text);
|
2020-10-22 22:01:58 +02:00
|
|
|
std::optional<size_t> ttfRunIndex;
|
2020-10-13 02:14:39 +02:00
|
|
|
for (auto it = codepoints.begin(); it != codepoints.end(); it++)
|
|
|
|
{
|
|
|
|
auto codepoint = *it;
|
2020-10-19 21:37:07 +02:00
|
|
|
if (ShouldUseSpriteForCodepoint(codepoint))
|
2020-10-13 02:14:39 +02:00
|
|
|
{
|
2020-10-22 22:01:58 +02:00
|
|
|
if (ttfRunIndex)
|
2020-10-13 02:14:39 +02:00
|
|
|
{
|
|
|
|
// Draw the TTF run
|
2020-10-22 22:01:58 +02:00
|
|
|
auto len = it.GetIndex() - *ttfRunIndex;
|
|
|
|
ttf_draw_string_raw_ttf(dpi, text.substr(*ttfRunIndex, len), info);
|
|
|
|
ttfRunIndex = std::nullopt;
|
2020-10-13 02:14:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the sprite font glyph
|
|
|
|
ttf_draw_character_sprite(dpi, codepoint, info);
|
|
|
|
}
|
2020-10-22 22:01:58 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!ttfRunIndex)
|
|
|
|
{
|
|
|
|
ttfRunIndex = it.GetIndex();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ttfRunIndex)
|
|
|
|
{
|
|
|
|
// Final TTF run
|
|
|
|
auto len = text.size() - *ttfRunIndex;
|
|
|
|
ttf_draw_string_raw_ttf(dpi, text.substr(*ttfRunIndex, len), info);
|
2020-10-13 02:14:39 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-10-19 21:37:07 +02:00
|
|
|
#endif // NO_TTF
|
2015-07-26 01:55:17 +02:00
|
|
|
}
|
|
|
|
|
2020-11-21 19:16:56 +01:00
|
|
|
static void ttf_process_string_codepoint(rct_drawpixelinfo* dpi, codepoint_t codepoint, text_draw_info* info)
|
|
|
|
{
|
|
|
|
char buffer[8]{};
|
|
|
|
utf8_write_codepoint(buffer, codepoint);
|
|
|
|
ttf_process_string_literal(dpi, buffer, info);
|
|
|
|
}
|
|
|
|
|
2020-10-13 02:14:39 +02:00
|
|
|
static void ttf_process_string(rct_drawpixelinfo* dpi, std::string_view text, text_draw_info* info)
|
2015-07-26 01:55:17 +02:00
|
|
|
{
|
2020-11-22 01:36:40 +01:00
|
|
|
if (info->flags & TEXT_DRAW_FLAG_NO_FORMATTING)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2020-11-22 01:36:40 +01:00
|
|
|
ttf_process_string_literal(dpi, text, info);
|
2018-01-05 19:21:57 +01:00
|
|
|
info->maxX = std::max(info->maxX, info->x);
|
|
|
|
info->maxY = std::max(info->maxY, info->y);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-11-22 01:36:40 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
FmtString fmt(text);
|
|
|
|
for (const auto& token : fmt)
|
|
|
|
{
|
|
|
|
if (token.IsLiteral())
|
|
|
|
{
|
|
|
|
ttf_process_string_literal(dpi, token.text, info);
|
|
|
|
}
|
|
|
|
else if (token.IsCodepoint())
|
|
|
|
{
|
|
|
|
auto codepoint = token.GetCodepoint();
|
|
|
|
ttf_process_string_codepoint(dpi, codepoint, info);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ttf_process_format_code(dpi, token, info);
|
|
|
|
}
|
|
|
|
info->maxX = std::max(info->maxX, info->x);
|
|
|
|
info->maxY = std::max(info->maxY, info->y);
|
|
|
|
}
|
|
|
|
}
|
2015-07-26 14:58:53 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
static void ttf_process_initial_colour(int32_t colour, text_draw_info* info)
|
2015-07-26 14:58:53 +02:00
|
|
|
{
|
2018-06-22 22:59:03 +02:00
|
|
|
if (colour != TEXT_COLOUR_254 && colour != TEXT_COLOUR_255)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
info->flags &= ~(TEXT_DRAW_FLAG_INSET | TEXT_DRAW_FLAG_OUTLINE | TEXT_DRAW_FLAG_DARK | TEXT_DRAW_FLAG_EXTRA_DARK);
|
2020-04-22 17:09:29 +02:00
|
|
|
if (static_cast<int16_t>(info->font_sprite_base) < 0)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
info->flags |= TEXT_DRAW_FLAG_DARK;
|
2020-04-22 17:09:29 +02:00
|
|
|
if (static_cast<int16_t>(info->font_sprite_base) == FONT_SPRITE_BASE_MEDIUM_EXTRA_DARK)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
info->flags |= TEXT_DRAW_FLAG_EXTRA_DARK;
|
|
|
|
}
|
|
|
|
info->font_sprite_base = FONT_SPRITE_BASE_MEDIUM;
|
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
if (colour & COLOUR_FLAG_OUTLINE)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
info->flags |= TEXT_DRAW_FLAG_OUTLINE;
|
|
|
|
}
|
|
|
|
colour &= ~COLOUR_FLAG_OUTLINE;
|
2018-06-22 22:59:03 +02:00
|
|
|
if (!(colour & COLOUR_FLAG_INSET))
|
|
|
|
{
|
|
|
|
if (!(info->flags & TEXT_DRAW_FLAG_INSET))
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint16_t flags = info->flags;
|
2020-04-22 17:09:29 +02:00
|
|
|
colour_char_window(colour, &flags, reinterpret_cast<uint8_t*>(&info->palette));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
info->flags |= TEXT_DRAW_FLAG_INSET;
|
|
|
|
colour &= ~COLOUR_FLAG_INSET;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t eax;
|
2018-06-22 22:59:03 +02:00
|
|
|
if (info->flags & TEXT_DRAW_FLAG_DARK)
|
|
|
|
{
|
|
|
|
if (info->flags & TEXT_DRAW_FLAG_EXTRA_DARK)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
eax = ColourMapA[colour].mid_light;
|
|
|
|
eax = eax << 16;
|
|
|
|
eax = eax | ColourMapA[colour].dark;
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
eax = ColourMapA[colour].light;
|
|
|
|
eax = eax << 16;
|
|
|
|
eax = eax | ColourMapA[colour].mid_dark;
|
|
|
|
}
|
2018-06-22 22:59:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
eax = ColourMapA[colour].lighter;
|
|
|
|
eax = eax << 16;
|
|
|
|
eax = eax | ColourMapA[colour].mid_light;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust text palette. Store current colour? ;
|
|
|
|
info->palette[1] = eax & 0xFF;
|
|
|
|
info->palette[2] = (eax >> 8) & 0xFF;
|
|
|
|
info->palette[3] = (eax >> 16) & 0xFF;
|
|
|
|
info->palette[4] = (eax >> 24) & 0xFF;
|
|
|
|
eax = 0;
|
|
|
|
}
|
|
|
|
}
|
2015-07-26 14:58:53 +02:00
|
|
|
}
|
|
|
|
|
2020-11-22 01:36:40 +01:00
|
|
|
void ttf_draw_string(
|
|
|
|
rct_drawpixelinfo* dpi, const_utf8string text, int32_t colour, const ScreenCoordsXY& coords, bool noFormatting)
|
2015-07-26 14:58:53 +02:00
|
|
|
{
|
2018-06-22 22:59:03 +02:00
|
|
|
if (text == nullptr)
|
|
|
|
return;
|
2015-08-16 17:36:57 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
text_draw_info info;
|
|
|
|
info.font_sprite_base = gCurrentFontSpriteBase;
|
2021-01-30 16:46:22 +01:00
|
|
|
info.flags = 0;
|
2020-06-21 13:58:29 +02:00
|
|
|
info.startX = coords.x;
|
|
|
|
info.startY = coords.y;
|
|
|
|
info.x = coords.x;
|
|
|
|
info.y = coords.y;
|
2015-07-26 14:58:53 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
if (LocalisationService_UseTrueTypeFont())
|
|
|
|
{
|
2017-06-13 21:50:14 +02:00
|
|
|
info.flags |= TEXT_DRAW_FLAG_TTF;
|
|
|
|
}
|
2015-07-26 14:58:53 +02:00
|
|
|
|
2020-11-22 01:36:40 +01:00
|
|
|
if (noFormatting)
|
|
|
|
{
|
|
|
|
info.flags |= TEXT_DRAW_FLAG_NO_FORMATTING;
|
|
|
|
}
|
|
|
|
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(info.palette, text_palette, sizeof(info.palette));
|
2017-06-06 23:24:18 +02:00
|
|
|
ttf_process_initial_colour(colour, &info);
|
|
|
|
ttf_process_string(dpi, text, &info);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(text_palette, info.palette, sizeof(info.palette));
|
2015-07-26 01:55:17 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
gCurrentFontSpriteBase = info.font_sprite_base;
|
2015-07-26 19:47:26 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
gLastDrawStringX = info.x;
|
|
|
|
gLastDrawStringY = info.y;
|
2015-07-26 01:55:17 +02:00
|
|
|
}
|
|
|
|
|
2020-11-22 01:36:40 +01:00
|
|
|
static int32_t ttf_get_string_width(std::string_view text, bool noFormatting)
|
2015-07-26 01:55:17 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
text_draw_info info;
|
|
|
|
info.font_sprite_base = gCurrentFontSpriteBase;
|
2021-01-30 16:46:22 +01:00
|
|
|
info.flags = 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
info.startX = 0;
|
|
|
|
info.startY = 0;
|
|
|
|
info.x = 0;
|
|
|
|
info.y = 0;
|
|
|
|
info.maxX = 0;
|
|
|
|
info.maxY = 0;
|
|
|
|
|
|
|
|
info.flags |= TEXT_DRAW_FLAG_NO_DRAW;
|
2018-06-22 22:59:03 +02:00
|
|
|
if (LocalisationService_UseTrueTypeFont())
|
|
|
|
{
|
2017-06-13 21:50:14 +02:00
|
|
|
info.flags |= TEXT_DRAW_FLAG_TTF;
|
|
|
|
}
|
2015-07-26 01:55:17 +02:00
|
|
|
|
2020-11-22 01:36:40 +01:00
|
|
|
if (noFormatting)
|
|
|
|
{
|
|
|
|
info.flags |= TEXT_DRAW_FLAG_NO_FORMATTING;
|
|
|
|
}
|
|
|
|
|
2018-01-07 21:43:07 +01:00
|
|
|
ttf_process_string(nullptr, text, &info);
|
2015-07-26 01:55:17 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
return info.maxX;
|
2015-07-26 01:55:17 +02:00
|
|
|
}
|
2015-08-03 19:06:54 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x00682F28
|
|
|
|
*/
|
2018-06-22 22:59:03 +02:00
|
|
|
void gfx_draw_string_with_y_offsets(
|
2020-06-21 13:55:06 +02:00
|
|
|
rct_drawpixelinfo* dpi, const utf8* text, int32_t colour, const ScreenCoordsXY& coords, const int8_t* yOffsets,
|
2018-06-22 22:59:03 +02:00
|
|
|
bool forceSpriteFont)
|
2015-08-03 19:06:54 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
text_draw_info info;
|
|
|
|
info.font_sprite_base = gCurrentFontSpriteBase;
|
2021-01-30 16:46:22 +01:00
|
|
|
info.flags = 0;
|
2020-06-21 13:55:06 +02:00
|
|
|
info.startX = coords.x;
|
|
|
|
info.startY = coords.y;
|
|
|
|
info.x = coords.x;
|
|
|
|
info.y = coords.y;
|
2017-06-06 23:24:18 +02:00
|
|
|
info.y_offset = yOffsets;
|
2015-08-03 19:06:54 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
info.flags |= TEXT_DRAW_FLAG_Y_OFFSET_EFFECT;
|
2015-08-03 19:06:54 +02:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
if (!forceSpriteFont && LocalisationService_UseTrueTypeFont())
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
info.flags |= TEXT_DRAW_FLAG_TTF;
|
|
|
|
}
|
2015-08-03 19:06:54 +02:00
|
|
|
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(info.palette, text_palette, sizeof(info.palette));
|
2017-06-06 23:24:18 +02:00
|
|
|
ttf_process_initial_colour(colour, &info);
|
|
|
|
ttf_process_string(dpi, text, &info);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(text_palette, info.palette, sizeof(info.palette));
|
2015-08-03 19:06:54 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
gCurrentFontSpriteBase = info.font_sprite_base;
|
2015-08-03 19:06:54 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
gLastDrawStringX = info.x;
|
|
|
|
gLastDrawStringY = info.y;
|
2015-08-03 19:06:54 +02:00
|
|
|
}
|
2016-01-02 23:41:35 +01:00
|
|
|
|
2018-06-22 22:59:03 +02:00
|
|
|
void shorten_path(utf8* buffer, size_t bufferSize, const utf8* path, int32_t availableWidth)
|
2016-01-02 23:41:35 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t length = strlen(path);
|
|
|
|
|
|
|
|
// Return full string if it fits
|
2020-04-22 17:09:29 +02:00
|
|
|
if (gfx_get_string_width(const_cast<char*>(path)) <= availableWidth)
|
2018-06-22 22:59:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
safe_strcpy(buffer, path, bufferSize);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Count path separators
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t path_separators = 0;
|
2018-06-22 22:59:03 +02:00
|
|
|
for (size_t x = 0; x < length; x++)
|
|
|
|
{
|
|
|
|
if (path[x] == *PATH_SEPARATOR || path[x] == '/')
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
path_separators++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Replace with unicode ellipsis when supported
|
|
|
|
safe_strcpy(buffer, "...", bufferSize);
|
|
|
|
|
|
|
|
// Abbreviate beginning with xth separator
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t begin = -1;
|
2018-06-22 22:59:03 +02:00
|
|
|
for (int32_t x = 0; x < path_separators; x++)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
begin++;
|
2017-10-08 21:28:38 +02:00
|
|
|
} while (path[begin] != *PATH_SEPARATOR && path[begin] != '/');
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
safe_strcpy(buffer + 3, path + begin, bufferSize - 3);
|
2018-06-22 22:59:03 +02:00
|
|
|
if (gfx_get_string_width(buffer) <= availableWidth)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
safe_strcpy(buffer, path, bufferSize);
|
2016-01-02 23:41:35 +01:00
|
|
|
}
|