From 2b821037d7c3ded52fc91a601446c858e16b2672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 15 May 2026 13:35:21 +0200 Subject: [PATCH] SDL3_ttf work --- CMakeLists.txt | 10 +++- Common/Render/Text/draw_text.cpp | 2 +- Common/Render/Text/draw_text_sdl.cpp | 85 +++++++++++++++------------- Common/Render/Text/draw_text_sdl.h | 12 ++-- 4 files changed, 61 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b4c2bbf71..7b67f613d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,17 +326,21 @@ if(USING_EGL) endif() set(SDL_LIB_TARGET "") +set(SDL_TTF_LIB_TARGET "") if(NOT LIBRETRO AND NOT IOS AND NOT MACOSX) find_package(SDL3 REQUIRED) set(SDL_LIB_TARGET SDL3::SDL3) + find_package(SDL3_ttf REQUIRED) + set(SDL_TTF_LIB_TARGET SDL3_ttf::SDL3_ttf) + add_compile_definitions(USE_SDL3_TTF) message(STATUS "Using SDL3") endif() if(MACOSX AND NOT IOS) if(USE_SYSTEM_LIBSDL2) find_package(SDL2) - find_package(SDL2_ttf) + find_package(SDL3_ttf QUIET) else() find_library(SDL2Fwk SDL2 REQUIRED PATHS SDL/macOS) message(STATUS "found SDL2Fwk=${SDL2Fwk}") @@ -1521,7 +1525,9 @@ else() SDL/SDLVulkanGraphicsContext.cpp ) endif() - # SDL2_ttf integration is disabled while migrating to SDL3-only. + if(SDL_TTF_LIB_TARGET) + list(APPEND nativeExtraLibs ${SDL_TTF_LIB_TARGET}) + endif() if(APPLE) list(APPEND nativeExtra SDL/SDLJoystick.h diff --git a/Common/Render/Text/draw_text.cpp b/Common/Render/Text/draw_text.cpp index 41305009ff..5e08919dc8 100644 --- a/Common/Render/Text/draw_text.cpp +++ b/Common/Render/Text/draw_text.cpp @@ -316,7 +316,7 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) { drawer = new TextDrawerQt(draw); #elif PPSSPP_PLATFORM(ANDROID) drawer = new TextDrawerAndroid(draw); -#elif USE_SDL2_TTF +#elif USE_SDL3_TTF drawer = new TextDrawerSDL(draw); #endif if (drawer && !drawer->IsReady()) { diff --git a/Common/Render/Text/draw_text_sdl.cpp b/Common/Render/Text/draw_text_sdl.cpp index dbec9cbc43..8d19b9e9c4 100644 --- a/Common/Render/Text/draw_text_sdl.cpp +++ b/Common/Render/Text/draw_text_sdl.cpp @@ -13,10 +13,24 @@ #include "Common/Render/Text/draw_text.h" #include "Common/Render/Text/draw_text_sdl.h" -#if defined(USE_SDL2_TTF) +#if defined(USE_SDL3_TTF) -#include "SDL2/SDL.h" -#include "SDL2/SDL_ttf.h" +#include +#include +#include + +static TTF_Font *OpenFontFace(const std::string &fontPath, float ptSize, int faceIndex) { + SDL_PropertiesID props = SDL_CreateProperties(); + if (!props) { + return nullptr; + } + SDL_SetStringProperty(props, TTF_PROP_FONT_CREATE_FILENAME_STRING, fontPath.c_str()); + SDL_SetFloatProperty(props, TTF_PROP_FONT_CREATE_SIZE_FLOAT, ptSize); + SDL_SetNumberProperty(props, TTF_PROP_FONT_CREATE_FACE_NUMBER, faceIndex); + TTF_Font *font = TTF_OpenFontWithProperties(props); + SDL_DestroyProperties(props); + return font; +} static std::string getlocale() { // setlocale is not an intuitive function... @@ -31,13 +45,13 @@ static std::string getlocale() { } TextDrawerSDL::TextDrawerSDL(Draw::DrawContext *draw): TextDrawer(draw) { - if (TTF_Init() < 0) { - ERROR_LOG(Log::G3D, "Unable to initialize SDL2_ttf"); + if (!TTF_Init()) { + ERROR_LOG(Log::G3D, "Unable to initialize SDL3_ttf"); } dpiScale_ = CalculateDPIScale(); -#if defined(USE_SDL2_TTF_FONTCONFIG) +#if defined(USE_SDL3_TTF_FONTCONFIG) config = FcInitLoadConfigAndFonts(); #endif @@ -50,7 +64,7 @@ TextDrawerSDL::~TextDrawerSDL() { TTF_Quit(); -#if defined(USE_SDL2_TTF_FONTCONFIG) + #if defined(USE_SDL3_TTF_FONTCONFIG) FcConfigDestroy(config); // Don't call this - it crashes, see https://github.com/openframeworks/openFrameworks/issues/5061. //FcFini(); @@ -59,7 +73,7 @@ TextDrawerSDL::~TextDrawerSDL() { // If a user complains about missing characters on SDL, re-visit this! void TextDrawerSDL::PrepareFallbackFonts(std::string_view locale) { -#if defined(USE_SDL2_TTF_FONTCONFIG) +#if defined(USE_SDL3_TTF_FONTCONFIG) FcObjectSet *os = FcObjectSetBuild (FC_FILE, FC_INDEX, (char *) 0); // To install the recommended Droid Sans fallback font in Ubuntu: @@ -189,22 +203,11 @@ void TextDrawerSDL::PrepareFallbackFonts(std::string_view locale) { Path fontPath = Path(fontDirs[i]) / fallbackFonts[j]; if (File::Exists(fontPath)) { - TTF_Font *openedFont = TTF_OpenFont(fontPath.ToString().c_str(), 24); - int64_t numFaces = TTF_FontFaces(openedFont); - + TTF_Font *openedFont = TTF_OpenFont(fontPath.ToString().c_str(), 24.0f); + int numFaces = TTF_GetNumFontFaces(openedFont); for (int k = 0; k < numFaces; k++) { - TTF_Font *fontFace = TTF_OpenFontIndex(fontPath.ToString().c_str(), 24, k); - std::string fontFaceName(TTF_FontFaceStyleName(fontFace)); - TTF_CloseFont(fontFace); - - if (strstr(fontFaceName.c_str(), "Medium") || - strstr(fontFaceName.c_str(), "Regular")) - { - fallbackFontPaths_.push_back(std::make_pair(fontPath.ToString(), k)); - break; - } + fallbackFontPaths_.push_back(std::make_pair(fontPath.ToString(), k)); } - TTF_CloseFont(openedFont); } } @@ -230,7 +233,7 @@ uint32_t TextDrawerSDL::CheckMissingGlyph(std::string_view text) { uint32_t missingGlyph = 0; while (!utf8Decoded.end()) { uint32_t glyph = utf8Decoded.next(); - if (!TTF_GlyphIsProvided32(font, glyph)) { + if (!TTF_GetGlyphMetrics(font, glyph, nullptr, nullptr, nullptr, nullptr, nullptr)) { missingGlyph = glyph; break; } @@ -249,7 +252,7 @@ int TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) { // If we encounter a missing glyph, try to use one of already loaded fallback fonts. for (int i = 0; i < fallbackFonts_.size(); i++) { TTF_Font *fallbackFont = fallbackFonts_[i]; - if (TTF_GlyphIsProvided32(fallbackFont, missingGlyph)) { + if (TTF_GetGlyphMetrics(fallbackFont, missingGlyph, nullptr, nullptr, nullptr, nullptr, nullptr)) { glyphFallbackFontIndex_[missingGlyph] = i; return i; } @@ -261,13 +264,15 @@ int TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) { std::string& fontPath = fallbackFontPaths_[i].first; int faceIndex = fallbackFontPaths_[i].second; - TTF_Font *font = TTF_OpenFontIndex(fontPath.c_str(), ptSize, faceIndex); + TTF_Font *font = OpenFontFace(fontPath, static_cast(ptSize), faceIndex); - if (TTF_GlyphIsProvided32(font, missingGlyph)) { + if (font && TTF_GetGlyphMetrics(font, missingGlyph, nullptr, nullptr, nullptr, nullptr, nullptr)) { fallbackFonts_.push_back(font); return fallbackFonts_.size() - 1; } else { - TTF_CloseFont(font); + if (font) { + TTF_CloseFont(font); + } } } @@ -292,9 +297,9 @@ void TextDrawerSDL::SetOrCreateFont(const FontStyle &style) { size_t fileSz; fileData = g_VFS.ReadFile(useFont.c_str(), &fileSz); if (fileData) { - SDL_RWops *rw = SDL_RWFromMem(fileData, static_cast(fileSz)); + SDL_IOStream *rw = SDL_IOFromConstMem(fileData, fileSz); INFO_LOG(Log::G3D, "Opened font from RW: '%p' '%d'", fileData, (int)fileSz); - font = TTF_OpenFontRW(rw, 1, ptSize); + font = TTF_OpenFontIO(rw, true, static_cast(ptSize)); if (!font) { ERROR_LOG(Log::G3D, "Failed to load font from asset file: '%s'", useFont.c_str()); } @@ -343,7 +348,7 @@ void TextDrawerSDL::MeasureStringInternal(std::string_view str, float *w, float line = " "; } temp = line; // zero-terminate, ugh. - TTF_SizeUTF8(font, temp.c_str(), &width, &height); + TTF_GetStringSize(font, temp.c_str(), temp.size(), &width, &height); if (width > extW) extW = width; @@ -375,7 +380,7 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStrin std::string processedStr(str); - // If a string includes only newlines, SDL2_ttf will refuse to render it + // If a string includes only newlines, SDL3_ttf will refuse to render it // thinking it is empty. Add a space to avoid that. bool isAllNewline = processedStr.find_first_not_of('\n') == std::string::npos; @@ -386,24 +391,26 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStrin uint32_t missingGlyph = CheckMissingGlyph(processedStr); if (missingGlyph) { - const int ptSize = TTF_FontHeight(font) / 1.25; + const int ptSize = static_cast(TTF_GetFontSize(font)); int fallbackFont = FindFallbackFonts(missingGlyph, ptSize); if (fallbackFont >= 0 && fallbackFont < (int)fallbackFonts_.size()) { font = fallbackFonts_[fallbackFont]; } } -#if SDL_TTF_VERSION_ATLEAST(2, 20, 0) if (align & ALIGN_HCENTER) - TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_CENTER); + TTF_SetFontWrapAlignment(font, TTF_HORIZONTAL_ALIGN_CENTER); else if (align & ALIGN_RIGHT) - TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_RIGHT); + TTF_SetFontWrapAlignment(font, TTF_HORIZONTAL_ALIGN_RIGHT); else - TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_LEFT); -#endif + TTF_SetFontWrapAlignment(font, TTF_HORIZONTAL_ALIGN_LEFT); SDL_Color fgColor = { 0xFF, 0xFF, 0xFF, 0xFF }; - SDL_Surface *text = TTF_RenderUTF8_Blended_Wrapped(font, processedStr.c_str(), fgColor, 0); + SDL_Surface *text = TTF_RenderText_Blended_Wrapped(font, processedStr.c_str(), processedStr.size(), fgColor, 0); + if (!text) { + ERROR_LOG(Log::G3D, "Failed to render SDL3_ttf text: %s", SDL_GetError()); + return false; + } SDL_LockSurface(text); entry.texture = nullptr; @@ -440,7 +447,7 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStrin } SDL_UnlockSurface(text); - SDL_FreeSurface(text); + SDL_DestroySurface(text); return true; } diff --git a/Common/Render/Text/draw_text_sdl.h b/Common/Render/Text/draw_text_sdl.h index fc0d3bf19f..34c657dd82 100644 --- a/Common/Render/Text/draw_text_sdl.h +++ b/Common/Render/Text/draw_text_sdl.h @@ -5,12 +5,12 @@ #include #include "Common/Render/Text/draw_text.h" -#if defined(USE_SDL2_TTF) +#if defined(USE_SDL3_TTF) -#include "SDL2/SDL.h" -#include "SDL2/SDL_ttf.h" +#include +#include -#if defined(USE_SDL2_TTF_FONTCONFIG) +#if defined(USE_SDL3_TTF_FONTCONFIG) #include #endif @@ -40,9 +40,9 @@ private: std::map glyphFallbackFontIndex_; -#if defined(USE_SDL2_TTF_FONTCONFIG) + #if defined(USE_SDL3_TTF_FONTCONFIG) FcConfig *config; -#endif + #endif }; #endif