SDL Tutorial in C Part 2: Displaying Text
Tags: c, gamedev, tutorial
Published: 2024-02-23 00:20:22

> This is part 2 of my SDL series using C. If you missed part 1 you can [find it here](https://dev.to/deusinmachina/sdl-tutorial-part-1-opening-a-window-133p).📌
 


![SDL window shoing hello world](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggrab8jlgq7k9xa0980o.png)

## Displaying Text Cheat Sheet
1. [TTF_Init](https://wiki.libsdl.org/SDL2_ttf/TTF_Init)
2. [TTF_OpenFont](https://wiki.libsdl.org/SDL2_ttf/TTF_OpenFont).
3. [TTF_RenderText_Solid](https://wiki.libsdl.org/SDL2_ttf/TTF_RenderText_Solid)
4. [SDL_CreateTextureFromSurface](https://wiki.libsdl.org/SDL2/SDL_CreateTextureFromSurface)
5. [SDL_RenderCopy](https://wiki.libsdl.org/SDL2/SDL_RenderCopy)

## Tutorial
Displaying Text in SDL2 requires a very similar set of operations to how we handled creating a window and renderer in part 1. But before we get to displaying text, we need to include the appropriate header. Since SDL2 is designed to be modular, the libraries that work with text are in their own separate headers/files. To work with text, you will need `SDL_ttf.h`. You can either include it and the appropriate files directly in your project, linking them at compile time, or if you are on Linux like I am, use your package manager to install them globally. While we won’t be going over the specifics for how to set up SDL in this tutorial, [the SDL Wiki](https://wiki.libsdl.org/SDL2/Installation) provides a page on how to properly set up SDL that is pretty thorough. Now, for those of you who just want to see the code, here is the entire source 

```
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>

#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 720
#define MY_FONT "/usr/share/fonts/truetype/freefont/FreeSans.ttf"

int main(){
  if (SDL_Init(SDL_INIT_VIDEO) < 0){
    printf("Couldn't initialize SDL: %s\n", SDL_GetError());
    return EXIT_FAILURE;
  }
    // Initialize SDL_ttf
    if (TTF_Init() < 0) {
        printf("SDL_ttf could not initialize! TTF_Error: %s\n", TTF_GetError());
        SDL_Quit();
        return EXIT_FAILURE;
    }

  SDL_Window *window = SDL_CreateWindow("Drawing Text", SDL_WINDOWPOS_UNDEFINED,
					SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, 0);

  if (!window){
    printf("Failed to open %d x %d window: %s\n", SCREEN_WIDTH, SCREEN_HEIGHT, SDL_GetError());
    return EXIT_FAILURE;
  }

  SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);

  if (!renderer){
    printf("Failed to create renderer: %s\n", SDL_GetError());
    return EXIT_FAILURE;
  }

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

  TTF_Font *font = TTF_OpenFont(MY_FONT, 64); // specify the path to your font file and font size

  if (!font){
    printf("Failed to load font: %s\n", TTF_GetError());
    return EXIT_FAILURE;
  }
  
  // Create surface with rendered text
  SDL_Color textColor = {0, 0, 0, 255}; // black color
  SDL_Surface *textSurface = TTF_RenderText_Solid(font, "Hello World!", textColor);

  if (!textSurface) {
    printf("Failed to create text surface: %s\n", TTF_GetError());
    return EXIT_FAILURE;
  }

  // Create texture from surface
  SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);

  if (!textTexture){
    printf("Failed to create text texture: %s\n", SDL_GetError());
    return EXIT_FAILURE;
  }

  // Render text
  SDL_Rect textRect = {50, 50, textSurface->w, textSurface->h}; // rectangle where the text is drawn 
  SDL_RenderCopy(renderer, textTexture, NULL, &textRect);

  SDL_RenderPresent(renderer);
  SDL_Delay(2000);
  SDL_DestroyWindow(window);
  SDL_DestroyRenderer(renderer);
  SDL_Quit();

  return EXIT_SUCCESS;
}
```

Starting off we `#include <SDL2/SDL_ttf.h>` which gives us access to functions that help us display text. Since the ttf module works with fonts, which are normally, .ttf files, we need a font to properly display the text in our Window. You can either download a font from the internet, or use a .ttf file that you have on your computer. To keep it simple, I’ll just use one of the default ones that comes on my computer. At the top of my program, I've created a define that simply points to a default font
 ```
 #define MY_FONT "/usr/share/fonts/truetype/freefont/FreeSans.ttf"
```

> On macOS you should be able to find some .ttf files located at `/Library/Fonts` and on Windows `C:\Windows\Fonts`

Following the same paradigm we used to initialize our SDL video module, we have to initialize our ttf library. The same pattern is used, where we check to see if the return value of the [TTF_Init](https://wiki.libsdl.org/SDL2_ttf/TTF_Init) function is less than zero, and if it is, we display an error to the user

 ```
    // Initialize SDL_ttf
    if (TTF_Init() < 0) {
        printf("SDL_ttf could not initialize! TTF_Error: %s\n", TTF_GetError());
        SDL_Quit();
        return EXIT_FAILURE;
    }
```
If the ttf library was installed and linked correctly then we can attempt to load a font using [TTF_OpenFont](https://wiki.libsdl.org/SDL2_ttf/TTF_OpenFont). Again we want to check that the font was actually loaded and we do that with our if statement

```
  TTF_Font *font = TTF_OpenFont(MY_FONT, 64); // specify the path to your font file and font size

  if (!font){
    printf("Failed to load font: %s\n", TTF_GetError());
    return EXIT_FAILURE;
  }
```
Now that we've loaded our font we need to create a text surface using [TTF_RenderText_Solid](https://wiki.libsdl.org/SDL2_ttf/TTF_RenderText_Solid) This surface is essentially an image containing the rendered text. Since this is an operation that can fail, we want to make sure that we check this using an if statement like we've done before

```
 // Create surface with rendered text
  SDL_Color textColor = {0, 0, 0, 255}; // black color
  SDL_Surface *textSurface = TTF_RenderText_Solid(font, "Hello World!", textColor);

  if (!textSurface) {
    printf("Failed to create text surface: %s\n", TTF_GetError());
    return EXIT_FAILURE;
  }
```
The next step uses [SDL_CreateTextureFromSurface](https://wiki.libsdl.org/SDL2/SDL_CreateTextureFromSurface) to convert our `SDL_Surface` (your rendered text) into an `SDL_Texture`. This is necessary because modern SDL2 uses the GPU for rendering, and textures are the GPU-friendly way of handling images. This conversion process uploads the surface data to the GPU memory, enabling efficient rendering. This operation can also fail, so it is important to check that it doesn't before doing anything with the texture

```
  // Create texture from surface
  SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);

  if (!textTexture){
    printf("Failed to create text texture: %s\n", SDL_GetError());
    return EXIT_FAILURE;
  }
```

Finally, we call [SDL_RenderCopy](https://wiki.libsdl.org/SDL2/SDL_RenderCopy). This function call tells SDL2 to copy the text texture to the renderer. The `SDL_Rect` `textRect` that is defined is used to position and size the text on the screen.

It is important to understand that nothing will be drawn on the screen yet. It's more like preparing what needs to be drawn. The actual drawing happens when you call [SDL_RenderPresent](https://wiki.libsdl.org/SDL2/SDL_RenderPresent)

```
  // Render text
  SDL_Rect textRect = {50, 50, textSurface->w, textSurface->h}; // rectangle where the text is drawn 
  SDL_RenderCopy(renderer, textTexture, NULL, &textRect);
```

If all goes well then when you run the code, you should see some text on the screen. With that, you are well on your way to learning the core components of using the SDL2 library. With just the information that you’ve learned from part 1 and part 2, you should be able to make a graphical version of the guess my number game, a common beginner programming task, but with a SDL flavored twist. You could even create a text adventure, if you were so inclined.

One final note. Between this and the last tutorial, you may be seeing a lot of repetitive code that might be better off abstracted away in functions. That’s good, and means you are getting familiar with the patterns in the library. Opinions about the structure of code should be based off the needs of the code base. While I’m keeping it simple for these tutorials, eventually we should abstract away some common patterns.

## FAQ
### Why Not Draw Directly to the Renderer?

SDL2 uses two main types of objects for drawing: `SDL_Surface` and `SDL_Texture`. `SDL_Surface` is a CPU-based bitmap, useful for manipulating pixel data directly. `SDL_Texture`, on the other hand, is GPU-based. Modern SDL2 applications use the GPU for rendering (via `SDL_Renderer`), which is faster and more efficient but requires textures, not surfaces.

The reason you can't directly render the font to the renderer is that `SDL2_ttf` is designed to work with surfaces, as historically SDL handled rendering with surfaces before textures and GPU rendering were introduced.

### Does that mean that SDL2 can render just the surface instead of the texture if I don't want to use the GPU?

Yes, SDL2 can render using just an `SDL_Surface` without converting it to an `SDL_Texture` if you prefer or need to render without using the GPU. This approach is known as "software rendering" and it's done entirely by the CPU, bypassing the GPU.

Software rendering with `SDL_Surface` was the primary method used in SDL before SDL2 introduced hardware-accelerated rendering with `SDL_Texture` and `SDL_Renderer`. While software rendering is less efficient and slower than hardware accelerated rendering, it can be useful in certain scenarios, like on systems with limited or no GPU capabilities. Instead of using a renderer like we are using you would instead create a `SDL_Surface` with [SDL_GetWindowSurface](https://wiki.libsdl.org/SDL2/SDL_GetWindowSurface)

```
SDL_Surface *windowSurface = SDL_GetWindowSurface(window);
```

## Call To Action 📣
Hi 👋 my name is Diego Crespo and I like to talk about technology, niche programming languages, and AI. I have a [Twitter](https://twitter.com/deusinmach) and a [Mastodon](https://mastodon.social/deck/@DiegoCrespo), if you’d like to follow me on other social media platforms. If you liked the article, consider checking out my [Substack](https://www.deusinmachina.net/). And if you haven’t why not check out another article of mine listed below! Thank you for reading and giving me a little of your valuable time. A.M.D.G