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

Tutorial 6: Blending and Display Features

From Allegro Wiki
Jump to: navigation, search
Outdated: Doesn't match current api (edit)

Abstract

This tutorial will guide you through the basics of blending in Allegro 5. It will also touch on two smaller, but certainly worthwhile features: resizing a display and setting the title for a display.

Data Files

   * hello5.c: Source code
   * stw-a5.zip: Additional source files
   * hello.bmp: A bitmap needed for this example

Tutorial

As always, make sure that you have the source code in front of you while reading it. Once you are ready, proceed.

This tutorial is mostly just source code, as blending is a very simple topic. This example program will play around and do probably more than it needs to do to demonstrate how blending works. The first thing you may have noticed about this tutorial is that I have included a zip file with extra source code. What gives? This is the keyboard and mouse code from the previous tutorials. Rather than clutter up the main source file with it, in this and all future tutorials, we will simply include them from their own files. In this case, we have the two new headers to include:

<highlightSyntax language="c"> #include "stw_mouse.h" #include "stw_keyboard.h" </highlightSyntax> Again, these just declare the mouse and keyboard stuff we covered in previous tutorials. Next up is a bunch of definitions for colors. Yuck.

<highlightSyntax language="c"> #define COLOR_BLACK 0 #define COLOR_WHITE 1 #define COLOR_RED 2 #define COLOR_ORANGE 3 #define COLOR_YELLOW 4 #define COLOR_GREEN 5 #define COLOR_BLUE 6 #define COLOR_PURPLE 7 #define NUM_COLORS 8 ALLEGRO_COLOR color[NUM_COLORS];

int current_color; float alpha=1.0; </highlightSyntax> We will be shading our bitmap all sorts of different colors in this tutorial, and cycling through the colors by clicking the mouse. The translucency of the bitmap will be controlled with the Up and Down arrows on the keyboard, so we'll also need to keep track of the alpha value.

We're also going to have some other bitmaps (which I will refer to as the icons for some strange reason) circling around the screen, taking on various shades based on their position. We can change the number by modifying the definition below: <highlightSyntax language="c"> #define NUM_ICONS 5 float icon_angle=0.0; </highlightSyntax> Next we have some things defined just in case your compiler is particularly boneheaded and didn't do it for you: <highlightSyntax language="c"> #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif

#ifndef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif

#ifndef MAX3 #define MAX3(a,b,c) (MAX(a,MAX(b,c))) #endif

#ifndef M_PI #define M_PI 3.141592654 #endif </highlightSyntax> MAX3 is my own macro, it makes it easier to find the largest of three elements. Our keycheck function does just what we said would happen earlier: Increases the alpha channel with the up arrow, and decreases it with the down arrow: <highlightSyntax language="c"> void keycheck(float dt) { if(key[ALLEGRO_KEY_UP]) { alpha += dt; if(alpha > 1.0) alpha = 1.0; } if(key[ALLEGRO_KEY_DOWN]) { alpha -= dt; if(alpha < 0.0) alpha = 0.0; } } </highlightSyntax> You'll note that it will take one second to move from full alpha to no alpha, because dt is the number of seconds elapsed since the last frame. Also, we take care to clamp the value between 0.0 and 1.0.

The mousecheck function as well does exactly as we said would happen with mouse clicks: cycling through the colors. Here, too, we take care to clamp the value, this time between COLOR_WHITE and the maximum color. <highlightSyntax language="c"> void mousecheck(float dt) { if(mouse_b & MOUSE_R_NEW && ++current_color >= NUM_COLORS) current_color = COLOR_WHITE;

if(mouse_b & MOUSE_L_NEW && --current_color < COLOR_WHITE) current_color = NUM_COLORS-1; } </highlightSyntax> The update function will make sure that the icons rotate at 30 degrees per second. This may look a little confusing to you at first. If so, you probably need to review high school trigonometry. libc's trig functions take arguments in radians, so that is the unit we use to represent angles in this tutorial. (You can check that this is indeed what is going on by using the formula 2pi radians = 360 degrees.) <highlightSyntax language="c"> void update(float dt) { icon_angle += M_PI/6.0*dt; } </highlightSyntax> The drawing function is probably the scariest portion of this program. Let's first look at the part where we draw the icons which circle the screen. We will loop through for as many icons as we have to draw. First, the distance rhofrom the center of the screen is computed as half the distance to the nearest edge of the display. Then we use this combined with the angle and index of the icon to determine where it gets placed on the display: <highlightSyntax language="c"> int i; float sw = al_get_display_width(); float sh = al_get_display_height(); float sw6 = sw/6;

