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