In this lesson we’ll see how to perform basic True Type font rendering with the SDL_ttf extension library. Setting up the library is identical to what we did in Lesson 3 for SDL_image, but just replace “image” with “ttf” (Windows users should also copy the included freetype dll over). So download SDL_ttf, take a peek at the documentation, and let’s get started!
The first thing we’ll need after the library is a font to render our text with. I made a pretty awful font using BitFontMaker which you can download from the repository, or if you have some other font you’d like to use that’s fine too. My font only provides the basic ASCII characters, so if you try to render non-ASCII characters they probably won’t show up. The code for this lesson will build off of what we wrote for Lesson 5, so if you don’t have that to start from grab it off Github, the image being loaded and the clips calculation and drawing will be replaced with what we learn here.
Rendering Text
SDL_ttf provides a few different ways for rendering text with varying speed/quality trade-offs, along with the
ability to render UTF8 and Unicode strings and glyphs. The documentation provides a nice overview of the different rendering methods available, so it’s worth reading
and knowing when you’d want to use which one depending on your speed/quality requirements. For this lesson
we’ll be using TTF_RenderText_Blended
since we don’t really have any time constraints and would like our
text to be stylin’. The various render text functions also take an RGB SDL_Color
which we can use to pick the text color to draw.
Unfortunately SDL_ttf can only render to a surface so we’ll have to run an additional
step after rendering the text to create a texture from it that we can draw with our renderer. Of course we’ll also
need to load a font to use, which is done with TTF_OpenFont
, where we can also specify the font size that we want.
Writing Our renderText
Function
To make this easy for ourselves we’ll create a function renderText
that will take a message, font file containing
the TTF font we want to use, the color and size we want and the renderer to load the final texture into. The
function will then open the font, render the text, convert it to a texture and return the texture. Since there
could be some problems along the way we’ll also need to check each of our library calls for errors and handle
them appropriately, i.e. clean up any resources, log the error and return nullptr
so we know something bad happened.
SDL_ttf will report any of its errors through SDL_GetError
so we can continue to use logSDLError
for error logging.
With those requirements in mind, let’s write our renderText
function:
/**
* Render the message we want to display to a texture for drawing
* @param message The message we want to display
* @param fontFile The font we want to use to render the text
* @param color The color we want the text to be
* @param fontSize The size we want the font to be
* @param renderer The renderer to load the texture in
* @return An SDL_Texture containing the rendered message, or nullptr if something went wrong
*/
SDL_Texture* renderText(const std::string &message, const std::string &fontFile,
SDL_Color color, int fontSize, SDL_Renderer *renderer)
{
//Open the font
TTF_Font *font = TTF_OpenFont(fontFile.c_str(), fontSize);
if (font == nullptr){
logSDLError(std::cout, "TTF_OpenFont");
return nullptr;
}
//We need to first render to a surface as that's what TTF_RenderText
//returns, then load that surface into a texture
SDL_Surface *surf = TTF_RenderText_Blended(font, message.c_str(), color);
if (surf == nullptr){
TTF_CloseFont(font);
logSDLError(std::cout, "TTF_RenderText");
return nullptr;
}
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surf);
if (texture == nullptr){
logSDLError(std::cout, "CreateTexture");
}
//Clean up the surface and font
SDL_FreeSurface(surf);
TTF_CloseFont(font);
return texture;
}
Performance Warning
An important thing to note here is that each time we want to render a message we re-open and close the font, which is
ok for this case since we’re only rendering one message a single time, but if we wanted to render a lot of text
and/or render text frequently it would be a much better idea to keep the font open for as long as we needed it.
Our version of renderText
for this more common use case would take a TTF_Font*
instead of the font file name,
and wouldn’t open or close the font, as the font’s lifetime would be managed separately.
Initializing SDL_ttf
As with SDL we need to initialize the library before we can use it. This is done via
TTF_Init
which will return 0 on success. To
initialize SDL_ttf we just call this function after initializing SDL and check the return value to make sure it went ok.
if (TTF_Init() != 0){
logSDLError(std::cout, "TTF_Init");
SDL_Quit();
return 1;
}
Using renderText
With our handy renderText
function we can render our message with a very simple call. For this lesson I’ve chosen
to render “TTF fonts are cool!” in white at a font size of 64 using the terrible font I made earlier. We can
then query the width and height the same as for any other texture and compute the x/y coordinates to draw the
message centered in the window.
const std::string resPath = getResourcePath("Lesson6");
//We'll render the string "TTF fonts are cool!" in white
//Color is in RGBA format
SDL_Color color = { 255, 255, 255, 255 };
SDL_Texture *image = renderText("TTF fonts are cool!", resPath + "sample.ttf",
color, 64, renderer);
if (image == nullptr){
cleanup(renderer, window);
TTF_Quit();
SDL_Quit();
return 1;
}
//Get the texture w/h so we can center it in the screen
int iW, iH;
SDL_QueryTexture(image, NULL, NULL, &iW, &iH);
int x = SCREEN_WIDTH / 2 - iW / 2;
int y = SCREEN_HEIGHT / 2 - iH / 2;
Drawing the Text
Finally we can draw the texture as we’ve done before with our renderTexture
function.
//Note: This is within the program's main loop
SDL_RenderClear(renderer);
//We can draw our message as we do any other texture, since it's been
//rendered to a texture
renderTexture(image, renderer, x, y);
SDL_RenderPresent(renderer);
If everything goes well you should see something like this rendered to the screen:
End of Lesson 6
That’s it for our quick look at SDL_ttf! Don’t forget to check out the documentation for the library to see what else it’s capable of. As always, if you run into any issues with the lesson feel free to send me an email or tweet.
While I meant to continue writing more lessons, I got tied up with other projects and didn’t get back to it. From this point you could continue following some of the Lazy Foo tutorials, or follow the SDL wiki examples and documentation.