Wie rendert man Text in SDL2?

Lesezeit: 20 Minuten

Benutzeravatar von Ethan Webster
Ethan Webster

Ich benutze eine SDL_Window und SDL_Renderer.

Ist es möglich zu verwenden SDL_TTF mit SDL_Render/SDL_Window? Wenn das so ist, wie?

Benutzeravatar von kdyz
kdyz

Ja, es ist möglich, da Sie einen Renderer und ein Fenster haben und nicht wirklich daran denken, sich mit Oberflächen zu beschäftigen, dann möchten Sie vielleicht daran denken, Texturen zu erstellen. Hier ist ein Beispielcode

//this opens a font style and sets a size
TTF_Font* Sans = TTF_OpenFont("Sans.ttf", 24);

// this is the color in rgb format,
// maxing out all would give you the color white,
// and it will be your text's color
SDL_Color White = {255, 255, 255};

// as TTF_RenderText_Solid could only be used on
// SDL_Surface then you have to create the surface first
SDL_Surface* surfaceMessage =
    TTF_RenderText_Solid(Sans, "put your text here", White); 

// now you can convert it into a texture
SDL_Texture* Message = SDL_CreateTextureFromSurface(renderer, surfaceMessage);

SDL_Rect Message_rect; //create a rect
Message_rect.x = 0;  //controls the rect's x coordinate 
Message_rect.y = 0; // controls the rect's y coordinte
Message_rect.w = 100; // controls the width of the rect
Message_rect.h = 100; // controls the height of the rect

// (0,0) is on the top left of the window/screen,
// think a rect as the text's box,
// that way it would be very simple to understand

// Now since it's a texture, you have to put RenderCopy
// in your game loop area, the area where the whole code executes

// you put the renderer's name first, the Message,
// the crop size (you can ignore this if you don't want
// to dabble with cropping), and the rect which is the size
// and coordinate of your texture
SDL_RenderCopy(renderer, Message, NULL, &Message_rect);

// Don't forget to free your surface and texture
SDL_FreeSurface(surfaceMessage);
SDL_DestroyTexture(Message);

Ich habe versucht, den Code Zeile für Zeile zu erklären, Sie sehen dort kein Fenster, da ich bereits davon ausgegangen bin, dass Sie wissen, wie man einen Renderer initialisiert, was mir eine Idee geben würde, dass Sie auch wissen, wie man ein Fenster initialisiert, dann alle Sie Notwendig ist die Idee, wie man eine Textur initialisiert.

Kleine Fragen hier, hat sich Ihr Fenster geöffnet? war es schwarz gefärbt? Wenn ja, dann waren meine Gedanken richtig, wenn nicht, dann können Sie mich einfach fragen und ich könnte diesen Code ändern, um den gesamten Abschnitt zu implementieren, der aus einem Renderer und einem Fenster besteht.

  • Könnten Sie bitte Ihre Oberfläche freigeben?

    – Leonardo

    6. Mai 2014 um 19:07 Uhr

  • Message_rect.w = 100; //soll der Programmierer wissen, wie breit sein Text am Ende wird?

    – IngenieurX

    21. März 2017 um 15:26 Uhr

  • Mit @engineerX können Sie Abmessungen von gerendertem Text erhalten TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h)

    – Einhorn

    1. August 2017 um 3:09 Uhr

  • TTF_Font & TTF_OpenFont undefined …wäre großartig zu erklären, woher diese kommen

    – Phil

    11. März 2018 um 16:51 Uhr

  • Wenn dies nicht funktioniert, versuchen Sie zu laufen TTF_Init() vor diesem Code.

    – Keatinge

    16. Juli 2018 um 4:42 Uhr

Ciro Santilli Benutzeravatar von OurBigBook.com
Ciro Santilli OurBigBook.com

SDL_ttf Minimales lauffähiges Beispiel

Geben Sie hier die Bildbeschreibung ein

Nicht supereffizient, aber einfach zu integrieren. Informationen zur Effizienz finden Sie unter: Wie werden Schriftarten und Text mit SDL2 effizient gerendert?

Wird in einem anderen Repo als die SDL-Hauptquelle aufbewahrt, aber auf demselben offiziellen Server gehostet, sollte also in Ordnung sein: http://hg.libsdl.org/SDL_ttf/

