L'affichage au sein d'Allegro

Un article de Mangue.org, l'encyclopéde libre.


Initialiser la librairie, c'est bien beau, mais c'est encore mieux de pouvoir afficher quelque chose à l'écran. C'est ce que va nous permettre ce cours.


Sommaire

Le système des BITMAPs

Dans Allegro, l'affichage est basé sur ce qu'on appelle des bitmaps. Qu'est-ce qu'un bitmap ? Un bitmap est une zone mémoire qui contient une image, un dessin, ou tout autre chose à afficher. En travaillant par analogie, nous pourrions symboliser l'écran par une feuille de papier, et les bitmaps par des morceaux de papier de la taille que l'on veut, sachant que la mémoire est un paquet de feuilles dans lequel nous tirons nos bouts de papier (les bitmaps). Donc pour afficher quelque chose sur la grande feuille, il suffit de le dessiner sur un bout de papier, puis de coller ce bout de papier sur la feuille. En pratique, pour afficher quelque chose avec Allegro, il suffit donc de le dessiner dans un bitmap, puis d'afficher le bitmap à l'écran.

//Déclaration d'un nouveau bitmap
BITMAP *nomdubitmap;

Il existe deux sortes de bitmaps, qui sont les screen bitmaps et les memory bitmaps. Le premier type désigne le bitmap (et ses sous-bitmaps) représentant l'écran. Celui-ci est déclaré ainsi dans la librairie Allegro :
BITMAP *screen;
Lorsque nous dessinons sur ce bitmap, c'est comme si nous dessinions sur l'écran. C'est pourquoi il existe des memory bitmaps. Ces bitmaps représentent tous les bitmaps créés par nous même, grâce aux fonctions create_bitmap et load_bitmap.


Créer et détruire des bitmaps

La création d'un bitmap commence avant tout par sa déclaration au début du fichier, comme vu précédemment. Une fois le bitmap déclaré, il ne reste plus qu'à le créer. Pour cela, nous avons deux possibilités. La première, c'est de créer un bitmap vierge de la taille voulue. Pour cela, nous utiliserons la fonction create_bitmap comme suit :

//Creation d'un bitmap d'une taille de 640 pixels sur 480 pixels
BITMAP *nomdubitmap;
 
nomdubitmap=create_bitmap(640,480);

La seconde solution est de charger une image. C'est d'ailleurs comme cela que l'on insére des images extérieures dans nos jeux Allegro. Pour cela nous utiliserons la fonction load_bitmap(), ou l'une de ses dérivées load_pcx(), load_tga(), load_bmp() ou load_lbm(). Regardons l'exemple suivant pour comprendre :

//Chargement d'une image
BITMAP *nomdubitmap;
PALETTE palette;
 
nomdubitmap=load_bitmap("image.pcx",palette)
Remarque
Nous verrons le concept de palette et ses utilisations un peu plus loin dans le cours.
Danger
Les fonctions de chargement d'images ne font pas toutes exactement le même travail :
- la fonction load_bitmap permet de charger des images bmp, pcx, tga ou lbm
- la fonction load_bmp permet de charger des images bmp utilisant 256 couleurs ou 24 bits
- la fonction load_lbm permet de charger des images lbm utilisant 256 couleurs
- la fonction load_pcx permet de charger des images pcx utilisant 256 couleurs ou 24 bits
- la fonction load_tga permet de charger des images tga utilisant 256 couleurs, 15 bits, 24 bits ou 32 bits

Après avoir créé des bitmaps, il est important de savoir les détruire. Pour cela, nous utiliserons la fonction destroy_bitmap qui s'utilise comme suit :

//Destruction d'un bitmap
BITMAP *nomdubitmap
nombubitmap=create_bitmap(10,10);
 
//...
 
destroy_bitmap(nomdubitmap);
Remarque
Pour effacer le contenu d'un bitmap, il faut utiliser la fonction clear avec comme argument le nom du bitmap, ou bien clear_to_color avec comme premier argument le bitmap, et second argument la couleur avec laquelle remplir le bitmap.


Afficher des bitmaps

Nos bitmaps étant maintenant créés, il ne reste plus qu'à les afficher à l'écran. Pour cela, nous allons utiliser le concept de double buffering. Le double buffering, c'est le fait de d'abord dessiner dans une zone de la mémoire que l'on va ensuite afficher à l'écran, plutôt que de directement dessiner à l'écran (souvenez-vous de l'analogie avec les feuilles de papier). Pour cela, nous allons créer un bitmap de la taille de l'écran, définie grâce à la fonction set_gfx_mode, puis nous allons afficher ce que nous avons à afficher dans ce bitmap, et finalement afficher ce bitmap à l'écran. Pour cela, nous utiliserons la fonction blit. Cette fonction permet d'afficher un bitmap à l'intérieur d'un autre. Ses paramètres sont :

  • le bitmap source que l'on veut afficher
  • le bitmap destination dans lequel on veut afficher le source
  • les coordonnées x et y du coin supérieur gauche du rectangle du bitmap source que l'on veut afficher dans le bitmap destination (en effet, nous ne sommes pas obligé d'afficher le bitmap source en entier dans le bitmap destination, nous pouvons choisir d'en afficher qu'une partie, nécessairement de la forme d'un rectangle)
  • les coordonnées x et y du coin supérieur gauche de la position où l'on veut afficher le bitmap source dans celui de destination
  • la largeur et la hauteur de la zone du bitmap source que nous voulons afficher

Voilà un bout de code démonstratif :

BITMAP *buffer;
BITMAP *image;
PALETTE palette;
 
buffer = create_bitmap(640, 480);
image = load_bitmap("image.tga", palette);
 
blit(image, buffer, 0, 0, 52, 52, image->w, image->h);
blit(buffer, screen, 0, 0, 0, 0, 640, 480);
Astuce
Pour avoir la largeur et la hauteur d'un bitmap, nous pouvons utiliser les variables nomdubitmap->w pour avoir sa largeur et nomdubitmap->h pour avoir sa hauteur.
Astuce
Si vous désirez afficher une image avec un zone transparente, remplissez cette zone de la couleur fushia (255, 0, 255) et utilisez la fonction masked_blit en lieu et place de la fonction blit, pour ainsi ne pas afficher les parties fushia de l'image.


Le blocage des bitmaps

Sous certaines plate formes (windows par exemple), tout accés à un bitmap par une fonction d'affichage (putpixel, etc.) se traduit par un blocage (lock) du dit bitmap. Cela représente une perte de performance considérable, puisque chaque accés au bitmap a pour conséquence un blocage (et donc un déblocage) du bitmap. C'est pourquoi il est conseillé d'utiliser la fonction acquire_bitmap(BITMAP*), qui a pour conséquence le blocage du bitmap passé en argument, avant d'effectuer des opérations sur le bitmap, et d'appeller la fonction release_bitmap(BITMAP*) qui débloque le bitmap. Voici un exemple d'utilisation de ces fonctions :

BITMAP *monbitmap = create_bitmap(640, 480);
int i;
 
...
 
acquire_bitmap(monbitmap);
for(i = 0;i < 640;i++)
  putpixel(monbitmap, i, 10, MAKECOL(12, 45, 96));
release_bitmap(monbitmap);
 
blit(monbitmap, screen, 0, 0, 0, 0, 640, 480);


Les sprites

Tout d'abord, définissons le terme de sprite. Un sprite est un bitmap, animé ou pas, et représentant un acteur (personnage, vaisseau, etc...), dans un jeu vidéo. Son utilisation est en voie de disparition, car remplacée par du mapping de textures sur des objets 3D. Mais cette technique reste tout de même utilisée dans les jeux en 2D. Donc en pratique, un sprite est un bitmap représentant un acteur du jeu. Allegro fournit des fonctions spéciales pour afficher, et surtout manier les sprites.

La toute première fonction utilisée est draw_sprite, qui affiche un sprite à l'écran. Cette fonction prend 4 arguments, qui sont le bitmap de destination, le bitmap source, et la position en x et en y. Il est à noter que cette fonction rend tranparent les pixels de couleur 0 en mode 256 couleurs et le fushia en mode truecolor.

Allegro possède des fonctions, au nombre de 3, permettant de faire effectuer des flips (mirroirs) aux sprites, et de les afficher à l'écran. Ces fonctions s'utilisent comme la fonction draw_sprite.
Ces fonctions sont draw_sprite_v_flip(), draw_sprite_h_flip() et draw_sprite_vh_flip(), ces fonctions effectuent respectivement un mirroir vertical, horizontal, et suivant les deux axes sur le sprite.

Autre que les fonctions de mirroir, Allegro nous propose des fonctions de rotation de sprite. Ces fonctions sont :

  • rotate_sprite : cette fonction affiche le sprite en lui faisant réaliser un angle, précisé en dernier paramètre de la fonction.
  • rotate_sprite_v_flip : en plus de faire effectuer un angle au sprite, cette fonction effectue un mirroir vertical au sprite. Pour effectuer un mirroir horizontal, ajoutez itofix(128) à l'angle donné. Pour faire un mirroir des deux axes, utilisez rotate_sprite et ajoutez itofix(128) à l'angle donné.
  • rotate_scaled_sprite : cette fonction applique une échelle (agrandit le sprite) passée en dernier argument au sprite en plus de lui faire effectuer une rotation.
  • rotate_scaled_sprite_v_flip : cette fonction est la combinée des deux fonctions précèdentes.
Danger
Dans les fonctions de rotation, les angles sont au format fixed, propre à Allegro. Donc, pour spécifier un angle, il faut au préalable convertir sa valeur dans une base 256 (0=0° et 256=360°), puis convertir cet entier compris entre 0 et 256 au format fixed grâce à la fonction itofix. Exemple ci-dessous.

Allegro propose aussi des fonctions permettant de faire réaliser au sprite un pivot d'angle donné autour d'un point. Ces fonctions sont :

  • pivot_sprite : fait effectuer un pivot au sprite avec un certain angle, et autour d'un point donné.
  • pivot_sprite_v_flip : même chose qu'au dessus, plus un mirroir vertical.
  • pivot_scaled_sprite : même chose que la première fonction, mais avec une échelle, tout comme la fonction rotate_scaled_sprite.
  • pivot_scaled_sprite_v_flip : une combinaison des deux précédentes fonctions.

Cette fonction affiche le sprite en modifiant sa taille. Elle s'utilise comme draw_sprite, si l'on excepte le fait qu'il y ait deux arguments supplémentaires qui sont w et h où l'on place les valeurs de la nouvelle largeur et de la nouvelle hauteur du sprite.

//Affiche sprite au point (72;76) dans bitmap
draw_sprite(bitmap,sprite,72,76);
 
//Affiche sprite au point (72;76) dans bitmap avec une rotation de 180° autour de son centre
rotate_sprite(bitmap,sprite,72,76,itofix(128));
 
//Affiche sprite au point (72;76) dans bitmap avec une rotation de 180° autour de son centre
//et une taille trois fois plus grande
rotate_scaled_sprite(bitmap,sprite,72,76,itofix(128),itofix(3));
 
//Affiche sprite au point (72;76) dans bitmap avec une rotation de 180° autour
//du point (256;134)
pivot_sprite(bitmap,sprite,72,76,256,134,itofix(128));
 
//Affiche sprite au point (72;76) dans bitmap avec une taille de 100 pixels par 105 pixels
stretch_sprite(bitmap,sprite,72,76,100,105);


Les palettes

Tous d'abord, avant même que nous commencions à voir le concept de palette, il est à préciser que les palettes n'existent qu'en mode 8 bits (256 couleurs), et donc n'existent pas en mode truecolor.
Donc, comme vu précédemment, en mode 8 bits, le programme ne peut afficher que 256 couleurs différentes. Cela présente évidemment un apparent bridage aux jeux 8 bits. Heureusement, Allegro permet de personnaliser cette palette. C'est-à-dire que dans cette palette de 256 couleurs, nous pouvons placer les couleurs que nous souhaitons. De plus, il est bien sûr possible de créer plusieurs palettes, que nous pouvons alternativement rendre actives. Mais attention tout de même, une seule palette peut être active à la fois. De plus, le dessin affiché à l'écran utilise la palette active, donc en cas de changement de palette sans effacement de l'écran, les couleurs affichées vont changer pour celles de la nouvelle palette.

Pour une utilisation normale des palettes (c'est à dire sans effets un peu ardus) il n'y a que très peu de fonctions à connaître. Surtout n'oubliez pas quand vous chargez une image dans un bitmap de bien spécifier la palette utilisée par l'image dans le second paramètre de la fonction.
Voici les quelques fonctions à connaître :

  • select_palette : cette fonction permet de rendre active une palette, en passant le nom de celle-ci en argument de la fonction.
  • set_color : cette fonction remplace la couleur de la palette référencée par son numéro passé en premier paramètre par la couleur au format RGB passé en second paramètre.
  • fade_in : cette fonction réalise un fade in de la palette passée en premier argument (affichage progressif du noir vers les couleurs de la palette) à la vitesse (comprise entre 1 et 64) passée en second argument.
  • fade_out : cette fonction réalise un fade out de l'écran à la vitesse (comprise entre 1 et 64) passée en argument

Les autres fonctions sont moins utiles, référez-vous à la documentation d'Allegro.

//Déclaration de la palette
PALETTE palette;
 
//Active la palette "palette"
select_palette(palette);
 
//Effectue un fade in
fade_in(palette,20);
 
//Effectue un fade out
fade_out(20);
Astuce
Pour être sûr que toutes vos images possèdent bien la même palette, je vous conseille d'utiliser les fonctions de gestion des palettes de votre logiciel de retouche d'image. Ainsi, lorsque vous créez vos images, vous enregistrez la palette qu'elles utilisent grâce à la fonction enregistrer la palette, et ainsi pour créer une nouvelle image, il suffit de faire charger une palette et de charger la palette précédemment enregistrée.


Le mode truecolor

Comme dit précédemment, lorsque l'on est en mode 8 bits, on se retrouve limité au niveau du nombre de couleurs à celles de la palette. Pour effacer ce problème, il faut se paramétrer en un nombre supérieur de couleurs, soit 16 bits, 24 bits ou 32 bits. Cela permet d'utiliser toutes les couleurs imaginables dans le jeu.

Danger
Il est conseillé que les images que vous chargez dans vos programmes utilisent le même nombre de couleurs que votre jeu (par exemple 24 bits).

Pour utiliser un mode supérieur à 8 bits, il faut appeler la fonction set_color_depth avant set_gfx_mode, comme il a déjà été dit.
Après cela, la seule chose qu'il faut savoir, c'est que pour utiliser une couleur spécifique avec les fonctions qui prennent en argument un numéro de couleur, il faut utiliser la fonction makecol avec comme arguments les composantes rouge, vert et bleu de la couleur.

//Initialise en 24 bits
set_color_depth(24);
 
//...
 
//Dessine un pixel de couleur rouge (r=255, v=0, b=0)
putpixel(bitmap,25,25,makecol(255,0,0));


Les primitives de dessin

Allegro propose un grand nombre de primitives de dessin, c'est à dire de fonctions qui permettent de dessiner un cercle, un rectangle, etc...
Ces fonctions n'étant pas compliquées à utiliser, nous allons les détailler par des exemples.

BITMAP *bitmap;
 
//Toutes ces fonctions vont afficher sur le bitmap nommé "bitmap"
//avec la couleur donnée par makecol(0,0,0), donc le blanc
 
//Affiche un pixel blanc à la position (100;100)
putpixel(bitmap,100,100,makecol(0,0,0));
 
//renvoie la couleur du pixel (100;100)
getpixel(bitmap,100,100)
 
//Dessine une ligne verticale entre les points (100;50) et (100;60)
vline(bitmap,100,50,60,makecol(0,0,0));
 
//Dessine une ligne horizontale entre les points (100;50) et (150;50)
hline(bitmap,100,50,150,makecol(0,0,0));
 
//Dessine une ligne entre les points (100;50) et (150;60)
line(bitmap,100,50,150,60,makecol(0,0,0));
 
//Dessine un triangle de sommets (50;50), (100;100) et (75;75)
triangle(bitmap,50,50,100,100,75,75,makecol(0,0,0));
 
//Dessine un polygone rempli à 5 sommets, et où leurs
//coordonnées sont dans le tableau points définies ainsi :
//int point[10]={x1,y1,x2,y2,x3,y3,x4,y4,x5,y5}
polygon(bitmap,5,points,makecol(0,0,0));
 
//Dessine un rectangle dont le coin supérieur droit est (50;50) et le
//coin inférieur bas (100;100)
rect(bitmap,50,50,100,100,makecol(0,0,0));
 
//Même chose que rect mais remplit le rectangle
rectfill(bitmap,50,50,100,100,makecol(0,0,0));
 
//Dessine un cercle de centre (100;100) et de rayon 5 pixels
circle(bitmap,100,100,5,makecol(0,0,0));
 
//Même chose que circle, mais rempli le cercle
circlefill(bitmap,100,100,5,makecol(0,0,0));
 
//Dessine une ellipse de centre (100;100), de rayon sur x 50 et de rayon sur y 60
ellipse(bitmap,100,100,50,60,makecol(0,0,0));
 
//Dessine un arc de cercle de centre (100;100), partant à l'angle 90° et finissant
//à l'angle 180° dans le sens trigonométrique
arc(bitamp,100,100,itofix(64),itofix(128),5,makecol(0,0,0));
 
//Dessine une courbe de bézier passant par les 4 points définis dans le tableau points 
//défini ainsi : int points[8]={x1,y1,x2,y2,x3,y3,x4,y4}
int points[8]={50,50,25,25,63,65,78,76}
spline(bitmap,points,makecol(0,0,0));
 
//Remplit une zone fermée dans laquelle se situe le point (100;100)
floodfill(bitmap,100,100,makecol(0,0,0));


Le texte

Il y a peu de fonctions gérant le texte dans Allegro, et de plus celles-ci s'avèrent enfantines à utiliser. Le texte ne posera donc aucun problème dans notre apprentissage.

Dans Allegro, les informations de la police utilisée sont stockées dans le pointeur font, qui représente la police par défaut. Cela est très important pour la suite, car toutes les fonctions textes d'Allegro utilisent ce pointeur.

La toute première fonction à connaître est la fonction text_mode. Cette fonction ne prend qu'un seul paramètre, un entier. Si cet entier est positif, le fond du texte sera de la couleur référencée par cet entier dans la palette courante. Si, au contraire, il est négatif, le fond du texte sera transparent.

Voici maintenant les quelques fonctions permettant d'afficher du texte à l'écran :

//Ces fonctions affichent "Hello world !" sur le bitmap "bitmap" aux coordonnées (100;100)
// avec la couleur makecol(0,0,0) sur fond de couleur makecol(255,255,255)
//aligné à gauche - au centre - à droite - justifié
textout_ex(bitmap, font, "Hello world !", 100, 100, makecol(0,0,0), makecol(255,255,255));
textout_centre_ex(bitmap, font, "Hello world !", 100, 100, makecol(0,0,0), makecol(255,255,255));
textout_right_ex(bitmap, font, "Hello world !", 100, 100, makecol(0,0,0), makecol(255,255,255));
 
//Le texte sera justifié entre les valeurs de x [100,200]
//si l'espace restant est supérieur à 50, le texte sera affiché justifié à gauche
textout_justify_ex(bitmap, font, "Hello world !", 100, 200, 100, 50, makecol(0,0,0), makecol(255,255,255));
 
//Ces fonctions s'utilisent comme les précédentes, si l'on excepte le fait qu'elles
//permettent aussi d'afficher des variables grâce à une syntaxe similaire à printf
textprintf_ex(bitmap, font, 100, 100, makecol(0,0,0), makecol(255,255,255), "Affiche la variable id %1", id);
textprintf_centre_ex(bitmap, font, 100, 100, makecol(0,0,0), makecol(255,255,255), "Affiche la variable id %1", id);
textprintf_right_ex(bitmap, font, 100, 100, makecol(0,0,0), makecol(255,255,255), "Affiche la variable id %1", id);
textprintf_justify_ex(bitmap, font, 100, 200, 100, 50, makecol(0,0,0), makecol(255,255,255), "Affiche la variable id %1", id);


Modifier la police par défaut

Comme nous l'avons vu précèdemment, les fonctions de texte d'Allegro peuvent afficher du texte dans n'importe quelle police de caractére, définie par le type FONT. Allegro propose ainsi par défaut la police de caractére du DOS, référencée par le pointeur font.

Il faut savoir qu'allegro n'utilise par des polices de caractére au format TTF comme Windows, mais au format graphique (.pcx). Ainsi, il nous faut un convertisseur capable de transformer un fichier TTF en fichier PCX. TTF2PCX en est un, d'une simplicité exemplaire, et téléchargeable ici : http://www.geocities.com/davidcapello/ttf2pcx/ttf2pcx.zip. Une fois le fichier graphique obtenu, il ne nous reste plus qu'à l'insérer dans un fichier .dat grâce à GRABBER (cf. annexe 2), et à charger cette police ainsi :

//Pointeur vers la police
FONT* ma_police;
//Pointeur vers le datafile
DATAFILE* datafile;
 
//Chagement du datafile
datafile=load_datafile("mondatafile");
//Chargement de la police contenue dans le datafile
ma_police=(FONT *) datafile[policedudatafile].dat;
 
//Affichage de "Hello world !" grâce à la police personnelle
textout(screen, ma_police, "Hello world !", 100, 100, makecol(255,255,255));
Outils personels