Chapter 7 – Texts, Fonts, Surface conversion

In the last chapters you have heard a lot about the creation of textures from image files and how to manipulate and draw to them. But there is nearly no application out there where you go out without some text messages. Of course, you could print some text to an image and load this image to your application to introduce text to it. But what if you like to implement a chat feature to your application where text messages are created dynamically? Or you like the user to insert his name for a highscore list? There are thousands of other circumstances where you need dynamically generated texts. Thus, this chapter will introduce you on how to load fonts and write to the screen dynamically. The ability to do this is not implemented in the native SDL 2.0 library itself but the SDL project provides an official extension called SDL2_ttf to work with TrueType fonts based on the FreeType project and their FreeType 2.0 release.

What’s a font?

A font represents the style how the letters of a word or sentence appear (e.g. Arial, New Times Roman, and so on). These style informations are saved in so-called font files. There are different standards how to save the font into a file, but the most important font file standard is the TrueType Font standard, thus the name SDL2_ttf. SDL2_ttf is capable to work with TrueType fonts. The typical file extension of TrueType font files is “.ttf”. FreeType 2.0 is no font file standard but a software library to load TrueType font files.

As for the work with SDL2_image you will need to add some .dll files to your Windows system to use fonts since SDL2_ttf isn’t part of native SDL 2.0.

Software
Version
File name
Link
Description
SDL2_ttf dynamic link library
2.0.12
SDL2_ttf-2.0.12-win32-x86.zip (32-bit Windows)
SDL2_ttf-2.0.12-win32-x64.zip (64-bit Windows)
https://www.libsdl.org/projects/SDL_ttf/
This is the corresponding dynamic link library file for the unit for Windows. Note: Mac OS X and Linux versions are also available.

Download the corresponding SDL2_ttf package depending on your system (32 bit or 64 bit) and extract the zip file. You will end up with a SDL2_ttf.dll and two more dlls (zlib1.dll, libfreetype-6.dll) which are necessary for support of compression and FreeType routines. Copy all these files to your system folder, e.g. for Windows XP or Windows 8.1 32 bit C:\WINDOWS\system32\. If you are not sure about your system folder, you should copy all these files into the same folder where the source code file (.pas or .pp) of your SDL 2.0 program is. If you are running on a different platform (e.g. Mac OS X, Linux, …), please check the link given in the table above for further information on SDL2_ttf installation.

The road for texts in SDL 2.0

Before jumping right into the code, let’s discuss the two concepts of SDL 2.0 of saving images in memory again. We prefer to work with textures because they have all the advantages we discussed in prior chapters, especially in Chapter 4. Anyway, in SDL2_ttf there is no such function for loading a text directly as a texture but there are several functions to load a text as a surface (details later). Hence we need to convert the surface into a texture in a second step. Look at the following diagram to understand the way we need to go:

SDL2 text creation diagram

 

So according to the diagram the way to go is as follows, if you have a text in mind (left quadrat in diagram) you would like to print to the screen, first create a SDL_Surface by using one of the functions SDL2_ttf provides (lower path to quad at the bottom in the diagram). Next you convert this SDL_Surface into a SDL_Texture by the function SDL_CreateTextureFromSurface (go straight from the buttom quad to the upper quad in the diagram). Finally render the text as you like to the screen, onto a 3d object or whatever you have in mind (go from upper quad to the right quad in diagram). Maybe this sounds bit of complicated, but as you will see, it is not.

Notice, in principle it is possible to directly “blit” the text containing surface to the screen (path from lower quad to the right quad in diagram) but to benefit from hardware acceleration you should not go this way.

Let’s jump into the code now .

The final result should behave like this: The text “Hello World!” appears for five seconds in italics and underlined. The text is red and the background is cyan. The following screenshot gives an impression what is to be expected.

Result screenshot for chapter 7

Let’s begin with the initial lines of code.

The program is called “Chapter7_SDL2” and we will need unit SDL2_ttf additional to SDL2 to have access to the TrueType font engine.

Then we are preparing for some new types of variables. First of all we have a pointer variable called “sdlSurface1” which is of type PSDL_Surface and points at a SDL_Surface. The text message we provide will be rendered into this SDL_Surface first.

Next we find a PTTF_Font variable called “ttfFont” which points at TTF_Font that holds the font’s data. The font’s data itself is usually stored in a font file.

Finally there are two color variables “sdlColor1” and “sdlColor” of pointer type PSDL_Color. These pointers point at records which contain color data as composition of a r, b and g value. The exact definition will be shown when they are used in the code.

Well, the next variables are known from previous chapters. Also the initialization of SDL 2.0 and the creation of the window and the renderer are known already. So we should proceed to the next code chunk now.

To inialize the TrueType font engine

TTF_Init(): Integer

without any arguments is called which returns 0 on success and -1 on error. There is no specific error code returned.

By

TTF_OpenFont(_file: PAnsiChar; ptsize: Integer): PTTF_Font

a font is prepared to be used. The first parameter “_file” asks for the absolute file path of the font. The parameter “ptsize” asks for an integer value which determines the size of the font. The larger the value, the larger the letters will appear finally (just as known from text editors). Anyway, if you choose too large size the largeste size will be chosen.

Styling the text in SDL 2.0

By procedures

TTF_SetFontStyle(font: PTTF_Font; style: Integer),

TTF_SetFontOutline(font: PTTF_Font; outline: Integer)

and

TTF_SetFontHinting(font: PTTF_Font; hinting: Integer)

further shaping of the appearence of the text can be performed. All the procedures need to know as first argument to which font they should be applied, so “ttfFont” in our case. Then the style can be set by the constants shown in the following table, which are kind of self-explanatory. By OR’ing the style constants you can even combine them as shown in the example code. The text shall be in italics and underlined.

TTF_STYLE_NORMAL No application of a certain style
TTF_STYLE_BOLD Set a bold style
TTF_STYLE_ITALIC Set letters in italics
TTF_STYLE_UNDERLINE Have the text underlined
TTF_STYLE_STRIKETHROUGH Have the text stroken through

The outline is simply set by a number. The larger the number, the larger the outline of the letters will appear. If you don’t need an outline, the argument has to be 0.

The hinting is set similar to the style by pre-defined constants shown in the following table. The hinting setting influences the appearance of the letters and text. In general it should lead to sharper letters, anyway, which setting is best for a certain situation and display device may differ. So if you are unsure what setting to use you should choose TTF_HINTING_NORMAL. If you don’t call this procedure this setting is the default anyway.

TTF_HINTING_NORMAL Normal hinting is applied
TTF_HINTING_LIGHT Light hinting is applied
TTF_HINTING_MONO I guess monospaced characters, so all the characters have the same space between each other
TTF_HINTING_NONE Have the text underlined

All of these three procedure have a counter-function which returns the set style, outline and hinting as an integer number, defined as

TTF_GetFontStyle(font: PTTF_Font): Integer,

TTF_GetFontOutline(font: PTTF_Font): Integer,

TTF_GetFontHinting(font: PTTF_Font): Integer.

Obviously the only parameter to be set is the font whose style, outline or hinting you like to know. These functions aren’t demonstrated in the sample though.

Colouring the text using SDL_Color

The next lines of code are wating for us.

Here we are starting to consider some coloring of the text. The first color set up in “sdlColor1” of kind PSDL_Color will be the color of the letters. The second color set up in “sdlColor2” will be the background color of the letters. Let’s have a look at the definition of PSDL_Color:

The record has four fields. For additive color mixing you often have a red share, a green share and a blue share (RGB triple). These three fundamental colors are capable of generating any other color by mixing in the respective proportions. E.g. 100% red and 0% green and 0% blue will lead to red. But 100% red, 100% green and 0% blue will lead to yellow. If all three colours are 100% you will get white. If all of them are 0% you will get black. You may notice the variable type UInt8 which means 8bit unsigned integer. From this follows that the values can range between 0 and 255 where 0 equals 0% and 255 equals 100% of the corresponding color. So 2563 = 16,777,216 individual colors can be created.

For setting up the colors we need to allocate some memory by the new command as shown. After that for each color (red, green and blue) an individual value is set by accessing the corresponding field. The color of the letters will be (R 255, G 0, B 0) red. For the background (R 0, G 255, B 255) cyan is set.

And finally a text with the chosen colors has to be created. There are several functions to do so and just one of them is chosen for demonstration how the creation generally works. The chosen function is

TTF_RenderText_Shaded(font: PTTF_Font; text: PAnsiChar; fg, bg: TSDL_Color): PSDL_Surface

It requires four arguments and returns a PSDL_Surface (no PSDL_Texture!), a pointer to a SDL_Surface. The first argument is the font you would like to use. Next is the actual text. The text can be directly written as shown in the example but also could be stored in a variable of type PAnsiChar.

PAnsiChar and Pascal Strings

Usually String variables are used in Pascal programming language, so why here we have a PAnsiChar? Well, since SDL is written in C/C++, and there typically null-terminated strings of characters are used to store and handle text, this is kept for better compatibility and easier translation of SDL 2.0 for Pascal compilers. In principle it is no big deal to use PAnsiChar instead of String, even though Pascal programmers prefer String variables.

Last but not least there are the two arguments “fg” and “bg” which translates to foreground and background. Here we insert the colors we defined before. That’s it.

Text quality and rendering speed

Okay, as mentioned before there are more functions to create a SDL_Surface with text. In general there are three groups of functions according to their properties. The general naming scheme is as follows:

TTF_Render[encoding]_[suffix]