Zeilenumbrüche funktionieren nicht. Sie müssen mit Zeilenhöhen arbeiten.

Kompilieren und ausführen:

sudo apt-get install -y libsdl2-dev
gcc -lSDL2 -lSDL2_ttf -o ttf ttf.c
./ttf /usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf

Sie müssen den Pfad einer TTF-Schriftartdatei an das Programm übergeben.

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.

Getestet in Ubuntu 16.04, SDL 2.0.4.

Benutzeravatar von jpw
jpw

Ja, so ist es. Sie erstellen eine Oberfläche mit dem gewünschten Text und konvertieren sie dann in eine Textur, die Sie rendern können.

Beispielcode aus einem meiner Projekte:

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);

Dies setzt voraus, dass Sie SDL_ttf ordnungsgemäß initialisiert und eine Schriftart geladen haben. Im Beispiel scoreist ein int. Der Bildschirm wird gelöscht und woanders gerendert (ich habe diesen Teil nicht eingeschlossen).

Ein voll funktionsfähiges Beispiel finden Sie in der Tutorial für SDL_ttf in SDL2 bei Lazy Foo.

  • Dies löst aus irgendeinem Grund eine unbehandelte Speicherausnahme aus?

    – Ethan Webster

    5. April 2014 um 21:09 Uhr

  • @EthanWebster Oh, der Code war lediglich als (nicht funktionierendes) Beispiel gedacht, da ihm wichtige Dinge zum Ausführen fehlen, wie der Init-Code, die Fehlerprüfung usw.

    – jpw

    5. April 2014 um 21:22 Uhr

  • Ist dies effizient, selbst wenn der Text sich in jedem Rahmen ändert?

    – Damian Yerrick

    14. Dezember 2015 um 19:26 Uhr

  • nicht wirklich kompatibel mit meinem vorhandenen Code, der SDL_BlitScaled verwendet, und die Schriftart wird schlecht gerendert

    – Phil

    11. März 2018 um 17:22 Uhr


Benutzeravatar von Bruno Myrrha
Bruno Myrrha

