diff --git a/openrct2.common.props b/openrct2.common.props
index 7c315c9315..b4046c449c 100644
--- a/openrct2.common.props
+++ b/openrct2.common.props
@@ -89,7 +89,7 @@
- $(SolutionDir)src;$(SolutionDir)lib\include;$(SolutionDir)lib\include\breakpad;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(SolutionDir)lib\include\zlib;$(SolutionDir)lib\include\libzip;$(IncludePath)
+ $(SolutionDir)src;$(SolutionDir)lib\include;$(SolutionDir)lib\include\breakpad;$(SolutionDir)lib\include\freetype;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(SolutionDir)lib\include\zlib;$(SolutionDir)lib\include\libzip;$(IncludePath)
$(SolutionDir)lib;$(LibraryPath)
diff --git a/src/openrct2-ui/CMakeLists.txt b/src/openrct2-ui/CMakeLists.txt
index 2076650767..dfbc69b9ec 100644
--- a/src/openrct2-ui/CMakeLists.txt
+++ b/src/openrct2-ui/CMakeLists.txt
@@ -48,7 +48,6 @@ add_executable(${PROJECT} ${OPENRCT2_UI_SOURCES} ${OPENRCT2_UI_M_SOURCES} ${OPEN
target_link_libraries(${PROJECT} "libopenrct2"
${SDL2_LIBRARIES}
- ${SDL2_TTF_LIBRARIES}
${SPEEX_LIBRARIES})
if (APPLE)
diff --git a/src/openrct2/CMakeLists.txt b/src/openrct2/CMakeLists.txt
index 3b87b6fc7a..743dc1e61c 100644
--- a/src/openrct2/CMakeLists.txt
+++ b/src/openrct2/CMakeLists.txt
@@ -14,7 +14,7 @@ option(DISABLE_RCT2 "Build a standalone version, without using code and data seg
option(DISABLE_HTTP_TWITCH "Disable HTTP and Twitch support.")
option(DISABLE_NETWORK "Disable multiplayer functionality. Mainly for testing.")
-option(DISABLE_TTF "Disable support for TTF provided by SDL2_ttf.")
+option(DISABLE_TTF "Disable support for TTF provided by freetype2.")
option(ENABLE_LIGHTFX "Enable lighting effects." ON)
if (NOT DISABLE_RCT2)
@@ -59,14 +59,10 @@ endif ()
PKG_CHECK_MODULES(SDL2 REQUIRED sdl2)
PKG_CHECK_MODULES(SPEEX REQUIRED speexdsp)
if (NOT DISABLE_TTF)
- if (STATIC)
- # FreeType is required by SDL2_ttf, but not wired up properly in package
- PKG_CHECK_MODULES(FREETYPE REQUIRED freetype2)
- endif ()
if (UNIX AND NOT APPLE)
PKG_CHECK_MODULES(FONTCONFIG REQUIRED fontconfig)
endif ()
- PKG_CHECK_MODULES(SDL2_TTF REQUIRED SDL2_ttf)
+ PKG_CHECK_MODULES(FREETYPE REQUIRED freetype2)
endif ()
# Sources
@@ -189,13 +185,12 @@ endif ()
if (NOT DISABLE_TTF)
if (STATIC)
- target_link_libraries(${PROJECT} ${FREETYPE_STATIC_LIBRARIES}
- ${SDL2_TTF_STATIC_LIBRARIES})
+ target_link_libraries(${PROJECT} ${FREETYPE_STATIC_LIBRARIES})
if (UNIX AND NOT APPLE)
target_link_libraries(${PROJECT} ${FONTCONFIG_STATIC_LIBRARIES})
endif ()
else ()
- target_link_libraries(${PROJECT} ${SDL2_TTF_LIBRARIES})
+ target_link_libraries(${PROJECT} ${FREETYPE_LIBRARIES})
if (UNIX AND NOT APPLE)
target_link_libraries(${PROJECT} ${FONTCONFIG_LIBRARIES})
endif ()
@@ -210,6 +205,7 @@ endif()
# Includes
target_include_directories(${PROJECT} SYSTEM PRIVATE ${LIBZIP_INCLUDE_DIRS})
target_include_directories(${PROJECT} PRIVATE ${SDL2_INCLUDE_DIRS}
+ ${FREETYPE_INCLUDE_DIRS}
${JANSSON_INCLUDE_DIRS}
${SPEEX_INCLUDE_DIRS}
${PNG_INCLUDE_DIRS}
diff --git a/src/openrct2/drawing/drawing.h b/src/openrct2/drawing/drawing.h
index 981b6cd976..d764d1303a 100644
--- a/src/openrct2/drawing/drawing.h
+++ b/src/openrct2/drawing/drawing.h
@@ -21,8 +21,6 @@
#include "../interface/colour.h"
#include "font.h"
-typedef struct SDL_Surface SDL_Surface;
-
// For g1 only enable packing when still relying on vanilla
#ifndef NO_RCT2
#pragma pack(push, 1)
@@ -360,13 +358,6 @@ void gfx_draw_string_centred_wrapped_partial(rct_drawpixelinfo *dpi, sint32 x, s
void gfx_draw_string_with_y_offsets(rct_drawpixelinfo *dpi, const utf8 *text, sint32 colour, sint32 x, sint32 y, const sint8 *yOffsets, bool forceSpriteFont);
sint32 gfx_clip_string(char* buffer, sint32 width);
void shorten_path(utf8 *buffer, size_t bufferSize, const utf8 *path, sint32 availableWidth);
-#ifndef NO_TTF
-SDL_Surface *ttf_surface_cache_get_or_add(TTF_Font *font, const utf8 *text);
-TTFFontDescriptor *ttf_get_font_from_sprite_base(uint16 spriteBase);
-#endif // NO_TTF
-
-bool ttf_initialise();
-void ttf_dispose();
// scrolling text
void scrolling_text_initialise_bitmaps();
diff --git a/src/openrct2/drawing/font.c b/src/openrct2/drawing/font.c
index 34a7d89684..8077259ffb 100644
--- a/src/openrct2/drawing/font.c
+++ b/src/openrct2/drawing/font.c
@@ -14,15 +14,12 @@
*****************************************************************************/
#pragma endregion
-#ifndef NO_TTF
-#include "../common.h"
-#include
-#endif
#include "../rct2/addresses.h"
#include "../localisation/localisation.h"
#include "../sprites.h"
#include "drawing.h"
#include "font.h"
+#include "ttf.h"
static const sint32 SpriteFontLineHeight[] = { 6, 10, 10, 18 };
@@ -202,14 +199,14 @@ bool font_supports_string_ttf(const utf8 *text, sint32 fontSize)
{
#ifndef NO_TTF
const utf8 *src = text;
- const TTF_Font *font = gCurrentTTFFontSet->size[fontSize].font;
+ const TTFFont *font = gCurrentTTFFontSet->size[fontSize].font;
if (font == NULL) {
return false;
}
uint32 codepoint;
while ((codepoint = utf8_get_next(src, &src)) != 0) {
- bool supported = TTF_GlyphIsProvided(font, (uint16)codepoint);
+ bool supported = ttf_provides_glyph(font, codepoint);
if (!supported) {
return false;
}
diff --git a/src/openrct2/drawing/font.h b/src/openrct2/drawing/font.h
index a662431b45..f77ee4a1d9 100644
--- a/src/openrct2/drawing/font.h
+++ b/src/openrct2/drawing/font.h
@@ -17,10 +17,6 @@
#ifndef _DRAWING_FONT_H_
#define _DRAWING_FONT_H_
-#ifndef NO_TTF
-typedef struct _TTF_Font TTF_Font;
-#endif // NO_TTF
-
#include "../common.h"
enum {
@@ -43,6 +39,11 @@ enum {
};
#ifndef NO_TTF
+typedef struct FT_FaceRec_* FT_Face;
+typedef struct TTFFont {
+ FT_Face face;
+} TTFFont;
+
typedef struct TTFFontDescriptor {
const utf8 *filename;
const utf8 *font_name;
@@ -50,7 +51,7 @@ typedef struct TTFFontDescriptor {
sint32 offset_x;
sint32 offset_y;
sint32 line_height;
- TTF_Font *font;
+ TTFFont * font;
} TTFFontDescriptor;
typedef struct TTFFontSetDescriptor {
diff --git a/src/openrct2/drawing/scrolling_text.c b/src/openrct2/drawing/scrolling_text.c
index 3b13286098..1585d962c0 100644
--- a/src/openrct2/drawing/scrolling_text.c
+++ b/src/openrct2/drawing/scrolling_text.c
@@ -14,16 +14,13 @@
*****************************************************************************/
#pragma endregion
-#ifndef NO_TTF
-#include "../common.h"
-#include
-#endif
#include "../rct2/addresses.h"
#include "../config/Config.h"
#include "../interface/colour.h"
#include "../localisation/localisation.h"
#include "../sprites.h"
#include "drawing.h"
+#include "ttf.h"
#pragma pack(push, 1)
/* size: 0xA12 */
@@ -1543,19 +1540,15 @@ void scrolling_text_set_bitmap_for_ttf(utf8 *text, sint32 scroll, uint8 *bitmap,
colour = g1Elements[SPR_TEXT_PALETTE].offset[(colour - FORMAT_COLOUR_CODE_START) * 4];
}
- SDL_Surface *surface = ttf_surface_cache_get_or_add(fontDesc->font, text);
+ TTFSurface * surface = ttf_surface_cache_get_or_add(fontDesc->font, text);
if (surface == NULL) {
return;
}
- if (SDL_MUSTLOCK(surface) && SDL_LockSurface(surface) == -1) {
- return;
- }
-
sint32 pitch = surface->pitch;
sint32 width = surface->w;
sint32 height = surface->h;
- uint8 *src = surface->pixels;
+ const uint8 *src = surface->pixels;
// Offset
height -= 3;
@@ -1586,7 +1579,5 @@ void scrolling_text_set_bitmap_for_ttf(utf8 *text, sint32 scroll, uint8 *bitmap,
x++;
if (x >= width) x = 0;
}
-
- if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
#endif // NO_TTF
}
diff --git a/src/openrct2/drawing/string.c b/src/openrct2/drawing/string.c
index 0304d13242..0ff2036830 100644
--- a/src/openrct2/drawing/string.c
+++ b/src/openrct2/drawing/string.c
@@ -14,16 +14,13 @@
*****************************************************************************/
#pragma endregion
-#ifndef NO_TTF
-#include "../common.h"
-#include
-#endif
#include "../interface/colour.h"
#include "../interface/viewport.h"
#include "../localisation/localisation.h"
#include "../platform/platform.h"
#include "../sprites.h"
#include "../util/util.h"
+#include "ttf.h"
enum {
TEXT_DRAW_FLAG_INSET = 1 << 0,
@@ -31,46 +28,13 @@ enum {
TEXT_DRAW_FLAG_DARK = 1 << 2,
TEXT_DRAW_FLAG_EXTRA_DARK = 1 << 3,
TEXT_DRAW_FLAG_Y_OFFSET_EFFECT = 1 << 29,
-#ifndef NO_TTF
TEXT_DRAW_FLAG_TTF = 1 << 30,
-#endif // NO_TTF
TEXT_DRAW_FLAG_NO_DRAW = 1u << 31
};
static sint32 ttf_get_string_width(const utf8 *text);
static void ttf_draw_string(rct_drawpixelinfo *dpi, char *buffer, sint32 colour, sint32 x, sint32 y);
-#ifndef NO_TTF
-static bool _ttfInitialised = false;
-
-#define TTF_SURFACE_CACHE_SIZE 256
-#define TTF_GETWIDTH_CACHE_SIZE 1024
-
-typedef struct ttf_cache_entry {
- SDL_Surface *surface;
- TTF_Font *font;
- utf8 *text;
- uint32 lastUseTick;
-} ttf_cache_entry;
-
-static ttf_cache_entry _ttfSurfaceCache[TTF_SURFACE_CACHE_SIZE] = { 0 };
-static sint32 _ttfSurfaceCacheCount = 0;
-static sint32 _ttfSurfaceCacheHitCount = 0;
-static sint32 _ttfSurfaceCacheMissCount = 0;
-
-typedef struct ttf_getwidth_cache_entry {
- uint32 width;
- TTF_Font *font;
- utf8 *text;
- uint32 lastUseTick;
-} ttf_getwidth_cache_entry;
-
-static ttf_getwidth_cache_entry _ttfGetWidthCache[TTF_GETWIDTH_CACHE_SIZE] = { 0 };
-static sint32 _ttfGetWidthCacheCount = 0;
-static sint32 _ttfGetWidthCacheHitCount = 0;
-static sint32 _ttfGetWidthCacheMissCount = 0;
-#endif // NO_TTF
-
/**
*
* rct2: 0x006C23B1
@@ -711,206 +675,6 @@ void gfx_draw_string_centred_wrapped_partial(rct_drawpixelinfo *dpi, sint32 x, s
}
}
-#ifndef NO_TTF
-static uint32 _ttf_surface_cache_hash(TTF_Font *font, const utf8 *text)
-{
- uint32 hash = (uint32)((((uintptr_t)font * 23) ^ 0xAAAAAAAA) & 0xFFFFFFFF);
- for (const utf8 *ch = text; *ch != 0; ch++) {
- hash = ror32(hash, 3) ^ (*ch * 13);
- }
- return hash;
-}
-
-static void _ttf_surface_cache_dispose(ttf_cache_entry *entry)
-{
- if (entry->surface != NULL) {
- SDL_FreeSurface(entry->surface);
- free(entry->text);
-
- entry->surface = NULL;
- entry->font = NULL;
- entry->text = NULL;
- }
-}
-
-static void _ttf_surface_cache_dispose_all()
-{
- for (sint32 i = 0; i < TTF_SURFACE_CACHE_SIZE; i++) {
- _ttf_surface_cache_dispose(&_ttfSurfaceCache[i]);
- _ttfSurfaceCacheCount--;
- }
-}
-
-SDL_Surface *ttf_surface_cache_get_or_add(TTF_Font *font, const utf8 *text)
-{
- ttf_cache_entry *entry;
-
- uint32 hash = _ttf_surface_cache_hash(font, text);
- sint32 index = hash % TTF_SURFACE_CACHE_SIZE;
- for (sint32 i = 0; i < TTF_SURFACE_CACHE_SIZE; i++) {
- entry = &_ttfSurfaceCache[index];
-
- // Check if entry is a hit
- if (entry->surface == NULL) break;
- if (entry->font == font && strcmp(entry->text, text) == 0) {
- _ttfSurfaceCacheHitCount++;
- entry->lastUseTick = gCurrentDrawCount;
- return entry->surface;
- }
-
- // If entry hasn't been used for a while, replace it
- if (entry->lastUseTick < gCurrentDrawCount - 64) {
- break;
- }
-
- // Check if next entry is a hit
- if (++index >= TTF_SURFACE_CACHE_SIZE) index = 0;
- }
-
- // Cache miss, replace entry with new surface
- entry = &_ttfSurfaceCache[index];
- _ttf_surface_cache_dispose(entry);
-
- SDL_Color c = { 0, 0, 0, 255 };
- SDL_Surface *surface = TTF_RenderUTF8_Solid(font, text, c);
- if (surface == NULL) {
- return NULL;
- }
-
- _ttfSurfaceCacheMissCount++;
- // printf("CACHE HITS: %d MISSES: %d)\n", _ttfSurfaceCacheHitCount, _ttfSurfaceCacheMissCount);
-
- _ttfSurfaceCacheCount++;
- entry->surface = surface;
- entry->font = font;
- entry->text = _strdup(text);
- entry->lastUseTick = gCurrentDrawCount;
- return entry->surface;
-}
-
-static void _ttf_getwidth_cache_dispose(ttf_getwidth_cache_entry *entry)
-{
- if (entry->text != NULL) {
- free(entry->text);
-
- entry->width = 0;
- entry->font = NULL;
- entry->text = NULL;
- }
-}
-
-static void _ttf_getwidth_cache_dispose_all()
-{
- for (sint32 i = 0; i < TTF_GETWIDTH_CACHE_SIZE; i++) {
- _ttf_getwidth_cache_dispose(&_ttfGetWidthCache[i]);
- _ttfGetWidthCacheCount--;
- }
-}
-
-static uint32 _ttf_getwidth_cache_get_or_add(TTF_Font *font, const utf8 *text)
-{
- ttf_getwidth_cache_entry *entry;
-
- uint32 hash = _ttf_surface_cache_hash(font, text);
- sint32 index = hash % TTF_GETWIDTH_CACHE_SIZE;
- for (sint32 i = 0; i < TTF_GETWIDTH_CACHE_SIZE; i++) {
- entry = &_ttfGetWidthCache[index];
-
- // Check if entry is a hit
- if (entry->text == NULL) break;
- if (entry->font == font && strcmp(entry->text, text) == 0) {
- _ttfGetWidthCacheHitCount++;
- entry->lastUseTick = gCurrentDrawCount;
- return entry->width;
- }
-
- // If entry hasn't been used for a while, replace it
- if (entry->lastUseTick < gCurrentDrawCount - 64) {
- break;
- }
-
- // Check if next entry is a hit
- if (++index >= TTF_GETWIDTH_CACHE_SIZE) index = 0;
- }
-
- // Cache miss, replace entry with new width
- entry = &_ttfGetWidthCache[index];
- _ttf_getwidth_cache_dispose(entry);
-
- sint32 width, height;
- TTF_SizeUTF8(font, text, &width, &height);
-
- _ttfGetWidthCacheMissCount++;
-
- _ttfGetWidthCacheCount++;
- entry->width = width;
- entry->font = font;
- entry->text = _strdup(text);
- entry->lastUseTick = gCurrentDrawCount;
- return entry->width;
-}
-
-bool ttf_initialise()
-{
- if (!_ttfInitialised) {
- if (TTF_Init() != 0) {
- return false;
- }
-
- for (sint32 i = 0; i < 4; i++) {
- TTFFontDescriptor *fontDesc = &(gCurrentTTFFontSet->size[i]);
-
- utf8 fontPath[MAX_PATH];
- if (!platform_get_font_path(fontDesc, fontPath, sizeof(fontPath))) {
- log_error("Unable to load font '%s'", fontDesc->font_name);
- return false;
- }
-
- fontDesc->font = TTF_OpenFont(fontPath, fontDesc->ptSize);
- if (fontDesc->font == NULL) {
- log_error("Unable to load '%s'", fontPath);
- return false;
- }
- }
-
- _ttfInitialised = true;
- }
- return true;
-}
-
-void ttf_dispose()
-{
- if (!_ttfInitialised)
- return;
-
- _ttf_surface_cache_dispose_all();
- _ttf_getwidth_cache_dispose_all();
-
- for (sint32 i = 0; i < 4; i++) {
- TTFFontDescriptor *fontDesc = &(gCurrentTTFFontSet->size[i]);
- if (fontDesc->font != NULL) {
- TTF_CloseFont(fontDesc->font);
- fontDesc->font = NULL;
- }
- }
-
- TTF_Quit();
- _ttfInitialised = false;
-}
-
-TTFFontDescriptor *ttf_get_font_from_sprite_base(uint16 spriteBase)
-{
- return &gCurrentTTFFontSet->size[font_get_size_from_sprite_base(spriteBase)];
-}
-#else
-bool ttf_initialise()
-{
- return false;
-}
-
-void ttf_dispose() {}
-#endif // NO_TTF
-
typedef struct text_draw_info {
sint32 startX;
sint32 startY;
@@ -924,6 +688,8 @@ typedef struct text_draw_info {
const sint8 *y_offset;
} text_draw_info;
+#ifndef NO_TTF
+
static void ttf_draw_character_sprite(rct_drawpixelinfo *dpi, sint32 codepoint, text_draw_info *info)
{
sint32 characterWidth = font_sprite_get_codepoint_width(info->font_sprite_base, codepoint);
@@ -953,10 +719,9 @@ static void ttf_draw_string_raw_sprite(rct_drawpixelinfo *dpi, const utf8 *text,
};
}
-#ifndef NO_TTF
static void ttf_draw_string_raw_ttf(rct_drawpixelinfo *dpi, const utf8 *text, text_draw_info *info)
{
- if (!_ttfInitialised && !ttf_initialise())
+ if (!ttf_initialise())
return;
TTFFontDescriptor *fontDesc = ttf_get_font_from_sprite_base(info->font_sprite_base);
@@ -966,20 +731,14 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo *dpi, const utf8 *text, te
}
if (info->flags & TEXT_DRAW_FLAG_NO_DRAW) {
- info->x += _ttf_getwidth_cache_get_or_add(fontDesc->font, text);
+ info->x += ttf_getwidth_cache_get_or_add(fontDesc->font, text);
return;
} else {
uint8 colour = info->palette[1];
- SDL_Surface *surface = ttf_surface_cache_get_or_add(fontDesc->font, text);
+ TTFSurface * surface = ttf_surface_cache_get_or_add(fontDesc->font, text);
if (surface == NULL)
return;
- if (SDL_MUSTLOCK(surface)) {
- if (SDL_LockSurface(surface) != 0) {
- return;
- }
- }
-
sint32 drawX = info->x + fontDesc->offset_x;
sint32 drawY = info->y + fontDesc->offset_y;
sint32 width = surface->w;
@@ -993,7 +752,7 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo *dpi, const utf8 *text, te
sint32 skipY = drawY - dpi->y;
info->x += width;
- uint8 *src = surface->pixels;
+ const uint8 *src = surface->pixels;
uint8 *dst = dpi->bits;
if (skipX < 0) {
@@ -1013,7 +772,7 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo *dpi, const utf8 *text, te
sint32 srcScanSkip = surface->pitch - width;
sint32 dstScanSkip = dpi->width + dpi->pitch - width;
uint8 *dst_orig = dst;
- uint8 *src_orig = src;
+ const uint8 *src_orig = src;
// Draw shadow/outline
if (info->flags & TEXT_DRAW_FLAG_OUTLINE) {
@@ -1051,12 +810,9 @@ static void ttf_draw_string_raw_ttf(rct_drawpixelinfo *dpi, const utf8 *text, te
dst += dstScanSkip;
}
}
-
- if (SDL_MUSTLOCK(surface)) {
- SDL_UnlockSurface(surface);
- }
}
}
+
#endif // NO_TTF
static void ttf_draw_string_raw(rct_drawpixelinfo *dpi, const utf8 *text, text_draw_info *info)
@@ -1296,9 +1052,9 @@ static void ttf_draw_string(rct_drawpixelinfo *dpi, char *text, sint32 colour, s
info.x = x;
info.y = y;
-#ifndef NO_TTF
- if (gUseTrueTypeFont) info.flags |= TEXT_DRAW_FLAG_TTF;
-#endif // NO_TTF
+ if (gUseTrueTypeFont) {
+ info.flags |= TEXT_DRAW_FLAG_TTF;
+ }
memcpy(info.palette, text_palette, sizeof(info.palette));
ttf_process_initial_colour(colour, &info);
@@ -1325,9 +1081,9 @@ static sint32 ttf_get_string_width(const utf8 *text)
info.maxY = 0;
info.flags |= TEXT_DRAW_FLAG_NO_DRAW;
-#ifndef NO_TTF
- if (gUseTrueTypeFont) info.flags |= TEXT_DRAW_FLAG_TTF;
-#endif // NO_TTF
+ if (gUseTrueTypeFont) {
+ info.flags |= TEXT_DRAW_FLAG_TTF;
+ }
ttf_process_string(NULL, text, &info);
@@ -1351,11 +1107,9 @@ void gfx_draw_string_with_y_offsets(rct_drawpixelinfo *dpi, const utf8 *text, si
info.flags |= TEXT_DRAW_FLAG_Y_OFFSET_EFFECT;
-#ifndef NO_TTF
if (!forceSpriteFont && gUseTrueTypeFont) {
info.flags |= TEXT_DRAW_FLAG_TTF;
}
-#endif // NO_TTF
memcpy(info.palette, text_palette, sizeof(info.palette));
ttf_process_initial_colour(colour, &info);
diff --git a/src/openrct2/drawing/ttf.c b/src/openrct2/drawing/ttf.c
new file mode 100644
index 0000000000..3b1bbe614b
--- /dev/null
+++ b/src/openrct2/drawing/ttf.c
@@ -0,0 +1,329 @@
+#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
+/*****************************************************************************
+ * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
+ *
+ * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
+ * For more information, visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * A full copy of the GNU General Public License can be found in licence.txt
+ *****************************************************************************/
+#pragma endregion
+
+#ifndef NO_TTF
+
+#include
+#include FT_FREETYPE_H
+
+#include "../platform/platform.h"
+#include "../rct2.h"
+#include "ttf.h"
+
+static bool _ttfInitialised = false;
+static FT_Library _ftLibrary;
+
+#define TTF_SURFACE_CACHE_SIZE 256
+#define TTF_GETWIDTH_CACHE_SIZE 1024
+
+typedef struct ttf_cache_entry
+{
+ TTFSurface * surface;
+ TTFFont * font;
+ utf8 * text;
+ uint32 lastUseTick;
+} ttf_cache_entry;
+
+typedef struct ttf_getwidth_cache_entry
+{
+ uint32 width;
+ TTFFont * font;
+ utf8 * text;
+ uint32 lastUseTick;
+} ttf_getwidth_cache_entry;
+
+static ttf_cache_entry _ttfSurfaceCache[TTF_SURFACE_CACHE_SIZE] = { 0 };
+static sint32 _ttfSurfaceCacheCount = 0;
+static sint32 _ttfSurfaceCacheHitCount = 0;
+static sint32 _ttfSurfaceCacheMissCount = 0;
+
+static ttf_getwidth_cache_entry _ttfGetWidthCache[TTF_GETWIDTH_CACHE_SIZE] = { 0 };
+static sint32 _ttfGetWidthCacheCount = 0;
+static sint32 _ttfGetWidthCacheHitCount = 0;
+static sint32 _ttfGetWidthCacheMissCount = 0;
+
+static TTFFont * ttf_open_font(const utf8 * fontPath, sint32 ptSize);
+static void ttf_close_font(TTFFont * font);
+static uint32 ttf_surface_cache_hash(TTFFont * 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 void ttf_get_size(TTFFont * font, const utf8 * text, sint32 * width, sint32 * height);
+static TTFSurface * ttf_render(TTFFont * font, const utf8 * text);
+static void ttf_free_surface(TTFSurface * surface);
+
+bool ttf_initialise()
+{
+ if (!_ttfInitialised) {
+ FT_Error error = FT_Init_FreeType(&_ftLibrary);
+ if (error != 0) {
+ log_error("Couldn't initialise FreeType engine");
+ return false;
+ }
+
+ for (sint32 i = 0; i < 4; i++) {
+ TTFFontDescriptor *fontDesc = &(gCurrentTTFFontSet->size[i]);
+
+ utf8 fontPath[MAX_PATH];
+ if (!platform_get_font_path(fontDesc, fontPath, sizeof(fontPath))) {
+ log_error("Unable to load font '%s'", fontDesc->font_name);
+ return false;
+ }
+
+ fontDesc->font = ttf_open_font(fontPath, fontDesc->ptSize);
+ if (fontDesc->font == NULL) {
+ log_error("Unable to load '%s'", fontPath);
+ return false;
+ }
+ }
+ _ttfInitialised = true;
+ }
+ return true;
+}
+
+void ttf_dispose()
+{
+ if (_ttfInitialised)
+ {
+ ttf_surface_cache_dispose_all();
+ ttf_getwidth_cache_dispose_all();
+
+ for (sint32 i = 0; i < 4; i++) {
+ TTFFontDescriptor *fontDesc = &(gCurrentTTFFontSet->size[i]);
+ if (fontDesc->font != NULL) {
+ ttf_close_font(fontDesc->font);
+ fontDesc->font = NULL;
+ }
+ }
+
+ FT_Done_FreeType(_ftLibrary);
+ _ttfInitialised = false;
+ }
+}
+
+static TTFFont * ttf_open_font(const utf8 * fontPath, sint32 ptSize)
+{
+ TTFFont * font = malloc(sizeof(TTFFont));
+ if (font != NULL) {
+ FT_Error error = FT_New_Face(_ftLibrary, fontPath, 0, &font->face);
+ if (error != 0) {
+ ttf_close_font(font);
+ return NULL;
+ }
+
+ error = FT_Set_Char_Size(font->face, 0, ptSize * 64, 0, 0);
+ if (error != 0) {
+ ttf_close_font(font);
+ return NULL;
+ }
+ }
+ return font;
+}
+
+static void ttf_close_font(TTFFont * font)
+{
+ FT_Done_Face(font->face);
+ free(font);
+}
+
+static uint32 ttf_surface_cache_hash(TTFFont *font, const utf8 *text)
+{
+ uint32 hash = (uint32)((((uintptr_t)font * 23) ^ 0xAAAAAAAA) & 0xFFFFFFFF);
+ for (const utf8 *ch = text; *ch != 0; ch++) {
+ hash = ror32(hash, 3) ^ (*ch * 13);
+ }
+ return hash;
+}
+
+static void ttf_surface_cache_dispose(ttf_cache_entry *entry)
+{
+ if (entry->surface != NULL) {
+ ttf_free_surface(entry->surface);
+ free(entry->text);
+
+ entry->surface = NULL;
+ entry->font = NULL;
+ entry->text = NULL;
+ }
+}
+
+static void ttf_surface_cache_dispose_all()
+{
+ for (sint32 i = 0; i < TTF_SURFACE_CACHE_SIZE; i++) {
+ ttf_surface_cache_dispose(&_ttfSurfaceCache[i]);
+ _ttfSurfaceCacheCount--;
+ }
+}
+
+TTFSurface * ttf_surface_cache_get_or_add(TTFFont * font, const utf8 * text)
+{
+ ttf_cache_entry *entry;
+
+ uint32 hash = ttf_surface_cache_hash(font, text);
+ sint32 index = hash % TTF_SURFACE_CACHE_SIZE;
+ for (sint32 i = 0; i < TTF_SURFACE_CACHE_SIZE; i++) {
+ entry = &_ttfSurfaceCache[index];
+
+ // Check if entry is a hit
+ if (entry->surface == NULL) break;
+ if (entry->font == font && strcmp(entry->text, text) == 0) {
+ _ttfSurfaceCacheHitCount++;
+ entry->lastUseTick = gCurrentDrawCount;
+ return entry->surface;
+ }
+
+ // If entry hasn't been used for a while, replace it
+ if (entry->lastUseTick < gCurrentDrawCount - 64) {
+ break;
+ }
+
+ // Check if next entry is a hit
+ if (++index >= TTF_SURFACE_CACHE_SIZE) index = 0;
+ }
+
+ // Cache miss, replace entry with new surface
+ entry = &_ttfSurfaceCache[index];
+ ttf_surface_cache_dispose(entry);
+
+ TTFSurface * surface = ttf_render(font, text);
+ if (surface == NULL) {
+ return NULL;
+ }
+
+ _ttfSurfaceCacheMissCount++;
+ // printf("CACHE HITS: %d MISSES: %d)\n", _ttfSurfaceCacheHitCount, _ttfSurfaceCacheMissCount);
+
+ _ttfSurfaceCacheCount++;
+ entry->surface = surface;
+ entry->font = font;
+ entry->text = _strdup(text);
+ entry->lastUseTick = gCurrentDrawCount;
+ return entry->surface;
+}
+
+static void ttf_getwidth_cache_dispose(ttf_getwidth_cache_entry *entry)
+{
+ if (entry->text != NULL) {
+ free(entry->text);
+
+ entry->width = 0;
+ entry->font = NULL;
+ entry->text = NULL;
+ }
+}
+
+static void ttf_getwidth_cache_dispose_all()
+{
+ for (sint32 i = 0; i < TTF_GETWIDTH_CACHE_SIZE; i++) {
+ ttf_getwidth_cache_dispose(&_ttfGetWidthCache[i]);
+ _ttfGetWidthCacheCount--;
+ }
+}
+
+uint32 ttf_getwidth_cache_get_or_add(TTFFont * font, const utf8 * text)
+{
+ ttf_getwidth_cache_entry *entry;
+
+ uint32 hash = ttf_surface_cache_hash(font, text);
+ sint32 index = hash % TTF_GETWIDTH_CACHE_SIZE;
+ for (sint32 i = 0; i < TTF_GETWIDTH_CACHE_SIZE; i++) {
+ entry = &_ttfGetWidthCache[index];
+
+ // Check if entry is a hit
+ if (entry->text == NULL) break;
+ if (entry->font == font && strcmp(entry->text, text) == 0) {
+ _ttfGetWidthCacheHitCount++;
+ entry->lastUseTick = gCurrentDrawCount;
+ return entry->width;
+ }
+
+ // If entry hasn't been used for a while, replace it
+ if (entry->lastUseTick < gCurrentDrawCount - 64) {
+ break;
+ }
+
+ // Check if next entry is a hit
+ if (++index >= TTF_GETWIDTH_CACHE_SIZE) index = 0;
+ }
+
+ // Cache miss, replace entry with new width
+ entry = &_ttfGetWidthCache[index];
+ ttf_getwidth_cache_dispose(entry);
+
+ sint32 width, height;
+ ttf_get_size(font, text, &width, &height);
+
+ _ttfGetWidthCacheMissCount++;
+
+ _ttfGetWidthCacheCount++;
+ entry->width = width;
+ entry->font = font;
+ entry->text = _strdup(text);
+ entry->lastUseTick = gCurrentDrawCount;
+ return entry->width;
+}
+
+TTFFontDescriptor * ttf_get_font_from_sprite_base(uint16 spriteBase)
+{
+ return &gCurrentTTFFontSet->size[font_get_size_from_sprite_base(spriteBase)];
+}
+
+bool ttf_provides_glyph(const TTFFont * font, codepoint_t codepoint)
+{
+ return FT_Get_Char_Index(font->face, codepoint) != 0;
+}
+
+static void ttf_get_size(TTFFont * font, const utf8 * text, sint32 * width, sint32 * height)
+{
+ *width = 128;
+ *height = 32;
+}
+
+static TTFSurface * ttf_render(TTFFont * font, const utf8 * text)
+{
+ sint32 width = 128;
+ sint32 height = 32;
+ uint8 * pixels = (uint8 *)malloc(width * height);
+ memset(pixels, 0, width * height);
+
+ TTFSurface * surface = (TTFSurface *)malloc(sizeof(TTFSurface));
+ surface->w = width;
+ surface->h = height;
+ surface->pitch = width;
+ surface->pixels = pixels;
+ return NULL;
+}
+
+static void ttf_free_surface(TTFSurface * surface)
+{
+ free((void *)surface->pixels);
+ free(surface);
+}
+
+#else
+
+#include "ttf.h"
+
+bool ttf_initialise()
+{
+ return false;
+}
+
+void ttf_dispose()
+{
+}
+
+#endif // NO_TTF
diff --git a/src/openrct2/drawing/ttf.h b/src/openrct2/drawing/ttf.h
new file mode 100644
index 0000000000..9b6d505197
--- /dev/null
+++ b/src/openrct2/drawing/ttf.h
@@ -0,0 +1,38 @@
+#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
+/*****************************************************************************
+ * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
+ *
+ * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
+ * For more information, visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * A full copy of the GNU General Public License can be found in licence.txt
+ *****************************************************************************/
+#pragma endregion
+
+#pragma once
+
+#include "font.h"
+
+bool ttf_initialise();
+void ttf_dispose();
+
+#ifndef NO_TTF
+
+typedef struct TTFSurface {
+ const void * pixels;
+ sint32 w;
+ sint32 h;
+ sint32 pitch;
+} TTFSurface;
+
+TTFFontDescriptor * ttf_get_font_from_sprite_base(uint16 spriteBase);
+TTFSurface * ttf_surface_cache_get_or_add(TTFFont * font, const utf8 * text);
+uint32 ttf_getwidth_cache_get_or_add(TTFFont * font, const utf8 * text);
+bool ttf_provides_glyph(const TTFFont * font, codepoint_t codepoint);
+
+#endif // NO_TTF
diff --git a/src/openrct2/interface/Fonts.cpp b/src/openrct2/interface/Fonts.cpp
index f241bf89ac..151f04da41 100644
--- a/src/openrct2/interface/Fonts.cpp
+++ b/src/openrct2/interface/Fonts.cpp
@@ -14,16 +14,17 @@
*****************************************************************************/
#pragma endregion
-#include "../common.h"
+#include "../config/Config.h"
#include "../core/Console.hpp"
#include "../core/String.hpp"
#include "../localisation/LanguagePack.h"
#include "Fonts.h"
-extern "C" {
-#include "../config/Config.h"
-#include "../drawing/drawing.h"
-#include "../localisation/language.h"
+extern "C"
+{
+ #include "../drawing/drawing.h"
+ #include "../drawing/ttf.h"
+ #include "../localisation/language.h"
}
#ifndef NO_TTF