Image format PCX - Effet de Fade-Out
------------------------------------

Le format PCX
-------------

Nous avons vu dans le prcedent chapitre, la manipulation des images en
format RAW, c'est  dire binaire. Cependant, il est intressant de jeter un
coup d'oeil vers les images dites compresses. Les formats sont nombreux et
leurs performances sont diverses. Un des formats les plus simples est le
format PCX. Celui-ci a t cre par ZSOFT pour allger la taille des images.
L'algorithme est bas sur le RLE (Run Length Compression). La compression
agit sur les rpetitions que l'on peut trouver dans le fichier en crivant
simplement le nombre de rpetitions et la couleur du pixel. On peut
conomiser ainsi jusqu' 90% de la taille du fichier suivant sa complexit.

Le header du format PCX (l'en-tte)
-----------------------------------

Le header est la partie qui se trouve au dbut du fichier. Il donne des
informations sur la taille de l'image, le nombre de couleurs, la palette...
ZSOFT a cre plusieurs types de format PCX - le format 5 est celui destin
aux images de 256 couleurs. Je passe sur les formats 16 couleurs et
2 couleurs, ils sont presque devenus inutiles.

Voici la composition de cette en-tte :

Octet 0-1                Version et fabricant (le plus souvent 5 ou 10)
Octet 2                  Type de compression (0 = non compress / 1 = compress)
Octet 3                  Nombre de bits pour dsigner un pixel, 256 couleurs = 8

Octet 4-5                Valeur de X au minimum
Octet 6-7                Valeur de Y au minimum
Octet 8-9                Valeur de X au maximum
Octet 10-11              Valeur de Y au maximum  
                         (largeur = XMAX-XMIN+1) (hauteur = YMAX-YMIN+1)

Octet 12-13              HDPI - Rsolution horizontale en DPI
Octet 14-15              VDPI - Rsolution verticale en DPI
Octet 16-65              Paramtres palette
Octet 64                 ??? - Doit tre gal  0
Octet 65                 Nombre de plans 
Octet 66-67              Nombre d'octets pour une ligne (doit tre pair)
Octet 68-69              Interprtation Palette / 1=Couleur ou Noir-Blanc 2=Gris

Octet 70-71              Taille horizontale cran en pixels
Octet 72-73              Taille verticale cran en pixels
Octet 74-127             Remplissage par des 0 pour arriver  un header de 128 bytes.

Ce header n'est vraiment utile que si vous voulez crer un viewer qui
doit s'adapter selon la taille de l'image. Dans les programmes en ASM de
cette page, on ne soucie pas du header car l'on connait dj la taille de
l'cran (320x200) et nous savons que l'image est en 256 couleurs.
Pas de problmes, on oublie le header...et on passe  la suite.

Les donnes compresses et la palette
-------------------------------------

Comme nous savons que les premiers octets sont constitus du header,
nous allons les sauter et arriver  l'octet 128 du fichier. Nous allons lire
les octets. Si l'octet en cours de lecture est infrieur  ou gal  0C0h,
alors il s'agit d'un pixel non compress. Nous plaons sa valeur
(sa couleur) directement  l'cran et nous passons  l'octet suivant.

Si l'octet lu est suprieur  0C0h, alors nous avons affaire  une
compression. Il faut soustraire 0C0h  la valeur de l'octet, ainsi nous
obtenons le nombre de rpetitions. L'octet qui suit l'octet de compression,
est la couleur du pixel  rpeter. Nous copions X fois (valeur de l'octet de
rpetition) le pixel de couleur. Pour tre un peu plus clair, voici
l'algorithme en pseudo-code :

Position_Fichier=128
Position_Ecran=0

1. Lire octet A1  la position_fichier

2. Si A1 est infrieur ou gal  0C0h alors rajouter pixel  l'cran
    et Position_Fichier=Position_Fichier+1
    et Position_Ecran=Position_Ecran+1
    Aller au point 4

3. Si A1 > 0C0h alors on a une compression
    Nb_Repet = A1-0C0h
    Lire l'octet A2  la  Position_Fichier+1 (l'octet qui suit)
    Rajouter A1 fois, le pixel de couleur A2 dans l'image
    et Position_Fichier=Position_Fichier+2
    et Position_Ecran=Position_Ecran+A1

4. Recommencer jusqu' ce que Position_Ecran = 64000 (avec image de 320x200)

