¿Cómo renderizar texto en SDL2?

19 minutos de lectura

¿Como renderizar texto en SDL2
Ethan Webster

estoy usando un SDL_Window y SDL_Renderer.

¿Es posible usar SDL_TTF con SDL_Render/SDL_Window? ¿Si es así, cómo?

1647563348 607 ¿Como renderizar texto en SDL2
kdyz

Sí, es posible, dado que tiene un renderizador y una ventana, además de que realmente no tiene ninguna idea de jugar con las superficies, entonces es posible que desee pensar en crear texturas, aquí hay un código de muestra

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

Traté de explicar el código línea por línea, no ves ninguna ventana allí mismo ya que supuse que sabías cómo inicializar un renderizador lo que me daría una idea de que también sabes cómo inicializar una ventana, entonces todo lo que necesidad es la idea de cómo inicializar una textura.

Preguntas menores aquí, ¿se abrió su ventana? era de color negro? si es así, mis pensamientos eran correctos, si no, entonces puede preguntarme y podría cambiar este código para implementar toda la sección que consiste en un renderizador y una ventana.

  • ¿Podría liberar su superficie?

    –Leonardo

    6 mayo 2014 a las 19:07

  • Mensaje_rect.w = 100; // ¿Se supone que el programador sabe qué tan ancho terminará siendo su texto?

    – ingenieroX

    21 de marzo de 2017 a las 15:26

  • @engineerX puede obtener las dimensiones del texto renderizado con TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h)

    – Unicornio

    1 de agosto de 2017 a las 3:09

  • TTF_Font & TTF_OpenFont undefined …sería genial explicar de dónde vienen

    – fdsfdsfdsfds

    11 de marzo de 2018 a las 16:51

  • Si esto no funciona, intente ejecutar TTF_Init() antes de este código.

    – Keatinge

    16 de julio de 2018 a las 4:42

¿Como renderizar texto en SDL2
Ciro Santilli Путлер Капут 六四事

Ejemplo ejecutable mínimo de SDL_ttf

ingrese la descripción de la imagen aquí

No muy eficiente, pero fácil de integrar. Para mayor eficiencia, consulte: ¿Cómo representar fuentes y texto con SDL2 de manera eficiente?

Mantenido en un repositorio separado de la fuente principal de SDL, pero alojado en el mismo servidor oficial, por lo que debería estar bien: http://hg.libsdl.org/SDL_ttf/

Las nuevas líneas no funcionarán. Tienes que trabajar con alturas de línea.

Compilar y ejecutar:

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

Debe pasar la ruta de un archivo de fuente TTF al programa.

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 ascendente.

Probado en Ubuntu 16.04, SDL 2.0.4.

1647563349 685 ¿Como renderizar texto en SDL2
jpw

Sí lo es. Crea una superficie con el texto que desea y luego lo convierte en una textura que puede renderizar.

Algunos ejemplos de código de uno de mis proyectos:

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

Esto supone que ha inicializado correctamente SDL_ttf y ha cargado una fuente. en el ejemplo scorees un int. La pantalla se borra y se muestra en otro lugar (no incluí esa parte).

Para ver un ejemplo de trabajo completo, consulte el tutorial para SDL_ttf en SDL2 en Lazy Foo.

  • ¿Esto lanza una excepción de memoria no controlada por alguna razón?

    –Ethan Webster

    5 abr 2014 a las 21:09

  • @EthanWebster Oh, el código fue simplemente un ejemplo (que no funciona), ya que carece de cosas importantes para ejecutar, como el código de inicio, la verificación de errores, etc.

    – jpw

    5 abr 2014 a las 21:22

  • @EthanWebster Agregué un enlace a un tutorial con un código de ejemplo funcional.

    – jpw

    5 abr 2014 a las 21:25

  • Gracias, voy a comprobar eso.

    –Ethan Webster

    6 abr 2014 a las 9:30

  • ¿Es esto eficiente incluso si el texto cambiará cada cuadro?

    – Damián Yerrick

    14 de diciembre de 2015 a las 19:26

1647563350 473 ¿Como renderizar texto en SDL2
bruno mirra

Editado para que sea más fácil de seguir, de usar Roboto.ttf (https://fonts.google.com/specimen/Roboto) en lugar de Verdana.ttf y para agregar not2qubit sugerencia. Tenga en cuenta que esto no sigue ninguna convención de clase de C++ en absoluto. Solo quería estar seguro de que esto sería lo suficientemente fácil de copiar/pegar y ejecutar.

Para construir esto necesitas agregar la biblioteca SDL_ttf (https://www.libsdl.org/projects/SDL_ttf/).

g++ demo.cpp -o demo -Pared -Incluyo -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;
}

Para Powershell en Windows

Si intenta hacer esto desde Powershell en Windows, pronto descubrirá que se trata de un PITA real. Hasta ahora…

Acabo de pasar las horas para depurar todos los detalles para que esto funcione, cuando quieres insistir en usar Clang++ y SDL2 para representar una ventana nativa de Windows con texto.

Hay 3 cosas que necesitas instalar; LLVM, SDL2, SDL2_ttf. Luego, debe asegurarse de que su programa encuentre sus bibliotecas, encabezados y fuentes. Esto se resume básicamente en el siguiente programa aquí:

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

El resultado de ejecutar el código anterior es este:

ingrese la descripción de la imagen aquí


¿Cómo llegar a este punto?

  1. Instalar en pc LLVM para Windows:

    • Marque la casilla para [x] add to Windows PATH.
  2. Si no agregó LLVM a su RUTA de Windows, al menos agréguelo de forma temporal y manual.

    • Abra un Powershell y escriba: $env:path += ";C:\Program Files\LLVM\bin"
  3. Instale SDL2 para Windows: Descargue y extraiga el SDL2 & SDL2_ttf binarios de tiempo de ejecución (*.dll) y bibliotecas de encabezado (que se encuentran en [2,3]) y colóquelos en lugares separados SDL carpeta(s) en el mismo directorio que su archivo C++.

    Ahora deberías tener algo como:

# 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. Copia todo el pertinente archivos DLL descargados en el C:\Windows\a menos que sepa cómo hacer clang++.exe poder encontrarlos. (No pude…)
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. Descargue el programa de Windows SDL2 “Hello World” anterior.

    • Utilizar el PoC mínimo código de aquí.
  2. Compilar el programa con:

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

El orden de cómo se colocan las bibliotecas parece tener una importancia. Asegúrate de que sea como arriba.

Además, observe los dos diferentes -Xlinker opciones:

/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

Para ver otras opciones de vinculación, utilice:

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

¡Ya estás listo para irte!


Descargar Referencias

Si no quiere o no puede usar la biblioteca SDL2 TTF, puede implementarla usted mismo fácilmente con solo la biblioteca de tipo libre.

Incluir tipo libre

#include <ft2build.h>
#include FT_FREETYPE_H

Crear clase de textura

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

Crear clase de glifo

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

Determinar endianness y configurar máscaras

#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

Cree un convertidor desde la profundidad de píxeles de 8 bits de tipo libre a 32 bits para 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);
        }
    }
}

Crear clase de textura de almacenamiento de fuentes

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

Función de impresión de texto

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad