Copy Link
Add to Bookmark
Report

2d Chapitre 5 - Charger une image graphique

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

CHAPITRE 5 - Charger une image graphique

Dans ce chapitre, nous allons voir comment charger des images à partir du disque et comment décoder une image compressé avec l’algorithme Run-Length encoding. Ils existent de nombreux formats graphiques, et dans le cadre de ce cours nous verrons le format .PCX de Zsoft et le format bitmap, le BMP. Nous ajouterons à notre bibliothèque de fonctions graphiques la capacité de charger des images.


FICHIERS PCX

Si les données graphiques n’étaient jamais compressé, les jeux se distribueraient sur 15 CDROM plutôt qu’un seul. En effet les données graphiques comptent pour une très bonne partie de la place occupé par un jeu sur un disque. Heureusement, il existe plusieurs algorithmes de compression pour réduire la taille de ces bitmaps. Tout le monde connaît le format .GIF ou le .JPEG. Ces algorithmes sont très complexes. C’est donc pourquoi j’ai choisi le format PCX de Zsoft. Le format PCX se sert du Run-Length Encoding, un algorithme qui ne sert pas seulement aux graphiques. Son principe est très simple. Il consiste à grouper les suites d'octets semblables. Par exemple, si dans un fichier non compressé se trouve 15 octets ayant la valeur "1", le RLE va compresser ces octets, afin que dans le fichiers se retrouve seulement une indication que les 15 prochains octets sont des "1". Regarder cet exemple :

5 2 3 5 3 7 2 1 1 1 1 1 1 1 1 1 5 1 8 6

Supposons que ces chiffres représentent un bitmap. Dans cette série, il y a 9 octets «1» qui se suivent. La compression RLE va grouper ces 9 octets dans 1 seul. Comment procède - t’il? C’est très simple. Il va ajouter un octet devant le premier «1», qui aura la valeur hexadécimale C0, soit 1100 0000 en binaire (les 2 premiers bits à 1). Les 6 derniers bits seront mis à 8, et il enlèvera les huit « 1 ». Le décodeur saura alors que le prochain octet est encodé autant de fois qu’il est indiqué dans les 6 derniers bit de l’octet d’encodage C0. Concrètement, il suffit de:

  1. Vérifier si l’octet qu’on lit est encodé (si ces 2 premiers bits sont à 1)
  2. Si c’est le cas, les 6 derniers bit deviennent une boucle for qui répétera le prochain octet autant de fois qu’indiqué.
  3. Si ce n’est pas le cas, on affiche l’octet normalement et on continue tant qu’on a pas atteint la fin du fichier.

La fonction loadpcx ci-dessus s’attend à recevoir en paramètres le nom du fichier, la taille du fichier en octets, et l’espace mémoire où sera décoder l’image. La palette se situe à la fin du fichier, de façon linéaire, dans les 768 derniers octets. Il suffit d’ajuster la palette pour obtenir la bonne image.

 int loadpcx(char *nomfich,unsigned long taille,char *image) 
{
unsigned char data, nb_octets, palette[768];
unsigned long index = 0;
unsigned int indexrle;
FILE *fichpcx;

if (!(fichpcx = fopen(nomfich, "rb"))) return 0;
fseek(fichpcx, -768, SEEK_END);
fread(&palette, 768, 1, fichpcx);
for (int coul=0;coul<=255;coul++)
setpal(coul,palette[coul*3]>>2,palette[coul*3+1]>>2,palette[coul*3+2]>>2);
fseek(fichpcx, 128, SEEK_SET);

do
{
fread(&data, 1, 1, fichpcx);
if ((data & 0xC0) == 0xC0)
{
nb_octets = (data & 0x3F);
fread(&data,1,1,fichpcx);
for (indexrle=1;indexrle<=nb_octets;indexrle++) image[index++]=data;
}
else image[index++] = data;
} while(index < taille);

fclose(fichpcx);
return 1;
}


Dans mon exemple, j’ignore totalement l’entête du fichier. Pas très sécuritaire, en fait le seul test qui est fait est à savoir si le fichier existe. Si vous voulez améliorer ce chargeur de PCX, voici pour vous la structure de cet entête:

 Octets  Description                 Longeur 

0 Signature du format 1
1 Version 1
2 Encodage 1
3 Bit par pixel 1
4 XMin, Ymin, XMax, YMax 8
12 Résolution horizontale 2
14 Résolution verticale 2
16 Palette 16 couleurs(EGA) 48
64 Réservé 1
65 Nombre de plans 1
66 Octets par lignes 2
68 1 = Couleur 2 = noir et blanc 2
70 Réservé (0) 58


Notre chargeur PCX ne prendra pas en compte les images plus grande que 320x200. Également, le numéro de version doit être 5 (pour le mode 256 couleurs). Cependant, elle ne conserve pas l'image à l'intérieur de l'exécutable : le fichier doit être dans le même répertoire que celui-ci. Pour accéder aux données graphiques, on utilise le pointeur image, qu’on peut (mem)copier sur l’écran pour affichage.


FICHIERS BITMAPS

