Copy Link
Add to Bookmark
Report
Tutorial Assembleur - Chapitre 10
TUTORIAL ASSEMBLEUR - chapitre 10
---------------------------------
Image format PCX - Effet de Fade-Out
------------------------------------
Le format PCX
-------------
Nous avons vu dans le précedent chapitre, la manipulation des images en
format RAW, c'est à dire binaire. Cependant, il est intéressant de jeter un
coup d'oeil vers les images dites compressées. Les formats sont nombreux et
leurs performances sont diverses. Un des formats les plus simples est le
format PCX. Celui-ci a été crée par ZSOFT pour alléger la taille des images.
L'algorithme est basé sur le RLE (Run Length Compression). La compression
agit sur les répetitions que l'on peut trouver dans le fichier en écrivant
simplement le nombre de répetitions 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-tête)
-----------------------------------
Le header est la partie qui se trouve au début du fichier. Il donne des
informations sur la taille de l'image, le nombre de couleurs, la palette...
ZSOFT a crée 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-tête :
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 désigner 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 - Résolution horizontale en DPI
Octet 14-15 VDPI - Résolution verticale en DPI
Octet 16-65 Paramètres 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 Interprétation 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 créer 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 déjà la taille de
l'écran (320x200) et nous savons que l'image est en 256 couleurs.
Pas de problèmes, on oublie le header...et on passe à la suite.
Les données compressées et la palette
-------------------------------------
Comme nous savons que les premiers octets sont constitués 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 inférieur à ou égal à 0C0h,
alors il s'agit d'un pixel non compressé. Nous plaçons sa valeur
(sa couleur) directement à l'écran et nous passons à l'octet suivant.
Si l'octet lu est supérieur à 0C0h, alors nous avons affaire à une
compression. Il faut soustraire 0C0h à la valeur de l'octet, ainsi nous
obtenons le nombre de répetitions. L'octet qui suit l'octet de compression,
est la couleur du pixel à répeter. Nous copions X fois (valeur de l'octet de
répetition) 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 inférieur 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 après décompression. Cependant,
il vaut mieux paramétrer la palette avant d'afficher l'image. Il faut
aller à la fin du fichier et "reculer" de 768 octets pour se positionner au
début des données de la palette. Le format de la palette est le
classique R,V,B (rouge-vert-bleu). Chaque composante est codée 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 données
de l'image à proprement parler - les parties déjà vues sont moins
commentées - si vous avez des lacunes, allez voir aux
chapitres qui précèdent :
Start:
mov ax,013h
int 10h
push 0a000h
pop es ;Mode 320x200 - ES:DI = mémoire vidéo
Debut_Palette:
lea si,fin_image ;On se place au début de la palette
sub si,768 ;qui se trouve 768 bytes avant la fin du fichier
Pal_PCX: ;Nous allons lire les données de la palette
xor ax,ax
xor cx,cx ;Mise à zéro pour éviter que cela ne perturbe
;le déroulement 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 séparement
;à 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 après le header qui nous sert à rien
xor di,di ;Et au début de l'écran enfin...de la mémoire vidéo
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 inférieur ou égal à 0C0h
;alors on doit passer à la décompression
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 décompression alors on passe
;directement à l'octet qui suit
Rle: ;Si jamais le pixel était supérieur à 0C0h
;alors il faut décompresser
sub al,0c0h ;on soustrait 0C0h pour retrouver le nombre de répet.
mov bl,ds:[si+1] ;et dans Bl, on met la couleur du pixel à répeter
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 répeté
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 début de l'image
include gill.db ;ici, c'est le nom du fichier PCX converti en données
;ASM
Fin_Image: ;ce label là, pour situer la fin de l'image
Améliorions un peu, avec un effet de FADE-OUT
---------------------------------------------
Vous avez sûrement déjà vu, un effet de FADE-OUT. L'image s'assombrit pour
disparaître dans un écran noir comme si la lumière s'éteignait
progressivement. C'est un effet très simple à faire. Il existe plusieures
méthodes. Certains préfèrent stocker la palette à quelque part.
Nous n'allons pas nous encombrer avec des DB DUP (?)... La méthode que
je vais expliquer, utilise les routines déjà présentées dans le chapitre
sur la palette. Nous utiliserons la procédure SET_COLOR pour mettre une
nouvelle couleur et la procédure 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 à zéro. Dans ce cas, il ne faut pas descendre la valeur
de la couleur pour ne pas tomber dans des nombres négatifs, il s'agit tout
simplement du noir. Si la composante a une valeur supérieure à 0, alors
nous descendons cette valeur de 1. Après avoir traité, les trois composantes
de la couleur, nous renvoyons la nouvelle couleur modifiée par nos soins.
Il suffit de parcourir entièrement la palette 63 fois (1 fois à chaque frame)
puisque la valeur maximale d'une composante est 63.
Nous ajoutons aussi une procédure de syncro avec le rayon du tube
cathodique pour éviter que tout cela tourne trop vite.
Arrêtons la théorie et passons au code pour A86
------------------------------------------------
Vous trouvez ici, juste la routine pour créer 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 à zéro 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 décrémente
Dec_vert: ;pareil pour le vert
cmp b[vert],0
jz dec_bleu
dec b[vert]
Dec_bleu: ;et de même 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 à zéro / 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 ;récupère 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 ###