for(i=0; i<NUM_ICONS; i++) { float rho = MIN(sw, sh)/2.0; float x = sw/2 + rho * cos(icon_angle + i*2*M_PI/NUM_ICONS); float y = sh/2 + rho * sin(icon_angle + i*2*M_PI/NUM_ICONS); </highlightSyntax> Next we split up the screen into six regions horizontally to ramp between all possible hues. It takes a lot of code, but really it's just finding which region the icon is in horizontally, and applying the proper ramp. I will not go into detail as to what this means. I suggest you look at a color picker for a graphics program to get the feeling for what is going on. <highlightSyntax language="c"> float r, g, b; if(x < sw6) { r = 1.0; g = 0.0; b = x / sw6; } else if(x < sw/3) { r = 1.0 - (x - sw6) / sw6; g = 0.0; b = 1.0; } else if(x < sw/2) { r = 0.0; g = (x - sw/3) / sw6; b = 1.0; } else if(x < 2*sw/3) { r = 0.0; g = 1.0; b = 1.0 - (x - sw/2) / sw6; } else if(x < 5*sw6) { r = (x - 2*sw/3) / sw6; g = 1.0; b = 0.0; } else { r = 1.0; g = 1.0 - (x - 5*sw6) / sw6; b = 0.0; } </highlightSyntax> Now we will compute the value (brightness) based on the y coordinate of the icon. <highlightSyntax language="c"> float val = y / sh; r *= 1-val; g *= 1-val; b *= 1-val; </highlightSyntax> Finally, we set the blender and associate a color with it. In this case, it is the color we computed in all the code above. <highlightSyntax language="c"> al_set_blender(ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA, al_map_rgb_f(r,g,b)); al_draw_bitmap(bmp, x - al_get_bitmap_width(bmp)/2, y - al_get_bitmap_height(bmp)/2, 0); } </highlightSyntax> Let's look at the al_set_blender function. We looked at it in the tutorial TrueType Fonts and Display Modes, but did not go into detail on what it meant. The first parameter is operation to use for the source image. In this case and all previous cases, we have used the constant ALLEGRO_ALPHA. This means apply the alpha channel from the source image without modifications. The second argument is the operation for the destination. Again, we've always used ALLEGRO_INVERSE_ALPHA. This will apply the opposite of the alpha from the destination bitmap. (That is, using the floating-point system, 1.0 - alpha is the value which would be used.) This ensures a smooth, natural blending occurs.

There are two other parameters we can pass for these arguments: <highlightSyntax language="c"> ALLEGRO_ONE ALLEGRO_ZERO </highlightSyntax> ALLEGRO_ONE will treat the alpha as though it were 1.0 even if this is not the case. Similarly, ALLEGRO_ZERO will treat the alpha as though it were 0.0. These can be used to produce different types of blending. For example, additive blending can be performed by setting both source and destination parameters to ALLEGRO_ONE.

The last parameter for al_set_blender is the color to tint it at. In previous versions of Allegro, tinting and using alpha could not be performed in one step as we can here. Setting the color to anything other than white will tint all future drawing operations with that color.

Finally for the drawing function, we apply the alpha channel to the bitmap drawn at the mouse position, and draw it: <highlightSyntax language="c"> float r, g, b; al_unmap_rgb_f(color[current_color], &r, &g, &b); al_set_blender(ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA, al_map_rgba_f(r, g, b, alpha)); al_draw_bitmap(bmp, mouse_x-al_get_bitmap_width(bmp)/2, mouse_y-al_get_bitmap_height(bmp)/2, 0); </highlightSyntax> We first "unwrap" the color currently selected (recall that it is selected by clicking the mouse buttons) into it's red, green, and blue components. We ignore the alpha since we will be replacing that with the alpha value set by using the arrow keys. We wrap it all back up when setting the blender, and finally draw the bitmap at the mouse's coordinates.

We're almost done! Let's see what's new in the main function. This is where the "and Display Features" part of the title comes in to play. <highlightSyntax language="c"> al_set_new_display_flags(ALLEGRO_RESIZABLE); display = al_create_display(800, 600); al_set_window_title("Allegro 5 Tutorial 5: Blending and Display Features"); </highlightSyntax> The first line here tells Allegro 5 that we want a resizable display. How interesting, this was not possible in previous versions of Allegro. Also, I've taken the liberty to create a display a little bit larger than in the previous tutorials. If you don't like it, you can resize it after the program starts. The last line here sets a title for the display. Didn't you get tired of all the previous examples having no title? I found it to be somewhat annoying. If you are like you, be annoyed no more! We now know how to set the title of our Allegro 5 programs.

We create some colors which we used in the draw function. This is not particularly novel, and what is happening here should be pretty obvious. Note that I have specified the alpha value. This is completely unnecessary. <highlightSyntax language="c"> color[COLOR_BLACK] = al_map_rgba_f(0.0, 0.0, 0.0, 1.0); color[COLOR_WHITE] = al_map_rgba_f(1.0, 1.0, 1.0, 1.0); color[COLOR_RED] = al_map_rgba_f(1.0, 0.0, 0.0, 1.0); color[COLOR_ORANGE]= al_map_rgba_f(1.0, 0.5, 0.0, 1.0); color[COLOR_YELLOW]= al_map_rgba_f(1.0, 1.0, 0.0, 1.0); color[COLOR_GREEN] = al_map_rgba_f(0.0, 1.0, 0.0, 1.0); color[COLOR_BLUE] = al_map_rgba_f(0.0, 0.0, 1.0, 1.0); color[COLOR_PURPLE]= al_map_rgba_f(1.0, 0.0, 1.0, 1.0); current_color = COLOR_WHITE; </highlightSyntax> To have a functioning resizable display, we must register the display with our event queue so that we know when a resize event is fired:

al_register_event_source(queue, (ALLEGRO_EVENT_SOURCE*)display);

And finally, we must process resize events. <highlightSyntax language="c"> case ALLEGRO_EVENT_DISPLAY_RESIZE: al_acknowledge_resize(event.display.source); break; </highlightSyntax> Allegro 5 has made resizing a cakewalk. To process a resize event, simply acknowledge it. You could also do other things here, such as outputting what the new display dimensions are.

That wraps up this tutorial on blending and display features. Since I added in some extra files, be sure to include them on your compiler line.

Linux users:

$ gcc hello5.c stw_keyboard.c stw_mouse.c \ -o hello5 -la5_iio-4.9.8 `allegro5-config --libs`

Windows users:

> gcc hello5.c stw_keyboard.c stw_mouse.c \ -o hello5.exe -la5_iio-4.9.8 -lallegro-4.9.8

Remember to change the version on the library with whichever the most recent version is!