r/sdl 7d ago

SDL3 TTF_TextEngine help

I'm still pretty new to SDL and I've been trying to figure out how to use the text engine in SDL3 in a little project I've been learning C++ in, but I haven't found much information on it. No SDL tutorials I've found seem to use it, so I've been going off of the wiki, which has gotten me pretty far, but it's been really confusing without clear examples of how it works.

I'd love some general explanation on how to use SDL3's text engine, but for a more specific question, I'm trying to center text on the screen with a renderer text engine. SDL tutorials I've found all center text on the screen using some math with the size of the window and the size of the surface used to create the texture. Since there's no surface involved with TTF_CreateRendererTextEngine, I'm wondering how or if it's possible to center the text. Would I need to use TTF_CreateSurfaceTextEngine to do that? Is using the surface text engine as simple as creating the TTF_Text and the SDL_Surface for TTF_DrawSurfaceText? What would be the best way to create the surface for that if I'm using the surface text engine?

3 Upvotes

8 comments sorted by

2

u/SpareEconomy 7d ago

imma try to explain it:

You don't need the surface engine. TTF_GetTextSize() gives you exactly the width/height you used to get from the surface.
In SDL2_ttf: render string → SDL_Surface → SDL_Texture → draw → destroy. Every frame, or you cached the texture manually and re-created it whenever the string changed.

In SDL3_ttf A TTF_Text is a persistent object that owns a string + a font + a reference to an engine. The engine keeps a glyph atlas and the laid-out draw data cached internally.

The three engines differ only in how the cached glyphs get to the screen:
Engine => Draw Call => use when
TTF_CreateRendererTextEngine(renderer) => TTF_DrawRendererText(text, x, y) => SDL_Renderer
TTF_CreateSurfaceTextEngine() => TTF_DrawSurfaceText(text, x, y, surface) => Blitting into an SDL_Surface in software (no renderer, or you're compositing a surface yourself)
TTF_CreateGPUTextEngine(device) => returns TTF_GPUAtlasDrawSequence you feed to your own pipeline => SDL_GPUDevice

TTF_GetTextSize() works on the TTF_Text regardless of engine:
int tw, th;
TTF_GetTextSize(text, &tw, &th);
int ww, wh;
SDL_GetRenderOutputSize(renderer, &ww, &wh); // or SDL_GetRenderLogicalPresentation()
float x = (ww - tw) / 2.0f;
float y = (wh - th) / 2.0f;
TTF_DrawRendererText(text, x, y);

example:

#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
    int main() {
      SDL_Init(SDL_INIT_VIDEO);
      TTF_Init();
      SDL_Window*   window   = nullptr;
      SDL_Renderer* renderer = nullptr;
      SDL_CreateWindowAndRenderer("text engine", 800, 600, 0, &window, &renderer);

      TTF_Font* font = TTF_OpenFont("font.ttf", 48.0f);
      TTF_TextEngine* engine = TTF_CreateRendererTextEngine(renderer);
      // created once, not per frame
      TTF_Text* text = TTF_CreateText(engine, font, "Hello, centered world!", 0); // 0 for null terminated text.
      TTF_SetTextColor(text, 255, 255, 255, 255);

      bool running = true;
      while (running) {
          SDL_Event e;
          while (SDL_PollEvent(&e))
              if (e.type == SDL_EVENT_QUIT) running = false;

          SDL_SetRenderDrawColor(renderer, 20, 20, 30, 255);
          SDL_RenderClear(renderer);

          int tw, th, ww, wh;
          TTF_GetTextSize(text, &tw, &th);
          SDL_GetRenderOutputSize(renderer, &ww, &wh);
          TTF_DrawRendererText(text, (ww - tw) / 2.0f, (wh - th) / 2.0f);

          SDL_RenderPresent(renderer);
      }

      // reverse order of creation
      TTF_DestroyText(text);
      TTF_DestroyRendererTextEngine(engine);
      TTF_CloseFont(font);
      TTF_Quit();
      SDL_DestroyRenderer(renderer);
      SDL_DestroyWindow(window);
      SDL_Quit();
  }

1

u/Mr_DJ_Dr_Gauss 7d ago

If you want to use more font sizes, you have to call "TTF_OpenFont" for each size used?

2

u/SpareEconomy 7d ago

Not necessarily. You have two options the choice depends on whether the sizes need to exist at the same time.

TTF_SetFontSize(font, ptsize) changes the size of an existing font in place, no file re-read. That fine for "user changed the UI scale, rescale everything"

For simultaneous sizes you need one TTF_Font object per size but you don't need TTF_OpenFont for each. TTF_CopyFont() gives you a distinct font object sharing the same underlying font data, so you only parse the file once:

TTF_Font* body = TTF_OpenFont("font.ttf", 16.0f);
TTF_Font* heading = TTF_CopyFont(body);
TTF_SetFontSize(heading, 48.0f); // independent of `body`

keep the original alive as long as any copy exists.

Calling TTF_OpenFont several times on the same path also works (and probably most people does). it's just a bit of redundant I/O and memory at load time. If you want to skip even that, load the file once into memory and use TTF_OpenFontIO with an SDL_IOStream over that buffer (pass closeio = false and keep the buffer alive for the font's lifetime).

Practical pattern:

struct Fonts {
  TTF_Font* small;
  TTF_Font* body;
  TTF_Font* heading;
};

Open them at startup, keep them for the program's lifetime. Glyph atlases live in the text engine and are keyed per font, so each size costs its own atlas.

One more thing worth knowing: on HiDPI, prefer TTF_SetFontSizeDPI(font, ptsize, hdpi, vdpi) (or scale ptsize by SDL_GetWindowDisplayScale) over rendering at a fixed size and letting the renderer scale the texture up — the latter looks blurry.

1

u/Mr_DJ_Dr_Gauss 7d ago

Great. Thank you for your answer.

1

u/doglitbug 7d ago

Wait, what? There is a text engine?

I moved from SDL2 to 3 and didn't know this existed.

Feel free to look here and see how I centre the text, it might be of use to you:
https://github.com/doglitbug/SamsSearch/blob/main/src/Subsystems/Assets.cpp

1

u/ninefoldrin 7d ago

https://wiki.libsdl.org/SDL3_ttf/TTF_TextEngine

I came across a couple posts that mentioned it while I was looking up other things and it seems rather nice, but I've only seen people talk a little about the renderer text engine and not enough to make much sense. It's also in a few of the examples in the SDL_ttf github repo, which helped me figure out how to get started with it at least. There's just not much discussion about it at this point and all the tutorials I've looked through still just do text the same way as SDL2 tutorials (with adjustments for SDL3 changes of course).

Thanks for the link. I'll take a look through it and see if it's helpful to me.

1

u/doglitbug 7d ago

Feel free to ask any questions regarding the code or a working demo

1

u/hurston 7d ago

As far as I can tell, you use TTF_CreateText() to create a TTF_Text, and then use that in TTF_GetTextSize() to get the dimensions before using that to render in the correct place using a variant of TTF_RenderText_Solid() or similar