Bearbeitet, um es einfacher zu machen, mitzumachen und zu verwenden Roboto.ttf (https://fonts.google.com/specimen/Roboto) Anstatt von Verdana.ttf und hinzuzufügen not2qubit Anregung. Bitte beachten Sie, dass dies überhaupt keiner C++-Klassenkonvention folgt. Ich wollte nur sicher sein, dass das Kopieren/Einfügen und Ausführen einfach genug ist.

Um dies zu erstellen, müssen Sie die Bibliothek hinzufügen SDL_ttf (https://www.libsdl.org/projects/SDL_ttf/).

g++ demo.cpp -o demo -Wall -I include -lsdl2 -lsdl2_ttf

Since there are some people struggling with more complex code, I've included my own snippet here to help some beginners like myself. This will just show a red screen with a black hello world. Don't forget to add -lsdl2 and -lsdl2_ttf on your build and include the Verdana.ttf font on the same folder.

#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h> //This is an sample library not included with stock SDL2. https://www.libsdl.org/projects/SDL_ttf/release-1.2.html

const char* WINDOW_TITLE = "Hello World SDL2 + TTF";
const char* FONT_NAME = "roboto.ttf";
const int FONT_SIZE = 128;
const int WINDOW_WIDTH = 1280, WINDOW_HEIGHT = 720;

SDL_Window* Window; // Window created by SDL.
SDL_Renderer* Renderer; // The renderer that shows our textures.
SDL_Event WindowEvent; // Event capturer from SDL Window.
SDL_Color TextColor = { 255, 0, 0, 255}; // Red SDL color.
TTF_Font* Font; // The font to be loaded from the ttf file.
SDL_Surface* TextSurface; // The surface necessary to create the font texture.
SDL_Texture* TextTexture; // The font texture prepared for render.
SDL_Rect TextRect; // Text rectangle area with the position for the texture text.

void CreateWindow() {
    Window = SDL_CreateWindow(WINDOW_TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_ALLOW_HIGHDPI);
    if (!Window)
        std::cout << "There was a problem creating the window.";
    Renderer = SDL_CreateRenderer(Window, -1, 0);
    if (!Renderer)
        std::cout << "There was a problem creating the renderer.";
}

void CreateText(const char* Message) {
    TTF_Init();
    TTF_Font *font = TTF_OpenFont(FONT_NAME, FONT_SIZE);
    if (!font)
        std::cout << "Couldn't find/init open ttf font." << std::endl;
    TextSurface = TTF_RenderText_Solid(font, Message, TextColor);
    TextTexture = SDL_CreateTextureFromSurface(Renderer, TextSurface);
    TextRect.x = WINDOW_WIDTH - TextSurface->w * 0.5; // Center horizontaly
    TextRect.y = WINDOW_HEIGHT - TextSurface->h * 0.5; // Center verticaly
    TextRect.w = TextSurface->w;
    TextRect.h = TextSurface->h;
    // After you create the texture you can release the surface memory allocation because we actually render the texture not the surface.
    SDL_FreeSurface(TextSurface);
    TTF_Quit();
}

bool IsPollingEvent() {
    while(SDL_PollEvent(&WindowEvent)) {
        switch (WindowEvent.type) {
            case SDL_QUIT: return false;
        }
    }
    return true;
}

void RenderText() {
    SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255); // Make window bg black.
    SDL_RenderClear(Renderer); // Paint screen black.
    SDL_RenderCopy(Renderer, TextTexture, NULL, &TextRect); // Add text to render queue.
    SDL_RenderPresent(Renderer); // Render everything that's on the queue.
    SDL_Delay(10); // Delay to prevent CPU overhead as suggested by the user `not2qubit`
}

void ClearMemory() {
    SDL_DestroyTexture(TextTexture);
    SDL_DestroyRenderer(Renderer);
    SDL_DestroyWindow(Window);
    SDL_Quit();
    std::cout << "Clear proccess done." << std::endl;
}

int main() {
    CreateWindow();
    CreateText("Hello SDL_Ttf");
    while (IsPollingEvent()) {
        RenderText();
    }
    ClearMemory();
    return EXIT_SUCCESS;
}

Für Powershell unter Windows

Wenn Sie versuchen, dies über Powershell unter Windows zu tun, werden Sie schnell feststellen, dass dies ein echtes PITA ist. Bis jetzt…

Ich habe gerade die Stunden damit verbracht, alle Details zu debuggen, damit dies funktioniert, wenn Sie darauf bestehen möchten, es zu verwenden Clang++ und SDL2 um ein natives Windows-Fenster mit Text zu rendern.

Es gibt 3 Dinge, die Sie installieren müssen; LLVM, SDL2, SDL2_ttf. Dann müssen Sie sicherstellen, dass Ihr Programm Ihre Bibliotheken, Kopfzeilen und Schriftarten findet. Dies ist im Wesentlichen in dem folgenden Programm zusammengefasst hier:

//---------------------------------------------------------------------
//  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;
}

Das Ergebnis der Ausführung des obigen Codes lautet wie folgt:

Geben Sie hier die Bildbeschreibung ein


Wie kommt man an diesen Punkt?

  1. Installieren LLVM für Windows:

    • Aktivieren Sie das Kontrollkästchen für [x] add to Windows PATH.
  2. Wenn Sie LLVM nicht zu Ihrem Windows PATH hinzugefügt haben, fügen Sie es zumindest vorübergehend und manuell hinzu.

    • Öffnen Sie eine Powershell und geben Sie Folgendes ein: $env:path += ";C:\Program Files\LLVM\bin"
  3. Installieren Sie SDL2 für Windows: Laden Sie die SDL2 & SDL2_ttf Laufzeit-Binärdateien (*.dll) und Header-Bibliotheken (zu finden in [2,3]) und trennen Sie sie SDL Ordner im selben Verzeichnis wie Ihre C++-Datei.

    Sie sollten jetzt so etwas haben wie:

# 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. Kopieren Sie alle relevant heruntergeladene DLLs in die C:\Windows\es sei denn, Sie wissen, wie man es macht clang++.exe in der Lage, sie zu finden. (ich konnte nicht…)
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. Laden Sie das obige SDL2-Windows-Programm „Hello World“ herunter.

    • Verwenden Sie die Minimaler PoC Code ab hier.
  2. Kompilieren Sie das Programm mit:

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

