Site News
Research
Texts
X-philo
Code
KB
Awards
Links
Site Map





Copyright (c)
2000-04
by Newsdee

   

La programmation graphique en C/C++

. un bref aperçu introductif de la création graphique sur PC .

par Newsdee/Kaotyk Breïn (alias Gérard Imbert)

version 1.0
 

OBJET:

Ce document est tout d'abord destiné à tous les étudiants de la Miage Dauphine intéressés par le graphisme, afin qu'ils puissent déployer à fond leur potentiel de programmation sur l'ordinateur personnel le plus repandu sur le marché, à savoir les clones IBM PC.
Le partage de connaissance est une source d'enrichissement perpétuel, j'écris ce texte car il me remet en place des idées sur le graphisme et il m'évitera de me répeter si j'explique ces bases à plusieurs personnes... vous aussi, peut etre, vous aurez un jour une technique de dingues à communiquer, et vous saurez alors ce qu'il vous reste à faire, faire une doc comme celle ci :-)
 

AVERTISSEMENT:

Ce texte aborde la programmation graphique avancée. Si ce que vous voulez faire est juste quelques lignes en 16 couleurs, reférez-vous aux fonctions internes de n'importe quel compilateur C/C++. Mais si vous voulez charger des images, jongler avec 256 couleurs et apprendre toutes les bases qui vous permettront de faire des animations de folie, bienvenue chez moi, jettez vos affaires là derrière et mettez vous à l'aise, le spectacle va commencer.

DISCLAIMER:

Vous pouvez distribuer ce texte où bon vous somble, avec comme seule condition de ne pas le modifier. Je préfererais cependant connaître les personnes intéressées et qui se serviront de ces notions, pour les mettre tous en contact et que ces techniques soient developpées par plusieurs personnes en même temps. Donc mailez-moi, bossez entre vous, posez-vous des questions, posez-moi des questions, et j'y repondrais si je sais, sinon on cherchera comment faire... et peut être un jour on sortira un nouveau texte avec des techniques plus avancées.
 
Ce qui est écrit dans ce texte est le résumé de plusieurs mois de recherche. Faites-en bon usage, déployez vos ailes de créateurs et n'hésitez pas à aller là où aucun Miagiste n'est jamais allé auparavant... sinon je vous fais confiance pour remercier votre mentor dans vos futurs beaux projets. ;-)

 

SOMMAIRE:

 
. Préfaces (Objet, Avertissement, Disclaimer et Sommaire)
1. Introduction: Le grand secret du graphisme
2. ça veut dire quoi tous ces noms chelous ?
3. Balance la sauce, on part en mode graphique
4. Exprimez-vous, dessinez des pixels et des lignes
5. Une image vaut mille paroles
6. Le texte en mode graphique
7. Conclusion: c'est déjà fini ?
8. Bibliographie
9. Remerciements
 
 
 

- 1. Introduction: le grand secret du graphisme -

 
Le but de ce texte est d'apporter quelques bases sur la programmation graphique, qui est beaucoup moins complexe de ce que le commun des mortels a tendance à croire. En tant que Miagistes, et avec les cours de M. Quement, vous avez toutes les connaissances requises et donc le pouvoir de faire que votre PC se donne à fond.
 
Les possibilités vont au-délà de tout ce que vous pouvez imaginer.
 
Si vous ne me croyez pas, allez faire un tour sur le net et cherchez ce qu'est une "demo" sur PC. Cherchez le "PC Demo Fan Club" (je ne me souviens plus de l'adresse) et suivez quelques exemples. Essayez "JIZZ" de The Black Lotus, qui tient en 256 Ko, ecoutez la musique qui est en train d'être faite depuis l'ère de l'Amiga sur des simples machines, voyez le design et la création graphique pure que ces hommes et femmes dénommés Demomakers sont capables de faire.
 
Et tout ça, c'est à votre portée. Evidemment, je sais bien que votre but n'est pas de devenir un ou une Maitre Es Graphisme, mais d'impressioner le prof avec une jolie animation... c'est celui de tout le monde !  Mais connaître ce qui se fait ailleurs, surtout en Démo, est, du moins en mon opinion, une façon de voir un modèle que l'on peut aspirer à s'approcher.
 
Car ce qui est vrai en Démo est aussi vrai pour toute programmation graphique bien faite, les deux seuls véritables secrets du graphisme tiennent en deux mots:
 
DESIGN
et
MOTIVATION

Design, car finalement la technique n'est pas très difficile à apprivoiser, et qu'après tout le résultat graphique et la beauté ou non de ce qui apparaît à l'écran dépend de plus de l'usage que l'on en fait. En d'autres termes, avoir des beaux graphismes dans ses programmes est surtout avoir un bon design graphique.

Motivation, car il ne faut pas hésiter à chercher longuement des nouvelles techniques, il ne faut pas avoir peur d'apprendre des nouvelles choses et en expérimenter pour chercher d'autres moyens de repousser les limites de vos programmes.

Si vous vous attendez à ce que ce texte vous donne un code source et qu'apres vous n'aurez plus qu'à faire un "affiche_image("mon_image"); ", passez votre chemin. Ce texte est ici pour ceux qui n'auront pas peur d'aller lobotomiser les registres du processeur pour voir ce qu'il pense quand vous jouez sans vergogne avec les interruptions du Bios. A ceux qui feront rebooter sans cesse leur machine parce qu'ils auront trop forcé la main sur une fonction obscure. A ceux qui créeront eux-mêmes des fonctions tellement puissantes qu'ils auront du mal à les faire avaler à la machine...

Bon, là je vois qu'il n'y a plus personne pour me lire... hey, revenez !

Tout ça ne veut pas dire être un dieu en assembleur... Il n'y a pas de rapport en fait. Ca veut tout simplement dire que vous êtes prêts à fournir l'effort nécessaire pour comprendre un domaine nouveau, en l'ocurrence le graphisme. L'Assembleur, sujet tabou, n'est là en réalité que pour mieux comprendre le fonctionnement interne du processeur. A long terme, vous pourrez vous en servir pour optimiser vos programmes, les faire tenir en moins de place et tourner plus vite. Mais cela va très en dehors du cadre de ce texte. Référez-vous à la bibliographie à la fin si vous voulez des livres sur le sujet. Mais à plus court terme, vous n'aurez à utiliser qu'episodiquement l'assembleur, et il est même possible de remplacer dans la plupart des cas les maigres fonctions en assembleur que vous verrez ici par du code en pur C... alors ne vous en faites pas. ça aboie mais ça ne mord pas.

En fait, le plus important est de voir comment marche le PC au niveau electronique, c'est à dire comment manipuler les interruptions du BIOS et quels Ports matériels appeller pour obtenir les informations que l'on cherche, ou l'effet désiré. Ce sont des choses que l'on voit très facilement quand on programme en Assembleur, et qu'il faut avoir en tête quand on programme du graphisme. D'où l'utilité de ne pas partir en courant à chaque fois que l'on entend le nom de ce complexe langage...
 

 

- 2. ça veut dire quoi tous ces noms chelous ? -

 
La première chose en graphisme est d'apprendre le petit jargon que tout expert a tendance à devellopper entre ses collègues. Mais attention, parce que loin d'être des mots incompréhensibles, vous verrez qu'ils représentent bien ce qu'ils signifient. Cette partie est plus ici pour réference, pour qu'en cas de doute vous puissiez revenir ici.
 
. Le Pixel .
 
C'est l'unité de base du graphisme, un Element d'Image (Picture Element en anglais, ou Pixel). L'écran et les images sont en fait composées d'une matrice de pixels, chaque pixel pouvant avoir une couleur différente.
 
Imaginez un stade rempli de gens avec des pancartes. Selon la face de la pancarte qu'ils montrent, on voit des images apparaître. C'est exactement le même principe, chaque pancarte étant un pixel ! On combine les pixels d'une manière ou d'une autre pour obtenir une image.

 

. Résolution graphique .
 
Non, ce n'est pas du tout une décision à prendre ou un exo de maths... Il s'agit en fait de la taille de l'écran, exprimée en résolution horizontale et verticale.
ça veut dire quoi ? C'est tout simplement la taille de la matrice de pixels à l'écran, exprimée par nombre de pixels horizontaux et pixels verticaux.
 
Les modes standard que l'on verra dans ce texte est le 320*200, c'est à dire 320 pixels horizontaux fois 200 pixels verticaux. Le mode graphique de la plupart des compilateurs est en revanche un peu plus grand: 640*480.
 
Il existe des programmes qui tournent dans des résolutions bien différentes, les deux autres modes standard étant le 800*600 et le 1024*740. Ces résolutions sont plus ou moins lourdes à gérer par l'ordinateur selon le nombre de mémoire graphique présente dans l'ordinateur... les premières cartes avaient 64 Ko, et les plus récentes en ont plus de 12 Mo.
 
Le mode que l'on utilisera dans ce texte est le 320*200 en 256 couleurs. C'est un mode commun à tous les PCs existant, et un des plus simples à gérer. D'où son interêt. Les compilateurs C offrent quant à eux un mode 640*480 en 16 couleurs. C'est beaucoup plus fin, graphiquement... alors lequel choisir ? Le programmeur intelligent utilisera les deux... ou tentera d'obtenir une résolution supérieure en plus de couleurs... mais ça, c'est plutôt le sujet pour la fin de ce texte.

 

. Bits par Pixel (ou Color Depth) .
 
Le Bits par Pixel, ou "bpp" représentent le nombre de bits où sont codifiés la couleur des pixels.  Ils sont doublement importants: ils permettent à la fois de voir combien de place sera nécessaire pour gérer l'écran et les images, mais aussi pour voir combien de couleur sont disponibles.
 
Je m'explique. Imaginez que chaque pixel est codé sur un seul bit. On aura donc que deux possibilités, 0 et 1. On obtiendra donc un mode graphique avec une seule couleur, sans nuances de gris. On peut noter cette resolution "1 bpp"
 
Si chaque pixel est codé sur quatre bits, il pourra prendre en revanche seize valeurs. C'est ce que l'on utilisait dans des anciens modes graphiques (le EGA, pour ceux qui connaissent). Ici, on aurait du "4 bpp".
 
Dans notre cas, les pixels sont codés sur huit bits. Ce qui nous donne, bien évidemment, 2 puissance 8 couleurs possibles, soit 256. C'est un mode très efficace, car comme les pixels prennent un pile poil un octet, il est très simple est rapide de les manipuler.
 
Il existe aussi quatre autres modes graphiques standard:
 
Le 15 bits par pixels permet l'affichage de 32000 couleurs simultanées, au prix de certaines astuces techniques.
Le 16 bits par pixel permet quant à lui beaucoup plus, 64000 couleurs... C'est déjà plus intéressant !
 
Mais rien ne surpasse actuellement les modes 24 et 32 bits. Ils s'agit exactement du même mode, la seule différence étant en fait que 32 bits (un Double Word en assembleur) est plus rapide à manier que 24 bits (un Word et un Byte). Mais dans les deux cas les couleurs sont codées sur trois octets. La différence est vraiment technique, car à l'écran les deux modes permettent d'afficher... 16,4 millions de couleurs !! On est bien loin des 16 couleurs des fonctions de base du compilateur...
 
L'autre aspect à connaître des bits par pixel est qu'ils représentent aussi la taille de mémoire nécessaire pour manipuler le graphisme. Car en fait, l'écran doit contenir chaque pixel avec ses informations. D'où le grand dilemme du graphisme: il faut savoir équilibrer couleurs disponibles et taille de mémoire, car gérer 640*480, soit 307200 pixels en 32 bits nécessitent en fait 307200*4 (4 octets par pixels), 1,23 Mo de mémoire !  La même résolution en 16 couleurs n'aura besoin que de 154 Ko (1/2 octet par pixel)...
 
Bon, ne vous en faites pas, en général toute carte moderne dispose d'au moins 1 Mo de mémoire. Le mode que l'on utilisera n'utilise que 64 Ko de mémoire, car il est en 320*200 avec un seul octet par pixel...
 
 
. Les modes graphiques .
 
