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 ###