Die Reihenfolge, in der die Bibliotheken angeordnet sind, scheint eine Bedeutung zu haben. Stellen Sie sicher, dass es wie oben ist.

Beachten Sie auch die beiden Unterschiede -Xlinker Optionen:

/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

Um andere Linker-Optionen anzuzeigen, verwenden Sie:

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

Sie können jetzt loslegen!


Referenzen herunterladen

Wenn Sie die SDL2-TTF-Bibliothek nicht verwenden möchten oder können, können Sie sie einfach selbst mit der Freetype-Bibliothek implementieren.

Freetype einbeziehen

#include <ft2build.h>
#include FT_FREETYPE_H

Erstellen Sie eine Texturklasse

class texture
{
public:
    texture() : t{nullptr} {}

    texture(const texture &) = delete;
    texture &operator=(const texture &) = delete;

    texture(texture &&o) : t{o.t}
    {
        o.t = nullptr;
    }

    inline texture &operator=(texture &&o)
    {
        release();
        t = o.t;
        o.t = nullptr;
        return *this;
    }

    inline texture(SDL_Renderer *renderer, void *image, int width, int height, int depth, int pitch, uint32_t rmask, uint32_t gmask, uint32_t bmask, uint32_t amask) : t{nullptr}
    {
        attach_image(renderer, image, width, height, depth, pitch, rmask, gmask, bmask, amask);
    }

    inline void attach_image(SDL_Renderer *renderer, void *image, int width, int height, int depth, int pitch, uint32_t rmask, uint32_t gmask, uint32_t bmask, uint32_t amask)
    {
        release();

        SDL_Surface *s = SDL_CreateRGBSurfaceFrom(image, width, height, depth, pitch, rmask, gmask, bmask, amask);

        t = SDL_CreateTextureFromSurface(renderer, s);

        SDL_FreeSurface(s);
    }

    inline void draw(SDL_Renderer *renderer, const SDL_Rect *src, const SDL_Rect *dest) const
    {
        if 
            SDL_RenderCopyEx(renderer, t, src, dest, 0, nullptr, SDL_FLIP_NONE);
    }

    int width() const
    {
        if(!t) return 0;

        int w;

        SDL_QueryTexture(t, nullptr, nullptr, &w, nullptr);

        return w;
    }

    int height() const {
        if(!t) return 0;
        
        int h;

        SDL_QueryTexture(t, nullptr, nullptr, nullptr, &h);

        return h;
    }

    ~texture()
    {
        release();
    }

private:
    SDL_Texture *t;

    inline void release()
    {
        if 
            SDL_DestroyTexture
        t = nullptr;
    }
};

Erstellen Sie eine Glyphenklasse

struct character : texture
{
    using texture::texture;
    unsigned int advance;
    int bearing_x;
    int bearing_y;
};

Bestimmen Sie die Endianness und richten Sie Masken ein

#if SDL_BYTEORDER == SDL_BIG_ENDIAN

#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000

#else

#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff

#endif

Erstellen Sie einen Konverter von der 8-Bit-Pixeltiefe des freien Typs zu 32 Bit für SDL2

void convert_8_to_32_depth(std::vector<uint32_t> &res, unsigned char *image, int width, int height)
{
    res.clear();
    res.reserve(width * height);
    for (int y = 0; y < height; ++y)
    {
        for (int x = 0; x < width; ++x)
        {
            if (image[y * width + x])
                res.push_back(amask);
            else
                res.push_back(0);
        }
    }
}

Erstellen Sie eine Schriftspeicher-Texturklasse

struct lib
{
    lib() = default;

    lib(SDL_Renderer *renderer, int height)
    {
        init(renderer, height);
    }

