Utiliser la librairie gd

Introduction

Présentation

Traduction de la présentation de gd1.8.3 sur le site officiel.

"gd1.8.3 permet la création d'images aux formats png, jpeg et wbmp, mais pas gif. C'est une bonne chose, png est un format plus compact, et la "full compression" est permise. le format JPEG est bien pour les images photographiques et est plus compatible avec les browsers actuels que le format png. le format WBMP sert aux appareils sans fils (ndt: applications wap) mais pas aux browsers classiques. Le code existant basé sur d'anciennes versions de gd qui prenaient en charge le format gif devra être modifié. Il faudra utiliser gdImagePng ou gdImageJpeg en lieu et place des appels à gdImageGif. Ne nous demandez pas de vous fournir d'anciennes versions de gd. Unisys possède une licence sur l'algorithme de compression LZW qui est utilisé pour générer les "images GIF compressées". La meilleure solution est d'adopter des formats modernes, libres, bien compressés tels que PNG et JPEG autant que possible et le plus tôt sera le mieux. "
Nota : Cet article est basé sur la version 1.8.3 de la librairie gd.

Il vous faut

Pour installer la version minimale de gd1.8.3 vous devez avoir sur votre système (les librairies + les .h):

Si vous désirez inclure le support du format jpeg, vous devez également avoir la librairie jpeg6b ou supérieure.
Si vous désirez inclure le support des TrueTypeFonts, vous devez également avoir la librairie Freetype et quelques polices ttf.
Si vous désirez inclure le support de chargement d'images XPM, vous devez avoir X et la librairie XPM sur votre système.

Préparatifs

Trouver le tout

Sur ma machine, j'avais déjà toutes les librairies et fichiers .h requis (sauf gd naturellement et freetype) mais ça ne sera peut être pas le cas pour tout le monde. J'ai donc regroupé ci-dessous les emplacements des choses dont vous pourriez avoir besoin, y compris gd bien sûr.

Compilation et installation

Vous avez à ce stade compilé et installé les pré-requis. Nous sommes donc prêts à aller plus avant.
Commencez par vous placer dans le répertoire où vous avez placé l'archive de gd puis tapez tar xvfz gd-1.8.3.tar.gz. Cela vous créé un répertoire gd-1.8.3. Débarrassez vous de l'archive éventuellement en tapant rm -f gd-1.8.3.tar.gz.
Placez vous dans le répertoire en tapant cd gd-1.8.3. Editez le makefile en fonction des librairies dont vous disposez et de leurs emplacements.

1. Mettez un '#' devant les lignes commençant par LIBS puis tapez la vôtre comme suit :
LIBS=-lm -lgd -lpng -lz [-ljpeg] [-lttf] [-lXpm -lX11]
Expliquons ce que les options sont :

En clair, la ligne libs minimale pour réaliser des images png est LIBS=-lm -lgd -lpng -lz. C'est ce qui est saisi par défaut donc si cela vous va, ne changez rien à cette ligne.

2. Editez les lignes INCLUDEDIRS et LIBDIRS si vos librairies ne se trouvent pas dans les répertoires saisis par défaut.

3. Editez les lignes INSTALL_LIB, INSTALL_INCLUDE et INSTALL_BIN si les valeurs par défaut ne vous conviennent pas (respectivement /usr/local/lib, /usr/local/include et /usr/local/bin). Il est à noter que, généralement, ces répertoires ne sont pas parcourus par défaut. Donc, si vous êtes root et que vous voulez rendre accessible gd à tous sans vous poser de questions, remplacez ces lignes par :
INSTALL_LIB=/usr/lib
INSTALL_INCLUDE=/usr/include
INSTALL_BIN=/usr/bin

4. Mettez un '#' devant les lignes commençant par CFLAGS puis tapez la vôtre comme suit:
CFLAGS=-O [-DHAVE_XPM] [-DHAVE_JPEG] [-DHAVE_LIBTTF]
Expliquons :

Comme il est dit dans le Makefile, les lignes du dessous ne vous regardent pas. Vous pouvez quand même les modifier si vous voulez, notamment pour corriger le numéro de version ;-).

