SDL2_ttf-2.24.0/.gitmodules0000664000000000000000000000040514551317614012273 0ustar00[submodule "external/freetype"] path = external/freetype url = https://github.com/libsdl-org/freetype.git branch = VER-2-13-2-SDL [submodule "external/harfbuzz"] path = external/harfbuzz url = https://github.com/libsdl-org/harfbuzz.git branch = 8.1.1-SDL SDL2_ttf-2.24.0/.wikiheaders-options0000664000000000000000000000143714632426313014112 0ustar00projectfullname = SDL_ttf projectshortname = SDL_ttf incsubdir = wikisubdir = SDL_ttf apiprefixregex = TTF_ mainincludefname = SDL_ttf.h versionfname = SDL_ttf.h versionmajorregex = \A\#define\s+SDL_TTF_MAJOR_VERSION\s+(\d+)\Z versionminorregex = \A\#define\s+SDL_TTF_MINOR_VERSION\s+(\d+)\Z versionpatchregex = \A\#define\s+SDL_TTF_PATCHLEVEL\s+(\d+)\Z selectheaderregex = \ASDL_ttf\.h\Z projecturl = https://libsdl.org/projects/SDL_ttf wikiurl = https://wiki.libsdl.org/SDL_ttf bugreporturl = https://github.com/libsdl-org/sdlwiki/issues/new warn_about_missing = 0 wikipreamble = (This function is part of SDL_ttf, a separate library from SDL.) wikiheaderfiletext = Defined in [<%fname%>](https://github.com/libsdl-org/SDL_ttf/blob/SDL2/include/%fname%) manpageheaderfiletext = Defined in %fname% SDL2_ttf-2.24.0/Android.mk0000664000000000000000000000251414235550625012032 0ustar00# Save the local path SDL_TTF_LOCAL_PATH := $(call my-dir) # Enable this if you want to use HarfBuzz SUPPORT_HARFBUZZ ?= true HARFBUZZ_LIBRARY_PATH := external/harfbuzz FREETYPE_LIBRARY_PATH := external/freetype # Build freetype library ifneq ($(FREETYPE_LIBRARY_PATH),) include $(SDL_TTF_LOCAL_PATH)/$(FREETYPE_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_HARFBUZZ),true) include $(SDL_TTF_LOCAL_PATH)/$(HARFBUZZ_LIBRARY_PATH)/Android.mk endif # Restore local path LOCAL_PATH := $(SDL_TTF_LOCAL_PATH) include $(CLEAR_VARS) LOCAL_MODULE := SDL2_ttf LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_SRC_FILES := SDL_ttf.c.neon LOCAL_CFLAGS += -O2 ifneq ($(FREETYPE_LIBRARY_PATH),) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(FREETYPE_LIBRARY_PATH)/include LOCAL_STATIC_LIBRARIES += freetype endif ifeq ($(SUPPORT_HARFBUZZ),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(HARFBUZZ_LIBRARY_PATH)/src LOCAL_CFLAGS += -DTTF_USE_HARFBUZZ LOCAL_STATIC_LIBRARIES += harfbuzz endif LOCAL_SHARED_LIBRARIES := SDL2 LOCAL_EXPORT_C_INCLUDES += $(LOCAL_C_INCLUDES) include $(BUILD_SHARED_LIBRARY) ########################### # # SDL2_ttf static library # ########################### LOCAL_MODULE := SDL2_ttf_static LOCAL_MODULE_FILENAME := libSDL2_ttf LOCAL_LDLIBS := LOCAL_EXPORT_LDLIBS := include $(BUILD_STATIC_LIBRARY) SDL2_ttf-2.24.0/CHANGES.txt0000664000000000000000000000772214735274341011743 0ustar002.24.0: * Added TTF_SetFontLineSkip() 2.22.0: * Updated to FreeType version 2.13.2 and HarfBuzz version 8.1.1 2.20.0: * Added support for building with CMake * Added TTF_GetFontWrappedAlign() and TTF_SetFontWrappedAlign() to set alignment on wrapped text * Added functions to render using FreeType LCD algorithm: TTF_RenderText_LCD() TTF_RenderUTF8_LCD() TTF_RenderUNICODE_LCD() TTF_RenderText_LCD_Wrapped() TTF_RenderUTF8_LCD_Wrapped() TTF_RenderUNICODE_LCD_Wrapped() TTF_RenderGlyph_LCD() TTF_RenderGlyph32_LCD() * Added TTF_SetFontDirection() and TTF_SetFontScriptName() for additional control over fonts using HarfBuzz * Updated to FreeType version 2.12.1 and HarfBuzz version 2.9.1, fixing CVE-2018-25032 * Fixed crash when loading fonts at certain sizes on Windows * Fix memory corruption loading malformed TTF files (CVE-2022-27470) 2.0.18: Ozkan Sezer - Wed Jan 5 14:15:46 PST 2022 * Added TTF_GetFreeTypeVersion() and TTF_GetHarfBuzzVersion() Sylvain - Jan 16, 2021 * Added support for Signed Distance Field rendering with TTF_SetFontSDF() and TTF_GetFontSDF() David Ludwig - Dec 28, 2019 * Added optional DPI-scaling of fonts, with the following new functions: TTF_OpenFontDPI() TTF_OpenFontIndexDPI() TTF_OpenFontDPIRW() TTF_OpenFontIndexDPIRW() TTF_SetFontSizeDPI() Weard Anaether - Dec 2, 2019 * Added 32-bit character support with: TTF_RenderGlyph32_Solid() TTF_RenderGlyph32_Shaded() TTF_RenderGlyph32_Blended() TTF_GetFontKerningSizeGlyphs32() Arthur Danskin - Sep 3, 2019 * Added 32-bit character support with TTF_GlyphIsProvided32() and TTF_GlyphMetrics32() Sylvain - Apr 5, 2019 * Added functions to set direction and script when using Harfbuzz: TTF_SetDirection() TTF_SetScript() Sylvain - Mar 25, 2019 * Added extended API for text measurement: TTF_MeasureText() TTF_MeasureUTF8() TTF_MeasureUNICODE() Sylvain - Jan 31, 2019 * Added TTF_SetFontSize() to set font size dynamically * Added 'Shaded' and 'Solid' text wrapped functions: TTF_RenderText_Solid_Wrapped() TTF_RenderUTF8_Solid_Wrapped() TTF_RenderUNICODE_Solid_Wrapped() TTF_RenderText_Shaded_Wrapped() TTF_RenderUTF8_Shaded_Wrapped() TTF_RenderUNICODE_Shaded_Wrapped() * Added TTF_HINTING_LIGHT_SUBPIXEL for better results at small text sizes at a performance cost 2.0.15: Sam Lantinga - Fri Oct 26 13:26:54 PDT 2018 * Updated to FreeType version 2.9.1 Sam Lantinga - Sun Sep 10 00:18:45 PDT 2017 * Text rendering functions now use the alpha component of the text colors Sam Lantinga - Sat Sep 9 22:21:55 PDT 2017 * Added support for characters greater than 0xFFFF (e.g. emoji) in the UTF-8 APIs 2.0.14: Ryan Gordon - Fri Jan 29 12:53:29 PST 2016 * Deprecated TTF_GetFontKerningSize() which takes font glyph indices and added TTF_GetFontKerningSizeGlyphs() which takes characters 2.0.13: Sylvain - Sat Jun 28 11:42:42 2014 * Fixed bug rendering text starting with a glyph with negative starting offset beuc - Sun Jun 15 18:27:28 2014 * Fixed regression loading non-scalable fonts Sam Lantinga - Sun Jun 15 18:21:04 PDT 2014 * TTF_GetFontKerningSize() gets kerning between two characters, not two glyph indices David Ludwig - Sun Apr 13 22:28:26 2014 * Added support for building for Windows RT and Windows Phone 2.0.12: Sam Lantinga - Sat Jun 1 19:11:26 PDT 2013 * Updated for SDL 2.0 release 2.0.11: Sam Lantinga - Sat Dec 31 10:49:42 EST 2011 * SDL_ttf is now under the zlib license Peter Kosyh - Mon Feb 28 14:57:03 PST 2011 * Improved font glyph caching for non-latin languages Erik Snoek - Wed Jan 12 09:10:15 PST 2011 * Added API to get kerning info: TTF_GetFontKerningSize() Sam Lantinga - Mon Jan 10 10:58:34 2011 -0800 * Added Android.mk to build on the Android platform 2.0.10: Adam Strzelecki - Wed Oct 21 21:02:37 PDT 2009 * Find the Unicode or symbol character map if it's available in the font SDL2_ttf-2.24.0/CMakeLists.txt0000664000000000000000000003611714735274341012672 0ustar00cmake_minimum_required(VERSION 3.16) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # See docs/release_checklist.md set(MAJOR_VERSION 2) set(MINOR_VERSION 24) set(MICRO_VERSION 0) set(SDL_REQUIRED_VERSION 2.0.10) include(PrivateSdlFunctions) sdl_calculate_derived_version_variables() if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the SDL_ttf source code and call cmake from there") endif() project(SDL2_ttf LANGUAGES C VERSION "${FULL_VERSION}" ) message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}") if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SDL2TTF_ROOTPROJECT ON) else() set(SDL2TTF_ROOTPROJECT OFF) endif() # Set defaults preventing destination file conflicts set(SDL2TTF_DEBUG_POSTFIX "d" CACHE STRING "Name suffix for debug builds") mark_as_advanced(SDL2TTF_DEBUG_POSTFIX) # Assume MSVC projects don't have a package manager and need vendored dependencies (by default). # Most other platforms have some kind of package manager. # FIXME: consider a package manager such as conan/vcpkg instead of vendoring if(ANDROID OR MSVC) set(vendored_default TRUE) else() set(vendored_default FALSE) endif() set(sdl2ttf_install_enableable ON) if ((TARGET SDL2 OR TARGET SDL2-static) AND SDL2_DISABLE_INSTALL) # Cannot install SDL2_mixer when SDL2 is built in same built, and is not installed. set(sdl2ttf_install_enableable OFF) endif() include(CMakeDependentOption) include(CMakePackageConfigHelpers) include(GNUInstallDirs) include(CheckSymbolExists) option(CMAKE_POSITION_INDEPENDENT_CODE "Build static libraries with -fPIC" ON) option(BUILD_SHARED_LIBS "Build the library as a shared library" ON) option(SDL2TTF_SAMPLES "Build the SDL2_ttf sample program(s)" ${SDL2TTF_ROOTPROJECT}) cmake_dependent_option(SDL2TTF_INSTALL "Enable SDL2_ttf install target" ${SDL2TTF_ROOTPROJECT} "${sdl2ttf_install_enableable}" OFF) option(SDL2TTF_VENDORED "Use vendored third-party libraries" ${vendored_default}) # For style consistency, create a SDL2TTF_FREETYPE CMake variable. This variable is NOT configurable. set(SDL2TTF_FREETYPE ON) set(SDL2TTF_FREETYPE_VENDORED "${SDL2TTF_VENDORED}") option(SDL2TTF_HARFBUZZ "Use harfbuzz to improve text shaping" OFF) set(SDL2TTF_HARFBUZZ_VENDORED "${SDL2TTF_VENDORED}") # Save BUILD_SHARED_LIBS variable set(SDL2TTF_BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS}") if(SDL2TTF_BUILD_SHARED_LIBS) set(sdl2_ttf_export_name SDL2_ttf) set(sdl2_ttf_install_name_infix shared) set(sdl2_target_name SDL2::SDL2) else() set(sdl2_ttf_export_name SDL2_ttf-static) set(sdl2_ttf_install_name_infix static) set(sdl2_target_name SDL2::SDL2-static) endif() sdl_find_sdl2(${sdl2_target_name} ${SDL_REQUIRED_VERSION}) # Enable large file support on 32-bit glibc, so that the vendored libraries # can access files with large inode numbers check_symbol_exists("__GLIBC__" "stdlib.h" LIBC_IS_GLIBC) if (LIBC_IS_GLIBC AND CMAKE_SIZEOF_VOID_P EQUAL 4) add_compile_definitions(_FILE_OFFSET_BITS=64) endif() add_library(SDL2_ttf SDL_ttf.c SDL_ttf.h ) add_library(SDL2_ttf::${sdl2_ttf_export_name} ALIAS SDL2_ttf) target_include_directories(SDL2_ttf PUBLIC "$" "$" ) target_compile_definitions(SDL2_ttf PRIVATE BUILD_SDL SDL_BUILD_MAJOR_VERSION=${MAJOR_VERSION} SDL_BUILD_MINOR_VERSION=${MINOR_VERSION} SDL_BUILD_MICRO_VERSION=${MICRO_VERSION} ) target_link_libraries(SDL2_ttf PRIVATE $) if(WIN32 AND SDL2TTF_BUILD_SHARED_LIBS) target_sources(SDL2_ttf PRIVATE version.rc ) endif() set_target_properties(SDL2_ttf PROPERTIES DEFINE_SYMBOL DLL_EXPORT EXPORT_NAME ${sdl2_ttf_export_name} C_VISIBILITY_PRESET "hidden" ) if(NOT ANDROID) set_target_properties(SDL2_ttf PROPERTIES DEBUG_POSTFIX "${SDL2TTF_DEBUG_POSTFIX}" SOVERSION "${LT_MAJOR}" VERSION "${LT_VERSION}" ) if(APPLE) cmake_minimum_required(VERSION 3.17) set_target_properties(SDL2_ttf PROPERTIES MACHO_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}" MACHO_CURRENT_VERSION "${MACHO_CURRENT_VERSION}" ) endif() endif() if(SDL2TTF_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID))) add_custom_command(TARGET SDL2_ttf POST_BUILD COMMAND "${CMAKE_COMMAND}" -E create_symlink "$" "libSDL2_ttf$<$:${SDL2TTF_DEBUG_POSTFIX}>$" # BYPRODUCTS "libSDL2_ttf$<$:${SDL2TTF_DEBUG_POSTFIX}>$" # Needs CMake 3.20 WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" ) endif() if(SDL2TTF_BUILD_SHARED_LIBS) if(WIN32 OR OS2) set_target_properties(SDL2_ttf PROPERTIES PREFIX "" ) endif() if(OS2) # OS/2 doesn't support a DLL name longer than 8 characters. set_target_properties(SDL2_ttf PROPERTIES OUTPUT_NAME "SDL2ttf" ) elseif(UNIX AND NOT ANDROID) set_target_properties(SDL2_ttf PROPERTIES OUTPUT_NAME "SDL2_ttf-${LT_RELEASE}" ) endif() endif() if(SDL2TTF_BUILD_SHARED_LIBS) # Use `Compatible Interface Properties` to ensure a shared SDL2_ttf is linked to a shared SDL2 library set_property(TARGET SDL2_ttf PROPERTY INTERFACE_SDL2_SHARED ${SDL2TTF_BUILD_SHARED_LIBS}) set_property(TARGET SDL2_ttf APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL SDL2_SHARED) endif() if(SDL2TTF_BUILD_SHARED_LIBS) sdl_target_link_options_no_undefined(SDL2_ttf) endif() if(SDL2TTF_BUILD_SHARED_LIBS) # Make sure static library dependencies are built with -fPIC when building a shared SDL2_ttf set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() set(INSTALL_EXTRA_TARGETS) set(PC_LIBS) set(PC_REQUIRES) # Build freetype and harfbuzz as a static library set(BUILD_SHARED_LIBS OFF) if(SDL2TTF_HARFBUZZ) if(SDL2TTF_HARFBUZZ_VENDORED) message(STATUS "${PROJECT_NAME}: Using vendored harfbuzz library") # HB_BUILD_UTILS variable is used by harfbuzz set(HB_BUILD_UTILS OFF CACHE BOOL "harfbuzz build utils" FORCE) # SKIP_INSTALL_LIBRARIES variable is used by harfbuzz set(SKIP_INSTALL_LIBRARIES ON CACHE BOOL "harfbuzz install option" FORCE) # HB_HAVE_FREETYPE variable is used by harfbuzz set(HB_HAVE_FREETYPE ${SDL2TTF_FREETYPE} CACHE BOOL "harfbuzz freetype helpers" FORCE) if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/harfbuzz/CMakeLists.txt") message(FATAL_ERROR "No harfbuzz sources found. Install a harfbuzz development package or run the download script in the external folder.") endif() add_subdirectory(external/harfbuzz EXCLUDE_FROM_ALL) # harfbuzz is a c++ project, enable c++ here to ensure linking to the c++ standard library enable_language(CXX) if(NOT SDL2TTF_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS harfbuzz) list(APPEND PC_LIBS -l$) endif() if(NOT TARGET harfbuzz::harfbuzz) add_library(harfbuzz::harfbuzz ALIAS harfbuzz) endif() else() message(STATUS "${PROJECT_NAME}: Using system harfbuzz library") find_package(harfbuzz REQUIRED) list(APPEND PC_REQUIRES harfbuzz) endif() target_compile_definitions(SDL2_ttf PRIVATE TTF_USE_HARFBUZZ=1) target_link_libraries(SDL2_ttf PRIVATE harfbuzz::harfbuzz) endif() if(SDL2TTF_FREETYPE) if(SDL2TTF_FREETYPE_VENDORED) message(STATUS "${PROJECT_NAME}: Using vendored freetype library") # FT_DISABLE_ZLIB variable is used by freetype set(FT_DISABLE_ZLIB ON CACHE BOOL "freetype zlib option") # FT_DISABLE_BZIP2 variable is used by freetype set(FT_DISABLE_BZIP2 ON CACHE BOOL "freetype bzip2 option") # FT_DISABLE_PNG variable is used by freetype set(FT_DISABLE_PNG ON CACHE BOOL "freetype png option") # FT_DISABLE_BROTLI variable is used by freetype set(FT_DISABLE_BROTLI ON CACHE BOOL "freetype option") if(SDL2TTF_HARFBUZZ) # FT_DISABLE_HARFBUZZ variable is used by freetype set(FT_DISABLE_HARFBUZZ OFF CACHE BOOL "freetype harfbuzz option" FORCE) # FT_REQUIRE_HARFBUZZ variable is used by freetype set(FT_REQUIRE_HARFBUZZ ON CACHE BOOL "freetype harfbuzz option" FORCE) else() # FT_DISABLE_HARFBUZZ variable is used by freetype set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "freetype harfbuzz option" FORCE) # FT_REQUIRE_HARFBUZZ variable is used by freetype set(FT_REQUIRE_HARFBUZZ OFF CACHE BOOL "freetype harfbuzz option" FORCE) endif() if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/freetype/CMakeLists.txt") message(FATAL_ERROR "No freetype sources found. Install a freetype development package or run the download script in the external folder.") endif() add_subdirectory(external/freetype EXCLUDE_FROM_ALL) if(NOT TARGET Freetype::Freetype) add_library(Freetype::Freetype ALIAS freetype) endif() if(NOT SDL2TTF_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS freetype) list(APPEND PC_LIBS -l$) endif() else() message(STATUS "${PROJECT_NAME}: Using system freetype library") find_package(Freetype REQUIRED) list(APPEND PC_REQUIRES freetype2) endif() target_link_libraries(SDL2_ttf PRIVATE Freetype::Freetype) endif() # Restore BUILD_SHARED_LIBS variable set(BUILD_SHARED_LIBS ${SDL2TTF_BUILD_SHARED_LIBS}) if (APPLE) # TODO: Use DYLIB_COMPATIBILITY_VERSION, DYLIB_CURRENT_VERSION here elseif (UNIX AND NOT APPLE AND NOT ANDROID) set_target_properties(SDL2_ttf PROPERTIES SOVERSION "${LT_MAJOR}" VERSION "${LT_VERSION}" ) endif() # Restore BUILD_SHARED_LIBS variable set(BUILD_SHARED_LIBS "${SDL2TTF_BUILD_SHARED_LIBS}") if(SDL2TTF_INSTALL) install( TARGETS SDL2_ttf EXPORT SDL2_ttfTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library ) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/SDL_ttf.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL2" COMPONENT devel ) if(INSTALL_EXTRA_TARGETS) install(TARGETS ${INSTALL_EXTRA_TARGETS} EXPORT SDL2_ttfTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" COMPONENT devel ) endif() if(WIN32 AND NOT MINGW) set(SDLTTF_INSTALL_CMAKEDIR_DEFAULT "cmake") else() set(SDLTTF_INSTALL_CMAKEDIR_DEFAULT "${CMAKE_INSTALL_LIBDIR}/cmake/SDL2_ttf") endif() set(SDLTTF_INSTALL_CMAKEDIR "${SDLTTF_INSTALL_CMAKEDIR_DEFAULT}" CACHE STRING "Location where to install SDL2_ttfConfig.cmake") configure_package_config_file(SDL2_ttfConfig.cmake.in SDL2_ttfConfig.cmake INSTALL_DESTINATION "${SDLTTF_INSTALL_CMAKEDIR}" ) write_basic_package_version_file("${PROJECT_BINARY_DIR}/SDL2_ttfConfigVersion.cmake" VERSION ${FULL_VERSION} COMPATIBILITY AnyNewerVersion ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttfConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttfConfigVersion.cmake" DESTINATION ${SDLTTF_INSTALL_CMAKEDIR} COMPONENT devel ) install(EXPORT SDL2_ttfTargets FILE SDL2_ttf-${sdl2_ttf_install_name_infix}-targets.cmake NAMESPACE SDL2_ttf:: DESTINATION "${SDLTTF_INSTALL_CMAKEDIR}" COMPONENT devel ) set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "\${prefix}") set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") set(PACKAGE "${PROJECT_NAME}") set(VERSION ${FULL_VERSION}) set(SDL_VERSION ${SDL_REQUIRED_VERSION}) string(JOIN " " PC_REQUIRES ${PC_REQUIRES}) string(JOIN " " PC_LIBS ${PC_LIBS}) configure_file("${PROJECT_SOURCE_DIR}/SDL2_ttf.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc.intermediate" @ONLY) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf-$.pc" INPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc.intermediate") set(PC_DESTDIR) if(CMAKE_SYSTEM_NAME MATCHES FreeBSD) # FreeBSD uses ${PREFIX}/libdata/pkgconfig set(PC_DESTDIR "libdata/pkgconfig") else() set(PC_DESTDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") endif() # Always install SDL2_ttf.pc file: libraries might be different between config modes install(CODE " # FIXME: use file(COPY_FILE) if CMake 3.21+ execute_process(COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf-$.pc\" \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc\") file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${PC_DESTDIR}\" TYPE FILE FILES \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_ttf.pc\")" COMPONENT devel) if(SDL2TTF_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID))) install(FILES "${PROJECT_BINARY_DIR}/libSDL2_ttf$<$:${SDL2TTF_DEBUG_POSTFIX}>$" DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel ) endif() install(FILES "LICENSE.txt" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/licenses/${PROJECT_NAME}" COMPONENT library ) endif() if(SDL2TTF_SAMPLES) add_executable(glfont glfont.c) add_executable(showfont showfont.c) find_package(SDL2main QUIET) set(OpenGL_GL_PREFERENCE GLVND) find_package(OpenGL) if(TARGET OpenGL::OpenGL) target_compile_definitions(glfont PRIVATE HAVE_OPENGL) target_link_libraries(glfont PRIVATE OpenGL::OpenGL) elseif(TARGET OpenGL::GL) target_compile_definitions(glfont PRIVATE HAVE_OPENGL) target_link_libraries(glfont PRIVATE OpenGL::GL) endif() foreach(prog glfont showfont) if(MINGW) target_link_libraries(${prog} PRIVATE mingw32) target_link_options(${prog} PRIVATE -mwindows) endif() target_link_libraries(${prog} PRIVATE SDL2_ttf::${sdl2_ttf_export_name}) if(TARGET SDL2::SDL2main) target_link_libraries(${prog} PRIVATE SDL2::SDL2main) endif() target_link_libraries(${prog} PRIVATE ${sdl2_target_name}) endforeach() endif() add_library(SDL2::ttf INTERFACE IMPORTED GLOBAL) set_target_properties(SDL2::ttf PROPERTIES INTERFACE_LINK_LIBRARIES "SDL2_ttf" ) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17") set_target_properties(SDL2::ttf PROPERTIES DEPRECATION "Use SDL2_ttf::SDL2_ttf or SDL2_ttf::SDL2_ttf-static instead" ) endif() SDL2_ttf-2.24.0/LICENSE.txt0000664000000000000000000000155614735261343011752 0ustar00Copyright (C) 1997-2025 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. SDL2_ttf-2.24.0/Makefile.am0000664000000000000000000001456114735530234012161 0ustar00# Makefile.am for the SDL truetype font loading library and viewer ACLOCAL_AMFLAGS = -I acinclude lib_LTLIBRARIES = libSDL2_ttf.la libSDL2_ttfincludedir = $(includedir)/SDL2 libSDL2_ttfinclude_HEADERS = \ SDL_ttf.h libSDL2_ttf_la_SOURCES = \ SDL_ttf.c $(LOCAL_FT2_SOURCES) $(LOCAL_HB_SOURCES) libSDL2_ttf_la_CPPFLAGS = $(TTF_CFLAGS) $(LOCAL_FT2_FLAGS) $(LOCAL_HB_FLAGS) FREETYPE_PATH = external/freetype FREETYPE_SOURCES = \ $(FREETYPE_PATH)/src/autofit/autofit.c \ $(FREETYPE_PATH)/src/base/ftbase.c \ $(FREETYPE_PATH)/src/base/ftbbox.c \ $(FREETYPE_PATH)/src/base/ftbdf.c \ $(FREETYPE_PATH)/src/base/ftbitmap.c \ $(FREETYPE_PATH)/src/base/ftcid.c \ $(FREETYPE_PATH)/src/base/ftdebug.c \ $(FREETYPE_PATH)/src/base/ftfstype.c \ $(FREETYPE_PATH)/src/base/ftgasp.c \ $(FREETYPE_PATH)/src/base/ftglyph.c \ $(FREETYPE_PATH)/src/base/ftgxval.c \ $(FREETYPE_PATH)/src/base/ftinit.c \ $(FREETYPE_PATH)/src/base/ftmm.c \ $(FREETYPE_PATH)/src/base/ftotval.c \ $(FREETYPE_PATH)/src/base/ftpatent.c \ $(FREETYPE_PATH)/src/base/ftpfr.c \ $(FREETYPE_PATH)/src/base/ftstroke.c \ $(FREETYPE_PATH)/src/base/ftsynth.c \ $(FREETYPE_PATH)/src/base/ftsystem.c \ $(FREETYPE_PATH)/src/base/fttype1.c \ $(FREETYPE_PATH)/src/base/ftwinfnt.c \ $(FREETYPE_PATH)/src/bdf/bdf.c \ $(FREETYPE_PATH)/src/bzip2/ftbzip2.c \ $(FREETYPE_PATH)/src/cache/ftcache.c \ $(FREETYPE_PATH)/src/cff/cff.c \ $(FREETYPE_PATH)/src/cid/type1cid.c \ $(FREETYPE_PATH)/src/gzip/ftgzip.c \ $(FREETYPE_PATH)/src/lzw/ftlzw.c \ $(FREETYPE_PATH)/src/pcf/pcf.c \ $(FREETYPE_PATH)/src/pfr/pfr.c \ $(FREETYPE_PATH)/src/psaux/psaux.c \ $(FREETYPE_PATH)/src/pshinter/pshinter.c \ $(FREETYPE_PATH)/src/psnames/psmodule.c \ $(FREETYPE_PATH)/src/raster/raster.c \ $(FREETYPE_PATH)/src/sdf/sdf.c \ $(FREETYPE_PATH)/src/sfnt/sfnt.c \ $(FREETYPE_PATH)/src/smooth/smooth.c \ $(FREETYPE_PATH)/src/svg/svg.c \ $(FREETYPE_PATH)/src/truetype/truetype.c \ $(FREETYPE_PATH)/src/type1/type1.c \ $(FREETYPE_PATH)/src/type42/type42.c \ $(FREETYPE_PATH)/src/winfonts/winfnt.c if USE_BUILTIN_FREETYPE LOCAL_FT2_FLAGS = -I$(srcdir)/$(FREETYPE_PATH)/include -DFT2_BUILD_LIBRARY -DFT_PUBLIC_FUNCTION_ATTRIBUTE= LOCAL_FT2_SOURCES = $(FREETYPE_SOURCES) endif HARFBUZZ_PATH = external/harfbuzz HARFBUZZ_SOURCES = \ $(HARFBUZZ_PATH)/src/hb-aat-layout.cc \ $(HARFBUZZ_PATH)/src/hb-aat-map.cc \ $(HARFBUZZ_PATH)/src/hb-blob.cc \ $(HARFBUZZ_PATH)/src/hb-buffer-serialize.cc \ $(HARFBUZZ_PATH)/src/hb-buffer-verify.cc \ $(HARFBUZZ_PATH)/src/hb-buffer.cc \ $(HARFBUZZ_PATH)/src/hb-common.cc \ $(HARFBUZZ_PATH)/src/hb-draw.cc \ $(HARFBUZZ_PATH)/src/hb-face.cc \ $(HARFBUZZ_PATH)/src/hb-fallback-shape.cc \ $(HARFBUZZ_PATH)/src/hb-font.cc \ $(HARFBUZZ_PATH)/src/hb-ft.cc \ $(HARFBUZZ_PATH)/src/hb-number.cc \ $(HARFBUZZ_PATH)/src/hb-ot-cff1-table.cc \ $(HARFBUZZ_PATH)/src/hb-ot-cff2-table.cc \ $(HARFBUZZ_PATH)/src/hb-ot-color.cc \ $(HARFBUZZ_PATH)/src/hb-ot-face.cc \ $(HARFBUZZ_PATH)/src/hb-ot-font.cc \ $(HARFBUZZ_PATH)/src/hb-ot-layout.cc \ $(HARFBUZZ_PATH)/src/hb-ot-map.cc \ $(HARFBUZZ_PATH)/src/hb-ot-math.cc \ $(HARFBUZZ_PATH)/src/hb-ot-metrics.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-arabic.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-default.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-hangul.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-hebrew.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-indic.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-indic-table.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-khmer.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-myanmar.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-syllabic.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-thai.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-use.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shaper-vowel-constraints.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shape.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shape-fallback.cc \ $(HARFBUZZ_PATH)/src/hb-ot-shape-normalize.cc \ $(HARFBUZZ_PATH)/src/hb-ot-tag.cc \ $(HARFBUZZ_PATH)/src/hb-ot-var.cc \ $(HARFBUZZ_PATH)/src/hb-outline.cc \ $(HARFBUZZ_PATH)/src/hb-paint.cc \ $(HARFBUZZ_PATH)/src/hb-paint-extents.cc \ $(HARFBUZZ_PATH)/src/hb-set.cc \ $(HARFBUZZ_PATH)/src/hb-shape-plan.cc \ $(HARFBUZZ_PATH)/src/hb-shape.cc \ $(HARFBUZZ_PATH)/src/hb-shaper.cc \ $(HARFBUZZ_PATH)/src/hb-static.cc \ $(HARFBUZZ_PATH)/src/hb-ucd.cc \ $(HARFBUZZ_PATH)/src/hb-coretext.cc \ $(HARFBUZZ_PATH)/src/hb-gdi.cc \ $(HARFBUZZ_PATH)/src/hb-uniscribe.cc \ $(HARFBUZZ_PATH)/src/hb-unicode.cc if USE_BUILTIN_HARFBUZZ LOCAL_HB_FLAGS = -I$(srcdir)/$(HARFBUZZ_PATH) -I$(srcdir)/$(HARFBUZZ_PATH)/src -DHAVE_CONFIG_H -DFT_CONFIG_OPTION_USE_HARFBUZZ LOCAL_HB_SOURCES = $(HARFBUZZ_SOURCES) if OS_WIN32 LINKER = $(LINK) #$(CXXLINK) else LINKER = $(LINK) endif else LINKER = $(LINK) endif libSDL2_ttf_la_LDFLAGS = \ -no-undefined \ -release $(LT_RELEASE) \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) $(LT_EXTRA) if USE_VERSION_RC libSDL2_ttf_la_DEPENDENCIES = version.o endif libSDL2_ttf_la_LINK = $(LINKER) $(libSDL2_ttf_la_LDFLAGS) libSDL2_ttf_la_LIBADD = $(TTF_LIBS) @MATHLIB@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = SDL2_ttf.pc .rc.o: $(RC) $< $@ EXTRA_DIST = \ .gitmodules \ Android.mk \ CHANGES.txt \ CMakeLists.txt \ LICENSE.txt \ Makefile.os2 \ README.txt \ SDL2_ttf.pc.in \ SDL2_ttf.spec.in \ SDL2_ttfConfig.cmake.in \ VisualC \ VisualC-WinRT \ Xcode \ autogen.sh \ cmake \ external \ mingw \ sdl2_ttf-config-version.cmake.in \ sdl2_ttf-config.cmake.in \ version.rc noinst_PROGRAMS = showfont glfont showfont_LDADD = libSDL2_ttf.la glfont_LDADD = libSDL2_ttf.la @GL_LIBS@ @MATHLIB@ # Rule to build tar-gzipped distribution package $(PACKAGE)-$(VERSION).tar.gz: distcheck # Rule to build RPM distribution package rpm: $(PACKAGE)-$(VERSION).tar.gz rpmbuild -ta $(PACKAGE)-$(VERSION).tar.gz distclean-local: -rm -rf `find $(srcdir) -name .deps` dist-hook: -rm -rf `find $(distdir) -name .deps` -rm -rf `find $(distdir)/external -name '.git*'` -rm -rf `find $(distdir)/external -name '.ci*'` -rm -rf `find $(distdir)/external -name .dirstamp` -rm -rf $(distdir)/external/harfbuzz/test install-data-local: $(MKDIR_P) $(DESTDIR)$(libdir)/cmake/SDL2_ttf $(INSTALL) -m 644 sdl2_ttf-config.cmake $(DESTDIR)$(libdir)/cmake/SDL2_ttf $(INSTALL) -m 644 sdl2_ttf-config-version.cmake $(DESTDIR)$(libdir)/cmake/SDL2_ttf uninstall-hook: rm $(DESTDIR)$(libdir)/cmake/SDL2_ttf/sdl2_ttf-config.cmake rm $(DESTDIR)$(libdir)/cmake/SDL2_ttf/sdl2_ttf-config-version.cmake rm -r $(DESTDIR)$(libdir)/cmake/SDL2_ttf SDL2_ttf-2.24.0/Makefile.os20000664000000000000000000000520014735300110012241 0ustar00# Open Watcom makefile to build SDL2ttf.dll for OS/2 # wmake -f Makefile.os2 # # Remember to edit DEPS_INC and DEPS_LIB below to meet # your own environment!. LIBNAME = SDL2ttf MAJOR_VERSION = 2 MINOR_VERSION = 24 MICRO_VERSION = 0 VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION) TITLENAME = $(LIBNAME) $(VERSION) LIBFILE = $(LIBNAME).lib DLLFILE = $(LIBNAME).dll LNKFILE = $(LIBNAME).lnk # change DEPS_INC in order to point to the dependency headers. DEPS_INC=-IC:\SDL2DEV\h\SDL2 -IC:\SDL2DEV\h # change DEPS_LIB in order to point to the dependency libraries. DEPS_LIB=C:\SDL2DEV\lib SRCS = SDL_ttf.c OBJS = $(SRCS:.c=.obj) # assuming freetype is built without FT_CONFIG_OPTION_SYSTEM_ZLIB # if not, add zlib.lib to LIBS LIBS = freetype.lib SDL2.lib CFLAGS_BASE = -bt=os2 -d0 -q -bm -5s -fp5 -fpi87 -sg -oeatxh -ei -j # warnings: CFLAGS_BASE+= -wx # include paths: CFLAGS_BASE+= -I$(%WATCOM)/h/os2 -I$(%WATCOM)/h CFLAGS_BASE+= -I. $(DEPS_INC) CFLAGS =$(CFLAGS_BASE) # to build a dll: CFLAGS+= -bd # for DECLSPEC: CFLAGS+= -DBUILD_SDL # ignore many 'W201: Unreachable code' warnings: CFLAGS+= -wcd=201 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # For the static assertions in SDL_ttf.c CFLAGS+= -DSDL_BUILD_MAJOR_VERSION=$(MAJOR_VERSION) CFLAGS+= -DSDL_BUILD_MINOR_VERSION=$(MINOR_VERSION) CFLAGS+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION) all: $(LIBFILE) showfont.exe $(LIBFILE): $(DLLFILE) @echo * Create library: $@... wlib -b -n -q -c -pa -s -t -zld -ii -io $@ $(DLLFILE) $(DLLFILE): $(OBJS) $(LNKFILE) @echo * Link: $@ wlink @$(LNKFILE) $(LNKFILE): @%create $@ @%append $@ SYSTEM os2v2_dll INITINSTANCE TERMINSTANCE @%append $@ NAME $(LIBNAME) @for %i in ($(OBJS)) do @%append $@ FILE %i @%append $@ OPTION QUIET @%append $@ OPTION DESCRIPTION '@$#libsdl org:$(VERSION)$#@Simple DirectMedia Layer truetype font library' @%append $@ LIBPATH $(DEPS_LIB) @for %i in ($(LIBS)) do @%append $@ LIB %i @%append $@ OPTION MAP=$* @%append $@ OPTION ELIMINATE @%append $@ OPTION MANYAUTODATA @%append $@ OPTION OSNAME='OS/2 and eComStation' @%append $@ OPTION SHOWDEAD .c.obj: wcc386 $(CFLAGS) -fo=$^@ $< showfont.obj: showfont.c wcc386 $(CFLAGS_BASE) -fo=$^@ $< showfont.exe: $(LIBFILE) showfont.obj wlink SYS os2v2 OP q LIBPATH $(DEPS_LIB) LIBR {$(LIBFILE) SDL2.lib} F {showfont.obj} N showfont.exe clean: .SYMBOLIC @echo * Clean: $(TITLENAME) @if exist *.obj rm *.obj @if exist *.err rm *.err @if exist $(LNKFILE) rm $(LNKFILE) distclean: .SYMBOLIC clean @if exist $(DLLFILE) rm $(DLLFILE) @if exist $(LIBFILE) rm $(LIBFILE) @if exist *.map rm *.map @if exist *.exe rm *.exe SDL2_ttf-2.24.0/README-versions.md0000664000000000000000000000503114235204164013235 0ustar00# Versioning ## Since 2.19.0 `SDL_ttf` follows an "odd/even" versioning policy, similar to GLib, GTK, Flatpak and older versions of the Linux kernel: * The major version (first part) increases when backwards compatibility is broken, which will happen infrequently. * If the minor version (second part) is divisible by 2 (for example 2.20.x, 2.22.x), this indicates a version that is believed to be stable and suitable for production use. * In stable releases, the patchlevel or micro version (third part) indicates bugfix releases. Bugfix releases should not add or remove ABI, so the ".0" release (for example 2.20.0) should be forwards-compatible with all the bugfix releases from the same cycle (for example 2.20.1). * The minor version increases when new API or ABI is added, or when other significant changes are made. Newer minor versions are backwards-compatible, but not fully forwards-compatible. For example, programs built against `SDL_ttf` 2.20.x should work fine with 2.20.x, but programs built against 2.22.x will not necessarily work with 2.20.x. * If the minor version (second part) is not divisible by 2 (for example 2.19.x, 2.21.x), this indicates a development prerelease that is not suitable for stable software distributions. Use with caution. * The patchlevel or micro version (third part) increases with each prerelease. * Each prerelease might add new API and/or ABI. * Prereleases are backwards-compatible with older stable branches. For example, 2.21.x will be backwards-compatible with 2.20.x. * Prereleases are not guaranteed to be backwards-compatible with each other. For example, new API or ABI added in 2.19.1 might be removed or changed in 2.19.2. If this would be a problem for you, please do not use prereleases. * Only upgrade to a prerelease if you can guarantee that you will promptly upgrade to the stable release that follows it. For example, do not upgrade to 2.19.x unless you will be able to upgrade to 2.20.0 when it becomes available. * Software distributions that have a freeze policy (in particular Linux distributions with a release cycle, such as Debian and Fedora) should usually only package stable releases, and not prereleases. ## Before 2.19.0 Older versions of `SDL_ttf` used the patchlevel (micro version, mixerthird part) for feature releases, and did not distinguish between feature and bugfix releases. SDL2_ttf-2.24.0/README.txt0000664000000000000000000000201014254101076011577 0ustar00 SDL_ttf 2.0 The latest version of this library is available from GitHub: https://github.com/libsdl-org/SDL_ttf/releases This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications. See the header file SDL_ttf.h and the example showfont.c for documentation on this library. This documentation is also available online at https://wiki.libsdl.org/SDL_ttf Be careful when including fonts with your application, as many of them are copyrighted. The Microsoft fonts, for example, are not freely redistributable and even the free "web" fonts they provide are only redistributable in their special executable installer form (May 1998). There are plenty of freeware and shareware fonts available on the Internet though, and may suit your purposes. This library is under the zlib license, see the file "LICENSE.txt" for details. Portions of this software are copyright © 2013 The FreeType Project (www.freetype.org). All rights reserved. SDL2_ttf-2.24.0/SDL2_ttf.pc.in0000664000000000000000000000052514167365416012441 0ustar00prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: SDL2_ttf Description: ttf library for Simple DirectMedia Layer with FreeType 2 support Version: @VERSION@ Requires: sdl2 >= @SDL_VERSION@ Libs: -L${libdir} -lSDL2_ttf Cflags: -I${includedir}/SDL2 Requires.private: @PC_REQUIRES@ Libs.private: @PC_LIBS@ SDL2_ttf-2.24.0/SDL2_ttf.spec.in0000664000000000000000000000261214243501572012756 0ustar00%define name @PACKAGE@ %define version @VERSION@ %define release 1 Summary: Simple DirectMedia Layer - Sample TrueType Font Library Name: %{name} Version: %{version} Release: %{release} Source0: %{name}-%{version}.tar.gz License: LGPL Group: System Environment/Libraries BuildRoot: /var/tmp/%{name}-buildroot Prefix: %{_prefix} Packager: Hakan Tandogan #BuildRequires: SDL2-devel #BuildRequires: freetype-devel %description This library allows you to use TrueType fonts to render text in SDL applications. %package devel Summary: Libraries, includes and more to develop SDL applications. Group: Development/Libraries Requires: %{name} Requires: SDL2-devel %description devel This library allows you to use TrueType fonts to render text in SDL applications. %prep rm -rf ${RPM_BUILD_ROOT} %setup %build CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} make %install rm -rf $RPM_BUILD_ROOT make install prefix=$RPM_BUILD_ROOT/%{prefix} %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %doc README.txt CHANGES.txt LICENSE.txt %{prefix}/lib/lib*.so.* %files devel %defattr(-,root,root) %{prefix}/lib/lib*.a %{prefix}/lib/lib*.la %{prefix}/lib/lib*.so %{prefix}/include/*/ %{prefix}/lib/pkgconfig/*.pc %changelog * Wed Jan 19 2000 Sam Lantinga - converted to get package information from configure * Sun Jan 16 2000 Hakan Tandogan - initial spec file SDL2_ttf-2.24.0/SDL2_ttfConfig.cmake.in0000664000000000000000000000313414252202562014227 0ustar00# sdl2_ttf cmake project-config input for CMakeLists.txt script include(FeatureSummary) set_package_properties(SDL2_ttf PROPERTIES URL "https://www.libsdl.org/projects/SDL_ttf/" DESCRIPTION "Support for TrueType (.ttf) font files with Simple Directmedia Layer" ) set(SDL2_ttf_FOUND ON) set(SDL2TTF_VENDORED @SDL2TTF_VENDORED@) set(SDL2TTF_HARFBUZZ @SDL2TTF_HARFBUZZ@) set(SDL2TTF_FREETYPE @SDL2TTF_FREETYPE@) set(SDL2TTF_SDL2_REQUIRED_VERSION @SDL_REQUIRED_VERSION@) include(CMakeFindDependencyMacro) if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL2_ttf-shared-targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/SDL2_ttf-shared-targets.cmake") endif() if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL2_ttf-static-targets.cmake") if(SDL2TTF_VENDORED) include(CheckLanguage) check_language(CXX) if(NOT CMAKE_CXX_COMPILER AND NOT _sdl2ttf_nowarning) message(WARNING "CXX language not enabled. Linking to SDL2_ttf::SDL2_ttf-static might fail.") endif() endif() include("${CMAKE_CURRENT_LIST_DIR}/SDL2_ttf-static-targets.cmake") endif() if(NOT SDL2TTF_VENDORED) set(_sdl_cmake_module_path "${CMAKE_MODULE_PATH}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") if(TARGET SDL2_ttf::SDL2_ttf-static) if(SDL2TTF_FREETYPE) find_dependency(Freetype) endif() if(SDL2TTF_HARFBUZZ) list(APPEND harfbuzz_ROOT "${CMAKE_CURRENT_LIST_DIR}") find_dependency(harfbuzz) endif() endif() set(CMAKE_MODULE_PATH "${_sdl_cmake_module_path}") unset(_sdl_cmake_module_path) endif() SDL2_ttf-2.24.0/SDL_ttf.c0000664000000000000000000045341114735261343011573 0ustar00/* SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts Copyright (C) 2001-2025 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 "SDL.h" #include "SDL_cpuinfo.h" #include "SDL_endian.h" #include "SDL_ttf.h" #include #include FT_FREETYPE_H #include FT_OUTLINE_H #include FT_STROKER_H #include FT_GLYPH_H #include FT_TRUETYPE_IDS_H #include FT_IMAGE_H /* Enable rendering with color * Freetype may need to be compiled with FT_CONFIG_OPTION_USE_PNG */ #if defined(FT_HAS_COLOR) # define TTF_USE_COLOR 1 #else # define TTF_USE_COLOR 0 #endif /* Enable Signed Distance Field rendering (requires latest FreeType version) */ #if defined(FT_RASTER_FLAG_SDF) # define TTF_USE_SDF 1 #else # define TTF_USE_SDF 0 #endif #if TTF_USE_SDF #include FT_MODULE_H #endif /* Enable HarfBuzz for Complex text rendering * Freetype may need to be compiled with FT_CONFIG_OPTION_USE_HARFBUZZ */ #ifndef TTF_USE_HARFBUZZ # define TTF_USE_HARFBUZZ 0 #endif #if defined(SDL_BUILD_MAJOR_VERSION) && defined(SDL_COMPILE_TIME_ASSERT) SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION, SDL_TTF_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION); SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION, SDL_TTF_MINOR_VERSION == SDL_BUILD_MINOR_VERSION); SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MICRO_VERSION, SDL_TTF_PATCHLEVEL == SDL_BUILD_MICRO_VERSION); #endif #if defined(SDL_COMPILE_TIME_ASSERT) SDL_COMPILE_TIME_ASSERT(SDL_TTF_MAJOR_VERSION_min, SDL_TTF_MAJOR_VERSION >= 0); /* Limited only by the need to fit in SDL_version */ SDL_COMPILE_TIME_ASSERT(SDL_TTF_MAJOR_VERSION_max, SDL_TTF_MAJOR_VERSION <= 255); SDL_COMPILE_TIME_ASSERT(SDL_TTF_MINOR_VERSION_min, SDL_TTF_MINOR_VERSION >= 0); /* Limited only by the need to fit in SDL_version */ SDL_COMPILE_TIME_ASSERT(SDL_TTF_MINOR_VERSION_max, SDL_TTF_MINOR_VERSION <= 255); SDL_COMPILE_TIME_ASSERT(SDL_TTF_PATCHLEVEL_min, SDL_TTF_PATCHLEVEL >= 0); /* Limited by its encoding in SDL_VERSIONNUM and in the ABI versions */ SDL_COMPILE_TIME_ASSERT(SDL_TTF_PATCHLEVEL_max, SDL_TTF_PATCHLEVEL <= 99); #endif #if TTF_USE_HARFBUZZ #include #include /* Default configuration */ static hb_direction_t g_hb_direction = HB_DIRECTION_LTR; static hb_script_t g_hb_script = HB_SCRIPT_UNKNOWN; #endif /* Harfbuzz */ int TTF_SetDirection(int direction) /* hb_direction_t */ { #if TTF_USE_HARFBUZZ g_hb_direction = direction; return 0; #else (void) direction; return -1; #endif } int TTF_SetScript(int script) /* hb_script_t */ { #if TTF_USE_HARFBUZZ g_hb_script = script; return 0; #else (void) script; return -1; #endif } /* Round glyph to 16 bytes width and use SSE2 instructions */ #if defined(__SSE2__) # define HAVE_SSE2_INTRINSICS 1 #endif /* Round glyph width to 16 bytes use NEON instructions */ #if 0 /*defined(__ARM_NEON)*/ # define HAVE_NEON_INTRINSICS 1 #endif /* Round glyph width to 8 bytes */ #define HAVE_BLIT_GLYPH_64 /* Android armeabi-v7a doesn't like int64 (Maybe all other __ARM_ARCH < 7 ?), * un-activate it, especially if NEON isn't detected */ #if defined(__ARM_ARCH) # if __ARM_ARCH < 8 # if defined(HAVE_BLIT_GLYPH_64) # undef HAVE_BLIT_GLYPH_64 # endif # endif #endif /* Default: round glyph width to 4 bytes to copy them faster */ #define HAVE_BLIT_GLYPH_32 /* Use Duff's device to unroll loops */ //#define USE_DUFFS_LOOP #if defined(HAVE_SSE2_INTRINSICS) static SDL_INLINE int hasSSE2(void) { static int val = -1; if (val != -1) { return val; } val = SDL_HasSSE2(); return val; } #endif #if defined(HAVE_NEON_INTRINSICS) static SDL_INLINE int hasNEON(void) { static int val = -1; if (val != -1) { return val; } val = SDL_HasNEON(); return val; } #endif /* 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 /* x offset = cos(((90.0-12)/360) * 2 * M_PI), or 12 degree angle */ /* same value as in FT_GlyphSlot_Oblique, fixed point 16.16 */ #define GLYPH_ITALICS 0x0366AL /* Handy routines for converting from fixed point 26.6 */ #define FT_FLOOR(X) (int)(((X) & -64) / 64) #define FT_CEIL(X) FT_FLOOR((X) + 63) /* Handy routine for converting to fixed point 26.6 */ #define F26Dot6(X) ((X) << 6) /* Faster divide by 255, with same result * in range [0; 255]: (x + 1 + (x >> 8)) >> 8 * in range [-255; 0]: (x + 255 + (x >> 8)) >> 8 */ #define DIVIDE_BY_255_SIGNED(x, sign_val) (((x) + (sign_val) + ((x)>>8)) >> 8) /* When x positive */ #define DIVIDE_BY_255(x) DIVIDE_BY_255_SIGNED(x, 1) #define CACHED_METRICS 0x20 #define CACHED_BITMAP 0x01 #define CACHED_PIXMAP 0x02 #define CACHED_COLOR 0x04 #define CACHED_LCD 0x08 #define CACHED_SUBPIX 0x10 typedef struct { unsigned char *buffer; /* aligned */ int left; int top; int width; int rows; int pitch; int is_color; } TTF_Image; /* Cached glyph information */ typedef struct cached_glyph { int stored; FT_UInt index; TTF_Image bitmap; TTF_Image pixmap; int sz_left; int sz_top; int sz_width; int sz_rows; int advance; union { /* TTF_HINTING_LIGHT_SUBPIXEL (only pixmap) */ struct { int lsb_minus_rsb; int translation; } subpixel; /* Other hinting */ struct { int rsb_delta; int lsb_delta; } kerning_smart; }; } c_glyph; /* Internal buffer to store positions computed by TTF_Size_Internal() * for rendered string by Render_Line() */ typedef struct PosBuf { FT_UInt index; int x; int y; } PosBuf_t; /* 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 style; int outline_val; /* Whether kerning is desired */ int allow_kerning; #if !TTF_USE_HARFBUZZ int use_kerning; #endif /* Extra width in glyph bounds for text styles */ int glyph_overhang; /* Information in the font for underlining */ int line_thickness; int underline_top_row; int strikethrough_top_row; /* Cache for style-transformed glyphs */ c_glyph cache[256]; FT_UInt cache_index[128]; /* We are responsible for closing the font stream */ SDL_RWops *src; int freesrc; FT_Open_Args args; /* Internal buffer to store positions computed by TTF_Size_Internal() * for rendered string by Render_Line() */ PosBuf_t *pos_buf; Uint32 pos_len; Uint32 pos_max; /* Hinting modes */ int ft_load_target; int render_subpixel; #if TTF_USE_HARFBUZZ hb_font_t *hb_font; /* If HB_SCRIPT_INVALID, use global default script */ hb_script_t hb_script; /* If HB_DIRECTION_INVALID, use global default direction */ hb_direction_t hb_direction; #endif int render_sdf; /* Extra layout setting for wrapped text */ int horizontal_align; }; /* Tell if SDL_ttf has to handle the style */ #define TTF_HANDLE_STYLE_BOLD(font) ((font)->style & TTF_STYLE_BOLD) #define TTF_HANDLE_STYLE_ITALIC(font) ((font)->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 = NULL; static int TTF_initialized = 0; static SDL_bool TTF_byteswapped = SDL_FALSE; #define TTF_CHECK_INITIALIZED(errval) \ if (!TTF_initialized) { \ TTF_SetError("Library not initialized"); \ return errval; \ } #define TTF_CHECK_POINTER(p, errval) \ if (!(p)) { \ TTF_SetError("Passed a NULL pointer"); \ return errval; \ } typedef enum { RENDER_SOLID = 0, RENDER_SHADED, RENDER_BLENDED, RENDER_LCD } render_mode_t; typedef enum { STR_UTF8 = 0, STR_TEXT, STR_UNICODE } str_type_t; static int TTF_initFontMetrics(TTF_Font *font); static int TTF_Size_Internal(TTF_Font *font, const char *text, str_type_t str_type, int *w, int *h, int *xstart, int *ystart, int measure_width, int *extent, int *count); #define NO_MEASUREMENT \ 0, NULL, NULL static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, str_type_t str_type, SDL_Color fg, SDL_Color bg, render_mode_t render_mode); static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, str_type_t str_type, SDL_Color fg, SDL_Color bg, Uint32 wrapLength, render_mode_t render_mode); static SDL_INLINE int Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want_bitmap, int want_pixmap, int want_color, int want_lcd, int want_subpixel, int translation, c_glyph **out_glyph, TTF_Image **out_image); static void Flush_Cache(TTF_Font *font); #if defined(USE_DUFFS_LOOP) /* 4-times unrolled loop */ #define DUFFS_LOOP4(pixel_copy_increment, width) \ { int n = (width+3)/4; \ switch (width & 3) { \ case 0: do { pixel_copy_increment; /* fallthrough */ \ case 3: pixel_copy_increment; /* fallthrough */ \ case 2: pixel_copy_increment; /* fallthrough */ \ case 1: pixel_copy_increment; /* fallthrough */ \ } while (--n > 0); \ } \ } #else /* Don't use Duff's device to unroll loops */ #define DUFFS_LOOP(pixel_copy_increment, width) \ { int n; \ for ( n=width; n > 0; --n ) { \ pixel_copy_increment; \ } \ } #define DUFFS_LOOP4(pixel_copy_increment, width) \ DUFFS_LOOP(pixel_copy_increment, width) #endif /* Blend colored glyphs */ static SDL_INLINE void BG_Blended_Color(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha) { const Uint32 *src = (Uint32 *)image->buffer; Uint32 *dst = destination; Uint32 width = image->width; Uint32 height = image->rows; if (fg_alpha == 0) { /* SDL_ALPHA_OPAQUE */ while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( *dst++ = *src++; , width); /* *INDENT-ON* */ src = (const Uint32 *)((const Uint8 *)src + srcskip); dst = (Uint32 *)((Uint8 *)dst + dstskip); } } else { Uint32 alpha; Uint32 tmp; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* prevent misaligned load: tmp = *src++; */ /* eventually, we can expect the compiler to replace the memcpy call with something optimized */ memcpy(&tmp, src++, sizeof(tmp)); /* This should NOT be SDL_memcpy */ alpha = tmp >> 24; tmp &= ~0xFF000000; alpha = fg_alpha * alpha; alpha = DIVIDE_BY_255(alpha) << 24; *dst++ = tmp | alpha , width); /* *INDENT-ON* */ src = (const Uint32 *)((const Uint8 *)src + srcskip); dst = (Uint32 *)((Uint8 *)dst + dstskip); } } } /* Blend with LCD rendering */ static SDL_INLINE void BG_Blended_LCD(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, SDL_Color *fg) { const Uint32 *src = (Uint32 *)image->buffer; Uint32 *dst = destination; Uint32 width = image->width; Uint32 height = image->rows; Uint32 tmp, bg; Uint32 r, g, b; Uint8 fg_r, fg_g, fg_b; Uint8 bg_r, bg_g, bg_b; Uint32 bg_a; fg_r = fg->r; fg_g = fg->g; fg_b = fg->b; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* prevent misaligned load: tmp = *src++; */ memcpy(&tmp, src++, sizeof(tmp)); /* This should NOT be SDL_memcpy */ if (tmp) { bg = *dst; bg_a = bg & 0xff000000; bg_r = (bg >> 16) & 0xff; bg_g = (bg >> 8) & 0xff; bg_b = (bg >> 0) & 0xff; r = (tmp >> 16) & 0xff; g = (tmp >> 8) & 0xff; b = (tmp >> 0) & 0xff; r = fg_r * r + bg_r * (255 - r) + 127; r = DIVIDE_BY_255(r); g = fg_g * g + bg_g * (255 - g) + 127; g = DIVIDE_BY_255(g); b = fg_b * b + bg_b * (255 - b) + 127; b = DIVIDE_BY_255(b); r <<= 16; g <<= 8; b <<= 0; *dst = r | g | b | bg_a; } dst++; , width); /* *INDENT-ON* */ src = (const Uint32 *)((const Uint8 *)src + srcskip); dst = (Uint32 *)((Uint8 *)dst + dstskip); } } #if TTF_USE_SDF /* Blended Opaque SDF */ static SDL_INLINE void BG_Blended_Opaque_SDF(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint8 *src = image->buffer; Uint32 *dst = destination; Uint32 width = image->width; Uint32 height = image->rows; Uint32 s; Uint32 d; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( d = *dst; s = ((Uint32)*src++) << 24; if (s > d) { *dst = s; } dst++; , width); /* *INDENT-ON* */ src += srcskip; dst = (Uint32 *)((Uint8 *)dst + dstskip); } } /* Blended non-opaque SDF */ static SDL_INLINE void BG_Blended_SDF(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha) { const Uint8 *src = image->buffer; Uint32 *dst = destination; Uint32 width = image->width; Uint32 height = image->rows; Uint32 s; Uint32 d; Uint32 tmp; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( d = *dst; tmp = fg_alpha * (*src++); s = DIVIDE_BY_255(tmp) << 24; if (s > d) { *dst = s; } dst++; , width); /* *INDENT-ON* */ src += srcskip; dst = (Uint32 *)((Uint8 *)dst + dstskip); } } #endif /* TTF_USE_SDF */ /* Blended Opaque */ static SDL_INLINE void BG_Blended_Opaque(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint8 *src = image->buffer; Uint32 *dst = destination; Uint32 width = image->width; Uint32 height = image->rows; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( *dst++ |= ((Uint32)*src++) << 24; , width); /* *INDENT-ON* */ src += srcskip; dst = (Uint32 *)((Uint8 *)dst + dstskip); } } /* Blended non-opaque */ static SDL_INLINE void BG_Blended(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha) { const Uint8 *src = image->buffer; Uint32 *dst = destination; Uint32 width = image->width; Uint32 height = image->rows; Uint32 tmp; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( tmp = fg_alpha * (*src++); *dst++ |= DIVIDE_BY_255(tmp) << 24; , width); /* *INDENT-ON* */ src += srcskip; dst = (Uint32 *)((Uint8 *)dst + dstskip); } } #if defined(HAVE_BLIT_GLYPH_32) || defined(HAVE_BLIT_GLYPH_64) static SDL_INLINE void BG_Blended_Opaque_32(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint8 *src = image->buffer; Uint32 *dst = destination; Uint32 width = image->width / 4; Uint32 height = image->rows; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( *dst++ |= ((Uint32)*src++) << 24; *dst++ |= ((Uint32)*src++) << 24; *dst++ |= ((Uint32)*src++) << 24; *dst++ |= ((Uint32)*src++) << 24; , width); /* *INDENT-ON* */ src += srcskip; dst = (Uint32 *)((Uint8 *)dst + dstskip); } } static SDL_INLINE void BG_Blended_32(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha) { const Uint8 *src = image->buffer; Uint32 *dst = destination; Uint32 width = image->width / 4; Uint32 height = image->rows; Uint32 tmp0, tmp1, tmp2, tmp3; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( tmp0 = fg_alpha * (*src++); tmp1 = fg_alpha * (*src++); tmp2 = fg_alpha * (*src++); tmp3 = fg_alpha * (*src++); *dst++ |= DIVIDE_BY_255(tmp0) << 24; *dst++ |= DIVIDE_BY_255(tmp1) << 24; *dst++ |= DIVIDE_BY_255(tmp2) << 24; *dst++ |= DIVIDE_BY_255(tmp3) << 24; , width); /* *INDENT-ON* */ src += srcskip; dst = (Uint32 *)((Uint8 *)dst + dstskip); } } #endif #if defined(HAVE_SSE2_INTRINSICS) /* Apply: alpha_table[i] = i << 24; */ static SDL_INLINE void BG_Blended_Opaque_SSE(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip) { const __m128i *src = (__m128i *)image->buffer; __m128i *dst = (__m128i *)destination; Uint32 width = image->width / 16; Uint32 height = image->rows; __m128i s, s0, s1, s2, s3, d0, d1, d2, d3, r0, r1, r2, r3, L, H; const __m128i zero = _mm_setzero_si128(); while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* Read 16 Uint8 at once and put into 4 __m128i */ s = _mm_loadu_si128(src); // load unaligned d0 = _mm_load_si128(dst); // load d1 = _mm_load_si128(dst + 1); // load d2 = _mm_load_si128(dst + 2); // load d3 = _mm_load_si128(dst + 3); // load L = _mm_unpacklo_epi8(zero, s); H = _mm_unpackhi_epi8(zero, s); s0 = _mm_unpacklo_epi8(zero, L); s1 = _mm_unpackhi_epi8(zero, L); s2 = _mm_unpacklo_epi8(zero, H); s3 = _mm_unpackhi_epi8(zero, H); // already shifted by 24 r0 = _mm_or_si128(d0, s0); // or r1 = _mm_or_si128(d1, s1); // or r2 = _mm_or_si128(d2, s2); // or r3 = _mm_or_si128(d3, s3); // or _mm_store_si128(dst, r0); // store _mm_store_si128(dst + 1, r1); // store _mm_store_si128(dst + 2, r2); // store _mm_store_si128(dst + 3, r3); // store dst += 4; src += 1; , width); /* *INDENT-ON* */ src = (const __m128i *)((const Uint8 *)src + srcskip); dst = (__m128i *)((Uint8 *)dst + dstskip); } } static SDL_INLINE void BG_Blended_SSE(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha) { const __m128i *src = (__m128i *)image->buffer; __m128i *dst = (__m128i *)destination; Uint32 width = image->width / 16; Uint32 height = image->rows; const __m128i alpha = _mm_set1_epi16(fg_alpha); const __m128i one = _mm_set1_epi16(1); const __m128i zero = _mm_setzero_si128(); __m128i s, s0, s1, s2, s3, d0, d1, d2, d3, r0, r1, r2, r3, L, H, Ls8, Hs8; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* Read 16 Uint8 at once and put into 4 __m128i */ s = _mm_loadu_si128(src); // load unaligned d0 = _mm_load_si128(dst); // load d1 = _mm_load_si128(dst + 1); // load d2 = _mm_load_si128(dst + 2); // load d3 = _mm_load_si128(dst + 3); // load L = _mm_unpacklo_epi8(s, zero); // interleave, no shifting H = _mm_unpackhi_epi8(s, zero); // enough room to multiply /* Apply: alpha_table[i] = ((i * fg.a / 255) << 24; */ /* Divide by 255 is done as: (x + 1 + (x >> 8)) >> 8 */ L = _mm_mullo_epi16(L, alpha); // x := i * fg.a H = _mm_mullo_epi16(H, alpha); Ls8 = _mm_srli_epi16(L, 8); // x >> 8 Hs8 = _mm_srli_epi16(H, 8); L = _mm_add_epi16(L, one); // x + 1 H = _mm_add_epi16(H, one); L = _mm_add_epi16(L, Ls8); // x + 1 + (x >> 8) H = _mm_add_epi16(H, Hs8); L = _mm_srli_epi16(L, 8); // ((x + 1 + (x >> 8)) >> 8 H = _mm_srli_epi16(H, 8); L = _mm_slli_epi16(L, 8); // shift << 8, so we're prepared H = _mm_slli_epi16(H, 8); // to have final format << 24 s0 = _mm_unpacklo_epi8(zero, L); s1 = _mm_unpackhi_epi8(zero, L); s2 = _mm_unpacklo_epi8(zero, H); s3 = _mm_unpackhi_epi8(zero, H); // already shifted by 24 r0 = _mm_or_si128(d0, s0); // or r1 = _mm_or_si128(d1, s1); // or r2 = _mm_or_si128(d2, s2); // or r3 = _mm_or_si128(d3, s3); // or _mm_store_si128(dst, r0); // store _mm_store_si128(dst + 1, r1); // store _mm_store_si128(dst + 2, r2); // store _mm_store_si128(dst + 3, r3); // store dst += 4; src += 1; , width); /* *INDENT-ON* */ src = (const __m128i *)((const Uint8 *)src + srcskip); dst = (__m128i *)((Uint8 *)dst + dstskip); } } #endif #if defined(HAVE_NEON_INTRINSICS) /* Apply: alpha_table[i] = i << 24; */ static SDL_INLINE void BG_Blended_Opaque_NEON(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint32 *src = (Uint32 *)image->buffer; Uint32 *dst = destination; Uint32 width = image->width / 16; Uint32 height = image->rows; uint32x4_t s, d0, d1, d2, d3, r0, r1, r2, r3; uint8x16x2_t sx, sx01, sx23; const uint8x16_t zero = vdupq_n_u8(0); while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* Read 4 Uint32 and put 16 Uint8 into uint32x4x2_t (uint8x16x2_t) * takes advantage of vzipq_u8 which produces two lanes */ s = vld1q_u32(src); // load d0 = vld1q_u32(dst); // load d1 = vld1q_u32(dst + 4); // load d2 = vld1q_u32(dst + 8); // load d3 = vld1q_u32(dst + 12); // load sx = vzipq_u8(zero, (uint8x16_t)s); // interleave sx01 = vzipq_u8(zero, sx.val[0]); // interleave sx23 = vzipq_u8(zero, sx.val[1]); // interleave // already shifted by 24 r0 = vorrq_u32(d0, (uint32x4_t)sx01.val[0]); // or r1 = vorrq_u32(d1, (uint32x4_t)sx01.val[1]); // or r2 = vorrq_u32(d2, (uint32x4_t)sx23.val[0]); // or r3 = vorrq_u32(d3, (uint32x4_t)sx23.val[1]); // or vst1q_u32(dst, r0); // store vst1q_u32(dst + 4, r1); // store vst1q_u32(dst + 8, r2); // store vst1q_u32(dst + 12, r3); // store dst += 16; src += 4; , width); /* *INDENT-ON* */ src = (const Uint32 *)((const Uint8 *)src + srcskip); dst = (Uint32 *)((Uint8 *)dst + dstskip); } } /* Non-opaque, computes alpha blending on the fly */ static SDL_INLINE void BG_Blended_NEON(const TTF_Image *image, Uint32 *destination, Sint32 srcskip, Uint32 dstskip, Uint8 fg_alpha) { const Uint32 *src = (Uint32 *)image->buffer; Uint32 *dst = destination; Uint32 width = image->width / 16; Uint32 height = image->rows; uint32x4_t s, d0, d1, d2, d3, r0, r1, r2, r3; uint16x8_t Ls8, Hs8; uint8x16x2_t sx, sx01, sx23; uint16x8x2_t sxm; const uint16x8_t alpha = vmovq_n_u16(fg_alpha); const uint16x8_t one = vmovq_n_u16(1); const uint8x16_t zero = vdupq_n_u8(0); while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* Read 4 Uint32 and put 16 Uint8 into uint32x4x2_t (uint8x16x2_t) * takes advantage of vzipq_u8 which produces two lanes */ s = vld1q_u32(src); // load d0 = vld1q_u32(dst); // load d1 = vld1q_u32(dst + 4); // load d2 = vld1q_u32(dst + 8); // load d3 = vld1q_u32(dst + 12); // load sx = vzipq_u8((uint8x16_t)s, zero); // interleave, no shifting // enough room to multiply /* Apply: alpha_table[i] = ((i * fg.a / 255) << 24; */ /* Divide by 255 is done as: (x + 1 + (x >> 8)) >> 8 */ sxm.val[0] = vmulq_u16((uint16x8_t)sx.val[0], alpha); // x := i * fg.a sxm.val[1] = vmulq_u16((uint16x8_t)sx.val[1], alpha); Ls8 = vshrq_n_u16(sxm.val[0], 8); // x >> 8 Hs8 = vshrq_n_u16(sxm.val[1], 8); sxm.val[0] = vaddq_u16(sxm.val[0], one); // x + 1 sxm.val[1] = vaddq_u16(sxm.val[1], one); sxm.val[0] = vaddq_u16(sxm.val[0], Ls8); // x + 1 + (x >> 8) sxm.val[1] = vaddq_u16(sxm.val[1], Hs8); sxm.val[0] = vshrq_n_u16(sxm.val[0], 8); // ((x + 1 + (x >> 8)) >> 8 sxm.val[1] = vshrq_n_u16(sxm.val[1], 8); sxm.val[0] = vshlq_n_u16(sxm.val[0], 8); // shift << 8, so we're prepared sxm.val[1] = vshlq_n_u16(sxm.val[1], 8); // to have final format << 24 sx01 = vzipq_u8(zero, (uint8x16_t)sxm.val[0]); // interleave sx23 = vzipq_u8(zero, (uint8x16_t)sxm.val[1]); // interleave // already shifted by 24 r0 = vorrq_u32(d0, (uint32x4_t)sx01.val[0]); // or r1 = vorrq_u32(d1, (uint32x4_t)sx01.val[1]); // or r2 = vorrq_u32(d2, (uint32x4_t)sx23.val[0]); // or r3 = vorrq_u32(d3, (uint32x4_t)sx23.val[1]); // or vst1q_u32(dst, r0); // store vst1q_u32(dst + 4, r1); // store vst1q_u32(dst + 8, r2); // store vst1q_u32(dst + 12, r3); // store dst += 16; src += 4; , width); /* *INDENT-ON* */ src = (const Uint32 *)((const Uint8 *)src + srcskip); dst = (Uint32 *)((Uint8 *)dst + dstskip); } } #endif static SDL_INLINE void BG(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint8 *src = image->buffer; Uint8 *dst = destination; Uint32 width = image->width; Uint32 height = image->rows; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( *dst++ |= *src++; , width); /* *INDENT-ON* */ src += srcskip; dst += dstskip; } } #if defined(HAVE_BLIT_GLYPH_64) static SDL_INLINE void BG_64(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint64 *src = (Uint64 *)image->buffer; Uint64 *dst = (Uint64 *)destination; Uint32 width = image->width / 8; Uint32 height = image->rows; Uint64 tmp; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* prevent misaligned load: *dst++ |= *src++; */ memcpy(&tmp, src++, sizeof(tmp)); /* This should NOT be SDL_memcpy */ *dst++ |= tmp; , width); /* *INDENT-ON* */ src = (const Uint64 *)((const Uint8 *)src + srcskip); dst = (Uint64 *)((Uint8 *)dst + dstskip); } } #elif defined(HAVE_BLIT_GLYPH_32) static SDL_INLINE void BG_32(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint32 *src = (Uint32 *)image->buffer; Uint32 *dst = (Uint32 *)destination; Uint32 width = image->width / 4; Uint32 height = image->rows; Uint32 tmp; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( /* prevent misaligned load: *dst++ |= *src++; */ memcpy(&tmp, src++, sizeof(tmp)); /* This should NOT be SDL_memcpy */ *dst++ |= tmp; , width); /* *INDENT-ON* */ src = (const Uint32 *)((const Uint8 *)src + srcskip); dst = (Uint32 *)((Uint8 *)dst + dstskip); } } #endif #if defined(HAVE_SSE2_INTRINSICS) static SDL_INLINE void BG_SSE(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip) { const __m128i *src = (__m128i *)image->buffer; __m128i *dst = (__m128i *)destination; Uint32 width = image->width / 16; Uint32 height = image->rows; __m128i s, d, r; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( s = _mm_loadu_si128(src); // load unaligned d = _mm_load_si128(dst); // load r = _mm_or_si128(d, s); // or _mm_store_si128(dst, r); // store src += 1; dst += 1; , width); /* *INDENT-ON* */ src = (const __m128i *)((const Uint8 *)src + srcskip); dst = (__m128i *)((Uint8 *)dst + dstskip); } } #endif #if defined(HAVE_NEON_INTRINSICS) static SDL_INLINE void BG_NEON(const TTF_Image *image, Uint8 *destination, Sint32 srcskip, Uint32 dstskip) { const Uint8 *src = image->buffer; Uint8 *dst = destination; Uint32 width = image->width / 16; Uint32 height = image->rows; uint8x16_t s, d, r; while (height--) { /* *INDENT-OFF* */ DUFFS_LOOP4( s = vld1q_u8(src); // load d = vld1q_u8(dst); // load r = vorrq_u8(d, s); // or vst1q_u8(dst, r); // store src += 16; dst += 16; , width); /* *INDENT-ON* */ src = (const Uint8 *)((const Uint8 *)src + srcskip); dst += dstskip; } } #endif /* Underline and Strikethrough style. Draw a line at the given row. */ static void Draw_Line(TTF_Font *font, const SDL_Surface *textbuf, int column, int row, int line_width, int line_thickness, Uint32 color, const render_mode_t render_mode) { int tmp = row + line_thickness - textbuf->h; int x_offset = column * textbuf->format->BytesPerPixel; Uint8 *dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch + x_offset; #if TTF_USE_HARFBUZZ hb_direction_t hb_direction = font->hb_direction; if (hb_direction == HB_DIRECTION_INVALID) { hb_direction = g_hb_direction; } /* No Underline/Strikethrough style if direction is vertical */ if (hb_direction == HB_DIRECTION_TTB || hb_direction == HB_DIRECTION_BTT) { return; } #else (void) font; #endif /* Not needed because of "font->height = SDL_max(font->height, bottom_row);". * But if you patch to render textshaping and break line in middle of a cluster, * (which is a bad usage and a corner case), you need this to prevent out of bounds. * You can get an "ystart" for the "whole line", which is different (and smaller) * than the ones of the "splitted lines". */ if (tmp > 0) { line_thickness -= tmp; } /* Previous case also happens with SDF (render_sdf) , because 'spread' property * requires to increase 'ystart' * Check for valid value anyway. */ if (line_thickness <= 0) { return; } /* Wrapped mode with an unbroken line: 'line_width' is greater that 'textbuf->w' */ line_width = SDL_min(line_width, textbuf->w); if (render_mode == RENDER_BLENDED || render_mode == RENDER_LCD) { while (line_thickness--) { SDL_memset4(dst, color, line_width); dst += textbuf->pitch; } } else { while (line_thickness--) { SDL_memset(dst, color, line_width); dst += textbuf->pitch; } } } static void clip_glyph(int *_x, int *_y, TTF_Image *image, const SDL_Surface *textbuf, int is_lcd) { int above_w; int above_h; int x = *_x; int y = *_y; int srcbpp = 1; if (image->is_color || is_lcd) { srcbpp = 4; } /* Don't go below x=0 */ if (x < 0) { int tmp = -x; x = 0; image->width -= tmp; image->buffer += srcbpp * tmp; } /* Don't go above textbuf->w */ above_w = x + image->width - textbuf->w; if (above_w > 0) { image->width -= above_w; } /* Don't go below y=0 */ if (y < 0) { int tmp = -y; y = 0; image->rows -= tmp; image->buffer += tmp * image->pitch; } /* Don't go above textbuf->h */ above_h = y + image->rows - textbuf->h; if (above_h > 0) { image->rows -= above_h; } /* Could be negative if (x > textbuf->w), or if (x + width < 0) */ image->width = SDL_max(0, image->width); image->rows = SDL_max(0, image->rows); /* After 'image->width' clipping: * Make sure 'rows' is also 0, so it doesn't break USE_DUFFS_LOOP */ if (image->width == 0) { image->rows = 0; } *_x = x; *_y = y; } /* Glyph width is rounded, dst addresses are aligned, src addresses are not aligned */ static int Get_Alignment(void) { #if defined(HAVE_NEON_INTRINSICS) if (hasNEON()) { return 16; } #endif #if defined(HAVE_SSE2_INTRINSICS) if (hasSSE2()) { return 16; } #endif #if defined(HAVE_BLIT_GLYPH_64) return 8; #elif defined(HAVE_BLIT_GLYPH_32) return 4; #else return 1; #endif } #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-value" #endif #define BUILD_RENDER_LINE(NAME, IS_BLENDED, IS_BLENDED_OPAQUE, IS_LCD, WB_WP_WC, WS, BLIT_GLYPH_BLENDED_OPAQUE_OPTIM, BLIT_GLYPH_BLENDED_OPTIM, BLIT_GLYPH_OPTIM) \ \ static SDL_INLINE \ int Render_Line_##NAME(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) \ { \ const int alignment = Get_Alignment() - 1; \ const int bpp = ((IS_BLENDED || IS_LCD) ? 4 : 1); \ unsigned int i; \ Uint8 fg_alpha = (fg ? fg->a : 0); \ for (i = 0; i < font->pos_len; i++) { \ FT_UInt idx = font->pos_buf[i].index; \ int x = font->pos_buf[i].x; \ int y = font->pos_buf[i].y; \ TTF_Image *image; \ \ if (Find_GlyphByIndex(font, idx, WB_WP_WC, WS, x & 63, NULL, &image) == 0) { \ int above_w, above_h; \ Uint32 dstskip; \ Sint32 srcskip; /* Can be negative */ \ Uint8 *dst; \ int remainder; \ Uint8 *saved_buffer = image->buffer; \ int saved_width = image->width; \ image->buffer += alignment; \ /* Position updated after glyph rendering */ \ x = xstart + FT_FLOOR(x) + image->left; \ y = ystart + FT_FLOOR(y) - image->top; \ \ /* Make sure glyph is inside textbuf */ \ above_w = x + image->width - textbuf->w; \ above_h = y + image->rows - textbuf->h; \ \ if (x >= 0 && y >= 0 && above_w <= 0 && above_h <= 0) { \ /* Most often, glyph is inside textbuf */ \ /* Compute dst */ \ dst = (Uint8 *)textbuf->pixels + y * textbuf->pitch + x * bpp; \ /* Align dst, get remainder, shift & align glyph width */ \ remainder = ((uintptr_t)dst & alignment) / bpp; \ dst = (Uint8 *)((uintptr_t)dst & ~alignment); \ image->buffer -= remainder; \ image->width = (image->width + remainder + alignment) & ~alignment; \ /* Compute srcskip, dstskip */ \ srcskip = image->pitch - image->width; \ dstskip = textbuf->pitch - image->width * bpp; \ /* Render glyph at (x, y) with optimized copy functions */ \ if (IS_LCD) { \ image->buffer = saved_buffer; \ image->buffer += alignment; \ image->width = saved_width; \ dst = (Uint8 *)textbuf->pixels + y * textbuf->pitch + x * bpp; \ /* Compute srcskip, dstskip */ \ srcskip = image->pitch - 4 * image->width; \ dstskip = textbuf->pitch - image->width * bpp; \ BG_Blended_LCD(image, (Uint32 *)dst, srcskip, dstskip, fg); \ } else if (!IS_BLENDED || image->is_color == 0) { \ if (IS_BLENDED_OPAQUE) { \ BLIT_GLYPH_BLENDED_OPAQUE_OPTIM(image, (Uint32 *)dst, srcskip, dstskip); \ } else if (IS_BLENDED) { \ BLIT_GLYPH_BLENDED_OPTIM(image, (Uint32 *)dst, srcskip, dstskip, fg_alpha); \ } else { \ BLIT_GLYPH_OPTIM(image, dst, srcskip, dstskip); \ } \ } else if (IS_BLENDED && image->is_color) { \ image->buffer = saved_buffer; \ image->buffer += alignment; \ image->width = saved_width; \ dst = (Uint8 *)textbuf->pixels + y * textbuf->pitch + x * bpp; \ /* Compute srcskip, dstskip */ \ srcskip = image->pitch - 4 * image->width; \ dstskip = textbuf->pitch - image->width * bpp; \ BG_Blended_Color(image, (Uint32 *)dst, srcskip, dstskip, fg_alpha); \ } \ /* restore modification */ \ image->width = saved_width; \ } else { \ /* Modify a copy, and clip it */ \ TTF_Image image_clipped = *image; \ /* Intersect image glyph at (x,y) with textbuf */ \ clip_glyph(&x, &y, &image_clipped, textbuf, IS_LCD); \ /* Compute dst */ \ dst = (Uint8 *)textbuf->pixels + y * textbuf->pitch + x * bpp; \ /* Compute srcskip, dstskip */ \ srcskip = image_clipped.pitch - image_clipped.width; \ dstskip = textbuf->pitch - image_clipped.width * bpp; \ /* Render glyph at (x, y) */ \ if (IS_LCD) { \ srcskip -= 3 * image_clipped.width; \ BG_Blended_LCD(&image_clipped, (Uint32 *)dst, srcskip, dstskip, fg); \ } else if (!IS_BLENDED || image->is_color == 0) { \ if (IS_BLENDED_OPAQUE) { \ BG_Blended_Opaque(&image_clipped, (Uint32 *)dst, srcskip, dstskip); \ } else if (IS_BLENDED) { \ BG_Blended(&image_clipped, (Uint32 *)dst, srcskip, dstskip, fg_alpha); \ } else { \ BG(&image_clipped, dst, srcskip, dstskip); \ } \ } else if (IS_BLENDED && image->is_color) { \ srcskip -= 3 * image_clipped.width; \ BG_Blended_Color(&image_clipped, (Uint32 *)dst, srcskip, dstskip, fg_alpha); \ } \ } \ image->buffer = saved_buffer; \ } else { \ return -1; \ } \ } \ \ return 0; \ } \ \ #define BITMAP CACHED_BITMAP, 0, 0, 0 #define PIXMAP 0, CACHED_PIXMAP, 0, 0 #define COLOR 0, 0, CACHED_COLOR, 0 #define LCD 0, 0, 0, CACHED_LCD #define SUBPIX CACHED_SUBPIX /* BUILD_RENDER_LINE(NAME, IS_BLENDED, IS_BLENDED_OPAQUE, WANT_BITMAP_PIXMAP_COLOR_LCD, WANT_SUBPIXEL, BLIT_GLYPH_BLENDED_OPAQUE_OPTIM, BLIT_GLYPH_BLENDED_OPTIM, BLIT_GLYPH_OPTIM) */ #if defined(HAVE_SSE2_INTRINSICS) BUILD_RENDER_LINE(SSE_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_SSE ) BUILD_RENDER_LINE(SSE_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_SSE , ) BUILD_RENDER_LINE(SSE_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_SSE , , ) BUILD_RENDER_LINE(SSE_Solid , 0, 0, 0, BITMAP, 0 , , , BG_SSE ) BUILD_RENDER_LINE(SSE_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_SSE ) BUILD_RENDER_LINE(SSE_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_SSE , ) BUILD_RENDER_LINE(SSE_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_SSE , , ) BUILD_RENDER_LINE(SSE_LCD , 0, 0, 1, LCD, 0, , , ) BUILD_RENDER_LINE(SSE_LCD_SP , 0, 0, 1, LCD, SUBPIX, , , ) #endif #if defined(HAVE_NEON_INTRINSICS) BUILD_RENDER_LINE(NEON_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_NEON ) BUILD_RENDER_LINE(NEON_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_NEON, ) BUILD_RENDER_LINE(NEON_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_NEON, , ) BUILD_RENDER_LINE(NEON_Solid , 0, 0, 0, BITMAP, 0 , , , BG_NEON ) BUILD_RENDER_LINE(NEON_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_NEON ) BUILD_RENDER_LINE(NEON_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_NEON, ) BUILD_RENDER_LINE(NEON_Blended_Opaque_SP, 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_NEON, , ) BUILD_RENDER_LINE(NEON_LCD , 0, 0, 1, LCD, 0 , , , ) BUILD_RENDER_LINE(NEON_LCD_SP , 0, 0, 1, LCD, SUBPIX, , , ) #endif #if defined(HAVE_BLIT_GLYPH_64) BUILD_RENDER_LINE(64_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_64 ) BUILD_RENDER_LINE(64_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_32 , ) BUILD_RENDER_LINE(64_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_32 , , ) BUILD_RENDER_LINE(64_Solid , 0, 0, 0, BITMAP, 0 , , , BG_64 ) BUILD_RENDER_LINE(64_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_64 ) BUILD_RENDER_LINE(64_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_32 , ) BUILD_RENDER_LINE(64_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_32 , , ) BUILD_RENDER_LINE(64_LCD , 0, 0, 1, LCD, 0 , , , ) BUILD_RENDER_LINE(64_LCD_SP , 0, 0, 1, LCD, SUBPIX, , , ) #elif defined(HAVE_BLIT_GLYPH_32) BUILD_RENDER_LINE(32_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG_32 ) BUILD_RENDER_LINE(32_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_32 , ) BUILD_RENDER_LINE(32_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_32 , , ) BUILD_RENDER_LINE(32_Solid , 0, 0, 0, BITMAP, 0 , , , BG_32 ) BUILD_RENDER_LINE(32_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG_32 ) BUILD_RENDER_LINE(32_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_32 , ) BUILD_RENDER_LINE(32_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_32 , , ) BUILD_RENDER_LINE(32_LCD , 0, 0, 1, LCD, 0 , , , ) BUILD_RENDER_LINE(32_LCD_SP , 0, 0, 1, LCD, SUBPIX, , , ) #else BUILD_RENDER_LINE(8_Shaded , 0, 0, 0, PIXMAP, 0 , , , BG ) BUILD_RENDER_LINE(8_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended , ) BUILD_RENDER_LINE(8_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque , , ) BUILD_RENDER_LINE(8_Solid , 0, 0, 0, BITMAP, 0 , , , BG ) BUILD_RENDER_LINE(8_Shaded_SP , 0, 0, 0, PIXMAP, SUBPIX, , , BG ) BUILD_RENDER_LINE(8_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended , ) BUILD_RENDER_LINE(8_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque , , ) BUILD_RENDER_LINE(8_LCD , 0, 0, 1, LCD, 0 , , , ) BUILD_RENDER_LINE(8_LCD_SP , 0, 0, 1, LCD, SUBPIX, , , ) #endif #if TTF_USE_SDF static int (*Render_Line_SDF_Shaded)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; BUILD_RENDER_LINE(SDF_Blended , 1, 0, 0, COLOR, 0 , , BG_Blended_SDF , ) BUILD_RENDER_LINE(SDF_Blended_Opaque , 1, 1, 0, COLOR, 0 , BG_Blended_Opaque_SDF , , ) static int (*Render_Line_SDF_Solid)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; static int (*Render_Line_SDF_Shaded_SP)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; BUILD_RENDER_LINE(SDF_Blended_SP , 1, 0, 0, COLOR, SUBPIX, , BG_Blended_SDF , ) BUILD_RENDER_LINE(SDF_Blended_Opaque_SP , 1, 1, 0, COLOR, SUBPIX, BG_Blended_Opaque_SDF , , ) static int (*Render_Line_SDF_LCD)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; static int (*Render_Line_SDF_LCD_SP)(TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color *fg) = NULL; #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif static SDL_INLINE int Render_Line(const render_mode_t render_mode, int subpixel, TTF_Font *font, SDL_Surface *textbuf, int xstart, int ystart, SDL_Color fg) { /* Render line (pos_buf) to textbuf at (xstart, ystart) */ /* Subpixel with RENDER_SOLID doesn't make sense. */ /* (and 'cached->subpixel.translation' would need to distinguish bitmap/pixmap). */ int is_opaque = (fg.a == SDL_ALPHA_OPAQUE); #define Call_Specific_Render_Line(NAME) \ if (render_mode == RENDER_SHADED) { \ if (subpixel == 0) { \ return Render_Line_##NAME##_Shaded(font, textbuf, xstart, ystart, NULL); \ } else { \ return Render_Line_##NAME##_Shaded_SP(font, textbuf, xstart, ystart, NULL); \ } \ } else if (render_mode == RENDER_BLENDED) { \ if (is_opaque) { \ if (subpixel == 0) { \ return Render_Line_##NAME##_Blended_Opaque(font, textbuf, xstart, ystart, NULL); \ } else { \ return Render_Line_##NAME##_Blended_Opaque_SP(font, textbuf, xstart, ystart, NULL); \ } \ } else { \ if (subpixel == 0) { \ return Render_Line_##NAME##_Blended(font, textbuf, xstart, ystart, &fg); \ } else { \ return Render_Line_##NAME##_Blended_SP(font, textbuf, xstart, ystart, &fg); \ } \ } \ } else if (render_mode == RENDER_LCD) { \ if (subpixel == 0) { \ return Render_Line_##NAME##_LCD(font, textbuf, xstart, ystart, &fg); \ } else { \ return Render_Line_##NAME##_LCD_SP(font, textbuf, xstart, ystart, &fg); \ } \ } else { \ return Render_Line_##NAME##_Solid(font, textbuf, xstart, ystart, NULL); \ } #if TTF_USE_SDF if (font->render_sdf && render_mode == RENDER_BLENDED) { Call_Specific_Render_Line(SDF) } #endif #if defined(HAVE_NEON_INTRINSICS) if (hasNEON()) { Call_Specific_Render_Line(NEON) } #endif #if defined(HAVE_SSE2_INTRINSICS) if (hasSSE2()) { Call_Specific_Render_Line(SSE) } #endif #if defined(HAVE_BLIT_GLYPH_64) Call_Specific_Render_Line(64) #elif defined(HAVE_BLIT_GLYPH_32) Call_Specific_Render_Line(32) #else Call_Specific_Render_Line(8) #endif } #ifndef SIZE_MAX # define SIZE_MAX ((size_t)(-1)) #endif #if !SDL_VERSION_ATLEAST(2, 23, 1) SDL_FORCE_INLINE int compat_size_add_overflow (size_t a, size_t b, size_t *ret) { if (b > SIZE_MAX - a) { return -1; } *ret = a + b; return 0; } SDL_FORCE_INLINE int compat_size_mul_overflow (size_t a, size_t b, size_t *ret) { if (a != 0 && b > SIZE_MAX / a) { return -1; } *ret = a * b; return 0; } #define SDL_size_add_overflow(a, b, r) compat_size_add_overflow(a, b, r) #define SDL_size_mul_overflow(a, b, r) compat_size_mul_overflow(a, b, r) #endif /* SDL < 2.23.1 */ /* Create a surface with memory: * - pitch is rounded to alignment * - address is aligned * * If format is 4 bytes per pixel, bgcolor is used to initialize each * 4-byte word in the image data. * * Otherwise, the low byte of format is used to initialize each byte * in the image data. */ static SDL_Surface *AllocateAlignedPixels(size_t width, size_t height, SDL_PixelFormatEnum format, Uint32 bgcolor) { const size_t alignment = Get_Alignment() - 1; const size_t bytes_per_pixel = SDL_BYTESPERPIXEL(format); SDL_Surface *textbuf = NULL; size_t size; size_t data_bytes; void *pixels, *ptr; size_t pitch; /* Worst case at the end of line pulling 'alignment' extra blank pixels */ if (width > SDL_MAX_SINT32 || height > SDL_MAX_SINT32 || SDL_size_add_overflow(width, alignment, &pitch) || SDL_size_mul_overflow(pitch, bytes_per_pixel, &pitch) || SDL_size_add_overflow(pitch, alignment, &pitch) || pitch > SDL_MAX_SINT32) { return NULL; } pitch &= ~alignment; if (SDL_size_mul_overflow(height, pitch, &data_bytes) || SDL_size_add_overflow(data_bytes, sizeof (void *) + alignment, &size) || size > SDL_MAX_SINT32) { /* Overflow... */ return NULL; } ptr = SDL_malloc(size); if (ptr == NULL) { return NULL; } /* address is aligned */ pixels = (void *)(((uintptr_t)ptr + sizeof(void *) + alignment) & ~alignment); ((void **)pixels)[-1] = ptr; textbuf = SDL_CreateRGBSurfaceWithFormatFrom(pixels, (int)width, (int)height, 0, (int)pitch, format); if (textbuf == NULL) { SDL_free(ptr); return NULL; } /* Let SDL handle the memory allocation */ textbuf->flags &= ~SDL_PREALLOC; textbuf->flags |= SDL_SIMD_ALIGNED; if (bytes_per_pixel == 4) { SDL_memset4(pixels, bgcolor, data_bytes / 4); } else { SDL_memset(pixels, (bgcolor & 0xff), data_bytes); } return textbuf; } static SDL_Surface* Create_Surface_Solid(int width, int height, SDL_Color fg, Uint32 *color) { SDL_Surface *textbuf = AllocateAlignedPixels(width, height, SDL_PIXELFORMAT_INDEX8, 0); if (textbuf == NULL) { return NULL; } /* Underline/Strikethrough color style */ *color = 1; /* Fill the palette: 1 is foreground */ { SDL_Palette *palette = textbuf->format->palette; palette->colors[0].r = 255 - fg.r; palette->colors[0].g = 255 - fg.g; palette->colors[0].b = 255 - fg.b; palette->colors[1].r = fg.r; palette->colors[1].g = fg.g; palette->colors[1].b = fg.b; palette->colors[1].a = fg.a; } SDL_SetColorKey(textbuf, SDL_TRUE, 0); return textbuf; } static SDL_Surface* Create_Surface_Shaded(int width, int height, SDL_Color fg, SDL_Color bg, Uint32 *color) { SDL_Surface *textbuf = AllocateAlignedPixels(width, height, SDL_PIXELFORMAT_INDEX8, 0); Uint8 bg_alpha = bg.a; if (textbuf == NULL) { return NULL; } /* Underline/Strikethrough color style */ *color = NUM_GRAYS - 1; /* Support alpha blending */ if (fg.a != SDL_ALPHA_OPAQUE || bg.a != SDL_ALPHA_OPAQUE) { SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); /* Would disturb alpha palette */ if (bg.a == SDL_ALPHA_OPAQUE) { bg.a = 0; } } /* Fill the palette with NUM_GRAYS levels of shading from bg to fg */ { SDL_Palette *palette = textbuf->format->palette; int rdiff = fg.r - bg.r; int gdiff = fg.g - bg.g; int bdiff = fg.b - bg.b; int adiff = fg.a - bg.a; int sign_r = (rdiff >= 0) ? 1 : 255; int sign_g = (gdiff >= 0) ? 1 : 255; int sign_b = (bdiff >= 0) ? 1 : 255; int sign_a = (adiff >= 0) ? 1 : 255; int i; for (i = 0; i < NUM_GRAYS; ++i) { /* Compute color[i] = (i * color_diff / 255) */ int tmp_r = i * rdiff; int tmp_g = i * gdiff; int tmp_b = i * bdiff; int tmp_a = i * adiff; palette->colors[i].r = (Uint8)(bg.r + DIVIDE_BY_255_SIGNED(tmp_r, sign_r)); palette->colors[i].g = (Uint8)(bg.g + DIVIDE_BY_255_SIGNED(tmp_g, sign_g)); palette->colors[i].b = (Uint8)(bg.b + DIVIDE_BY_255_SIGNED(tmp_b, sign_b)); palette->colors[i].a = (Uint8)(bg.a + DIVIDE_BY_255_SIGNED(tmp_a, sign_a)); } /* Make sure background has the correct alpha value */ palette->colors[0].a = bg_alpha; } return textbuf; } static SDL_Surface *Create_Surface_Blended(int width, int height, SDL_Color fg, Uint32 *color) { SDL_Surface *textbuf = NULL; Uint32 bgcolor; /* Background color: initialize with fg and 0 alpha */ bgcolor = (fg.r << 16) | (fg.g << 8) | fg.b; /* Underline/Strikethrough color style */ *color = bgcolor | ((Uint32)fg.a << 24); /* Create the target surface if required */ if (width != 0) { textbuf = AllocateAlignedPixels(width, height, SDL_PIXELFORMAT_ARGB8888, bgcolor); if (textbuf == NULL) { return NULL; } /* Support alpha blending */ if (fg.a != SDL_ALPHA_OPAQUE) { SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); } } return textbuf; } static SDL_Surface* Create_Surface_LCD(int width, int height, SDL_Color fg, SDL_Color bg, Uint32 *color) { SDL_Surface *textbuf = NULL; Uint32 bgcolor; /* Background color */ bgcolor = (((Uint32)bg.a) << 24) | (bg.r << 16) | (bg.g << 8) | bg.b; /* Underline/Strikethrough color style */ *color = (((Uint32)bg.a) << 24) | (fg.r << 16) | (fg.g << 8) | fg.b; /* Create the target surface if required */ if (width != 0) { textbuf = AllocateAlignedPixels(width, height, SDL_PIXELFORMAT_ARGB8888, bgcolor); if (textbuf == NULL) { return NULL; } /* Support alpha blending */ if (bg.a != SDL_ALPHA_OPAQUE) { SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND); } } return textbuf; } /* rcg06192001 get linked library's version. */ const SDL_version* TTF_Linked_Version(void) { static SDL_version linked_version; SDL_TTF_VERSION(&linked_version); return &linked_version; } /* This function tells the library whether UNICODE text is generally byteswapped. A UNICODE BOM character at the beginning of a string will override this setting for that string. */ void TTF_ByteSwappedUNICODE(SDL_bool swapped) { TTF_byteswapped = swapped; } #if defined(USE_FREETYPE_ERRORS) static void TTF_SetFTError(const char *msg, FT_Error error) { #undef FTERRORS_H_ #define FT_ERRORDEF(e, v, s) { e, s }, #define FT_ERROR_START_LIST { #define FT_ERROR_END_LIST { 0, NULL } }; const struct { int err_code; const char *err_msg; } ft_errors[] = #include FT_ERRORS_H unsigned int i; const char *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 #define TTF_SetFTError(msg, error) TTF_SetError(msg) #endif /* USE_FREETYPE_ERRORS */ int TTF_Init(void) { int status = 0; /* Some debug to know how it gets compiled */ #if 0 int duffs = 0, sse2 = 0, neon = 0, compil_sse2 = 0, compil_neon = 0; # if defined(USE_DUFFS_LOOP) duffs = 1; # endif # if defined(HAVE_SSE2_INTRINSICS) sse2 = hasSSE2(); compil_sse2 = 1; # endif # if defined(HAVE_NEON_INTRINSICS) neon = hasNEON(); compil_neon = 1; # endif SDL_Log("SDL_ttf: hasSSE2=%d hasNEON=%d alignment=%d duffs_loop=%d compil_sse2=%d compil_neon=%d", sse2, neon, Get_Alignment(), duffs, compil_sse2, compil_neon); SDL_Log("Sizeof TTF_Image: %d c_glyph: %d TTF_Font: %d", sizeof (TTF_Image), sizeof (c_glyph), sizeof (TTF_Font)); #endif 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; #if TTF_USE_SDF # if 0 /* Set various properties of the renderers. */ int spread = 4; int overlaps = 0; FT_Property_Set( library, "bsdf", "spread", &spread); FT_Property_Set( library, "sdf", "spread", &spread); FT_Property_Set( library, "sdf", "overlaps", &overlaps); # endif #endif } return status; } SDL_COMPILE_TIME_ASSERT(FT_Int, sizeof(int) == sizeof(FT_Int)); /* just in case. */ void TTF_GetFreeTypeVersion(int *major, int *minor, int *patch) { FT_Library_Version(library, major, minor, patch); } void TTF_GetHarfBuzzVersion(int *major, int *minor, int *patch) { unsigned int hb_major = 0; unsigned int hb_minor = 0; unsigned int hb_micro = 0; #if TTF_USE_HARFBUZZ hb_version(&hb_major, &hb_minor, &hb_micro); #endif if (major) { *major = (int)hb_major; } if (minor) { *minor = (int)hb_minor; } if (patch) { *patch = (int)hb_micro; } } static unsigned long RWread( FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count ) { SDL_RWops *src; src = (SDL_RWops *)stream->descriptor.pointer; SDL_RWseek(src, (int)offset, RW_SEEK_SET); if (count == 0) { return 0; } return (unsigned long)SDL_RWread(src, buffer, 1, (int)count); } TTF_Font* TTF_OpenFontIndexDPIRW(SDL_RWops *src, int freesrc, int ptsize, long index, unsigned int hdpi, unsigned int vdpi) { TTF_Font *font; FT_Error error; FT_Face face; FT_Stream stream; FT_CharMap found; Sint64 position; int i; if (!TTF_initialized) { TTF_SetError("Library not initialized"); if (src && freesrc) { SDL_RWclose(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 = SDL_RWtell(src); if (position < 0) { TTF_SetError("Can't seek in stream"); if (freesrc) { SDL_RWclose(src); } return NULL; } font = (TTF_Font *)SDL_malloc(sizeof (*font)); if (font == NULL) { TTF_SetError("Out of memory"); if (freesrc) { SDL_RWclose(src); } return NULL; } SDL_memset(font, 0, sizeof (*font)); font->src = src; font->freesrc = freesrc; stream = (FT_Stream)SDL_malloc(sizeof (*stream)); if (stream == NULL) { TTF_SetError("Out of memory"); TTF_CloseFont(font); return NULL; } SDL_memset(stream, 0, sizeof (*stream)); stream->read = RWread; stream->descriptor.pointer = src; stream->pos = (unsigned long)position; stream->size = (unsigned long)(SDL_RWsize(src) - position); font->args.flags = FT_OPEN_STREAM; font->args.stream = stream; error = FT_Open_Face(library, &font->args, index, &font->face); if (error || font->face == NULL) { TTF_SetFTError("Couldn't load font file", error); TTF_CloseFont(font); return NULL; } face = font->face; /* Set charmap for loaded font */ found = 0; #if 0 /* Font debug code */ for (i = 0; i < face->num_charmaps; i++) { FT_CharMap charmap = face->charmaps[i]; SDL_Log("Found charmap: platform id %d, encoding id %d", charmap->platform_id, charmap->encoding_id); } #endif if (!found) { for (i = 0; i < face->num_charmaps; i++) { FT_CharMap charmap = face->charmaps[i]; if (charmap->platform_id == 3 && charmap->encoding_id == 10) { /* UCS-4 Unicode */ found = charmap; break; } } } if (!found) { 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); } /* Set the default font style */ font->style = TTF_STYLE_NORMAL; font->outline_val = 0; font->ft_load_target = FT_LOAD_TARGET_NORMAL; TTF_SetFontKerning(font, 1); font->pos_len = 0; font->pos_max = 16; font->pos_buf = (PosBuf_t *)SDL_malloc(font->pos_max * sizeof (font->pos_buf[0])); if (! font->pos_buf) { TTF_SetError("Out of memory"); TTF_CloseFont(font); return NULL; } #if TTF_USE_HARFBUZZ font->hb_font = hb_ft_font_create(face, NULL); if (font->hb_font == NULL) { TTF_SetError("Cannot create harfbuzz font"); TTF_CloseFont(font); return NULL; } /* Default load-flags of hb_ft_font_create is no-hinting. * So unless you call hb_ft_font_set_load_flags to match what flags you use for rendering, * you will get mismatching advances and raster. */ hb_ft_font_set_load_flags(font->hb_font, FT_LOAD_DEFAULT | font->ft_load_target); /* By default the script / direction are inherited from global variables */ font->hb_script = HB_SCRIPT_INVALID; font->hb_direction = HB_DIRECTION_INVALID; #endif if (TTF_SetFontSizeDPI(font, ptsize, hdpi, vdpi) < 0) { TTF_SetFTError("Couldn't set font size", error); TTF_CloseFont(font); return NULL; } return font; } int TTF_SetFontSizeDPI(TTF_Font *font, int ptsize, unsigned int hdpi, unsigned int vdpi) { FT_Face face = font->face; FT_Error error; /* Make sure that our font face is scalable (global metrics) */ if (FT_IS_SCALABLE(face)) { /* Set the character size using the provided DPI. If a zero DPI * is provided, then the other DPI setting will be used. If both * are zero, then Freetype's default 72 DPI will be used. */ error = FT_Set_Char_Size(face, 0, ptsize * 64, hdpi, vdpi); if (error) { TTF_SetFTError("Couldn't set font size", error); return -1; } } 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 (face->num_fixed_sizes <= 0) { TTF_SetError("Couldn't select size : no num_fixed_sizes"); return -1; } /* within [0; num_fixed_sizes - 1] */ ptsize = SDL_max(ptsize, 0); ptsize = SDL_min(ptsize, face->num_fixed_sizes - 1); error = FT_Select_Size(face, ptsize); if (error) { TTF_SetFTError("Couldn't select size", error); return -1; } } if (TTF_initFontMetrics(font) < 0) { TTF_SetError("Cannot initialize metrics"); return -1; } Flush_Cache(font); #if TTF_USE_HARFBUZZ /* Call when size or variations settings on underlying FT_Face change. */ hb_ft_font_changed(font->hb_font); #endif return 0; } int TTF_SetFontSize(TTF_Font *font, int ptsize) { return TTF_SetFontSizeDPI(font, ptsize, 0, 0); } /* Update font parameter depending on a style change */ static int TTF_initFontMetrics(TTF_Font *font) { FT_Face face = font->face; int underline_offset; /* Make sure that our font face is scalable (global metrics) */ if (FT_IS_SCALABLE(face)) { /* Get the scalable font metrics for this font */ FT_Fixed 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 = FT_CEIL(FT_MulFix(face->ascender - face->descender, scale)); font->lineskip = FT_CEIL(FT_MulFix(face->height, scale)); underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale)); font->line_thickness = FT_FLOOR(FT_MulFix(face->underline_thickness, scale)); } else { /* Get the font metrics for this font, for the selected size */ font->ascent = FT_CEIL(face->size->metrics.ascender); font->descent = FT_CEIL(face->size->metrics.descender); font->height = FT_CEIL(face->size->metrics.height); font->lineskip = FT_CEIL(face->size->metrics.height); /* face->underline_position and face->underline_height are only * relevant for scalable formats (see freetype.h FT_FaceRec) */ underline_offset = font->descent / 2; font->line_thickness = 1; } if (font->line_thickness < 1) { font->line_thickness = 1; } font->underline_top_row = font->ascent - underline_offset - 1; font->strikethrough_top_row = font->height / 2; /* Adjust OutlineStyle, only for scalable fonts */ /* TTF_Size(): increase w and h by 2 * outline_val, translate positionning by 1 * outline_val */ if (font->outline_val > 0) { int fo = font->outline_val; font->line_thickness += 2 * fo; font->underline_top_row -= fo; font->strikethrough_top_row -= fo; } /* Robustness: no negative values allowed */ font->underline_top_row = SDL_max(0, font->underline_top_row); font->strikethrough_top_row = SDL_max(0, font->strikethrough_top_row); /* Update height according to the needs of the underline style */ if (TTF_HANDLE_STYLE_UNDERLINE(font)) { int bottom_row = font->underline_top_row + font->line_thickness; font->height = SDL_max(font->height, bottom_row); } /* Update height according to the needs of the strikethrough style */ if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { int bottom_row = font->strikethrough_top_row + font->line_thickness; font->height = SDL_max(font->height, bottom_row); } #if defined(DEBUG_FONTS) SDL_Log("Font metrics:"); SDL_Log("ascent = %d, descent = %d", font->ascent, font->descent); SDL_Log("height = %d, lineskip = %d", font->height, font->lineskip); SDL_Log("underline_offset = %d, line_thickness = %d", underline_offset, font->line_thickness); SDL_Log("underline_top_row = %d, strikethrough_top_row = %d", font->underline_top_row, font->strikethrough_top_row); SDL_Log("scalable=%d fixed_sizes=%d", FT_IS_SCALABLE(face), FT_HAS_FIXED_SIZES(face)); #endif font->glyph_overhang = face->size->metrics.y_ppem / 10; return 0; } TTF_Font* TTF_OpenFontDPIRW( SDL_RWops *src, int freesrc, int ptsize, unsigned int hdpi, unsigned int vdpi ) { return TTF_OpenFontIndexDPIRW(src, freesrc, ptsize, 0, hdpi, vdpi); } TTF_Font* TTF_OpenFontIndexRW( SDL_RWops *src, int freesrc, int ptsize, long index ) { return TTF_OpenFontIndexDPIRW(src, freesrc, ptsize, index, 0, 0); } TTF_Font* TTF_OpenFontIndexDPI( const char *file, int ptsize, long index, unsigned int hdpi, unsigned int vdpi ) { SDL_RWops *rw = SDL_RWFromFile(file, "rb"); if ( rw == NULL ) { return NULL; } return TTF_OpenFontIndexDPIRW(rw, 1, ptsize, index, hdpi, vdpi); } TTF_Font* TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize) { return TTF_OpenFontIndexRW(src, freesrc, ptsize, 0); } TTF_Font* TTF_OpenFontDPI(const char *file, int ptsize, unsigned int hdpi, unsigned int vdpi) { return TTF_OpenFontIndexDPI(file, ptsize, 0, hdpi, vdpi); } TTF_Font* TTF_OpenFontIndex(const char *file, int ptsize, long index) { return TTF_OpenFontIndexDPI(file, ptsize, index, 0, 0); } TTF_Font* TTF_OpenFont(const char *file, int ptsize) { return TTF_OpenFontIndex(file, ptsize, 0); } static void Flush_Glyph_Image(TTF_Image *image) { if (image->buffer) { SDL_free(image->buffer); image->buffer = NULL; } } static void Flush_Glyph(c_glyph *glyph) { glyph->stored = 0; glyph->index = 0; Flush_Glyph_Image(&glyph->pixmap); Flush_Glyph_Image(&glyph->bitmap); } 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].stored) { Flush_Glyph(&font->cache[i]); } } } static FT_Error Load_Glyph(TTF_Font *font, c_glyph *cached, int want, int translation) { const int alignment = Get_Alignment() - 1; FT_GlyphSlot slot; FT_Error error; int ft_load = FT_LOAD_DEFAULT | font->ft_load_target; #if TTF_USE_COLOR if (want & CACHED_COLOR) { ft_load |= FT_LOAD_COLOR; } #endif error = FT_Load_Glyph(font->face, cached->index, ft_load); if (error) { goto ft_failure; } /* Get our glyph shortcut */ slot = font->face->glyph; if (want & CACHED_LCD) { if (slot->format == FT_GLYPH_FORMAT_BITMAP) { TTF_SetError("LCD mode not possible with bitmap font"); return -1; } } /* Get the glyph metrics, always needed */ if (cached->stored == 0) { cached->sz_left = slot->bitmap_left; cached->sz_top = slot->bitmap_top; cached->sz_rows = slot->bitmap.rows; cached->sz_width = slot->bitmap.width; /* Current version of freetype is 2.9.1, but on older freetype (2.8.1) this can be 0. * Try to get them from 'FT_Glyph_Metrics' */ if (cached->sz_left == 0 && cached->sz_top == 0 && cached->sz_rows == 0 && cached->sz_width == 0) { FT_Glyph_Metrics *metrics = &slot->metrics; if (metrics) { int minx = FT_FLOOR(metrics->horiBearingX); int maxx = FT_CEIL(metrics->horiBearingX + metrics->width); int maxy = FT_FLOOR(metrics->horiBearingY); int miny = maxy - FT_CEIL(metrics->height); cached->sz_left = minx; cached->sz_top = maxy; cached->sz_rows = maxy - miny; cached->sz_width = maxx - minx; } } /* All FP 26.6 are 'long' but 'int' should be engouh */ cached->advance = (int)slot->metrics.horiAdvance; /* FP 26.6 */ if (font->render_subpixel == 0) { /* FT KERNING_MODE_SMART */ cached->kerning_smart.rsb_delta = (int)slot->rsb_delta; /* FP 26.6 */ cached->kerning_smart.lsb_delta = (int)slot->lsb_delta; /* FP 26.6 */ } else { /* FT LCD_MODE_LIGHT_SUBPIXEL */ cached->subpixel.lsb_minus_rsb = (int)(slot->lsb_delta - slot->rsb_delta); /* FP 26.6 */ cached->subpixel.translation = 0; /* FP 26.6 */ } #if defined(DEBUG_FONTS) SDL_Log("Index=%d sz_left=%d sz_top=%d sz_width=%d sz_rows=%d advance=%d is_outline=%d is_bitmap=%d", cached->index, cached->sz_left, cached->sz_top, cached->sz_width, cached->sz_rows, cached->advance, slot->format == FT_GLYPH_FORMAT_OUTLINE, slot->format == FT_GLYPH_FORMAT_BITMAP); #endif /* Adjust for bold text */ if (TTF_HANDLE_STYLE_BOLD(font)) { cached->sz_width += font->glyph_overhang; cached->advance += F26Dot6(font->glyph_overhang); } /* Adjust for italic text */ if (TTF_HANDLE_STYLE_ITALIC(font) && slot->format == FT_GLYPH_FORMAT_OUTLINE) { cached->sz_width += (GLYPH_ITALICS * font->height) >> 16; } /* Adjust for subpixel */ if (font->render_subpixel) { cached->sz_width += 1; } /* Adjust for SDF */ if (font->render_sdf) { /* Default 'spread' property */ cached->sz_width += 2 * 8; cached->sz_rows += 2 * 8; } cached->stored |= CACHED_METRICS; } if (((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) || ((want & CACHED_COLOR) && !(cached->stored & CACHED_COLOR)) || ((want & CACHED_LCD) && !(cached->stored & CACHED_LCD)) || (want & CACHED_SUBPIX) ) { const int mono = (want & CACHED_BITMAP); TTF_Image *dst = (mono ? &cached->bitmap : &cached->pixmap); FT_Glyph glyph = NULL; FT_Bitmap *src; FT_Render_Mode ft_render_mode; if (mono) { ft_render_mode = FT_RENDER_MODE_MONO; } else { ft_render_mode = FT_RENDER_MODE_NORMAL; #if TTF_USE_SDF if ((want & CACHED_COLOR) && font->render_sdf) { ft_render_mode = FT_RENDER_MODE_SDF; } #endif if ((want & CACHED_LCD)) { ft_render_mode = FT_RENDER_MODE_LCD; } } /* Subpixel translation, flush previous datas */ if (want & CACHED_SUBPIX) { Flush_Glyph_Image(&cached->pixmap); FT_Outline_Translate(&slot->outline, translation, 0 ); cached->subpixel.translation = translation; } /* Handle the italic style, only for scalable fonts */ if (TTF_HANDLE_STYLE_ITALIC(font) && slot->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Matrix shear; shear.xx = 1 << 16; shear.xy = GLYPH_ITALICS; shear.yx = 0; shear.yy = 1 << 16; FT_Outline_Transform(&slot->outline, &shear); } /* Render as outline */ if ((font->outline_val > 0 && slot->format == FT_GLYPH_FORMAT_OUTLINE) || slot->format == FT_GLYPH_FORMAT_BITMAP) { FT_BitmapGlyph bitmap_glyph; error = FT_Get_Glyph(slot, &glyph); if (error) { goto ft_failure; } if (font->outline_val > 0) { FT_Stroker stroker; error = FT_Stroker_New(library, &stroker); if (error) { goto ft_failure; } FT_Stroker_Set(stroker, font->outline_val * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_Stroke(&glyph, stroker, 1 /* delete the original glyph */); FT_Stroker_Done(stroker); } /* Render the glyph */ error = FT_Glyph_To_Bitmap(&glyph, ft_render_mode, 0, 1); if (error) { FT_Done_Glyph(glyph); goto ft_failure; } /* Access bitmap content by typecasting */ bitmap_glyph = (FT_BitmapGlyph) glyph; src = &bitmap_glyph->bitmap; /* Get new metrics, from bitmap */ dst->left = bitmap_glyph->left; dst->top = bitmap_glyph->top; } else { /* Render the glyph */ error = FT_Render_Glyph(slot, ft_render_mode); if (error) { goto ft_failure; } /* Access bitmap from slot */ src = &slot->bitmap; /* Get new metrics, from slot */ dst->left = slot->bitmap_left; dst->top = slot->bitmap_top; } /* Common metrics */ dst->width = src->width; dst->rows = src->rows; dst->buffer = NULL; /* FT can make small size glyph of 'width == 0', and 'rows != 0'. * Make sure 'rows' is also 0, so it doesn't break USE_DUFFS_LOOP */ if (dst->width == 0) { dst->rows = 0; } /* Adjust for bold text */ if (TTF_HANDLE_STYLE_BOLD(font)) { dst->width += font->glyph_overhang; } /* Compute pitch: glyph is padded right to be able to read an 'aligned' size expanding on the right */ dst->pitch = dst->width + alignment; #if TTF_USE_COLOR if (src->pixel_mode == FT_PIXEL_MODE_BGRA) { dst->pitch += 3 * dst->width; } #endif if (src->pixel_mode == FT_PIXEL_MODE_LCD) { dst->pitch += 3 * dst->width; } if (dst->rows != 0) { unsigned int i; /* Glyph buffer is NOT aligned, * Extra width so it can read an 'aligned' size expanding on the left */ dst->buffer = (unsigned char *)SDL_malloc(alignment + dst->pitch * dst->rows); if (!dst->buffer) { error = FT_Err_Out_Of_Memory; goto ft_failure; } /* Memset */ SDL_memset(dst->buffer, 0, alignment + dst->pitch * dst->rows); /* Shift, so that the glyph is decoded centered */ dst->buffer += alignment; /* 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 face contains outline glyphs, 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. */ for (i = 0; i < (unsigned int)src->rows; i++) { unsigned char *srcp = src->buffer + i * src->pitch; unsigned char *dstp = dst->buffer + i * dst->pitch; unsigned int k, quotient, remainder; /* Decode exactly the needed size from src->width */ if (src->pixel_mode == FT_PIXEL_MODE_MONO) { quotient = src->width / 8; remainder = src->width & 0x7; } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) { quotient = src->width / 4; remainder = src->width & 0x3; } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) { quotient = src->width / 2; remainder = src->width & 0x1; #if TTF_USE_COLOR } else if (src->pixel_mode == FT_PIXEL_MODE_BGRA) { quotient = src->width; remainder = 0; #endif } else if (src->pixel_mode == FT_PIXEL_MODE_LCD) { quotient = src->width / 3; remainder = 0; } else { quotient = src->width; remainder = 0; } /* FT_RENDER_MODE_MONO and src->pixel_mode MONO */ #ifdef _MSC_VER #pragma warning(push, 1) #pragma warning(disable:4127) #endif #define MONO_MONO(K_MAX) \ if ((K_MAX)) { \ unsigned char c = *srcp++; \ for (k = 0; k < (K_MAX); ++k) { \ *dstp++ = (c & 0x80) >> 7; \ c <<= 1; \ } \ } /* FT_RENDER_MODE_MONO and src->pixel_mode GRAY2 */ #define MONO_GRAY2(K_MAX) \ if ((K_MAX)) { \ unsigned char c = *srcp++; \ for (k = 0; k < (K_MAX); ++k) { \ *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; \ c <<= 2; \ } \ } /* FT_RENDER_MODE_MONO and src->pixel_mode GRAY4 */ #define MONO_GRAY4(K_MAX) \ if ((K_MAX)) { \ unsigned char c = *srcp++; \ for (k = 0; k < (K_MAX); ++k) { \ *dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0; \ c <<= 4; \ } \ } /* FT_RENDER_MODE_NORMAL and src->pixel_mode MONO */ #define NORMAL_MONO(K_MAX) \ if ((K_MAX)) { \ unsigned char c = *srcp++; \ for (k = 0; k < (K_MAX); ++k) { \ if ((c&0x80) >> 7) { \ *dstp++ = NUM_GRAYS - 1; \ } else { \ *dstp++ = 0x00; \ } \ c <<= 1; \ } \ } /* FT_RENDER_MODE_NORMAL and src->pixel_mode GRAY2 */ #define NORMAL_GRAY2(K_MAX) \ if ((K_MAX)) { \ unsigned char c = *srcp++; \ for (k = 0; k < (K_MAX); ++k) { \ if ((c&0xA0) >> 6) { \ *dstp++ = NUM_GRAYS * ((c&0xA0) >> 6) / 3 - 1; \ } else { \ *dstp++ = 0x00; \ } \ c <<= 2; \ } \ } /* FT_RENDER_MODE_NORMAL and src->pixel_mode GRAY4 */ #define NORMAL_GRAY4(K_MAX) \ if ((K_MAX)) { \ unsigned char c = *srcp++; \ for (k = 0; k < (K_MAX); ++k) { \ if ((c&0xF0) >> 4) { \ *dstp++ = NUM_GRAYS * ((c&0xF0) >> 4) / 15 - 1; \ } else { \ *dstp++ = 0x00; \ } \ c <<= 4; \ } \ } if (mono) { if (src->pixel_mode == FT_PIXEL_MODE_MONO) { while (quotient--) { MONO_MONO(8); } MONO_MONO(remainder); } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) { while (quotient--) { MONO_GRAY2(4); } MONO_GRAY2(remainder); } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) { while (quotient--) { MONO_GRAY4(2); } MONO_GRAY4(remainder); } else { while (quotient--) { 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. */ while (quotient--) { NORMAL_MONO(8); } NORMAL_MONO(remainder); } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) { while (quotient--) { NORMAL_GRAY2(4); } NORMAL_GRAY2(remainder); } else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) { while (quotient--) { NORMAL_GRAY4(2); } NORMAL_GRAY4(remainder); #if TTF_USE_COLOR } else if (src->pixel_mode == FT_PIXEL_MODE_BGRA) { SDL_memcpy(dstp, srcp, 4 * src->width); #endif } else if (src->pixel_mode == FT_PIXEL_MODE_LCD) { while (quotient--) { Uint8 alpha = 0; Uint8 r, g, b; r = *srcp++; g = *srcp++; b = *srcp++; *dstp++ = b; *dstp++ = g; *dstp++ = r; *dstp++ = alpha; } } else { #if TTF_USE_SDF if (ft_render_mode != FT_RENDER_MODE_SDF) { SDL_memcpy(dstp, srcp, src->width); } else { int x; for (x = 0; x < src->width; x++) { Uint8 s = srcp[x]; Uint8 d; if (s < 128) { d = 256 - (128 - s) * 2; } else { d = 255; /* some glitch ? if (s == 255) { d = 0; }*/ } dstp[x] = d; } } #else SDL_memcpy(dstp, srcp, src->width); #endif } } } #ifdef _MSC_VER #pragma warning(pop) #endif /* Handle the bold style */ if (TTF_HANDLE_STYLE_BOLD(font)) { int row; /* The pixmap is a little hard, we have to add and clamp */ for (row = dst->rows - 1; row >= 0; --row) { Uint8 *pixmap = dst->buffer + row * dst->pitch; int col, offset; /* Minimal memset */ /* SDL_memset(pixmap + dst->width - font->glyph_overhang, 0, font->glyph_overhang); */ for (offset = 1; offset <= font->glyph_overhang; ++offset) { for (col = dst->width - 1; col > 0; --col) { if (mono) { pixmap[col] |= pixmap[col-1]; } else { int pixel = (pixmap[col] + pixmap[col-1]); if (pixel > NUM_GRAYS - 1) { pixel = NUM_GRAYS - 1; } pixmap[col] = (Uint8) pixel; } } } } } /* Shift back */ if (dst->buffer) { dst->buffer -= alignment; } #if TTF_USE_COLOR if (src->pixel_mode == FT_PIXEL_MODE_BGRA) { dst->is_color = 1; } else { dst->is_color = 0; } #else dst->is_color = 0; #endif /* Mark that we rendered this format */ if (mono) { cached->stored |= CACHED_BITMAP; } else if (src->pixel_mode == FT_PIXEL_MODE_LCD) { cached->stored |= CACHED_LCD; } else { #if TTF_USE_COLOR if (want & CACHED_COLOR) { cached->stored |= CACHED_COLOR; /* Most of the time, glyphs loaded with FT_LOAD_COLOR are non colored, so the cache is also suitable for Shaded rendering (eg, loaded without FT_LOAD_COLOR) */ if (dst->is_color == 0) { cached->stored |= CACHED_PIXMAP; } } else { cached->stored |= CACHED_PIXMAP; /* If font has no color information, Shaded/Pixmap cache is also suitable for Blend/Color */ if (!FT_HAS_COLOR(font->face)) { cached->stored |= CACHED_COLOR; } } #else cached->stored |= CACHED_COLOR | CACHED_PIXMAP; #endif } /* Free outlined glyph */ if (glyph) { FT_Done_Glyph(glyph); } } /* We're done, this glyph is cached since 'stored' is not 0 */ return 0; ft_failure: TTF_SetFTError("Couldn't find glyph", error); return -1; } static SDL_INLINE int Find_GlyphByIndex(TTF_Font *font, FT_UInt idx, int want_bitmap, int want_pixmap, int want_color, int want_lcd, int want_subpixel, int translation, c_glyph **out_glyph, TTF_Image **out_image) { /* cache size is 256, get key by masking */ c_glyph *glyph = &font->cache[idx & 0xff]; if (out_glyph) { *out_glyph = glyph; } if (want_pixmap || want_color || want_lcd) { *out_image = &glyph->pixmap; } if (want_bitmap) { *out_image = &glyph->bitmap; } if (want_subpixel) { /* No a real cache, but if it always advances by integer pixels (eg translation 0 or same as previous), * this allows to render as fast as normal mode. */ int retval; int want = CACHED_METRICS | want_bitmap | want_pixmap | want_color | want_lcd | want_subpixel; if (glyph->stored && glyph->index != idx) { Flush_Glyph(glyph); } if (glyph->subpixel.translation == translation) { want &= ~CACHED_SUBPIX; } if ((glyph->stored & want) == want) { return 0; } if (want_color || want_pixmap || want_lcd) { if (glyph->stored & (CACHED_COLOR|CACHED_PIXMAP|CACHED_LCD)) { Flush_Glyph(glyph); } } glyph->index = idx; retval = Load_Glyph(font, glyph, want, translation); if (retval == 0) { return 0; } else { return -1; } } else { int retval; const int want = CACHED_METRICS | want_bitmap | want_pixmap | want_color | want_lcd; /* Faster check as it gets inlined */ if (want_pixmap) { if ((glyph->stored & CACHED_PIXMAP) && glyph->index == idx) { return 0; } } else if (want_bitmap) { if ((glyph->stored & CACHED_BITMAP) && glyph->index == idx) { return 0; } } else if (want_color) { if ((glyph->stored & CACHED_COLOR) && glyph->index == idx) { return 0; } } else if (want_lcd) { if ((glyph->stored & CACHED_LCD) && glyph->index == idx) { return 0; } } else { /* Get metrics */ if (glyph->stored && glyph->index == idx) { return 0; } } /* Cache cannot contain both PIXMAP and COLOR (unless COLOR is actually not colored) and LCD So, if it's already used, clear it */ if (want_color || want_pixmap || want_lcd) { if (glyph->stored & (CACHED_COLOR|CACHED_PIXMAP|CACHED_LCD)) { Flush_Glyph(glyph); } } if (glyph->stored && glyph->index != idx) { Flush_Glyph(glyph); } glyph->index = idx; retval = Load_Glyph(font, glyph, want, 0); if (retval == 0) { return 0; } else { return -1; } } } static SDL_INLINE FT_UInt get_char_index(TTF_Font *font, Uint32 ch) { Uint32 cache_index_size = sizeof (font->cache_index) / sizeof (font->cache_index[0]); if (ch < cache_index_size) { FT_UInt idx = font->cache_index[ch]; if (idx) { return idx; } idx = FT_Get_Char_Index(font->face, ch); font->cache_index[ch] = idx; return idx; } return FT_Get_Char_Index(font->face, ch); } static SDL_INLINE int Find_GlyphMetrics(TTF_Font *font, Uint32 ch, c_glyph **out_glyph) { FT_UInt idx = get_char_index(font, ch); return Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, 0, out_glyph, NULL); } void TTF_CloseFont(TTF_Font *font) { if (font) { #if TTF_USE_HARFBUZZ hb_font_destroy(font->hb_font); #endif Flush_Cache(font); if (font->face) { FT_Done_Face(font->face); } if (font->args.stream) { SDL_free(font->args.stream); } if (font->freesrc) { SDL_RWclose(font->src); } if (font->pos_buf) { SDL_free(font->pos_buf); } SDL_free(font); } } /* Gets the number of bytes needed to convert a Latin-1 string to UTF-8 */ static size_t LATIN1_to_UTF8_len(const char *text) { size_t bytes = 1; while (*text) { Uint8 ch = *(const Uint8 *)text++; if (ch <= 0x7F) { bytes += 1; } else { bytes += 2; } } return bytes; } /* Gets the number of bytes needed to convert a UCS2 string to UTF-8 */ static size_t UCS2_to_UTF8_len(const Uint16 *text) { SDL_bool swapped = TTF_byteswapped; size_t bytes = 1; while (*text) { Uint16 ch = *text++; if (ch == UNICODE_BOM_NATIVE) { swapped = SDL_FALSE; continue; } if (ch == UNICODE_BOM_SWAPPED) { swapped = SDL_TRUE; continue; } if (swapped) { ch = SDL_Swap16(ch); } if (ch <= 0x7F) { bytes += 1; } else if (ch <= 0x7FF) { bytes += 2; } else { bytes += 3; } } return bytes; } /* Convert a Latin-1 string to a UTF-8 string */ static void LATIN1_to_UTF8(const char *src, Uint8 *dst) { while (*src) { Uint8 ch = *(const Uint8 *)src++; if (ch <= 0x7F) { *dst++ = ch; } else { *dst++ = 0xC0 | ((ch >> 6) & 0x1F); *dst++ = 0x80 | (ch & 0x3F); } } *dst = '\0'; } /* Convert a UCS-2 string to a UTF-8 string */ static void UCS2_to_UTF8(const Uint16 *src, Uint8 *dst) { SDL_bool swapped = TTF_byteswapped; while (*src) { Uint16 ch = *src++; if (ch == UNICODE_BOM_NATIVE) { swapped = SDL_FALSE; continue; } if (ch == UNICODE_BOM_SWAPPED) { swapped = SDL_TRUE; continue; } if (swapped) { ch = SDL_Swap16(ch); } if (ch <= 0x7F) { *dst++ = (Uint8) ch; } else if (ch <= 0x7FF) { *dst++ = 0xC0 | (Uint8) ((ch >> 6) & 0x1F); *dst++ = 0x80 | (Uint8) (ch & 0x3F); } else { *dst++ = 0xE0 | (Uint8) ((ch >> 12) & 0x0F); *dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F); *dst++ = 0x80 | (Uint8) (ch & 0x3F); } } *dst = '\0'; } /* Convert a unicode char to a UTF-8 string */ static SDL_bool Char_to_UTF8(Uint32 ch, Uint8 *dst) { if (ch <= 0x7F) { *dst++ = (Uint8) ch; } else if (ch <= 0x7FF) { *dst++ = 0xC0 | (Uint8) ((ch >> 6) & 0x1F); *dst++ = 0x80 | (Uint8) (ch & 0x3F); } else if (ch <= 0xFFFF) { *dst++ = 0xE0 | (Uint8) ((ch >> 12) & 0x0F); *dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F); *dst++ = 0x80 | (Uint8) (ch & 0x3F); } else if (ch <= 0x1FFFFF) { *dst++ = 0xF0 | (Uint8) ((ch >> 18) & 0x07); *dst++ = 0x80 | (Uint8) ((ch >> 12) & 0x3F); *dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F); *dst++ = 0x80 | (Uint8) (ch & 0x3F); } else if (ch <= 0x3FFFFFF) { *dst++ = 0xF8 | (Uint8) ((ch >> 24) & 0x03); *dst++ = 0x80 | (Uint8) ((ch >> 18) & 0x3F); *dst++ = 0x80 | (Uint8) ((ch >> 12) & 0x3F); *dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F); *dst++ = 0x80 | (Uint8) (ch & 0x3F); } else if (ch < 0x7FFFFFFF) { *dst++ = 0xFC | (Uint8) ((ch >> 30) & 0x01); *dst++ = 0x80 | (Uint8) ((ch >> 24) & 0x3F); *dst++ = 0x80 | (Uint8) ((ch >> 18) & 0x3F); *dst++ = 0x80 | (Uint8) ((ch >> 12) & 0x3F); *dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F); *dst++ = 0x80 | (Uint8) (ch & 0x3F); } else { TTF_SetError("Invalid character"); return SDL_FALSE; } *dst = '\0'; return SDL_TRUE; } /* Gets a unicode value from a UTF-8 encoded string * Ouputs increment to advance the string */ #define UNKNOWN_UNICODE 0xFFFD static Uint32 UTF8_getch(const char *src, size_t srclen, int *inc) { const Uint8 *p = (const Uint8 *)src; size_t left = 0; size_t save_srclen = srclen; SDL_bool overlong = SDL_FALSE; SDL_bool underflow = SDL_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 = SDL_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 = SDL_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 = SDL_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 = SDL_TRUE; } ch = (Uint32) (p[0] & 0x0F); left = 2; } } else if (p[0] >= 0xC0) { if ((p[0] & 0xE0) == 0xC0) { if ((p[0] & 0xDE) == 0xC0) { overlong = SDL_TRUE; } ch = (Uint32) (p[0] & 0x1F); left = 1; } } else { if ((p[0] & 0x80) == 0x00) { ch = (Uint32) p[0]; } } --srclen; while (left > 0 && srclen > 0) { ++p; if ((p[0] & 0xC0) != 0x80) { ch = UNKNOWN_UNICODE; break; } ch <<= 6; ch |= (p[0] & 0x3F); --srclen; --left; } if (left > 0) { underflow = SDL_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; */ (void) overlong; if (underflow || (ch >= 0xD800 && ch <= 0xDFFF) || (ch == 0xFFFE || ch == 0xFFFF) || ch > 0x10FFFF) { ch = UNKNOWN_UNICODE; } *inc = (int)(save_srclen - srclen); return ch; } int TTF_FontHeight(const TTF_Font *font) { return font->height; } int TTF_FontAscent(const TTF_Font *font) { return font->ascent + 2 * font->outline_val; } int TTF_FontDescent(const TTF_Font *font) { return font->descent; } int TTF_FontLineSkip(const TTF_Font *font) { return font->lineskip; } void TTF_SetFontLineSkip(TTF_Font *font, int lineskip) { if (!font) { return; } font->lineskip = lineskip; } int TTF_GetFontKerning(const TTF_Font *font) { return font->allow_kerning; } void TTF_SetFontKerning(TTF_Font *font, int allowed) { font->allow_kerning = allowed; #if TTF_USE_HARFBUZZ /* Harfbuzz can do kerning positioning even if the font hasn't the data */ #else font->use_kerning = allowed && FT_HAS_KERNING(font->face); #endif } long TTF_FontFaces(const TTF_Font *font) { return font->face->num_faces; } int TTF_FontFaceIsFixedWidth(const TTF_Font *font) { return FT_IS_FIXED_WIDTH(font->face); } const char * TTF_FontFaceFamilyName(const TTF_Font *font) { return font->face->family_name; } const char * TTF_FontFaceStyleName(const TTF_Font *font) { return font->face->style_name; } int TTF_GlyphIsProvided(TTF_Font *font, Uint16 ch) { return (int)get_char_index(font, ch); } int TTF_GlyphIsProvided32(TTF_Font *font, Uint32 ch) { return (int)get_char_index(font, ch); } int TTF_GlyphMetrics(TTF_Font *font, Uint16 ch, int *minx, int *maxx, int *miny, int *maxy, int *advance) { return TTF_GlyphMetrics32(font, ch, minx, maxx, miny, maxy, advance); } int TTF_GlyphMetrics32(TTF_Font *font, Uint32 ch, int *minx, int *maxx, int *miny, int *maxy, int *advance) { c_glyph *glyph; TTF_CHECK_POINTER(font, -1); if (Find_GlyphMetrics(font, ch, &glyph) < 0) { return -1; } if (minx) { *minx = glyph->sz_left; } if (maxx) { *maxx = glyph->sz_left + glyph->sz_width; *maxx += 2 * font->outline_val; } if (miny) { *miny = glyph->sz_top - glyph->sz_rows; } if (maxy) { *maxy = glyph->sz_top; *maxy += 2 * font->outline_val; } if (advance) { *advance = FT_CEIL(glyph->advance); } return 0; } int TTF_SetFontDirection(TTF_Font *font, TTF_Direction direction) { #if TTF_USE_HARFBUZZ hb_direction_t dir; if (direction == TTF_DIRECTION_LTR) { dir = HB_DIRECTION_LTR; } else if (direction == TTF_DIRECTION_RTL) { dir = HB_DIRECTION_RTL; } else if (direction == TTF_DIRECTION_TTB) { dir = HB_DIRECTION_BTT; } else if (direction == TTF_DIRECTION_BTT) { dir = HB_DIRECTION_TTB; } else { return -1; } font->hb_direction = dir; return 0; #else (void) font; (void) direction; return -1; #endif } int TTF_SetFontScriptName(TTF_Font *font, const char *script) { #if TTF_USE_HARFBUZZ Uint8 a, b, c, d; hb_script_t scr; if (script == NULL || SDL_strlen(script) != 4) { return -1; } a = script[0]; b = script[1]; c = script[2]; d = script[3]; scr = HB_TAG(a, b, c, d); font->hb_script = scr; return 0; #else (void) font; (void) script; return -1; #endif } static int TTF_Size_Internal(TTF_Font *font, const char *text, const str_type_t str_type, int *w, int *h, int *xstart, int *ystart, int measure_width, int *extent, int *count) { int x = 0; int pos_x, pos_y; int minx = 0, maxx = 0; int miny = 0, maxy = 0; Uint8 *utf8_alloc = NULL; c_glyph *glyph; #if TTF_USE_HARFBUZZ hb_direction_t hb_direction; hb_script_t hb_script; hb_buffer_t *hb_buffer = NULL; unsigned int g; unsigned int glyph_count; hb_glyph_info_t *hb_glyph_info; hb_glyph_position_t *hb_glyph_position; int y = 0; int advance_if_bold = 0; #else size_t textlen; int skip_first = 1; FT_UInt prev_index = 0; FT_Pos prev_delta = 0; #endif int prev_advance = 0; /* Measurement mode */ int char_count = 0; int current_width = 0; TTF_CHECK_INITIALIZED(-1); TTF_CHECK_POINTER(font, -1); TTF_CHECK_POINTER(text, -1); /* Convert input string to default encoding UTF-8 */ if (str_type == STR_TEXT) { utf8_alloc = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); if (utf8_alloc == NULL) { SDL_OutOfMemory(); goto failure; } LATIN1_to_UTF8(text, utf8_alloc); text = (const char *)utf8_alloc; } else if (str_type == STR_UNICODE) { const Uint16 *text16 = (const Uint16 *) text; utf8_alloc = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text16)); if (utf8_alloc == NULL) { SDL_OutOfMemory(); goto failure; } UCS2_to_UTF8(text16, utf8_alloc); text = (const char *)utf8_alloc; } maxy = font->height; /* Reset buffer */ font->pos_len = 0; #if TTF_USE_HARFBUZZ /* Adjust for bold text */ if (TTF_HANDLE_STYLE_BOLD(font)) { advance_if_bold = F26Dot6(font->glyph_overhang); } /* Create a buffer for harfbuzz to use */ hb_buffer = hb_buffer_create(); if (hb_buffer == NULL) { TTF_SetError("Cannot create harfbuzz buffer"); goto failure; } hb_direction = font->hb_direction; hb_script = font->hb_script; if (hb_script == HB_SCRIPT_INVALID) { hb_script = g_hb_script; } if (hb_direction == HB_DIRECTION_INVALID) { hb_direction = g_hb_direction; } /* Set global configuration */ hb_buffer_set_direction(hb_buffer, hb_direction); hb_buffer_set_script(hb_buffer, hb_script); hb_buffer_guess_segment_properties(hb_buffer); /* Layout the text */ hb_buffer_add_utf8(hb_buffer, text, -1, 0, -1); hb_feature_t userfeatures[1]; userfeatures[0].tag = HB_TAG('k','e','r','n'); userfeatures[0].value = font->allow_kerning; userfeatures[0].start = HB_FEATURE_GLOBAL_START; userfeatures[0].end = HB_FEATURE_GLOBAL_END; hb_shape(font->hb_font, hb_buffer, userfeatures, 1); /* Get the result */ hb_glyph_info = hb_buffer_get_glyph_infos(hb_buffer, &glyph_count); hb_glyph_position = hb_buffer_get_glyph_positions(hb_buffer, &glyph_count); /* Load and render each character */ for (g = 0; g < glyph_count; g++) { FT_UInt idx = hb_glyph_info[g].codepoint; int x_advance = hb_glyph_position[g].x_advance; int y_advance = hb_glyph_position[g].y_advance; int x_offset = hb_glyph_position[g].x_offset; int y_offset = hb_glyph_position[g].y_offset; #else /* Load each character and sum it's bounding box */ textlen = SDL_strlen(text); while (textlen > 0) { int inc = 0; Uint32 c = UTF8_getch(text, textlen, &inc); FT_UInt idx = get_char_index(font, c); text += inc; textlen -= inc; if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { continue; } #endif if (Find_GlyphByIndex(font, idx, 0, 0, 0, 0, 0, 0, &glyph, NULL) < 0) { goto failure; } /* Realloc, if needed */ if (font->pos_len >= font->pos_max) { PosBuf_t *saved = font->pos_buf; font->pos_max *= 2; font->pos_buf = (PosBuf_t *)SDL_realloc(font->pos_buf, font->pos_max * sizeof (font->pos_buf[0])); if (font->pos_buf == NULL) { font->pos_max /= 2; font->pos_buf = saved; TTF_SetError("Out of memory"); goto failure; } } #if TTF_USE_HARFBUZZ /* Compute positions */ pos_x = x + x_offset; pos_y = y + F26Dot6(font->ascent) - y_offset; x += x_advance + advance_if_bold; y += y_advance; #else /* Compute positions */ x += prev_advance; prev_advance = glyph->advance; if (font->use_kerning) { if (prev_index && glyph->index) { FT_Vector delta; FT_Get_Kerning(font->face, prev_index, glyph->index, FT_KERNING_UNFITTED, &delta); x += delta.x; } prev_index = glyph->index; } /* FT SUBPIXEL : LCD_MODE_LIGHT_SUBPIXEL */ if (font->render_subpixel) { x += prev_delta; /* Increment by prev_glyph->lsb_delta - prev_glyph->rsb_delta; */ prev_delta = glyph->subpixel.lsb_minus_rsb; } else { /* FT KERNING_MODE_SMART: Use `lsb_delta' and `rsb_delta' to improve integer positioning of glyphs */ if (skip_first) { skip_first = 0; } else { if (prev_delta - glyph->kerning_smart.lsb_delta > 32 ) { x -= 64; } else if (prev_delta - glyph->kerning_smart.lsb_delta < -31 ) { x += 64; } } prev_delta = glyph->kerning_smart.rsb_delta; x = ((x + 32) & -64); /* ROUND() */ } /* Compute positions where to copy the glyph bitmap */ pos_x = x; pos_y = F26Dot6(font->ascent); #endif /* Store things for Render_Line() */ font->pos_buf[font->pos_len].x = pos_x; font->pos_buf[font->pos_len].y = pos_y; font->pos_buf[font->pos_len].index = idx; font->pos_len += 1; /* Compute previsionnal global bounding box */ pos_x = FT_FLOOR(pos_x) + glyph->sz_left; pos_y = FT_FLOOR(pos_y) - glyph->sz_top; minx = SDL_min(minx, pos_x); maxx = SDL_max(maxx, pos_x + glyph->sz_width); miny = SDL_min(miny, pos_y); maxy = SDL_max(maxy, pos_y + glyph->sz_rows); /* Measurement mode */ if (measure_width) { int cw = SDL_max(maxx, FT_FLOOR(x + prev_advance)) - minx; cw += 2 * font->outline_val; if (cw <= measure_width) { current_width = cw; char_count += 1; } if (cw >= measure_width) { break; } } } /* Allows to render a string with only one space (bug 4344). */ maxx = SDL_max(maxx, FT_FLOOR(x + prev_advance)); /* Initial x start position: often 0, except when a glyph would be written at * a negative position. In this case an offset is needed for the whole line. */ if (xstart) { *xstart = (minx < 0)? -minx : 0; *xstart += font->outline_val; if (font->render_sdf) { *xstart += 8; /* Default 'spread' property */ } } /* Initial y start: compensation for a negative y offset */ if (ystart) { *ystart = (miny < 0)? -miny : 0; *ystart += font->outline_val; if (font->render_sdf) { *ystart += 8; /* Default 'spread' property */ } } /* Fill the bounds rectangle */ if (w) { *w = (maxx - minx); if (*w != 0) { *w += 2 * font->outline_val; } } if (h) { *h = (maxy - miny); *h += 2 * font->outline_val; } /* Measurement mode */ if (measure_width) { if (extent) { *extent = current_width; } if (count) { #if TTF_USE_HARFBUZZ if (char_count == glyph_count) { /* The higher level code doesn't know about ligatures, * so if we've covered all the glyphs, report the full * string length. * * If we have to line wrap somewhere in the middle, we * might be off by the number of ligatures, but there * isn't an easy way around that without using hb_buffer * at that level instead. */ *count = (int)SDL_utf8strlen(text); } else #endif *count = char_count; } } #if TTF_USE_HARFBUZZ if (hb_buffer) { hb_buffer_destroy(hb_buffer); } #endif if (utf8_alloc) { SDL_stack_free(utf8_alloc); } return 0; failure: #if TTF_USE_HARFBUZZ if (hb_buffer) { hb_buffer_destroy(hb_buffer); } #endif if (utf8_alloc) { SDL_stack_free(utf8_alloc); } return -1; } int TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h) { return TTF_Size_Internal(font, text, STR_TEXT, w, h, NULL, NULL, NO_MEASUREMENT); } int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h) { return TTF_Size_Internal(font, text, STR_UTF8, w, h, NULL, NULL, NO_MEASUREMENT); } int TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h) { return TTF_Size_Internal(font, (const char *)text, STR_UNICODE, w, h, NULL, NULL, NO_MEASUREMENT); } int TTF_MeasureText(TTF_Font *font, const char *text, int width, int *extent, int *count) { return TTF_Size_Internal(font, text, STR_TEXT, NULL, NULL, NULL, NULL, width, extent, count); } int TTF_MeasureUTF8(TTF_Font *font, const char *text, int width, int *extent, int *count) { return TTF_Size_Internal(font, text, STR_UTF8, NULL, NULL, NULL, NULL, width, extent, count); } int TTF_MeasureUNICODE(TTF_Font *font, const Uint16 *text, int width, int *extent, int *count) { return TTF_Size_Internal(font, (const char *)text, STR_UNICODE, NULL, NULL, NULL, NULL, width, extent, count); } static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, const str_type_t str_type, SDL_Color fg, SDL_Color bg, const render_mode_t render_mode) { Uint32 color; int xstart, ystart, width, height; SDL_Surface *textbuf = NULL; Uint8 *utf8_alloc = NULL; TTF_CHECK_INITIALIZED(NULL); TTF_CHECK_POINTER(font, NULL); TTF_CHECK_POINTER(text, NULL); if (render_mode == RENDER_LCD && !FT_IS_SCALABLE(font->face)) { TTF_SetError("LCD rendering is not available for non-scalable font"); goto failure; } /* Convert input string to default encoding UTF-8 */ if (str_type == STR_TEXT) { utf8_alloc = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); if (utf8_alloc == NULL) { SDL_OutOfMemory(); goto failure; } LATIN1_to_UTF8(text, utf8_alloc); text = (const char *)utf8_alloc; } else if (str_type == STR_UNICODE) { const Uint16 *text16 = (const Uint16 *) text; utf8_alloc = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text16)); if (utf8_alloc == NULL) { SDL_OutOfMemory(); goto failure; } UCS2_to_UTF8(text16, utf8_alloc); text = (const char *)utf8_alloc; } #if TTF_USE_SDF /* Invalid cache if we were using SDF */ if (render_mode != RENDER_BLENDED) { if (font->render_sdf) { font->render_sdf = 0; Flush_Cache(font); } } #endif /* Get the dimensions of the text surface */ if ((TTF_Size_Internal(font, text, STR_UTF8, &width, &height, &xstart, &ystart, NO_MEASUREMENT) < 0) || !width) { TTF_SetError("Text has zero width"); goto failure; } /* Support alpha blending */ fg.a = fg.a ? fg.a : SDL_ALPHA_OPAQUE; bg.a = bg.a ? bg.a : SDL_ALPHA_OPAQUE; /* Create surface for rendering */ if (render_mode == RENDER_SOLID) { textbuf = Create_Surface_Solid(width, height, fg, &color); } else if (render_mode == RENDER_SHADED) { textbuf = Create_Surface_Shaded(width, height, fg, bg, &color); } else if (render_mode == RENDER_BLENDED) { textbuf = Create_Surface_Blended(width, height, fg, &color); } else { /* render_mode == RENDER_LCD */ textbuf = Create_Surface_LCD(width, height, fg, bg, &color); } if (textbuf == NULL) { goto failure; } /* Render one text line to textbuf at (xstart, ystart) */ if (Render_Line(render_mode, font->render_subpixel, font, textbuf, xstart, ystart, fg) < 0) { goto failure; } /* Apply underline or strikethrough style, if needed */ if (TTF_HANDLE_STYLE_UNDERLINE(font)) { Draw_Line(font, textbuf, 0, ystart + font->underline_top_row, width, font->line_thickness, color, render_mode); } if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { Draw_Line(font, textbuf, 0, ystart + font->strikethrough_top_row, width, font->line_thickness, color, render_mode); } if (utf8_alloc) { SDL_stack_free(utf8_alloc); } return textbuf; failure: if (textbuf) { SDL_FreeSurface(textbuf); } if (utf8_alloc) { SDL_stack_free(utf8_alloc); } return NULL; } SDL_Surface* TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg) { return TTF_Render_Internal(font, text, STR_TEXT, fg, fg /* unused */, RENDER_SOLID); } SDL_Surface* TTF_RenderUTF8_Solid(TTF_Font *font, const char *text, SDL_Color fg) { return TTF_Render_Internal(font, text, STR_UTF8, fg, fg /* unused */, RENDER_SOLID); } SDL_Surface* TTF_RenderUNICODE_Solid(TTF_Font *font, const Uint16 *text, SDL_Color fg) { return TTF_Render_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, RENDER_SOLID); } SDL_Surface* TTF_RenderGlyph_Solid(TTF_Font *font, Uint16 ch, SDL_Color fg) { return TTF_RenderGlyph32_Solid(font, ch, fg); } SDL_Surface* TTF_RenderGlyph32_Solid(TTF_Font *font, Uint32 ch, SDL_Color fg) { Uint8 utf8[7]; TTF_CHECK_POINTER(font, NULL); if (!Char_to_UTF8(ch, utf8)) { return NULL; } return TTF_RenderUTF8_Solid(font, (char *)utf8, fg); } SDL_Surface* TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg) { return TTF_Render_Internal(font, text, STR_TEXT, fg, bg, RENDER_SHADED); } SDL_Surface* TTF_RenderUTF8_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg) { return TTF_Render_Internal(font, text, STR_UTF8, fg, bg, RENDER_SHADED); } SDL_Surface* TTF_RenderUNICODE_Shaded(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg) { return TTF_Render_Internal(font, (const char *)text, STR_UNICODE, fg, bg, RENDER_SHADED); } SDL_Surface* TTF_RenderGlyph_Shaded(TTF_Font *font, Uint16 ch, SDL_Color fg, SDL_Color bg) { return TTF_RenderGlyph32_Shaded(font, ch, fg, bg); } SDL_Surface* TTF_RenderGlyph32_Shaded(TTF_Font *font, Uint32 ch, SDL_Color fg, SDL_Color bg) { Uint8 utf8[7]; TTF_CHECK_POINTER(font, NULL); if (!Char_to_UTF8(ch, utf8)) { return NULL; } return TTF_RenderUTF8_Shaded(font, (char *)utf8, fg, bg); } SDL_Surface* TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg) { return TTF_Render_Internal(font, text, STR_TEXT, fg, fg /* unused */, RENDER_BLENDED); } SDL_Surface* TTF_RenderUTF8_Blended(TTF_Font *font, const char *text, SDL_Color fg) { return TTF_Render_Internal(font, text, STR_UTF8, fg, fg /* unused */, RENDER_BLENDED); } SDL_Surface* TTF_RenderUNICODE_Blended(TTF_Font *font, const Uint16 *text, SDL_Color fg) { return TTF_Render_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, RENDER_BLENDED); } SDL_Surface* TTF_RenderText_LCD(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg) { return TTF_Render_Internal(font, text, STR_TEXT, fg, bg, RENDER_LCD); } SDL_Surface* TTF_RenderUTF8_LCD(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg) { return TTF_Render_Internal(font, text, STR_UTF8, fg, bg, RENDER_LCD); } SDL_Surface* TTF_RenderUNICODE_LCD(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg) { return TTF_Render_Internal(font, (const char *)text, STR_UNICODE, fg, bg, RENDER_LCD); } SDL_Surface* TTF_RenderGlyph_LCD(TTF_Font *font, Uint16 ch, SDL_Color fg, SDL_Color bg) { return TTF_RenderGlyph32_LCD(font, ch, fg, bg); } SDL_Surface* TTF_RenderGlyph32_LCD(TTF_Font *font, Uint32 ch, SDL_Color fg, SDL_Color bg) { Uint8 utf8[7]; TTF_CHECK_POINTER(font, NULL); if (!Char_to_UTF8(ch, utf8)) { return NULL; } return TTF_RenderUTF8_LCD(font, (char *)utf8, fg, bg); } static SDL_bool CharacterIsDelimiter(Uint32 c) { if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { return SDL_TRUE; } return SDL_FALSE; } static SDL_bool CharacterIsNewLine(Uint32 c) { if (c == '\n') { return SDL_TRUE; } return SDL_FALSE; } static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, const str_type_t str_type, SDL_Color fg, SDL_Color bg, Uint32 wrapLength, const render_mode_t render_mode) { Uint32 color; int width, height; SDL_Surface *textbuf = NULL; Uint8 *utf8_alloc = NULL; int i, numLines, rowHeight, lineskip; char **strLines = NULL, *text_cpy; TTF_CHECK_INITIALIZED(NULL); TTF_CHECK_POINTER(font, NULL); TTF_CHECK_POINTER(text, NULL); if (render_mode == RENDER_LCD && !FT_IS_SCALABLE(font->face)) { TTF_SetError("LCD rendering is not available for non-scalable font"); goto failure; } /* Convert input string to default encoding UTF-8 */ if (str_type == STR_TEXT) { utf8_alloc = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); if (utf8_alloc == NULL) { SDL_OutOfMemory(); goto failure; } LATIN1_to_UTF8(text, utf8_alloc); text_cpy = (char *)utf8_alloc; } else if (str_type == STR_UNICODE) { const Uint16 *text16 = (const Uint16 *) text; utf8_alloc = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text16)); if (utf8_alloc == NULL) { SDL_OutOfMemory(); goto failure; } UCS2_to_UTF8(text16, utf8_alloc); text_cpy = (char *)utf8_alloc; } else { /* Use a copy anyway */ size_t str_len = SDL_strlen(text); utf8_alloc = SDL_stack_alloc(Uint8, str_len + 1); if (utf8_alloc == NULL) { SDL_OutOfMemory(); goto failure; } SDL_memcpy(utf8_alloc, text, str_len + 1); text_cpy = (char *)utf8_alloc; } #if TTF_USE_SDF /* Invalid cache if we were using SDF */ if (render_mode != RENDER_BLENDED) { if (font->render_sdf) { font->render_sdf = 0; Flush_Cache(font); } } #endif /* Get the dimensions of the text surface */ if ((TTF_SizeUTF8(font, text_cpy, &width, &height) < 0) || !width) { TTF_SetError("Text has zero width"); goto failure; } /* wrapLength is unsigned, but don't allow negative values */ if ((int)wrapLength < 0) { TTF_SetError("Invalid parameter 'wrapLength'"); goto failure; } numLines = 1; if (*text_cpy) { int maxNumLines = 0; size_t textlen = SDL_strlen(text_cpy); numLines = 0; do { int extent = 0, max_count = 0, char_count = 0; size_t save_textlen = (size_t)(-1); char *save_text = NULL; if (numLines >= maxNumLines) { char **saved = strLines; if (wrapLength == 0) { maxNumLines += 32; } else { maxNumLines += (width / wrapLength) + 1; } strLines = (char **)SDL_realloc(strLines, maxNumLines * sizeof (*strLines)); if (strLines == NULL) { strLines = saved; SDL_OutOfMemory(); goto failure; } } strLines[numLines++] = text_cpy; if (TTF_MeasureUTF8(font, text_cpy, wrapLength, &extent, &max_count) < 0) { TTF_SetError("Error measure text"); goto failure; } if (wrapLength != 0) { if (max_count == 0) { max_count = 1; } } while (textlen > 0) { int inc = 0; int is_delim; Uint32 c = UTF8_getch(text_cpy, textlen, &inc); text_cpy += inc; textlen -= inc; if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { continue; } char_count += 1; /* With wrapLength == 0, normal text rendering but newline aware */ is_delim = (wrapLength > 0) ? CharacterIsDelimiter(c) : CharacterIsNewLine(c); /* Record last delimiter position */ if (is_delim) { save_textlen = textlen; save_text = text_cpy; /* Break, if new line */ if (c == '\n' || c == '\r') { *(text_cpy - 1) = '\0'; break; } } /* Break, if reach the limit */ if (char_count == max_count) { break; } } /* Cut at last delimiter/new lines, otherwise in the middle of the word */ if (save_text && textlen) { text_cpy = save_text; textlen = save_textlen; } } while (textlen > 0); } lineskip = TTF_FontLineSkip(font); rowHeight = SDL_max(height, lineskip); if (wrapLength == 0) { /* Find the max of all line lengths */ if (numLines > 1) { width = 0; for (i = 0; i < numLines; i++) { char save_c = 0; int w, h; /* Add end-of-line */ if (strLines) { text = strLines[i]; if (i + 1 < numLines) { save_c = strLines[i + 1][0]; strLines[i + 1][0] = '\0'; } } if (TTF_SizeUTF8(font, text, &w, &h) == 0) { width = SDL_max(w, width); } /* Remove end-of-line */ if (strLines) { if (i + 1 < numLines) { strLines[i + 1][0] = save_c; } } } /* In case there are all newlines */ width = SDL_max(width, 1); } } else { if (numLines <= 1 && font->horizontal_align == TTF_WRAPPED_ALIGN_LEFT) { /* Don't go above wrapLength if you have only 1 line which hasn't been cut */ width = SDL_min((int)wrapLength, width); } else { width = wrapLength; } } height = rowHeight + lineskip * (numLines - 1); /* Support alpha blending */ fg.a = fg.a ? fg.a : SDL_ALPHA_OPAQUE; bg.a = bg.a ? bg.a : SDL_ALPHA_OPAQUE; /* Create surface for rendering */ if (render_mode == RENDER_SOLID) { textbuf = Create_Surface_Solid(width, height, fg, &color); } else if (render_mode == RENDER_SHADED) { textbuf = Create_Surface_Shaded(width, height, fg, bg, &color); } else if (render_mode == RENDER_BLENDED) { textbuf = Create_Surface_Blended(width, height, fg, &color); } else { /* render_mode == RENDER_LCD */ textbuf = Create_Surface_LCD(width, height, fg, bg, &color); } if (textbuf == NULL) { goto failure; } /* Render each line */ for (i = 0; i < numLines; i++) { int xstart, ystart, line_width, xoffset; char save_c = 0; /* Add end-of-line */ if (strLines) { text = strLines[i]; if (i + 1 < numLines) { save_c = strLines[i + 1][0]; strLines[i + 1][0] = '\0'; } } /* Initialize xstart, ystart and compute positions */ if (TTF_Size_Internal(font, text, STR_UTF8, &line_width, NULL, &xstart, &ystart, NO_MEASUREMENT) < 0) { goto failure; } /* Move to i-th line */ ystart += i * lineskip; /* Control left/right/center align of each bit of text */ if (font->horizontal_align == TTF_WRAPPED_ALIGN_RIGHT) { xoffset = width - line_width; } else if (font->horizontal_align == TTF_WRAPPED_ALIGN_CENTER) { xoffset = width / 2 - line_width / 2; } else { xoffset = 0; } xoffset = SDL_max(0, xoffset); /* Render one text line to textbuf at (xstart, ystart) */ if (Render_Line(render_mode, font->render_subpixel, font, textbuf, xstart + xoffset, ystart, fg) < 0) { goto failure; } /* Apply underline or strikethrough style, if needed */ if (TTF_HANDLE_STYLE_UNDERLINE(font)) { Draw_Line(font, textbuf, xoffset, ystart + font->underline_top_row, line_width, font->line_thickness, color, render_mode); } if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) { Draw_Line(font, textbuf, xoffset, ystart + font->strikethrough_top_row, line_width, font->line_thickness, color, render_mode); } /* Remove end-of-line */ if (strLines) { if (i + 1 < numLines) { strLines[i + 1][0] = save_c; } } } if (strLines) { SDL_free(strLines); } if (utf8_alloc) { SDL_stack_free(utf8_alloc); } return textbuf; failure: if (textbuf) { SDL_FreeSurface(textbuf); } if (strLines) { SDL_free(strLines); } if (utf8_alloc) { SDL_stack_free(utf8_alloc); } return NULL; } SDL_Surface* TTF_RenderText_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_TEXT, fg, fg /* unused */, wrapLength, RENDER_SOLID); } SDL_Surface* TTF_RenderUTF8_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_UTF8, fg, fg /* unused */, wrapLength, RENDER_SOLID); } SDL_Surface* TTF_RenderUNICODE_Solid_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, wrapLength, RENDER_SOLID); } SDL_Surface* TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_TEXT, fg, bg, wrapLength, RENDER_SHADED); } SDL_Surface* TTF_RenderUTF8_Shaded_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_UTF8, fg, bg, wrapLength, RENDER_SHADED); } SDL_Surface* TTF_RenderUNICODE_Shaded_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, (const char *)text, STR_UNICODE, fg, bg, wrapLength, RENDER_SHADED); } SDL_Surface* TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_TEXT, fg, fg /* unused */, wrapLength, RENDER_BLENDED); } SDL_Surface* TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_UTF8, fg, fg /* unused */, wrapLength, RENDER_BLENDED); } SDL_Surface* TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, (const char *)text, STR_UNICODE, fg, fg /* unused */, wrapLength, RENDER_BLENDED); } SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_TEXT, fg, bg, wrapLength, RENDER_LCD); } SDL_Surface* TTF_RenderUTF8_LCD_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, text, STR_UTF8, fg, bg, wrapLength, RENDER_LCD); } SDL_Surface* TTF_RenderUNICODE_LCD_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength) { return TTF_Render_Wrapped_Internal(font, (const char *)text, STR_UNICODE, fg, bg, wrapLength, RENDER_LCD); } SDL_Surface* TTF_RenderGlyph_Blended(TTF_Font *font, Uint16 ch, SDL_Color fg) { return TTF_RenderGlyph32_Blended(font, ch, fg); } SDL_Surface* TTF_RenderGlyph32_Blended(TTF_Font *font, Uint32 ch, SDL_Color fg) { Uint8 utf8[7]; TTF_CHECK_POINTER(font, NULL); if (!Char_to_UTF8(ch, utf8)) { return NULL; } return TTF_RenderUTF8_Blended(font, (char *)utf8, fg); } void TTF_SetFontStyle(TTF_Font *font, int style) { int prev_style; long face_style; TTF_CHECK_POINTER(font,); prev_style = font->style; face_style = font->face->style_flags; /* Don't add a style if already in the font, SDL_ttf doesn't need to handle them */ if (face_style & FT_STYLE_FLAG_BOLD) { style &= ~TTF_STYLE_BOLD; } if (face_style & FT_STYLE_FLAG_ITALIC) { style &= ~TTF_STYLE_ITALIC; } font->style = style; TTF_initFontMetrics(font); /* Flush the cache if the style has changed. * Ignore styles which do not impact glyph drawning. */ if ((font->style | TTF_STYLE_NO_GLYPH_CHANGE) != (prev_style | TTF_STYLE_NO_GLYPH_CHANGE)) { Flush_Cache(font); } } int TTF_GetFontStyle(const TTF_Font *font) { int style; long face_style; TTF_CHECK_POINTER(font, -1); style = font->style; face_style = font->face->style_flags; /* Add the style already in the font */ if (face_style & FT_STYLE_FLAG_BOLD) { style |= TTF_STYLE_BOLD; } if (face_style & FT_STYLE_FLAG_ITALIC) { style |= TTF_STYLE_ITALIC; } return style; } void TTF_SetFontOutline(TTF_Font *font, int outline) { TTF_CHECK_POINTER(font,); font->outline_val = SDL_max(0, outline); TTF_initFontMetrics(font); Flush_Cache(font); } int TTF_GetFontOutline(const TTF_Font *font) { TTF_CHECK_POINTER(font, -1); return font->outline_val; } void TTF_SetFontHinting(TTF_Font *font, int hinting) { TTF_CHECK_POINTER(font,); if (hinting == TTF_HINTING_LIGHT || hinting == TTF_HINTING_LIGHT_SUBPIXEL) { font->ft_load_target = FT_LOAD_TARGET_LIGHT; } else if (hinting == TTF_HINTING_MONO) { font->ft_load_target = FT_LOAD_TARGET_MONO; } else if (hinting == TTF_HINTING_NONE) { font->ft_load_target = FT_LOAD_NO_HINTING; } else { font->ft_load_target = FT_LOAD_TARGET_NORMAL; } font->render_subpixel = (hinting == TTF_HINTING_LIGHT_SUBPIXEL) ? 1 : 0; #if TTF_USE_HARFBUZZ /* update flag for HB */ hb_ft_font_set_load_flags(font->hb_font, FT_LOAD_DEFAULT | font->ft_load_target); #endif Flush_Cache(font); } int TTF_GetFontHinting(const TTF_Font *font) { TTF_CHECK_POINTER(font, -1); if (font->ft_load_target == FT_LOAD_TARGET_LIGHT) { if (font->render_subpixel == 0) { return TTF_HINTING_LIGHT; } else { return TTF_HINTING_LIGHT_SUBPIXEL; } } else if (font->ft_load_target == FT_LOAD_TARGET_MONO) { return TTF_HINTING_MONO; } else if (font->ft_load_target == FT_LOAD_NO_HINTING) { return TTF_HINTING_NONE; } return TTF_HINTING_NORMAL; } int TTF_SetFontSDF(TTF_Font *font, SDL_bool on_off) { TTF_CHECK_POINTER(font, -1); #if TTF_USE_SDF font->render_sdf = on_off; Flush_Cache(font); return 0; #else TTF_SetError("SDL_ttf compiled without SDF support"); return -1; #endif } SDL_bool TTF_GetFontSDF(const TTF_Font *font) { TTF_CHECK_POINTER(font, SDL_FALSE); return font->render_sdf; } void TTF_SetFontWrappedAlign(TTF_Font *font, int align) { TTF_CHECK_POINTER(font,); /* input not checked, unknown values assumed to be TTF_WRAPPED_ALIGN_LEFT */ if (align == TTF_WRAPPED_ALIGN_CENTER) { font->horizontal_align = TTF_WRAPPED_ALIGN_CENTER; } else if (align == TTF_WRAPPED_ALIGN_RIGHT) { font->horizontal_align = TTF_WRAPPED_ALIGN_RIGHT; } else { font->horizontal_align = TTF_WRAPPED_ALIGN_LEFT; } } int TTF_GetFontWrappedAlign(const TTF_Font *font) { TTF_CHECK_POINTER(font,-1); return font->horizontal_align; } void TTF_Quit(void) { if (TTF_initialized) { if (--TTF_initialized == 0) { FT_Done_FreeType(library); library = NULL; } } } int TTF_WasInit(void) { return TTF_initialized; } /* don't use this function. It's just here for binary compatibility. */ int TTF_GetFontKerningSize(TTF_Font *font, int prev_index, int index) { FT_Vector delta; TTF_CHECK_POINTER(font, -1); FT_Get_Kerning(font->face, (FT_UInt)prev_index, (FT_UInt)index, FT_KERNING_DEFAULT, &delta); return (int)(delta.x >> 6); } int TTF_GetFontKerningSizeGlyphs(TTF_Font *font, Uint16 previous_ch, Uint16 ch) { return TTF_GetFontKerningSizeGlyphs32(font, previous_ch, ch); } int TTF_GetFontKerningSizeGlyphs32(TTF_Font *font, Uint32 previous_ch, Uint32 ch) { FT_Error error; c_glyph *prev_glyph, *glyph; FT_Vector delta; TTF_CHECK_POINTER(font, -1); if (ch == UNICODE_BOM_NATIVE || ch == UNICODE_BOM_SWAPPED) { return 0; } if (previous_ch == UNICODE_BOM_NATIVE || previous_ch == UNICODE_BOM_SWAPPED) { return 0; } if (Find_GlyphMetrics(font, ch, &glyph) < 0) { return -1; } if (Find_GlyphMetrics(font, previous_ch, &prev_glyph) < 0) { return -1; } error = FT_Get_Kerning(font->face, prev_glyph->index, glyph->index, FT_KERNING_DEFAULT, &delta); if (error) { TTF_SetFTError("Couldn't get glyph kerning", error); return -1; } return (int)(delta.x >> 6); } /* vi: set ts=4 sw=4 expandtab: */ SDL2_ttf-2.24.0/SDL_ttf.h0000664000000000000000000025413214735274341011601 0ustar00/* SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts Copyright (C) 2001-2025 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. */ /** * \file SDL_ttf.h * * Header file for SDL_ttf library * * This library is a wrapper around the excellent FreeType 2.0 library, * available at: https://www.freetype.org/ * * Note: In many places, SDL_ttf will say "glyph" when it means "code point." * Unicode is hard, we learn as we go, and we apologize for adding to the * confusion. * */ #ifndef SDL_TTF_H_ #define SDL_TTF_H_ #include "SDL.h" #include "begin_code.h" /* Set up for C function definitions, even when using C++ */ #ifdef __cplusplus extern "C" { #endif /** * Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL */ #define SDL_TTF_MAJOR_VERSION 2 #define SDL_TTF_MINOR_VERSION 24 #define SDL_TTF_PATCHLEVEL 0 /** * This macro can be used to fill a version structure with the compile-time * version of the SDL_ttf library. */ #define SDL_TTF_VERSION(X) \ { \ (X)->major = SDL_TTF_MAJOR_VERSION; \ (X)->minor = SDL_TTF_MINOR_VERSION; \ (X)->patch = SDL_TTF_PATCHLEVEL; \ } /** * Backwards compatibility */ #define TTF_MAJOR_VERSION SDL_TTF_MAJOR_VERSION #define TTF_MINOR_VERSION SDL_TTF_MINOR_VERSION #define TTF_PATCHLEVEL SDL_TTF_PATCHLEVEL #define TTF_VERSION(X) SDL_TTF_VERSION(X) #if SDL_TTF_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3 /** * This is the version number macro for the current SDL_ttf version. * * In versions higher than 2.9.0, the minor version overflows into the * thousands digit: for example, 2.23.0 is encoded as 4300. This macro will * not be available in SDL 3.x or SDL_ttf 3.x. */ #define SDL_TTF_COMPILEDVERSION \ SDL_VERSIONNUM(SDL_TTF_MAJOR_VERSION, SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL) #endif /* SDL_TTF_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3 */ /** * This macro will evaluate to true if compiled with SDL_ttf at least X.Y.Z. */ #define SDL_TTF_VERSION_ATLEAST(X, Y, Z) \ ((SDL_TTF_MAJOR_VERSION >= X) && \ (SDL_TTF_MAJOR_VERSION > X || SDL_TTF_MINOR_VERSION >= Y) && \ (SDL_TTF_MAJOR_VERSION > X || SDL_TTF_MINOR_VERSION > Y || SDL_TTF_PATCHLEVEL >= Z)) /* Make sure this is defined (only available in newer SDL versions) */ #ifndef SDL_DEPRECATED #define SDL_DEPRECATED #endif /** * Query the version of SDL_ttf that the program is linked against. * * This function gets the version of the dynamically linked SDL_ttf library. * This is separate from the SDL_TTF_VERSION() macro, which tells you what * version of the SDL_ttf headers you compiled against. * * This returns static internal data; do not free or modify it! * * \returns a pointer to the version information. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC const SDL_version * SDLCALL TTF_Linked_Version(void); /** * Query the version of the FreeType library in use. * * TTF_Init() should be called before calling this function. * * \param major to be filled in with the major version number. Can be NULL. * \param minor to be filled in with the minor version number. Can be NULL. * \param patch to be filled in with the param version number. Can be NULL. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_Init */ extern DECLSPEC void SDLCALL TTF_GetFreeTypeVersion(int *major, int *minor, int *patch); /** * Query the version of the HarfBuzz library in use. * * If HarfBuzz is not available, the version reported is 0.0.0. * * \param major to be filled in with the major version number. Can be NULL. * \param minor to be filled in with the minor version number. Can be NULL. * \param patch to be filled in with the param version number. Can be NULL. * * \since This function is available since SDL_ttf 2.0.18. */ extern DECLSPEC void SDLCALL TTF_GetHarfBuzzVersion(int *major, int *minor, int *patch); /** * ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */ #define UNICODE_BOM_NATIVE 0xFEFF #define UNICODE_BOM_SWAPPED 0xFFFE /** * Tell SDL_ttf whether UNICODE text is generally byteswapped. * * A UNICODE BOM character in a string will override this setting for the * remainder of that string. * * \param swapped boolean to indicate whether text is byteswapped. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC void SDLCALL TTF_ByteSwappedUNICODE(SDL_bool swapped); /** * The internal structure containing font information. * * Opaque data! */ typedef struct TTF_Font TTF_Font; /** * Initialize SDL_ttf. * * You must successfully call this function before it is safe to call any * other function in this library, with one exception: a human-readable error * message can be retrieved from TTF_GetError() if this function fails. * * SDL must be initialized before calls to functions in this library, because * this library uses utility functions from the SDL library. * * It is safe to call this more than once; the library keeps a counter of init * calls, and decrements it on each call to TTF_Quit, so you must pair your * init and quit calls. * * \returns 0 on success, -1 on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_Quit * \sa TTF_WasInit */ extern DECLSPEC int SDLCALL TTF_Init(void); /** * Create a font from a file, using a specified point size. * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param file path to font file. * \param ptsize point size to use for the newly-opened font. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFont(const char *file, int ptsize); /** * Create a font from a file, using a specified face index. * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * Some fonts have multiple "faces" included. The index specifies which face * to use from the font file. Font files with only one face should specify * zero for the index. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param file path to font file. * \param ptsize point size to use for the newly-opened font. * \param index index of the face in the font file. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int ptsize, long index); /** * Create a font from an SDL_RWops, using a specified point size. * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * If `freesrc` is non-zero, the RWops will be automatically closed once the * font is closed. Otherwise you should close the RWops yourself after closing * the font. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param src an SDL_RWops to provide a font file's data. * \param freesrc non-zero to close the RWops when the font is closed, zero to * leave it open. * \param ptsize point size to use for the newly-opened font. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize); /** * Create a font from an SDL_RWops, using a specified face index. * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * If `freesrc` is non-zero, the RWops will be automatically closed once the * font is closed. Otherwise you should close the RWops yourself after closing * the font. * * Some fonts have multiple "faces" included. The index specifies which face * to use from the font file. Font files with only one face should specify * zero for the index. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param src an SDL_RWops to provide a font file's data. * \param freesrc non-zero to close the RWops when the font is closed, zero to * leave it open. * \param ptsize point size to use for the newly-opened font. * \param index index of the face in the font file. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index); /** * Create a font from a file, using target resolutions (in DPI). * * DPI scaling only applies to scalable fonts (e.g. TrueType). * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param file path to font file. * \param ptsize point size to use for the newly-opened font. * \param hdpi the target horizontal DPI. * \param vdpi the target vertical DPI. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontDPI(const char *file, int ptsize, unsigned int hdpi, unsigned int vdpi); /** * Create a font from a file, using target resolutions (in DPI). * * DPI scaling only applies to scalable fonts (e.g. TrueType). * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * Some fonts have multiple "faces" included. The index specifies which face * to use from the font file. Font files with only one face should specify * zero for the index. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param file path to font file. * \param ptsize point size to use for the newly-opened font. * \param index index of the face in the font file. * \param hdpi the target horizontal DPI. * \param vdpi the target vertical DPI. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexDPI(const char *file, int ptsize, long index, unsigned int hdpi, unsigned int vdpi); /** * Opens a font from an SDL_RWops with target resolutions (in DPI). * * DPI scaling only applies to scalable fonts (e.g. TrueType). * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * If `freesrc` is non-zero, the RWops will be automatically closed once the * font is closed. Otherwise you should close the RWops yourself after closing * the font. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param src an SDL_RWops to provide a font file's data. * \param freesrc non-zero to close the RWops when the font is closed, zero to * leave it open. * \param ptsize point size to use for the newly-opened font. * \param hdpi the target horizontal DPI. * \param vdpi the target vertical DPI. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontDPIRW(SDL_RWops *src, int freesrc, int ptsize, unsigned int hdpi, unsigned int vdpi); /** * Opens a font from an SDL_RWops with target resolutions (in DPI). * * DPI scaling only applies to scalable fonts (e.g. TrueType). * * Some .fon fonts will have several sizes embedded in the file, so the point * size becomes the index of choosing which size. If the value is too high, * the last indexed size will be the default. * * If `freesrc` is non-zero, the RWops will be automatically closed once the * font is closed. Otherwise you should close the RWops yourself after closing * the font. * * Some fonts have multiple "faces" included. The index specifies which face * to use from the font file. Font files with only one face should specify * zero for the index. * * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it. * * \param src an SDL_RWops to provide a font file's data. * \param freesrc non-zero to close the RWops when the font is closed, zero to * leave it open. * \param ptsize point size to use for the newly-opened font. * \param index index of the face in the font file. * \param hdpi the target horizontal DPI. * \param vdpi the target vertical DPI. * \returns a valid TTF_Font, or NULL on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_CloseFont */ extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexDPIRW(SDL_RWops *src, int freesrc, int ptsize, long index, unsigned int hdpi, unsigned int vdpi); /** * Set a font's size dynamically. * * This clears already-generated glyphs, if any, from the cache. * * \param font the font to resize. * \param ptsize the new point size. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.18. */ extern DECLSPEC int SDLCALL TTF_SetFontSize(TTF_Font *font, int ptsize); /** * Set font size dynamically with target resolutions (in DPI). * * This clears already-generated glyphs, if any, from the cache. * * \param font the font to resize. * \param ptsize the new point size. * \param hdpi the target horizontal DPI. * \param vdpi the target vertical DPI. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.18. */ extern DECLSPEC int SDLCALL TTF_SetFontSizeDPI(TTF_Font *font, int ptsize, unsigned int hdpi, unsigned int vdpi); /** * Font style flags */ #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 /** * Query a font's current style. * * The font styles are a set of bit flags, OR'd together: * * - `TTF_STYLE_NORMAL` (is zero) * - `TTF_STYLE_BOLD` * - `TTF_STYLE_ITALIC` * - `TTF_STYLE_UNDERLINE` * - `TTF_STYLE_STRIKETHROUGH` * * \param font the font to query. * \returns the current font style, as a set of bit flags. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_SetFontStyle */ extern DECLSPEC int SDLCALL TTF_GetFontStyle(const TTF_Font *font); /** * Set a font's current style. * * Setting the style clears already-generated glyphs, if any, from the cache. * * The font styles are a set of bit flags, OR'd together: * * - `TTF_STYLE_NORMAL` (is zero) * - `TTF_STYLE_BOLD` * - `TTF_STYLE_ITALIC` * - `TTF_STYLE_UNDERLINE` * - `TTF_STYLE_STRIKETHROUGH` * * \param font the font to set a new style on. * \param style the new style values to set, OR'd together. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_GetFontStyle */ extern DECLSPEC void SDLCALL TTF_SetFontStyle(TTF_Font *font, int style); /** * Query a font's current outline. * * \param font the font to query. * \returns the font's current outline value. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_SetFontOutline */ extern DECLSPEC int SDLCALL TTF_GetFontOutline(const TTF_Font *font); /** * Set a font's current outline. * * \param font the font to set a new outline on. * \param outline positive outline value, 0 to default. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_GetFontOutline */ extern DECLSPEC void SDLCALL TTF_SetFontOutline(TTF_Font *font, int outline); /** * Hinting flags */ #define TTF_HINTING_NORMAL 0 #define TTF_HINTING_LIGHT 1 #define TTF_HINTING_MONO 2 #define TTF_HINTING_NONE 3 #define TTF_HINTING_LIGHT_SUBPIXEL 4 /** * Query a font's current FreeType hinter setting. * * The hinter setting is a single value: * * - `TTF_HINTING_NORMAL` * - `TTF_HINTING_LIGHT` * - `TTF_HINTING_MONO` * - `TTF_HINTING_NONE` * - `TTF_HINTING_LIGHT_SUBPIXEL` (available in SDL_ttf 2.0.18 and later) * * \param font the font to query. * \returns the font's current hinter value. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_SetFontHinting */ extern DECLSPEC int SDLCALL TTF_GetFontHinting(const TTF_Font *font); /** * Set a font's current hinter setting. * * Setting it clears already-generated glyphs, if any, from the cache. * * The hinter setting is a single value: * * - `TTF_HINTING_NORMAL` * - `TTF_HINTING_LIGHT` * - `TTF_HINTING_MONO` * - `TTF_HINTING_NONE` * - `TTF_HINTING_LIGHT_SUBPIXEL` (available in SDL_ttf 2.0.18 and later) * * \param font the font to set a new hinter setting on. * \param hinting the new hinter setting. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_GetFontHinting */ extern DECLSPEC void SDLCALL TTF_SetFontHinting(TTF_Font *font, int hinting); /** * Special layout option for rendering wrapped text */ #define TTF_WRAPPED_ALIGN_LEFT 0 #define TTF_WRAPPED_ALIGN_CENTER 1 #define TTF_WRAPPED_ALIGN_RIGHT 2 /** * Query a font's current wrap alignment option. * * The wrap alignment option can be one of the following: * * - `TTF_WRAPPED_ALIGN_LEFT` * - `TTF_WRAPPED_ALIGN_CENTER` * - `TTF_WRAPPED_ALIGN_RIGHT` * * \param font the font to query. * \returns the font's current wrap alignment option. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_SetFontWrappedAlign */ extern DECLSPEC int SDLCALL TTF_GetFontWrappedAlign(const TTF_Font *font); /** * Set a font's current wrap alignment option. * * The wrap alignment option can be one of the following: * * - `TTF_WRAPPED_ALIGN_LEFT` * - `TTF_WRAPPED_ALIGN_CENTER` * - `TTF_WRAPPED_ALIGN_RIGHT` * * \param font the font to set a new wrap alignment option on. * \param align the new wrap alignment option. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_GetFontWrappedAlign */ extern DECLSPEC void SDLCALL TTF_SetFontWrappedAlign(TTF_Font *font, int align); /** * Query the total height of a font. * * This is usually equal to point size. * * \param font the font to query. * \returns the font's height. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC int SDLCALL TTF_FontHeight(const TTF_Font *font); /** * Query the offset from the baseline to the top of a font. * * This is a positive value, relative to the baseline. * * \param font the font to query. * \returns the font's ascent. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC int SDLCALL TTF_FontAscent(const TTF_Font *font); /** * Query the offset from the baseline to the bottom of a font. * * This is a negative value, relative to the baseline. * * \param font the font to query. * \returns the font's descent. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC int SDLCALL TTF_FontDescent(const TTF_Font *font); /** * Query the spacing between lines of text for a font. * * \param font the font to query. * \returns the font's line spacing. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC int SDLCALL TTF_FontLineSkip(const TTF_Font *font); /** * Set the spacing between lines of text for a font. * * \param font the font to modify. * \param lineskip the new line spacing for the font. * * \since This function is available since SDL_ttf 2.22.0. */ extern DECLSPEC void SDLCALL TTF_SetFontLineSkip(TTF_Font *font, int lineskip); /** * Query whether or not kerning is allowed for a font. * * \param font the font to query. * \returns non-zero if kerning is enabled, zero otherwise. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC int SDLCALL TTF_GetFontKerning(const TTF_Font *font); /** * Set if kerning is allowed for a font. * * Newly-opened fonts default to allowing kerning. This is generally a good * policy unless you have a strong reason to disable it, as it tends to * produce better rendering (with kerning disabled, some fonts might render * the word `kerning` as something that looks like `keming` for example). * * \param font the font to set kerning on. * \param allowed non-zero to allow kerning, zero to disallow. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC void SDLCALL TTF_SetFontKerning(TTF_Font *font, int allowed); /** * Query the number of faces of a font. * * \param font the font to query. * \returns the number of FreeType font faces. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC long SDLCALL TTF_FontFaces(const TTF_Font *font); /** * Query whether a font is fixed-width. * * A "fixed-width" font means all glyphs are the same width across; a * lowercase 'i' will be the same size across as a capital 'W', for example. * This is common for terminals and text editors, and other apps that treat * text as a grid. Most other things (WYSIWYG word processors, web pages, etc) * are more likely to not be fixed-width in most cases. * * \param font the font to query. * \returns non-zero if fixed-width, zero if not. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC int SDLCALL TTF_FontFaceIsFixedWidth(const TTF_Font *font); /** * Query a font's family name. * * This string is dictated by the contents of the font file. * * Note that the returned string is to internal storage, and should not be * modifed or free'd by the caller. The string becomes invalid, with the rest * of the font, when `font` is handed to TTF_CloseFont(). * * \param font the font to query. * \returns the font's family name. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC const char * SDLCALL TTF_FontFaceFamilyName(const TTF_Font *font); /** * Query a font's style name. * * This string is dictated by the contents of the font file. * * Note that the returned string is to internal storage, and should not be * modifed or free'd by the caller. The string becomes invalid, with the rest * of the font, when `font` is handed to TTF_CloseFont(). * * \param font the font to query. * \returns the font's style name. * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC const char * SDLCALL TTF_FontFaceStyleName(const TTF_Font *font); /** * Check whether a glyph is provided by the font for a 16-bit codepoint. * * Note that this version of the function takes a 16-bit character code, which * covers the Basic Multilingual Plane, but is insufficient to cover the * entire set of possible Unicode values, including emoji glyphs. You should * use TTF_GlyphIsProvided32() instead, which offers the same functionality * but takes a 32-bit codepoint instead. * * The only reason to use this function is that it was available since the * beginning of time, more or less. * * \param font the font to query. * \param ch the character code to check. * \returns non-zero if font provides a glyph for this character, zero if not. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_GlyphIsProvided32 */ extern DECLSPEC int SDLCALL TTF_GlyphIsProvided(TTF_Font *font, Uint16 ch); /** * Check whether a glyph is provided by the font for a 32-bit codepoint. * * This is the same as TTF_GlyphIsProvided(), but takes a 32-bit character * instead of 16-bit, and thus can query a larger range. If you are sure * you'll have an SDL_ttf that's version 2.0.18 or newer, there's no reason * not to use this function exclusively. * * \param font the font to query. * \param ch the character code to check. * \returns non-zero if font provides a glyph for this character, zero if not. * * \since This function is available since SDL_ttf 2.0.18. */ extern DECLSPEC int SDLCALL TTF_GlyphIsProvided32(TTF_Font *font, Uint32 ch); /** * Query the metrics (dimensions) of a font's 16-bit glyph. * * To understand what these metrics mean, here is a useful link: * * https://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html * * Note that this version of the function takes a 16-bit character code, which * covers the Basic Multilingual Plane, but is insufficient to cover the * entire set of possible Unicode values, including emoji glyphs. You should * use TTF_GlyphMetrics32() instead, which offers the same functionality but * takes a 32-bit codepoint instead. * * The only reason to use this function is that it was available since the * beginning of time, more or less. * * \param font the font to query. * \param ch the character code to check. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_GlyphMetrics32 */ extern DECLSPEC int SDLCALL TTF_GlyphMetrics(TTF_Font *font, Uint16 ch, int *minx, int *maxx, int *miny, int *maxy, int *advance); /** * Query the metrics (dimensions) of a font's 32-bit glyph. * * To understand what these metrics mean, here is a useful link: * * https://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html * * This is the same as TTF_GlyphMetrics(), but takes a 32-bit character * instead of 16-bit, and thus can query a larger range. If you are sure * you'll have an SDL_ttf that's version 2.0.18 or newer, there's no reason * not to use this function exclusively. * * \param font the font to query. * \param ch the character code to check. * * \since This function is available since SDL_ttf 2.0.18. */ extern DECLSPEC int SDLCALL TTF_GlyphMetrics32(TTF_Font *font, Uint32 ch, int *minx, int *maxx, int *miny, int *maxy, int *advance); /** * Calculate the dimensions of a rendered string of Latin1 text. * * This will report the width and height, in pixels, of the space that the * specified string will take to fully render. * * This does not need to render the string to do this calculation. * * You almost certainly want TTF_SizeUTF8() unless you're sure you have a * 1-byte Latin1 encoding. US ASCII characters will work with either function, * but most other Unicode characters packed into a `const char *` will need * UTF-8. * * \param font the font to query. * \param text text to calculate, in Latin1 encoding. * \param w will be filled with width, in pixels, on return. * \param h will be filled with height, in pixels, on return. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_SizeUTF8 * \sa TTF_SizeUNICODE */ extern DECLSPEC int SDLCALL TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h); /** * Calculate the dimensions of a rendered string of UTF-8 text. * * This will report the width and height, in pixels, of the space that the * specified string will take to fully render. * * This does not need to render the string to do this calculation. * * \param font the font to query. * \param text text to calculate, in UTF-8 encoding. * \param w will be filled with width, in pixels, on return. * \param h will be filled with height, in pixels, on return. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_SizeUNICODE */ extern DECLSPEC int SDLCALL TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h); /** * Calculate the dimensions of a rendered string of UCS-2 text. * * This will report the width and height, in pixels, of the space that the * specified string will take to fully render. * * This does not need to render the string to do this calculation. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * \param font the font to query. * \param text text to calculate, in UCS-2 encoding. * \param w will be filled with width, in pixels, on return. * \param h will be filled with height, in pixels, on return. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_SizeUTF8 */ extern DECLSPEC int SDLCALL TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h); /** * Calculate how much of a Latin1 string will fit in a given width. * * This reports the number of characters that can be rendered before reaching * `measure_width`. * * This does not need to render the string to do this calculation. * * You almost certainly want TTF_MeasureUTF8() unless you're sure you have a * 1-byte Latin1 encoding. US ASCII characters will work with either function, * but most other Unicode characters packed into a `const char *` will need * UTF-8. * * \param font the font to query. * \param text text to calculate, in Latin1 encoding. * \param measure_width maximum width, in pixels, available for the string. * \param extent on return, filled with latest calculated width. * \param count on return, filled with number of characters that can be * rendered. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_MeasureText * \sa TTF_MeasureUTF8 * \sa TTF_MeasureUNICODE */ extern DECLSPEC int SDLCALL TTF_MeasureText(TTF_Font *font, const char *text, int measure_width, int *extent, int *count); /** * Calculate how much of a UTF-8 string will fit in a given width. * * This reports the number of characters that can be rendered before reaching * `measure_width`. * * This does not need to render the string to do this calculation. * * \param font the font to query. * \param text text to calculate, in UTF-8 encoding. * \param measure_width maximum width, in pixels, available for the string. * \param extent on return, filled with latest calculated width. * \param count on return, filled with number of characters that can be * rendered. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_MeasureText * \sa TTF_MeasureUTF8 * \sa TTF_MeasureUNICODE */ extern DECLSPEC int SDLCALL TTF_MeasureUTF8(TTF_Font *font, const char *text, int measure_width, int *extent, int *count); /** * Calculate how much of a UCS-2 string will fit in a given width. * * This reports the number of characters that can be rendered before reaching * `measure_width`. * * This does not need to render the string to do this calculation. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * \param font the font to query. * \param text text to calculate, in UCS-2 encoding. * \param measure_width maximum width, in pixels, available for the string. * \param extent on return, filled with latest calculated width. * \param count on return, filled with number of characters that can be * rendered. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_MeasureText * \sa TTF_MeasureUTF8 * \sa TTF_MeasureUNICODE */ extern DECLSPEC int SDLCALL TTF_MeasureUNICODE(TTF_Font *font, const Uint16 *text, int measure_width, int *extent, int *count); /** * Render Latin1 text at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderText_Solid_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_Solid() unless you're sure you * have a 1-byte Latin1 encoding. US ASCII characters will work with either * function, but most other Unicode characters packed into a `const char *` * will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Shaded, * TTF_RenderText_Blended, and TTF_RenderText_LCD. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUTF8_Solid * \sa TTF_RenderUNICODE_Solid */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg); /** * Render UTF-8 text at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUTF8_Solid_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Shaded, * TTF_RenderUTF8_Blended, and TTF_RenderUTF8_LCD. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUTF8_Shaded * \sa TTF_RenderUTF8_Blended * \sa TTF_RenderUTF8_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Solid(TTF_Font *font, const char *text, SDL_Color fg); /** * Render UCS-2 text at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUNICODE_Solid_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with TTF_RenderUNICODE_Shaded, * TTF_RenderUNICODE_Blended, and TTF_RenderUNICODE_LCD. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUTF8_Solid */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid(TTF_Font *font, const Uint16 *text, SDL_Color fg); /** * Render word-wrapped Latin1 text at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_Solid_Wrapped() unless you're sure * you have a 1-byte Latin1 encoding. US ASCII characters will work with * either function, but most other Unicode characters packed into a `const * char *` will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Shaded_Wrapped, * TTF_RenderText_Blended_Wrapped, and TTF_RenderText_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Solid_Wrapped * \sa TTF_RenderUNICODE_Solid_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength); /** * Render word-wrapped UTF-8 text at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Shaded_Wrapped, * TTF_RenderUTF8_Blended_Wrapped, and TTF_RenderUTF8_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Shaded_Wrapped * \sa TTF_RenderUTF8_Blended_Wrapped * \sa TTF_RenderUTF8_LCD_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength); /** * Render word-wrapped UCS-2 text at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with * TTF_RenderUNICODE_Shaded_Wrapped, TTF_RenderUNICODE_Blended_Wrapped, and * TTF_RenderUNICODE_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Solid_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, Uint32 wrapLength); /** * Render a single 16-bit glyph at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * Note that this version of the function takes a 16-bit character code, which * covers the Basic Multilingual Plane, but is insufficient to cover the * entire set of possible Unicode values, including emoji glyphs. You should * use TTF_RenderGlyph32_Solid() instead, which offers the same functionality * but takes a 32-bit codepoint instead. * * The only reason to use this function is that it was available since the * beginning of time, more or less. * * You can render at other quality levels with TTF_RenderGlyph_Shaded, * TTF_RenderGlyph_Blended, and TTF_RenderGlyph_LCD. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderGlyph32_Solid */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Solid(TTF_Font *font, Uint16 ch, SDL_Color fg); /** * Render a single 32-bit glyph at fast quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the colorkey, giving a transparent background. The 1 pixel * will be set to the text color. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * This is the same as TTF_RenderGlyph_Solid(), but takes a 32-bit character * instead of 16-bit, and thus can render a larger range. If you are sure * you'll have an SDL_ttf that's version 2.0.18 or newer, there's no reason * not to use this function exclusively. * * You can render at other quality levels with TTF_RenderGlyph32_Shaded, * TTF_RenderGlyph32_Blended, and TTF_RenderGlyph32_LCD. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderGlyph32_Shaded * \sa TTF_RenderGlyph32_Blended * \sa TTF_RenderGlyph32_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph32_Solid(TTF_Font *font, Uint32 ch, SDL_Color fg); /** * Render Latin1 text at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderText_Shaded_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_Shaded() unless you're sure you * have a 1-byte Latin1 encoding. US ASCII characters will work with either * function, but most other Unicode characters packed into a `const char *` * will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Solid, * TTF_RenderText_Blended, and TTF_RenderText_LCD. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUTF8_Shaded * \sa TTF_RenderUNICODE_Shaded */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg); /** * Render UTF-8 text at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUTF8_Shaded_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Solid, * TTF_RenderUTF8_Blended, and TTF_RenderUTF8_LCD. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUNICODE_Shaded */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg); /** * Render UCS-2 text at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUNICODE_Shaded_Wrapped() instead if you need to wrap the output * to multiple lines. * * This will not wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with TTF_RenderUNICODE_Solid, * TTF_RenderUNICODE_Blended, and TTF_RenderUNICODE_LCD. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUTF8_Shaded */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg); /** * Render word-wrapped Latin1 text at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_Shaded_Wrapped() unless you're * sure you have a 1-byte Latin1 encoding. US ASCII characters will work with * either function, but most other Unicode characters packed into a `const * char *` will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Solid_Wrapped, * TTF_RenderText_Blended_Wrapped, and TTF_RenderText_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Shaded_Wrapped * \sa TTF_RenderUNICODE_Shaded_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); /** * Render word-wrapped UTF-8 text at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Solid_Wrapped, * TTF_RenderUTF8_Blended_Wrapped, and TTF_RenderUTF8_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Solid_Wrapped * \sa TTF_RenderUTF8_Blended_Wrapped * \sa TTF_RenderUTF8_LCD_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Shaded_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); /** * Render word-wrapped UCS-2 text at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with * TTF_RenderUNICODE_Solid_Wrapped, TTF_RenderUNICODE_Blended_Wrapped, and * TTF_RenderUNICODE_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Shaded_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); /** * Render a single 16-bit glyph at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * Note that this version of the function takes a 16-bit character code, which * covers the Basic Multilingual Plane, but is insufficient to cover the * entire set of possible Unicode values, including emoji glyphs. You should * use TTF_RenderGlyph32_Shaded() instead, which offers the same functionality * but takes a 32-bit codepoint instead. * * The only reason to use this function is that it was available since the * beginning of time, more or less. * * You can render at other quality levels with TTF_RenderGlyph_Solid, * TTF_RenderGlyph_Blended, and TTF_RenderGlyph_LCD. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderGlyph32_Shaded */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Shaded(TTF_Font *font, Uint16 ch, SDL_Color fg, SDL_Color bg); /** * Render a single 32-bit glyph at high quality to a new 8-bit surface. * * This function will allocate a new 8-bit, palettized surface. The surface's * 0 pixel will be the specified background color, while other pixels have * varying degrees of the foreground color. This function returns the new * surface, or NULL if there was an error. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * This is the same as TTF_RenderGlyph_Shaded(), but takes a 32-bit character * instead of 16-bit, and thus can render a larger range. If you are sure * you'll have an SDL_ttf that's version 2.0.18 or newer, there's no reason * not to use this function exclusively. * * You can render at other quality levels with TTF_RenderGlyph32_Solid, * TTF_RenderGlyph32_Blended, and TTF_RenderGlyph32_LCD. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \returns a new 8-bit, palettized surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderGlyph32_Solid * \sa TTF_RenderGlyph32_Blended * \sa TTF_RenderGlyph32_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph32_Shaded(TTF_Font *font, Uint32 ch, SDL_Color fg, SDL_Color bg); /** * Render Latin1 text at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderText_Blended_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_Blended() unless you're sure you * have a 1-byte Latin1 encoding. US ASCII characters will work with either * function, but most other Unicode characters packed into a `const char *` * will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Solid, * TTF_RenderText_Blended, and TTF_RenderText_LCD. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUTF8_Shaded * \sa TTF_RenderUNICODE_Shaded */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg); /** * Render UTF-8 text at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUTF8_Blended_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Solid, * TTF_RenderUTF8_Shaded, and TTF_RenderUTF8_LCD. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUNICODE_Blended */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended(TTF_Font *font, const char *text, SDL_Color fg); /** * Render UCS-2 text at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUNICODE_Blended_Wrapped() instead if you need to wrap the output * to multiple lines. * * This will not wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with TTF_RenderUNICODE_Solid, * TTF_RenderUNICODE_Shaded, and TTF_RenderUNICODE_LCD. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderUTF8_Blended */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended(TTF_Font *font, const Uint16 *text, SDL_Color fg); /** * Render word-wrapped Latin1 text at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_Blended_Wrapped() unless you're * sure you have a 1-byte Latin1 encoding. US ASCII characters will work with * either function, but most other Unicode characters packed into a `const * char *` will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Solid_Wrapped, * TTF_RenderText_Shaded_Wrapped, and TTF_RenderText_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \param wrapLength the text is wrapped to multiple lines on line endings and * on word boundaries if it extends beyond this value in * pixels. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Blended_Wrapped * \sa TTF_RenderUNICODE_Blended_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength); /** * Render word-wrapped UTF-8 text at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Solid_Wrapped, * TTF_RenderUTF8_Shaded_Wrapped, and TTF_RenderUTF8_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \param wrapLength the text is wrapped to multiple lines on line endings and * on word boundaries if it extends beyond this value in * pixels. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Solid_Wrapped * \sa TTF_RenderUTF8_Shaded_Wrapped * \sa TTF_RenderUTF8_LCD_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength); /** * Render word-wrapped UCS-2 text at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with * TTF_RenderUNICODE_Solid_Wrapped, TTF_RenderUNICODE_Shaded_Wrapped, and * TTF_RenderUNICODE_LCD_Wrapped. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \param wrapLength the text is wrapped to multiple lines on line endings and * on word boundaries if it extends beyond this value in * pixels. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderUTF8_Blended_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, Uint32 wrapLength); /** * Render a single 16-bit glyph at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * Note that this version of the function takes a 16-bit character code, which * covers the Basic Multilingual Plane, but is insufficient to cover the * entire set of possible Unicode values, including emoji glyphs. You should * use TTF_RenderGlyph32_Blended() instead, which offers the same * functionality but takes a 32-bit codepoint instead. * * The only reason to use this function is that it was available since the * beginning of time, more or less. * * You can render at other quality levels with TTF_RenderGlyph_Solid, * TTF_RenderGlyph_Shaded, and TTF_RenderGlyph_LCD. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_RenderGlyph32_Blended */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font, Uint16 ch, SDL_Color fg); /** * Render a single 32-bit glyph at high quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, using alpha * blending to dither the font with the given color. This function returns the * new surface, or NULL if there was an error. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * This is the same as TTF_RenderGlyph_Blended(), but takes a 32-bit character * instead of 16-bit, and thus can render a larger range. If you are sure * you'll have an SDL_ttf that's version 2.0.18 or newer, there's no reason * not to use this function exclusively. * * You can render at other quality levels with TTF_RenderGlyph32_Solid, * TTF_RenderGlyph32_Shaded, and TTF_RenderGlyph32_LCD. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_RenderGlyph32_Solid * \sa TTF_RenderGlyph32_Shaded * \sa TTF_RenderGlyph32_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph32_Blended(TTF_Font *font, Uint32 ch, SDL_Color fg); /** * Render Latin1 text at LCD subpixel quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderText_LCD_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_LCD() unless you're sure you have * a 1-byte Latin1 encoding. US ASCII characters will work with either * function, but most other Unicode characters packed into a `const char *` * will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Solid, * TTF_RenderText_Shaded, and TTF_RenderText_Blended. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderUTF8_LCD * \sa TTF_RenderUNICODE_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg); /** * Render UTF-8 text at LCD subpixel quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUTF8_LCD_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Solid, * TTF_RenderUTF8_Shaded, and TTF_RenderUTF8_Blended. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderUNICODE_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_LCD(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg); /** * Render UCS-2 text at LCD subpixel quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * This will not word-wrap the string; you'll get a surface with a single line * of text, as long as the string requires. You can use * TTF_RenderUNICODE_LCD_Wrapped() instead if you need to wrap the output to * multiple lines. * * This will not wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with TTF_RenderUNICODE_Solid, * TTF_RenderUNICODE_Shaded, and TTF_RenderUNICODE_Blended. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderUTF8_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_LCD(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg); /** * Render word-wrapped Latin1 text at LCD subpixel quality to a new ARGB * surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You almost certainly want TTF_RenderUTF8_LCD_Wrapped() unless you're sure * you have a 1-byte Latin1 encoding. US ASCII characters will work with * either function, but most other Unicode characters packed into a `const * char *` will need UTF-8. * * You can render at other quality levels with TTF_RenderText_Solid_Wrapped, * TTF_RenderText_Shaded_Wrapped, and TTF_RenderText_Blended_Wrapped. * * \param font the font to render with. * \param text text to render, in Latin1 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderUTF8_LCD_Wrapped * \sa TTF_RenderUNICODE_LCD_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); /** * Render word-wrapped UTF-8 text at LCD subpixel quality to a new ARGB * surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * You can render at other quality levels with TTF_RenderUTF8_Solid_Wrapped, * TTF_RenderUTF8_Shaded_Wrapped, and TTF_RenderUTF8_Blended_Wrapped. * * \param font the font to render with. * \param text text to render, in UTF-8 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderUTF8_Solid_Wrapped * \sa TTF_RenderUTF8_Shaded_Wrapped * \sa TTF_RenderUTF8_Blended_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_LCD_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); /** * Render word-wrapped UCS-2 text at LCD subpixel quality to a new ARGB * surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * Text is wrapped to multiple lines on line endings and on word boundaries if * it extends beyond `wrapLength` in pixels. * * If wrapLength is 0, this function will only wrap on newline characters. * * Please note that this function is named "Unicode" but currently expects * UCS-2 encoding (16 bits per codepoint). This does not give you access to * large Unicode values, such as emoji glyphs. These codepoints are accessible * through the UTF-8 version of this function. * * You can render at other quality levels with * TTF_RenderUNICODE_Solid_Wrapped, TTF_RenderUNICODE_Shaded_Wrapped, and * TTF_RenderUNICODE_Blended_Wrapped. * * \param font the font to render with. * \param text text to render, in UCS-2 encoding. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderUTF8_LCD_Wrapped */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_LCD_Wrapped(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg, Uint32 wrapLength); /** * Render a single 16-bit glyph at LCD subpixel quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * Note that this version of the function takes a 16-bit character code, which * covers the Basic Multilingual Plane, but is insufficient to cover the * entire set of possible Unicode values, including emoji glyphs. You should * use TTF_RenderGlyph32_LCD() instead, which offers the same functionality * but takes a 32-bit codepoint instead. * * This function only exists for consistency with the existing API at the time * of its addition. * * You can render at other quality levels with TTF_RenderGlyph_Solid, * TTF_RenderGlyph_Shaded, and TTF_RenderGlyph_Blended. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderGlyph32_LCD */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_LCD(TTF_Font *font, Uint16 ch, SDL_Color fg, SDL_Color bg); /** * Render a single 32-bit glyph at LCD subpixel quality to a new ARGB surface. * * This function will allocate a new 32-bit, ARGB surface, and render * alpha-blended text using FreeType's LCD subpixel rendering. This function * returns the new surface, or NULL if there was an error. * * The glyph is rendered without any padding or centering in the X direction, * and aligned normally in the Y direction. * * This is the same as TTF_RenderGlyph_LCD(), but takes a 32-bit character * instead of 16-bit, and thus can render a larger range. Between the two, you * should always use this function. * * You can render at other quality levels with TTF_RenderGlyph32_Solid, * TTF_RenderGlyph32_Shaded, and TTF_RenderGlyph32_Blended. * * \param font the font to render with. * \param ch the character to render. * \param fg the foreground color for the text. * \param bg the background color for the text. * \returns a new 32-bit, ARGB surface, or NULL if there was an error. * * \since This function is available since SDL_ttf 2.20.0. * * \sa TTF_RenderGlyph32_Solid * \sa TTF_RenderGlyph32_Shaded * \sa TTF_RenderGlyph32_Blended */ extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph32_LCD(TTF_Font *font, Uint32 ch, SDL_Color fg, SDL_Color bg); /* For compatibility with previous versions, here are the old functions */ #define TTF_RenderText(font, text, fg, bg) \ TTF_RenderText_Shaded(font, text, fg, bg) #define TTF_RenderUTF8(font, text, fg, bg) \ TTF_RenderUTF8_Shaded(font, text, fg, bg) #define TTF_RenderUNICODE(font, text, fg, bg) \ TTF_RenderUNICODE_Shaded(font, text, fg, bg) /** * Dispose of a previously-created font. * * Call this when done with a font. This function will free any resources * associated with it. It is safe to call this function on NULL, for example * on the result of a failed call to TTF_OpenFont(). * * The font is not valid after being passed to this function. String pointers * from functions that return information on this font, such as * TTF_FontFaceFamilyName() and TTF_FontFaceStyleName(), are no longer valid * after this call, as well. * * \param font the font to dispose of. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_OpenFont * \sa TTF_OpenFontIndexDPIRW * \sa TTF_OpenFontRW * \sa TTF_OpenFontDPI * \sa TTF_OpenFontDPIRW * \sa TTF_OpenFontIndex * \sa TTF_OpenFontIndexDPI * \sa TTF_OpenFontIndexDPIRW * \sa TTF_OpenFontIndexRW */ extern DECLSPEC void SDLCALL TTF_CloseFont(TTF_Font *font); /** * Deinitialize SDL_ttf. * * You must call this when done with the library, to free internal resources. * It is safe to call this when the library isn't initialized, as it will just * return immediately. * * Once you have as many quit calls as you have had successful calls to * TTF_Init, the library will actually deinitialize. * * Please note that this does not automatically close any fonts that are still * open at the time of deinitialization, and it is possibly not safe to close * them afterwards, as parts of the library will no longer be initialized to * deal with it. A well-written program should call TTF_CloseFont() on any * open fonts before calling this function! * * \since This function is available since SDL_ttf 2.0.12. */ extern DECLSPEC void SDLCALL TTF_Quit(void); /** * Check if SDL_ttf is initialized. * * This reports the number of times the library has been initialized by a call * to TTF_Init(), without a paired deinitialization request from TTF_Quit(). * * In short: if it's greater than zero, the library is currently initialized * and ready to work. If zero, it is not initialized. * * Despite the return value being a signed integer, this function should not * return a negative number. * * \returns the current number of initialization calls, that need to * eventually be paired with this many calls to TTF_Quit(). * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_Init * \sa TTF_Quit */ extern DECLSPEC int SDLCALL TTF_WasInit(void); /** * Query the kerning size of two glyphs indices. * * \deprecated This function accidentally requires FreeType font indexes, * not codepoints, which we don't expose through this API, so * it could give wildly incorrect results, especially with * non-ASCII values. Going forward, please use * TTF_GetFontKerningSizeGlyphs() instead, which does what you * probably expected this function to do. * * \param font the font to query. * \param prev_index the font index, NOT codepoint, of the previous character. * \param index the font index, NOT codepoint, of the current character. * \returns The kerning size between the two specified characters, in pixels, or -1 on error. * * \since This function is available since SDL_ttf 2.0.12. * * \sa TTF_GetFontKerningSizeGlyphs */ extern SDL_DEPRECATED DECLSPEC int TTF_GetFontKerningSize(TTF_Font *font, int prev_index, int index); /** * Query the kerning size of two 16-bit glyphs. * * Note that this version of the function takes 16-bit character * codes, which covers the Basic Multilingual Plane, but is insufficient * to cover the entire set of possible Unicode values, including emoji * glyphs. You should use TTF_GetFontKerningSizeGlyphs32() instead, which * offers the same functionality but takes a 32-bit codepoints instead. * * The only reason to use this function is that it was available since * the beginning of time, more or less. * * \param font the font to query. * \param previous_ch the previous character's code, 16 bits. * \param ch the current character's code, 16 bits. * \returns The kerning size between the two specified characters, in pixels, or -1 on error. * * \since This function is available since SDL_ttf 2.0.14. * * \sa TTF_GetFontKerningSizeGlyphs32 */ extern DECLSPEC int TTF_GetFontKerningSizeGlyphs(TTF_Font *font, Uint16 previous_ch, Uint16 ch); /** * Query the kerning size of two 32-bit glyphs. * * This is the same as TTF_GetFontKerningSizeGlyphs(), but takes 32-bit * characters instead of 16-bit, and thus can manage a larger range. If * you are sure you'll have an SDL_ttf that's version 2.0.18 or newer, * there's no reason not to use this function exclusively. * * \param font the font to query. * \param previous_ch the previous character's code, 32 bits. * \param ch the current character's code, 32 bits. * \returns The kerning size between the two specified characters, in pixels, or -1 on error. * * \since This function is available since SDL_ttf 2.0.18. */ extern DECLSPEC int TTF_GetFontKerningSizeGlyphs32(TTF_Font *font, Uint32 previous_ch, Uint32 ch); /** * Enable Signed Distance Field rendering for a font. * * This works with the Blended APIs. SDF is a technique that * helps fonts look sharp even when scaling and rotating. * * This clears already-generated glyphs, if any, from the cache. * * \param font the font to set SDF support on. * \param on_off SDL_TRUE to enable SDF, SDL_FALSE to disable. * * \returns 0 on success, -1 on error. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_GetFontSDF */ extern DECLSPEC int TTF_SetFontSDF(TTF_Font *font, SDL_bool on_off); /** * Query whether Signed Distance Field rendering is enabled for a font. * * \param font the font to query * * \returns SDL_TRUE if enabled, SDL_FALSE otherwise. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_SetFontSDF */ extern DECLSPEC SDL_bool TTF_GetFontSDF(const TTF_Font *font); /** * Report SDL_ttf errors * * \sa TTF_GetError */ #define TTF_SetError SDL_SetError /** * Get last SDL_ttf error * * \sa TTF_SetError */ #define TTF_GetError SDL_GetError /** * Direction flags * * \sa TTF_SetFontDirection */ typedef enum TTF_Direction { TTF_DIRECTION_LTR = 0, /* Left to Right */ TTF_DIRECTION_RTL, /* Right to Left */ TTF_DIRECTION_TTB, /* Top to Bottom */ TTF_DIRECTION_BTT /* Bottom to Top */ } TTF_Direction; /** * Set a global direction to be used for text shaping. * * \deprecated This function expects an hb_direction_t value, from HarfBuzz, * cast to an int, and affects all fonts globally. Please use * TTF_SetFontDirection() instead, which uses an enum supplied by * SDL_ttf itself and operates on a per-font basis. * * This is a global setting; fonts will favor a value set with * TTF_SetFontDirection(), but if they have not had one explicitly * set, they will use the value specified here. * * The default value is `HB_DIRECTION_LTR` (left-to-right text * flow). * * \param direction an hb_direction_t value. * \returns 0, or -1 if SDL_ttf is not compiled with HarfBuzz support. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_SetFontDirection */ extern SDL_DEPRECATED DECLSPEC int SDLCALL TTF_SetDirection(int direction); /* hb_direction_t */ /** * Set a global script to be used for text shaping. * * \deprecated This function expects an hb_script_t value, from HarfBuzz, cast * to an int, and affects all fonts globally. Please use * TTF_SetFontScriptName() instead, which accepts a string that is * converted to an equivalent int internally, and operates on a * per-font basis. * * This is a global setting; fonts will favor a value set with * TTF_SetFontScriptName(), but if they have not had one * explicitly set, they will use the value specified here. * * The default value is `HB_SCRIPT_UNKNOWN`. * * \returns 0, or -1 if SDL_ttf is not compiled with HarfBuzz support. * * \since This function is available since SDL_ttf 2.0.18. * * \sa TTF_SetFontScriptName */ extern SDL_DEPRECATED DECLSPEC int SDLCALL TTF_SetScript(int script); /* hb_script_t */ /** * Set direction to be used for text shaping by a font. * * Any value supplied here will override the global direction set with the * deprecated TTF_SetDirection(). * * Possible direction values are: * * - `TTF_DIRECTION_LTR` (Left to Right) * - `TTF_DIRECTION_RTL` (Right to Left) * - `TTF_DIRECTION_TTB` (Top to Bottom) * - `TTF_DIRECTION_BTT` (Bottom to Top) * * If SDL_ttf was not built with HarfBuzz support, this function returns -1. * * \param font the font to specify a direction for. * \param direction the new direction for text to flow. * \returns 0 on success, or -1 on error. * * \since This function is available since SDL_ttf 2.20.0. */ extern DECLSPEC int SDLCALL TTF_SetFontDirection(TTF_Font *font, TTF_Direction direction); /** * Set script to be used for text shaping by a font. * * Any value supplied here will override the global script set with the * deprecated TTF_SetScript(). * * The supplied script value must be a null-terminated string of exactly four * characters. * * If SDL_ttf was not built with HarfBuzz support, this function returns -1. * * \param font the font to specify a direction for. * \param script null-terminated string of exactly 4 characters. * \returns 0 on success, or -1 on error. * * \since This function is available since SDL_ttf 2.20.0. */ extern DECLSPEC int SDLCALL TTF_SetFontScriptName(TTF_Font *font, const char *script); /* Ends C function definitions when using C++ */ #ifdef __cplusplus } #endif #include "close_code.h" #endif /* SDL_TTF_H_ */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_ttf-2.24.0/acinclude/ax_compute_relative_paths.m40000664000000000000000000001605414267355340017556 0ustar00# ============================================================================== # https://www.gnu.org/software/autoconf-archive/ax_compute_relative_paths.html # ============================================================================== # # SYNOPSIS # # AX_COMPUTE_RELATIVE_PATHS(PATH_LIST) # # DESCRIPTION # # PATH_LIST is a space-separated list of colon-separated triplets of the # form 'FROM:TO:RESULT'. This function iterates over these triplets and # set $RESULT to the relative path from $FROM to $TO. Note that $FROM and # $TO needs to be absolute filenames for this macro to success. # # For instance, # # first=/usr/local/bin # second=/usr/local/share # AX_COMPUTE_RELATIVE_PATHS([first:second:fs second:first:sf]) # # $fs is set to ../share # # $sf is set to ../bin # # $FROM and $TO are both eval'ed recursively and normalized, this means # that you can call this macro with autoconf's dirnames like `prefix' or # `datadir'. For example: # # AX_COMPUTE_RELATIVE_PATHS([bindir:datadir:bin_to_data]) # # AX_COMPUTE_RELATIVE_PATHS should also works with DOS filenames. # # You may want to use this macro in order to make your package # relocatable. Instead of hardcoding $datadir into your programs just # encode $bin_to_data and try to determine $bindir at run-time. # # This macro requires AX_NORMALIZE_PATH and AX_RECURSIVE_EVAL. # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program 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 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 12 AU_ALIAS([ADL_COMPUTE_RELATIVE_PATHS], [AX_COMPUTE_RELATIVE_PATHS]) AC_DEFUN([AX_COMPUTE_RELATIVE_PATHS], [for _lcl_i in $1; do _lcl_from=\[$]`echo "[$]_lcl_i" | sed 's,:.*$,,'` _lcl_to=\[$]`echo "[$]_lcl_i" | sed 's,^[[^:]]*:,,' | sed 's,:[[^:]]*$,,'` _lcl_result_var=`echo "[$]_lcl_i" | sed 's,^.*:,,'` AX_RECURSIVE_EVAL([[$]_lcl_from], [_lcl_from]) AX_RECURSIVE_EVAL([[$]_lcl_to], [_lcl_to]) _lcl_notation="$_lcl_from$_lcl_to" AX_NORMALIZE_PATH([_lcl_from],['/']) AX_NORMALIZE_PATH([_lcl_to],['/']) AX_COMPUTE_RELATIVE_PATH([_lcl_from], [_lcl_to], [_lcl_result_tmp]) AX_NORMALIZE_PATH([_lcl_result_tmp],["[$]_lcl_notation"]) eval $_lcl_result_var='[$]_lcl_result_tmp' done]) ## Note: ## ***** ## The following helper macros are too fragile to be used out ## of AX_COMPUTE_RELATIVE_PATHS (mainly because they assume that ## paths are normalized), that's why I'm keeping them in the same file. ## Still, some of them maybe worth to reuse. dnl AX_COMPUTE_RELATIVE_PATH(FROM, TO, RESULT) dnl =========================================== dnl Compute the relative path to go from $FROM to $TO and set the value dnl of $RESULT to that value. This function work on raw filenames dnl (for instead it will considerate /usr//local and /usr/local as dnl two distinct paths), you should really use AX_COMPUTE_RELATIVE_PATHS dnl instead to have the paths sanitized automatically. dnl dnl For instance: dnl first_dir=/somewhere/on/my/disk/bin dnl second_dir=/somewhere/on/another/disk/share dnl AX_COMPUTE_RELATIVE_PATH(first_dir, second_dir, first_to_second) dnl will set $first_to_second to '../../../another/disk/share'. AC_DEFUN([AX_COMPUTE_RELATIVE_PATH], [AX_COMPUTE_COMMON_PATH([$1], [$2], [_lcl_common_prefix]) AX_COMPUTE_BACK_PATH([$1], [_lcl_common_prefix], [_lcl_first_rel]) AX_COMPUTE_SUFFIX_PATH([$2], [_lcl_common_prefix], [_lcl_second_suffix]) $3="[$]_lcl_first_rel[$]_lcl_second_suffix"]) dnl AX_COMPUTE_COMMON_PATH(LEFT, RIGHT, RESULT) dnl ============================================ dnl Compute the common path to $LEFT and $RIGHT and set the result to $RESULT. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on/another/disk/share dnl AX_COMPUTE_COMMON_PATH(first_path, second_path, common_path) dnl will set $common_path to '/somewhere/on'. AC_DEFUN([AX_COMPUTE_COMMON_PATH], [$3='' _lcl_second_prefix_match='' while test "[$]_lcl_second_prefix_match" != 0; do _lcl_first_prefix=`expr "x[$]$1" : "x\([$]$3/*[[^/]]*\)"` _lcl_second_prefix_match=`expr "x[$]$2" : "x[$]_lcl_first_prefix"` if test "[$]_lcl_second_prefix_match" != 0; then if test "[$]_lcl_first_prefix" != "[$]$3"; then $3="[$]_lcl_first_prefix" else _lcl_second_prefix_match=0 fi fi done]) dnl AX_COMPUTE_SUFFIX_PATH(PATH, SUBPATH, RESULT) dnl ============================================== dnl Subtract $SUBPATH from $PATH, and set the resulting suffix dnl (or the empty string if $SUBPATH is not a subpath of $PATH) dnl to $RESULT. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on dnl AX_COMPUTE_SUFFIX_PATH(first_path, second_path, common_path) dnl will set $common_path to '/my/disk/bin'. AC_DEFUN([AX_COMPUTE_SUFFIX_PATH], [$3=`expr "x[$]$1" : "x[$]$2/*\(.*\)"`]) dnl AX_COMPUTE_BACK_PATH(PATH, SUBPATH, RESULT) dnl ============================================ dnl Compute the relative path to go from $PATH to $SUBPATH, knowing that dnl $SUBPATH is a subpath of $PATH (any other words, only repeated '../' dnl should be needed to move from $PATH to $SUBPATH) and set the value dnl of $RESULT to that value. If $SUBPATH is not a subpath of PATH, dnl set $RESULT to the empty string. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on dnl AX_COMPUTE_BACK_PATH(first_path, second_path, back_path) dnl will set $back_path to '../../../'. AC_DEFUN([AX_COMPUTE_BACK_PATH], [AX_COMPUTE_SUFFIX_PATH([$1], [$2], [_lcl_first_suffix]) $3='' _lcl_tmp='xxx' while test "[$]_lcl_tmp" != ''; do _lcl_tmp=`expr "x[$]_lcl_first_suffix" : "x[[^/]]*/*\(.*\)"` if test "[$]_lcl_first_suffix" != ''; then _lcl_first_suffix="[$]_lcl_tmp" $3="../[$]$3" fi done]) SDL2_ttf-2.24.0/acinclude/ax_cxx_compile_stdcxx.m40000664000000000000000000005041514267355340016716 0ustar00# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11', '14', '17', or '20' for # the respective C++ standard version. # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for no added switch, and then for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016, 2018 Krzesimir Nowak # Copyright (c) 2019 Enji Cooper # Copyright (c) 2020 Jason Merrill # Copyright (c) 2021 Jörn Heusipp # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 14 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [$1], [20], [ax_cxx_compile_alternatives="20"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no m4_if([$2], [], [dnl AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi]) m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) dnl Test body for checking C++17 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Test body for checking C++20 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 _AX_CXX_COMPILE_STDCXX_testbody_new_in_20 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L ]]) dnl Tests for new features in C++20 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[ #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 202002L #error "This is not a C++20 compiler" #else #include namespace cxx20 { // As C++20 supports feature test macros in the standard, there is no // immediate need to actually test for feature availability on the // Autoconf side. } // namespace cxx20 #endif // __cplusplus < 202002L ]]) SDL2_ttf-2.24.0/acinclude/ax_normalize_path.m40000664000000000000000000001134214267355340016017 0ustar00# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_normalize_path.html # =========================================================================== # # SYNOPSIS # # AX_NORMALIZE_PATH(VARNAME, [REFERENCE_STRING]) # # DESCRIPTION # # Perform some cleanups on the value of $VARNAME (interpreted as a path): # # - empty paths are changed to '.' # - trailing slashes are removed # - repeated slashes are squeezed except a leading doubled slash '//' # (which might indicate a networked disk on some OS). # # REFERENCE_STRING is used to turn '/' into '\' and vice-versa: if # REFERENCE_STRING contains some backslashes, all slashes and backslashes # are turned into backslashes, otherwise they are all turned into slashes. # # This makes processing of DOS filenames quite easier, because you can # turn a filename to the Unix notation, make your processing, and turn it # back to original notation. # # filename='A:\FOO\\BAR\' # old_filename="$filename" # # Switch to the unix notation # AX_NORMALIZE_PATH([filename], ["/"]) # # now we have $filename = 'A:/FOO/BAR' and we can process it as if # # it was a Unix path. For instance let's say that you want # # to append '/subpath': # filename="$filename/subpath" # # finally switch back to the original notation # AX_NORMALIZE_PATH([filename], ["$old_filename"]) # # now $filename equals to 'A:\FOO\BAR\subpath' # # One good reason to make all path processing with the unix convention is # that backslashes have a special meaning in many cases. For instance # # expr 'A:\FOO' : 'A:\Foo' # # will return 0 because the second argument is a regex in which # backslashes have to be backslashed. In other words, to have the two # strings to match you should write this instead: # # expr 'A:\Foo' : 'A:\\Foo' # # Such behavior makes DOS filenames extremely unpleasant to work with. So # temporary turn your paths to the Unix notation, and revert them to the # original notation after the processing. See the macro # AX_COMPUTE_RELATIVE_PATHS for a concrete example of this. # # REFERENCE_STRING defaults to $VARIABLE, this means that slashes will be # converted to backslashes if $VARIABLE already contains some backslashes # (see $thirddir below). # # firstdir='/usr/local//share' # seconddir='C:\Program Files\\' # thirddir='C:\home/usr/' # AX_NORMALIZE_PATH([firstdir]) # AX_NORMALIZE_PATH([seconddir]) # AX_NORMALIZE_PATH([thirddir]) # # $firstdir = '/usr/local/share' # # $seconddir = 'C:\Program Files' # # $thirddir = 'C:\home\usr' # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program 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 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 8 AU_ALIAS([ADL_NORMALIZE_PATH], [AX_NORMALIZE_PATH]) AC_DEFUN([AX_NORMALIZE_PATH], [case ":[$]$1:" in # change empty paths to '.' ::) $1='.' ;; # strip trailing slashes :*[[\\/]]:) $1=`echo "[$]$1" | sed 's,[[\\/]]*[$],,'` ;; :*:) ;; esac # squeeze repeated slashes case ifelse($2,,"[$]$1",$2) in # if the path contains any backslashes, turn slashes into backslashes *\\*) $1=`echo "[$]$1" | sed 's,\(.\)[[\\/]][[\\/]]*,\1\\\\,g'` ;; # if the path contains slashes, also turn backslashes into slashes *) $1=`echo "[$]$1" | sed 's,\(.\)[[\\/]][[\\/]]*,\1/,g'` ;; esac]) SDL2_ttf-2.24.0/acinclude/ax_recursive_eval.m40000664000000000000000000000455014267355340016024 0ustar00# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_recursive_eval.html # =========================================================================== # # SYNOPSIS # # AX_RECURSIVE_EVAL(VALUE, RESULT) # # DESCRIPTION # # Interpolate the VALUE in loop until it doesn't change, and set the # result to $RESULT. WARNING: It's easy to get an infinite loop with some # unsane input. # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program 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 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 1 AC_DEFUN([AX_RECURSIVE_EVAL], [_lcl_receval="$1" $2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix" test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" _lcl_receval_old='' while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do _lcl_receval_old="[$]_lcl_receval" eval _lcl_receval="\"[$]_lcl_receval\"" done echo "[$]_lcl_receval")`]) SDL2_ttf-2.24.0/acinclude/freetype2.m40000664000000000000000000001426714072662176014233 0ustar00# Configure paths for FreeType2 # Marcelo Magallon 2001-10-26, based on `gtk.m4` by Owen Taylor # # Copyright (C) 2001-2021 by # David Turner, Robert Wilhelm, and Werner Lemberg. # # This file is part of the FreeType project, and may only be used, modified, # and distributed under the terms of the FreeType project license, # LICENSE.TXT. By continuing to use, modify, or distribute this file you # indicate that you have read the license and understand and accept it # fully. # # As a special exception to the FreeType project license, this file may be # distributed as part of a program that contains a configuration script # generated by Autoconf, under the same distribution terms as the rest of # that program. # # serial 6 # AC_CHECK_FT2([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) # Test for FreeType 2, and define FT2_CFLAGS and FT2_LIBS. # MINIMUM-VERSION is what libtool reports; the default is '7.0.1' (this is # FreeType 2.0.4). # # To make this code work with older autoconf versions, `AS_HELP_STRING` is # not quoted. # AC_DEFUN([AC_CHECK_FT2], [# Get the cflags and libraries from the freetype-config script # AC_ARG_WITH([ft-prefix], AS_HELP_STRING([--with-ft-prefix=PREFIX], [Prefix where FreeType is installed (optional)]), [ft_config_prefix="$withval"], [ft_config_prefix=""]) AC_ARG_WITH([ft-exec-prefix], AS_HELP_STRING([--with-ft-exec-prefix=PREFIX], [Exec prefix where FreeType is installed (optional)]), [ft_config_exec_prefix="$withval"], [ft_config_exec_prefix=""]) AC_ARG_ENABLE([freetypetest], AS_HELP_STRING([--disable-freetypetest], [Do not try to compile and run a test FreeType program]), [], [enable_fttest=yes]) if test x$ft_config_exec_prefix != x ; then ft_config_args="$ft_config_args --exec-prefix=$ft_config_exec_prefix" if test x${FT2_CONFIG+set} != xset ; then FT2_CONFIG=$ft_config_exec_prefix/bin/freetype-config fi fi if test x$ft_config_prefix != x ; then ft_config_args="$ft_config_args --prefix=$ft_config_prefix" if test x${FT2_CONFIG+set} != xset ; then FT2_CONFIG=$ft_config_prefix/bin/freetype-config fi fi if test "x$FT2_CONFIG" = x ; then AC_PATH_TOOL([FT2_CONFIG], [freetype-config], [no]) fi min_ft_version=m4_if([$1], [], [7.0.1], [$1]) AC_MSG_CHECKING([for FreeType -- version >= $min_ft_version]) no_ft="" if test "$FT2_CONFIG" = "no" ; then no_ft=yes else FT2_CFLAGS=`$FT2_CONFIG $ft_config_args --cflags` FT2_LIBS=`$FT2_CONFIG $ft_config_args --libs` ft_config_major_version=`$FT2_CONFIG $ft_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` ft_config_minor_version=`$FT2_CONFIG $ft_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` ft_config_micro_version=`$FT2_CONFIG $ft_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` ft_min_major_version=`echo $min_ft_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` ft_min_minor_version=`echo $min_ft_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` ft_min_micro_version=`echo $min_ft_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test x$enable_fttest = xyes ; then ft_config_is_lt="" if test $ft_config_major_version -lt $ft_min_major_version ; then ft_config_is_lt=yes else if test $ft_config_major_version -eq $ft_min_major_version ; then if test $ft_config_minor_version -lt $ft_min_minor_version ; then ft_config_is_lt=yes else if test $ft_config_minor_version -eq $ft_min_minor_version ; then if test $ft_config_micro_version -lt $ft_min_micro_version ; then ft_config_is_lt=yes fi fi fi fi fi if test x$ft_config_is_lt = xyes ; then no_ft=yes else ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $FT2_CFLAGS" LIBS="$FT2_LIBS $LIBS" # # Sanity checks for the results of freetype-config to some extent. # AC_RUN_IFELSE([ AC_LANG_SOURCE([[ #include #include #include #include int main() { FT_Library library; FT_Error error; error = FT_Init_FreeType(&library); if (error) return 1; else { FT_Done_FreeType(library); return 0; } } ]]) ], [], [no_ft=yes], [echo $ECHO_N "cross compiling; assuming OK... $ECHO_C"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi # test $ft_config_version -lt $ft_min_version fi # test x$enable_fttest = xyes fi # test "$FT2_CONFIG" = "no" if test x$no_ft = x ; then AC_MSG_RESULT([yes]) m4_if([$2], [], [:], [$2]) else AC_MSG_RESULT([no]) if test "$FT2_CONFIG" = "no" ; then AC_MSG_WARN([ The freetype-config script installed by FreeType 2 could not be found. If FreeType 2 was installed in PREFIX, make sure PREFIX/bin is in your path, or set the FT2_CONFIG environment variable to the full path to freetype-config. ]) else if test x$ft_config_is_lt = xyes ; then AC_MSG_WARN([ Your installed version of the FreeType 2 library is too old. If you have different versions of FreeType 2, make sure that correct values for --with-ft-prefix or --with-ft-exec-prefix are used, or set the FT2_CONFIG environment variable to the full path to freetype-config. ]) else AC_MSG_WARN([ The FreeType test program failed to run. If your system uses shared libraries and they are installed outside the normal system library path, make sure the variable LD_LIBRARY_PATH (or whatever is appropriate for your system) is correctly set. ]) fi fi FT2_CFLAGS="" FT2_LIBS="" m4_if([$3], [], [:], [$3]) fi AC_SUBST([FT2_CFLAGS]) AC_SUBST([FT2_LIBS])]) # end of freetype2.m4 SDL2_ttf-2.24.0/acinclude/pkg.m40000664000000000000000000002400714020407636013067 0ustar00# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR SDL2_ttf-2.24.0/acinclude/sdl2.m40000664000000000000000000001471414611641252013156 0ustar00# Configure paths for SDL # Sam Lantinga 9/21/99 # stolen from Manish Singh # stolen back from Frank Belew # stolen from Manish Singh # Shamelessly stolen from Owen Taylor # serial 2 dnl AM_PATH_SDL2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS dnl AC_DEFUN([AM_PATH_SDL2], [dnl dnl Get the cflags and libraries from the sdl2-config script dnl AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], sdl_prefix="$withval", sdl_prefix="") AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], sdl_exec_prefix="$withval", sdl_exec_prefix="") AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], , enable_sdltest=yes) min_sdl_version=ifelse([$1], ,2.0.0,$1) if test "x$sdl_prefix$sdl_exec_prefix" = x ; then PKG_CHECK_MODULES([SDL], [sdl2 >= $min_sdl_version], [sdl_pc=yes], [sdl_pc=no]) else sdl_pc=no if test x$sdl_exec_prefix != x ; then sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" if test x${SDL2_CONFIG+set} != xset ; then SDL2_CONFIG=$sdl_exec_prefix/bin/sdl2-config fi fi if test x$sdl_prefix != x ; then sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" if test x${SDL2_CONFIG+set} != xset ; then SDL2_CONFIG=$sdl_prefix/bin/sdl2-config fi fi fi if test "x$sdl_pc" = xyes ; then no_sdl="" SDL2_CONFIG="$PKG_CONFIG sdl2" else as_save_PATH="$PATH" if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then PATH="$prefix/bin:$prefix/usr/bin:$PATH" fi AC_PATH_PROG(SDL2_CONFIG, sdl2-config, no, [$PATH]) PATH="$as_save_PATH" AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) no_sdl="" if test "$SDL2_CONFIG" = "no" ; then no_sdl=yes else SDL_CFLAGS=`$SDL2_CONFIG $sdl_config_args --cflags` SDL_LIBS=`$SDL2_CONFIG $sdl_config_args --libs` sdl_major_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` sdl_minor_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` sdl_micro_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x$enable_sdltest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_CXXFLAGS="$CXXFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" dnl dnl Now check if the installed SDL is sufficiently new. (Also sanity dnl checks the results of sdl2-config to some extent dnl rm -f conf.sdltest AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include "SDL.h" int main (int argc, char *argv[]) { int major, minor, micro; FILE *fp = fopen("conf.sdltest", "w"); if (fp) fclose(fp); if (sscanf("$min_sdl_version", "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_sdl_version"); exit(1); } if (($sdl_major_version > major) || (($sdl_major_version == major) && ($sdl_minor_version > minor)) || (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) { return 0; } else { printf("\n*** 'sdl2-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); printf("*** of SDL required is %d.%d.%d. If sdl2-config is correct, then it is\n", major, minor, micro); printf("*** best to upgrade to the required version.\n"); printf("*** If sdl2-config was wrong, set the environment variable SDL2_CONFIG\n"); printf("*** to point to the correct copy of sdl2-config, and remove the file\n"); printf("*** config.cache before re-running configure\n"); return 1; } } ]])], [], [no_sdl=yes], [echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_sdl" = x ; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi fi if test "x$no_sdl" = x ; then ifelse([$2], , :, [$2]) else if test "$SDL2_CONFIG" = "no" ; then echo "*** The sdl2-config script installed by SDL could not be found" echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" echo "*** your path, or set the SDL2_CONFIG environment variable to the" echo "*** full path to sdl2-config." else if test -f conf.sdltest ; then : else echo "*** Could not run SDL test program, checking why..." CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include "SDL.h" int main(int argc, char *argv[]) { return 0; } #undef main #define main K_and_R_C_main ]], [[ return 0; ]])], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding SDL or finding the wrong" echo "*** version of SDL. If it is not finding SDL, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means SDL was incorrectly installed" echo "*** or that you have moved SDL since it was installed. In the latter case, you" echo "*** may want to edit the sdl2-config script: $SDL2_CONFIG" ]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" fi fi SDL_CFLAGS="" SDL_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(SDL_CFLAGS) AC_SUBST(SDL_LIBS) rm -f conf.sdltest ]) SDL2_ttf-2.24.0/acinclude/tar.m40000664000000000000000000001132114210747152013070 0ustar00# Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' dnl SDL: No following the symlinks: removed tar 'h' and cpio/pax '-L' switches m4_if([$1], [v7], [am__tar='$${TAR-tar} cof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test x$am_uid = xunknown; then AC_MSG_WARN([ancient id detected; assuming current UID is ok, but dist-ustar might not work]) elif test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test x$gm_gid = xunknown; then AC_MSG_WARN([ancient id detected; assuming current GID is ok, but dist-ustar might not work]) elif test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -cf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -cf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar cf - "$$tardir"' am__tar_='tar cf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -x $1 -w "$$tardir"' am__tar_='pax -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1' am__tar_='find "$tardir" -print | cpio -o -H $1' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR SDL2_ttf-2.24.0/autogen.sh0000775000000000000000000000030114252362742012112 0ustar00#!/bin/sh set -e "${ACLOCAL:-aclocal}" -I acinclude "${AUTOMAKE:-automake}" --foreign --include-deps --add-missing --copy "${AUTOCONF:-autoconf}" echo "Now you are ready to run ./configure" SDL2_ttf-2.24.0/build-scripts/build-release.py0000775000000000000000000022601714733545702016010 0ustar00#!/usr/bin/env python3 """ This script is shared between SDL2, SDL3, and all satellite libraries. Don't specialize this script for doing project-specific modifications. Rather, modify release-info.json. """ import argparse import collections import dataclasses from collections.abc import Callable import contextlib import datetime import fnmatch import glob import io import json import logging import multiprocessing import os from pathlib import Path import platform import re import shlex import shutil import subprocess import sys import tarfile import tempfile import textwrap import typing import zipfile logger = logging.getLogger(__name__) GIT_HASH_FILENAME = ".git-hash" REVISION_TXT = "REVISION.txt" def safe_isotime_to_datetime(str_isotime: str) -> datetime.datetime: try: return datetime.datetime.fromisoformat(str_isotime) except ValueError: pass logger.warning("Invalid iso time: %s", str_isotime) if str_isotime[-6:-5] in ("+", "-"): # Commits can have isotime with invalid timezone offset (e.g. "2021-07-04T20:01:40+32:00") modified_str_isotime = str_isotime[:-6] + "+00:00" try: return datetime.datetime.fromisoformat(modified_str_isotime) except ValueError: pass raise ValueError(f"Invalid isotime: {str_isotime}") def arc_join(*parts: list[str]) -> str: assert all(p[:1] != "/" and p[-1:] != "/" for p in parts), f"None of {parts} may start or end with '/'" return "/".join(p for p in parts if p) @dataclasses.dataclass(frozen=True) class VsArchPlatformConfig: arch: str configuration: str platform: str def extra_context(self): return { "ARCH": self.arch, "CONFIGURATION": self.configuration, "PLATFORM": self.platform, } @contextlib.contextmanager def chdir(path): original_cwd = os.getcwd() try: os.chdir(path) yield finally: os.chdir(original_cwd) class Executer: def __init__(self, root: Path, dry: bool=False): self.root = root self.dry = dry def run(self, cmd, cwd=None, env=None): logger.info("Executing args=%r", cmd) sys.stdout.flush() if not self.dry: subprocess.check_call(cmd, cwd=cwd or self.root, env=env, text=True) def check_output(self, cmd, cwd=None, dry_out=None, env=None, text=True): logger.info("Executing args=%r", cmd) sys.stdout.flush() if self.dry: return dry_out return subprocess.check_output(cmd, cwd=cwd or self.root, env=env, text=text) class SectionPrinter: @contextlib.contextmanager def group(self, title: str): print(f"{title}:") yield class GitHubSectionPrinter(SectionPrinter): def __init__(self): super().__init__() self.in_group = False @contextlib.contextmanager def group(self, title: str): print(f"::group::{title}") assert not self.in_group, "Can enter a group only once" self.in_group = True yield self.in_group = False print("::endgroup::") class VisualStudio: def __init__(self, executer: Executer, year: typing.Optional[str]=None): self.executer = executer self.vsdevcmd = self.find_vsdevcmd(year) self.msbuild = self.find_msbuild() @property def dry(self) -> bool: return self.executer.dry VS_YEAR_TO_VERSION = { "2022": 17, "2019": 16, "2017": 15, "2015": 14, "2013": 12, } def find_vsdevcmd(self, year: typing.Optional[str]=None) -> typing.Optional[Path]: vswhere_spec = ["-latest"] if year is not None: try: version = self.VS_YEAR_TO_VERSION[year] except KeyError: logger.error("Invalid Visual Studio year") return None vswhere_spec.extend(["-version", f"[{version},{version+1})"]) vswhere_cmd = ["vswhere"] + vswhere_spec + ["-property", "installationPath"] vs_install_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp").strip()) logger.info("VS install_path = %s", vs_install_path) assert vs_install_path.is_dir(), "VS installation path does not exist" vsdevcmd_path = vs_install_path / "Common7/Tools/vsdevcmd.bat" logger.info("vsdevcmd path = %s", vsdevcmd_path) if self.dry: vsdevcmd_path.parent.mkdir(parents=True, exist_ok=True) vsdevcmd_path.touch(exist_ok=True) assert vsdevcmd_path.is_file(), "vsdevcmd.bat batch file does not exist" return vsdevcmd_path def find_msbuild(self) -> typing.Optional[Path]: vswhere_cmd = ["vswhere", "-latest", "-requires", "Microsoft.Component.MSBuild", "-find", r"MSBuild\**\Bin\MSBuild.exe"] msbuild_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp/MSBuild.exe").strip()) logger.info("MSBuild path = %s", msbuild_path) if self.dry: msbuild_path.parent.mkdir(parents=True, exist_ok=True) msbuild_path.touch(exist_ok=True) assert msbuild_path.is_file(), "MSBuild.exe does not exist" return msbuild_path def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path]): assert projects, "Need at least one project to build" vsdev_cmd_str = f"\"{self.vsdevcmd}\" -arch={arch_platform.arch}" msbuild_cmd_str = " && ".join([f"\"{self.msbuild}\" \"{project}\" /m /p:BuildInParallel=true /p:Platform={arch_platform.platform} /p:Configuration={arch_platform.configuration}" for project in projects]) bat_contents = f"{vsdev_cmd_str} && {msbuild_cmd_str}\n" bat_path = Path(tempfile.gettempdir()) / "cmd.bat" with bat_path.open("w") as f: f.write(bat_contents) logger.info("Running cmd.exe script (%s): %s", bat_path, bat_contents) cmd = ["cmd.exe", "/D", "/E:ON", "/V:OFF", "/S", "/C", f"CALL {str(bat_path)}"] self.executer.run(cmd) class Archiver: def __init__(self, zip_path: typing.Optional[Path]=None, tgz_path: typing.Optional[Path]=None, txz_path: typing.Optional[Path]=None): self._zip_files = [] self._tar_files = [] self._added_files = set() if zip_path: self._zip_files.append(zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED)) if tgz_path: self._tar_files.append(tarfile.open(tgz_path, "w:gz")) if txz_path: self._tar_files.append(tarfile.open(txz_path, "w:xz")) @property def added_files(self) -> set[str]: return self._added_files def add_file_data(self, arcpath: str, data: bytes, mode: int, time: datetime.datetime): for zf in self._zip_files: file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second) zip_info = zipfile.ZipInfo(filename=arcpath, date_time=file_data_time) zip_info.external_attr = mode << 16 zip_info.compress_type = zipfile.ZIP_DEFLATED zf.writestr(zip_info, data=data) for tf in self._tar_files: tar_info = tarfile.TarInfo(arcpath) tar_info.type = tarfile.REGTYPE tar_info.mode = mode tar_info.size = len(data) tar_info.mtime = int(time.timestamp()) tf.addfile(tar_info, fileobj=io.BytesIO(data)) self._added_files.add(arcpath) def add_symlink(self, arcpath: str, target: str, time: datetime.datetime, files_for_zip): logger.debug("Adding symlink (target=%r) -> %s", target, arcpath) for zf in self._zip_files: file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second) for f in files_for_zip: zip_info = zipfile.ZipInfo(filename=f["arcpath"], date_time=file_data_time) zip_info.external_attr = f["mode"] << 16 zip_info.compress_type = zipfile.ZIP_DEFLATED zf.writestr(zip_info, data=f["data"]) for tf in self._tar_files: tar_info = tarfile.TarInfo(arcpath) tar_info.type = tarfile.SYMTYPE tar_info.mode = 0o777 tar_info.mtime = int(time.timestamp()) tar_info.linkname = target tf.addfile(tar_info) self._added_files.update(f["arcpath"] for f in files_for_zip) def add_git_hash(self, arcdir: str, commit: str, time: datetime.datetime): arcpath = arc_join(arcdir, GIT_HASH_FILENAME) data = f"{commit}\n".encode() self.add_file_data(arcpath=arcpath, data=data, mode=0o100644, time=time) def add_file_path(self, arcpath: str, path: Path): assert path.is_file(), f"{path} should be a file" logger.debug("Adding %s -> %s", path, arcpath) for zf in self._zip_files: zf.write(path, arcname=arcpath) for tf in self._tar_files: tf.add(path, arcname=arcpath) def add_file_directory(self, arcdirpath: str, dirpath: Path): assert dirpath.is_dir() if arcdirpath and arcdirpath[-1:] != "/": arcdirpath += "/" for f in dirpath.iterdir(): if f.is_file(): arcpath = f"{arcdirpath}{f.name}" logger.debug("Adding %s to %s", f, arcpath) self.add_file_path(arcpath=arcpath, path=f) def close(self): # Archiver is intentionally made invalid after this function del self._zip_files self._zip_files = None del self._tar_files self._tar_files = None def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() class NodeInArchive: def __init__(self, arcpath: str, path: typing.Optional[Path]=None, data: typing.Optional[bytes]=None, mode: typing.Optional[int]=None, symtarget: typing.Optional[str]=None, time: typing.Optional[datetime.datetime]=None, directory: bool=False): self.arcpath = arcpath self.path = path self.data = data self.mode = mode self.symtarget = symtarget self.time = time self.directory = directory @classmethod def from_fs(cls, arcpath: str, path: Path, mode: int=0o100644, time: typing.Optional[datetime.datetime]=None) -> "NodeInArchive": if time is None: time = datetime.datetime.fromtimestamp(os.stat(path).st_mtime) return cls(arcpath=arcpath, path=path, mode=mode) @classmethod def from_data(cls, arcpath: str, data: bytes, time: datetime.datetime) -> "NodeInArchive": return cls(arcpath=arcpath, data=data, time=time, mode=0o100644) @classmethod def from_text(cls, arcpath: str, text: str, time: datetime.datetime) -> "NodeInArchive": return cls.from_data(arcpath=arcpath, data=text.encode(), time=time) @classmethod def from_symlink(cls, arcpath: str, symtarget: str) -> "NodeInArchive": return cls(arcpath=arcpath, symtarget=symtarget) @classmethod def from_directory(cls, arcpath: str) -> "NodeInArchive": return cls(arcpath=arcpath, directory=True) def __repr__(self) -> str: return f"<{type(self).__name__}:arcpath={self.arcpath},path='{str(self.path)}',len(data)={len(self.data) if self.data else 'n/a'},directory={self.directory},symtarget={self.symtarget}>" def configure_file(path: Path, context: dict[str, str]) -> bytes: text = path.read_text() return configure_text(text, context=context).encode() def configure_text(text: str, context: dict[str, str]) -> str: original_text = text for txt, repl in context.items(): text = text.replace(f"@<@{txt}@>@", repl) success = all(thing not in text for thing in ("@<@", "@>@")) if not success: raise ValueError(f"Failed to configure {repr(original_text)}") return text def configure_text_list(text_list: list[str], context: dict[str, str]) -> list[str]: return [configure_text(text=e, context=context) for e in text_list] class ArchiveFileTree: def __init__(self): self._tree: dict[str, NodeInArchive] = {} def add_file(self, file: NodeInArchive): self._tree[file.arcpath] = file def __iter__(self) -> typing.Iterable[NodeInArchive]: yield from self._tree.values() def __contains__(self, value: str) -> bool: return value in self._tree def get_latest_mod_time(self) -> datetime.datetime: return max(item.time for item in self._tree.values() if item.time) def add_to_archiver(self, archive_base: str, archiver: Archiver): remaining_symlinks = set() added_files = dict() def calculate_symlink_target(s: NodeInArchive) -> str: dest_dir = os.path.dirname(s.arcpath) if dest_dir: dest_dir += "/" target = dest_dir + s.symtarget while True: new_target, n = re.subn(r"([^/]+/+[.]{2}/)", "", target) target = new_target if not n: break return target # Add files in first pass for arcpath, node in self._tree.items(): assert node is not None, f"{arcpath} -> node" if node.data is not None: archiver.add_file_data(arcpath=arc_join(archive_base, arcpath), data=node.data, time=node.time, mode=node.mode) assert node.arcpath is not None, f"{node=}" added_files[node.arcpath] = node elif node.path is not None: archiver.add_file_path(arcpath=arc_join(archive_base, arcpath), path=node.path) assert node.arcpath is not None, f"{node=}" added_files[node.arcpath] = node elif node.symtarget is not None: remaining_symlinks.add(node) elif node.directory: pass else: raise ValueError(f"Invalid Archive Node: {repr(node)}") assert None not in added_files # Resolve symlinks in second pass: zipfile does not support symlinks, so add files to zip archive while True: if not remaining_symlinks: break symlinks_this_time = set() extra_added_files = {} for symlink in remaining_symlinks: symlink_files_for_zip = {} symlink_target_path = calculate_symlink_target(symlink) if symlink_target_path in added_files: symlink_files_for_zip[symlink.arcpath] = added_files[symlink_target_path] else: symlink_target_path_slash = symlink_target_path + "/" for added_file in added_files: if added_file.startswith(symlink_target_path_slash): path_in_symlink = symlink.arcpath + "/" + added_file.removeprefix(symlink_target_path_slash) symlink_files_for_zip[path_in_symlink] = added_files[added_file] if symlink_files_for_zip: symlinks_this_time.add(symlink) extra_added_files.update(symlink_files_for_zip) files_for_zip = [{"arcpath": f"{archive_base}/{sym_path}", "data": sym_info.data, "mode": sym_info.mode} for sym_path, sym_info in symlink_files_for_zip.items()] archiver.add_symlink(arcpath=f"{archive_base}/{symlink.arcpath}", target=symlink.symtarget, time=symlink.time, files_for_zip=files_for_zip) # if not symlinks_this_time: # logger.info("files added: %r", set(path for path in added_files.keys())) assert symlinks_this_time, f"No targets found for symlinks: {remaining_symlinks}" remaining_symlinks.difference_update(symlinks_this_time) added_files.update(extra_added_files) def add_directory_tree(self, arc_dir: str, path: Path, time: datetime.datetime): assert path.is_dir() for files_dir, _, filenames in os.walk(path): files_dir_path = Path(files_dir) rel_files_path = files_dir_path.relative_to(path) for filename in filenames: self.add_file(NodeInArchive.from_fs(arcpath=arc_join(arc_dir, str(rel_files_path), filename), path=files_dir_path / filename, time=time)) def _add_files_recursively(self, arc_dir: str, paths: list[Path], time: datetime.datetime): logger.debug(f"_add_files_recursively({arc_dir=} {paths=})") for path in paths: arcpath = arc_join(arc_dir, path.name) if path.is_file(): logger.debug("Adding %s as %s", path, arcpath) self.add_file(NodeInArchive.from_fs(arcpath=arcpath, path=path, time=time)) elif path.is_dir(): self._add_files_recursively(arc_dir=arc_join(arc_dir, path.name), paths=list(path.iterdir()), time=time) else: raise ValueError(f"Unsupported file type to add recursively: {path}") def add_file_mapping(self, arc_dir: str, file_mapping: dict[str, list[str]], file_mapping_root: Path, context: dict[str, str], time: datetime.datetime): for meta_rel_destdir, meta_file_globs in file_mapping.items(): rel_destdir = configure_text(meta_rel_destdir, context=context) assert "@" not in rel_destdir, f"archive destination should not contain an @ after configuration ({repr(meta_rel_destdir)}->{repr(rel_destdir)})" for meta_file_glob in meta_file_globs: file_glob = configure_text(meta_file_glob, context=context) assert "@" not in rel_destdir, f"archive glob should not contain an @ after configuration ({repr(meta_file_glob)}->{repr(file_glob)})" if ":" in file_glob: original_path, new_filename = file_glob.rsplit(":", 1) assert ":" not in original_path, f"Too many ':' in {repr(file_glob)}" assert "/" not in new_filename, f"New filename cannot contain a '/' in {repr(file_glob)}" path = file_mapping_root / original_path arcpath = arc_join(arc_dir, rel_destdir, new_filename) if path.suffix == ".in": data = configure_file(path, context=context) logger.debug("Adding processed %s -> %s", path, arcpath) self.add_file(NodeInArchive.from_data(arcpath=arcpath, data=data, time=time)) else: logger.debug("Adding %s -> %s", path, arcpath) self.add_file(NodeInArchive.from_fs(arcpath=arcpath, path=path, time=time)) else: relative_file_paths = glob.glob(file_glob, root_dir=file_mapping_root) assert relative_file_paths, f"Glob '{file_glob}' does not match any file" self._add_files_recursively(arc_dir=arc_join(arc_dir, rel_destdir), paths=[file_mapping_root / p for p in relative_file_paths], time=time) class SourceCollector: # TreeItem = collections.namedtuple("TreeItem", ("path", "mode", "data", "symtarget", "directory", "time")) def __init__(self, root: Path, commit: str, filter: typing.Optional[Callable[[str], bool]], executer: Executer): self.root = root self.commit = commit self.filter = filter self.executer = executer def get_archive_file_tree(self) -> ArchiveFileTree: git_archive_args = ["git", "archive", "--format=tar.gz", self.commit, "-o", "/dev/stdout"] logger.info("Executing args=%r", git_archive_args) contents_tgz = subprocess.check_output(git_archive_args, cwd=self.root, text=False) tar_archive = tarfile.open(fileobj=io.BytesIO(contents_tgz), mode="r:gz") filenames = tuple(m.name for m in tar_archive if (m.isfile() or m.issym())) file_times = self._get_file_times(paths=filenames) git_contents = ArchiveFileTree() for ti in tar_archive: if self.filter and not self.filter(ti.name): continue data = None symtarget = None directory = False file_time = None if ti.isfile(): contents_file = tar_archive.extractfile(ti.name) data = contents_file.read() file_time = file_times[ti.name] elif ti.issym(): symtarget = ti.linkname file_time = file_times[ti.name] elif ti.isdir(): directory = True else: raise ValueError(f"{ti.name}: unknown type") node = NodeInArchive(arcpath=ti.name, data=data, mode=ti.mode, symtarget=symtarget, time=file_time, directory=directory) git_contents.add_file(node) return git_contents def _get_file_times(self, paths: tuple[str, ...]) -> dict[str, datetime.datetime]: dry_out = textwrap.dedent("""\ time=2024-03-14T15:40:25-07:00 M\tCMakeLists.txt """) git_log_out = self.executer.check_output(["git", "log", "--name-status", '--pretty=time=%cI', self.commit], dry_out=dry_out, cwd=self.root).splitlines(keepends=False) current_time = None set_paths = set(paths) path_times: dict[str, datetime.datetime] = {} for line in git_log_out: if not line: continue if line.startswith("time="): current_time = safe_isotime_to_datetime(line.removeprefix("time=")) continue mod_type, file_paths = line.split(maxsplit=1) assert current_time is not None for file_path in file_paths.split("\t"): if file_path in set_paths and file_path not in path_times: path_times[file_path] = current_time # FIXME: find out why some files are not shown in "git log" # assert set(path_times.keys()) == set_paths if set(path_times.keys()) != set_paths: found_times = set(path_times.keys()) paths_without_times = set_paths.difference(found_times) logger.warning("No times found for these paths: %s", paths_without_times) max_time = max(time for time in path_times.values()) for path in paths_without_times: path_times[path] = max_time return path_times class Releaser: def __init__(self, release_info: dict, commit: str, revision: str, root: Path, dist_path: Path, section_printer: SectionPrinter, executer: Executer, cmake_generator: str, deps_path: Path, overwrite: bool, github: bool, fast: bool): self.release_info = release_info self.project = release_info["name"] self.version = self.extract_sdl_version(root=root, release_info=release_info) self.root = root self.commit = commit self.revision = revision self.dist_path = dist_path self.section_printer = section_printer self.executer = executer self.cmake_generator = cmake_generator self.cpu_count = multiprocessing.cpu_count() self.deps_path = deps_path self.overwrite = overwrite self.github = github self.fast = fast self.arc_time = datetime.datetime.now() self.artifacts: dict[str, Path] = {} def get_context(self, extra_context: typing.Optional[dict[str, str]]=None) -> dict[str, str]: ctx = { "PROJECT_NAME": self.project, "PROJECT_VERSION": self.version, "PROJECT_COMMIT": self.commit, "PROJECT_REVISION": self.revision, "PROJECT_ROOT": str(self.root), } if extra_context: ctx.update(extra_context) return ctx @property def dry(self) -> bool: return self.executer.dry def prepare(self): logger.debug("Creating dist folder") self.dist_path.mkdir(parents=True, exist_ok=True) @classmethod def _path_filter(cls, path: str) -> bool: if ".gitmodules" in path: return True if path.startswith(".git"): return False return True @classmethod def _external_repo_path_filter(cls, path: str) -> bool: if not cls._path_filter(path): return False if path.startswith("test/") or path.startswith("tests/"): return False return True def create_source_archives(self) -> None: source_collector = SourceCollector(root=self.root, commit=self.commit, executer=self.executer, filter=self._path_filter) print(f"Collecting sources of {self.project}...") archive_tree: ArchiveFileTree = source_collector.get_archive_file_tree() latest_mod_time = archive_tree.get_latest_mod_time() archive_tree.add_file(NodeInArchive.from_text(arcpath=REVISION_TXT, text=f"{self.revision}\n", time=latest_mod_time)) archive_tree.add_file(NodeInArchive.from_text(arcpath=f"{GIT_HASH_FILENAME}", text=f"{self.commit}\n", time=latest_mod_time)) archive_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["source"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=latest_mod_time) if "Makefile.am" in archive_tree: patched_time = latest_mod_time + datetime.timedelta(minutes=1) print(f"Makefile.am detected -> touching aclocal.m4, */Makefile.in, configure") for node_data in archive_tree: arc_name = os.path.basename(node_data.arcpath) arc_name_we, arc_name_ext = os.path.splitext(arc_name) if arc_name in ("aclocal.m4", "configure", "Makefile.in"): print(f"Bumping time of {node_data.arcpath}") node_data.time = patched_time archive_base = f"{self.project}-{self.version}" zip_path = self.dist_path / f"{archive_base}.zip" tgz_path = self.dist_path / f"{archive_base}.tar.gz" txz_path = self.dist_path / f"{archive_base}.tar.xz" logger.info("Creating zip/tgz/txz source archives ...") if self.dry: zip_path.touch() tgz_path.touch() txz_path.touch() else: with Archiver(zip_path=zip_path, tgz_path=tgz_path, txz_path=txz_path) as archiver: print(f"Adding source files of {self.project}...") archive_tree.add_to_archiver(archive_base=archive_base, archiver=archiver) for extra_repo in self.release_info["source"].get("extra-repos", []): extra_repo_root = self.root / extra_repo assert (extra_repo_root / ".git").exists(), f"{extra_repo_root} must be a git repo" extra_repo_commit = self.executer.check_output(["git", "rev-parse", "HEAD"], dry_out=f"gitsha-extra-repo-{extra_repo}", cwd=extra_repo_root).strip() extra_repo_source_collector = SourceCollector(root=extra_repo_root, commit=extra_repo_commit, executer=self.executer, filter=self._external_repo_path_filter) print(f"Collecting sources of {extra_repo} ...") extra_repo_archive_tree = extra_repo_source_collector.get_archive_file_tree() print(f"Adding source files of {extra_repo} ...") extra_repo_archive_tree.add_to_archiver(archive_base=f"{archive_base}/{extra_repo}", archiver=archiver) for file in self.release_info["source"]["checks"]: assert f"{archive_base}/{file}" in archiver.added_files, f"'{archive_base}/{file}' must exist" logger.info("... done") self.artifacts["src-zip"] = zip_path self.artifacts["src-tar-gz"] = tgz_path self.artifacts["src-tar-xz"] = txz_path if not self.dry: with tgz_path.open("r+b") as f: # Zero the embedded timestamp in the gzip'ed tarball f.seek(4, 0) f.write(b"\x00\x00\x00\x00") def create_dmg(self, configuration: str="Release") -> None: dmg_in = self.root / self.release_info["dmg"]["path"] xcode_project = self.root / self.release_info["dmg"]["project"] assert xcode_project.is_dir(), f"{xcode_project} must be a directory" assert (xcode_project / "project.pbxproj").is_file, f"{xcode_project} must contain project.pbxproj" if not self.fast: dmg_in.unlink(missing_ok=True) build_xcconfig = self.release_info["dmg"].get("build-xcconfig") if build_xcconfig: shutil.copy(self.root / build_xcconfig, xcode_project.parent / "build.xcconfig") xcode_scheme = self.release_info["dmg"].get("scheme") xcode_target = self.release_info["dmg"].get("target") assert xcode_scheme or xcode_target, "dmg needs scheme or target" assert not (xcode_scheme and xcode_target), "dmg cannot have both scheme and target set" if xcode_scheme: scheme_or_target = "-scheme" target_like = xcode_scheme else: scheme_or_target = "-target" target_like = xcode_target self.executer.run(["xcodebuild", "ONLY_ACTIVE_ARCH=NO", "-project", xcode_project, scheme_or_target, target_like, "-configuration", configuration]) if self.dry: dmg_in.parent.mkdir(parents=True, exist_ok=True) dmg_in.touch() assert dmg_in.is_file(), f"{self.project}.dmg was not created by xcodebuild" dmg_out = self.dist_path / f"{self.project}-{self.version}.dmg" shutil.copy(dmg_in, dmg_out) self.artifacts["dmg"] = dmg_out @property def git_hash_data(self) -> bytes: return f"{self.commit}\n".encode() def create_mingw_archives(self) -> None: build_type = "Release" build_parent_dir = self.root / "build-mingw" ARCH_TO_GNU_ARCH = { # "arm64": "aarch64", "x86": "i686", "x64": "x86_64", } ARCH_TO_TRIPLET = { # "arm64": "aarch64-w64-mingw32", "x86": "i686-w64-mingw32", "x64": "x86_64-w64-mingw32", } new_env = dict(os.environ) cmake_prefix_paths = [] mingw_deps_path = self.deps_path / "mingw-deps" if "dependencies" in self.release_info["mingw"]: shutil.rmtree(mingw_deps_path, ignore_errors=True) mingw_deps_path.mkdir() for triplet in ARCH_TO_TRIPLET.values(): (mingw_deps_path / triplet).mkdir() def extract_filter(member: tarfile.TarInfo, path: str, /): if member.name.startswith("SDL"): member.name = "/".join(Path(member.name).parts[1:]) return member for dep in self.release_info.get("dependencies", {}): extract_path = mingw_deps_path / f"extract-{dep}" extract_path.mkdir() with chdir(extract_path): tar_path = self.deps_path / glob.glob(self.release_info["mingw"]["dependencies"][dep]["artifact"], root_dir=self.deps_path)[0] logger.info("Extracting %s to %s", tar_path, mingw_deps_path) assert tar_path.suffix in (".gz", ".xz") with tarfile.open(tar_path, mode=f"r:{tar_path.suffix.strip('.')}") as tarf: tarf.extractall(filter=extract_filter) for arch, triplet in ARCH_TO_TRIPLET.items(): install_cmd = self.release_info["mingw"]["dependencies"][dep]["install-command"] extra_configure_data = { "ARCH": ARCH_TO_GNU_ARCH[arch], "TRIPLET": triplet, "PREFIX": str(mingw_deps_path / triplet), } install_cmd = configure_text(install_cmd, context=self.get_context(extra_configure_data)) self.executer.run(shlex.split(install_cmd), cwd=str(extract_path)) dep_binpath = mingw_deps_path / triplet / "bin" assert dep_binpath.is_dir(), f"{dep_binpath} for PATH should exist" dep_pkgconfig = mingw_deps_path / triplet / "lib/pkgconfig" assert dep_pkgconfig.is_dir(), f"{dep_pkgconfig} for PKG_CONFIG_PATH should exist" new_env["PATH"] = os.pathsep.join([str(dep_binpath), new_env["PATH"]]) new_env["PKG_CONFIG_PATH"] = str(dep_pkgconfig) cmake_prefix_paths.append(mingw_deps_path) new_env["CFLAGS"] = f"-O2 -ffile-prefix-map={self.root}=/src/{self.project}" new_env["CXXFLAGS"] = f"-O2 -ffile-prefix-map={self.root}=/src/{self.project}" assert any(system in self.release_info["mingw"] for system in ("autotools", "cmake")) assert not all(system in self.release_info["mingw"] for system in ("autotools", "cmake")) mingw_archs = set() arc_root = f"{self.project}-{self.version}" archive_file_tree = ArchiveFileTree() if "autotools" in self.release_info["mingw"]: for arch in self.release_info["mingw"]["autotools"]["archs"]: triplet = ARCH_TO_TRIPLET[arch] new_env["CC"] = f"{triplet}-gcc" new_env["CXX"] = f"{triplet}-g++" new_env["RC"] = f"{triplet}-windres" assert arch not in mingw_archs mingw_archs.add(arch) build_path = build_parent_dir / f"build-{triplet}" install_path = build_parent_dir / f"install-{triplet}" shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) context = self.get_context({ "ARCH": arch, "DEP_PREFIX": str(mingw_deps_path / triplet), }) extra_args = configure_text_list(text_list=self.release_info["mingw"]["autotools"]["args"], context=context) with self.section_printer.group(f"Configuring MinGW {triplet} (autotools)"): assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" self.executer.run([ self.root / "configure", f"--prefix={install_path}", f"--includedir=${{prefix}}/include", f"--libdir=${{prefix}}/lib", f"--bindir=${{prefix}}/bin", f"--host={triplet}", f"--build=x86_64-none-linux-gnu", "CFLAGS=-O2", "CXXFLAGS=-O2", "LDFLAGS=-Wl,-s", ] + extra_args, cwd=build_path, env=new_env) with self.section_printer.group(f"Build MinGW {triplet} (autotools)"): self.executer.run(["make", f"-j{self.cpu_count}"], cwd=build_path, env=new_env) with self.section_printer.group(f"Install MinGW {triplet} (autotools)"): self.executer.run(["make", "install"], cwd=build_path, env=new_env) archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) print("Recording arch-dependent extra files for MinGW development archive ...") extra_context = { "TRIPLET": ARCH_TO_TRIPLET[arch], } archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["autotools"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) if "cmake" in self.release_info["mingw"]: assert self.release_info["mingw"]["cmake"]["shared-static"] in ("args", "both") for arch in self.release_info["mingw"]["cmake"]["archs"]: triplet = ARCH_TO_TRIPLET[arch] new_env["CC"] = f"{triplet}-gcc" new_env["CXX"] = f"{triplet}-g++" new_env["RC"] = f"{triplet}-windres" assert arch not in mingw_archs mingw_archs.add(arch) context = self.get_context({ "ARCH": arch, "DEP_PREFIX": str(mingw_deps_path / triplet), }) extra_args = configure_text_list(text_list=self.release_info["mingw"]["cmake"]["args"], context=context) build_path = build_parent_dir / f"build-{triplet}" install_path = build_parent_dir / f"install-{triplet}" shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) if self.release_info["mingw"]["cmake"]["shared-static"] == "args": args_for_shared_static = ([], ) elif self.release_info["mingw"]["cmake"]["shared-static"] == "both": args_for_shared_static = (["-DBUILD_SHARED_LIBS=ON"], ["-DBUILD_SHARED_LIBS=OFF"]) for arg_for_shared_static in args_for_shared_static: with self.section_printer.group(f"Configuring MinGW {triplet} (CMake)"): assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" self.executer.run([ f"cmake", f"-S", str(self.root), "-B", str(build_path), f"-DCMAKE_BUILD_TYPE={build_type}", f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f"-DCMAKE_PREFIX_PATH={mingw_deps_path / triplet}", f"-DCMAKE_INSTALL_PREFIX={install_path}", f"-DCMAKE_INSTALL_INCLUDEDIR=include", f"-DCMAKE_INSTALL_LIBDIR=lib", f"-DCMAKE_INSTALL_BINDIR=bin", f"-DCMAKE_INSTALL_DATAROOTDIR=share", f"-DCMAKE_TOOLCHAIN_FILE={self.root}/build-scripts/cmake-toolchain-mingw64-{ARCH_TO_GNU_ARCH[arch]}.cmake", f"-G{self.cmake_generator}", ] + extra_args + ([] if self.fast else ["--fresh"]) + arg_for_shared_static, cwd=build_path, env=new_env) with self.section_printer.group(f"Build MinGW {triplet} (CMake)"): self.executer.run(["cmake", "--build", str(build_path), "--verbose", "--config", build_type], cwd=build_path, env=new_env) with self.section_printer.group(f"Install MinGW {triplet} (CMake)"): self.executer.run(["cmake", "--install", str(build_path)], cwd=build_path, env=new_env) archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) print("Recording arch-dependent extra files for MinGW development archive ...") extra_context = { "TRIPLET": ARCH_TO_TRIPLET[arch], } archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["cmake"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) print("... done") print("Recording extra files for MinGW development archive ...") archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) print("... done") print("Creating zip/tgz/txz development archives ...") zip_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.zip" tgz_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.tar.gz" txz_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.tar.xz" with Archiver(zip_path=zip_path, tgz_path=tgz_path, txz_path=txz_path) as archiver: archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) print("... done") self.artifacts["mingw-devel-zip"] = zip_path self.artifacts["mingw-devel-tar-gz"] = tgz_path self.artifacts["mingw-devel-tar-xz"] = txz_path def _detect_android_api(self, android_home: str) -> typing.Optional[int]: platform_dirs = list(Path(p) for p in glob.glob(f"{android_home}/platforms/android-*")) re_platform = re.compile("android-([0-9]+)") platform_versions = [] for platform_dir in platform_dirs: logger.debug("Found Android Platform SDK: %s", platform_dir) if m:= re_platform.match(platform_dir.name): platform_versions.append(int(m.group(1))) platform_versions.sort() logger.info("Available platform versions: %s", platform_versions) platform_versions = list(filter(lambda v: v >= self._android_api_minimum, platform_versions)) logger.info("Valid platform versions (>=%d): %s", self._android_api_minimum, platform_versions) if not platform_versions: return None android_api = platform_versions[0] logger.info("Selected API version %d", android_api) return android_api def _get_prefab_json_text(self) -> str: return textwrap.dedent(f"""\ {{ "schema_version": 2, "name": "{self.project}", "version": "{self.version}", "dependencies": [] }} """) def _get_prefab_module_json_text(self, library_name: typing.Optional[str], export_libraries: list[str]) -> str: for lib in export_libraries: assert isinstance(lib, str), f"{lib} must be a string" module_json_dict = { "export_libraries": export_libraries, } if library_name: module_json_dict["library_name"] = f"lib{library_name}" return json.dumps(module_json_dict, indent=4) @property def _android_api_minimum(self): return self.release_info["android"]["api-minimum"] @property def _android_api_target(self): return self.release_info["android"]["api-target"] @property def _android_ndk_minimum(self): return self.release_info["android"]["ndk-minimum"] def _get_prefab_abi_json_text(self, abi: str, cpp: bool, shared: bool) -> str: abi_json_dict = { "abi": abi, "api": self._android_api_minimum, "ndk": self._android_ndk_minimum, "stl": "c++_shared" if cpp else "none", "static": not shared, } return json.dumps(abi_json_dict, indent=4) def _get_android_manifest_text(self) -> str: return textwrap.dedent(f"""\ """) def create_android_archives(self, android_api: int, android_home: Path, android_ndk_home: Path) -> None: cmake_toolchain_file = Path(android_ndk_home) / "build/cmake/android.toolchain.cmake" if not cmake_toolchain_file.exists(): logger.error("CMake toolchain file does not exist (%s)", cmake_toolchain_file) raise SystemExit(1) aar_path = self.dist_path / f"{self.project}-{self.version}.aar" android_abis = self.release_info["android"]["abis"] java_jars_added = False module_data_added = False android_deps_path = self.deps_path / "android-deps" shutil.rmtree(android_deps_path, ignore_errors=True) for dep, depinfo in self.release_info["android"].get("dependencies", {}).items(): android_aar = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] with self.section_printer.group(f"Extracting Android dependency {dep} ({android_aar.name})"): self.executer.run([sys.executable, str(android_aar), "-o", str(android_deps_path)]) for module_name, module_info in self.release_info["android"]["modules"].items(): assert "type" in module_info and module_info["type"] in ("interface", "library"), f"module {module_name} must have a valid type" archive_file_tree = ArchiveFileTree() for android_abi in android_abis: with self.section_printer.group(f"Building for Android {android_api} {android_abi}"): build_dir = self.root / "build-android" / f"{android_abi}-build" install_dir = self.root / "install-android" / f"{android_abi}-install" shutil.rmtree(install_dir, ignore_errors=True) assert not install_dir.is_dir(), f"{install_dir} should not exist prior to build" build_type = "Release" cmake_args = [ "cmake", "-S", str(self.root), "-B", str(build_dir), f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f"-DCMAKE_TOOLCHAIN_FILE={cmake_toolchain_file}", f"-DCMAKE_PREFIX_PATH={str(android_deps_path)}", f"-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH", f"-DANDROID_HOME={android_home}", f"-DANDROID_PLATFORM={android_api}", f"-DANDROID_ABI={android_abi}", "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", f"-DCMAKE_INSTALL_PREFIX={install_dir}", "-DCMAKE_INSTALL_INCLUDEDIR=include ", "-DCMAKE_INSTALL_LIBDIR=lib", "-DCMAKE_INSTALL_DATAROOTDIR=share", f"-DCMAKE_BUILD_TYPE={build_type}", f"-G{self.cmake_generator}", ] + self.release_info["android"]["cmake"]["args"] + ([] if self.fast else ["--fresh"]) build_args = [ "cmake", "--build", str(build_dir), "--verbose", "--config", build_type, ] install_args = [ "cmake", "--install", str(build_dir), "--config", build_type, ] self.executer.run(cmake_args) self.executer.run(build_args) self.executer.run(install_args) for module_name, module_info in self.release_info["android"]["modules"].items(): arcdir_prefab_module = f"prefab/modules/{module_name}" if module_info["type"] == "library": library = install_dir / module_info["library"] assert library.suffix in (".so", ".a") assert library.is_file(), f"CMake should have built library '{library}' for module {module_name}" arcdir_prefab_libs = f"{arcdir_prefab_module}/libs/android.{android_abi}" archive_file_tree.add_file(NodeInArchive.from_fs(arcpath=f"{arcdir_prefab_libs}/{library.name}", path=library, time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_text(arcpath=f"{arcdir_prefab_libs}/abi.json", text=self._get_prefab_abi_json_text(abi=android_abi, cpp=False, shared=library.suffix == ".so"), time=self.arc_time)) if not module_data_added: library_name = None if module_info["type"] == "library": library_name = Path(module_info["library"]).stem.removeprefix("lib") export_libraries = module_info.get("export-libraries", []) archive_file_tree.add_file(NodeInArchive.from_text(arcpath=arc_join(arcdir_prefab_module, "module.json"), text=self._get_prefab_module_json_text(library_name=library_name, export_libraries=export_libraries), time=self.arc_time)) arcdir_prefab_include = f"prefab/modules/{module_name}/include" if "includes" in module_info: archive_file_tree.add_file_mapping(arc_dir=arcdir_prefab_include, file_mapping=module_info["includes"], file_mapping_root=install_dir, context=self.get_context(), time=self.arc_time) else: archive_file_tree.add_file(NodeInArchive.from_text(arcpath=arc_join(arcdir_prefab_include, ".keep"), text="\n", time=self.arc_time)) module_data_added = True if not java_jars_added: java_jars_added = True if "jars" in self.release_info["android"]: classes_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["classes"], context=self.get_context()) sources_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["sources"], context=self.get_context()) doc_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["doc"], context=self.get_context()) assert classes_jar_path.is_file(), f"CMake should have compiled the java sources and archived them into a JAR ({classes_jar_path})" assert sources_jar_path.is_file(), f"CMake should have archived the java sources into a JAR ({sources_jar_path})" assert doc_jar_path.is_file(), f"CMake should have archived javadoc into a JAR ({doc_jar_path})" archive_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes.jar", path=classes_jar_path, time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes-sources.jar", path=sources_jar_path, time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes-doc.jar", path=doc_jar_path, time=self.arc_time)) assert ("jars" in self.release_info["android"] and java_jars_added) or "jars" not in self.release_info["android"], "Must have archived java JAR archives" archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["android"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) archive_file_tree.add_file(NodeInArchive.from_text(arcpath="prefab/prefab.json", text=self._get_prefab_json_text(), time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_text(arcpath="AndroidManifest.xml", text=self._get_android_manifest_text(), time=self.arc_time)) with Archiver(zip_path=aar_path) as archiver: archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) archiver.add_git_hash(arcdir="", commit=self.commit, time=self.arc_time) self.artifacts[f"android-aar"] = aar_path def download_dependencies(self): shutil.rmtree(self.deps_path, ignore_errors=True) self.deps_path.mkdir(parents=True) if self.github: with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"dep-path={self.deps_path.absolute()}\n") for dep, depinfo in self.release_info.get("dependencies", {}).items(): startswith = depinfo["startswith"] dep_repo = depinfo["repo"] # FIXME: dropped "--exclude-pre-releases" dep_string_data = self.executer.check_output(["gh", "-R", dep_repo, "release", "list", "--exclude-drafts", "--json", "name,createdAt,tagName", "--jq", f'[.[]|select(.name|startswith("{startswith}"))]|max_by(.createdAt)']).strip() dep_data = json.loads(dep_string_data) dep_tag = dep_data["tagName"] dep_version = dep_data["name"] logger.info("Download dependency %s version %s (tag=%s) ", dep, dep_version, dep_tag) self.executer.run(["gh", "-R", dep_repo, "release", "download", dep_tag], cwd=self.deps_path) if self.github: with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"dep-{dep.lower()}-version={dep_version}\n") def verify_dependencies(self): for dep, depinfo in self.release_info.get("dependencies", {}).items(): if "mingw" in self.release_info: mingw_matches = glob.glob(self.release_info["mingw"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(mingw_matches) == 1, f"Exactly one archive matches mingw {dep} dependency: {mingw_matches}" if "dmg" in self.release_info: dmg_matches = glob.glob(self.release_info["dmg"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(dmg_matches) == 1, f"Exactly one archive matches dmg {dep} dependency: {dmg_matches}" if "msvc" in self.release_info: msvc_matches = glob.glob(self.release_info["msvc"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(msvc_matches) == 1, f"Exactly one archive matches msvc {dep} dependency: {msvc_matches}" if "android" in self.release_info: android_matches = glob.glob(self.release_info["android"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(android_matches) == 1, f"Exactly one archive matches msvc {dep} dependency: {msvc_matches}" @staticmethod def _arch_to_vs_platform(arch: str, configuration: str="Release") -> VsArchPlatformConfig: ARCH_TO_VS_PLATFORM = { "x86": VsArchPlatformConfig(arch="x86", platform="Win32", configuration=configuration), "x64": VsArchPlatformConfig(arch="x64", platform="x64", configuration=configuration), "arm64": VsArchPlatformConfig(arch="arm64", platform="ARM64", configuration=configuration), } return ARCH_TO_VS_PLATFORM[arch] def build_msvc(self): with self.section_printer.group("Find Visual Studio"): vs = VisualStudio(executer=self.executer) for arch in self.release_info["msvc"].get("msbuild", {}).get("archs", []): self._build_msvc_msbuild(arch_platform=self._arch_to_vs_platform(arch=arch), vs=vs) if "cmake" in self.release_info["msvc"]: deps_path = self.root / "msvc-deps" shutil.rmtree(deps_path, ignore_errors=True) dep_roots = [] for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): dep_extract_path = deps_path / f"extract-{dep}" msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] with zipfile.ZipFile(msvc_zip, "r") as zf: zf.extractall(dep_extract_path) contents_msvc_zip = glob.glob(str(dep_extract_path / "*")) assert len(contents_msvc_zip) == 1, f"There must be exactly one root item in the root directory of {dep}" dep_roots.append(contents_msvc_zip[0]) for arch in self.release_info["msvc"].get("cmake", {}).get("archs", []): self._build_msvc_cmake(arch_platform=self._arch_to_vs_platform(arch=arch), dep_roots=dep_roots) with self.section_printer.group("Create SDL VC development zip"): self._build_msvc_devel() def _build_msvc_msbuild(self, arch_platform: VsArchPlatformConfig, vs: VisualStudio): platform_context = self.get_context(arch_platform.extra_context()) for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] src_globs = [configure_text(instr["src"], context=platform_context) for instr in depinfo["copy"]] with zipfile.ZipFile(msvc_zip, "r") as zf: for member in zf.namelist(): member_path = "/".join(Path(member).parts[1:]) for src_i, src_glob in enumerate(src_globs): if fnmatch.fnmatch(member_path, src_glob): dst = (self.root / configure_text(depinfo["copy"][src_i]["dst"], context=platform_context)).resolve() / Path(member_path).name zip_data = zf.read(member) if dst.exists(): identical = False if dst.is_file(): orig_bytes = dst.read_bytes() if orig_bytes == zip_data: identical = True if not identical: logger.warning("Extracting dependency %s, will cause %s to be overwritten", dep, dst) if not self.overwrite: raise RuntimeError("Run with --overwrite to allow overwriting") logger.debug("Extracting %s -> %s", member, dst) dst.parent.mkdir(exist_ok=True, parents=True) dst.write_bytes(zip_data) prebuilt_paths = set(self.root / full_prebuilt_path for prebuilt_path in self.release_info["msvc"]["msbuild"].get("prebuilt", []) for full_prebuilt_path in glob.glob(configure_text(prebuilt_path, context=platform_context), root_dir=self.root)) msbuild_paths = set(self.root / configure_text(f, context=platform_context) for file_mapping in (self.release_info["msvc"]["msbuild"]["files-lib"], self.release_info["msvc"]["msbuild"]["files-devel"]) for files_list in file_mapping.values() for f in files_list) assert prebuilt_paths.issubset(msbuild_paths), f"msvc.msbuild.prebuilt must be a subset of (msvc.msbuild.files-lib, msvc.msbuild.files-devel)" built_paths = msbuild_paths.difference(prebuilt_paths) logger.info("MSbuild builds these files, to be included in the package: %s", built_paths) if not self.fast: for b in built_paths: b.unlink(missing_ok=True) rel_projects: list[str] = self.release_info["msvc"]["msbuild"]["projects"] projects = list(self.root / p for p in rel_projects) directory_build_props_src_relpath = self.release_info["msvc"]["msbuild"].get("directory-build-props") for project in projects: dir_b_props = project.parent / "Directory.Build.props" dir_b_props.unlink(missing_ok = True) if directory_build_props_src_relpath: src = self.root / directory_build_props_src_relpath logger.debug("Copying %s -> %s", src, dir_b_props) shutil.copy(src=src, dst=dir_b_props) with self.section_printer.group(f"Build {arch_platform.arch} VS binary"): vs.build(arch_platform=arch_platform, projects=projects) if self.dry: for b in built_paths: b.parent.mkdir(parents=True, exist_ok=True) b.touch() for b in built_paths: assert b.is_file(), f"{b} has not been created" b.parent.mkdir(parents=True, exist_ok=True) b.touch() zip_path = self.dist_path / f"{self.project}-{self.version}-win32-{arch_platform.arch}.zip" zip_path.unlink(missing_ok=True) logger.info("Collecting files...") archive_file_tree = ArchiveFileTree() archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["msbuild"]["files-lib"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["files-lib"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) logger.info("Writing to %s", zip_path) with Archiver(zip_path=zip_path) as archiver: arc_root = f"" archive_file_tree.add_to_archiver(archive_base=arc_root, archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) self.artifacts[f"VC-{arch_platform.arch}"] = zip_path for p in built_paths: assert p.is_file(), f"{p} should exist" def _arch_platform_to_build_path(self, arch_platform: VsArchPlatformConfig) -> Path: return self.root / f"build-vs-{arch_platform.arch}" def _arch_platform_to_install_path(self, arch_platform: VsArchPlatformConfig) -> Path: return self._arch_platform_to_build_path(arch_platform) / "prefix" def _build_msvc_cmake(self, arch_platform: VsArchPlatformConfig, dep_roots: list[Path]): build_path = self._arch_platform_to_build_path(arch_platform) install_path = self._arch_platform_to_install_path(arch_platform) platform_context = self.get_context(extra_context=arch_platform.extra_context()) build_type = "Release" built_paths = set(install_path / configure_text(f, context=platform_context) for file_mapping in (self.release_info["msvc"]["cmake"]["files-lib"], self.release_info["msvc"]["cmake"]["files-devel"]) for files_list in file_mapping.values() for f in files_list) logger.info("CMake builds these files, to be included in the package: %s", built_paths) if not self.fast: for b in built_paths: b.unlink(missing_ok=True) shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) with self.section_printer.group(f"Configure VC CMake project for {arch_platform.arch}"): self.executer.run([ "cmake", "-S", str(self.root), "-B", str(build_path), "-A", arch_platform.platform, "-DCMAKE_INSTALL_BINDIR=bin", "-DCMAKE_INSTALL_DATAROOTDIR=share", "-DCMAKE_INSTALL_INCLUDEDIR=include", "-DCMAKE_INSTALL_LIBDIR=lib", f"-DCMAKE_BUILD_TYPE={build_type}", f"-DCMAKE_INSTALL_PREFIX={install_path}", # MSVC debug information format flags are selected by an abstraction "-DCMAKE_POLICY_DEFAULT_CMP0141=NEW", # MSVC debug information format "-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=ProgramDatabase", # Linker flags for executables "-DCMAKE_EXE_LINKER_FLAGS=-INCREMENTAL:NO -DEBUG -OPT:REF -OPT:ICF", # Linker flag for shared libraries "-DCMAKE_SHARED_LINKER_FLAGS=-INCREMENTAL:NO -DEBUG -OPT:REF -OPT:ICF", # MSVC runtime library flags are selected by an abstraction "-DCMAKE_POLICY_DEFAULT_CMP0091=NEW", # Use statically linked runtime (-MT) (ideally, should be "MultiThreaded$<$:Debug>") "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded", f"-DCMAKE_PREFIX_PATH={';'.join(str(s) for s in dep_roots)}", ] + self.release_info["msvc"]["cmake"]["args"] + ([] if self.fast else ["--fresh"])) with self.section_printer.group(f"Build VC CMake project for {arch_platform.arch}"): self.executer.run(["cmake", "--build", str(build_path), "--verbose", "--config", build_type]) with self.section_printer.group(f"Install VC CMake project for {arch_platform.arch}"): self.executer.run(["cmake", "--install", str(build_path), "--config", build_type]) if self.dry: for b in built_paths: b.parent.mkdir(parents=True, exist_ok=True) b.touch() zip_path = self.dist_path / f"{self.project}-{self.version}-win32-{arch_platform.arch}.zip" zip_path.unlink(missing_ok=True) logger.info("Collecting files...") archive_file_tree = ArchiveFileTree() archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["cmake"]["files-lib"], file_mapping_root=install_path, context=platform_context, time=self.arc_time) archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["files-lib"], file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) logger.info("Creating %s", zip_path) with Archiver(zip_path=zip_path) as archiver: arc_root = f"" archive_file_tree.add_to_archiver(archive_base=arc_root, archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) for p in built_paths: assert p.is_file(), f"{p} should exist" def _build_msvc_devel(self) -> None: zip_path = self.dist_path / f"{self.project}-devel-{self.version}-VC.zip" arc_root = f"{self.project}-{self.version}" def copy_files_devel(ctx): archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["files-devel"], file_mapping_root=self.root, context=ctx, time=self.arc_time) logger.info("Collecting files...") archive_file_tree = ArchiveFileTree() if "msbuild" in self.release_info["msvc"]: for arch in self.release_info["msvc"]["msbuild"]["archs"]: arch_platform = self._arch_to_vs_platform(arch=arch) platform_context = self.get_context(arch_platform.extra_context()) archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["msbuild"]["files-devel"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) copy_files_devel(ctx=platform_context) if "cmake" in self.release_info["msvc"]: for arch in self.release_info["msvc"]["cmake"]["archs"]: arch_platform = self._arch_to_vs_platform(arch=arch) platform_context = self.get_context(arch_platform.extra_context()) archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["cmake"]["files-devel"], file_mapping_root=self._arch_platform_to_install_path(arch_platform), context=platform_context, time=self.arc_time) copy_files_devel(ctx=platform_context) with Archiver(zip_path=zip_path) as archiver: archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) self.artifacts["VC-devel"] = zip_path @classmethod def extract_sdl_version(cls, root: Path, release_info: dict) -> str: with open(root / release_info["version"]["file"], "r") as f: text = f.read() major = next(re.finditer(release_info["version"]["re_major"], text, flags=re.M)).group(1) minor = next(re.finditer(release_info["version"]["re_minor"], text, flags=re.M)).group(1) micro = next(re.finditer(release_info["version"]["re_micro"], text, flags=re.M)).group(1) return f"{major}.{minor}.{micro}" def main(argv=None) -> int: if sys.version_info < (3, 11): logger.error("This script needs at least python 3.11") return 1 parser = argparse.ArgumentParser(allow_abbrev=False, description="Create SDL release artifacts") parser.add_argument("--root", metavar="DIR", type=Path, default=Path(__file__).absolute().parents[1], help="Root of project") parser.add_argument("--release-info", metavar="JSON", dest="path_release_info", type=Path, default=Path(__file__).absolute().parent / "release-info.json", help="Path of release-info.json") parser.add_argument("--dependency-folder", metavar="FOLDER", dest="deps_path", type=Path, default="deps", help="Directory containing pre-built archives of dependencies (will be removed when downloading archives)") parser.add_argument("--out", "-o", metavar="DIR", dest="dist_path", type=Path, default="dist", help="Output directory") parser.add_argument("--github", action="store_true", help="Script is running on a GitHub runner") parser.add_argument("--commit", default="HEAD", help="Git commit/tag of which a release should be created") parser.add_argument("--actions", choices=["download", "source", "android", "mingw", "msvc", "dmg"], required=True, nargs="+", dest="actions", help="What to do?") parser.set_defaults(loglevel=logging.INFO) parser.add_argument('--vs-year', dest="vs_year", help="Visual Studio year") parser.add_argument('--android-api', type=int, dest="android_api", help="Android API version") parser.add_argument('--android-home', dest="android_home", default=os.environ.get("ANDROID_HOME"), help="Android Home folder") parser.add_argument('--android-ndk-home', dest="android_ndk_home", default=os.environ.get("ANDROID_NDK_HOME"), help="Android NDK Home folder") parser.add_argument('--cmake-generator', dest="cmake_generator", default="Ninja", help="CMake Generator") parser.add_argument('--debug', action='store_const', const=logging.DEBUG, dest="loglevel", help="Print script debug information") parser.add_argument('--dry-run', action='store_true', dest="dry", help="Don't execute anything") parser.add_argument('--force', action='store_true', dest="force", help="Ignore a non-clean git tree") parser.add_argument('--overwrite', action='store_true', dest="overwrite", help="Allow potentially overwriting other projects") parser.add_argument('--fast', action='store_true', dest="fast", help="Don't do a rebuild") args = parser.parse_args(argv) logging.basicConfig(level=args.loglevel, format='[%(levelname)s] %(message)s') args.deps_path = args.deps_path.absolute() args.dist_path = args.dist_path.absolute() args.root = args.root.absolute() args.dist_path = args.dist_path.absolute() if args.dry: args.dist_path = args.dist_path / "dry" if args.github: section_printer: SectionPrinter = GitHubSectionPrinter() else: section_printer = SectionPrinter() if args.github and "GITHUB_OUTPUT" not in os.environ: os.environ["GITHUB_OUTPUT"] = "/tmp/github_output.txt" executer = Executer(root=args.root, dry=args.dry) root_git_hash_path = args.root / GIT_HASH_FILENAME root_is_maybe_archive = root_git_hash_path.is_file() if root_is_maybe_archive: logger.warning("%s detected: Building from archive", GIT_HASH_FILENAME) archive_commit = root_git_hash_path.read_text().strip() if args.commit != archive_commit: logger.warning("Commit argument is %s, but archive commit is %s. Using %s.", args.commit, archive_commit, archive_commit) args.commit = archive_commit revision = (args.root / REVISION_TXT).read_text().strip() else: args.commit = executer.check_output(["git", "rev-parse", args.commit], dry_out="e5812a9fd2cda317b503325a702ba3c1c37861d9").strip() revision = executer.check_output(["git", "describe", "--always", "--tags", "--long", args.commit], dry_out="preview-3.1.3-96-g9512f2144").strip() logger.info("Using commit %s", args.commit) try: with args.path_release_info.open() as f: release_info = json.load(f) except FileNotFoundError: logger.error(f"Could not find {args.path_release_info}") releaser = Releaser( release_info=release_info, commit=args.commit, revision=revision, root=args.root, dist_path=args.dist_path, executer=executer, section_printer=section_printer, cmake_generator=args.cmake_generator, deps_path=args.deps_path, overwrite=args.overwrite, github=args.github, fast=args.fast, ) if root_is_maybe_archive: logger.warning("Building from archive. Skipping clean git tree check.") else: porcelain_status = executer.check_output(["git", "status", "--ignored", "--porcelain"], dry_out="\n").strip() if porcelain_status: print(porcelain_status) logger.warning("The tree is dirty! Do not publish any generated artifacts!") if not args.force: raise Exception("The git repo contains modified and/or non-committed files. Run with --force to ignore.") if args.fast: logger.warning("Doing fast build! Do not publish generated artifacts!") with section_printer.group("Arguments"): print(f"project = {releaser.project}") print(f"version = {releaser.version}") print(f"revision = {revision}") print(f"commit = {args.commit}") print(f"out = {args.dist_path}") print(f"actions = {args.actions}") print(f"dry = {args.dry}") print(f"force = {args.force}") print(f"overwrite = {args.overwrite}") print(f"cmake_generator = {args.cmake_generator}") releaser.prepare() if "download" in args.actions: releaser.download_dependencies() if set(args.actions).intersection({"msvc", "mingw", "android"}): print("Verifying presence of dependencies (run 'download' action to download) ...") releaser.verify_dependencies() print("... done") if "source" in args.actions: if root_is_maybe_archive: raise Exception("Cannot build source archive from source archive") with section_printer.group("Create source archives"): releaser.create_source_archives() if "dmg" in args.actions: if platform.system() != "Darwin" and not args.dry: parser.error("framework artifact(s) can only be built on Darwin") releaser.create_dmg() if "msvc" in args.actions: if platform.system() != "Windows" and not args.dry: parser.error("msvc artifact(s) can only be built on Windows") releaser.build_msvc() if "mingw" in args.actions: releaser.create_mingw_archives() if "android" in args.actions: if args.android_home is None or not Path(args.android_home).is_dir(): parser.error("Invalid $ANDROID_HOME or --android-home: must be a directory containing the Android SDK") if args.android_ndk_home is None or not Path(args.android_ndk_home).is_dir(): parser.error("Invalid $ANDROID_NDK_HOME or --android_ndk_home: must be a directory containing the Android NDK") if args.android_api is None: with section_printer.group("Detect Android APIS"): args.android_api = releaser._detect_android_api(android_home=args.android_home) if args.android_api is None or not (Path(args.android_home) / f"platforms/android-{args.android_api}").is_dir(): parser.error("Invalid --android-api, and/or could not be detected") with section_printer.group("Android arguments"): print(f"android_home = {args.android_home}") print(f"android_ndk_home = {args.android_ndk_home}") print(f"android_api = {args.android_api}") releaser.create_android_archives( android_api=args.android_api, android_home=args.android_home, android_ndk_home=args.android_ndk_home, ) with section_printer.group("Summary"): print(f"artifacts = {releaser.artifacts}") if args.github: with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"project={releaser.project}\n") f.write(f"version={releaser.version}\n") for k, v in releaser.artifacts.items(): f.write(f"{k}={v.name}\n") return 0 if __name__ == "__main__": raise SystemExit(main()) SDL2_ttf-2.24.0/build-scripts/cmake-toolchain-mingw64-i686.cmake0000664000000000000000000000103314733545702020726 0ustar00set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR x86) find_program(CMAKE_C_COMPILER NAMES i686-w64-mingw32-gcc) find_program(CMAKE_CXX_COMPILER NAMES i686-w64-mingw32-g++) find_program(CMAKE_RC_COMPILER NAMES i686-w64-mingw32-windres windres) if(NOT CMAKE_C_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_C_COMPILER.") endif() if(NOT CMAKE_CXX_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_CXX_COMPILER.") endif() if(NOT CMAKE_RC_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_RC_COMPILER.") endif() SDL2_ttf-2.24.0/build-scripts/cmake-toolchain-mingw64-x86_64.cmake0000664000000000000000000000104414733545702021172 0ustar00set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR x86_64) find_program(CMAKE_C_COMPILER NAMES x86_64-w64-mingw32-gcc) find_program(CMAKE_CXX_COMPILER NAMES x86_64-w64-mingw32-g++) find_program(CMAKE_RC_COMPILER NAMES x86_64-w64-mingw32-windres windres) if(NOT CMAKE_C_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_C_COMPILER.") endif() if(NOT CMAKE_CXX_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_CXX_COMPILER.") endif() if(NOT CMAKE_RC_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_RC_COMPILER.") endif() SDL2_ttf-2.24.0/build-scripts/create-release.py0000775000000000000000000000272114733545702016146 0ustar00#!/usr/bin/env python3 import argparse from pathlib import Path import json import logging import re import subprocess ROOT = Path(__file__).resolve().parents[1] def determine_remote() -> str: text = (ROOT / "build-scripts/release-info.json").read_text() release_info = json.loads(text) if "remote" in release_info: return release_info["remote"] project_with_version = release_info["name"] project, _ = re.subn("([^a-zA-Z_])", "", project_with_version) return f"libsdl-org/{project}" def main(): default_remote = determine_remote() parser = argparse.ArgumentParser(allow_abbrev=False) parser.add_argument("--ref", required=True, help=f"Name of branch or tag containing release.yml") parser.add_argument("--remote", "-R", default=default_remote, help=f"Remote repo (default={default_remote})") parser.add_argument("--commit", help=f"Input 'commit' of release.yml (default is the hash of the ref)") args = parser.parse_args() if args.commit is None: args.commit = subprocess.check_output(["git", "rev-parse", args.ref], cwd=ROOT, text=True).strip() print(f"Running release.yml workflow:") print(f" remote = {args.remote}") print(f" ref = {args.ref}") print(f" commit = {args.commit}") subprocess.check_call(["gh", "-R", args.remote, "workflow", "run", "release.yml", "--ref", args.ref, "-f", f"commit={args.commit}"], cwd=ROOT) if __name__ == "__main__": raise SystemExit(main()) SDL2_ttf-2.24.0/build-scripts/release-info.json0000664000000000000000000000553214733545702016157 0ustar00{ "name": "SDL2_ttf", "remote": "libsdl-org/SDL_ttf", "dependencies": { "SDL": { "startswith": "2.", "repo": "libsdl-org/SDL" } }, "version": { "file": "SDL_ttf.h", "re_major": "^#define SDL_TTF_MAJOR_VERSION\\s+([0-9]+)$", "re_minor": "^#define SDL_TTF_MINOR_VERSION\\s+([0-9]+)$", "re_micro": "^#define SDL_TTF_PATCHLEVEL\\s+([0-9]+)$" }, "source": { "extra-repos": [ "external/freetype", "external/harfbuzz" ], "checks": [ "SDL_ttf.h", "SDL_ttf.c", "external/freetype/include/freetype/freetype.h", "external/harfbuzz/src/hb.h" ] }, "dmg": { "project": "Xcode/SDL_ttf.xcodeproj", "path": "Xcode/build/SDL2_ttf.dmg", "scheme": "Create DMG", "dependencies": { "SDL": { "artifact": "SDL2-*.dmg" } } }, "mingw": { "autotools": { "archs": ["x86", "x64"], "args": [ "--with-sdl-prefix=@<@DEP_PREFIX@>@", "CFLAGS=-O2 -I@<@DEP_PREFIX@>@/include -I@<@DEP_PREFIX@>@/include/SDL2", "LDFLAGS=-Wl,-s -L@<@DEP_PREFIX@>@/lib -lSDL2" ] }, "files": { "": [ "CHANGES.txt", "LICENSE.txt", "README.txt", "mingw/pkg-support/Makefile" ], "cmake": [ "mingw/pkg-support/cmake/sdl2_ttf-config.cmake", "mingw/pkg-support/cmake/sdl2_ttf-config-version.cmake" ] }, "dependencies": { "SDL": { "artifact": "SDL2-devel-*-mingw.tar.gz", "install-command": "make install-package arch=@<@TRIPLET@>@ prefix=@<@PREFIX@>@" } } }, "msvc": { "msbuild": { "archs": [ "x86", "x64" ], "projects": [ "VisualC/SDL_ttf.vcxproj" ], "files-lib": { "": [ "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_ttf.dll" ] }, "files-devel": { "lib/@<@ARCH@>@": [ "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_ttf.dll", "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_ttf.lib", "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_ttf.pdb" ] } }, "files-lib": { "": [ "README.txt" ] }, "files-devel": { "": [ "CHANGES.txt", "LICENSE.txt", "README.txt" ], "include": [ "SDL_ttf.h" ], "cmake": [ "VisualC/pkg-support/cmake/sdl2_ttf-config.cmake", "VisualC/pkg-support/cmake/sdl2_ttf-config-version.cmake" ] }, "dependencies": { "SDL": { "artifact": "SDL2-devel-*-VC.zip", "copy": [ { "src": "lib/@<@ARCH@>@/SDL2.*", "dst": "../SDL/VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@" }, { "src": "include/*", "dst": "../SDL/include" } ] } } } } SDL2_ttf-2.24.0/build-scripts/test-versioning.sh0000775000000000000000000001357714735320472016417 0ustar00#!/bin/sh # Copyright 2022 Collabora Ltd. # SPDX-License-Identifier: Zlib set -eu cd `dirname $0`/.. # Needed so sed doesn't report illegal byte sequences on macOS export LC_CTYPE=C header=SDL_ttf.h ref_major=$(sed -ne 's/^#define SDL_TTF_MAJOR_VERSION *//p' $header) ref_minor=$(sed -ne 's/^#define SDL_TTF_MINOR_VERSION *//p' $header) ref_micro=$(sed -ne 's/^#define SDL_TTF_PATCHLEVEL *//p' $header) ref_version="${ref_major}.${ref_minor}.${ref_micro}" tests=0 failed=0 ok () { tests=$(( tests + 1 )) echo "ok - $*" } not_ok () { tests=$(( tests + 1 )) echo "not ok - $*" failed=1 } major=$(sed -Ene 's/^m4_define\(\[MAJOR_VERSION_MACRO\], \[([0-9]*)\]\)$/\1/p' configure.ac) minor=$(sed -Ene 's/^m4_define\(\[MINOR_VERSION_MACRO\], \[([0-9]*)\]\)$/\1/p' configure.ac) micro=$(sed -Ene 's/^m4_define\(\[MICRO_VERSION_MACRO\], \[([0-9]*)\]\)$/\1/p' configure.ac) version="${major}.${minor}.${micro}" ref_sdl_req=$(sed -ne 's/^SDL_VERSION=//p' configure.ac) if [ "$ref_version" = "$version" ]; then ok "configure.ac $version" else not_ok "configure.ac $version disagrees with $header $ref_version" fi major=$(sed -ne 's/^MAJOR_VERSION=//p' configure) minor=$(sed -ne 's/^MINOR_VERSION=//p' configure) micro=$(sed -ne 's/^MICRO_VERSION=//p' configure) version="${major}.${minor}.${micro}" if [ "$ref_version" = "$version" ]; then ok "configure $version" else not_ok "configure $version disagrees with $header $ref_version" fi major=$(sed -ne 's/^set(MAJOR_VERSION \([0-9]*\))$/\1/p' CMakeLists.txt) minor=$(sed -ne 's/^set(MINOR_VERSION \([0-9]*\))$/\1/p' CMakeLists.txt) micro=$(sed -ne 's/^set(MICRO_VERSION \([0-9]*\))$/\1/p' CMakeLists.txt) sdl_req=$(sed -ne 's/^set(SDL_REQUIRED_VERSION \([0-9.]*\))$/\1/p' CMakeLists.txt) version="${major}.${minor}.${micro}" if [ "$ref_version" = "$version" ]; then ok "CMakeLists.txt $version" else not_ok "CMakeLists.txt $version disagrees with $header $ref_version" fi if [ "$ref_sdl_req" = "$sdl_req" ]; then ok "CMakeLists.txt $sdl_req" else not_ok "CMakeLists.txt SDL_REQUIRED_VERSION=$sdl_req disagrees with configure.ac SDL_VERSION=$ref_sdl_req" fi major=$(sed -ne 's/^MAJOR_VERSION *= *//p' Makefile.os2) minor=$(sed -ne 's/^MINOR_VERSION *= *//p' Makefile.os2) micro=$(sed -ne 's/^MICRO_VERSION *= *//p' Makefile.os2) version="${major}.${minor}.${micro}" if [ "$ref_version" = "$version" ]; then ok "Makefile.os2 $version" else not_ok "Makefile.os2 $version disagrees with $header $ref_version" fi for rcfile in version.rc VisualC/Version.rc; do tuple=$(sed -ne 's/^ *FILEVERSION *//p' "$rcfile" | tr -d '\r') ref_tuple="${ref_major},${ref_minor},${ref_micro},0" if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile FILEVERSION $tuple" else not_ok "$rcfile FILEVERSION $tuple disagrees with $header $ref_tuple" fi tuple=$(sed -ne 's/^ *PRODUCTVERSION *//p' "$rcfile" | tr -d '\r') if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile PRODUCTVERSION $tuple" else not_ok "$rcfile PRODUCTVERSION $tuple disagrees with $header $ref_tuple" fi tuple=$(sed -Ene 's/^ *VALUE "FileVersion", "([0-9, ]*)\\0"\r?$/\1/p' "$rcfile" | tr -d '\r') ref_tuple="${ref_major}, ${ref_minor}, ${ref_micro}, 0" if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile FileVersion $tuple" else not_ok "$rcfile FileVersion $tuple disagrees with $header $ref_tuple" fi tuple=$(sed -Ene 's/^ *VALUE "ProductVersion", "([0-9, ]*)\\0"\r?$/\1/p' "$rcfile" | tr -d '\r') if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile ProductVersion $tuple" else not_ok "$rcfile ProductVersion $tuple disagrees with $header $ref_tuple" fi done version=$(sed -Ene '/CFBundleShortVersionString/,+1 s/.*(.*)<\/string>.*/\1/p' Xcode/Info-Framework.plist) if [ "$ref_version" = "$version" ]; then ok "Info-Framework.plist CFBundleShortVersionString $version" else not_ok "Info-Framework.plist CFBundleShortVersionString $version disagrees with $header $ref_version" fi version=$(sed -Ene '/CFBundleVersion/,+1 s/.*(.*)<\/string>.*/\1/p' Xcode/Info-Framework.plist) if [ "$ref_version" = "$version" ]; then ok "Info-Framework.plist CFBundleVersion $version" else not_ok "Info-Framework.plist CFBundleVersion $version disagrees with $header $ref_version" fi # For simplicity this assumes we'll never break ABI before SDL 3. dylib_compat=$(sed -Ene 's/.*DYLIB_COMPATIBILITY_VERSION = (.*);$/\1/p' Xcode/SDL_ttf.xcodeproj/project.pbxproj) case "$ref_minor" in (*[02468]) major="$(( ref_minor * 100 + 1 ))" minor="0" ;; (*) major="$(( ref_minor * 100 + ref_micro + 1 ))" minor="0" ;; esac ref="${major}.${minor}.0 ${major}.${minor}.0" if [ "$ref" = "$dylib_compat" ]; then ok "project.pbxproj DYLIB_COMPATIBILITY_VERSION is consistent" else not_ok "project.pbxproj DYLIB_COMPATIBILITY_VERSION is inconsistent, expected $ref, got $dylib_compat" fi dylib_cur=$(sed -Ene 's/.*DYLIB_CURRENT_VERSION = (.*);$/\1/p' Xcode/SDL_ttf.xcodeproj/project.pbxproj) case "$ref_minor" in (*[02468]) major="$(( ref_minor * 100 + 1 ))" minor="$ref_micro" ;; (*) major="$(( ref_minor * 100 + ref_micro + 1 ))" minor="0" ;; esac ref="${major}.${minor}.0 ${major}.${minor}.0" if [ "$ref" = "$dylib_cur" ]; then ok "project.pbxproj DYLIB_CURRENT_VERSION is consistent" else not_ok "project.pbxproj DYLIB_CURRENT_VERSION is inconsistent, expected $ref, got $dylib_cur" fi if [ -f .github/fetch_sdl_vc.ps1 ]; then sdl_req=$(sed -ne 's/\$sdl2_version = "\([0-9.]*\)"$/\1/p' .github/fetch_sdl_vc.ps1) if [ "$ref_sdl_req" = "$sdl_req" ]; then ok ".github/fetch_sdl_vc.ps1 $sdl_req" else not_ok ".github/fetch_sdl_vc.ps1 sdl2_version=$sdl_req disagrees with configure.ac SDL_VERSION=$ref_sdl_req" fi fi echo "1..$tests" exit "$failed" SDL2_ttf-2.24.0/build-scripts/touch-autofoo.sh0000775000000000000000000000033514735320320016027 0ustar00#!/bin/sh # Run this script from the root of $srcdir to touch the # autotools generated files, so that the build procedure # doesn't attempt to regenerate them. cd `dirname $0`/.. touch aclocal.m4 configure Makefile.in SDL2_ttf-2.24.0/cmake/CommonFindSDL2.cmake0000664000000000000000000000121614252202562014650 0ustar00# Common variables for FindSDL2*.cmake modules set(_inc_suffixes include) set(_lib_suffixes) if(MSVC) if(CMAKE_SIZEOF_VOID_P EQUAL 4) list(APPEND _lib_suffixes "lib/x86") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _lib_suffixes "lib/x64") endif() endif() if(MINGW) if(CMAKE_SIZEOF_VOID_P EQUAL 4) list(APPEND _lib_suffixes "i686-w64-mingw32/lib") list(APPEND _inc_suffixes "i686-w64-mingw32/include") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _lib_suffixes "x86_46-w64-mingw32/lib") list(APPEND _inc_suffixes "x86_46-w64-mingw32/include") endif() endif()SDL2_ttf-2.24.0/cmake/FindPrivateSDL2.cmake0000664000000000000000000000311014252202562015025 0ustar00# FIXME: this should be provided by SDL2 include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/CommonFindSDL2.cmake") find_library(SDL2_LIBRARY NAMES SDL2 HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_lib_suffixes} ) find_path(SDL2_INCLUDE_DIR NAMES SDL_haptic.h PATH_SUFFIXES SDL2 HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_inc_suffixes} ) set(SDL2_VERSION) if(SDL2_INCLUDE_DIR) file(READ "${SDL2_INCLUDE_DIR}/SDL_version.h" _sdl_version_h) string(REGEX MATCH "#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)" _sdl2_major_re "${_sdl_version_h}") set(_sdl2_major "${CMAKE_MATCH_1}") string(REGEX MATCH "#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)" _sdl2_minor_re "${_sdl_version_h}") set(_sdl2_minor "${CMAKE_MATCH_1}") string(REGEX MATCH "#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)" _sdl2_patch_re "${_sdl_version_h}") set(_sdl2_patch "${CMAKE_MATCH_1}") if(_sdl2_major_re AND _sdl2_minor_re AND _sdl2_patch_re) set(SDL2_VERSION "${_sdl2_major}.${_sdl2_minor}.${_sdl2_patch}") endif() endif() find_package_handle_standard_args(PrivateSDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR VERSION_VAR SDL2_VERSION ) if(PrivateSDL2_FOUND) if(NOT TARGET PrivateSDL2::PrivateSDL2) add_library(PrivateSDL2::PrivateSDL2 UNKNOWN IMPORTED) set_target_properties(PrivateSDL2::PrivateSDL2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${SDL2_LIBRARY}" ) endif() endif() SDL2_ttf-2.24.0/cmake/FindSDL2main.cmake0000664000000000000000000000120014252202562014335 0ustar00# FIXME: this should be provided by SDL2 include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/CommonFindSDL2.cmake") find_library(SDL2_MAIN_LIBRARY NAMES SDL2main HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_lib_suffixes} ) find_package_handle_standard_args(SDL2main REQUIRED_VARS SDL2_MAIN_LIBRARY ) if(SDL2main_FOUND) if(NOT TARGET SDL2::SDL2main) add_library(SDL2::SDL2main UNKNOWN IMPORTED) set_target_properties(SDL2::SDL2main PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${SDL2_MAIN_LIBRARY}" ) endif() endif() SDL2_ttf-2.24.0/cmake/Findharfbuzz.cmake0000664000000000000000000000223114252202562014624 0ustar00# Create our own Findharfbuzz.cmake module because harfbuzz-config.cmake, # distributed along e.g. msys2-mingw64 does not support anything other then APPLE and UNIX. include(FindPackageHandleStandardArgs) find_library(harfbuzz_LIBRARY NAMES harfbuzz ) find_path(harfbuzz_INCLUDE_PATH NAMES hb.h PATH_SUFFIXES harfbuzz ) set(harfbuzz_COMPILE_FLAGS "" CACHE STRING "Extra compile flags of harfbuzz") set(harfbuzz_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of harfbuzz") set(harfbuzz_LINK_FLAGS "" CACHE STRING "Extra link flags of harfbuzz") find_package_handle_standard_args(harfbuzz REQUIRED_VARS harfbuzz_LIBRARY harfbuzz_INCLUDE_PATH ) if (harfbuzz_FOUND) if (NOT TARGET harfbuzz::harfbuzz) add_library(harfbuzz::harfbuzz UNKNOWN IMPORTED) set_target_properties(harfbuzz::harfbuzz PROPERTIES IMPORTED_LOCATION "${harfbuzz_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${harfbuzz_INCLUDE_PATH}" COMPILE_FLAGS "${harfbuzz_COMPILE_FLAGS}" INTERFACE_LINK_LIBRARIES "${harfbuzz_LINK_LIBRARIES}" INTERFACE_LINK_FLAGS "${harfbuzz_LINK_FLAGS}" ) endif() endif() SDL2_ttf-2.24.0/cmake/PrivateSdlFunctions.cmake0000664000000000000000000003050714727366713016165 0ustar00# This file is shared amongst SDL_image/SDL_mixer/SDL_ttf macro(sdl_calculate_derived_version_variables) if(NOT DEFINED MAJOR_VERSION OR NOT DEFINED MINOR_VERSION OR NOT DEFINED MICRO_VERSION) message(FATAL_ERROR "MAJOR_VERSION, MINOR_VERSION and MICRO_VERSION need to be defined") endif() set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}") # Calculate a libtool-like version number math(EXPR BINARY_AGE "${MINOR_VERSION} * 100 + ${MICRO_VERSION}") math(EXPR IS_DEVELOPMENT "${MINOR_VERSION} % 2") if(IS_DEVELOPMENT) # Development branch, 2.5.1 -> libSDL2_XXXXX-2.0.so.0.501.0 set(INTERFACE_AGE 0) else() # Stable branch, 2.6.1 -> libSDL2_XXXXX-2.0.so.0.600.1 set(INTERFACE_AGE ${MICRO_VERSION}) endif() # Increment this if there is an incompatible change - but if that happens, # we should rename the library from SDL2 to SDL3, at which point this would # reset to 0 anyway. set(LT_MAJOR "0") math(EXPR LT_AGE "${BINARY_AGE} - ${INTERFACE_AGE}") math(EXPR LT_CURRENT "${LT_MAJOR} + ${LT_AGE}") set(LT_REVISION "${INTERFACE_AGE}") # For historical reasons, the library name redundantly includes the major # version twice: libSDL2_XXXXX-2.0.so.0. # TODO: in SDL 3, set the OUTPUT_NAME to plain SDL3_XXXXX, which will simplify # it to libSDL2_XXXXX.so.0 set(LT_RELEASE "2.0") set(LT_VERSION "${LT_MAJOR}.${LT_AGE}.${LT_REVISION}") # The following should match the versions in the Xcode project file. # Each version is 1 higher than you might expect, for compatibility # with libtool: macOS ABI versioning is 1-based, unlike other platforms # which are normally 0-based. math(EXPR DYLIB_CURRENT_VERSION_MAJOR "${LT_MAJOR} + ${LT_AGE} + 1") math(EXPR DYLIB_CURRENT_VERSION_MINOR "${LT_REVISION}") set(DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.${DYLIB_CURRENT_VERSION_MINOR}.0") set(DYLIB_COMPATIBILITY_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.0.0") endmacro() macro(sdl_find_sdl2 TARGET VERSION) if(NOT TARGET ${TARGET}) # FIXME: can't add REQUIRED since not all SDL2 installs ship SDL2ConfigVersion.cmake (or sdl2-config-version.cmake) find_package(SDL2 ${VERSION} QUIET) endif() if(NOT TARGET ${TARGET}) # FIXME: can't add REQUIRED since not all SDL2 installs ship SDL2Config.cmake (or sdl2-config.cmake) find_package(SDL2 QUIET) if(SDL2_FOUND) message(WARNING "Could not verify SDL2 version. Assuming SDL2 has version of at least ${VERSION}.") endif() endif() # Use Private FindSDL2.cmake module to find SDL2 for installations where no SDL2Config.cmake is available, # or for those installations where no target is generated. if(NOT TARGET ${TARGET}) message(STATUS "Using private SDL2 find module") find_package(PrivateSDL2 ${VERSION} REQUIRED) add_library(${TARGET} INTERFACE IMPORTED) set_target_properties(${TARGET} PROPERTIES INTERFACE_LINK_LIBRARIES "PrivateSDL2::PrivateSDL2" ) endif() endmacro() function(read_absolute_symlink DEST PATH) file(READ_SYMLINK "${PATH}" p) if(NOT IS_ABSOLUTE "${p}") get_filename_component(pdir "${PATH}" DIRECTORY) set(p "${pdir}/${p}") endif() get_filename_component(p "${p}" ABSOLUTE) set("${DEST}" "${p}" PARENT_SCOPE) endfunction() function(win32_implib_identify_dll DEST IMPLIB) cmake_parse_arguments(ARGS "NOTFATAL" "" "" ${ARGN}) if(CMAKE_DLLTOOL) execute_process( COMMAND "${CMAKE_DLLTOOL}" --identify "${IMPLIB}" RESULT_VARIABLE retcode OUTPUT_VARIABLE stdout ERROR_VARIABLE stderr) if(NOT retcode EQUAL 0) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "${CMAKE_DLLTOOL} failed.") else() set("${DEST}" "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() string(STRIP "${stdout}" result) set(${DEST} "${result}" PARENT_SCOPE) elseif(MSVC) get_filename_component(CMAKE_C_COMPILER_DIRECTORY "${CMAKE_C_COMPILER}" DIRECTORY CACHE) find_program(CMAKE_DUMPBIN NAMES dumpbin PATHS "${CMAKE_C_COMPILER_DIRECTORY}") if(CMAKE_DUMPBIN) execute_process( COMMAND "${CMAKE_DUMPBIN}" "-headers" "${IMPLIB}" RESULT_VARIABLE retcode OUTPUT_VARIABLE stdout ERROR_VARIABLE stderr) if(NOT retcode EQUAL 0) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "dumpbin failed.") else() set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() string(REGEX MATCH "DLL name[ ]+:[ ]+([^\n]+)\n" match "${stdout}") if(NOT match) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "dumpbin did not find any associated dll for ${IMPLIB}.") else() set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() set(result "${CMAKE_MATCH_1}") set(${DEST} "${result}" PARENT_SCOPE) else() message(FATAL_ERROR "Cannot find dumpbin, please set CMAKE_DUMPBIN cmake variable") endif() else() if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "Don't know how to identify dll from import library. Set CMAKE_DLLTOOL (for mingw) or CMAKE_DUMPBIN (for MSVC)") else() set(${DEST} "${DEST}-NOTFOUND") endif() endif() endfunction() function(get_actual_target) set(dst "${ARGV0}") set(target "${${dst}}") get_target_property(alias "${target}" ALIASED_TARGET) while(alias) set(target "${alias}") get_target_property(alias "${target}" ALIASED_TARGET) endwhile() set("${dst}" "${target}" PARENT_SCOPE) endfunction() function(target_get_dynamic_library DEST TARGET) set(result) get_actual_target(TARGET) if(WIN32) # Use the target dll of the import library set(props_to_check IMPORTED_IMPLIB) if(CMAKE_BUILD_TYPE) list(APPEND props_to_check IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE}) endif() list(APPEND props_to_check IMPORTED_LOCATION) if(CMAKE_BUILD_TYPE) list(APPEND props_to_check IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) endif() foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) list(APPEND props_to_check IMPORTED_IMPLIB_${config_type}) list(APPEND props_to_check IMPORTED_LOCATION_${config_type}) endforeach() foreach(prop_to_check ${props_to_check}) if(NOT result) get_target_property(propvalue "${TARGET}" ${prop_to_check}) if(propvalue AND EXISTS "${propvalue}") win32_implib_identify_dll(result "${propvalue}" NOTFATAL) endif() endif() endforeach() else() # 1. find the target library a file might be symbolic linking to # 2. find all other files in the same folder that symolic link to it # 3. sort all these files, and select the 1st item on Linux, and last on Macos set(location_properties IMPORTED_LOCATION) if(CMAKE_BUILD_TYPE) list(APPEND location_properties IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) endif() foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) list(APPEND location_properties IMPORTED_LOCATION_${config_type}) endforeach() if(APPLE) set(valid_shared_library_regex "\\.[0-9]+\\.dylib$") else() set(valid_shared_library_regex "\\.so\\.([0-9.]+)?[0-9]") endif() foreach(location_property ${location_properties}) if(NOT result) get_target_property(library_path "${TARGET}" ${location_property}) if(EXISTS "${library_path}") get_filename_component(library_path "${library_path}" ABSOLUTE) while (IS_SYMLINK "${library_path}") read_absolute_symlink(library_path "${library_path}") endwhile() get_filename_component(libdir "${library_path}" DIRECTORY) file(GLOB subfiles "${libdir}/*") set(similar_files "${library_path}") foreach(subfile ${subfiles}) if(IS_SYMLINK "${subfile}") read_absolute_symlink(subfile_target "${subfile}") while (IS_SYMLINK "${subfile_target}") read_absolute_symlink(subfile_target "${subfile_target}") endwhile() get_filename_component(subfile_target "${subfile_target}" ABSOLUTE) if(subfile_target STREQUAL library_path AND subfile MATCHES "${valid_shared_library_regex}") list(APPEND similar_files "${subfile}") endif() endif() endforeach() list(SORT similar_files) set(index 0) if(APPLE) list(LENGTH similar_files len) math(EXPR index "${len}-1") endif() list(GET similar_files ${index} item) get_filename_component(result "${item}" NAME) endif() endif() endforeach() endif() if(result) string(TOLOWER "${result}" result_lower) if(WIN32 OR OS2) if(NOT result_lower MATCHES ".*dll") message(FATAL_ERROR "\"${result}\" is not a .dll library") endif() elseif(APPLE) if(NOT result_lower MATCHES ".*dylib.*") message(FATAL_ERROR "\"${result}\" is not a .dylib shared library") endif() else() if(NOT result_lower MATCHES ".*so.*") message(FATAL_ERROR "\"${result}\" is not a .so shared library") endif() endif() else() get_target_property(target_type ${TARGET} TYPE) if(target_type MATCHES "SHARED_LIBRARY|MODULE_LIBRARY") # OK elseif(target_type MATCHES "STATIC_LIBRARY|OBJECT_LIBRARY|INTERFACE_LIBRARY|EXECUTABLE") message(SEND_ERROR "${TARGET} is not a shared library, but has type=${target_type}") else() message(WARNING "Unable to extract dynamic library from target=${TARGET}, type=${target_type}.") endif() # TARGET_SONAME_FILE is not allowed for DLL target platforms. if(WIN32) set(result "$") else() set(result "$") endif() endif() set(${DEST} ${result} PARENT_SCOPE) endfunction() macro(sdl_check_project_in_subfolder relative_subfolder name vendored_option) if(NOT EXISTS "${PROJECT_SOURCE_DIR}/${relative_subfolder}/CMakeLists.txt") message(FATAL_ERROR "No cmake project for ${name} found in ${relative_subfolder}.\n" "Run the download script in the external folder, or re-configure with -D${vendored_option}=OFF to use system packages.") endif() endmacro() macro(sdl_check_linker_flag flag var) # FIXME: Use CheckLinkerFlag module once cmake minimum version >= 3.18 include(CMakePushCheckState) include(CheckCSourceCompiles) cmake_push_check_state(RESET) set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") check_c_source_compiles("int main() { return 0; }" ${var}) cmake_pop_check_state() endmacro() function(sdl_target_link_options_no_undefined TARGET) if(NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*") if(CMAKE_C_COMPILER_ID MATCHES "AppleClang") target_link_options(${TARGET} PRIVATE "-Wl,-undefined,error") else() sdl_check_linker_flag("-Wl,--no-undefined" HAVE_WL_NO_UNDEFINED) if(HAVE_WL_NO_UNDEFINED AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") AND WIN32)) target_link_options(${TARGET} PRIVATE "-Wl,--no-undefined") endif() endif() endif() endfunction() SDL2_ttf-2.24.0/cmake/test/CMakeLists.txt0000664000000000000000000000351214252202562014710 0ustar00# This cmake build script is meant for verifying the various CMake configuration script. cmake_minimum_required(VERSION 3.12) project(sdl_test LANGUAGES C) cmake_policy(SET CMP0074 NEW) # Override CMAKE_FIND_ROOT_PATH_MODE to allow search for SDL2_ttf outside of sysroot set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) include(FeatureSummary) option(TEST_SHARED "Test linking to shared SDL2_ttf library" ON) add_feature_info("TEST_SHARED" TEST_SHARED "Test linking with shared library") option(TEST_STATIC "Test linking to static SDL2_ttf libary" ON) add_feature_info("TEST_STATIC" TEST_STATIC "Test linking with static library") if(TEST_SHARED) # FIXME: in the distant future, must become REQUIRED find_package(SDL2 CONFIG COMPONENTS SDL2) # FIXME: and the following should be removed if(NOT TARGET SDL2::SDL2) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/..") include(PrivateSdlFunctions) sdl_find_sdl2(SDL2::SDL2 2.0) endif() find_package(SDL2_ttf REQUIRED CONFIG) add_executable(main_shared main.c) target_link_libraries(main_shared PRIVATE SDL2::SDL2 SDL2_ttf::SDL2_ttf) endif() if(TEST_STATIC) # FIXME: in the distant future, must become REQUIRED find_package(SDL2 CONFIG COMPONENTS SDL2-static) # FIXME: and the following should be removed if(NOT TARGET SDL2::SDL2-static) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/..") include(PrivateSdlFunctions) sdl_find_sdl2(SDL2::SDL2-static 2.0) endif() # some static vendored libraries use c++ (enable CXX after `find_package` might show a warning) enable_language(CXX) find_package(SDL2_ttf REQUIRED CONFIG) add_executable(main_static main.c) target_link_libraries(main_static PRIVATE SDL2::SDL2-static SDL2_ttf::SDL2_ttf-static) endif() feature_summary(WHAT ALL) SDL2_ttf-2.24.0/cmake/test/main.c0000664000000000000000000000063214252202562013240 0ustar00#define SDL_MAIN_HANDLED #include "SDL.h" #include "SDL_ttf.h" #include int main(int argc, char *argv[]) { SDL_SetMainReady(); if (SDL_Init(0) < 0) { fprintf(stderr, "could not initialize sdl2: %s\n", SDL_GetError()); return 1; } if (TTF_Init() == -1) { fprintf(stderr, "TTF_Init: %s\n", TTF_GetError()); } TTF_Quit(); SDL_Quit(); return 0; } SDL2_ttf-2.24.0/configure.ac0000664000000000000000000002565314735275764012435 0ustar00dnl Process this file with autoconf to produce a configure script. dnl Set various version strings - taken gratefully from the GTk sources # See docs/release_checklist.md m4_define([MAJOR_VERSION_MACRO], [2]) m4_define([MINOR_VERSION_MACRO], [24]) m4_define([MICRO_VERSION_MACRO], [0]) AC_INIT([SDL2_ttf], [MAJOR_VERSION_MACRO.MINOR_VERSION_MACRO.MICRO_VERSION_MACRO], [https://github.com/libsdl-org/SDL_ttf/issues], [SDL2_ttf]) AC_CONFIG_MACRO_DIR([acinclude]) AC_CONFIG_SRCDIR([SDL_ttf.c]) AC_SUBST([MAJOR_VERSION], MAJOR_VERSION_MACRO) AC_SUBST([MINOR_VERSION], MINOR_VERSION_MACRO) AC_SUBST([MICRO_VERSION], MICRO_VERSION_MACRO) BINARY_AGE=`expr $MINOR_VERSION \* 100 + $MICRO_VERSION` AS_CASE(["$MINOR_VERSION"], [*@<:@02468@:>@], dnl Stable branch, 2.20.1 -> libSDL2_ttf-2.0.so.0.2000.1 [INTERFACE_AGE="$MICRO_VERSION"], [*], dnl Development branch, 2.19.1 -> libSDL2_ttf-2.0.so.0.1901.0 [INTERFACE_AGE=0]) dnl libtool versioning LT_INIT([win32-dll]) # For historical reasons, the library name redundantly includes the major # version twice: libSDL2_ttf-2.0.so.0. # TODO: in SDL 3, stop using -release, which will simplify it to # libSDL3_ttf.so.0 LT_RELEASE=2.0 # Increment this if there is an incompatible change - but if that happens, # we should rename the library from SDL2 to SDL3, at which point this would # reset to 0 anyway. LT_MAJOR=0 LT_AGE=`expr $BINARY_AGE - $INTERFACE_AGE` LT_CURRENT=`expr $LT_MAJOR + $LT_AGE` LT_REVISION=$INTERFACE_AGE LT_EXTRA="" m4_pattern_allow([^LT_MAJOR$]) AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) AC_SUBST(LT_EXTRA) dnl For use in static assertions AC_DEFINE_UNQUOTED([SDL_BUILD_MAJOR_VERSION], $MAJOR_VERSION, [ ]) AC_DEFINE_UNQUOTED([SDL_BUILD_MINOR_VERSION], $MINOR_VERSION, [ ]) AC_DEFINE_UNQUOTED([SDL_BUILD_MICRO_VERSION], $MICRO_VERSION, [ ]) dnl Detect the canonical build and host environments AC_CANONICAL_HOST dnl Setup for automake AM_INIT_AUTOMAKE([1.16 foreign subdir-objects tar-ustar]) dnl Check for tools AC_PROG_CC AC_PROG_CXX AC_CHECK_TOOL(RC,[windres],[:]) AC_SYS_LARGEFILE AC_PROG_INSTALL AC_PROG_MAKE_SET case "$host" in *-*-beos*) ac_default_prefix=/boot/develop/tools/gnupro ;; *-*-cygwin* | *-*-mingw*) if test "$build" != "$host"; then # cross-compiling # Default cross-compile location ac_default_prefix=/usr/local/cross-tools/$host else # Look for the location of the tools and install there if test "$BUILD_PREFIX" != ""; then ac_default_prefix=$BUILD_PREFIX fi fi use_version_rc=true LT_EXTRA="-Wl,version.o" ;; *-*-os2*) # disable static builds on os/2 enable_static=no # -DBUILD_SDL is needed for DECLSPEC CFLAGS="$CFLAGS -DBUILD_SDL" # OS/2 does not support a DLL name longer than 8 characters. LT_EXTRA="-os2dllname SDL2ttf" ;; esac AM_CONDITIONAL(USE_VERSION_RC, test x$use_version_rc = xtrue) SUMMARY="\nSDL2_ttf Configure Summary:\n" dnl Check for SDL SDL_VERSION=2.0.10 AC_SUBST(SDL_VERSION) AM_PATH_SDL2($SDL_VERSION, :, AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) ) CFLAGS="$CFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" CheckNoUndef() { AC_MSG_CHECKING(for linker option --no-undefined) have_no_undefined=no case "${host_os}" in dnl Skip this on platforms where it is just simply busted. openbsd*) ;; darwin*) have_no_undefined="-Wl,-undefined,error" LDFLAGS="$LDFLAGS -Wl,-undefined,error" ;; *) save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--no-undefined" AC_LINK_IFELSE([AC_LANG_PROGRAM], [have_no_undefined=yes],[LDFLAGS="$save_LDFLAGS"]) ;; esac AC_MSG_RESULT($have_no_undefined) } dnl See if GCC's -Wall is supported. CheckWarnAll() { AC_MSG_CHECKING(for GCC -Wall option) have_gcc_Wall=no save_CFLAGS="$CFLAGS" CFLAGS="$save_CFLAGS -Wall" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int x = 0;])], [have_gcc_Wall=yes]) AC_MSG_RESULT($have_gcc_Wall) CFLAGS="$save_CFLAGS" if test x$have_gcc_Wall = xyes; then CFLAGS="$CFLAGS -Wall" CXXFLAGS="$CXXFLAGS -Wall" dnl Haiku headers use multicharacter constants all over the place. Ignore these warnings when using -Wall. AC_MSG_CHECKING(for necessary GCC -Wno-multichar option) need_gcc_Wno_multichar=no case "$host" in *-*-haiku*) need_gcc_Wno_multichar=yes ;; esac AC_MSG_RESULT($need_gcc_Wno_multichar) if test x$need_gcc_Wno_multichar = xyes; then CFLAGS="$CFLAGS -Wno-multichar" CXXFLAGS="$CXXFLAGS -Wno-multichar" fi fi } dnl See if GCC's -fvisibility=hidden is supported (gcc4 and later, usually). CheckVisibilityHidden() { AC_MSG_CHECKING(for GCC -fvisibility=hidden option) have_gcc_fvisibility=no case "$host" in *-*-cygwin* | *-*-mingw* | *-*-os2*) AC_MSG_RESULT([ignored for $host_os]) return ;; esac visibility_CFLAGS="-fvisibility=hidden" save_CFLAGS="$CFLAGS" CFLAGS="$save_CFLAGS $visibility_CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if !defined(__GNUC__) || __GNUC__ < 4 #error SDL only uses visibility attributes in GCC 4 or newer #endif ]],[])], [have_gcc_fvisibility=yes],[]) AC_MSG_RESULT($have_gcc_fvisibility) CFLAGS="$save_CFLAGS" if test x$have_gcc_fvisibility = xyes; then CFLAGS="$CFLAGS $visibility_CFLAGS" CXXFLAGS="$CXXFLAGS $visibility_CFLAGS" fi } dnl check for GCC warning options CheckWarnAll dnl check for GCC visibility attributes CheckVisibilityHidden AC_SUBST([SDL2TTF_VENDORED]) AC_SUBST([TTF_USE_HARFBUZZ]) SDL2TTF_VENDORED=0 TTF_LIBS= TTF_CPPFLAGS= PC_REQUIRES= PC_LIBS= dnl Check for the FreeType 2 library AC_ARG_ENABLE(freetype-builtin, [AS_HELP_STRING([--enable-freetype-builtin], [Use included version of FreeType [default=yes]])], [], enable_freetype_builtin=yes) if test x$enable_freetype_builtin = xyes; then SUMMARY="${SUMMARY}Using included FreeType : YES\n" else PKG_CHECK_MODULES([FT2], [freetype2 >= 7.0.1], [ PC_REQUIRES="freetype2 $PC_REQUIRES" ], [dnl AC_CHECK_FT2(,,[AC_MSG_ERROR([dnl *** Unable to find FreeType2 library (https://www.freetype.org)])] ) PC_LIBS="$FT2_LIBS $PC_LIBS" ]) TTF_CFLAGS="$TTF_CFLAGS $FT2_CFLAGS" TTF_LIBS="$TTF_LIBS $FT2_LIBS" SUMMARY="${SUMMARY}Using included FreeType : NO\n" fi AM_CONDITIONAL(USE_BUILTIN_FREETYPE, test x$enable_freetype_builtin = xyes) dnl Check for the HarfBuzz library AC_ARG_ENABLE(harfbuzz, [AS_HELP_STRING([--enable-harfbuzz], [Enable HarfBuzz [default=yes]])], [], enable_harfbuzz=yes) AC_ARG_ENABLE(harfbuzz-builtin, [AS_HELP_STRING([--enable-harfbuzz-builtin], [Use included version of HarfBuzz [default=yes]])], [], enable_harfbuzz_builtin=yes) TTF_USE_HARFBUZZ=0 if test x$enable_harfbuzz = xyes; then AC_DEFINE(TTF_USE_HARFBUZZ, 1, []) TTF_USE_HARFBUZZ=1 SUMMARY="${SUMMARY}Using HarfBuzz : YES\n" if test x$enable_harfbuzz_builtin = xyes; then SDL2TTF_VENDORED=1 SUMMARY="${SUMMARY}Using included HarfBuzz : YES\n" AX_CXX_COMPILE_STDCXX(11) AC_CHECK_ALIGNOF([struct{char;}]) case "$host" in *-*-mingw*) hb_os_win32=yes CXXFLAGS="$CXXFLAGS -static-libgcc -static-libstdc++" CFLAGS="$CFLAGS -static-libgcc" # for uniscribe support: TTF_LIBS="$TTF_LIBS -lusp10 -lgdi32 -lrpcrt4" PC_LIBS="$PC_LIBS -lusp10 -lgdi32 -lrpcrt4" ;; *) # compiler might optimize sinf+cosf into sincosf TTF_LIBS="$TTF_LIBS -lm" ;; esac case "$host" in arm-*-*) if test "x$ac_cv_alignof_struct_char__" != x1; then CXXFLAGS="$CXXFLAGS -mstructure-size-boundary=8" fi ;; esac # silence a lot of harfbuzz warnings: CXXFLAGS="$CXXFLAGS -Wno-unused-result" # prevent linking to libstdc++ : CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions -fno-threadsafe-statics" else SUMMARY="${SUMMARY}Using included HarfBuzz : NO\n" PKG_CHECK_MODULES([HB], [harfbuzz >= 2.3.1], harfbuzz=yes, harfbuzz=no) if test x$harfbuzz = xyes; then save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" CFLAGS="$CFLAGS $HB_CFLAGS" LIBS="$LIBS $HB_LIBS" AC_MSG_CHECKING(for freetype support in harfbuzz) harbuzz_has_freetype=no AC_LINK_IFELSE([AC_LANG_PROGRAM([],[[ void* hb_ft_font_create(void*, void*); return !hb_ft_font_create((void*)0, (void*)0);]]) ],[harbuzz_has_freetype=yes]) CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" AC_MSG_RESULT($harbuzz_has_freetype) if test x$harbuzz_has_freetype = xno; then harfbuzz=no AC_MSG_ERROR([dnl *** HarfBuzz library was built without FreeType support.)]) fi fi if test x$harfbuzz = xyes; then AC_DEFINE(TTF_USE_HARFBUZZ, 1, []) PC_REQUIRES="harfbuzz $PC_REQUIRES" TTF_CFLAGS="$TTF_CFLAGS $HB_CFLAGS" TTF_LIBS="$TTF_LIBS $HB_LIBS" SUMMARY="${SUMMARY}Using harfbuzz : YES\n" else AC_MSG_ERROR([dnl *** Unable to find HarfBuzz library (https://harfbuzz.github.io)]) fi fi else AC_DEFINE(TTF_USE_HARFBUZZ, 0, []) SUMMARY="${SUMMARY}Using HarfBuzz : NO\n" enable_harfbuzz_builtin=no fi AM_CONDITIONAL(OS_WIN32, test x$hb_os_win32 = xyes) AM_CONDITIONAL(USE_BUILTIN_HARFBUZZ, test x$enable_harfbuzz_builtin = xyes) dnl check for LD --no-undefined option CheckNoUndef dnl Check for OpenGL case "$host" in *-*-cygwin* | *-*-mingw*) MATHLIB="" SYS_GL_LIBS="-lopengl32" ;; *-*-beos*) MATHLIB="" SYS_GL_LIBS="-lGL" ;; *-*-darwin*) MATHLIB="" SYS_GL_LIBS="-Wl,-framework,OpenGL" ;; *-*-aix*) if test x$ac_cv_c_compiler_gnu = xyes; then CFLAGS="-mthreads" fi SYS_GL_LIBS="" ;; *) MATHLIB="-lm" AC_PATH_X AC_PATH_XTRA if test x$have_x = xyes; then CFLAGS="$CFLAGS $X_CFLAGS" SYS_GL_LIBS="$X_LIBS -lGL" else SYS_GL_LIBS="-lGL" fi ;; esac AC_MSG_CHECKING(for OpenGL support) have_opengl=no save_LIBS="$LIBS" LIBS="$LIBS $SYS_GL_LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include "SDL_opengl.h" ]], [[ glOrtho(-2.0, 2.0, -2.0, 2.0, -20.0, 20.0); ]])], [have_opengl=yes],[]) AC_MSG_RESULT($have_opengl) LIBS="$save_LIBS" if test x$have_opengl = xyes; then CFLAGS="$CFLAGS -DHAVE_OPENGL" GL_LIBS="$SYS_GL_LIBS" else GL_LIBS="" fi AC_SUBST([GL_LIBS]) AC_SUBST([MATHLIB]) AC_SUBST([TTF_CFLAGS]) AC_SUBST([TTF_LIBS]) AC_SUBST([PC_REQUIRES]) AC_SUBST([PC_LIBS]) dnl Calculate the location of the prefix, relative to the cmake folder pkg_cmakedir='$libdir/cmake/SDL2_ttf' AX_COMPUTE_RELATIVE_PATHS([pkg_cmakedir:prefix:cmake_prefix_relpath]) AC_SUBST([cmake_prefix_relpath]) # Finally create all the generated files AC_CONFIG_FILES([ Makefile sdl2_ttf-config.cmake sdl2_ttf-config-version.cmake SDL2_ttf.spec SDL2_ttf.pc ]) AC_CONFIG_COMMANDS([summary], [printf "$SUMMARY"], [SUMMARY="$SUMMARY"]) AC_OUTPUT SDL2_ttf-2.24.0/glfont.c0000664000000000000000000003503114735261343011557 0ustar00/* glfont: An example of using the SDL_ttf library with OpenGL. Copyright (C) 2001-2025 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. */ /* A simple program to test the text rendering feature of the TTF library */ /* quiet windows compiler warnings */ #define _CRT_SECURE_NO_WARNINGS #include #include #include #include "SDL.h" #include "SDL_ttf.h" #ifdef HAVE_OPENGL #include "SDL_opengl.h" #define DEFAULT_PTSIZE 18 #define DEFAULT_TEXT "The quick brown fox jumped over the lazy dog" #define WIDTH 640 #define HEIGHT 480 #define TTF_GLFONT_USAGE \ "Usage: %s [-utf8|-unicode] [-b] [-i] [-u] [-fgcol r,g,b] [-bgcol r,g,b] \ .ttf [ptsize] [text]\n" static void SDL_GL_Enter2DMode(int width, int height) { /* Note, there may be other things you need to change, depending on how you have your OpenGL state set up. */ glPushAttrib(GL_ENABLE_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); /* This allows alpha blending of 2D textures with the scene */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, (GLdouble)width, (GLdouble)height, 0.0, 0.0, 1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } static void SDL_GL_Leave2DMode() { glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } /* Quick utility function for texture creation */ static int power_of_two(int input) { int value = 1; while (value < input) { value <<= 1; } return value; } static GLuint SDL_GL_LoadTexture(SDL_Surface *surface, GLfloat *texcoord) { GLuint texture; int w, h; SDL_Surface *image; SDL_Rect area; Uint8 saved_alpha; SDL_BlendMode saved_mode; /* Use the surface width and height expanded to powers of 2 */ w = power_of_two(surface->w); h = power_of_two(surface->h); texcoord[0] = 0.0f; /* Min X */ texcoord[1] = 0.0f; /* Min Y */ texcoord[2] = (GLfloat)surface->w / w; /* Max X */ texcoord[3] = (GLfloat)surface->h / h; /* Max Y */ image = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_RGBA32); if (image == NULL) { return 0; } /* Save the alpha blending attributes */ SDL_GetSurfaceAlphaMod(surface, &saved_alpha); SDL_SetSurfaceAlphaMod(surface, 0xFF); SDL_GetSurfaceBlendMode(surface, &saved_mode); SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE); /* Copy the surface into the GL texture image */ area.x = 0; area.y = 0; area.w = surface->w; area.h = surface->h; SDL_BlitSurface(surface, &area, image, &area); /* Restore the alpha blending attributes */ SDL_SetSurfaceAlphaMod(surface, saved_alpha); SDL_SetSurfaceBlendMode(surface, saved_mode); /* Create an OpenGL texture for the image */ glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); SDL_FreeSurface(image); /* No longer needed */ return texture; } static void cleanup(int exitcode) { TTF_Quit(); SDL_Quit(); exit(exitcode); } int main(int argc, char *argv[]) { char *argv0 = argv[0]; SDL_Window *window; SDL_GLContext context; TTF_Font *font; SDL_Surface *text = NULL; int ptsize; int i, done; SDL_Color white = { 0xFF, 0xFF, 0xFF, 0 }; SDL_Color black = { 0x00, 0x00, 0x00, 0 }; SDL_Color *forecol; SDL_Color *backcol; GLenum gl_error; GLuint texture; int x, y, w, h; GLfloat texcoord[4]; GLfloat texMinX, texMinY; GLfloat texMaxX, texMaxY; float color[8][3]= {{ 1.0, 1.0, 0.0}, { 1.0, 0.0, 0.0}, { 0.0, 0.0, 0.0}, { 0.0, 1.0, 0.0}, { 0.0, 1.0, 1.0}, { 1.0, 1.0, 1.0}, { 1.0, 0.0, 1.0}, { 0.0, 0.0, 1.0}}; float cube[8][3]= {{ 0.5, 0.5, -0.5}, { 0.5, -0.5, -0.5}, {-0.5, -0.5, -0.5}, {-0.5, 0.5, -0.5}, {-0.5, 0.5, 0.5}, { 0.5, 0.5, 0.5}, { 0.5, -0.5, 0.5}, {-0.5, -0.5, 0.5}}; SDL_Event event; int renderstyle; int dump; enum { RENDER_LATIN1, RENDER_UTF8, RENDER_UNICODE } rendertype; char *message; /* Look for special execution mode */ dump = 0; /* Look for special rendering types */ renderstyle = TTF_STYLE_NORMAL; rendertype = RENDER_LATIN1; /* Default is black and white */ forecol = &black; backcol = &white; for (i=1; argv[i] && argv[i][0] == '-'; ++i) { if (strcmp(argv[i], "-utf8") == 0) { rendertype = RENDER_UTF8; } else if (strcmp(argv[i], "-unicode") == 0) { rendertype = RENDER_UNICODE; } else if (strcmp(argv[i], "-b") == 0) { renderstyle |= TTF_STYLE_BOLD; } else if (strcmp(argv[i], "-i") == 0) { renderstyle |= TTF_STYLE_ITALIC; } else if (strcmp(argv[i], "-u") == 0) { renderstyle |= TTF_STYLE_UNDERLINE; } else if (strcmp(argv[i], "-dump") == 0) { dump = 1; } else if (strcmp(argv[i], "-fgcol") == 0) { int r, g, b; if (sscanf (argv[++i], "%d,%d,%d", &r, &g, &b) != 3) { fprintf(stderr, TTF_GLFONT_USAGE, argv0); return(1); } forecol->r = (Uint8)r; forecol->g = (Uint8)g; forecol->b = (Uint8)b; } else if (strcmp(argv[i], "-bgcol") == 0) { int r, g, b; if (sscanf (argv[++i], "%d,%d,%d", &r, &g, &b) != 3) { fprintf(stderr, TTF_GLFONT_USAGE, argv0); return(1); } backcol->r = (Uint8)r; backcol->g = (Uint8)g; backcol->b = (Uint8)b; } else { fprintf(stderr, TTF_GLFONT_USAGE, argv0); return(1); } } argv += i; argc -= i; /* Check usage */ if (!argv[0]) { fprintf(stderr, TTF_GLFONT_USAGE, argv0); return(1); } /* Initialize the TTF library */ if (TTF_Init() < 0) { fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError()); SDL_Quit(); return(2); } /* Open the font file with the requested point size */ ptsize = 0; if (argc > 1) { ptsize = atoi(argv[1]); } if (ptsize == 0) { i = 2; ptsize = DEFAULT_PTSIZE; } else { i = 3; } font = TTF_OpenFont(argv[0], ptsize); if (font == NULL) { fprintf(stderr, "Couldn't load %d pt font from %s: %s\n", ptsize, argv[0], SDL_GetError()); cleanup(2); } TTF_SetFontStyle(font, renderstyle); if(dump) { for(i = 48; i < 123; i++) { SDL_Surface* glyph = NULL; glyph = TTF_RenderGlyph_Shaded(font, i, *forecol, *backcol); if(glyph) { char outname[64]; sprintf(outname, "glyph-%d.bmp", i); SDL_SaveBMP(glyph, outname); } } cleanup(0); } /* Set a 640x480 video mode */ window = SDL_CreateWindow("glfont", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_OPENGL); if (window == NULL) { fprintf(stderr, "Couldn't create window: %s\n", SDL_GetError()); cleanup(2); } context = SDL_GL_CreateContext(window); if (context == NULL) { fprintf(stderr, "Couldn't create OpenGL context: %s\n", SDL_GetError()); cleanup(2); } /* Render and center the message */ if (argc > 2) { message = argv[2]; } else { message = DEFAULT_TEXT; } switch (rendertype) { case RENDER_LATIN1: text = TTF_RenderText_Blended(font, message, *forecol); break; case RENDER_UTF8: text = TTF_RenderUTF8_Blended(font, message, *forecol); break; case RENDER_UNICODE: { /* This doesn't actually work because you can't pass UNICODE text in via command line, AFAIK, but... */ Uint16 unicode_text[BUFSIZ]; int index; for (index = 0; (message[0] || message[1]); ++index) { unicode_text[index] = ((Uint8 *)message)[0]; unicode_text[index] <<= 8; unicode_text[index] |= ((Uint8 *)message)[1]; message += 2; } text = TTF_RenderUNICODE_Blended(font, unicode_text, *forecol); } break; } if (text == NULL) { fprintf(stderr, "Couldn't render text: %s\n", SDL_GetError()); TTF_CloseFont(font); cleanup(2); } x = (WIDTH - text->w)/2; y = (HEIGHT - text->h)/2; w = text->w; h = text->h; printf("Font is generally %d big, and string is %d big\n", TTF_FontHeight(font), text->h); /* Convert the text into an OpenGL texture */ glGetError(); texture = SDL_GL_LoadTexture(text, texcoord); if ((gl_error = glGetError()) != GL_NO_ERROR) { /* If this failed, the text may exceed texture size limits */ printf("Warning: Couldn't create texture: 0x%x\n", gl_error); } /* Make texture coordinates easy to understand */ texMinX = texcoord[0]; texMinY = texcoord[1]; texMaxX = texcoord[2]; texMaxY = texcoord[3]; /* We don't need the original text surface anymore */ SDL_FreeSurface(text); /* Initialize the GL state */ glViewport(0, 0, WIDTH, HEIGHT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-2.0, 2.0, -2.0, 2.0, -20.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glShadeModel(GL_SMOOTH); /* Wait for a keystroke, and blit text on mouse press */ done = 0; while (!done) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_MOUSEMOTION: x = event.motion.x - w/2; y = event.motion.y - h/2; break; case SDL_KEYDOWN: case SDL_QUIT: done = 1; break; default: break; } } /* Clear the screen */ glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Draw the spinning cube */ glBegin(GL_QUADS); glColor3fv(color[0]); glVertex3fv(cube[0]); glColor3fv(color[1]); glVertex3fv(cube[1]); glColor3fv(color[2]); glVertex3fv(cube[2]); glColor3fv(color[3]); glVertex3fv(cube[3]); glColor3fv(color[3]); glVertex3fv(cube[3]); glColor3fv(color[4]); glVertex3fv(cube[4]); glColor3fv(color[7]); glVertex3fv(cube[7]); glColor3fv(color[2]); glVertex3fv(cube[2]); glColor3fv(color[0]); glVertex3fv(cube[0]); glColor3fv(color[5]); glVertex3fv(cube[5]); glColor3fv(color[6]); glVertex3fv(cube[6]); glColor3fv(color[1]); glVertex3fv(cube[1]); glColor3fv(color[5]); glVertex3fv(cube[5]); glColor3fv(color[4]); glVertex3fv(cube[4]); glColor3fv(color[7]); glVertex3fv(cube[7]); glColor3fv(color[6]); glVertex3fv(cube[6]); glColor3fv(color[5]); glVertex3fv(cube[5]); glColor3fv(color[0]); glVertex3fv(cube[0]); glColor3fv(color[3]); glVertex3fv(cube[3]); glColor3fv(color[4]); glVertex3fv(cube[4]); glColor3fv(color[6]); glVertex3fv(cube[6]); glColor3fv(color[1]); glVertex3fv(cube[1]); glColor3fv(color[2]); glVertex3fv(cube[2]); glColor3fv(color[7]); glVertex3fv(cube[7]); glEnd(); /* Rotate the cube */ glMatrixMode(GL_MODELVIEW); glRotatef(5.0, 1.0, 1.0, 1.0); /* Show the text on the screen */ SDL_GL_Enter2DMode(WIDTH, HEIGHT); glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(texMinX, texMinY); glVertex2i(x, y); glTexCoord2f(texMaxX, texMinY); glVertex2i(x+w, y); glTexCoord2f(texMinX, texMaxY); glVertex2i(x, y+h); glTexCoord2f(texMaxX, texMaxY); glVertex2i(x+w, y+h); glEnd(); SDL_GL_Leave2DMode(); /* Swap the buffers so everything is visible */ SDL_GL_SwapWindow(window); } SDL_GL_DeleteContext(context); TTF_CloseFont(font); cleanup(0); /* Not reached, but fixes compiler warnings */ return 0; } #else /* HAVE_OPENGL */ int main(int argc, char *argv[]) { printf("No OpenGL support on this system\n"); return 1; } #endif /* HAVE_OPENGL */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_ttf-2.24.0/mingw/pkg-support/Makefile0000664000000000000000000000243314733545702015200 0ustar00# # Makefile for installing the mingw32 version of the SDL2_ttf library CROSS_PATH := /usr/local ARCHITECTURES := i686-w64-mingw32 x86_64-w64-mingw32 all install: @echo "Type \"make native\" to install 32-bit to /usr" @echo "Type \"make cross\" to install 32-bit and 64-bit to $(CROSS_PATH)" native: make install-package arch=i686-w64-mingw32 prefix=/usr cross: mkdir -p $(CROSS_PATH)/cmake cp -rv cmake/* $(CROSS_PATH)/cmake for arch in $(ARCHITECTURES); do \ make install-package arch=$$arch prefix=$(CROSS_PATH)/$$arch; \ done install-package: @if test -d $(arch) && test -d $(prefix); then \ (cd $(arch) && cp -rv bin include lib $(prefix)/); \ sed "s|^prefix=.*|prefix=\'$(prefix)\'|" <$(arch)/lib/pkgconfig/SDL2_ttf.pc >$(prefix)/lib/pkgconfig/SDL2_ttf.pc; \ sed "s|^libdir=.*|libdir=\'$(prefix)/lib\'|" <$(arch)/lib/libSDL2_ttf.la >$(prefix)/lib/libSDL2_ttf.la; \ sed -e "s|^set[(]bindir \".*|set(bindir \"$(prefix)/bin\")|" \ -e "s|^set[(]includedir \".*|set(includedir \"$(prefix)/include\")|" \ -e "s|^set[(]libdir \".*|set(libdir \"$(prefix)/lib\")|" <$(arch)/lib/cmake/SDL2_ttf/sdl2_ttf-config.cmake >$(prefix)/lib/cmake/SDL2_ttf/sdl2_ttf-config.cmake; \ else \ echo "*** ERROR: $(arch) or $(prefix) does not exist!"; \ exit 1; \ fi SDL2_ttf-2.24.0/mingw/pkg-support/cmake/sdl2_ttf-config-version.cmake0000664000000000000000000000144214252202562022256 0ustar00# SDL2_ttf CMake version configuration file: # This file is meant to be placed in a cmake subfolder of SDL2_ttf-devel-2.x.y-mingw if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(sdl2_ttf_config_path "${CMAKE_CURRENT_LIST_DIR}/../i686-w64-mingw32/lib/cmake/SDL2_ttf/sdl2_ttf-config-version.cmake") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(sdl2_ttf_config_path "${CMAKE_CURRENT_LIST_DIR}/../x86_64-w64-mingw32/lib/cmake/SDL2_ttf/sdl2_ttf-config-version.cmake") else("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") set(PACKAGE_VERSION_UNSUITABLE TRUE) return() endif() if(NOT EXISTS "${sdl2_ttf_config_path}") message(WARNING "${sdl2_ttf_config_path} does not exist: MinGW development package is corrupted") set(PACKAGE_VERSION_UNSUITABLE TRUE) return() endif() include("${sdl2_ttf_config_path}") SDL2_ttf-2.24.0/mingw/pkg-support/cmake/sdl2_ttf-config.cmake0000664000000000000000000000136414252202562020576 0ustar00# SDL2_ttf CMake configuration file: # This file is meant to be placed in a cmake subfolder of SDL2_ttf-devel-2.x.y-mingw if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(sdl2_ttf_config_path "${CMAKE_CURRENT_LIST_DIR}/../i686-w64-mingw32/lib/cmake/SDL2_ttf/sdl2_ttf-config.cmake") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(sdl2_ttf_config_path "${CMAKE_CURRENT_LIST_DIR}/../x86_64-w64-mingw32/lib/cmake/SDL2_ttf/sdl2_ttf-config.cmake") else("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") set(SDL2_ttf_FOUND FALSE) return() endif() if(NOT EXISTS "${sdl2_ttf_config_path}") message(WARNING "${sdl2_ttf_config_path} does not exist: MinGW development package is corrupted") set(SDL2_ttf_FOUND FALSE) return() endif() include("${sdl2_ttf_config_path}") SDL2_ttf-2.24.0/release_checklist.md0000664000000000000000000000534414305522512014110 0ustar00# Release checklist ## New feature release * Update `CHANGES.txt` * Bump version number to 2.EVEN.0 in all these locations: * `SDL_ttf.h`: `SDL_TTF_MAJOR_VERSION`, `SDL_TTF_MINOR_VERSION`, `SDL_TTF_PATCHLEVEL` * `configure.ac`: `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION` * `CMakeLists.txt`: `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION` * `Makefile.os2`: `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION` * `version.rc`: `FILEVERSION`, `PRODUCTVERSION`, `FileVersion`, `ProductVersion` * `VisualC/Version.rc`: `FILEVERSION`, `PRODUCTVERSION`, `FileVersion`, `ProductVersion` * `Xcode/Info-Framework.plist`: `CFBundleShortVersionString`, `CFBundleVersion` * Bump ABI version information * `Xcode/SDL_ttf.xcodeproj/project.pbxproj`: `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION` * set first number in `DYLIB_CURRENT_VERSION` to (100 * *minor*) + 1 * set second number in `DYLIB_CURRENT_VERSION` to 0 * set `DYLIB_COMPATIBILITY_VERSION` to the same value * Regenerate `configure` * Run `./test-versioning.sh` to verify that everything is consistent * Do the release ## New bugfix release * Check that no new API/ABI was added * If it was, do a new feature release (see above) instead * Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is even) * Same places as listed above * Bump ABI version information * `Xcode/SDL_ttf.xcodeproj/project.pbxproj`: `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION` * set second number in `DYLIB_CURRENT_VERSION` to *patchlevel* * Leave `DYLIB_COMPATIBILITY_VERSION` unchanged * Regenerate `configure` * Run test/versioning.sh to verify that everything is consistent * Do the release ## After a feature release * Create a branch like `release-2.6.x` * Bump version number to 2.ODD.0 for next development branch * Same places as listed above * Bump ABI version information * Same places as listed above * Assume that the next feature release will contain new API/ABI * Run test/versioning.sh to verify that everything is consistent * Add a new milestone for issues ## New development prerelease * Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is odd) * Same places as listed above * Bump ABI version information * `Xcode/SDL_ttf.xcodeproj/project.pbxproj`: `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION` * set first number in `DYLIB_CURRENT_VERSION` to (100 * *minor*) + *patchlevel* + 1 * set second number in `DYLIB_CURRENT_VERSION` to 0 * set `DYLIB_COMPATIBILITY_VERSION` to the same value * Regenerate `configure` * Run test/versioning.sh to verify that everything is consistent * Do the release SDL2_ttf-2.24.0/sdl2_ttf-config-version.cmake.in0000664000000000000000000000060514252202562016167 0ustar00# sdl2_ttf cmake project-config-version input for ./configure scripts set(PACKAGE_VERSION "@MAJOR_VERSION@.@MINOR_VERSION@.@MICRO_VERSION@") if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) set(PACKAGE_VERSION_COMPATIBLE FALSE) else() set(PACKAGE_VERSION_COMPATIBLE TRUE) if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) set(PACKAGE_VERSION_EXACT TRUE) endif() endif() SDL2_ttf-2.24.0/sdl2_ttf-config.cmake.in0000664000000000000000000000674714473144761014534 0ustar00# sdl2_ttf cmake project-config input for ./configure scripts include(FeatureSummary) set_package_properties(SDL2_ttf PROPERTIES URL "https://www.libsdl.org/projects/SDL_ttf/" DESCRIPTION "Support for TrueType (.ttf) font files with Simple Directmedia Layer" ) set(SDL2_ttf_FOUND TRUE) set(SDL2TTF_HARFBUZZ @TTF_USE_HARFBUZZ@) set(SDL2TTF_FREETYPE TRUE) set(SDL2TTF_VENDORED @SDL2TTF_VENDORED@) set(SDL2TTF_SDL2_REQUIRED_VERSION @SDL_VERSION@) get_filename_component(CMAKE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_DIR} REALPATH) get_filename_component(prefix "${CMAKE_CURRENT_LIST_DIR}/@cmake_prefix_relpath@" ABSOLUTE) set(exec_prefix "@exec_prefix@") set(bindir "@bindir@") set(includedir "@includedir@") set(libdir "@libdir@") set(_sdl2ttf_extra_static_libraries "@TTF_LIBS@ @PC_LIBS@") string(STRIP "${_sdl2ttf_extra_static_libraries}" _sdl2ttf_extra_static_libraries) set(_sdl2ttf_bindir "${bindir}") set(_sdl2ttf_libdir "${libdir}") set(_sdl2ttf_incdir "${includedir}/SDL2") # Convert _sdl2ttf_extra_static_libraries to list and keep only libraries string(REGEX MATCHALL "(-[lm]([-a-zA-Z0-9._]+))|(-Wl,[^ ]*framework[^ ]*)" _sdl2ttf_extra_static_libraries "${_sdl2ttf_extra_static_libraries}") string(REGEX REPLACE "^-l" "" _sdl2ttf_extra_static_libraries "${_sdl2ttf_extra_static_libraries}") string(REGEX REPLACE ";-l" ";" _sdl2ttf_extra_static_libraries "${_sdl2ttf_extra_static_libraries}") unset(prefix) unset(exec_prefix) unset(bindir) unset(includedir) unset(libdir) include(CMakeFindDependencyMacro) if(NOT TARGET SDL2_ttf::SDL2_ttf) if(WIN32) set(_sdl2ttf_dll "${_sdl2ttf_bindir}/SDL2_ttf.dll") set(_sdl2ttf_imp "${_sdl2ttf_libdir}/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2_ttf.dll${CMAKE_STATIC_LIBRARY_SUFFIX}") if(EXISTS "${_sdl2ttf_dll}" AND EXISTS "${_sdl2ttf_imp}") add_library(SDL2_ttf::SDL2_ttf SHARED IMPORTED) set_target_properties(SDL2_ttf::SDL2_ttf PROPERTIES IMPORTED_LOCATION "${_sdl2ttf_dll}" IMPORTED_IMPLIB "${_sdl2ttf_imp}" ) endif() unset(_sdl2ttf_dll) unset(_sdl2ttf_imp) else() set(_sdl2ttf_shl "${_sdl2ttf_libdir}/${CMAKE_SHARED_LIBRARY_PREFIX}SDL2_ttf${CMAKE_SHARED_LIBRARY_SUFFIX}") if(EXISTS "${_sdl2ttf_shl}") add_library(SDL2_ttf::SDL2_ttf SHARED IMPORTED) set_target_properties(SDL2_ttf::SDL2_ttf PROPERTIES IMPORTED_LOCATION "${_sdl2ttf_shl}" ) endif() endif() if(TARGET SDL2_ttf::SDL2_ttf) set_target_properties(SDL2_ttf::SDL2_ttf PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_sdl2ttf_incdir}" COMPATIBLE_INTERFACE_BOOL "SDL2_SHARED" INTERFACE_SDL2_SHARED "ON" ) endif() endif() if(NOT TARGET SDL2_ttf::SDL2_ttf-static) set(_sdl2ttf_stl "${_sdl2ttf_libdir}/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2_ttf${CMAKE_STATIC_LIBRARY_SUFFIX}") if(EXISTS "${_sdl2ttf_stl}") add_library(SDL2_ttf::SDL2_ttf-static STATIC IMPORTED) set_target_properties(SDL2_ttf::SDL2_ttf-static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_sdl2ttf_incdir}" IMPORTED_LOCATION "${_sdl2ttf_stl}" INTERFACE_LINK_LIBRARIES "${_sdl2ttf_extra_static_libraries}" ) endif() unset(_sdl2ttf_stl) endif() unset(_sdl2ttf_extra_static_libraries) unset(_sdl2ttf_bindir) unset(_sdl2ttf_libdir) unset(_sdl2ttf_incdir) SDL2_ttf-2.24.0/showfont.c0000664000000000000000000003156214735261343012142 0ustar00/* showfont: An example of using the SDL_ttf library with 2D graphics. Copyright (C) 2001-2025 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. */ /* A simple program to test the text rendering feature of the TTF library */ #include "SDL.h" #include "SDL_ttf.h" #include #include #include #define DEFAULT_PTSIZE 18 #define DEFAULT_TEXT "The quick brown fox jumped over the lazy dog" #define WIDTH 640 #define HEIGHT 480 #define TTF_SHOWFONT_USAGE \ "Usage: %s [-solid] [-shaded] [-blended] [-wrapped] [-utf8|-unicode] [-b] [-i] [-u] [-s] [-outline size] [-hintlight|-hintmono|-hintnone] [-nokerning] [-wrap] [-fgcol r,g,b,a] [-bgcol r,g,b,a] .ttf [ptsize] [text]\n" typedef enum { TextRenderSolid, TextRenderShaded, TextRenderBlended } TextRenderMethod; typedef struct { SDL_Texture *caption; SDL_Rect captionRect; SDL_Texture *message; SDL_Rect messageRect; } Scene; static void draw_scene(SDL_Renderer *renderer, Scene *scene) { /* Clear the background to background color */ SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, scene->caption, NULL, &scene->captionRect); SDL_RenderCopy(renderer, scene->message, NULL, &scene->messageRect); SDL_RenderPresent(renderer); } static void cleanup(int exitcode) { TTF_Quit(); SDL_Quit(); exit(exitcode); } int main(int argc, char *argv[]) { char *argv0 = argv[0]; SDL_Window *window; SDL_Renderer *renderer; TTF_Font *font; SDL_Surface *text = NULL; Scene scene; int ptsize; int i, done; SDL_Color white = { 0xFF, 0xFF, 0xFF, 0 }; SDL_Color black = { 0x00, 0x00, 0x00, 0 }; SDL_Color *forecol; SDL_Color *backcol; SDL_Event event; TextRenderMethod rendermethod; int renderstyle; int outline; int hinting; int kerning; int wrap; int dump; enum { RENDER_LATIN1, RENDER_UTF8, RENDER_UNICODE } rendertype; char *message, string[128]; /* Look for special execution mode */ dump = 0; /* Look for special rendering types */ rendermethod = TextRenderShaded; renderstyle = TTF_STYLE_NORMAL; rendertype = RENDER_LATIN1; outline = 0; hinting = TTF_HINTING_NORMAL; kerning = 1; /* Default is black and white */ forecol = &black; backcol = &white; for (i=1; argv[i] && argv[i][0] == '-'; ++i) { if (SDL_strcmp(argv[i], "-solid") == 0) { rendermethod = TextRenderSolid; } else if (SDL_strcmp(argv[i], "-shaded") == 0) { rendermethod = TextRenderShaded; } else if (SDL_strcmp(argv[i], "-blended") == 0) { rendermethod = TextRenderBlended; } else if (SDL_strcmp(argv[i], "-utf8") == 0) { rendertype = RENDER_UTF8; } else if (SDL_strcmp(argv[i], "-unicode") == 0) { rendertype = RENDER_UNICODE; } else if (SDL_strcmp(argv[i], "-b") == 0) { renderstyle |= TTF_STYLE_BOLD; } else if (SDL_strcmp(argv[i], "-i") == 0) { renderstyle |= TTF_STYLE_ITALIC; } else if (SDL_strcmp(argv[i], "-u") == 0) { renderstyle |= TTF_STYLE_UNDERLINE; } else if (SDL_strcmp(argv[i], "-s") == 0) { renderstyle |= TTF_STYLE_STRIKETHROUGH; } else if (SDL_strcmp(argv[i], "-outline") == 0) { if (SDL_sscanf(argv[++i], "%d", &outline) != 1) { SDL_Log(TTF_SHOWFONT_USAGE, argv0); return(1); } } else if (SDL_strcmp(argv[i], "-hintlight") == 0) { hinting = TTF_HINTING_LIGHT; } else if (SDL_strcmp(argv[i], "-hintmono") == 0) { hinting = TTF_HINTING_MONO; } else if (SDL_strcmp(argv[i], "-hintnone") == 0) { hinting = TTF_HINTING_NONE; } else if (SDL_strcmp(argv[i], "-nokerning") == 0) { kerning = 0; } else if (SDL_strcmp(argv[i], "-wrap") == 0) { wrap = 1; } else if (SDL_strcmp(argv[i], "-dump") == 0) { dump = 1; } else if (SDL_strcmp(argv[i], "-fgcol") == 0) { int r, g, b, a = 0xFF; if (SDL_sscanf(argv[++i], "%d,%d,%d,%d", &r, &g, &b, &a) < 3) { SDL_Log(TTF_SHOWFONT_USAGE, argv0); return(1); } forecol->r = (Uint8)r; forecol->g = (Uint8)g; forecol->b = (Uint8)b; forecol->a = (Uint8)a; } else if (SDL_strcmp(argv[i], "-bgcol") == 0) { int r, g, b, a = 0xFF; if (SDL_sscanf(argv[++i], "%d,%d,%d,%d", &r, &g, &b, &a) < 3) { SDL_Log(TTF_SHOWFONT_USAGE, argv0); return(1); } backcol->r = (Uint8)r; backcol->g = (Uint8)g; backcol->b = (Uint8)b; backcol->a = (Uint8)a; } else { SDL_Log(TTF_SHOWFONT_USAGE, argv0); return(1); } } argv += i; argc -= i; /* Check usage */ if (!argv[0]) { SDL_Log(TTF_SHOWFONT_USAGE, argv0); return(1); } /* Initialize the TTF library */ if (TTF_Init() < 0) { SDL_Log("Couldn't initialize TTF: %s\n",SDL_GetError()); SDL_Quit(); return(2); } /* Open the font file with the requested point size */ ptsize = 0; if (argc > 1) { ptsize = atoi(argv[1]); } if (ptsize == 0) { i = 2; ptsize = DEFAULT_PTSIZE; } else { i = 3; } font = TTF_OpenFont(argv[0], ptsize); if (font == NULL) { SDL_Log("Couldn't load %d pt font from %s: %s\n", ptsize, argv[0], SDL_GetError()); cleanup(2); } TTF_SetFontStyle(font, renderstyle); TTF_SetFontOutline(font, outline); TTF_SetFontKerning(font, kerning); TTF_SetFontHinting(font, hinting); if(dump) { for(i = 48; i < 123; i++) { SDL_Surface* glyph = NULL; glyph = TTF_RenderGlyph_Shaded(font, i, *forecol, *backcol); if(glyph) { char outname[64]; SDL_snprintf(outname, sizeof(outname), "glyph-%d.bmp", i); SDL_SaveBMP(glyph, outname); } } cleanup(0); } /* Create a window */ if (SDL_CreateWindowAndRenderer(WIDTH, HEIGHT, 0, &window, &renderer) < 0) { SDL_Log("SDL_CreateWindowAndRenderer() failed: %s\n", SDL_GetError()); cleanup(2); } /* Show which font file we're looking at */ SDL_snprintf(string, sizeof(string), "Font file: %s", argv[0]); /* possible overflow */ switch (rendermethod) { case TextRenderSolid: text = TTF_RenderText_Solid(font, string, *forecol); break; case TextRenderShaded: text = TTF_RenderText_Shaded(font, string, *forecol, *backcol); break; case TextRenderBlended: text = TTF_RenderText_Blended(font, string, *forecol); break; } if (text != NULL) { scene.captionRect.x = 4; scene.captionRect.y = 4; scene.captionRect.w = text->w; scene.captionRect.h = text->h; scene.caption = SDL_CreateTextureFromSurface(renderer, text); SDL_FreeSurface(text); } /* Render and center the message */ if (argc > 2) { message = argv[2]; } else { message = DEFAULT_TEXT; } switch (rendertype) { case RENDER_LATIN1: switch (rendermethod) { case TextRenderSolid: if (wrap) { text = TTF_RenderText_Solid_Wrapped(font, message, *forecol, 0); } else { text = TTF_RenderText_Solid(font, message, *forecol); } break; case TextRenderShaded: if (wrap) { text = TTF_RenderText_Shaded_Wrapped(font, message, *forecol, *backcol, 0); } else { text = TTF_RenderText_Shaded(font, message, *forecol, *backcol); } break; case TextRenderBlended: if (wrap) { text = TTF_RenderText_Blended_Wrapped(font, message, *forecol, 0); } else { text = TTF_RenderText_Blended(font, message, *forecol); } break; } break; case RENDER_UTF8: switch (rendermethod) { case TextRenderSolid: if (wrap) { text = TTF_RenderUTF8_Solid_Wrapped(font, message, *forecol, 0); } else { text = TTF_RenderUTF8_Solid(font, message, *forecol); } break; case TextRenderShaded: if (wrap) { text = TTF_RenderUTF8_Shaded_Wrapped(font, message, *forecol, *backcol, 0); } else { text = TTF_RenderUTF8_Shaded(font, message, *forecol, *backcol); } break; case TextRenderBlended: if (wrap) { text = TTF_RenderUTF8_Blended_Wrapped(font, message, *forecol, 0); } else { text = TTF_RenderUTF8_Blended(font, message, *forecol); } break; } break; case RENDER_UNICODE: { Uint16 *unicode_text = SDL_iconv_utf8_ucs2(message); switch (rendermethod) { case TextRenderSolid: if (wrap) { text = TTF_RenderUNICODE_Solid_Wrapped(font, unicode_text, *forecol, 0); } else { text = TTF_RenderUNICODE_Solid(font, unicode_text, *forecol); } break; case TextRenderShaded: if (wrap) { text = TTF_RenderUNICODE_Shaded_Wrapped(font, unicode_text, *forecol, *backcol, 0); } else { text = TTF_RenderUNICODE_Shaded(font, unicode_text, *forecol, *backcol); } break; case TextRenderBlended: if (wrap) { text = TTF_RenderUNICODE_Blended_Wrapped(font, unicode_text, *forecol, 0); } else { text = TTF_RenderUNICODE_Blended(font, unicode_text, *forecol); } break; } SDL_free(unicode_text); } break; } if (text == NULL) { SDL_Log("Couldn't render text: %s\n", SDL_GetError()); TTF_CloseFont(font); cleanup(2); } scene.messageRect.x = (WIDTH - text->w)/2; scene.messageRect.y = (HEIGHT - text->h)/2; scene.messageRect.w = text->w; scene.messageRect.h = text->h; scene.message = SDL_CreateTextureFromSurface(renderer, text); SDL_Log("Font is generally %d big, and string is %d big\n", TTF_FontHeight(font), text->h); draw_scene(renderer, &scene); /* Wait for a keystroke, and blit text on mouse press */ done = 0; while (!done) { if (SDL_WaitEvent(&event) < 0) { SDL_Log("SDL_PullEvent() error: %s\n", SDL_GetError()); done = 1; continue; } switch (event.type) { case SDL_MOUSEBUTTONDOWN: scene.messageRect.x = event.button.x - text->w/2; scene.messageRect.y = event.button.y - text->h/2; scene.messageRect.w = text->w; scene.messageRect.h = text->h; draw_scene(renderer, &scene); break; case SDL_KEYDOWN: case SDL_QUIT: done = 1; break; default: break; } } SDL_FreeSurface(text); TTF_CloseFont(font); SDL_DestroyTexture(scene.caption); SDL_DestroyTexture(scene.message); cleanup(0); /* Not reached, but fixes compiler warnings */ return 0; } /* vi: set ts=4 sw=4 expandtab: */ SDL2_ttf-2.24.0/version.rc0000664000000000000000000000172114735274341012136 0ustar00 #include "winresrc.h" LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 2,24,0,0 PRODUCTVERSION 2,24,0,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL2_ttf\0" VALUE "FileVersion", "2, 24, 0, 0\0" VALUE "InternalName", "SDL2_ttf\0" VALUE "LegalCopyright", "Copyright (C) 2025 Sam Lantinga\0" VALUE "OriginalFilename", "SDL2_ttf.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" VALUE "ProductVersion", "2, 24, 0, 0\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END SDL2_ttf-2.24.0/REVISION.txt0000644000000000000000000000003214735530234012106 0ustar00release-2.24.0-0-g2a89147 SDL2_ttf-2.24.0/.git-hash0000644000000000000000000000005114735530234011615 0ustar002a891473eaf05ba1707a4b7913e6c4db7de7458a