The Allegro Wiki is migrating to github at

Tutorial 2: TrueType Fonts and Display Modes

From Allegro Wiki
Revision as of 15:47, June 20, 2010 by Mark Oates (talk | contribs) (fixed a past/present tense error.)
Jump to: navigation, search
Outdated: Tutorial was edited, but code does not match 4.9 api (edit)


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/allegro_font.h> #include <allegro5/allegro_ttf.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_init_font_addon(); </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_load_ttf_font(font_file, 48, 0); data.f2 = al_load_ttf_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_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA, bright_green); </highlightSyntax>

The fourth argument is the easiest to understand: this is the color to apply. The first three 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_draw_text(data.f1, 10, 10, ALLEGRO_ALIGN_LEFT, "Allegro 5 Rocks!"); al_draw_text(data.f2, 10, 60, ALLEGRO_ALIGN_LEFT, "Allegro 5 Rocks!"); </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 allows you to pass flags telling allegro how to render the text. In this case, we're using the flag ALLEGRO_ALIGN_LEFT, which will left-align the text to the x and y coordinates. By default, allegro will align text to the left (you could pass NULL instead of any flags and it would default to left-align). The last and final argument is the string to output.

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_event_source()); </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.

That's it!

In some older versions of this tutorial you may have seen END_OF_MAIN() following the end of the main function. This used to be required to maintain consistency with certain operating systems which do not start in the main function. When you compiled your program, Allegro's headers would check which operating system is being compiled for, and would automatically create the proper main function if it is necessary. However, this is no longer necessary.

Compiling the Program

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 -allegro_ttf-4.9.8 -allegro_font-4.9.8 \ `allegro-config --libs`

Windows users:

> gcc hello1.c -o hello1 -allegro_ttf-4.9.8 -allegro_font-4.9.8 \ -allegro-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.)

Linking statically on Mac OS X

I'm leaving this note separate from the main tutorial because it might not be the best way to accomplish building, but as of allegro 4.9.19 I can't get cmake to generate allegro-config.

If you want to build the above example on Mac OS X from the command line using static libraries, the following command will do it fo you:

> gcc hello1.c -o hello1 -lallegro-static -lallegro_font-static -lallegro_ttf-static -lallegro_main-static -lallegro_image-static -framework AppKit -framework OpenGL /usr/lib/libIOKit.dylib -framework AGL `freetype-config --libs` -ljpeg -lpng -lz