    void init(SDL_Renderer *renderer, int height)
    {
        FT_Library ft;
        if (FT_Init_FreeType(&ft))
        {
            std::cout << "Can't init freetype lib\n";
        }

        FT_Face face;

        //use if you have font data in array
        // if (FT_New_Memory_Face(ft, font_array, std::size(font_array), 0, &face))
        // {
        //  std::cout << "Failed to load font\n";
        // }

        if (FT_New_Face(ft, "location/to/my/font.ttf", 0, &face))
        {
            std::cout << "Failed to load font\n";
        }

        //set size of future glyphs
        FT_Set_Pixel_Sizes(face, 0, height);

        std::vector<uint32_t> image;

        for (unsigned int c = 0; c < 256; ++c)
        {
            //load freetype glyph
            if (FT_Load_Char(face, c, FT_LOAD_RENDER))
            {
                std::cout << "failed to load glyph\n";
            }

            if (face->glyph->bitmap.width)
            {
                ///get image data that works for sdl2
                convert_8_to_32_depth(image, face->glyph->bitmap.buffer, face->glyph->bitmap.width, face->glyph->bitmap.rows);
                chars[c].attach_image(renderer, image.data(), face->glyph->bitmap.width, face->glyph->bitmap.rows,
                                      32, face->glyph->bitmap.width * sizeof(decltype(image)::value_type),
                                      rmask, gmask, bmask, amask);
            }

            chars[c].bearing_x = face->glyph->bitmap_left;
            chars[c].bearing_y = face->glyph->bitmap_top;
            chars[c].advance = face->glyph->advance.x;
        }

        FT_Done_Face(face);
        FT_Done_FreeType(ft);
    }
    character chars[256];
};

Textfunktion drucken

void print_text(SDL_Renderer *renderer, int x, int y, int height, std::string_view text)
{
    static constexpr int default_height = 50;
    
    //store map of each renderer used to avoid creating more libs than neccesary
    static std::map<SDL_Renderer *, lib> l;
    const lib& ts = l.try_emplace(renderer, renderer, default_height).first->second;

    float scale = height / default_height;

    SDL_Rect dest;

    for (auto c : text)
    {

        dest.x = x + ts.chars[c].bearing_x * scale;
        dest.y = y - ts.chars[c].bearing_y * scale;

        dest.w = ts.chars[c].width() * scale;
        dest.h = ts.chars[c].height() * scale;

        ts.chars[c].draw(renderer, nullptr, &dest);

        x += (ts.chars[c].advance >> 6) * scale;
    }
}

Benutzeravatar von BIOHAZARD
BIOGEFAHR

Wenn es mit Klasse zu tun haben soll:

// Include somewhere
#include <SDL2/SDL_ttf.h>

// Do not forget to init TTF once somewhere main.cpp
if (TTF_Init()==-1) {
    printf("Failed to TTF: %s", SDL_GetError());
    return 1;
}

// Have a font instance ready
TTF_Font* font = TTF_OpenFont("assets/fonts/ugly.ttf", 72);
if (font==NULL){
    printf("Failed to load font: %s", SDL_GetError());
}

// Have a Text instance ready
// (Class file is below this code)
// Also you need to provide SDL_Renderer* renderer to init
Text* fps_tracker = new Text(renderer, font);

// Somewhere in while true
fps_tracker.setText("FPS: 232");

// Render it
fps_tracker.render();

Hier ist die Klasse:

using namespace std;
#include <string>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>

class Text {
    SDL_Texture* texture = NULL;
    SDL_Rect position;
    TTF_Font* font;
    SDL_Renderer* renderer;
    SDL_Color color;
    string text;
    public: 
    Text(SDL_Renderer* renderer, TTF_Font* font, string text="", int x=0, int y=0, SDL_Color color={255, 255, 255}) {
        position.x = x;
        position.y = y;
        this->color = color;
        this->font = font;
        this->renderer = renderer;
    }

    void setText(string text) {
        if (this->text==text){
            return;
        }
        this->text = text;
        SDL_DestroyTexture(texture);
        SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str(), color);
        if (surface==NULL) {
            printf("Failed to render text: %s", SDL_GetError());            
        }
        position.w = surface->w;
        position.h = surface->h;
        texture = SDL_CreateTextureFromSurface(renderer, surface);
        SDL_FreeSurface(surface);
    }
    
    void render() {
        SDL_RenderCopy(renderer, texture, NULL, &position);
    }  

    ~Text() {
        SDL_DestroyTexture(texture);
    }
    
};

1410920cookie-checkWie rendert man Text in SDL2?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy