The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

Tutorial 2: TrueType Fonts and Display Modes

From Allegro Wiki
Jump to: navigation, search

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.c: Source code
   * times.ttf: A font needed for this tutorial

Tutorial

Notice: This tutorial has been updated to match recent API, but the source code file has not! This page should take precedence.

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="cpp"> #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="cpp"> 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="cpp"> 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, and check that they came up correctly.


<highlightSyntax language="cpp"> if (!al_init()) {printf("Failed to initialize Allegro library.\n"); return 1} //if (!al_init_font_addon()) {printf("Failed to initialize Allegro fonts addon.\n"); return 1}

       al_init_font_addon();

</highlightSyntax>

Normally you would want to check that whatever you initialize actually came up, but al_init_font_addon returns void this version.

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="cpp"> 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="cpp"> 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="cpp"> 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="cpp"> 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="cpp"> 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 integers instead of floating point values 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.

Next we will set the color blending options. In this case we will use additive blending (ALLEGRO_ADD as opposed to ALLEGRO_DEST_MINUS_SRC or ALLEGRO_SRC_MINUS_DEST, which perform subtractive blending.) Previously the blender would take a color argument, but this has been moved elsewhere.

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

The 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. There are two default "buffers" onto which we can draw. The first is what you actually see at any given moment: The foreground buffer. Drawing to the foreground buffer is generally a bad idea because it can cause flicker and poor graphics in general. Instead we draw each frame to what is called the backbuffer and flip the display (al_flip_display();) to reveal what we just drew. The last frame is often cleared (al_clear_to_color(color);) and drawing begins again.

<highlightSyntax language="cpp"> al_draw_text(data.f1, data.bright_green, 10, 10, ALLEGRO_ALIGN_LEFT, "Allegro 5 Rocks!"); al_draw_text(data.f2, data.bright_green, 10, 60, ALLEGRO_ALIGN_LEFT, "Allegro 5 Rocks!"); </highlightSyntax>

The first argument specifies which font to use, followed by color and x/y coordinates. 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="cpp"> 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="cpp"> 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="cpp"> 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 dynamically in Visual C++ Express 2010

Linking in visual studio can be a little bit of a pain, especially in the 2010 version. A couple tips on linking before we start: 1. Visual studio's linker is going to look in a couple very specific places for the files. This can vary based on your installation and system, and not every guide takes outliers into consideration. You're going to want to get these variable filenames and copypasta it into a text file, or just paste it right into the properties sheet. If you still have the precompiled package you can get the filenames out of there. (in the lib folder) Otherwise you're going to want to open up your Visual C++ folder and go into the lib subdirectory. example: (C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib) Next you need to select the right library and copy its filename. (Rename, select, copy.) See each item below for criteria on selecting the correct lib file.

[General naming conventions] --Monolith means that all of the addons are included in one file, so you only have to put the name of the monolithic libfile in 'Additional Dependencies.' --Mt and md refer to the way the library links to the standard c library. mt version includes the libraries while md leaves them out, requiring you to manually link. --Static versus dynamic refers to the way the allegro library is distributed with your project. Static will include the dll in your executable, resulting in a larger file but doesn't require you to distribute the dll. Dynamic linking is dependent upon the dll in your build directory.

[Allegro Dynamic Link Library Debug File Name] In this case, you're probably going to want to select monolithic, mt, dynamic and debug. Currently this results in a filename as such: allegro-5.0.0-RC4-monolith-mt-debug.lib

[Allegro Dynamic Link Library File Name] Select monolithic, mt and dynamic. allegro-5.0.0-RC4-monolith-static-mt

Here are the steps to link your project against Allegro
  1. . Create a new Win32 project, empty. (File -> New -> Project -> Win32 Project) Once you choose a name and close the first wizard, VS should create your project and open up the second wizard.
  2. . Choose 'Windows Application' and check 'Empty Project' in the second wizard. Press finish.
  3. . In order to get the C/C++ tab in the property sheet window you need to have at least one C++ file. Go ahead and create main.cpp.
  4. . In the Project menu, select the properties for your project name. In Configuration dropdown select 'Debug'
  5. . Open up (Linker -> Input) and hit the little arrow aside 'Additional Dependencies.' Click 'Edit.'
  6. . Put [Allegro Dynamic Link Library Debug File Name] from above into the box and save. You don't have to follow it with a semicolon.
  7. . Check the (C/C++ -> Code Generation) tab to make sure that the Runtime Library is set to 'Multi-threaded Debug DLL.'
  8. . Repeat the above steps but instead choose 'Release' in the configuration dropdown, and use the [Allegro Dynamic Link Library File Name] and Multi-threaded DLL.
  9. . Now code up and press the green arrow to run and debug! (Or just build and go find your executable.)

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