diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index e45581d698..2b5311724e 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 4C8B42721EEB1AE400F015CA /* HardwareDisplayDrawingEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8B42711EEB1AE400F015CA /* HardwareDisplayDrawingEngine.cpp */; }; 4C8B42741EEB1B6F00F015CA /* Screenshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8B42731EEB1B6F00F015CA /* Screenshot.cpp */; }; 4CB832A71EFBDCCE00B88761 /* land_tool.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CB832A51EFBDCCE00B88761 /* land_tool.c */; }; + 4CB832AB1EFFB8D100B88761 /* ttf_sdlport.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CB832A81EFFB8D100B88761 /* ttf_sdlport.c */; }; + 4CB832AC1EFFB8D100B88761 /* ttf.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CB832A91EFFB8D100B88761 /* ttf.c */; }; C606CCBE1DB4054000FE4015 /* compat.c in Sources */ = {isa = PBXBuildFile; fileRef = C606CCAB1DB4054000FE4015 /* compat.c */; }; C606CCBF1DB4054000FE4015 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = C606CCAC1DB4054000FE4015 /* data.c */; }; C606CCC01DB4054000FE4015 /* FunctionCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCAE1DB4054000FE4015 /* FunctionCall.cpp */; }; @@ -56,6 +58,7 @@ C64FDAA71D6D9A2100F259B9 /* boat_ride.c in Sources */ = {isa = PBXBuildFile; fileRef = C686F9031CDBC3B7009F9BFC /* boat_ride.c */; }; C64FDAA81D6D9A2100F259B9 /* dingy_slide.c in Sources */ = {isa = PBXBuildFile; fileRef = C686F9041CDBC3B7009F9BFC /* dingy_slide.c */; }; C64FDAA91D6D9A2100F259B9 /* log_flume.c in Sources */ = {isa = PBXBuildFile; fileRef = C686F9051CDBC3B7009F9BFC /* log_flume.c */; }; + C6CB94F21EFFBF860065888F /* libfreetype.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B41CF3006400659A24 /* libfreetype.dylib */; }; C6E96E361E0408B40076A04F /* libzip.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; }; C6E96E371E040E040076A04F /* libzip.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; D41B73EF1C2101890080A7B9 /* libcurl.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B73EE1C2101890080A7B9 /* libcurl.tbd */; }; @@ -64,7 +67,6 @@ D43407E21D0E14CE00C2B3D4 /* shaders in Resources */ = {isa = PBXBuildFile; fileRef = D43407E11D0E14CE00C2B3D4 /* shaders */; }; D45A38BC1CF3006400659A24 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B31CF3006400659A24 /* libcrypto.dylib */; }; D45A38BE1CF3006400659A24 /* libjansson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B51CF3006400659A24 /* libjansson.dylib */; }; - D45A38C01CF3006400659A24 /* libSDL2_ttf.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B71CF3006400659A24 /* libSDL2_ttf.dylib */; }; D45A38C11CF3006400659A24 /* libSDL2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B81CF3006400659A24 /* libSDL2.dylib */; }; D45A38C21CF3006400659A24 /* libspeexdsp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B91CF3006400659A24 /* libspeexdsp.dylib */; }; D45A39591CF300AF00659A24 /* libcrypto.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B31CF3006400659A24 /* libcrypto.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -464,8 +466,6 @@ F7D7749B1EC6705F00BE6EBC /* libspeexdsp.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B91CF3006400659A24 /* libspeexdsp.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F7D7749C1EC6705F00BE6EBC /* libzip.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F7D7749E1EC6713200BE6EBC /* Cli.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C857D1EC4E80E00FA49E2 /* Cli.cpp */; }; - F7D7749F1EC6714C00BE6EBC /* libSDL2_ttf.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B71CF3006400659A24 /* libSDL2_ttf.dylib */; }; - F7D774A01EC6714C00BE6EBC /* libSDL2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B81CF3006400659A24 /* libSDL2.dylib */; }; F7D774A11EC6715C00BE6EBC /* libSDL2_ttf.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B71CF3006400659A24 /* libSDL2_ttf.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F7D774A21EC6715C00BE6EBC /* libSDL2.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B81CF3006400659A24 /* libSDL2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F7D774AC1EC6741D00BE6EBC /* language in CopyFiles */ = {isa = PBXBuildFile; fileRef = D4EC48E41C2637710024B507 /* language */; }; @@ -573,6 +573,9 @@ 4C8B42731EEB1B6F00F015CA /* Screenshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Screenshot.cpp; sourceTree = ""; }; 4CB832A51EFBDCCE00B88761 /* land_tool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = land_tool.c; sourceTree = ""; }; 4CB832A61EFBDCCE00B88761 /* land_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = land_tool.h; sourceTree = ""; }; + 4CB832A81EFFB8D100B88761 /* ttf_sdlport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ttf_sdlport.c; sourceTree = ""; }; + 4CB832A91EFFB8D100B88761 /* ttf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ttf.c; sourceTree = ""; }; + 4CB832AA1EFFB8D100B88761 /* ttf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ttf.h; sourceTree = ""; }; C606CCAB1DB4054000FE4015 /* compat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = compat.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; C606CCAC1DB4054000FE4015 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = data.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; C606CCAD1DB4054000FE4015 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = data.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -1373,7 +1376,7 @@ D45A38BE1CF3006400659A24 /* libjansson.dylib in Frameworks */, D4A8B4B41DB41873007A2F29 /* libpng16.dylib in Frameworks */, D45A38C11CF3006400659A24 /* libSDL2.dylib in Frameworks */, - D45A38C01CF3006400659A24 /* libSDL2_ttf.dylib in Frameworks */, + C6CB94F21EFFBF860065888F /* libfreetype.dylib in Frameworks */, D45A38C21CF3006400659A24 /* libspeexdsp.dylib in Frameworks */, C6E96E361E0408B40076A04F /* libzip.dylib in Frameworks */, ); @@ -1391,8 +1394,6 @@ F7D774921EC66FBA00BE6EBC /* libfreetype.dylib in Frameworks */, F7D774931EC66FBA00BE6EBC /* libjansson.dylib in Frameworks */, F7D774941EC66FBA00BE6EBC /* libpng16.dylib in Frameworks */, - F7D774A01EC6714C00BE6EBC /* libSDL2.dylib in Frameworks */, - F7D7749F1EC6714C00BE6EBC /* libSDL2_ttf.dylib in Frameworks */, F7D774951EC66FBA00BE6EBC /* libspeexdsp.dylib in Frameworks */, F7D774961EC66FBA00BE6EBC /* libzip.dylib in Frameworks */, ); @@ -1899,6 +1900,9 @@ F76C83AE1EC4E7CC00FA49E2 /* scrolling_text.c */, F76C83AF1EC4E7CC00FA49E2 /* sprite.cpp */, F76C83B01EC4E7CC00FA49E2 /* string.c */, + 4CB832A91EFFB8D100B88761 /* ttf.c */, + 4CB832AA1EFFB8D100B88761 /* ttf.h */, + 4CB832A81EFFB8D100B88761 /* ttf_sdlport.c */, 4C8B426E1EEB1ABD00F015CA /* X8DrawingEngine.cpp */, 4C8B426F1EEB1ABD00F015CA /* X8DrawingEngine.h */, ); @@ -2815,7 +2819,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "version=\"11\"\nzipname=\"openrct2-libs-macos.zip\"\nliburl=\"https://github.com/OpenRCT2/Dependencies/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/libxc\" || ! -e \"${SRCROOT}/libversion\" || $(head -n 1 \"${SRCROOT}/libversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/libxc\" ]]; then rm -r \"${SRCROOT}/libxc\"; fi\nmkdir \"${SRCROOT}/libxc\"\n\ncurl -L -o \"${SRCROOT}/libxc/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/libxc\" \"${SRCROOT}/libxc/$zipname\"\nrm \"${SRCROOT}/libxc/$zipname\"\n\necho $version > \"${SRCROOT}/libversion\"\nfi"; + shellScript = "version=\"12\"\nzipname=\"openrct2-libs-macos.zip\"\nliburl=\"https://github.com/OpenRCT2/Dependencies/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/libxc\" || ! -e \"${SRCROOT}/libversion\" || $(head -n 1 \"${SRCROOT}/libversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/libxc\" ]]; then rm -r \"${SRCROOT}/libxc\"; fi\nmkdir \"${SRCROOT}/libxc\"\n\ncurl -L -o \"${SRCROOT}/libxc/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/libxc\" \"${SRCROOT}/libxc/$zipname\"\nrm \"${SRCROOT}/libxc/$zipname\"\n\necho $version > \"${SRCROOT}/libversion\"\nfi"; }; D42C09D21C254F4E00309751 /* Build g2.dat */ = { isa = PBXShellScriptBuildPhase; @@ -2974,11 +2978,13 @@ 4C8B42741EEB1B6F00F015CA /* Screenshot.cpp in Sources */, F76C88781EC5324E00FA49E2 /* AudioChannel.cpp in Sources */, F76C88791EC5324E00FA49E2 /* AudioContext.cpp in Sources */, + 4CB832AC1EFFB8D100B88761 /* ttf.c in Sources */, 4C8B42721EEB1AE400F015CA /* HardwareDisplayDrawingEngine.cpp in Sources */, F76C887A1EC5324E00FA49E2 /* AudioMixer.cpp in Sources */, F76C887B1EC5324E00FA49E2 /* FileAudioSource.cpp in Sources */, F7CB864D1EEDA1A80030C877 /* DummyWindowManager.cpp in Sources */, F76C887C1EC5324E00FA49E2 /* MemoryAudioSource.cpp in Sources */, + 4CB832AB1EFFB8D100B88761 /* ttf_sdlport.c in Sources */, F76C887D1EC5324E00FA49E2 /* CursorData.cpp in Sources */, 4CB832A71EFBDCCE00B88761 /* land_tool.c in Sources */, F7D7747F1EC61E5100BE6EBC /* UiContext.macOS.mm in Sources */, diff --git a/openrct2.proj b/openrct2.proj index e240ae0756..21f0b7c118 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -21,7 +21,7 @@ 0.0.8 -$(GIT_BRANCH)-$(GIT_COMMIT_SHA1_SHORT) - 11 + 12 /D "OPENRCT2_BUILD_SERVER=\"$(BUILD_SERVER)\"" $(OPENRCT2_CL_ADDITIONALOPTIONS) @@ -64,7 +64,7 @@ $(RootDir).dependencies https://github.com/OpenRCT2/Dependencies/releases/download/v$(TargetLibsVersion)/openrct2-libs-vs2015.zip - f088adcd12450c2672f78679ea5d1fbffc28fd22 + f845fe2fad0a1dece905c42ac4cfc1234ec447a7 1.8.0 https://github.com/google/googletest/archive/release-1.8.0.zip 667f873ab7a4d246062565fad32fb6d8e203ee73 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..52e58cd12e 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 }; @@ -209,7 +206,7 @@ bool font_supports_string_ttf(const utf8 *text, sint32 fontSize) 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..4b6180ab53 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,8 @@ enum { }; #ifndef NO_TTF + +typedef struct _TTF_Font TTF_Font; typedef struct TTFFontDescriptor { const utf8 *filename; const utf8 *font_name; @@ -50,7 +48,7 @@ typedef struct TTFFontDescriptor { sint32 offset_x; sint32 offset_y; sint32 line_height; - TTF_Font *font; + TTF_Font * 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..bf2dc72051 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; @@ -954,9 +718,10 @@ 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..4a2772d72f --- /dev/null +++ b/src/openrct2/drawing/ttf.c @@ -0,0 +1,301 @@ +#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 "../localisation/localisation.h" +#include "../platform/platform.h" +#include "../rct2.h" +#include "ttf.h" + +static bool _ttfInitialised = false; + +#define TTF_SURFACE_CACHE_SIZE 256 +#define TTF_GETWIDTH_CACHE_SIZE 1024 + +typedef struct ttf_cache_entry +{ + TTFSurface * surface; + TTF_Font * font; + utf8 * text; + uint32 lastUseTick; +} ttf_cache_entry; + +typedef struct ttf_getwidth_cache_entry +{ + uint32 width; + TTF_Font * 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 TTF_Font * ttf_open_font(const utf8 * fontPath, sint32 ptSize); +static void ttf_close_font(TTF_Font * font); +static uint32 ttf_surface_cache_hash(TTF_Font * font, const utf8 * text); +static void ttf_surface_cache_dispose(ttf_cache_entry * entry); +static void ttf_surface_cache_dispose_all(); +static void ttf_getwidth_cache_dispose_all(); +static bool ttf_get_size(TTF_Font * font, const utf8 * text, sint32 * width, sint32 * height); +static TTFSurface * ttf_render(TTF_Font * font, const utf8 * text); + +bool ttf_initialise() +{ + if (!_ttfInitialised) { + if (TTF_Init() != 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; + } + } + + TTF_Quit(); + _ttfInitialised = false; + } +} + +static TTF_Font * ttf_open_font(const utf8 * fontPath, sint32 ptSize) +{ + return TTF_OpenFont(fontPath, ptSize); +} + +static void ttf_close_font(TTF_Font * font) +{ + TTF_CloseFont(font); +} + +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) { + 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(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); + + 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(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_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 TTF_Font * font, codepoint_t codepoint) +{ + return TTF_GlyphIsProvided(font, codepoint); +} + +static bool ttf_get_size(TTF_Font * font, const utf8 * text, sint32 * outWidth, sint32 * outHeight) +{ + return TTF_SizeUTF8(font, text, outWidth, outHeight); +} + +static TTFSurface * ttf_render(TTF_Font * font, const utf8 * text) +{ + return TTF_RenderUTF8_Solid(font, text, 0x000000FF); +} + +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..ea04cfe628 --- /dev/null +++ b/src/openrct2/drawing/ttf.h @@ -0,0 +1,48 @@ +#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(TTF_Font * font, const utf8 * text); +uint32 ttf_getwidth_cache_get_or_add(TTF_Font * font, const utf8 * text); +bool ttf_provides_glyph(const TTF_Font * font, codepoint_t codepoint); +void ttf_free_surface(TTFSurface * surface); + +// TTF_SDLPORT +int TTF_Init(void); +TTF_Font * TTF_OpenFont(const char *file, int ptsize); +int TTF_GlyphIsProvided(const TTF_Font *font, codepoint_t ch); +int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h); +TTFSurface * TTF_RenderUTF8_Solid(TTF_Font *font, const char *text, uint32 colour); +void TTF_CloseFont(TTF_Font *font); +void TTF_Quit(void); + +#endif // NO_TTF diff --git a/src/openrct2/drawing/ttf_sdlport.c b/src/openrct2/drawing/ttf_sdlport.c new file mode 100644 index 0000000000..5dc5c8f7e1 --- /dev/null +++ b/src/openrct2/drawing/ttf_sdlport.c @@ -0,0 +1,1284 @@ +/** + * The following code is from SDL2_ttf (2 Jan 2017). + * Taking just what was needed for OpenRCT2 with all SDL2 calls + * removed. + */ + +#ifndef NO_TTF + +/* +SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts +Copyright (C) 2001-2017 Sam Lantinga + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_STROKER_H +#include FT_GLYPH_H +#include FT_TRUETYPE_IDS_H + +#include "ttf.h" + +#pragma warning(disable : 4018) // '<': signed / unsigned mismatch + +/* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */ +#define UNICODE_BOM_NATIVE 0xFEFF +#define UNICODE_BOM_SWAPPED 0xFFFE + +/* Set and retrieve the font style */ +#define TTF_STYLE_NORMAL 0x00 +#define TTF_STYLE_BOLD 0x01 +#define TTF_STYLE_ITALIC 0x02 +#define TTF_STYLE_UNDERLINE 0x04 +#define TTF_STYLE_STRIKETHROUGH 0x08 + +/* Set and retrieve FreeType hinter settings */ +#define TTF_HINTING_NORMAL 0 +#define TTF_HINTING_LIGHT 1 +#define TTF_HINTING_MONO 2 +#define TTF_HINTING_NONE 3 + +/* FIXME: Right now we assume the gray-scale renderer Freetype is using +supports 256 shades of gray, but we should instead key off of num_grays +in the result FT_Bitmap after the FT_Render_Glyph() call. */ +#define NUM_GRAYS 256 + +/* Handy routines for converting from fixed point */ +#define FT_FLOOR(X) ((X & -64) / 64) +#define FT_CEIL(X) (((X + 63) & -64) / 64) + +#define CACHED_METRICS 0x10 +#define CACHED_BITMAP 0x01 +#define CACHED_PIXMAP 0x02 + +/* Cached glyph information */ +typedef struct cached_glyph { + int stored; + FT_UInt index; + FT_Bitmap bitmap; + FT_Bitmap pixmap; + int minx; + int maxx; + int miny; + int maxy; + int yoffset; + int advance; + uint16 cached; +} c_glyph; + +/* The structure used to hold internal font information */ +struct _TTF_Font { + /* Freetype2 maintains all sorts of useful info itself */ + FT_Face face; + + /* We'll cache these ourselves */ + int height; + int ascent; + int descent; + int lineskip; + + /* The font style */ + int face_style; + int style; + int outline; + + /* Whether kerning is desired */ + int kerning; + + /* Extra width in glyph bounds for text styles */ + int glyph_overhang; + float glyph_italics; + + /* Information in the font for underlining */ + int underline_offset; + int underline_height; + + /* Cache for style-transformed glyphs */ + c_glyph *current; + c_glyph cache[257]; /* 257 is a prime */ + + /* We are responsible for closing the font stream */ + FILE *src; + int freesrc; + FT_Open_Args args; + + /* For non-scalable formats, we must remember which font index size */ + int font_size_family; + + /* really just flags passed into FT_Load_Glyph */ + int hinting; +}; + +/* Handle a style only if the font does not already handle it */ +#define TTF_HANDLE_STYLE_BOLD(font) (((font)->style & TTF_STYLE_BOLD) && \ + !((font)->face_style & TTF_STYLE_BOLD)) +#define TTF_HANDLE_STYLE_ITALIC(font) (((font)->style & TTF_STYLE_ITALIC) && \ + !((font)->face_style & TTF_STYLE_ITALIC)) +#define TTF_HANDLE_STYLE_UNDERLINE(font) ((font)->style & TTF_STYLE_UNDERLINE) +#define TTF_HANDLE_STYLE_STRIKETHROUGH(font) ((font)->style & TTF_STYLE_STRIKETHROUGH) + +/* Font styles that does not impact glyph drawing */ +#define TTF_STYLE_NO_GLYPH_CHANGE (TTF_STYLE_UNDERLINE | TTF_STYLE_STRIKETHROUGH) + +/* The FreeType font engine/library */ +static FT_Library library; +static int TTF_initialized = 0; + +#define TTF_SetError log_error + +#define TTF_CHECKPOINTER(p, errval) \ + if ( !TTF_initialized ) { \ + TTF_SetError("Library not initialized"); \ + return errval; \ + } \ + if ( !p ) { \ + TTF_SetError("Passed a NULL pointer"); \ + return errval; \ + } + +/* Gets the top row of the underline. The outline +is taken into account. +*/ +static int TTF_underline_top_row(TTF_Font *font) +{ + /* With outline, the underline_offset is underline_offset+outline. */ + /* So, we don't have to remove the top part of the outline height. */ + return font->ascent - font->underline_offset - 1; +} + +/* Gets the top row of the underline. for a given glyph. The outline +is taken into account. +Need to update row according to height difference between font and glyph: +font_value - font->ascent + glyph->maxy +*/ +static int TTF_Glyph_underline_top_row(TTF_Font *font, c_glyph *glyph) +{ + return glyph->maxy - font->underline_offset - 1; +} + +/* Gets the bottom row of the underline. The outline +is taken into account. +*/ +static int TTF_underline_bottom_row(TTF_Font *font) +{ + int row = TTF_underline_top_row(font) + font->underline_height; + if (font->outline > 0) { + /* Add underline_offset outline offset and */ + /* the bottom part of the outline. */ + row += font->outline * 2; + } + return row; +} + +/* Gets the bottom row of the underline. for a given glyph. The outline +is taken into account. +Need to update row according to height difference between font and glyph: +font_value - font->ascent + glyph->maxy +*/ +static int TTF_Glyph_underline_bottom_row(TTF_Font *font, c_glyph *glyph) +{ + return TTF_underline_bottom_row(font) - font->ascent + glyph->maxy; +} + +/* Gets the top row of the strikethrough. The outline +is taken into account. +*/ +static int TTF_strikethrough_top_row(TTF_Font *font) +{ + /* With outline, the first text row is 'outline'. */ + /* So, we don't have to remove the top part of the outline height. */ + return font->height / 2; +} + +/* Gets the top row of the strikethrough for a given glyph. The outline +is taken into account. +Need to update row according to height difference between font and glyph: +font_value - font->ascent + glyph->maxy +*/ +static int TTF_Glyph_strikethrough_top_row(TTF_Font *font, c_glyph *glyph) +{ + return TTF_strikethrough_top_row(font) - font->ascent + glyph->maxy; +} + +static void TTF_initLineMectrics(const TTF_Font *font, const TTFSurface *textbuf, const int row, uint8 **pdst, int *pheight) +{ + uint8 *dst; + int height; + + dst = (uint8 *)textbuf->pixels; + if (row > 0) { + dst += row * textbuf->pitch; + } + + height = font->underline_height; + /* Take outline into account */ + if (font->outline > 0) { + height += font->outline * 2; + } + *pdst = dst; + *pheight = height; +} + +/* Draw a solid line of underline_height (+ optional outline) +at the given row. The row value must take the +outline into account. +*/ +static void TTF_drawLine_Solid(const TTF_Font *font, const TTFSurface *textbuf, const int row) +{ + int line; + uint8 *dst_check = (uint8*)textbuf->pixels + textbuf->pitch * textbuf->h; + uint8 *dst; + int height; + + TTF_initLineMectrics(font, textbuf, row, &dst, &height); + + /* Draw line */ + for (line = height; line>0 && dst < dst_check; --line) { + /* 1 because 0 is the bg color */ + memset(dst, 1, textbuf->w); + dst += textbuf->pitch; + } +} + +static void TTF_SetFTError(const char *msg, FT_Error error) +{ +#ifdef USE_FREETYPE_ERRORS +#undef FTERRORS_H +#define FT_ERRORDEF( e, v, s ) { e, s }, + static const struct + { + int err_code; + const char* err_msg; + } ft_errors[] = { +#include + }; + int i; + const char *err_msg; + char buffer[1024]; + + err_msg = NULL; + for (i = 0; i<((sizeof ft_errors) / (sizeof ft_errors[0])); ++i) { + if (error == ft_errors[i].err_code) { + err_msg = ft_errors[i].err_msg; + break; + } + } + if (!err_msg) { + err_msg = "unknown FreeType error"; + } + TTF_SetError("%s: %s", msg, err_msg); +#else + TTF_SetError("%s", msg); +#endif /* USE_FREETYPE_ERRORS */ +} + +int TTF_Init(void) +{ + int status = 0; + + if (!TTF_initialized) { + FT_Error error = FT_Init_FreeType(&library); + if (error) { + TTF_SetFTError("Couldn't init FreeType engine", error); + status = -1; + } + } + if (status == 0) { + ++TTF_initialized; + } + return status; +} + +static unsigned long RWread( + FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count +) +{ + FILE *src; + + src = (FILE *)stream->descriptor.pointer; + fseek(src, (int)offset, SEEK_SET); + if (count == 0) { + return 0; + } + return (unsigned long)fread(buffer, 1, (int)count, src); +} + +static size_t fsize(FILE * file) +{ + size_t origPos = ftell(file); + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fseek(file, (long)origPos, SEEK_SET); + return size; +} + +static TTF_Font* TTF_OpenFontIndexRW(FILE *src, int freesrc, int ptsize, long index) +{ + TTF_Font* font; + FT_Error error; + FT_Face face; + FT_Fixed scale; + FT_Stream stream; + FT_CharMap found; + sint64 position; + int i; + + if (!TTF_initialized) { + TTF_SetError("Library not initialized"); + if (src && freesrc) { + fclose(src); + } + return NULL; + } + + if (!src) { + TTF_SetError("Passed a NULL font source"); + return NULL; + } + + /* Check to make sure we can seek in this stream */ + position = ftell(src); + if (position < 0) { + TTF_SetError("Can't seek in stream"); + if (freesrc) { + fclose(src); + } + return NULL; + } + + font = (TTF_Font*)malloc(sizeof *font); + if (font == NULL) { + TTF_SetError("Out of memory"); + if (freesrc) { + fclose(src); + } + return NULL; + } + memset(font, 0, sizeof(*font)); + + font->src = src; + font->freesrc = freesrc; + + stream = (FT_Stream)malloc(sizeof(*stream)); + if (stream == NULL) { + TTF_SetError("Out of memory"); + TTF_CloseFont(font); + return NULL; + } + memset(stream, 0, sizeof(*stream)); + + stream->read = RWread; + stream->descriptor.pointer = src; + stream->pos = (unsigned long)position; + stream->size = (unsigned long)(fsize(src) - position); + + font->args.flags = FT_OPEN_STREAM; + font->args.stream = stream; + + error = FT_Open_Face(library, &font->args, index, &font->face); + if (error) { + TTF_SetFTError("Couldn't load font file", error); + TTF_CloseFont(font); + return NULL; + } + face = font->face; + + /* Set charmap for loaded font */ + found = 0; + for (i = 0; i < face->num_charmaps; i++) { + FT_CharMap charmap = face->charmaps[i]; + if ((charmap->platform_id == 3 && charmap->encoding_id == 1) /* Windows Unicode */ + || (charmap->platform_id == 3 && charmap->encoding_id == 0) /* Windows Symbol */ + || (charmap->platform_id == 2 && charmap->encoding_id == 1) /* ISO Unicode */ + || (charmap->platform_id == 0)) { /* Apple Unicode */ + found = charmap; + break; + } + } + if (found) { + /* If this fails, continue using the default charmap */ + FT_Set_Charmap(face, found); + } + + /* Make sure that our font face is scalable (global metrics) */ + if (FT_IS_SCALABLE(face)) { + /* Set the character size and use default DPI (72) */ + error = FT_Set_Char_Size(font->face, 0, ptsize * 64, 0, 0); + if (error) { + TTF_SetFTError("Couldn't set font size", error); + TTF_CloseFont(font); + return NULL; + } + + /* Get the scalable font metrics for this font */ + scale = face->size->metrics.y_scale; + font->ascent = FT_CEIL(FT_MulFix(face->ascender, scale)); + font->descent = FT_CEIL(FT_MulFix(face->descender, scale)); + font->height = font->ascent - font->descent + /* baseline */ 1; + font->lineskip = FT_CEIL(FT_MulFix(face->height, scale)); + font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale)); + font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale)); + + } + else { + /* Non-scalable font case. ptsize determines which family + * or series of fonts to grab from the non-scalable format. + * It is not the point size of the font. + * */ + if (ptsize >= font->face->num_fixed_sizes) + ptsize = font->face->num_fixed_sizes - 1; + font->font_size_family = ptsize; + error = FT_Set_Pixel_Sizes(face, + face->available_sizes[ptsize].width, + face->available_sizes[ptsize].height); + + /* With non-scalale fonts, Freetype2 likes to fill many of the + * font metrics with the value of 0. The size of the + * non-scalable fonts must be determined differently + * or sometimes cannot be determined. + * */ + font->ascent = face->available_sizes[ptsize].height; + font->descent = 0; + font->height = face->available_sizes[ptsize].height; + font->lineskip = FT_CEIL(font->ascent); + font->underline_offset = FT_FLOOR(face->underline_position); + font->underline_height = FT_FLOOR(face->underline_thickness); + } + + if (font->underline_height < 1) { + font->underline_height = 1; + } + +#ifdef DEBUG_FONTS + printf("Font metrics:\n"); + printf("\tascent = %d, descent = %d\n", + font->ascent, font->descent); + printf("\theight = %d, lineskip = %d\n", + font->height, font->lineskip); + printf("\tunderline_offset = %d, underline_height = %d\n", + font->underline_offset, font->underline_height); + printf("\tunderline_top_row = %d, strikethrough_top_row = %d\n", + TTF_underline_top_row(font), TTF_strikethrough_top_row(font)); +#endif + + /* Initialize the font face style */ + font->face_style = TTF_STYLE_NORMAL; + if (font->face->style_flags & FT_STYLE_FLAG_BOLD) { + font->face_style |= TTF_STYLE_BOLD; + } + if (font->face->style_flags & FT_STYLE_FLAG_ITALIC) { + font->face_style |= TTF_STYLE_ITALIC; + } + + /* Set the default font style */ + font->style = font->face_style; + font->outline = 0; + font->kerning = 1; + font->glyph_overhang = face->size->metrics.y_ppem / 10; + /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */ + font->glyph_italics = 0.207f; + font->glyph_italics *= font->height; + + return font; +} + +static TTF_Font* TTF_OpenFontRW(FILE *src, int freesrc, int ptsize) +{ + return TTF_OpenFontIndexRW(src, freesrc, ptsize, 0); +} + +static TTF_Font* TTF_OpenFontIndex(const char *file, int ptsize, long index) +{ + FILE *rw = fopen(file, "rb"); + if (rw == NULL) { + return NULL; + } + return TTF_OpenFontIndexRW(rw, 1, ptsize, index); +} + +TTF_Font* TTF_OpenFont(const char *file, int ptsize) +{ + return TTF_OpenFontIndex(file, ptsize, 0); +} + +static void Flush_Glyph(c_glyph* glyph) +{ + glyph->stored = 0; + glyph->index = 0; + if (glyph->bitmap.buffer) { + free(glyph->bitmap.buffer); + glyph->bitmap.buffer = 0; + } + if (glyph->pixmap.buffer) { + free(glyph->pixmap.buffer); + glyph->pixmap.buffer = 0; + } + glyph->cached = 0; +} + +static void Flush_Cache(TTF_Font* font) +{ + int i; + int size = sizeof(font->cache) / sizeof(font->cache[0]); + + for (i = 0; i < size; ++i) { + if (font->cache[i].cached) { + Flush_Glyph(&font->cache[i]); + } + + } +} + +static FT_Error Load_Glyph(TTF_Font* font, uint16 ch, c_glyph* cached, int want) +{ + FT_Face face; + FT_Error error; + FT_GlyphSlot glyph; + FT_Glyph_Metrics* metrics; + FT_Outline* outline; + + if (!font || !font->face) { + return FT_Err_Invalid_Handle; + } + + face = font->face; + + /* Load the glyph */ + if (!cached->index) { + cached->index = FT_Get_Char_Index(face, ch); + } + error = FT_Load_Glyph(face, cached->index, FT_LOAD_DEFAULT | font->hinting); + if (error) { + return error; + } + + /* Get our glyph shortcuts */ + glyph = face->glyph; + metrics = &glyph->metrics; + outline = &glyph->outline; + + /* Get the glyph metrics if desired */ + if ((want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS)) { + if (FT_IS_SCALABLE(face)) { + /* Get the bounding box */ + cached->minx = FT_FLOOR(metrics->horiBearingX); + cached->maxx = FT_CEIL(metrics->horiBearingX + metrics->width); + cached->maxy = FT_FLOOR(metrics->horiBearingY); + cached->miny = cached->maxy - FT_CEIL(metrics->height); + cached->yoffset = font->ascent - cached->maxy; + cached->advance = FT_CEIL(metrics->horiAdvance); + } + else { + /* Get the bounding box for non-scalable format. + * Again, freetype2 fills in many of the font metrics + * with the value of 0, so some of the values we + * need must be calculated differently with certain + * assumptions about non-scalable formats. + * */ + cached->minx = FT_FLOOR(metrics->horiBearingX); + cached->maxx = FT_CEIL(metrics->horiBearingX + metrics->width); + cached->maxy = FT_FLOOR(metrics->horiBearingY); + cached->miny = cached->maxy - FT_CEIL(face->available_sizes[font->font_size_family].height); + cached->yoffset = 0; + cached->advance = FT_CEIL(metrics->horiAdvance); + } + + /* Adjust for bold and italic text */ + if (TTF_HANDLE_STYLE_BOLD(font)) { + cached->maxx += font->glyph_overhang; + } + if (TTF_HANDLE_STYLE_ITALIC(font)) { + cached->maxx += (int)ceil(font->glyph_italics); + } + cached->stored |= CACHED_METRICS; + } + + if (((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || + ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP))) { + int mono = (want & CACHED_BITMAP); + int i; + FT_Bitmap* src; + FT_Bitmap* dst; + FT_Glyph bitmap_glyph = NULL; + + /* Handle the italic style */ + if (TTF_HANDLE_STYLE_ITALIC(font)) { + FT_Matrix shear; + + shear.xx = 1 << 16; + shear.xy = (int)(font->glyph_italics * (1 << 16)) / font->height; + shear.yx = 0; + shear.yy = 1 << 16; + + FT_Outline_Transform(outline, &shear); + } + + /* Render as outline */ + if ((font->outline > 0) && glyph->format != FT_GLYPH_FORMAT_BITMAP) { + FT_Stroker stroker; + FT_Get_Glyph(glyph, &bitmap_glyph); + error = FT_Stroker_New(library, &stroker); + if (error) { + return error; + } + FT_Stroker_Set(stroker, font->outline * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); + FT_Glyph_Stroke(&bitmap_glyph, stroker, 1 /* delete the original glyph */); + FT_Stroker_Done(stroker); + /* Render the glyph */ + error = FT_Glyph_To_Bitmap(&bitmap_glyph, mono ? ft_render_mode_mono : ft_render_mode_normal, 0, 1); + if (error) { + FT_Done_Glyph(bitmap_glyph); + return error; + } + src = &((FT_BitmapGlyph)bitmap_glyph)->bitmap; + } + else { + /* Render the glyph */ + error = FT_Render_Glyph(glyph, mono ? ft_render_mode_mono : ft_render_mode_normal); + if (error) { + return error; + } + src = &glyph->bitmap; + } + /* Copy over information to cache */ + if (mono) { + dst = &cached->bitmap; + } + else { + dst = &cached->pixmap; + } + memcpy(dst, src, sizeof(*dst)); + + /* FT_Render_Glyph() and .fon fonts always generate a + * two-color (black and white) glyphslot surface, even + * when rendered in ft_render_mode_normal. */ + /* FT_IS_SCALABLE() means that the font is in outline format, + * but does not imply that outline is rendered as 8-bit + * grayscale, because embedded bitmap/graymap is preferred + * (see FT_LOAD_DEFAULT section of FreeType2 API Reference). + * FT_Render_Glyph() canreturn two-color bitmap or 4/16/256- + * color graymap according to the format of embedded bitmap/ + * graymap. */ + if (src->pixel_mode == FT_PIXEL_MODE_MONO) { + dst->pitch *= 8; + } + else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) { + dst->pitch *= 4; + } + else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) { + dst->pitch *= 2; + } + + /* Adjust for bold and italic text */ + if (TTF_HANDLE_STYLE_BOLD(font)) { + int bump = font->glyph_overhang; + dst->pitch += bump; + dst->width += bump; + } + if (TTF_HANDLE_STYLE_ITALIC(font)) { + int bump = (int)ceil(font->glyph_italics); + dst->pitch += bump; + dst->width += bump; + } + + if (dst->rows != 0) { + dst->buffer = (unsigned char *)malloc(dst->pitch * dst->rows); + if (!dst->buffer) { + return FT_Err_Out_Of_Memory; + } + memset(dst->buffer, 0, dst->pitch * dst->rows); + + for (i = 0; i < src->rows; i++) { + int soffset = i * src->pitch; + int doffset = i * dst->pitch; + if (mono) { + unsigned char *srcp = src->buffer + soffset; + unsigned char *dstp = dst->buffer + doffset; + int j; + if (src->pixel_mode == FT_PIXEL_MODE_MONO) { + for (j = 0; j < src->width; j += 8) { + unsigned char c = *srcp++; + *dstp++ = (c & 0x80) >> 7; + c <<= 1; + *dstp++ = (c & 0x80) >> 7; + c <<= 1; + *dstp++ = (c & 0x80) >> 7; + c <<= 1; + *dstp++ = (c & 0x80) >> 7; + c <<= 1; + *dstp++ = (c & 0x80) >> 7; + c <<= 1; + *dstp++ = (c & 0x80) >> 7; + c <<= 1; + *dstp++ = (c & 0x80) >> 7; + c <<= 1; + *dstp++ = (c & 0x80) >> 7; + } + } + else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) { + for (j = 0; j < src->width; j += 4) { + unsigned char c = *srcp++; + *dstp++ = (((c & 0xA0) >> 6) >= 0x2) ? 1 : 0; + c <<= 2; + *dstp++ = (((c & 0xA0) >> 6) >= 0x2) ? 1 : 0; + c <<= 2; + *dstp++ = (((c & 0xA0) >> 6) >= 0x2) ? 1 : 0; + c <<= 2; + *dstp++ = (((c & 0xA0) >> 6) >= 0x2) ? 1 : 0; + } + } + else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) { + for (j = 0; j < src->width; j += 2) { + unsigned char c = *srcp++; + *dstp++ = (((c & 0xF0) >> 4) >= 0x8) ? 1 : 0; + c <<= 4; + *dstp++ = (((c & 0xF0) >> 4) >= 0x8) ? 1 : 0; + } + } + else { + for (j = 0; j < src->width; j++) { + unsigned char c = *srcp++; + *dstp++ = (c >= 0x80) ? 1 : 0; + } + } + } + else if (src->pixel_mode == FT_PIXEL_MODE_MONO) { + /* This special case wouldn't + * be here if the FT_Render_Glyph() + * function wasn't buggy when it tried + * to render a .fon font with 256 + * shades of gray. Instead, it + * returns a black and white surface + * and we have to translate it back + * to a 256 gray shaded surface. + * */ + unsigned char *srcp = src->buffer + soffset; + unsigned char *dstp = dst->buffer + doffset; + unsigned char c; + int j, k; + for (j = 0; j < src->width; j += 8) { + c = *srcp++; + for (k = 0; k < 8; ++k) { + if ((c & 0x80) >> 7) { + *dstp++ = NUM_GRAYS - 1; + } + else { + *dstp++ = 0x00; + } + c <<= 1; + } + } + } + else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) { + unsigned char *srcp = src->buffer + soffset; + unsigned char *dstp = dst->buffer + doffset; + unsigned char c; + int j, k; + for (j = 0; j < src->width; j += 4) { + c = *srcp++; + for (k = 0; k < 4; ++k) { + if ((c & 0xA0) >> 6) { + *dstp++ = NUM_GRAYS * ((c & 0xA0) >> 6) / 3 - 1; + } + else { + *dstp++ = 0x00; + } + c <<= 2; + } + } + } + else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) { + unsigned char *srcp = src->buffer + soffset; + unsigned char *dstp = dst->buffer + doffset; + unsigned char c; + int j, k; + for (j = 0; j < src->width; j += 2) { + c = *srcp++; + for (k = 0; k < 2; ++k) { + if ((c & 0xF0) >> 4) { + *dstp++ = NUM_GRAYS * ((c & 0xF0) >> 4) / 15 - 1; + } + else { + *dstp++ = 0x00; + } + c <<= 4; + } + } + } + else { + memcpy(dst->buffer + doffset, + src->buffer + soffset, src->pitch); + } + } + } + + /* Handle the bold style */ + if (TTF_HANDLE_STYLE_BOLD(font)) { + int row; + int col; + int offset; + int pixel; + uint8* pixmap; + + /* The pixmap is a little hard, we have to add and clamp */ + for (row = dst->rows - 1; row >= 0; --row) { + pixmap = (uint8*)dst->buffer + row * dst->pitch; + for (offset = 1; offset <= font->glyph_overhang; ++offset) { + for (col = dst->width - 1; col > 0; --col) { + if (mono) { + pixmap[col] |= pixmap[col - 1]; + } + else { + pixel = (pixmap[col] + pixmap[col - 1]); + if (pixel > NUM_GRAYS - 1) { + pixel = NUM_GRAYS - 1; + } + pixmap[col] = (uint8)pixel; + } + } + } + } + } + + /* Mark that we rendered this format */ + if (mono) { + cached->stored |= CACHED_BITMAP; + } + else { + cached->stored |= CACHED_PIXMAP; + } + + /* Free outlined glyph */ + if (bitmap_glyph) { + FT_Done_Glyph(bitmap_glyph); + } + } + + /* We're done, mark this glyph cached */ + cached->cached = ch; + + return 0; +} + +static FT_Error Find_Glyph(TTF_Font* font, uint16 ch, int want) +{ + int retval = 0; + int hsize = sizeof(font->cache) / sizeof(font->cache[0]); + + int h = ch % hsize; + font->current = &font->cache[h]; + + if (font->current->cached != ch) + Flush_Glyph(font->current); + + if ((font->current->stored & want) != want) { + retval = Load_Glyph(font, ch, font->current, want); + } + return retval; +} + +void TTF_CloseFont(TTF_Font* font) +{ + if (font) { + Flush_Cache(font); + if (font->face) { + FT_Done_Face(font->face); + } + if (font->args.stream) { + free(font->args.stream); + } + if (font->freesrc) { + fclose(font->src); + } + free(font); + } +} + +/* Gets a unicode value from a UTF-8 encoded string and advance the string */ +#define UNKNOWN_UNICODE 0xFFFD +static uint32 UTF8_getch(const char **src, size_t *srclen) +{ + const uint8 *p = *(const uint8**)src; + size_t left = 0; + bool overlong = false; + UNUSED(overlong); + bool underflow = false; + uint32 ch = UNKNOWN_UNICODE; + + if (*srclen == 0) { + return UNKNOWN_UNICODE; + } + if (p[0] >= 0xFC) { + if ((p[0] & 0xFE) == 0xFC) { + if (p[0] == 0xFC && (p[1] & 0xFC) == 0x80) { + overlong = true; + } + ch = (uint32)(p[0] & 0x01); + left = 5; + } + } + else if (p[0] >= 0xF8) { + if ((p[0] & 0xFC) == 0xF8) { + if (p[0] == 0xF8 && (p[1] & 0xF8) == 0x80) { + overlong = true; + } + ch = (uint32)(p[0] & 0x03); + left = 4; + } + } + else if (p[0] >= 0xF0) { + if ((p[0] & 0xF8) == 0xF0) { + if (p[0] == 0xF0 && (p[1] & 0xF0) == 0x80) { + overlong = true; + } + ch = (uint32)(p[0] & 0x07); + left = 3; + } + } + else if (p[0] >= 0xE0) { + if ((p[0] & 0xF0) == 0xE0) { + if (p[0] == 0xE0 && (p[1] & 0xE0) == 0x80) { + overlong = true; + } + ch = (uint32)(p[0] & 0x0F); + left = 2; + } + } + else if (p[0] >= 0xC0) { + if ((p[0] & 0xE0) == 0xC0) { + if ((p[0] & 0xDE) == 0xC0) { + overlong = true; + } + ch = (uint32)(p[0] & 0x1F); + left = 1; + } + } + else { + if ((p[0] & 0x80) == 0x00) { + ch = (uint32)p[0]; + } + } + ++*src; + --*srclen; + while (left > 0 && *srclen > 0) { + ++p; + if ((p[0] & 0xC0) != 0x80) { + ch = UNKNOWN_UNICODE; + break; + } + ch <<= 6; + ch |= (p[0] & 0x3F); + ++*src; + --*srclen; + --left; + } + if (left > 0) { + underflow = true; + } + /* Technically overlong sequences are invalid and should not be interpreted. + However, it doesn't cause a security risk here and I don't see any harm in + displaying them. The application is responsible for any other side effects + of allowing overlong sequences (e.g. string compares failing, etc.) + See bug 1931 for sample input that triggers this. + */ + /*if (overlong) return UNKNOWN_UNICODE;*/ + if (underflow || + (ch >= 0xD800 && ch <= 0xDFFF) || + (ch == 0xFFFE || ch == 0xFFFF) || ch > 0x10FFFF) { + ch = UNKNOWN_UNICODE; + } + return ch; +} + +int TTF_GlyphIsProvided(const TTF_Font *font, codepoint_t ch) +{ + return(FT_Get_Char_Index(font->face, ch)); +} + +int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h) +{ + int status; + int x, z; + int minx, maxx; + int miny, maxy; + c_glyph *glyph; + FT_Error error; + FT_Long use_kerning; + FT_UInt prev_index = 0; + int outline_delta = 0; + size_t textlen; + + TTF_CHECKPOINTER(text, -1); + + /* Initialize everything to 0 */ + status = 0; + minx = maxx = 0; + miny = maxy = 0; + + /* check kerning */ + use_kerning = FT_HAS_KERNING(font->face) && font->kerning; + + /* Init outline handling */ + if (font->outline > 0) { + outline_delta = font->outline * 2; + } + + /* Load each character and sum it's bounding box */ + textlen = strlen(text); + x = 0; + while (textlen > 0) { + uint16 c = UTF8_getch(&text, &textlen); + if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { + continue; + } + + error = Find_Glyph(font, c, CACHED_METRICS); + if (error) { + TTF_SetFTError("Couldn't find glyph", error); + return -1; + } + glyph = font->current; + + /* handle kerning */ + if (use_kerning && prev_index && glyph->index) { + FT_Vector delta; + FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta); + x += delta.x >> 6; + } + +#if 0 + if ((ch == text) && (glyph->minx < 0)) { + /* Fixes the texture wrapping bug when the first letter + * has a negative minx value or horibearing value. The entire + * bounding box must be adjusted to be bigger so the entire + * letter can fit without any texture corruption or wrapping. + * + * Effects: First enlarges bounding box. + * Second, xstart has to start ahead of its normal spot in the + * negative direction of the negative minx value. + * (pushes everything to the right). + * + * This will make the memory copy of the glyph bitmap data + * work out correctly. + * */ + z -= glyph->minx; + } +#endif + + z = x + glyph->minx; + if (minx > z) { + minx = z; + } + if (TTF_HANDLE_STYLE_BOLD(font)) { + x += font->glyph_overhang; + } + if (glyph->advance > glyph->maxx) { + z = x + glyph->advance; + } + else { + z = x + glyph->maxx; + } + if (maxx < z) { + maxx = z; + } + x += glyph->advance; + + if (glyph->miny < miny) { + miny = glyph->miny; + } + if (glyph->maxy > maxy) { + maxy = glyph->maxy; + } + prev_index = glyph->index; + } + + /* Fill the bounds rectangle */ + if (w) { + /* Add outline extra width */ + *w = (maxx - minx) + outline_delta; + } + if (h) { + /* Some fonts descend below font height (FletcherGothicFLF) */ + /* Add outline extra height */ + *h = (font->ascent - miny) + outline_delta; + if (*h < font->height) { + *h = font->height; + } + /* Update height according to the needs of the underline style */ + if (TTF_HANDLE_STYLE_UNDERLINE(font)) { + int bottom_row = TTF_underline_bottom_row(font); + if (*h < bottom_row) { + *h = bottom_row; + } + } + } + return status; +} + +TTFSurface *TTF_RenderUTF8_Solid(TTF_Font *font, + const char *text, uint32 colour) +{ + bool first; + int xstart; + int width; + int height; + TTFSurface* textbuf; + uint8* src; + uint8* dst; + uint8 *dst_check; + int row, col; + c_glyph *glyph; + + FT_Bitmap *current; + FT_Error error; + FT_Long use_kerning; + FT_UInt prev_index = 0; + size_t textlen; + + TTF_CHECKPOINTER(text, NULL); + + /* Get the dimensions of the text surface */ + if ((TTF_SizeUTF8(font, text, &width, &height) < 0) || !width) { + TTF_SetError("Text has zero width"); + return NULL; + } + + /* Create the target surface */ + textbuf = calloc(1, sizeof(TTFSurface)); + if (textbuf == NULL) { + return NULL; + } + textbuf->w = width; + textbuf->h = height; + textbuf->pitch = width; + textbuf->pixels = calloc(1, width * height); + + /* Adding bound checking to avoid all kinds of memory corruption errors + that may occur. */ + dst_check = (uint8*)textbuf->pixels + textbuf->pitch * textbuf->h; + + /* check kerning */ + use_kerning = FT_HAS_KERNING(font->face) && font->kerning; + + /* Load and render each character */ + textlen = strlen(text); + first = true; + xstart = 0; + while (textlen > 0) { + uint16 c = UTF8_getch(&text, &textlen); + if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { + continue; + } + + error = Find_Glyph(font, c, CACHED_METRICS | CACHED_BITMAP); + if (error) { + TTF_SetFTError("Couldn't find glyph", error); + ttf_free_surface(textbuf); + return NULL; + } + glyph = font->current; + current = &glyph->bitmap; + /* Ensure the width of the pixmap is correct. On some cases, + * freetype may report a larger pixmap than possible.*/ + width = current->width; + if (font->outline <= 0 && width > glyph->maxx - glyph->minx) { + width = glyph->maxx - glyph->minx; + } + /* do kerning, if possible AC-Patch */ + if (use_kerning && prev_index && glyph->index) { + FT_Vector delta; + FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta); + xstart += delta.x >> 6; + } + /* Compensate for wrap around bug with negative minx's */ + if (first && (glyph->minx < 0)) { + xstart -= glyph->minx; + } + first = false; + + for (row = 0; row < current->rows; ++row) { + /* Make sure we don't go either over, or under the + * limit */ + if (row + glyph->yoffset < 0) { + continue; + } + if (row + glyph->yoffset >= textbuf->h) { + continue; + } + dst = (uint8*)textbuf->pixels + + (row + glyph->yoffset) * textbuf->pitch + + xstart + glyph->minx; + src = current->buffer + row * current->pitch; + + for (col = width; col>0 && dst < dst_check; --col) { + *dst++ |= *src++; + } + } + + xstart += glyph->advance; + if (TTF_HANDLE_STYLE_BOLD(font)) { + xstart += font->glyph_overhang; + } + prev_index = glyph->index; + } + + /* Handle the underline style */ + if (TTF_HANDLE_STYLE_UNDERLINE(font)) { + row = TTF_underline_top_row(font); + TTF_drawLine_Solid(font, textbuf, row); + } + + /* Handle the strikethrough style */ + if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { + row = TTF_strikethrough_top_row(font); + TTF_drawLine_Solid(font, textbuf, row); + } + return textbuf; +} + +static bool CharacterIsDelimiter(char c, const char *delimiters) +{ + while (*delimiters) { + if (c == *delimiters) { + return true; + } + ++delimiters; + } + return false; +} + +void TTF_Quit(void) +{ + if (TTF_initialized) { + if (--TTF_initialized == 0) { + FT_Done_FreeType(library); + } + } +} + +#endif 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