Voila, nous avons le contenu de l'image aprs dcompression. Cependant,
il vaut mieux paramtrer la palette avant d'afficher l'image. Il faut
aller  la fin du fichier et "reculer" de 768 octets pour se positionner au
dbut des donnes de la palette. Le format de la palette est le
classique R,V,B (rouge-vert-bleu). Chaque composante est code sur
8 bits (0-255) mais la carte VGA n'accepte que 6 bits. Donc il faut
diviser chaque valeur par 4 pour arriver aux 6 bits.
On utilise bien entendu, SHR XXX,2. 

Le code pour A86
----------------

L'exemple complet est present dans PCXEX.zip.
Pour l'instant, il ne peut que lire les fichiers qui sont convertis du
format binaire vers le format DB ou DW (comme l'image du starfield).
Nous verrons certainement plus tard, la gestion des fichiers.
L'image  lire doit faire 320x200 pixels et doit avoir une palette qui
s'tend sur 256 couleurs... Bon, voici le code principal sans les donnes
de l'image  proprement parler - les parties dj vues sont moins
commentes - si vous avez des lacunes, allez voir aux
chapitres qui prcdent :


Start:
mov ax,013h
int 10h
push 0a000h                   
pop es                       ;Mode 320x200 - ES:DI = mmoire vido         

Debut_Palette:
lea si,fin_image             ;On se place au dbut de la palette
sub si,768                   ;qui se trouve 768 bytes avant la fin du fichier  

Pal_PCX:                     ;Nous allons lire les donnes de la palette
xor ax,ax                   
xor cx,cx                    ;Mise  zro pour viter que cela ne perturbe 
                             ;le droulement de la routine
Make_Pal:
mov ah,ds:[si]               ;Bon, on prend tout d'abord la composante ROUGE
mov bx,ds:[si+1]             ;Ensuite, le VERT et le BLEU directement en un WORD
                             ;c'est plus court que de les prendre sparement
                             
                             ; ce stade : AL=index_couleur AH=R BL=V BH=B

shr ah,2                     ;Ok ! On continue en divisant chaque composante          
shr bl,2                     ;par quatre car la carte n'accepte que 6 bits
shr bh,2                     

call SET_COLOR               ;On met la couleur en place
inc al                       ;On va vers index suivant
add si,3                     ;On augmente notre position dans le fichier de 3
                             ;puisque une couleur=3 bytes
cmp si,fin_image             ;Et on regarde si on est  la fin du fichier
jnz Make_Pal                 ;On recommence si on a pas fini

Affiche_1:                   ;Maintenant, on va afficher l'image
lea si,debut_image+128       ;On se place aprs le header qui nous sert  rien
xor di,di                    ;Et au dbut de l'cran enfin...de la mmoire vido

Affiche_2:                   ;Nous prenons un octet du fichier
mov al,ds:[si]               ;dans AL
cmp al,0c0h                  ;et nous regardons  quoi il ressemble
jnbe RLE                     ;s'il n'est pas infrieur ou gal  0C0h
                             ;alors on doit passer  la dcompression

Single:                      ;sinon on l'affiche tout seul et tout simplement
mov es:[di],al               ;sur l'cran  
inc di                       ;on passe au prochain octet pour la suite,
inc si                       ;dans l'cran et dans le fichier
jmp Fin_Decode               ;on ne fait pas de dcompression alors on passe
                             ;directement  l'octet qui suit

Rle:                         ;Si jamais le pixel tait suprieur  0C0h 
                             ;alors il faut dcompresser
sub al,0c0h                  ;on soustrait 0C0h pour retrouver le nombre de rpet.
mov bl,ds:[si+1]             ;et dans Bl, on met la couleur du pixel  rpeter   

Rle_1:                       ;Nous avons AL=nb de repet / BL=couleur du pixel
mov es:[di],bl               ;nous affichons  l'cran, un pixel
dec al                       ;nous descendons le compteur 
inc di                       ;mais passons au pixel suivant sur l'cran    
cmp al,0                     ;on regarde si on a tout rpet
jnz Rle_1                    ;tant qu'on a pas fini, on continue
add si,2                     ;ne pas oublier de passer  la position dans le fichier
                             ;qui se trouve  2 octets de l'actuelle...

Fin_Decode:                  ;on atterit  FIN_DECODE 
cmp di,64000                 ;qui regarde si on s'est fait tout l'cran
jna Affiche_2                ;si on a pas fini, alors on recommence avec
                             ;l'octet qui suit

xor ax,ax 
int 16h                      ;autrement on attend que l'utilisateur appuye sur
                             ;une touche
mov ax,03
int 10h                      ;si il a appuy, on remet le mode texte
ret                          ;et on retourne chez Mr.DOS


SET_COLOR PROC               ;al=index ah=rouge bl=vert bh=bleu  
pusha
mov dx,3c8h
out dx,al
mov dx,3c9h
mov al,ah
out dx,al
mov al,bl
out dx,al
mov al,bh
out dx,al
popa
ret
SET_COLOR ENDP

Debut_Image:                 ;ce label sert  situer le dbut de l'image 

include gill.db              ;ici, c'est le nom du fichier PCX converti en donnes
                             ;ASM

Fin_Image:                   ;ce label l, pour situer la fin de l'image


Amliorions un peu, avec un effet de FADE-OUT
---------------------------------------------

Vous avez srement dj vu, un effet de FADE-OUT. L'image s'assombrit pour
disparatre dans un cran noir comme si la lumire s'teignait
progressivement. C'est un effet trs simple  faire. Il existe plusieures
mthodes. Certains prfrent stocker la palette  quelque part.
Nous n'allons pas nous encombrer avec des DB DUP (?)... La mthode que
je vais expliquer, utilise les routines dj prsentes dans le chapitre
sur la palette. Nous utiliserons la procdure SET_COLOR pour mettre une
nouvelle couleur et la procdure GET_COLOR pour lire la valeur de la couleur. 

Pour assombrir l'image, il suffit de tester chaque composante et de voir
si elle est gale  zro. Dans ce cas, il ne faut pas descendre la valeur
de la couleur pour ne pas tomber dans des nombres ngatifs, il s'agit tout
simplement du noir. Si la composante a une valeur suprieure  0, alors
nous descendons cette valeur de 1. Aprs avoir trait, les trois composantes
de la couleur, nous renvoyons la nouvelle couleur modifie par nos soins.
Il suffit de parcourir entirement la palette 63 fois (1 fois  chaque frame)
puisque la valeur maximale d'une composante est 63.
Nous ajoutons aussi une procdure de syncro avec le rayon du tube
cathodique pour viter que tout cela tourne trop vite. 

Arrtons la thorie et passons au code pour A86
------------------------------------------------

Vous trouvez ici, juste la routine pour crer le fade-out. Elle se trouve  la fin du programme principal avant le retour au mode
texte et au DOS.

xor bp,bp                       ;BP nous sert de compteur
 
Fade_0:
mov b[color],0                  ;on met  zro COLOR qui est l'index en cours de traitement

Fade_1:
call GET_COLOR                  ;on va chercher la couleur dans la carte VGA
                                ;GET_COLOR nous met R,V,B dans les bytes ROUGE, VERT et BLEU


Dec_Rouge:
cmp b[rouge],0                  ;on regarde si la composante rouge = 0
jz dec_vert                     ;si oui, il faut pas descendre sa valeur et on passe  l'autre
                                ;couleur
dec b[rouge]                    ;autrement, tranquille, on dcrmente

Dec_vert:                       ;pareil pour le vert
cmp b[vert],0
jz dec_bleu
dec b[vert]

Dec_bleu:                       ;et de mme pour le bleu   
cmp b[bleu],0
jz Fin_Dec
dec b[bleu]

Fin_Dec:                                             
mov al,b[color]                 ;AL prend la valeur de l'index
mov ah,b[rouge]
mov bl,b[vert]                  ;on place toutes les couleurs dans les registres
mov bh,b[bleu]
call SET_COLOR                  ;et on met notre nouvelle couleur

inc b[color]                    ;on passe  l'index suivant
cmp b[color],0                  ;on regarde si l'on a fait toute la palette 
                                ;au lieu de regarder si on a color>255, on regarde
jnz Fade_1                      ;simplement si c'est gal  zro / 0-255, a fait le tour

inc bp                          ;si on a fait toute la palette, alors il faut faire un
                                ;nouveau tour
call SYNC                       ;on appelle la sync. pour ralentir
cmp bp,63                       ;aurait-on atteint la limite maximale d'une composante ?
jna Fade_0                      ;pas encore alors retour

GET_COLOR PROC                  ;rcupre la couleur de b[COLOR] 
mov dx, 03C7h 
mov al, color
out dx, al
add dx, 2
xor bh, bh
in al, dx
mov bl, al                      ;bl=rouge
in al, dx
mov ah, al                      ;ah=vert
in al, dx
mov dx, bx                      ;al=bleu   ok !!!
mov rouge,bl
mov vert,ah                     ;on place le tout dans les bytes
mov bleu,al
ret                             
GET_COLOR ENDP

SYNC PROC                
mov dx, 3DAh
retrace1:
in al, dx
test al, 8
jz retrace1                     ;attente du retour du faisceau lumineux.
retrace2:
in al, dx
test al, 8
jnz retrace2
ret
SYNC ENDP

### Chapitre 10 - dake / c a l o d o x ###
### http://www.space.ch/scene/calodox ###

