如何在SDL2中呈现文本?

时间:2014-04-05 20:39:54

标签: c sdl-2

我想知道如何使用SDL2渲染文本。我找到了一个名为SDL_TTF的API和一些教程,但是它们不适用于我的情况。

我使用SDL_WindowSDL_Renderer,而教程则专门针对SDL_Surface

是否可以将SDL_TTFSDL_Render/SDL_Window一起使用?如果是这样,怎么样?

4 个答案:

答案 0 :(得分:12)

SDL_ttf最小可运行示例

enter image description here

不是超级高效,但易于集成。为了提高效率,请参阅: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;
}

GitHub upstream

在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

如果您尝试从 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;
}

运行上面代码的结果是这样的:

enter image description here


如何做到这一点?

  1. 安装LLVM for Windows

    • 选中 [x] add to Windows PATH 的复选框。
  2. 如果您没有将 LLVM 添加到 Windows PATH,那么至少临时手动添加。

    • 打开 Powershell 并输入:$env:path += ";C:\Program Files\LLVM\bin"
  3. 为 Windows 安装 SDL2: 下载并提取 SDL2SDL2_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

  1. 将所有相关下载的 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...
  1. 下载上面的 SDL2“Hello World”Windows 程序。

    • 使用来自hereMinimal PoC代码。
  2. 编译程序:

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

您现在可以开始了!


下载参考资料