A ce point vous pouvez taper make. Tout devrait bien se passer. Continuer en tapant make install si vous êtes root ou si vous avez les droits en écriture dans les répertoires spécifiés dans l'étape 3. ci-dessus. Sinon, ce n'est pas grave, il vous suffira d'indiquer au compilateur où trouver les librairies et fichiers d'en-tête quand vous compilerez vos applications.

Utilisation

Je considère ici que vous avez installé les librairies dans des répertoires habituels, où le compilateur et le linker peuvent trouver les fichiers nécessaires.
Dans le cas contraire, créez un répertoire de travail où vous saisirez les exemples, copiez y le répertoire gd-1.8.3 et lors de la compilation, ajoutez après gcc -o expl_gd_x les options -I./gd-1.8.3 -L./gd-1.8.3.

Préambule

Avec gd vous pouvez créer vos images entièrement, à partir de rien, où vous servir d'images existantes. Les exemples simplistes ci-dessous vous montrent comment faire. Si vous avez déjà utilisé php pour réaliser des images, vous ne serez pas dépaysés par la syntaxe ;-).

Exemple1

Dans cet exemple nous allons créer une image simple, il s'agira d'une croix rouge sur fond blanc. Notez qu'il faut, dans l'ordre :

/*
 * Fichier expl_1.c
 */
#include <stdlib.h> /* Y'en aura bien besoin (pour le exit) */

#include <gd.h> /* On va utiliser gd */
/* Théoriquement il faut inclure stdio.h MAIS c'est fait dans gd.h donc ... */

int main(void) {
  gdImagePtr image; /* Pointeur vers notre image */
  FILE *image_png; /* Fichier image PNG */
  int rouge, blanc; /* Deux couleurs */

  /* On créée un image de 100 par 100 */
  image = gdImageCreate(100, 100);

  /*
   * On "alloue" deux couleurs.
   * Notez que la première sera la couleur de fond
   *
   * En paramètres : - l'image
   *                 - les valeurs de rouge, vert et bleu (de 0 à 255)
   */
  blanc = gdImageColorAllocate(image, 255, 255, 255);
  rouge = gdImageColorAllocate(image, 255, 0, 0);

  /*
   * On trace la croix, deux rectangles pleins qui se croisent
   * le premier de x=20,y=45 à x=80,y=55
   * le second de x=45,y=20 à x=55,y=80
   * tous les deux de couleurs rouge.
   */
  gdImageFilledRectangle(image, 20, 45, 80, 55, rouge);
  gdImageFilledRectangle(image, 45, 20, 55, 80, rouge);

  /* Ouvrir le fichier image en écriture. */
  image_png = fopen("expl1.png", "w");

  /* Créer l'image au format PNG. */
  gdImagePng(image, image_png);

  /* On ferme le fichier de l'image. */
  fclose(image_png);

  /* On détruit l'image. */
  gdImageDestroy(image);

  exit (0);
}
On compile en tapant
gcc -o expl_gd_1 expl_1.c -lgd -lpng
ou
gcc -o expl_gd_1 expl_1.c -I./gd-1.8.3 -L./gd-1.8.3 -lgd -lpng (Voir plus haut)
Après exécution par ./expl_gd_1, vous obtenez dans le répertoire courant une image expl1.png.

