Saturday, August 18, 2012

Lesson 6: True Type Fonts with SDL_ttf

Deprecation Notice:

I've been updating the lessons now that SDL2 is officially released, please visit my new site on Github that has updated lessons and improved code.


In this lesson we will start learning how to use the SDL_ttf library to render text using True Type fonts. For this lesson you'll need the SDL_ttf library which you should have from when you downloaded and compiled all the libraries on SDL2.0 Mercurial. If you don't have the SDL2.0 version of SDL_ttf, you should download and compile it now. The library is linked up the same as SDL_image was in Lesson 3.

In addition to the library we'll need a true type font to use to draw our text. It's important that you're careful when choosing a font as many fonts are proprietary or have rules about using them in software, and if used without the proper licensing can result in a nasty legal mess. Fortunately for us there are many excellent open fonts available online. For this lesson we'll be using a set of open source fonts recently released by Adobe which you can download directly from sourceforge, the specific font I chose for this lesson is SourceSansPro-Regular, but you can use any font you like.

 Now that we've got our font downloaded and libraries linked up lets take a look at how we can work with fonts via SDL_ttf. The library provides functionality for opening and closing TTF fonts and rendering text to SDL_Surface pointers. The library also lets us specify the color and font size we'd like to use when rendering the text. You can find the full library documentation here.

First we'll need to add SDL_ttf.h to our includes:
In order to use the library we need to initialize it in our main function after initializing SDL. Note that we check for errors similar to how we check for issues when starting SDL, except that now to get the appropriate error information we use TTF_GetError, similar to what we did in Lesson 3 to get SDL_image error information.
Next we'll want to make a function to render a message and abstract away the work that goes into it, and simply return an SDL_Texture that we can draw to the screen. In addition to rendering the message the function will also need to convert the SDL_Surface pointer returned from TTF_RenderText_X to a SDL_Texture pointer and return that.

The information needed to render the message is the message itself, a font file name, a font size and a font color. We can now add a nice function to render our desired message to our code:
That's quite a bit of code! Lets step through it and figure out exactly what's going on. First, we know that we need a message, font file, font color and font size so that we can properly draw our message. SDL provides the SDL_Color structure that is used for specifying what color we want and takes three values for RGB which can each range from 0-255.

The first step in drawing the text is to open our font, which we do on line 4 using TTF_OpenFont, which takes the font file and the font size to open the font as. Note that the same nullptr error checking that we've used before in LoadImage is used to make sure the font opened ok. If it fails we throw an error and include the font file name that caused the error along with TTF_GetError so that we can have enough information to diagnose the issue.

If the font loaded ok we use TTF_RenderText_Blended to write the message to an SDL_Surface and get the pointer back. There are a few methods provided by SDL_ttf for rendering text, blended is the slowest but provides a nice smooth look. The other methods can be found in the documentation linked above.

However because we use SDL_Textures in our rendering we need to convert the surface over. This isn't too difficult because we've made our SDL_Renderer a global and so it's easy to pass it into the function SDL_CreateTextureFromSurface. After creating the SDL_Texture we need to clean up things we no longer need to keep in memory, the SDL_Surface and the TTF_Font. We free the surface the same as before and can use TTF_CloseFont to close the font we opened.

Once everything is cleaned up we return the SDL_Texture pointer for usage in the program.

When we want to render some text in our program we can now perform a call like so:
Here we render the message "TTF fonts are cool!" using the font we downloaded above in white with a font size of 64.

You can then treat the texture as any other texture and draw it the same as we've done previously with our ApplySurface function. If you draw the message centered, you should see something like this when you run the program:
Try playing around with the other TTF_RenderText_X functions and see how they look!

End of Lesson 6

Thanks for joining me! I'll see you again soon in Lesson 7: Taking Advantage of Classes.