Avant de pouvoir utiliser les fonctions graphiques, il faut mettre l'ordinateur dans le "mode graphique" désiré. En fait on prépare la mémoire pour que toutes les fonctions soient prêtes à l'emploi. Le mode graphique désigne aussi la résolution utilisé: on parlera de mode graphique 320*200*8 (aussi nommé mode 13h pour des raisons que l'on verra dans le chapitre suivant), ou de mode 640*480*32 (à chaque fois, le dernier chiffre représente les bits par pixel utilisés).
 
Il faut donc, avant de lancer toute fonction graphique, lancer le mode graphique, et à la fin du programme, revenir au mode texte. On abordera ça dans le chapitre suivant.
 
. Les modes standard et ses hybridations .

Tout le graphisme sur PC a vraiment décollé quand la carte VGA est sortie. Nommée "Versatile Graphics Adapter", elle permettait le mode standard 320*200*8, mais le plus important de cette carte (dont découlent toutes les cartes modernes, ce qui signifie qu'elles sont toutes compatibles avec celle-ci) est qu'elle est très facilement reprogrammable. Ce qui permet une myriade d'astuces intéressantes. On ne les abordera pas dans ce texte, on ne se tiendra qu'au standard, mais si vous voulez plus d'informations, cherchez sur le net des informations sur un mode graphique nommé "Mode X", qui a longtemps été utilisé pour des jeux, et reste encore tout à fait intéressant aujourd'hui. C'est un bon exemple des possibilités qu'offre un PC avec son Bios.

 

. La notion de palette .

Dans le mode graphique 8 bits, on verra revenir parfois la notion de palette de couleurs. En fait, comme on ne dispose que de 256 couleurs, on ne pourrait à priori pas faire de belles images. Mais, et si on pouvait choisir ces 256 couleurs affichables simultanément parmi une myriade d'autres ? C'est ce que se sont dit les concepteurs de la carte VGA, et du coup, chaque couleur codée sur 8 bits fait référence à ce que l'on appelle un Registre de Palette.
A l'intérieur de la carte graphique, il existe un ensemble de registres qui contiennent les informations sur la palette. Chaque couleur est codée avec trois composantes, la Rouge, la Verte (Green) et la Bleue, qui peuvent avoir des valeurs allant de 0 à 63. Pour modifier la palette, il suffit de regarder dans le registre de palette, choisir une couleur, et modifier ses composantes. On peut donc ainsi avoir 256 nuances de bleu en même temps si l'on veut montrer une photo de la mer, ou avoir plus de choix de couleurs si la photo est plus complexe. On peut même faire des effets en faisant tourner les couleurs de la palette, mais ça, ce sera pour un autre texte.

 

- 3. balance la sauce, on part en mode graphique -

 Nous sommes enfin prêts pour le grand plongeon dans le monde du graphisme ! Vérifiez vos instruments, préparez vos yeux et affûtez vos neurones, le décollage est imminent ! Pour arriver à nos fins, nous utiliserons les anciennes arcanes du Bios: on appellera une fonction assez ancienne mais qui est extrêmement facile à utiliser.
 
Il s'agit de l'interruption 10h du BIOS.
 
Vous remarquerez par ce qui suivra qu'utiliser des interruptions est très utile. Et vous aurez probablement envie de demander "mais comment on aurait pu savoir ça?". C'est là qu'intervient le deuxième secret du graphisme... ah, vous avez déjà oublié ? Bon: c'est "la motivation". Il ne faut pas avoir peur de partir dans des livres ou, surtout de nos jours, sur le net, à la recheche d'informations.
 
En l'ocurrence, cherchez ce que l'on appelle une "liste d'interruptions", un document qui liste et décrit toutes les interruptions du BIOS. Vous verrez qu'il existe des tonnes de documents sur un gigantesque ensemble de domaines concernant la programmation PC. Dans ce cas, il faut se fixer un objectif, on prend la ou les docs en question et on se met à programmer. Ce n'est pas si difficile que ça... mais je pars dans les branches. Revenons au sujet.
 
On utilisera donc l'interruption 10h du BIOS. C'est la "BIOS video services interrupt", c'est à dire l'interruption qui gère le graphisme. Cette interruption dispose de beaucoup de fonctions, mais on n'utilisera que la première, la fonction 0: "set video mode".
 
On placera donc dans le registre AH du processeur la valeur 0, avant l'appel de l'interruption, de la manière suivante (attention les yeux, y'a de l'assembleur) :
 
MOV   AH, 0h

Il ne reste plus qu'à indiquer, toujours avant l'appel de l'interruption, quel mode graphique on veut lancer. Dans notre cas, on veut le mode graphique 320*200*8, ce qui correspond à la valeur 13h. Cette valeur a été choisie par les concepteurs du BIOS, pour connaitre les autres valeurs, il faut se réferer à une liste d'interruptions.

Si vous avez tout suivi depuis le début, vous remarquerez pourquoi le mode 320*200*8 s'appelle le mode 13h. On placera cette valeur dans le registre AL.
 

MOV   AL, 13h
 
Il ne nous reste plus qu'à lancer l'interruption:
 
INT  10h

Et voilà, nous sommes en mode graphique !  Simple, non ?

D'accord, on ne voit rien, mais c'est parce qu'on n'a pas encore dessiné quoi que ce soit... Tout d'abord, il vaudrait mieux se soucier de  comment revenir au mode texte, parce que sinon, ça va faire désordre: on reviendrait sous DOS avec le mode VGA... si ça vous arrive, tapez la commande DOS "mode co80" pour revenir au mode texte normal.
 
Pour revenir proprement au mode texte, on utilisera le mode 3h, ce qui correspond au mode texte en 80x25 colonnes en 16 couleurs. Sinon, c'est exactement la même fonction, il suffit de remplacer la deuxième instruction par:
 

MOV  AL, 03h

Pour clore ce chapitre, je signale juste qu'il existe une fonction en C et C++ qui permet l'appel à une interruption, cette fonction se nomme int86() sur le Borland C++. Pour les autres compilateurs, il faut chercher. Mais en tous cas elles existent et ça vous évite d'avoir à mettre de l'assembleur dans vos programmes. Ce qui est surtout utile pour ceux qui y sont allergiques.
 
Récapitulons: il suffit d'appeller l'interruption 10h, fonction 0h (dans AH) et ensuite 13h (dans AL) pour le mode graphique ou 03h (dans AL) pour le mode texte.
 
 

- 4. exprimez-vous, dessinez des pixels et des lignes -

Nous savons déjà entrer et sortir du mode graphique... maintenant il faut s'en servir ! C'est ici que vous verrez pourquoi le mode 13h est un mode de prédilection, car il est extrêmement facile à manipuler. A côté de celui-ci, si vous faites des recherches, la gestion de certains autres modes graphiques vous sembleront des véritables horreurs !
 
Pourquoi le mode 13 est si simple que ça, ben tout simplement parce que l'écran est représenté directement dans la mémoire de l'ordinateur, sous la forme d'un grand tableau de 64000 octets. Il est situé à l'adresse A000:0000 en mémoire. Vous pouvez écrire ce que vous voulez dans ce buffer, ça sera montré tout de suite à l'écran. Et cela se fait aussi simplement que mettre un entier dans un tableau.
 
Pour cela, on va définir une variable globale. J'en vois qui sursautent... les variables globales, c'est pas très beau, mais dans notre cas ça simplifiera les choses. A vous d'améliorer par la suite si ça vous choque... ;-)
 
Cette variable globale sera un pointeur sur un tableau d'octets, et il va représenter en fait l'écran de l'ordinateur. Ce qui signifie que tout ce que l'on écrira dans ce tableau sera montré immédiatement à l'écran !
 
static unsigned char far *video_buffer=0xA0000000L;
 
Nous utilisons un "L" à la fin pour indiquer qu'il s'agit d'une valeur "Long", et on spécifie "far" pour des raisons de gestion de la mémoire lors de la compilation (Note technique: si on ne le spécifie pas, il s'agirait d'un pointeur "near" qui ne peut pointer sur tout ce qui est à plus de 64 ko de distance de l'endroit où se situe le pointeur en mémoire). Enfin le "static" indique que sa valeur ne doit pas changer lors de l'éxécution du programme sous aucun prétexte.
 
Une fois que nous avons défini cette variable, il est très facile d'écrire à l'écran. Si nous voulons placer un pixel de couleur 10 à l'emplacement 1403, il suffit de faire:
 
video_buffer[1403]=10;
 
Vous remarquerez qu'on n'a qu'une seule coordonée ! Or le graphisme se gère le plus souvent par un X et un Y qui représentent les coordonées où l'on se placera dans la matrice de pixels. En fait, il suffit de transformer ces coordonées par une proyection, et de la passer en valeur dans le tableau.

Comme chaque ligne horizontale dispose de 320 pixels, il suffit de multiplier 320*Y. Ensuite il suffit d'ajouter la valeur de X pour obtenir l'emplacement du tableau à écrire:
 

emplacement = 320 * Y + X
 
Même un gamin pourrait le faire... qui a dit que le graphisme c'était dur ?
 
Faire une ligne est un peu plus compliqué, et je ne l'aborderai pas ici. Je vous dirais juste qu'il faut utiliser un algorithme nommé Algorithme de Bresenham, qui permet de faire une approximation linéaire des points d'une droite par rapport aux pixels. Cette technique est abordée dans des nombreux ouvrages ou sur de nombreuses pages web, donc vous n'aurez aucun mal à la trouver.
 
Cependant, pour faire une ligne horizontale à un Y donné, de X1 à X2, il suffit de faire:

for(i=X1,i<=X2,i++)
  { video_buffer[320*Y+i]=couleur;
  }

Pour faire un rectangle, il suffit de répeter le dessin de ligne horizontale plusieurs fois... A vous de voir comment faire les triangles, les cercles, et le remplissage. Cherchez des documents, ce n'est pas si dur...
 
 

- 5. une image vaut mille paroles -

 
On aborde à présent le coeur du graphisme, ce petit plus qui a fait que vous vous intéressiez au mode graphique, la possibilité de mettre des belles images dans vos programmes !  Ouvrez grands vos esprits et continuons notre quête...
 
Il faut savoir que manipuler une image n'est pas plus difficile que savoir décoder un fichier, en extraire les informations et les placer dans un tableau. Dans notre cas, on n'utilisera que des images au format PCX, pour les autres formats il vaut mieux aller regarder dans un livre ou sur le net, des documentations très détaillées existent.
 
La structure d'une image PCX 320*200*8 est comme suit:
 
1. EN-TETE (128 octets)
2. DONNEES DE L'IMAGE
3. INFORMATIONS DE PALETTE
 
L'en-tete ne nous intéresse pas, il contient des informations sur le format d'image à destination des applications graphiques. Après avoir ouvert le fichier,
 
imagePCX=fopen("Image.pcx","rb");

On peut "jeter" les premiers 128 octets:
 

fseek(imagePCX,128L,SEEK_SET);
 
Maintenant, il faut savoir que les images sont compressées. Ce qui signifie qu'il va falloir les décoder avant de les mettre à l'écran. L'algorithme est comme suit: on lit un octet du fichier. Si sa valeur est supérieure à 192, alors c'est un code qui indique qu'il y dans l'image "valeur-192" pixels suivants ayant la même couleur. Il suffit donc de lire la valeur du pixel suivant et de le répeter dans l'image autant de fois que nécessaire.
 
Respirez un coup, ne partez pas en courant, et voici l'algo en question:
 
int compteur=0, nombre_octets=0;
char *image_en_memoire[64000];
unsigned char data;

 while(compteur<=64000)
          { data=(unsigned char)getc(imagePCX);
            if ( data>=192 )
              { nombre_octets=data-192;
                data=(unsigned char)getc(image);
                while(nombre_octets-- > 0)
                  { image_en_memoire[compteur++]=data;
                  }
              }
            else
              { image_en_memoire[compteur++]=data;
              }
         }

Maintenant il suffira de copier le contenu de image_en_memoire à l'écran. Mais avant cela, il va falloir décoder la palette de l'image, parce que sinon les couleurs à l'écran seront faussées. En d'autres termes, il faut que les couleurs de l'écran soient les mêmes que celles qui composent l'image, pour des raisons évidentes de cohérence... à quoi ça servirait de faire des belles images sinon ?
 
Tout d'abord, on ira à la fin du fichier PCX où sont situés les informations de palette. Ces informations sont codées sur trois octets par couleurs, chaque représentant une composante RGB de la couleur. Dans le mode 8 bits (8 bpp), ces octets ont des valeurs allant de 0 à 63.
 
On doit donc aller à la fin du fichier moins (256*3)=768 octets.
 

fseek(imagePCX,-768,SEEK_END);

Pour se simplifier la vie dans la manipulation des couleurs, il convient de créer une structure de données qui contiendra les informations de chaque couleur. On appellera cette structure couleur_RGB et on en mettra 256 dans un tableau que l'on nommera palette.

typedef struct { unsigned char red;     // Composante rouge  (0-63)
                 unsigned char green;   // Composante verte  (0-63)
                 unsigned char blue;    // Composante blueue (0-63)
               } couleur_RGB;

couleur_RGB palette[256];

Une fois que l'on a défini cette structure de données, récuperer les informations de l'image devient très simple (n'oubliez pas le fseek cité plus haut).

for(index=0;index<256;index++)
  { palette[index].red   = (unsigned char)(getc(imagePCX) >> 2 );
    palette[index].green = (unsigned char)(getc(imagePCX) >> 2 );
    palette[index].blue  = (unsigned char)(getc(imagePCX) >> 2 );
  }

Une fois que l'on a fini avec l'image PCX, n'oubliez pas de faire le ménage en mémoire:

fclose(imagePCX);
imagePCX=NULL;

Il ne nous reste plus qu'à mettre les informations de la palette dans la carte graphique. C'est une petite technique assez particulière qui merite quelques expliquations, mais une fois que vous l'aurez comprise, vous devriez être capable de vous en servir facilement.

On utilisera ce que l'on appelle des Port Calls, des appels à ports matériels du PC. Dans le cas qui nous concerne, on utilisera deux ports de la carte graphique (j'en inclus un troisième pour les raisons que vous verrez un peu plus bas).  Pour cela, on utilise les commandes C outp et inp (qui existent aussi en assembleur), en appellant le port pour lire ou écrire une valeur.
 
Pour écrire la valeur des trois composantes, il suffit en fait de faire deux pas très simples:
 
1. On ecrit la valeur de la couleur à modifier dans le port "Ecriture couleur"
2. On écrit à la suite les valeurs R, G, et B dans le port "Données Couleur"
 
Le fait d'écrire quoi que ce soit dans le port "écriture couleur" indique à la carte graphique que la couleur entrée va recevoir trois valeurs consécutivement, qu'il faudra placer dans les registres R, G et B de la couleur.
 
Pour vous faciliter la tâche et ne pas avoir à cherche les valeurs des ports à chaque fois, il vaut mieux en faire des DEFINE au début du programme, comme suit:
 
#define LIRE_REGISTRE_COULEURS   0x3C7
#define ECRIRE_REGISTRE_COULEURS 0x3C8
#define DATA_COULEUR             0x3C9
 
J'ai volontairement inclus le port "lecture couleur", qui marche de manière inverse: c'est à dire on y met la valeur de la couleur à lire et ensuite on lit trois fois dans "donnees couleur" pour avoir les trois composantes. Ces trois ports devraient vous permettre de manipuler à votre aise les palettes et de faire des effets déments.
 
Mais revenons à notre cas. Pour écrire la palette de couleurs dans les registres de carte graphique, il suffit de faire:
 
      for(index=0; index<256; index++)
        { outp(ECRIRE_REGISTRE_COULEURS, index);
          outp(DATA_COULEUR, palette[index].red  );
          outp(DATA_COULEUR, palette[index].green);
          outp(DATA_COULEUR, palette[index].blue );
        }
 
Pour lire à partir de la carte, c'est la même chose mais avec "inp". L'autre interêt de faire un tableau "palette", est que l'on peut créer plusieurs et ainsi faire des jeux de couleurs dans le programme... Expérimentez un peu et vous verrez que ça peut être impressionnant.
 
Vous pouvez aussi découper vos images pour en faire des sprites, ou mettre plusieurs images dans la même... toutes les portes vous sont ouvertes... mais le principal est fait, vous voilà avec une image à l'écran ! Le monde est beau, non ?
 

- 6. le texte en mode graphique et autres bizarreries -

 
Il ne reste qu'une petite chose à aborder, et non des moindres... avoir des pixels, des figures géométriques et des images, c'est super joli, mais pour le texte, comment faire ? Tout simplement, il suffit de faire un printf...

Mais ce n'est pas très beau... on ne dispose que d'une seule police de caractères. Une solution, qui ne sera pas détaillée ici (mais rien ne vous empêche de chercher par vous mêmes, au contraire) consiste à prendre des caractères à partir d'une image PCX, que vous découperez en morceaux pour stocker dans un tableau. Ce tableau sera une sorte de table ascii, et vous construirez des fonctions de sorte que la chaine "Salut !" apparaisse à l'écran avec un seul appel de fonction: elle prendra chaque caractère de la chaîne et imprimera l'image correspondante.

Suivant cet exemple, des tonnes de possibilités existent:

Vous pouvez gérer la souris et lui doter un pointeur graphique.
Vous pouvez calculer une table de sinus puis générer un déplacement d'images suivant les courbes, pour obtenir des animations du plus bel effet.
Vous pouvez fabriquer une structure de données qui gérera plusieurs images pour un même objet et ainsi créer ce que l'on appelle un sprite (lutin), un objet animé que vous pouvez déplacer à l'écran.
Vous pouvez gérer des gros fichiers d'animation en prenant connaissance de la structure du fichier et en affichant image par image le fichier à l'écran.
Vous pouvez aussi vous lancer dans le calcul et affichage d'objets 3D texturée en temps réel avec gestion des effets de lumière, mais là je prends l'exemple extrême. :-)
 
 