Nous avons vu ici la fonction permettant de créer des rectangles pleins. Mais, nous pouvons également avec gd créer des rectangles vides, des lignes continues où non (tirets, style défini par l'utilisateur), des polygones pleins ou non, des arcs et ce en utilisant des lignes basiques ou en créant des "pinceaux" (brushes) ou bien encore ajouter du texte ...

Exemple2

Nous allons maintenant écrire, en noir, en utilisant les polices par défaut de gd, contenues dans les fichiers fournis dans l'archive. Ce sont les fichiers gdfont*.h. A ces fichiers correspondent les polices gdFontTiny, gdFontSmall, gdFontMediumBold, gdFontLarge et gdFontGiant
Dans cet exemple, on va générer une image de la taille requise par les textes, ni plus ni moins. La hauteur et la largeur d'un caractère étant données par gdFontxxx->h et gdFontxxx->w.
Si on souhaite écrire verticalement, on utilisera la fontion gdImageStringUp.
Si on souhaite utiliser les polices TrueType on pourra se référer à la fonction gdImageStringTTF. Attention, pour les polices TT, les prototypes de fonctions sont différents et la récupération de l'espace occupé ne se fait pas de la même manière. D'autre part, les polices TT ne sont pas fournies dans l'archive gd, faites un find / -name *.ttf pour rechercher les polices sur votre système.

/*
 * Fichier expl_2.c
 */
#include <stdlib.h> /* Y'en aura bien besoin (pour le exit) */

#include <gd.h> /* On va utiliser gd */
/* Théoriquement il faut inclure stdio.h MAIS c'est fait dans gd.h donc ... */

#include <gdfontt.h> /*on va utiliser la police gdFontTiny */
#include <gdfonts.h> /*on va utiliser la police gdFontSmall */
#include <gdfontmb.h> /*on va utiliser la police gdFontMediumBold */
#include <gdfontl.h> /*on va utiliser la police gdFontLarge */
#include <gdfontg.h> /*on va utiliser la police gdFontGiant */

int main(void) {
  gdImagePtr image; /* Pointeur vers notre image */
  gdFontPtr mesPolices[5]; /* tableau des polices */
  FILE *image_png; /* Fichier image PNG */
  int blanc, bleu; /* Nos deux couleurs */
  char *message = "Hello World !"; /* Un message original */
  int long_message; /* La taille du message */
  int tmp_larg, larg = 0, haut = 40; /* Dimension de l'image */
  int posx = 10, posy = 10; /* Position du texte */
  int i; /* Ca ressemble à une variable pour une boucle for ;-) */

  /* on remplit le tableau */
  mesPolices[0] = gdFontTiny;
  mesPolices[1] = gdFontSmall;
  mesPolices[2] = gdFontMediumBold;
  mesPolices[3] = gdFontLarge;
  mesPolices[4] = gdFontGiant;

  /* On calcule la longueur du message */
  long_message = strlen(message);

  /* On calcule les dimensions de l'image */
  for (i=0 ; i<5 ; i++) {
  	haut += mesPolices[i]->h;
  	larg = ( larg < ( tmp_larg = long_message*mesPolices[i]->w + 20 ) ) ? tmp_larg : larg;
  }

  /* On créée un image de larg par haut */
  image = gdImageCreate(larg, haut);

  /* On alloue deux couleurs. */
  blanc = gdImageColorAllocate(image, 255, 255, 255);
  bleu = gdImageColorAllocate(image, 0, 0, 88);

  /*
   * On écrit le texte avec les cinq polices en bleu.
   * On remet à jour la hauteur
   */
  for (i=0 ; i<5 ; i++) {
  	posy += mesPolices[i]->h;
  	gdImageString(image, mesPolices[i], posx, posy, message, bleu);
  }

  /* Ouvrir le fichier image en écriture. */
  image_png = fopen("expl2.png", "w");

  /* Créer l'image au format PNG. */
  gdImagePng(image, image_png);

  /* On ferme le fichier de l'image. */
  fclose(image_png);

  /* On détruit l'image. */
  gdImageDestroy(image);

  exit (0);
}
On compile en tapant
gcc -o expl_gd_2 expl_2.c -lgd -lpng
Après exécution par ./expl_gd_2, vous obtenez dans le répertoire courant une image expl2.png.

Exemple3

Nous savons à présent créer des images mais il peut être utile d'en utiliser une existante qui servira de base à notre création afin de créer un histogramme sur un fond dégradé, pour rendre une couleur d'image transparente où créer des boutons personnalisés sur une page web.

Prenons ce cas de figure comme exemple :
Vous avez une page qui permet de contacter les 10 personnes les plus actives d'un forum en cliquant sur un bouton contenant leur pseudo. Vous ne pouvez pas à l'avance savoir qui sera dans les 10 personnes ou non, vous avez donc un script qui vous donne les dix personnes et un lien vers eux. Examinons ce qu'il faut faire pour générer un bouton contenant leur nom.

Jusqu'à maintenant on a créé les images avec gdImageCreate. Il existe aussi :

Ces fonctions permettent de récupérer, pour traitement, une image existante. Nous allons travailler à partir d'un fichier image PNG contenant le bouton, vide, bout_in.png . Je sais, ce n'est pas beau mais le but est de montrer ce que l'on peut faire, pas de faire beau. On pourra améliorer cela en partant d'un plus beau bouton et en utilisant une police TrueType pour le texte.
On va créer un cgi, pour changer. Il enverra l'image sur le flux standard, précédée de l'entête adéquate, ici Content-type: image/png suivi de deux retours à la ligne, comme le veux la norme.
/*
 * Fichier expl_3.c
 */
#include <stdlib.h> /* Y'en aura bien besoin (pour le exit) */

#include <stdlib.h> /* Pour le getenv */

#include <gd.h> /* On va utiliser gd */
/* Théoriquement il faut inclure stdio.h MAIS c'est fait dans gd.h donc ... */

#include <gdfontmb.h> /* On va utiliser la police gdFontMediumBold */

int main(void) {
  gdImagePtr image; /* Pointeur vers notre image */
  FILE *image_png_in; /* Fichier image PNG */
  int jaune; /* Notre couleur */
  char *texte;
  int long_texte; /* La taille du texte */

  /* récupère le texte */
  if ( !(texte = getenv("QUERY_STRING")) ) {
  	texte = "Erreur !";
	}
  long_texte = strlen(texte);

  /* On ouvre l'image initiale */
  if ( !(image_png_in = fopen ("bout_in.png", "rb")) ) {
  	fprintf (stderr, "Impossible de trouver l'image bout_in.png.\n\n");
  	exit (1);
  }
  image = gdImageCreateFromPng(image_png_in);

  /* On ferme le fichier de l'image. */
  fclose(image_png_in);

  /* On alloue une couleur. */
  jaune = gdImageColorResolve(image, 0xff, 0xff, 0);

  /*
   * On écrit le texte
   */
  gdImageString(image, gdFontMediumBold,
                (image->sx-long_texte*gdFontMediumBold->w)/2,
                (image->sy-gdFontMediumBold->h)/2,
                texte, jaune);

  /* Envoyer l'image au format PNG. */
  printf ("Content-type: image/png\n\n");
  gdImagePng(image, stdout);

  /* On détruit l'image. */
  gdImageDestroy(image);
  exit (0);
}
On compile en tapant
gcc -o expl_gd_3 expl_3.c -lgd -lpng
On place le binaire obtenu et un fichier contenant le bouton vide (bout_in.png) dans le répertoire cgi-bin de son serveur web.
Pour utiliser ce que l'on vient de faire, il suffit d'inclure dans un fichier html (ce fichier peut être généré par un cgi, vous me suivez ?), un tag img, en passant le texte au script en "query_string". Et Hop !
Exemple :
<html>
<body bgcolor="#ffffff" text="#000088">
<p><b>Mes boutons : </b></p>
<p><img src="/cgi-bin/expl_gd_3?Zazou"></p>
<p><img src="/cgi-bin/expl_gd_3?Xavier"></p>
<p><img src="/cgi-bin/expl_gd_3?alaide"></p>
</body>
</html>
On obtient alors une page semblable à celle-ci :

Aller plus loin

J'ai présenté ici quelques fonctionnalités de la librairie gd. Cela vous aura, j'espère, donné envie de creuser plus avant par vous mêmes. D'autres fonctionnalités dont je n'ai pas parlé sont la copie de portions d'images, l'ouverture de portions d'images uniquement avec changement d'échelle à la volée, fonctionnalités très utiles pour se lancer dans la génération d'extraits de grosses images (photos aériennes ou satellitaires ou je ne sais quoi d'autre). Pour plus d'informations et pour la liste complète des fonctions de lalibrairie, consultez le fichier index.html de l'archive de gd ou la page officielle : http://www.boutell.com/gd/

J'ai placé les images, sources et toutes les archives nécessaires à la réalisation de ces exemples ainsi que les 3 binaires obtenus sur cette page. Attention, les versions des archives seront sans doute vite obsolètes mais ce sont celles utilisées pour cet article.

Pour info, si vous voulez tester les binaires, il vous faudra (en plus de la libc et ld-linux que vous avez) :

Comme d'habitude, j'ai "appris" ça en tapant ldd expl_gd_1, ldd expl_gd_2 et ldd expl_gd_3 dans ma console préférée.

a+

Auteur : Xavier GARREAU
Modifié le 14.09.2004