Copy Link
Add to Bookmark
Report

2d Chapitre 3 - La palette 8-bit VGA

eZine's profile picture
Published in 
2d3dfx
 · 20 Feb 2021

CHAPITRE 3- La palette 8-bit VGA


LA PALETTE VGA

La palette est tout simplement ce qui contient l'attribut rouge, vert et bleu des différentes couleurs. En mode 13h, il existe 262 144 couleurs, mais nous ne pouvons pas en avoir plus de 256 dans notre palette, numérotés de 0 à 255. Ces couleurs sont formées à partir d'une combinaison de rouge, de vert et de bleu (red, green & blue en anglais). En mode VGA 8-bit, chacune de ces couleurs primaires peuvent avoir une intensité variant entre 0 et 63. Par exemple, si on voulait un bleu, on pourrait mettre l'intensité du rouge à 0, du vert à 0, et du bleu à 63. On peut considérer la palette d'un ordinateur comme une véritable palette d'artiste, où l'on mélange et compose nos couleurs selon nos besoins. Nos palettes digitales peuvent contenir 256 couleurs à la fois, chacune composé de différentes nuances de rouge, de bleu et de vert. En changeant la palette, on peut obtenir les effets de Fade-In et de Fade-Out, très connu dans les démos et les jeux. On peut même faire de l'animation en effectuant ce qu'on appelle une rotation de palette.

Pour travailler avec la palette, on doit se servir du DAC, le convertisseur digital en analogique (Digital Analog Converter). Ce dernier traduit les codes de couleur numériques en signaux analogiques. Un registre DAC se compose de 18 bits différents dont chaques groupes de 6 bits reçoivent le code de couleur primaire. D'où l'intensité de 0 à 63 (2^6 = 64) et des 262 144 couleurs possibles du mode MCGA (2^18). On accède au DAC par les registres suivants:


Digital Analog Converter

 PORT   Nom de registre 

03C8h Pel Write Address
03C7h Pel Read Address
03C5h DAC state
03C9h Pel data
03C6h Pel Mask


Pour lire la valeur d'une couleur de la palette, il suffit d'entrer le numéro de la couleur qui nous intéresse (de 0 à 255) dans le port de lecture 0x3C7 et lire la valeur de rouge, vert et bleu dans le port de donnée 0x3C9. En code C, on obtient:

 void lirepal(unsigned char col,unsigned char &r,unsigned char &g,unsigned char &b)   
{
outp (0x03C7, col);
r = inp (0x03C9);
g = inp (0x03C9);
b = inp (0x03C9);
}


Dans cette fonction, nous utilisons le passage de paramètre par adresse (ou par référence, si vous préférez), c'est à dire que nous modifions directement les variables qui sont passée par paramètre. Donc, les valeurs rgb nous sont retournées par la commande inp, qui lit les informations du port. Auparavant, nous indiquons de quelle couleur il s'agit en envoyant dans le port de lecture le numéro de la couleur, avec outp qui elle envoie des informations vers un port d'I/O. De la même façon, si on veut ajuster la valeur d'attribut d'une couleur, il suffit d'écrire le numéro de la couleur dans le Pel Write Address, 0x3C8, et d'inscrire son attribut RGB dans le port de donnée 0x3C9! De cette façon, nous écrivons les nouvelles valeurs rgb que nous désirons, pour la couleur concernée. Voici le code:

 void setpal(unsigned char col, unsigned char r, unsigned char g, unsigned char b)   
{
outp (0x03C8, col);
outp (0x03C9,r);
outp (0x03C9,g);
outp (0x03C9,b);
}

LA RETRACE VERTICALE

