The Allegro Wiki is migrating to github at

Tutorial 2: TrueType Fonts and Display Modes

From Allegro Wiki
Revision as of 18:29, September 15, 2009 by Ib Quezada (talk | contribs) (New page: Abstract In this tutorial, we will learn how to set a display mode and how to load and display text using TrueType fonts. Data Files * [ hello1...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search


In this tutorial, we will learn how to set a display mode and how to load and display text using TrueType fonts.

Data Files

   * hello1.c: Source code
   * times.ttf: A font needed for this tutorial


This tutorial is intended to be read with the source code by your side. So be sure to download it and have it in front of you before you go any farther. Is it in front of you now? Good. What you see if one of the smallest programs you can write with Allegro 5. This might scare you at first, but it's mostly just in setting things up. Let's start with the files we include:

<highlightSyntax language="c"> #include <allegro5/allegro5.h> #include <allegro5/a5_font.h> #include <allegro5/a5_fft.h> #include <stdio.h> </highlightSyntax>

These include the standard Allegro 5 definitions, and definitions for both the general font routines as well as TrueType loading routines, respectively. Allegro 5 uses a more modular approach than previous versions of the library, and routines to load graphics, audio, fonts, etc. are kept in their own separate libraries. This means that you will need to include a couple of header files depending on what you will be doing in any particular source file. In the end, this will actually speed up compilation time when compared to previous versions of Allegro, so it is a good thing.

Next we are going to declare a structure to hold on to our data. This is not necessary, but I am doing so because it helps keep things organized. In this example we will be allocating two fonts, one display, one event queue, and using one color. If you are unfamiliar with structs, note below how we use them.

<highlightSyntax language="c"> struct Data { ALLEGRO_FONT *f1, *f2; ALLEGRO_DISPLAY *display; ALLEGRO_EVENT_QUEUE *queue;

ALLEGRO_COLOR bright_green; } data; </highlightSyntax>

Next, I am defining a constant string for the font file we will be using.

<highlightSyntax language="c"> const char *font_file = "times.ttf"; </highlightSyntax>

In this example, we will use the freely distributable Times font.

The first thing we want to do is initialize Allegro 5 and the various modules we wish to use.

<highlightSyntax language="c"> al_init() al_font_init(); </highlightSyntax>

The first line is for initializing Allegro 5, and the second is for the font routines. If we do not call these functions before using Allegro 5, unexpected things could happen. Most likely your program will crash.

Before we are able to display graphics, we need something to display them on. This is where the next set of code comes into play.

<highlightSyntax language="c"> data.display = al_create_display(640, 480); if(!data.display) { printf("Error creating display.\n"); return 1; } </highlightSyntax>

Here we are creating a display 640 pixels wide and 480 pixels tall, and storing it in the pointer data.display. If a graphics mode cannot be set, then a null value is returned, which you must make sure to check for. If we have a null pointer, we inform the user and quit the program. The pointer is kept because it is possible to have more than one display, and we need to be able to keep track of each display.

To be able to quit the program from a keypress, we must install the keyboard handler as well. This is part of the standard Allegro 5 routines, so we didn't need to include a special header for it. In general, every game you make will probably involve the keyboard at some point in time.

<highlightSyntax language="c"> if(!al_install_keyboard()) { printf("Error installing keyboard.\n"); return 1; } </highlightSyntax>

This is similar to the way the display was initialized, except we do not keep a pointer to the keyboard since most computers can only handle one keyboard at a time. If the keyboard driver cannot be initialized, then zero is returned, and we quit the program.

Since the goal of this program is to display a message on the screen, we will now load a font to output text with.

<highlightSyntax language="c"> data.f1 = al_ttf_load_font(font_file, 48, 0); data.f2 = al_ttf_load_font(font_file, -48, 0); if(!data.f1 || !data.f2) { printf("Error loading \"%s\".\n", font_file); return 1; } </highlightSyntax>

Note that the function al_ttf_load_font takes three arguments: first the filename of the font to load. Next the size of the glyphs. If a positive number is passed, it is measured in units per EM. I am not 100% positive what this means, so if you can clarify this, please leave a comment at the bottom of the page. If a negative value is passed, it is the total height of the glyph in pixels. Note that this will include the letters that go below the normal line (such as 'g' or 'j' or 'q' or 'y') as well as some gap between lines. The final argument is for flags. Currently the only flag is ALLEGRO_TTF_NO_KERNING, which will turn off kerning. In general, you want kerning, so a value of 0 does not use this option.

Before drawing can be performed, a color must be set. I have chosen bright green, since it reminds me of the first computer I used, and it's real easy to make:

<highlightSyntax language="c"> data.bright_green = al_map_rgba_f(0.5, 1.0, 0.5, 1.0); </highlightSyntax>

Colors are stored in ALLEGRO_COLOR structures, and this is one way they can be created. This function takes four arguments for the red, green, blue, and alpha components respectively. Arguments are floating-point numbers, with 0.0 representing the smallest quantity and 1.0 representing the largest. In this case, I used half red and blue, and full green and alpha.

This is not the only way to create colors, but it is my opinion to be the easiest. We could just as well have used any of the following lines, all of which have the same effect:

<highlightSyntax language="c"> data.bright_green = al_map_rgb_f(0.5, 1.0, 0.5); data.bright_green = al_map_rgba(127, 255, 127, 255); data.bright_green = al_map_rgb(127, 255, 127); </highlightSyntax>

The functions which do not end in "_f" take unsigned chars as parameters, with 0 as the smallest color component and 255 as the largest color component. The functions which do not take alpha values provide a maximum alpha channel.

Having created a color, the next step is to actually use it. This is done by assigning it in the blender. The color will be applied to all future drawing commands.

<highlightSyntax language="c"> al_set_blender(ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA, bright_green); </highlightSyntax>

The third argument is the easiest to understand: this is the color to apply. The first two arguments specify how alpha channels are treated. They are discussed in more detail in the Blending and Display Features tutorial. When you are not playing with visual effects, you probably will always want to use the values above.

Next we output text to the backbuffer.

<highlightSyntax language="c"> al_font_textout(data.f1, 10, 10, "Allegro 5 Rocks!", -1); al_font_textout(data.f2, 10, 60, "Allegro 5 Rocks!", -1); </highlightSyntax>

The first argument specifies which font to use, and the second two are the x and y coordinates, respectively. In Allegro 5, the origin is in the upper-left corner of the screen, with x increasing to the right and y increasing down. Thus the coordinates (10, 10) will be a ten pixels offset from the upper-left corner of the screen. The fourth parameter is the string to output, followed finally by the number of characters to print.

This last variable is interesting, and might not seem entirely useful at first. In this case, we have passed -1, which tells Allegro 5 to output the entire string. Had we set a different value, for example 2, it would only output the first two characters; just "Al" would be printed. This is useful if in your game, you want text to slowly appear letter-by-letter. Instead of manupulating your strings to get it to work out, you can just increment this parameter.

We are outputting the same string twice so that we can see the difference in defining the font sizes. However, before this text is visible, we must flip the screen and the backbuffer.

<highlightSyntax language="c"> al_flip_display(); </highlightSyntax>

This function does just that. In your games, you will most likely have time split up into frames such that time progresses a little bit between each frame. You will want to call this function whenever you are finished drawing an entire frame.

Now that the text is visible, we want to keep the program alive until the user presses the Esc key. To check for input, an event queue must first be created and the keyboard associated with it.

<highlightSyntax language="c"> data.queue = al_create_event_queue(); al_register_event_source(data.queue, (ALLEGRO_EVENT_SOURCE*)al_get_keyboard()); </highlightSyntax>

First an event queue is created. Next the keyboard is assigned to the queue. The first argument of al_register_event_source says that data.queue is the target of the source, and the second argument, al_get_keyboard(), passes a pointer to the keyboard event source. (Note that the (ALLEGRO_EVENT_SOURCE*) cast is required in this case.) This way all keyboard events are processed by data.queue. If you wanted it, another event queue could be created which would also process keyboard events; event sources can report to any number of queues, and queues can get reports from any number of event sources.

Finally, we want to wait until the user presses Esc.

<highlightSyntax language="c"> ALLEGRO_EVENT event; while(1) { al_wait_for_event(data.queue, &event); if(event.type == ALLEGRO_EVENT_KEY_DOWN && event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) break; } return 0; </highlightSyntax>

First, an ALLEGRO_EVENT object is created to store the event data, then we enter an endless loop. al_wait_for_event will halt the program until an event enters the queue. When it does, it is stored in event. Then we check the event. Was a key pressed down? Was it the escape key? Then break out of the loop. Otherwise, continue. This keeps running the loop until the user presses the correct key. We return zero to let the operating system know that everything went ok.

After the closing brace of the main function, we must always follow it with

<highlightSyntax language="c"> END_OF_MAIN() </highlightSyntax>

This is to maintain consistency with certain operating systems which do not start in the main function. When you are compiling your program, Allegro's headers will check which operating system is being compiled for, and will automatically create the proper main function if it is necessary.

To compile this example, you will need to link with the Allegro 5 library as well as the a5_ttf library. Take note, because this detail will not be included in future tutorials. (Thanks to Trent Gamblin and Chris Johnston for making my compiler lines more cross-platform.) Use the following commands:

Linux users:

$ gcc hello1.c -o hello1 -la5_ttf-4.9.8 -la5_font-4.9.8 \ `allegro5-config --libs`

Windows users:

> gcc hello1.c -o hello1 -la5_ttf-4.9.8 -la5_font-4.9.8 \ -lallegro-4.9.8

(Replace "-4.9.8" with your version of the library. As of the writing of this article, that was the most recent version, and it is now required of the add-on libraries. Presumably, when the API is stable the version number will be dropped from the library names.)

All other users: My apologies if you use MacOSX, Microsoft Visual Studio, or any other compiler or operating system. I cannot help you with the precise syntax for compilation or configuration.