- CONCLUSION: C'est déjà fini ? -

Oh que non. ça ne fait que commencer. Ce que vous venez de voir n'est que le début dans l'épopée fascninante de l'animation et le graphisme sur PC. Vous vous demandez peut être à quoi ça sert, ben, justement à rien, si ce n'est embellir vos productions ou à créer des productions cent pour cent design dont le seul but est de se montrer...
 
Il est possible de faire monter la résolution à plus de 320*200. Pour informations, si vous avez vu mon dernier projet de1998 (celui avec les Super Marios qui font une file d'attente devant un guichet), il tourne à 640*480 en 32 bits par pixel (donc 16,4 millions de couleurs). Mais pour y parvenir, il a fallu que je programme des fonctions capables de gérer les modes graphiques VESA, un standard moderne de graphisme haute résolution. C'est loin d'être facile... mais pas impossible.
 
Quoi qu'il en soit, maintenant, vous n'avez plus d'excuses pour ne pas faire de graphisme !
Je vous ai donné les outils de base pour voler de vos propres ailes... maintenant la tâche vous incombe de chercher et de trouver des choses nouvelles, et avant que vous vous en rendiez compte vous aurez réussi à faire des trucs incroyables dont vous ne reviendrez pas.
 
A vos claviers, montrez au monde entier ce que valent les Miagistes Dauphinois quand ils se mettent à coder !!
 
- Gérard "Newsdee" Imbert, le 4 avril 1999.
 
 

- BIBLIOGRAPHIE -

Utilitaires de programmation:

Les compilateurs du CRIO Inter-UFR suffisent amplement pour l'appliquation des notions ici abordées, mais il existe des compilateurs beaucoup plus puissants et gratuits:

1. DJGPP: compilateur C/C++ 32 bits freeware pour DOS, Windows et Linux.                     ( disponible sur http://www.delorie.com )

2. NASM: Netwide Assembler, assembleur freeware pour DJGPP. ( disponible sur: http://www.cryogen.com/Nasm )

3. HelpPC: Petit utilitaire assez ancien mais qui recense beaucoup d'interruptions du BIOS, de fonctions C, des commandes Assembleur, et d'autres trucs sur les entrailles de nos PCs ( disponible dans de nombreuses de pages web, me le demander sinon )

Livres de base:

1. N'importe quel livre de C et de C++.
2. La bible PC: programmation système, Michael Tischer.
3. Programmer en Assembleur sur PC, Holger Schakel.

Livres très avancés:

1. Zen of Graphics Programming, par Michael Abrash.
2. Black Art of 3D Game Programming, par André Lamothe.
3. Zen of Code Optimisation, par Michael Abrash.
4. Master Class: Assembly Language, par Wrox Press.

Il existe des ouvrages encore plus avancés, où sont abordés par exemple la programmation 3d par une approche mathématique. Mais c'est plutôt destiné à d'autres type de programmeurs... ceux qui programment des logiciels comme Adobe Photoshop... ça reste intéressant, mais ça va un peu trop loin. A chacun de voir.
 
 

- REMERCIEMENTS -

Je voudrais ici remercier certaines personnes qui m'ont aidé à faire ce texte ou à développer les techniques ici présentées d'une façon ou d'une autre.

Tout d'abord Xavier Vu-Van-Xung qui m'a donné l'idée de taper ce texte.  J'ai aimé l'idée et je m'y suis lancé... comme d'habitude j'ai écrit un peu trop, mais considérez ceci comme un petit livre. J'espère que ça vous servira, et surtout que vous mettrez à contribution tout ce que vous aurez appris.
 
Ensuite je voudrais remercier M. Bernard Quément, professeur d'Algorithmique, qui m'a fait découvrir et comprendre le fabuleux langage qu'est le C et le C++. Avant d'assister à ses cours, je ne savais même pas ce qu'était un pointeur ou un héritage de classes...
 
Je continue par M. Marc Csernel, et ses cours d'assembleur, qui m'ont permis d'approfondir mes connaissances dans ce langage difficile, et dont le dernier TD m'a permis de developper les bases de ce qui ensuite allaient être les techniques ici utilisées...
 
Ainsi, bien entendu, qu'à tous ceux qui m'ont lu et qui me connaissent en Miage. Notamment à tous ceux qui se sont intéressés au graphisme et n'ont pas hésité (et n'hésiteront pas, je suppose) à me poser des questions sur le sujet.
 
Plus généralement, je voudrais remercier Michael Abrash pour ses fantastiques ouvrages, ainsi qu'André Lamothe pour les conseils qu'ils prodigue dans son livre. Je dois aussi beaucoup à Diana Gruber qui m'a appris à voir comment marche un jeu vidéo et les implications sur la programmation.
 
Un petit coucou à tous les membres de mon demogroup, à savoir Fahruz et Mad Process (un jour, oui, un jour, on sortira une démo ! yay !), ainsi qu'à tous les autres demomakers que je connais... surtout Light Show/Eclipse, Random/Lithium et Roudoudou/Flower Corp qui m'ont appris quelques astuces en Assembleur lors de la Wired '97. Finalement, un gigantesque "Arf!" à tous les membres de Arf!Studios.
 
 

- THAT'S ALL FOLKS ! -

commentaires, idées, corrections: newsdee@hotmail.com