Ces deux nouvelles fonctions, setpal et lirepal, sont tout ce qu'il nous faut pour ajuster la palette selon nos besoins. Il est temps de passer à une notion très fondamentale de la programmation graphique, et particulièrement du demo coding: la retrace verticale. Les changements brusques dans la palette cause du scintillement, de la "neige" dans le moniteur, dégradant ainsi la qualité de l'image. C'est que notre moniteur est rafraîchit par un rayon d'électrons constant, qui balaye l'écran de gauche à droite, puis de haut en bas. Quand ce rayon est rendu en bas à droite, il doit revenir à la position 0,0, dans le coin supérieur gauche. Ce retour vers le haut se nomme le Vertical Retrace, ou retour de balayage vertical, en français. Cette retrace est considérablement longue, comparé au horizontal (200 fois plus longue, en fait!). Pour que nos effets soient bien dessiné à l'écran, il faut les faire pendant qu'une retrace verticale s'effectue, car l'image ne sera dessiné qu'au prochain balayage d'écran, évitant ainsi le scintillement et le "fuzz". Pour attendre le retour vertical, on exploite le fait que le registre général Input Status 1, un des registres généraux de la carte VGA, situé à l'adresse de port 0x3DA, contient dans son bit 3 l'information sur le Vertical Retrace (1 = activé, 0 = non activé) Il suffit de vérifier son contenu pour savoir si nous sommes dans une retrace verticale ou non.

Dans les fait, il ne suffit cependant pas seulement de vérifié si la retrace est activée, car il se pourrait qu'on appel ce registre en plein milieu d'un retour de balayage! Il faut donc attendre qu'un retour se termine, et en attendre un autre au complet pour être sûr que nous sommes bel et bien au début de notre retrace. Cette vérification se fait tout simplement avec, encore une fois, la commande inp :

 void syncretrace()   
{
while (inp(0x3DA) & 8);
while (!(inp(0x3DA) & 8));
}


Nous attendons tout d’abord la fin d’une première retrace, et le début d’une seconde. On effectue un opération logique and qui elle vérifie l'état du bit 3. Pour ceux qui ne savent pas ce qu'est une opération logique ET (&), voici un petit tableau qui devrait clarifier le tout, sachant que 8 en binaire est: 0000 0100. Avec l'opération logique ET, un affirmation est vrai si et seulement si les deux propositions sont vraies

 0110 0101  0000 0100 

0010 0011 0000 0100
0010 0001 0000 0100


En plus d'éviter une tempête de neige sur votre moniteur, cette fonction à un autre GROS avantage! Elle ralenti votre programme pour le synchronisé avec votre moniteur. C'est un excellent moyen de ralentir certain effets trop rapides. En contrepartie, votre programme ne sera jamais plus rapide que le taux de rafraîchissement du moniteur, peu importe les optimisations. Avec les taux de rafraîchissement autour de 75 par seconde, en général cela ne cause pas trop de problèmes.


FADE ET EFFETS DE LUMIÈRE

Comment faire un Fade-Out? Il suffit de gentiment abaisser la valeur RGB des couleurs à l'écran, pour tous les mettre à 0. Une boucle for est tout ce qu'il nous faut. Nous parcourons chacune des 256 couleurs, et pour chacune nous vérifions si ses attributs de rouge, de vert et de bleu sont à 0. S'il ne le sont pas, nous des décrementons. Le code pour cet effet va comme suit:

 void FadeOut()   
{
unsigned char temp[3]; // variable pour contenir le RGB temporaire
for (int i=0; i < 64; i++) // attribut RGB = 0 à 63
{
syncretrace(); // pas de neige + synchro
for (int j=0; j < 256; j++) // 256 couleurs de la palette courante
{
lirepal (j, temp[0], temp[1], temp[2]); // Lire chaques couleurs
if (temp[0] > 0) temp[0]--; // si différent de 0, decremente
if (temp[1] > 0) temp[1]--;
if (temp[2] > 0) temp[2]--;
setpal(j, temp[0], temp[1], temp[2]); // nouvelle valeur
}
}
}