Those which have the suffix “Solid” are very fast created but their quality is low. Use them when rendering fast changing text (e.g. the score of a pinball simulation). The second group has the suffix “Shaded”. They are slower rendered and have a background color but have much better quality. The last group of functions have the suffix “Blended”. They are of high quality but slow to be rendered. Use them for more static text which doesn’t change a lot.

For each group of quality and functions you find “Text”, “UTF8”, “UNICODE” and “Glyph” in the encoding part of the functions name right after “TTF_Render”. “Text”, “UTF8” and “UNICODE” are three different types of encoding of the text. “Text” corresponds to Latin1 encoding, “UTF8” to UTF-8 encoding and “UNICODE” to Unicode encoding. Which type to choose depends on what type of characters (e.g. Cyrillic letters, Latin characters, Chinese characters) you are going to use. If you are unsure which of these functions to use, go with the “UNICODE” version.

For rendering a single character by its Unicode code, use the function which contains “Glyph” as suffix to “TTF_Render”.

Finally I’d like to mention a special function which is just available in high quality “Blended” mode. It has the suffix “_Blended_Wrapped”. Additional to the usual parameters there is a parameter wrapLength of type UInt32 (32 bit unsigned integer). Here you can have an integer value which determines the amount of pixels until the text will be word-wrapped. So in our case with a width of the window of 500 pixels the setting of wrapLength to 250 for example would result in a word-wrap when a word would exceed 250 pixels.

The following table summarizes all the functions accompanied by the most important properties the resulting text surface will have.

Rendering mode Transparency Antialiasing Colour depth and format Quality Speed
Solid yes (colorkey, 0 pixel) no 8-bit palettized (RGB) low very fast
TTF_RenderText_Solid(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
TTF_RenderUTF8_Solid(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
TTF_RenderUNICODE_Solid(font: PTTF_Font; text: PUInt16; fg: TSDL_Color): PSDL_Surface
TTF_RenderGlyph_Solid(font: PTTF_Font; ch: UInt16; fg: TSDL_Color): PSDL_Surface
Shaded
no (0 pixel is background colour) yes 8-bit palettized (RGB) high fast
TTF_RenderText_Shaded(font: PTTF_Font; text: PAnsiChar; fg, bg: TSDL_Color): PSDL_Surface
TTF_RenderUTF8_Shaded(font: PTTF_Font; text: PAnsiChar; fg, bg: TSDL_Color): PSDL_Surface
TTF_RenderUNICODE_Shaded(font: PTTF_Font; text: PUInt16; fg, bg: TSDL_Color): PSDL_Surface
TTF_RenderGlyph_Shaded(font: PTTF_Font; ch: UInt16; fg, bg: TSDL_Color): PSDL_Surface
Blended yes (alpha channel) yes 32-bit unpalettized (RGBA) very high slow
TTF_RenderText_Blended(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
TTF_RenderUTF8_Blended(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
TTF_RenderUNICODE_Blended(font: PTTF_Font; text: UInt16; fg: TSDL_Color): PSDL_Surface
TTF_RenderText_Blended_Wrapped(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color; wrapLength: UInt32): PSDL_Surface
TTF_RenderGlyph_Blended(font: PTTF_Font; ch: UInt16; fg: TSDL_Color): PSDL_Surface

All the functions will return nil on failure to create a SDL_Surface.

Converting a SDL_Surface into a SDL_Texture

Okay, now let’s proceed to the next lines of code.

In the previous table you saw all the functions to generate SDL_Surfaces with a certain text. Now we need to transform it into a SDL_Texture. The function to do so is

SDL_CreateTextureFromSurface(renderer: PSDL_Renderer; surface: PSDL_Surface): PSDL_Texture

It is simple as that. You choose a renderer which is “sdlRenderer” for us and a SDL_Surface and this function returns a pointer to a newly created SDL_Texture. It will return nil on failure. By the way keep in mind that the SDL_Surface still exists even after creation of the SDL_Texture from it. So it has to be disposed, too.

Then the result will be rendered to the window. For simplicity the third and fourth argument for SDL_RenderCopy() are nil which means that the SDL_Texture with the text will be stretched to the window. The rendered result is shown for 5000 ms (5 seconds).

We can proceed to the cleaning process.

All the allocated memory has to be free’d now. In the case of the simple records “sdlColor1” and “sdlColor2” this is done by dispose. The font is free’d by procedure

TTF_CloseFont(font: PTTF_Font)

and the TrueType engine is quit by procedure

TTF_Quit.

“sdlSurface1” is free’d by procedure

SDL_FreeSurface(surface: PSDL_Surface)

and the texture, renderer and window as known. After that SDL 2.0 is shut down.

Have fun working with texts :-).

← Chapter 6 | Chapter 8, Part 1 →

Leave a Reply