8 comments:

  1. Hey Twinklebear, I love this tutorial and I'm trying to do pretty much what you did but using a class Font and a class Text... However when I try to compile, the program crashes at the line where I call SDL_CreateTextureFromSurface.
    The code is:

    SDL_Surface *temp = font->renderText(string, colour);
    if(temp)
    {
    message = SDL_CreateTextureFromSurface(this->renderer, temp);
    SDL_FreeSurface(temp);
    }

    And renderText is:
    SDL_Surface *result = TTF_RenderText_Blended(font, text.c_str(), colour);
    if(result == NULL)
    {
    // Error handling
    throw std::runtime_error(std::string("Failed to render blended text: ") + text);
    }
    return result;

    So I really don't understand what's happening...

    Thanks for your help :)

    ReplyDelete
    Replies
    1. Hmm I'm not sure, maybe make sure to initialize result and temp to NULL first? I realize there isn't much proper error checking in my code either heh. Have you tried running the exact code from the lesson?

      Perhaps printing out TTF_GetError (http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_12.html#SEC12) or SDL_GetError (http://wiki.libsdl.org/moin.fcg/SDL_GetError) could help some too.

      Delete
  2. SDL2_ttf release candidate version is available but not linked from the main SDL site: http://www.libsdl.org/tmp/SDL_ttf/

    Also, I wouldn't think you would want to use RenderText() as-is in production code, as loading a font every time you want to render text is probably not very efficient.

    For more realistic use cases, is there some way to streamline the string -> surface -> texture -> renderer path? The SDL2 wiki (http://wiki.libsdl.org/moin.fcg/FrontPage) doesn't have documentation for SDL2_ttf (yet?).

    ReplyDelete
    Replies
    1. Yea, I've got to get some time to update the links to the SDL download pages.

      RenderText is definitely not how you'd want to use it in reality, it's just to show SDL_ttf is used. A resource management system to check/manage loaded fonts would be much more efficient than reloading and freeing each time.

      The various extension libraries docs are located on their own project pages on the SDL site, SDL_ttf is here: http://www.libsdl.org/projects/SDL_ttf/

      From a quick glance at the docs I'm not sure if it provides a direct render to texture function, it looks like they all still return SDL_Surfaces.

      Delete
  3. Hi,

    I have a list of UTF8 chars that I will use, now I want to load them to some texture (used as a buffer) and later on just copy selected glyphs to the screen with selected color - how do I do that - is this possible without operations on "pixels" to change the color of glyph ?

    ReplyDelete
    Replies
    1. It's not something I've done before, but what I would try to do is render all the glyphs in white to a surface with a string with all the characters you want, and then I guess you'd also need to render each character independently to find each glyph's width and height. Then you could use the information about each glyphs w/h and the order of the glyph string you rendered to find the clip locations of each glyph.

      If that doesn't work you may need to render each one independently then compute the total width and height needed to contain them all, create a surface to hold them and then blit them to the surface. But hopefully rendering them all at once would be ok.

      From there you'd create a texture from the surface and select characters with your clips that you've built up. It'd probably be fastest to save this information once you build it (ie. the image and the clip information).

      When you want to change the color/alpha/blend modes of the glyph texture you can use the corresponding SDL_SetTextureXMod, documentation for these functions:

      http://wiki.libsdl.org/SDL_SetTextureColorMod?highlight=%28\bCategoryRender\b%29|%28CategoryEnum%29|%28CategoryStruct%29

      http://wiki.libsdl.org/SDL_SetTextureBlendMode?highlight=%28\bCategoryRender\b%29|%28CategoryEnum%29|%28CategoryStruct%29

      http://wiki.libsdl.org/SDL_SetTextureAlphaMod?highlight=%28\bCategoryRender\b%29|%28CategoryEnum%29|%28CategoryStruct%29

      This sounds kind of interesting, I may have to try playing around with it some myself today hehe. Let me know how it goes!

      Delete
  4. Great example. You make SDL seem almost as usable as Monogame. I'm just having one issue, my code compiles, and throws an exception when trying to open the truetype font. I've tried copying the ttf file from the project directory to the debug folder with no luck. Is there any reason SDL woudln't be able to open a ttf file sitting right in the project?

    ReplyDelete
    Replies
    1. Perhaps a naming or file permissions issue? There should be some error message given that will tell you what happened.

      Also, check out the updated version of this lesson it may also be helpful: http://www.willusher.io/sdl2%20tutorials/2013/12/18/lesson-6-true-type-fonts-with-sdl_ttf/

      Delete