De la même façon, pour faire un fade up, il suffit simplement de s'en aller dans l'autre direction. Cependant, puisqu'il faudra connaître les valeur initiales de la palette, il nous faudra sauvegarder notre palette. Si les attributs ne sont pas comme ils doivent l'être, on les incrémente jusqu'à temps qu'ils soient à la valeur voulue, celle contenue dans notre palette de sauvegarde. Nous utiliserons un tableau de 256 x 3, 0 à 255 pour les couleurs et 0 à 2 pour l'intensité RGB. Une fois que nous avons sauvegarder l'état initial de notre écran, il faut procéder à l'inverse du Fade-Out, c'est à dire on incrémente jusqu'à la valeur voulue. Si la valeur est inférieur à la valeur cible, on incrémente. Voici le code C:

 void FadeUp()   
{
unsigned char temp[3];
unsigned char pal[256][3];

for (int i=0; i<256; i++) // Sauvegarde valeurs initiales
lirepal(i,pal[i][0],pal[i][1],pal[i][2]);

for (int i=0; i < 64; i++)
{
syncretrace();
for (int j=0; j < 256; j++)
{
lirepal(j, temp[0], temp[1], temp[2]);
if ((temp[0] < pal[j][0]) && (temp[0] < 63)) temp[0]++;
if ((temp[1] < pal[j][1]) && (temp[1] < 63)) temp[1]++;
if ((temp[2] < pal[j][2]) && (temp[2] < 63)) temp[2]++;
setpal(j, temp[0], temp[1], temp[2]);
}
}
}


Il n’est cependant pas très élégant d’avoir à traîner dans sa bibliothèque graphiques deux fonctions différentes pour accomplir sensiblement la même chose. La solution est de créer une fonction qui reçoit une palette de destination, et qui transforme graduellement la palette d’origine en la palette de destination. Nous devons passer en paramètre à cette fonction un tableau de 256x3, représentant les 256 couleurs ainsi que leurs attributs RGB :

 void fadeto(unsigned char pal[256][3])   
{
unsigned char r,g,b;
int terminer = 0; // Flag pour determiner la fin

while (!(terminer)) // Avons-nous terminer?
{
terminer = 1;
syncretrace();
for (int coul=0;coul<256;coul++) // Parcourir les 256 couleurs
{
lirepal(coul,r,g,b); // Lire les attributs de la couleur

if (r > pal[coul][0]) // Traiter le cas du rouge
{
r--; // Si plus grand
terminer=0;
}
else if (r < pal[coul][0])
{
r++; // Si plus petit
terminer=0;
}

if (g > pal[coul][1]) // Traiter le cas du vert
{
g--;
terminer=0;
}
else if (g < pal[coul][1])
{
g++;
terminer=0;
}

if (b > pal[coul][2]) // Traiter le cas du bleu
{
b--;
terminer=0;
}
else if (b < pal[coul][2])
{
b++;
terminer=0;
}
setpal(coul,r,g,b); // Modifier la palette
}
}
}


Le principe est de parcourir les 256 couleurs tant que la valeur courante de la couleur d’origine est différente de celle de destination. Selon le cas, nous augmentons ou diminuons cette valeur. La variable terminer nous indique si toutes les couleurs sont en places, et permet de quitter la fonction.

Une façon très facile de créer des animations simplement à partir de la palette est l’utilisation de ce qu’on appelle la rotation de palette. C’est un procédé qui consiste à boucler dans les couleurs d'une palette pour créer une effet de "vague". On remplace la couleur courante par la couleur qui le précédait, et le suivant par ce dernier, ainsi de suite dans une boule :

 void rotationpal()   
{
unsigned char r,g,b;
do
{
for(unsigned char rgb=0;rgb<64;rgb++)
{
lirepal(rgb+1,r,g,b);
setpal(rgb+1,r+1,0,0); // Augmente le rouge
lirepal(rgb+64,r,g,b);
setpal(rgb+64,0,g+1,0); // Augmente le vert
lirepal(rgb+128,r,g,b);
setpal(rgb+128,0,0,b+1); // Augmente le bleu
}
} while (!kbhit());
}


Cette exemple effectue la rotation dans toute la palette, mais il est possible de cibler seulement quelques couleurs en particulier. Cet effet est fréquemment utilisé car il demande peu de travail côté CPU et donne un résultat très satisfaisant.

Avec ces instructions de base, nous pouvons construire de nombreuses applications pour la palette. En effet, en créant des dégradés de tons de couleurs semblables, nous pouvons réalisé des effets tels des vagues, simuler du feu, et autres petites animations. En résumé, nous avons vu :

  • Comment lire et écrire dans la palette VGA 8-bit par l’intermédiaire du DAC
  • Comment synchroniser nos programmes avec le retour de balayage vertical
  • Utiliser la palette pour des effets de fade ainsi que pour des rotations
  • L’opérateur logique & et son utilité sur les bits


