OpenRCT2/src/openrct2/drawing/Drawing.String.cpp

1058 lines
29 KiB
C++
Raw Normal View History

2014-10-06 18:36:58 +02:00
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
2014-10-06 18:36:58 +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
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
2014-10-06 18:36:58 +02:00
*****************************************************************************/
#include "../common.h"
#include "../config/Config.h"
2018-03-19 23:28:40 +01:00
#include "../drawing/Drawing.h"
2018-01-06 00:45:53 +01:00
#include "../interface/Viewport.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>
enum : uint32_t
{
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,
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
};
2018-06-22 22:59:03 +02:00
static int32_t ttf_get_string_width(const utf8* text);
2015-07-26 01:55:17 +02:00
2015-07-27 19:58:12 +02:00
/**
*
* rct2: 0x006C23B1
*/
2018-06-22 22:59:03 +02:00
int32_t gfx_get_string_width_new_lined(utf8* text)
2015-07-27 19:58:12 +02:00
{
2018-06-22 22:59:03 +02:00
utf8* ch = text;
utf8* firstCh = text;
utf8* nextCh;
utf8 backup;
int32_t codepoint;
int32_t maxWidth = 0;
2018-06-22 22:59:03 +02:00
while ((codepoint = utf8_get_next(ch, (const utf8**)&nextCh)) != 0)
{
if (codepoint == FORMAT_NEWLINE || codepoint == FORMAT_NEWLINE_SMALLER)
{
backup = *nextCh;
*nextCh = 0;
2018-01-05 19:21:57 +01:00
maxWidth = std::max(maxWidth, gfx_get_string_width(firstCh));
*nextCh = backup;
firstCh = nextCh;
}
ch = nextCh;
}
2018-01-05 19:21:57 +01:00
maxWidth = std::max(maxWidth, gfx_get_string_width(firstCh));
return maxWidth;
}
2014-10-06 18:36:58 +02:00
/**
* Return the width of the string in buffer
2014-10-06 18:36:58 +02:00
*
* rct2: 0x006C2321
* buffer (esi)
*/
2018-06-22 22:59:03 +02:00
int32_t gfx_get_string_width(const utf8* buffer)
2014-10-06 18:36:58 +02:00
{
return ttf_get_string_width(buffer);
2014-10-06 18:36:58 +02: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
{
int32_t clippedWidth;
2018-06-22 22:59:03 +02:00
if (width < 6)
{
*text = 0;
return 0;
}
clippedWidth = gfx_get_string_width(text);
2018-06-22 22:59:03 +02:00
if (clippedWidth <= width)
{
return clippedWidth;
}
utf8 backup[4];
2018-06-22 22:59:03 +02:00
utf8* ch = text;
utf8* nextCh = text;
utf8* clipCh = text;
int32_t codepoint;
2018-06-22 22:59:03 +02:00
while ((codepoint = utf8_get_next(ch, (const utf8**)&nextCh)) != 0)
{
if (utf8_is_format_code(codepoint))
{
ch = nextCh;
ch += utf8_get_format_code_arg_length(codepoint);
continue;
}
2018-06-22 22:59:03 +02:00
for (int32_t i = 0; i < 4; i++)
{
backup[i] = nextCh[i];
};
for (int32_t i = 0; i < 3; i++)
{
nextCh[i] = '.';
}
nextCh[3] = 0;
int32_t queryWidth = gfx_get_string_width(text);
2018-06-22 22:59:03 +02:00
if (queryWidth < width)
{
clipCh = nextCh;
clippedWidth = queryWidth;
2018-06-22 22:59:03 +02:00
}
else
{
for (int32_t i = 0; i < 3; i++)
{
clipCh[i] = '.';
}
clipCh[3] = 0;
return clippedWidth;
}
2018-06-22 22:59:03 +02:00
for (int32_t i = 0; i < 4; i++)
{
nextCh[i] = backup[i];
};
ch = nextCh;
}
return gfx_get_string_width(text);
2014-10-06 18:36:58 +02:00
}
/**
* Wrap the text in buffer to width, returns width of longest line.
2014-10-06 18:36:58 +02: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
{
int32_t lineWidth = 0;
int32_t maxWidth = 0;
*outNumLines = 0;
// Pointer to the start of the current word
2018-06-22 22:59:03 +02:00
utf8* currentWord = nullptr;
// Width of line up to current word
int32_t currentWidth = 0;
2018-06-22 22:59:03 +02:00
utf8* ch = text;
utf8* firstCh = text;
utf8* nextCh;
int32_t codepoint;
int32_t numCharactersOnLine = 0;
2018-06-22 22:59:03 +02:00
while ((codepoint = utf8_get_next(ch, (const utf8**)&nextCh)) != 0)
{
if (codepoint == ' ')
{
currentWord = ch;
currentWidth = lineWidth;
numCharactersOnLine++;
2018-06-22 22:59:03 +02:00
}
else if (codepoint == FORMAT_NEWLINE)
{
*ch++ = 0;
2018-01-05 19:21:57 +01:00
maxWidth = std::max(maxWidth, lineWidth);
(*outNumLines)++;
lineWidth = 0;
2018-01-07 21:43:07 +01:00
currentWord = nullptr;
firstCh = ch;
numCharactersOnLine = 0;
continue;
2018-06-22 22:59:03 +02:00
}
else if (utf8_is_format_code(codepoint))
{
ch = nextCh;
ch += utf8_get_format_code_arg_length(codepoint);
continue;
}
uint8_t saveCh = *nextCh;
*nextCh = 0;
lineWidth = gfx_get_string_width(firstCh);
*nextCh = saveCh;
2018-06-22 22:59:03 +02:00
if (lineWidth <= width || numCharactersOnLine == 0)
{
ch = nextCh;
numCharactersOnLine++;
2018-06-22 22:59:03 +02:00
}
else if (currentWord == nullptr)
{
// Single word is longer than line, insert null terminator
ch += utf8_insert_codepoint(ch, 0);
2018-01-05 19:21:57 +01:00
maxWidth = std::max(maxWidth, lineWidth);
(*outNumLines)++;
lineWidth = 0;
2018-01-07 21:43:07 +01:00
currentWord = nullptr;
firstCh = ch;
numCharactersOnLine = 0;
2018-06-22 22:59:03 +02:00
}
else
{
ch = currentWord;
*ch++ = 0;
2018-01-05 19:21:57 +01:00
maxWidth = std::max(maxWidth, currentWidth);
(*outNumLines)++;
lineWidth = 0;
2018-01-07 21:43:07 +01:00
currentWord = nullptr;
firstCh = ch;
numCharactersOnLine = 0;
}
}
2018-01-05 19:21:57 +01:00
maxWidth = std::max(maxWidth, lineWidth);
*outFontHeight = gCurrentFontSpriteBase;
return maxWidth == 0 ? lineWidth : maxWidth;
2014-10-06 18:36:58 +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(
rct_drawpixelinfo* dpi, rct_string_id format, void* args, int32_t colour, int32_t x, int32_t y)
{
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
2018-06-22 22:59:03 +02:00
char* buffer = gCommonStringFormatBuffer;
format_string(buffer, 256, format, args);
int32_t height = string_get_height_raw(buffer);
gfx_draw_string(dpi, buffer, colour, x, y - (height / 2));
}
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)
{
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
{
2018-06-22 22:59:03 +02:00
colour32 = ((uint32_t*)g1->offset)[colour & 0xFF];
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;
}
// 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
* 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)
{
int32_t eax;
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)
{
eax |= 0x0A0A00;
}
2018-06-22 22:59:03 +02:00
// Adjust text palette. Store current colour?
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
}
/**
*
* rct2: 0x006C1DB7
2014-10-06 18:36:58 +02:00
*
* left : cx
* top : dx
* numLines : bp
* text : esi
* dpi : edi
*/
2018-06-22 22:59:03 +02:00
void draw_string_centred_raw(rct_drawpixelinfo* dpi, int32_t x, int32_t y, int32_t numLines, char* text)
2014-10-06 18:36:58 +02:00
{
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
2018-06-22 22:59:03 +02:00
gfx_draw_string(dpi, (char*)"", COLOUR_BLACK, dpi->x, dpi->y);
gCurrentFontFlags = 0;
2015-06-09 16:42:25 +02:00
2018-06-22 22:59:03 +02:00
for (int32_t i = 0; i <= numLines; i++)
{
int32_t width = gfx_get_string_width(text);
gfx_draw_string(dpi, text, TEXT_COLOUR_254, x - (width / 2), y);
2015-06-09 16:42:25 +02:00
2018-06-22 22:59:03 +02:00
const utf8* ch = text;
const utf8* nextCh = nullptr;
2018-06-22 22:59:03 +02:00
while ((utf8_get_next(ch, &nextCh)) != 0)
{
ch = nextCh;
}
text = (char*)(ch + 1);
2015-06-09 16:42:25 +02:00
y += font_get_line_height(gCurrentFontSpriteBase);
}
}
2018-06-22 22:59:03 +02:00
int32_t string_get_height_raw(char* buffer)
{
uint16_t fontBase = gCurrentFontSpriteBase;
int32_t height = 0;
if (fontBase <= FONT_SPRITE_BASE_MEDIUM)
height += 10;
else if (fontBase == FONT_SPRITE_BASE_TINY)
height += 6;
2018-06-22 22:59:03 +02:00
char* ch = buffer;
while (*ch != 0)
{
char c = *ch++;
2018-06-22 22:59:03 +02:00
switch (c)
{
case FORMAT_NEWLINE:
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;
break;
2018-06-22 22:59:03 +02:00
case FORMAT_NEWLINE_SMALLER:
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;
break;
2018-06-22 22:59:03 +02:00
case FORMAT_TINYFONT:
fontBase = FONT_SPRITE_BASE_TINY;
break;
2018-06-22 22:59:03 +02:00
case FORMAT_MEDIUMFONT:
fontBase = FONT_SPRITE_BASE_MEDIUM;
break;
case FORMAT_SMALLFONT:
fontBase = FONT_SPRITE_BASE_SMALL;
break;
default:
if (c >= 32)
continue;
if (c <= 4)
{
ch++;
continue;
}
if (c <= 16)
continue;
ch += 2;
if (c <= 22)
continue;
ch += 2;
break;
}
}
return height;
2015-06-28 02:57:50 +02: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(
rct_drawpixelinfo* dpi, int32_t x, int32_t y, 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
{
int32_t numLines, fontSpriteBase, lineHeight, lineY;
2018-06-22 22:59:03 +02:00
utf8* buffer = gCommonStringFormatBuffer;
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
2018-06-22 22:59:03 +02:00
gfx_draw_string(dpi, (char*)"", colour, dpi->x, dpi->y);
format_string(buffer, 256, format, args);
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
gfx_wrap_string(buffer, width, &numLines, &fontSpriteBase);
lineHeight = font_get_line_height(fontSpriteBase);
int32_t numCharactersDrawn = 0;
int32_t numCharactersToDraw = ticks;
gCurrentFontFlags = 0;
lineY = y - ((numLines * lineHeight) / 2);
2018-06-22 22:59:03 +02:00
for (int32_t line = 0; line <= numLines; line++)
{
int32_t halfWidth = gfx_get_string_width(buffer) / 2;
2018-06-22 22:59:03 +02:00
utf8* ch = buffer;
utf8* nextCh;
int32_t codepoint;
2018-06-22 22:59:03 +02:00
while ((codepoint = utf8_get_next(ch, (const utf8**)&nextCh)) != 0)
{
if (!utf8_is_format_code(codepoint))
{
numCharactersDrawn++;
2018-06-22 22:59:03 +02:00
if (numCharactersDrawn > numCharactersToDraw)
{
*ch = 0;
break;
}
}
ch = nextCh;
}
gfx_draw_string(dpi, buffer, TEXT_COLOUR_254, x - halfWidth, lineY);
2018-06-22 22:59:03 +02:00
if (numCharactersDrawn > numCharactersToDraw)
{
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
{
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;
};
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
{
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))
{
int32_t x = info->x;
int32_t y = info->y;
2018-06-22 22:59:03 +02:00
if (info->flags & TEXT_DRAW_FLAG_Y_OFFSET_EFFECT)
{
y += *info->y_offset++;
}
gfx_draw_glpyh(dpi, sprite, x, y, info->palette);
}
2015-07-27 02:09:24 +02:00
info->x += characterWidth;
2015-07-27 02:09:24 +02:00
}
2018-06-22 22:59:03 +02:00
static void ttf_draw_string_raw_sprite(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
2015-07-26 14:58:53 +02:00
{
2018-06-22 22:59:03 +02:00
const utf8* ch = text;
int32_t codepoint;
2015-07-26 14:58:53 +02:00
2018-06-22 22:59:03 +02:00
while (!utf8_is_format_code(codepoint = utf8_get_next(ch, &ch)))
{
ttf_draw_character_sprite(dpi, codepoint, info);
};
2015-07-26 14:58:53 +02:00
}
2017-06-25 01:18:08 +02:00
#ifndef NO_TTF
2018-06-22 22:59:03 +02:00
static void ttf_draw_string_raw_ttf(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
2015-07-26 01:55:17 +02:00
{
if (!ttf_initialise())
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)
{
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)
{
info->x += ttf_getwidth_cache_get_or_add(fontDesc->font, text);
return;
2018-06-22 22:59:03 +02:00
}
else
{
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)
return;
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;
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;
int32_t skipX = drawX - dpi->x;
int32_t skipY = drawY - dpi->y;
info->x += width;
2018-06-22 22:59:03 +02:00
auto src = (const uint8_t*)surface->pixels;
uint8_t* dst = dpi->bits;
2018-06-22 22:59:03 +02:00
if (skipX < 0)
{
width += skipX;
src += -skipX;
skipX = 0;
}
2018-06-22 22:59:03 +02:00
if (skipY < 0)
{
height += skipY;
src += (-skipY * surface->pitch);
skipY = 0;
}
dst += skipX;
dst += skipY * (dpi->width + dpi->pitch);
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;
// 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)
{
// 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];
}
}
src++;
dst++;
}
// Skip any remaining bits
src += srcScanSkip;
dst += dstScanSkip;
}
}
{
dst = dst_orig;
src = src_orig;
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)
{
*(dst + width + dstScanSkip + 1) = info->palette[3];
}
if (*src > 180 || !use_hinting)
{
// Centre of the glyph: use full colour.
*dst = colour;
}
else if (use_hinting && *src > fontDesc->hinting_threshold)
{
// Simulate font hinting by shading the background colour instead.
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.
*dst = blendColours(colour, PALETTE_INDEX_0);
}
else
{
*dst = blendColours(colour, *dst);
}
}
}
src++;
dst++;
}
src += srcScanSkip;
dst += dstScanSkip;
}
}
}
2015-07-26 01:55:17 +02:00
}
2017-01-03 22:57:15 +01:00
#endif // NO_TTF
2015-07-26 01:55:17 +02:00
2018-06-22 22:59:03 +02:00
static void ttf_draw_string_raw(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
2015-07-26 01:55:17 +02:00
{
2017-01-03 22:57:15 +01:00
#ifndef NO_TTF
2018-06-22 22:59:03 +02:00
if (info->flags & TEXT_DRAW_FLAG_TTF)
{
ttf_draw_string_raw_ttf(dpi, text, info);
2018-06-22 22:59:03 +02:00
}
else
{
2017-01-03 22:57:15 +01:00
#endif // NO_TTF
ttf_draw_string_raw_sprite(dpi, text, info);
2017-01-03 22:57:15 +01:00
#ifndef NO_TTF
}
2017-01-03 22:57:15 +01:00
#endif // NO_TTF
2015-07-26 01:55:17 +02:00
}
2018-06-22 22:59:03 +02:00
static const utf8* ttf_process_format_code(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
2015-07-26 01:55:17 +02:00
{
2018-06-22 22:59:03 +02:00
const utf8* nextCh;
int32_t codepoint;
codepoint = utf8_get_next(text, &nextCh);
2018-06-22 22:59:03 +02:00
switch (codepoint)
{
2018-06-22 22:59:03 +02:00
case FORMAT_MOVE_X:
info->x = info->startX + (uint8_t)(*nextCh++);
break;
case FORMAT_ADJUST_PALETTE:
2017-10-26 14:14:37 +02:00
{
2018-06-22 22:59:03 +02:00
uint16_t eax = palette_to_g1_offset[(uint8_t)*nextCh++];
const rct_g1_element* g1 = gfx_get_g1_element(eax);
if (g1 != nullptr)
{
uint32_t ebx = g1->offset[249] + 256;
if (!(info->flags & TEXT_DRAW_FLAG_OUTLINE))
{
ebx = ebx & 0xFF;
}
info->palette[1] = ebx & 0xFF;
info->palette[2] = (ebx >> 8) & 0xFF;
2018-06-22 22:59:03 +02:00
// Adjust the text palette
std::memcpy(info->palette + 3, &(g1->offset[247]), 2);
std::memcpy(info->palette + 5, &(g1->offset[250]), 2);
2018-06-22 22:59:03 +02:00
}
break;
2017-10-26 14:14:37 +02:00
}
2018-06-22 22:59:03 +02:00
case FORMAT_3:
case FORMAT_4:
nextCh++;
break;
case FORMAT_NEWLINE:
info->x = info->startX;
info->y += font_get_line_height(info->font_sprite_base);
break;
case FORMAT_NEWLINE_SMALLER:
info->x = info->startX;
info->y += font_get_line_height_small(info->font_sprite_base);
break;
case FORMAT_TINYFONT:
info->font_sprite_base = FONT_SPRITE_BASE_TINY;
break;
case FORMAT_SMALLFONT:
info->font_sprite_base = FONT_SPRITE_BASE_SMALL;
break;
case FORMAT_MEDIUMFONT:
info->font_sprite_base = FONT_SPRITE_BASE_MEDIUM;
break;
case FORMAT_OUTLINE:
info->flags |= TEXT_DRAW_FLAG_OUTLINE;
break;
case FORMAT_OUTLINE_OFF:
info->flags &= ~TEXT_DRAW_FLAG_OUTLINE;
break;
case FORMAT_WINDOW_COLOUR_1:
{
2018-06-22 22:59:03 +02:00
uint16_t flags = info->flags;
colour_char_window(gCurrentWindowColours[0], &flags, info->palette);
break;
}
2018-06-22 22:59:03 +02:00
case FORMAT_WINDOW_COLOUR_2:
{
uint16_t flags = info->flags;
colour_char_window(gCurrentWindowColours[1], &flags, info->palette);
break;
}
case FORMAT_WINDOW_COLOUR_3:
{
uint16_t flags = info->flags;
2018-06-22 22:59:03 +02:00
colour_char_window(gCurrentWindowColours[2], &flags, info->palette);
break;
}
2018-06-22 22:59:03 +02:00
case FORMAT_16:
break;
case FORMAT_INLINE_SPRITE:
{
uint32_t imageId = *((uint32_t*)(nextCh));
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)
{
uint16_t flags = info->flags;
colour_char(codepoint - FORMAT_COLOUR_CODE_START, &flags, info->palette);
}
else if (codepoint <= 0x16)
{ // case 0x11? FORMAT_NEW_LINE_X_Y
nextCh += 2;
}
else
{
nextCh += 4; // never happens?
}
break;
}
return nextCh;
2015-07-26 01:55:17 +02:00
}
2018-06-22 22:59:03 +02:00
static const utf8* ttf_process_glyph_run(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
2015-07-26 01:55:17 +02:00
{
utf8 buffer[512];
2018-06-22 22:59:03 +02:00
const utf8* ch = text;
const utf8* lastCh;
int32_t codepoint;
2015-07-26 01:55:17 +02:00
2017-01-04 13:51:12 +01:00
#ifndef NO_TTF
bool isTTF = info->flags & TEXT_DRAW_FLAG_TTF;
2017-01-04 13:51:12 +01:00
#else
bool isTTF = false;
2017-01-04 13:51:12 +01:00
#endif // NO_TTF
2018-06-22 22:59:03 +02:00
while (!utf8_is_format_code(codepoint = utf8_get_next(ch, &lastCh)))
{
if (isTTF && utf8_should_use_sprite_for_codepoint(codepoint))
{
break;
}
ch = lastCh;
}
2018-06-22 22:59:03 +02:00
if (codepoint == 0)
{
ttf_draw_string_raw(dpi, text, info);
return ch;
2018-06-22 22:59:03 +02:00
}
else
{
size_t length = (size_t)(ch - text);
std::memcpy(buffer, text, length);
buffer[length] = 0;
ttf_draw_string_raw(dpi, buffer, info);
return ch;
}
2015-07-26 01:55:17 +02:00
}
2018-06-22 22:59:03 +02:00
static void ttf_process_string(rct_drawpixelinfo* dpi, const utf8* text, text_draw_info* info)
2015-07-26 01:55:17 +02:00
{
2018-06-22 22:59:03 +02:00
const utf8* ch = text;
const utf8* nextCh;
int32_t codepoint;
2015-07-26 18:50:01 +02:00
2017-01-04 13:51:12 +01:00
#ifndef NO_TTF
bool isTTF = info->flags & TEXT_DRAW_FLAG_TTF;
2017-01-04 13:51:12 +01:00
#else
bool isTTF = false;
2017-01-04 13:51:12 +01:00
#endif // NO_TTF
2015-07-26 18:50:01 +02:00
2018-06-22 22:59:03 +02:00
while ((codepoint = utf8_get_next(ch, &nextCh)) != 0)
{
if (utf8_is_format_code(codepoint))
{
ch = ttf_process_format_code(dpi, ch, info);
2018-06-22 22:59:03 +02:00
}
else if (isTTF && utf8_should_use_sprite_for_codepoint(codepoint))
{
ttf_draw_character_sprite(dpi, codepoint, info);
ch = nextCh;
2018-06-22 22:59:03 +02:00
}
else
{
ch = ttf_process_glyph_run(dpi, ch, 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);
}
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)
{
info->flags &= ~(TEXT_DRAW_FLAG_INSET | TEXT_DRAW_FLAG_OUTLINE | TEXT_DRAW_FLAG_DARK | TEXT_DRAW_FLAG_EXTRA_DARK);
2018-06-22 22:59:03 +02:00
if ((int16_t)info->font_sprite_base < 0)
{
info->flags |= TEXT_DRAW_FLAG_DARK;
2018-06-22 22:59:03 +02:00
if ((int16_t)info->font_sprite_base == FONT_SPRITE_BASE_MEDIUM_EXTRA_DARK)
{
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)
{
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))
{
uint16_t flags = info->flags;
colour_char_window(colour, &flags, (uint8_t*)&info->palette);
}
2018-06-22 22:59:03 +02:00
}
else
{
info->flags |= TEXT_DRAW_FLAG_INSET;
colour &= ~COLOUR_FLAG_INSET;
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)
{
eax = ColourMapA[colour].mid_light;
eax = eax << 16;
eax = eax | ColourMapA[colour].dark;
2018-06-22 22:59:03 +02:00
}
else
{
eax = ColourMapA[colour].light;
eax = eax << 16;
eax = eax | ColourMapA[colour].mid_dark;
}
2018-06-22 22:59:03 +02:00
}
else
{
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
}
2018-06-22 22:59:03 +02:00
void ttf_draw_string(rct_drawpixelinfo* dpi, const_utf8string text, int32_t colour, int32_t x, int32_t y)
2015-07-26 14:58:53 +02:00
{
2018-06-22 22:59:03 +02:00
if (text == nullptr)
return;
text_draw_info info;
info.font_sprite_base = gCurrentFontSpriteBase;
info.flags = gCurrentFontFlags;
info.startX = x;
info.startY = x;
info.x = x;
info.y = y;
2015-07-26 14:58:53 +02:00
2018-06-22 22:59:03 +02:00
if (LocalisationService_UseTrueTypeFont())
{
info.flags |= TEXT_DRAW_FLAG_TTF;
}
2015-07-26 14:58:53 +02:00
std::memcpy(info.palette, text_palette, sizeof(info.palette));
ttf_process_initial_colour(colour, &info);
ttf_process_string(dpi, text, &info);
std::memcpy(text_palette, info.palette, sizeof(info.palette));
2015-07-26 01:55:17 +02:00
gCurrentFontSpriteBase = info.font_sprite_base;
gCurrentFontFlags = info.flags;
2015-07-26 19:47:26 +02:00
gLastDrawStringX = info.x;
gLastDrawStringY = info.y;
2015-07-26 01:55:17 +02:00
}
2018-06-22 22:59:03 +02:00
static int32_t ttf_get_string_width(const utf8* text)
2015-07-26 01:55:17 +02:00
{
text_draw_info info;
info.font_sprite_base = gCurrentFontSpriteBase;
info.flags = gCurrentFontFlags;
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())
{
info.flags |= TEXT_DRAW_FLAG_TTF;
}
2015-07-26 01:55:17 +02:00
2018-01-07 21:43:07 +01:00
ttf_process_string(nullptr, text, &info);
2015-07-26 01:55:17 +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(
rct_drawpixelinfo* dpi, const utf8* text, int32_t colour, int32_t x, int32_t y, const int8_t* yOffsets,
2018-06-22 22:59:03 +02:00
bool forceSpriteFont)
2015-08-03 19:06:54 +02:00
{
text_draw_info info;
info.font_sprite_base = gCurrentFontSpriteBase;
info.flags = gCurrentFontFlags;
info.startX = x;
info.startY = x;
info.x = x;
info.y = y;
info.y_offset = yOffsets;
2015-08-03 19:06:54 +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())
{
info.flags |= TEXT_DRAW_FLAG_TTF;
}
2015-08-03 19:06:54 +02:00
std::memcpy(info.palette, text_palette, sizeof(info.palette));
ttf_process_initial_colour(colour, &info);
ttf_process_string(dpi, text, &info);
std::memcpy(text_palette, info.palette, sizeof(info.palette));
2015-08-03 19:06:54 +02:00
gCurrentFontSpriteBase = info.font_sprite_base;
gCurrentFontFlags = info.flags;
2015-08-03 19:06:54 +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
{
size_t length = strlen(path);
// Return full string if it fits
2018-06-22 22:59:03 +02:00
if (gfx_get_string_width((char*)path) <= availableWidth)
{
safe_strcpy(buffer, path, bufferSize);
return;
}
// Count path separators
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] == '/')
{
path_separators++;
}
}
// TODO: Replace with unicode ellipsis when supported
safe_strcpy(buffer, "...", bufferSize);
// Abbreviate beginning with xth separator
int32_t begin = -1;
2018-06-22 22:59:03 +02:00
for (int32_t x = 0; x < path_separators; x++)
{
do
{
begin++;
} while (path[begin] != *PATH_SEPARATOR && path[begin] != '/');
safe_strcpy(buffer + 3, path + begin, bufferSize - 3);
2018-06-22 22:59:03 +02:00
if (gfx_get_string_width(buffer) <= availableWidth)
{
return;
}
}
safe_strcpy(buffer, path, bufferSize);
2016-01-02 23:41:35 +01:00
}