Placage de texture
Par Xavier Michelon


 
 
La bibliothèque JPEG (libjpeg)

La bibliothèque JPEG permet la lecture et l'écriture de fichiers respectant la spécification du format JPEG. Le mode d'emploi complet de la bibliothèque se trouve dans le fichier libjpeg.doc de la distribution source disponible sur [8]. Contrairement à ce que son extension semble indiquer, il s'agit d'un fichier texte. Si vous parcourez ce fichier, vous vous rendrez compte combien le format JPEG et la bibliothèque sont complexes. Rassurez-vous, nous n'utiliserons aujourd'hui que la partie concernant la décompression de fichier JPEG, et dans le code source de notre exemple, tout se trouve dans la fonction loadJpegImage() :

void loadJpegImage(char *fichier)
{
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  FILE *file;	
  unsigned char *ligne;
  int i,j;

  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_decompress(&cinfo);
  if ((file=fopen(fichier,"rb"))==NULL)
    {
      fprintf(stderr,"Erreur : impossible d'ouvrir le fichier texture.jpg\n");
      exit(1);
    }
  jpeg_stdio_src(&cinfo, file);
  jpeg_read_header(&cinfo, TRUE);

  if ((cinfo.image_width!=256)||(cinfo.image_height!=256)) {
    fprintf(stdout,"Erreur : l'image doit etre de taille 256x256\n");
    exit(1);
  }
  if (cinfo.jpeg_color_space==JCS_GRAYSCALE) {
    fprintf(stdout,"Erreur : l'image doit etre de type RGB\n");
    exit(1);
  }

  jpeg_start_decompress(&cinfo);
  ligne=image;
  while (cinfo.output_scanline<cinfo.output_height)
    {
      ligne=image+3*256*cinfo.output_scanline;
      jpeg_read_scanlines(&cinfo,&ligne,1);
    }
  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);
  
  for (i=0;i<256;i++)
    for (j=0;j<256;j++) {
      texture[i][j][0]=image[i*256*3+j*3];
      texture[i][j][1]=image[i*256*3+j*3+1];
      texture[i][j][2]=image[i*256*3+j*3+2];
    }
}

Pour utilisez la bibliothèque JPEG, il faut commencer pas inclure les fichiers d'entête :

#include <jpeglib.h>
#include <jerror.h>

Le processus de décompression nécessite la mise en oeuvre de 2 structures de données nommées cinfo et jerr. La première va contenir les informations concernant la structure de l'image (ses dimensions, son type, ses paramètres de compression...). On initialise cette structure grâce à la fonction jpeg_create_decompress(). La seconde structure, jerr, va servir au traitement des erreurs qui surviendront éventuellement au cours de la décompression. Par un appel à jpeg_stdio_src(), on indique que  les messages d'erreur doivent être envoyés sur la sortie standard. Il convient ensuite d'ouvrir le fichier contenant l'image grâce à un classique fopen(). On prendra soin de vérifier que l'ouverture du fichier s'est bien passée en testant la valeur de retour de fopen(). On indique ensuite que les données concernant l'image à décompresser seront lues depuis le fichier que l'on vient d'ouvrir, avec un appel à jpeg_stdio_src().

A présent, il ne reste plus qu'a lire l'entête du fichier avec jpeg_read_header().  Les informations concernant l'image sont  alors placées dans la structure cinfo. On peut donc tester si celles-ci sont conformes à nos souhaits. Nous voulons une image de taille 256x256 en mode RGB. Si les tests sont passés avec succès, on peut commencer la décompression de l'image par un appel à jpeg_start_decompress(). L'image sera décompressée dans un tableau que nous aurons pris soin de déclarer. Sachant que nous souhaitons lire une image de 256x256 et qu'un pixel contient trois composantes (R,V et B) stockées chacune sur un octet, nous avons besoin d'un tableau de 256x256x3 octets (le type correspondant en C est unsigned char qui peut prendre 256 valeurs différentes, de 0 à 255).

unsigned char image[256*256*3];

Vous remarquerez que nous stockons l'image dans un tableau unidimensionnel  La bibliothèque JPEG ne permet pas de décompresser l'image dans un format qu'accepte OpenGL (il lui faut un tableau à trois dimension où la troisième dimension correspond à la composante couleur R,V,B ou A). Nous serons donc obligés de réorganiser le tableau après la fin de la lecture de l'image. La procédure de décompression se fait par un balayage de ligne avec une boucle « tant que » et un pointeur nommé « ligne » qui indique l'adresse à laquelle doivent être placées les données décompressées. La fonction jpeg_read_scanlines() provoque la décompression d'une ligne de l'image. Une fois la décompression de l'image achevée, il ne reste plus qu'à terminer le processus avec jpeg_finish_decompress() et à libérer la structure cinfo avec jpeg_destroy_decompress().

La double boucle imbriquée qui termine la fonction LoadJpegImage() copie les données du tableau image[] dans un nouveau tableau à 3 dimensions qui pourra être exploité comme une texture par OpenGL. Le tableau image[] contient la suite des composantes R,V et B de chacun des pixels, en parcourant l'image de gauche à droite et de haut en bas. Avec des indices démarrant à 0, la composante rouge du pixel de la ligne d'indice 4 et de la colonne d'indice 12 de l'image se trouve dans image[256*4+12], la composante verte de ce même pixel se trouve dans image[256*4+12+1] et sa composante bleue est dans image[256*4+12+2]. Dans le tableau texture, ces mêmes composantes se trouvent respectivement dans texture[4][12][0], texture[4][12][1], texture[4][12][2].

Figure 3 : la texture de bois