2dchap3.cpp

  
//----------------------------------------------------------------------//
// FICHIER : 2DCHAP3.CPP //
// AUTEUR : Shaun Dore //
// DESCRIPTION : La palette VGA 8-bit //
// DATE DE MODIFICATION : 10-01-98 //
// COMPILATEUR : Borland Turbo C++ Real Mode 16-bit compiler //
// NOTES : Compiler avec modele memoire Compact //
//----------------------------------------------------------------------//

//----------------------------------------------------------------------//
// Fichiers include //
//----------------------------------------------------------------------//

#include <mem.h>
#include <conio.h>
#include <stdio.h>

//----------------------------------------------------------------------//
// Variables globales //
//----------------------------------------------------------------------//

char *ecran = (char *) (0xA0000000L); // Pointeur sur RAM video

//----------------------------------------------------------------------//
// setmode - Appelle le mode passer en parametre //
//----------------------------------------------------------------------//
void setmode(unsigned int mode)
{
asm {
MOV AX, [mode]
INT 0x10
}
}

//----------------------------------------------------------------------//
// cls - vide l'ecran //
//----------------------------------------------------------------------//
void cls()
{
memset(ecran,0,64000L);
}

//----------------------------------------------------------------------//
// putpixel - Affiche un pixel directement dans la memoire //
//----------------------------------------------------------------------//
void putpixel(int x, int y, unsigned char coul)
{
ecran[(y*320)+x] = coul;
}


//----------------------------------------------------------------------//
// syncretrace - attend un retour de balayage vertical //
//----------------------------------------------------------------------//
void syncretrace()
{
while (inp(0x3DA) & 8); // Attend la fin d'une retrace verticale
while (!(inp(0x3DA) & 8)); // ainsi que le debut d'une autre
}



//----------------------------------------------------------------------//
// setpal - fixe les attributs r,g,b //
//----------------------------------------------------------------------//
void setpal(unsigned char coul,unsigned char r,unsigned char g,unsigned char b)
{
outp(0x03C8,coul); // Envoie les informations au DAC
outp(0x03C9,r);
outp(0x03C9,g);
outp(0x03C9,b);
}


//----------------------------------------------------------------------//
// lirepal - Lit les informations sur les attributs r,g,b //
//----------------------------------------------------------------------//
void lirepal(unsigned char coul,unsigned char &r,unsigned char &g,unsigned char &b)
{
outp (0x03C7, coul); // Recoit les informations du DAC
r = inp(0x03C9);
g = inp(0x03C9);
b = inp(0x03C9);
}

//----------------------------------------------------------------------//
// fadeto - Transforme la palette courante selon les attributs de pal //
//----------------------------------------------------------------------//
void fadeto(unsigned char pal[256][3])
{
unsigned char r,g,b;
int terminer = 0; // Flag pour determiner la fin

while (!(terminer)) // Avons-nous terminer?
{
terminer = 1;
syncretrace();
for (int coul=0;coul<256;coul++)// Parcourir les 256 couleurs
{
lirepal(coul,r,g,b); // Lire les attributs de la couleur

if (r > pal[coul][0]) // Traiter le cas du rouge
{
r--; // Si plus grand
terminer=0;
}
else if (r < pal[coul][0])
{
r++; // Si plus petit
terminer=0;
}

if (g > pal[coul][1]) // Traiter le cas du vert
{
g--;
terminer=0;
}
else if (g < pal[coul][1])
{
g++;
terminer=0;
}

if (b > pal[coul][2]) // Traiter le cas du bleu
{
b--;
terminer=0;
}
else if (b < pal[coul][2])
{
b++;
terminer=0;
}
setpal(coul,r,g,b); // Modifier la palette
}
}
}

//----------------------------------------------------------------------//
// preparepal - Initialise notre palette de couleur //
//----------------------------------------------------------------------//
void preparepal()
{
for(int coul=1;coul<65;coul++) setpal(coul,coul,0,0); // Rouge
for(coul=65;coul<130;coul++) setpal(coul,0,coul,0); // Vert
for(coul=130;coul<195;coul++) setpal(coul,0,0,coul); // Bleu
}


