我想知道如何使用SDL2渲染文本。我找到了一个名为SDL_TTF
的API和一些教程,但是它们不适用于我的情况。
我使用SDL_Window
和SDL_Renderer
,而教程则专门针对SDL_Surface
。
是否可以将SDL_TTF
与SDL_Render/SDL_Window
一起使用?如果是这样,怎么样?
答案 0 :(得分:12)
SDL_ttf最小可运行示例
不是超级高效,但易于集成。为了提高效率,请参阅:How to render fonts and text with SDL2 efficiently?
保存在与主SDL源不同的repo中,但托管在同一个官方服务器上,所以应该没问题:http://hg.libsdl.org/SDL_ttf/
换行不起作用。你必须使用线高。
编译并运行:
sudo apt-get install -y libsdl2-dev
gcc -lSDL2 -lSDL2_ttf -o ttf ttf.c
./ttf /usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf
您必须将TTF字体文件的路径传递给该程序。
ttf.c
#include <stdlib.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#define WINDOW_WIDTH 300
#define WINDOW_HEIGHT (WINDOW_WIDTH)
/*
- x, y: upper left corner.
- texture, rect: outputs.
*/
void get_text_and_rect(SDL_Renderer *renderer, int x, int y, char *text,
TTF_Font *font, SDL_Texture **texture, SDL_Rect *rect) {
int text_width;
int text_height;
SDL_Surface *surface;
SDL_Color textColor = {255, 255, 255, 0};
surface = TTF_RenderText_Solid(font, text, textColor);
*texture = SDL_CreateTextureFromSurface(renderer, surface);
text_width = surface->w;
text_height = surface->h;
SDL_FreeSurface(surface);
rect->x = x;
rect->y = y;
rect->w = text_width;
rect->h = text_height;
}
int main(int argc, char **argv) {
SDL_Event event;
SDL_Rect rect1, rect2;
SDL_Renderer *renderer;
SDL_Texture *texture1, *texture2;
SDL_Window *window;
char *font_path;
int quit;
if (argc == 1) {
font_path = "FreeSans.ttf";
} else if (argc == 2) {
font_path = argv[1];
} else {
fprintf(stderr, "error: too many arguments\n");
exit(EXIT_FAILURE);
}
/* Inint TTF. */
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_WIDTH, 0, &window, &renderer);
TTF_Init();
TTF_Font *font = TTF_OpenFont(font_path, 24);
if (font == NULL) {
fprintf(stderr, "error: font not found\n");
exit(EXIT_FAILURE);
}
get_text_and_rect(renderer, 0, 0, "hello", font, &texture1, &rect1);
get_text_and_rect(renderer, 0, rect1.y + rect1.h, "world", font, &texture2, &rect2);
quit = 0;
while (!quit) {
while (SDL_PollEvent(&event) == 1) {
if (event.type == SDL_QUIT) {
quit = 1;
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
/* Use TTF textures. */
SDL_RenderCopy(renderer, texture1, NULL, &rect1);
SDL_RenderCopy(renderer, texture2, NULL, &rect2);
SDL_RenderPresent(renderer);
}
/* Deinit TTF. */
SDL_DestroyTexture(texture1);
SDL_DestroyTexture(texture2);
TTF_Quit();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
在Ubuntu 16.04,SDL 2.0.4中测试。
答案 1 :(得分:11)
是的。您可以使用所需的文本创建表面,然后将其转换为可以渲染的纹理。
我的一个项目的一些示例代码:
std::string score_text = "score: " + std::to_string(score);
SDL_Color textColor = { 255, 255, 255, 0 };
SDL_Surface* textSurface = TTF_RenderText_Solid(font, score_text.c_str(), textColor);
SDL_Texture* text = SDL_CreateTextureFromSurface(renderer, textSurface);
int text_width = textSurface->w;
int text_height = textSurface->h;
SDL_FreeSurface(textSurface);
SDL_Rect renderQuad = { 20, win_height - 30, text_width, text_height };
SDL_RenderCopy(renderer, text, NULL, &renderQuad);
SDL_DestroyTexture(text);
这假设您已正确初始化SDL_ttf并加载了字体。在示例中,score
是一个int。屏幕被清除并呈现给其他地方(我没有包含那部分)。
有关完整的工作示例,请查看tutorial for SDL_ttf in SDL2 at Lazy Foo。
答案 2 :(得分:4)
由于有些人正在为更复杂的代码而苦苦挣扎,所以我在此处添加了自己的代码段,以帮助像我这样的初学者。这只会显示带有黑色问候世界的红色屏幕。不要忘记在构建中添加-lsdl2和-lsdl2_ttf,并在同一文件夹中包含Verdana.ttf字体。
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
const char* TITLE = "Hello World SDL2 + TTF";
const int WIDTH = 1280, HEIGHT = 720;
int main() {
SDL_Window *window = SDL_CreateWindow(TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Event windowEvent;
TTF_Init();
TTF_Font *verdanaFont = TTF_OpenFont("Verdana.ttf", 128);
SDL_Color textColor = { 0, 0, 0, 255 };
SDL_Surface *textSurface = TTF_RenderText_Solid(verdanaFont, "Hello World", textColor);
SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
SDL_Rect textRect;
textRect.x = WIDTH - textSurface->w * 0.5;
textRect.y = HEIGHT - textSurface->h * 0.5;
textRect.w = textSurface->w;
textRect.h = textSurface->h;
SDL_FreeSurface(textSurface);
TTF_Quit();
bool isRunning = true;
while (isRunning) {
while(SDL_PollEvent(&windowEvent)) {
switch (windowEvent.type) {
case SDL_QUIT:
isRunning = false;
break;
}
}
SDL_RenderCopy(renderer, textTexture, NULL, &textRect);
SDL_RenderPresent(renderer);
}
SDL_DestroyTexture(textTexture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
答案 3 :(得分:0)
如果您尝试从 Windows 上的 Powershell 执行此操作,您很快就会发现这是一个真正的 PITA。到现在...
当您想坚持使用 Clang++
和 SDL2
来呈现带有文本的原生 Windows 窗口时,我刚刚花了几个小时来调试所有细节以使其正常工作。
您需要安装 3 件东西; LLVM, SDL2, SDL2_ttf
。然后你必须确保你的程序能找到你的库、标题和字体。这基本上总结在以下程序 here 中:
//---------------------------------------------------------------------
// Name: HelloSDL2.cpp
// Author: EAML
// Date: 2021-05-16
//
// Description:
// A minimal PoC for producing a native SDL2 Windows app that can
// be ran from either Windows Explorer or from Powershell console.
// It's designed to use minimal command line, compiler options,
// and dependencies... It will display a gray window for 2 sec's.
//
// Dependencies:
// [1] LLVM Clang++ compiler package
// [2] SDL2 Libraries (DLL's) and Header files (*.h)
// [3] TTF Libraries (DLL's) and Header files (*.h)
//
// Notes:
// There is a slight variation in the bahaviour, depending on:
// (a) if you compile as a Windows GUI: the text will not show.
// (b) if you compile as a console CLI: text will show in both terminal and/or in a 2nd new window
// (c) You may need to use "main()" for console and "WinMain()" for GUI...
// (c) to install on Linux, use packages: clang, libsdl2-dev
// (d) Someone said: #define SDL_MAIN_HANDLED ...
//
// To Run:
// cp .\SDL2\lib\x64\SDL2.dll C:\Windows\. # For SDL2
// cp .\SDL2_ttf\lib\x64\*.dll C:\Windows\. # For SDL2 TTF
// cp C:\Windows\Fonts\arial.ttf . # Get a font...
//
// For a CLI version, with console output in 2nd Window:
// # clang++.exe -std=c++11 main.cpp -o main.exe -L .\SDL2\lib\x64\ -L .\SDL2_ttf\lib\x64\ -I .\SDL2_ttf\include\ -I .\SDL2\include\ -lShell32 -lSDL2main -lSDL2 -lSDL2_ttf -Wno-narrowing -Xlinker /subsystem:console
//
// For a GUI version, without any console output:
// # clang++.exe -std=c++11 main.cpp -o main.exe -L .\SDL2\lib\x64\ -L .\SDL2_ttf\lib\x64\ -I .\SDL2_ttf\include\ -I .\SDL2\include\ -lShell32 -lSDL2main -lSDL2 -lSDL2_ttf -Wno-narrowing -Xlinker /subsystem:windows
//
// References:
// [1] https://github.com/llvm/llvm-project/releases
// [2] http://www.libsdl.org/release/SDL2-devel-2.0.14-VC.zip
// [3] https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-VC.zip
// [4] https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html
// [5] http://www.sdltutorials.com/sdl-ttf
//---------------------------------------------------------------------
//#include <SDL2/SDL.h>
#include "SDL2/include/SDL.h"
#include "SDL2_ttf/include/SDL_ttf.h"
#include <stdio.h>
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define WINDOW_TITLE "Hello SDL2!"
//#define WINDOW_TEXT "Hello World!"
void drawText ( SDL_Surface* screen, char* string, int size, int x, int y, SDL_Color fgC, SDL_Color bgC) {
// Remember to call TTF_Init(), TTF_Quit(), before/after using this function.
TTF_Font* font = TTF_OpenFont("arial.ttf", size);
if(!font) {
printf("[ERROR] TTF_OpenFont() Failed with: %s\n", TTF_GetError());
exit(2);
}
TTF_SetFontStyle(font, TTF_STYLE_BOLD);
//SDL_Surface* textSurface = TTF_RenderText_Solid(font, string, fgC);
SDL_Surface* textSurface = TTF_RenderText_Shaded(font, string, fgC, bgC);
SDL_Rect textLocation = { x, y, 0, 0 };
SDL_BlitSurface(textSurface, NULL, screen, &textLocation);
SDL_FreeSurface(textSurface);
TTF_CloseFont(font);
//printf("Oh My Goodness, an error : %s\n", TTF_GetError()); return 1;
}
int main(int argc, char* args[]) {
SDL_Window* window = NULL; // The window we are rendering to
SDL_Surface* screenSurface = NULL; // The surface contained by the window
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError());
return 1;
}
window = SDL_CreateWindow(WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL) {
printf( "Window could not be created! SDL Error: %s\n", SDL_GetError());
return 1;
}
screenSurface = SDL_GetWindowSurface(window);
SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0x80, 0x80, 0x80)); // Set a gray background canvas
SDL_UpdateWindowSurface(window);
//-----------------------------------------------------
// Draw the Text
//-----------------------------------------------------
if(TTF_Init() == -1) {
printf("[ERROR] TTF_Init() Failed with: %s\n", TTF_GetError());
exit(2);
}
SDL_Color fgC1 = { 0xff,0xff,0xff }, bgC1 = {0x00,0x00,0xa0}; // white text on blue background
SDL_Color fgC2 = { 0x00,0x00,0x00 }, bgC2 = {0xff,0x00,0xff}; // black text on magenta background
drawText( screenSurface, (char*) "Hello World! @ (x=50, y=100)", 18, 50,100, fgC1, bgC1); // 18 pt @ (x=100,y=150)
drawText( screenSurface, (char*) "arial.ttf @ (x=200, y=150)", 16, 200,150, fgC2, bgC2); // 16 pt @ (x=100,y=150)
SDL_UpdateWindowSurface(window);
TTF_Quit();
//-----------------------------------------------------
// Get some info...
//-----------------------------------------------------
SDL_version compiled;
SDL_version linked;
SDL_version ttfv;
SDL_VERSION(&compiled);
SDL_GetVersion(&linked);
SDL_TTF_VERSION(&ttfv);
printf("Compiled using SDL version : %d.%d.%d \n", compiled.major, compiled.minor, compiled.patch);
printf("and linked with SDL version : %d.%d.%d \n", linked.major, linked.minor, linked.patch);
printf("and using SDL_TTF version : %d.%d.%d \n", ttfv.major, ttfv.minor, ttfv.patch);
SDL_Delay(3000); // Wait 3 seconds
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
运行上面代码的结果是这样的:
如何做到这一点?
[x] add to Windows PATH
的复选框。如果您没有将 LLVM 添加到 Windows PATH,那么至少临时手动添加。
$env:path += ";C:\Program Files\LLVM\bin"
为 Windows 安装 SDL2: 下载并提取 SDL2 和 SDL2_ttf 运行时二进制文件 (*.dll) 和头文件库(在 [2,3] 中找到) 并将它们放入与 C++ 文件相同目录中的单独 SDL 文件夹中。
你现在应该有类似的东西:
# tree --dirsfirst ./SDL2{,_ttf} -P *.h
./SDL2
├── include
│ ├── begin_code.h
│ ├── close_code.h
│ ├── SDL.h
│ ├── SDL_assert.h
...
│ ├── SDL_version.h
│ ├── SDL_video.h
│ └── SDL_vulkan.h
└── lib
./SDL2_ttf
└── include
└── SDL_ttf.h
# tree --dirsfirst ./SDL2{,_ttf}/lib -P *.dll
./SDL2/lib
├── x64
│ └── SDL2.dll
└── x86
└── SDL2.dll
./SDL2_ttf/lib
├── x64
│ ├── libfreetype-6.dll
│ ├── SDL2_ttf.dll
│ └── zlib1.dll
└── x86
├── libfreetype-6.dll
├── SDL2_ttf.dll
└── zlib1.dll
C:\Windows\
中,除非您知道如何让 clang++.exe 能够找到它们。 (我没能……)cd C:\path\to\main.cpp
cp .\SDL2\lib\x64\SDL2.dll C:\Windows\. # For SDL2
cp .\SDL2_ttf\lib\x64\*.dll C:\Windows\. # For SDL2 TTF
cp C:\Windows\Fonts\arial.ttf . # Get a font...
下载上面的 SDL2“Hello World”Windows 程序。
编译程序:
clang++.exe -std=c++11 main2.cpp -o main.exe -L .\SDL2\lib\x64\ -L .\SDL2_ttf\lib\x64\ -I .\SDL2_ttf\include\ -I .\SDL2\include\ -lShell32 -lSDL2main -lSDL2 -lSDL2_ttf -Wno-narrowing -Xlinker /subsystem:windows
库的放置顺序似乎很重要。确保它像上面一样。
另外,请注意两个不同的 -Xlinker
选项:
/subsystem:windows # This give you only one window but no console output
/subsystem:console # This give you console output, but in a 2nd window when in GUI
要查看其他链接器选项,请使用:
link.exe /link
link.exe /lib
# The most relevant are:
/DLL
/ENTRY:symbol
/LIBPATH:dir
/MACHINE:{ARM|ARM64|EBC|X64|X86}
/SUBSYSTEM:{CONSOLE | NATIVE | POSIX | WINDOWS | WINDOWSCE |...}
/VERBOSE
您现在可以开始了!
下载参考资料