Beaucoup plus simple que les fichiers .PCX, les BMP sont probablement le format le plus facile qui existe. Il n’y a pas de compression et les données sont stockés octets par octets. En fait, il n’y a qu’un seul hic : les données sont à l’envers. Il faut donc procéder à l’envers : notre offset de départ se situe à la fin du fichier, et on monte ligne par ligne jusqu'à la fin. La palette se compose de 256 entrée et de 4 attributs. On ignore le dernier, mais encore une fois les trois premiers sont à l’envers! On doit lire non pas r,g,b, mais b,g,r. Le reste est une simple question de parcourir le fichier sans se soucier de compression et de transférer les octets soit sur l’écran ou dans la mémoire.

 int loadbmp(char *nomfich,int largeur,int hauteur,char *image) 
{
unsigned char palette[256][4];
FILE *fichbmp;

if(!(fichbmp = fopen(nomfich, "rb"))) return 0;
fseek(fichbmp,54, SEEK_SET);
fread(&palette, 1024, 1, fichbmp);
for(int coul=0;coul<=255;coul++)
setpal(coul,palette[coul][2]>>2,palette[coul][1]>>2,palette[coul][0]>>2);

unsigned int offset = 64000;
for (int ligne=0;ligne<hauteur;ligne++)
{
fread (image+offset,largeur,1,fichbmp);
offset -= 320;
}
fclose (fichbmp);
return 1;
}


Le format BMP prend naturellement plus d’espace disque mais il brille par sa simplicité d’utilisation. Dans le code source, remarquez que les bitmaps sont chargés directement sur l’écran mais qu’ils auraient été possible de les charger dans un écran virtuel. En résumé, nous avons ajouté dans notre librairie graphique :

  • La capacité de décoder le Run-Length Encoding
  • Le chargement et l’affichage des fichiers PCX
  • Le chargement et l’affichage des fichiers BMP

BABYLON5.PCX
Pin it
BABYLON5.PCX
nuages.bmp
Pin it
nuages.bmp

2dchap5.cpp

  
//----------------------------------------------------------------------//
// FICHIER : 2DCHAP5.CPP //
// AUTEUR : Shaun Dore //
// DESCRIPTION : Charger une image .PCX et .BMP //
// DATE DE MODIFICATION : 19-03-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);

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

//----------------------------------------------------------------------//
// setpal - Modifie la palette //
//----------------------------------------------------------------------//
void setpal(unsigned char coul,unsigned char r,unsigned char g,unsigned char b)
{
outp (0x03C8,coul);
outp (0x03C9,r);
outp (0x03C9,g);
outp (0x03C9,b);
}

//----------------------------------------------------------------------//
// loadpcx - Charge en memoire un fichier .PCX //
//----------------------------------------------------------------------//
int loadpcx(char *nomfich,unsigned long taille,char *image)
{
unsigned char data, nb_octets, palette[768];
unsigned long index = 0;
unsigned int indexrle;
FILE *fichpcx;

if (!(fichpcx = fopen(nomfich, "rb"))) return 0;
fseek(fichpcx, -768, SEEK_END);
fread(&palette, 768, 1, fichpcx);
for (int coul=0;coul<=255;coul++)
setpal(coul,palette[coul*3]>>2,palette[coul*3+1]>>2,palette[coul*3+2]>>2);
fseek(fichpcx, 128, SEEK_SET);

do
{
fread(&data, 1, 1, fichpcx);
if ((data & 0xC0) == 0xC0)
{
nb_octets = (data & 0x3F);
fread(&data,1,1,fichpcx);
for (indexrle=1;indexrle<=nb_octets;indexrle++) image[index++]=data;
}
else image[index++] = data;
} while(index < taille);

fclose(fichpcx);
return 1;
}


//----------------------------------------------------------------------//
// loadbmp - Charge en memoire un fichier .BMP //
//----------------------------------------------------------------------//
int loadbmp(char *nomfich,int largeur,int hauteur,char *image)
{
unsigned char palette[256][4];
FILE *fichbmp;

if(!(fichbmp = fopen(nomfich, "rb"))) return 0;
fseek(fichbmp,54, SEEK_SET);
fread(&palette, 1024, 1, fichbmp);
for(int coul=0;coul<=255;coul++)
setpal(coul,palette[coul][2]>>2,palette[coul][1]>>2,palette[coul][0]>>2);

unsigned int offset = 64000;
for (int ligne=0;ligne<hauteur;ligne++)
{
fread (image+offset,largeur,1,fichbmp);
offset -= 320;
}
fclose (fichbmp);
return 1;
}


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

void main()
{
char *fich1 = "babylon5.pcx";
char *fich2 = "nuages.bmp";
setmode(0x13);

if (!(loadpcx(fich1,64000L,ecran)))
{
setmode(0x03);
printf("ERREUR: incapable de charger %s!",fich1);
return;
}

getch();
memset(ecran,0,64000L);

if (!(loadbmp(fich2,320,200,ecran)))
{
setmode(0x03);
printf("ERREUR: incapable de charger %s!",fich2);
return;
}
getch();
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