//----------------------------------------------------------------------//
// barreRGB - Rempli l'ecran avec des barres de degrade de rgb //
//----------------------------------------------------------------------//
void barreRGB()
{
for(short y=0;y<195;y++)
for(short x=0;x<319;x++)
putpixel(x,y,y);
}

//----------------------------------------------------------------------//
// rotationpal - Modifie tour a tour chacunes des couleurs des barres //
//----------------------------------------------------------------------//
void rotationpal()
{
unsigned char r,g,b;
do
{
for(unsigned char rgb=0;rgb<64;rgb++)
{
for (unsigned int ralenti=0;ralenti<16000;ralenti++) {}
lirepal(rgb+1,r,g,b);
setpal(rgb+1,r+1,0,0); // Augmente le rouge
lirepal(rgb+64,r,g,b);
setpal(rgb+64,0,g+1,0); // Augmente le vert
lirepal(rgb+128,r,g,b);
setpal(rgb+128,0,0,b+1); // Augmente le bleu
}
} while (!kbhit());
}


//----------------------------------------------------------------------//
// scanbarres - Pour chaque couleur, change sa valeur a violette //
//----------------------------------------------------------------------//
void scanbarres()
{
unsigned char temp[3]; // Pour sauvegarder la couleur precedente
unsigned char paltmp[256][3]; // Pour sauvegarder la palette initiale
unsigned char palnoir[256][3];
int y=2,dir=1;

for (int i=0; i<256;i++)
{
palnoir[i][0] = 0; palnoir[i][1] = 0; palnoir[i][2]=0;
lirepal(i,paltmp[i][0], paltmp[i][1], paltmp[i][2]);
setpal(i,0,0,0);
}

barreRGB(); // Dessine nos barres rouge verte & bleue (en noir!)
fadeto(paltmp);

do
{
if (y==195) dir = -1; // On repart vers le haut
if (y==2) dir = 1; // On repart vers le bas!
y+=dir; // Deplace la ligne mauve
syncretrace(); // Attend retrace verticale
lirepal(y,temp[0], temp[1], temp[2]); // Sauvegarde la couleur
setpal(y-dir,temp[0],temp[1],temp[2]); // Restore la couleur precedente
setpal(y,30,0,50); // Modifie la couleur a violet

} while (!kbhit());

fadeto(palnoir); // Petit fade-out!
for(i=0;i<256;i++) setpal(i,paltmp[i][0],paltmp[i][1],paltmp[i][2]);
getch(); // On ramene la palette originale
rotationpal();
for(i=0;i<256;i++) setpal(i,paltmp[i][0],paltmp[i][1],paltmp[i][2]);
}

//----------------------------------------------------------------------//
// retrace - Utilite de la fonction syncretrace //
//----------------------------------------------------------------------//
void retrace()
{
unsigned char r,g,b;

for(unsigned char x=130;x<190;x++)
for(unsigned char y=0;y<200;y++)
putpixel(x,y,x);

setpal(7,60,60,60);

gotoxy(1,1);printf("Test de rotation");
gotoxy(4,2);printf("de palette");
gotoxy(1,8);printf("Avec syncretrace");
getch();

do
{
syncretrace();
for(x=130;x<190;x++)
{
lirepal(x,r,g,b);
setpal(x,0,0,b+1);
}
} while (!kbhit());
getch();
gotoxy(1,8);printf("Sans syncretrace");

do
{
for(x=130;x<190;x++)
{
lirepal(x,r,g,b);
setpal(x,0,0,b+1);
}
} while (!kbhit());

}

//----------------------------------------------------------------------//
// Fonction MAIN //
//----------------------------------------------------------------------//

void main()
{
setmode(0x13);
gotoxy(5,12);printf("<Appuyez sur RETOUR pour debuter>");getch();
preparepal();
scanbarres(); // Animation par changement de couleurs
cls();
getch();
retrace(); // Demonstration de syncretrace()

setmode(0x03);
printf("Shaun Dore\ndores@videotron.ca\n http://pages.infinit.net/shaun ");
}

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT