Génération d'images avec PHP

• La librairie graphique
• Installation et configuration
• Rééchantillonage d'une image
• Mode Batch
• Dessin d'une chaîne de caractères
• Tracés
• Transparence
• Effets

 

La librairie graphiqueHaut


Sous Windows, la librairie graphique se nomme "php_gd.dll". Elle permet de manipuler des images au format PNG, JPEG, WBMP et SWF.

  • Le format Gif a été abandonné pour des raisons de copyright. Par contre, le PNG le remplace avantageusement, prenant en charge les couleurs 8 bits et, ce qui est plus important, 24 bits. Une image PNG 24 bits préservera la netteté du dessin et la transparence du fond (Gif), ainsi que la subtilité des couleurs (Jpeg), sans perte de données.
  • Le format JPEG est le plus utilisé sur le Web, pour son taux de compression élévé (avec perte de données).
  • Le format WBMP est particulier au WAP.
  • Le format SWF nécessite le plug-in Flash.

 

Installation et configuration


La librairie php_gd.dll se trouve dans le package complet de PHP 4. Vous pouvez télécharger la dernière version de GD ici. Pour charger automatiquement la dll, enlevez simplement le ";" en début de ligne dans le fichier php.ini (le fichier de configuration de PHP, situé dans WINNT ou Windows), section "Dynamic Extensions". Assurez-vous également que le chemin du répertoire contenant la dll est correct, dans la section "Paths and Directories", ex : "extension_dir = C:\php\extensions"

Rééchantillonnage d'une image


Une des utilisations les plus interessantes de GD est le rééchantillonnage d'images. Dans cet exemple, nous allons retailler une image JPEG à 50% de sa taille originale.
Le principe est le suivant :

  1. créer une image noire en vraies couleurs de la taille de l'image de destination
  2. copier et rééchantillonner les pixels de l'image originale
  3. générer l'image dans le flux de sortie standard (navigateur)

<?
Header("Content-type: image/jpeg");
$src_im = ImageCreateFromJpeg("palmier.jpg");
$src_w = imageSX($src_im);
$src_h = imageSY($src_im);
$dst_w = floor($src_w * 0.5);
$dst_h = floor($src_h * 0.5);
/* ImageCreateTrueColor crée  une image noire en vraie couleurs */
$dst_im = ImageCreateTrueColor($dst_w,$dst_h);
/* ImageCopyResampled copie et rééchantillonne l'image originale*/
ImageCopyResampled($dst_im,$src_im,0,0,0,0,$dst_w,$dst_h,$src_w,$src_h);
/* ImageJpeg génère l'image dans la sortie standard (c.à.d le navigateur).
Le second paramètre est optionnel ; dans ce cas, l'image est générée dans un fichier*/
ImageJpeg($dst_im);
ImageDestroy($dst_im);
imageDestroy($src_im);
?>

L'île au trésor     L'île au trésor sur mon journal de bord

Si vous souhaitez contraindre le rééchantillonnage à une valeur fixe en hauteur ou en largeur, vous pouvez procéder ainsi :

<?
$size = GetImageSize("palmier.jpg");
$src_w = $size[0];
$src_h = $size[1];
$dst_w = 150; // Contraint le rééchantillonage à une largeur fixe
$dst_h = round(($dst_w / $src_w) * $src_h); // Maintient le ratio de l'image ?>

 

Mode Batch


Pour utiliser cet exemple en mode batch (traitement par lots), il suffit de modifier le script pour en faire une fonction. On parcourt ensuite le répertoire contenant les images originales, et, à chaque image Jpeg rencontrée, on appelle la fonction redim() en lui passant en second argument le répertoire de destination.

<?
function redim($im_src,$im_dest) {
           $size = GetImageSize($im_src);
           $src_w = $size[0];
           $src_h = $size[1];
           $dst_w = floor($src_w * 0.5);
           $dst_h = floor($src_h * 0.5);
           $dst_im = ImageCreateTrueColor($dst_w,$dst_h);
           $src_im = ImageCreateFromJpeg($im_src);
           ImageCopyResampled($dst_im,$src_im,0,0,0,0,$dst_w,$dst_h,$src_w,$src_h);
           ImageJpeg($dst_im,$im_dest);
           ImageDestroy($dst_im);
           imageDestroy($src_im);
 }

$dst_dir = "thumbnails"; // le répertoire qui stockera les images redimensionnées
if(!file_exists($dst_dir)) 
           mkdir($dst_dir,0700); // crée le répertoire s'il n'existe pas
$src_dir = ".";
$dir = opendir($src_dir);
while($im = readdir($dir))
{
// ne prend que les images Jpeg
           if(strtolower(substr($im)),-3) == "jpg" || strtolower(substr($im,-4)) == "jpeg") { 
                      redim($im,$dst_dir."/50_".$im); // préfixe le nom des images avec "50_"
                      $i++;
           } 
}
closedir($dir);
?>

 

Dessin d'une chaîne de caractères


GD ne sert pas seulement à redimensionner des images, mais permet de manipuler du texte. GD écrit le texte soit avec une police par défaut (bitmap), soit une police TrueType ou PostScript.
La fonction ImageString est utilisée pour écrire du texte bitmap sur une image. L'exemple suivant crée une image qui s'adapte en fonction de la dimension du texte.

  • Exemple 1 :
<?
$texte = "Ecriture de texte bitmap avec GD...";
$font_size = 3;
/* Calcule la longueur du texte en pixels */
$font_width = strlen($texte) * ImageFontWidth($font_size);
/* Calcule la hauteur du texte en pixels */
$font_height = ImageFontHeight($font_size);
/* Crée une image de la dimension du texte */
$image = ImageCreate($font_width,$font_height);
/* ImageColorAllocate alloue un fond blanc à l'image */
$white = ImageColorAllocate($image,255,255,255);
/* Définit la couleur du texte */
$black = ImageColorAllocate($image,0,0,0);
/* ImageString écrit le texte dans l'image */
ImageString($image,$font_size,0,0,$texte,$black);
Imagepng($image,"texte_bmp.png");
ImageDestroy($image);
?>

Comme vous pouvez le constater, le texte n'est pas anti-aliasé (lissé) ; de plus, vous n'avez pas vraiment le choix de la police. Le second exemple vous montre comment utiliser une police TrueType de votre choix, ici arial bold italique (que vous pouvez télécharger pour tester l'exemple).
La fonction ImageTtfBBox retourne un tableau de 8 éléments contenant les coordonnées du rectangle qui entoure le texte, ce qui permet d'en calculer les dimensions en pixels. Le texte est tracé avec la fonction ImageTtfText, qui, contrairement à ImageString vue plus haut, prend pour origine le coin inférieur gauche du premier caractère (la ligne de base).

Note : pour cette raison, certaines polices de caractère possédant un jambage important peuvent être coupées à l'affichage. Si le cas se produit, vous devrez faire des essais en augmentant la hauteur de l'image. GD ne possède malheureusement pas de méthode permettant de calculer les valeurs entre la ligne de base et les parties supérieures ou inférieures de lettres comme "p" ou "b", comme en Java par exemple.

  • Exemple 2 :
<?
$corps = 30;
/* Le chemin du fichier de police True Type */
$font = "/WINNT/fonts/arialbi.ttf";
$texte = "Texte TrueType";
/* ImageTtfBBox retourne les dimensions du texte */
$size =ImageTtfBBox($corps,0,$font,$texte);
$dx = abs($size[2]-$size[0]);
$dy = abs($size[5]-$size[3]);
$xpad=10;
$ypad=10; $im = ImageCreate($dx+$xpad,$dy+$ypad); $fond = ImageColorAllocate($im,255,255,255); $couleur = ImageColorAllocate($im,255,210,100); /* ImageTtfText dessine le texte en partant de la ligne de base du premier caractère */ ImageTtfText($im,$corps,0,(int)($xpad/2),$dy-(int)($ypad/2),$couleur,$font,$texte); ImagePng($im,"texte_truetype.png"); ImageDestroy($im); ?>

 

Tracés


Avec GD, vous pouvez également tracer des lignes et tout type de figure géométrique. Ces fonctions sont très utiles pour la génération dynamique de graphiques. Voici un exemple simple d'utilisation de la fonction ImageArc, qui sert habituellement à dessiner des graphiques de type camembert.
Le code suivant crée un masque circulaire dans une image Jpeg. La fonction ImageArc trace des arcs d'ellipse. Puisque nous souhaitons dessiner un cercle, la hauteur et la largeur de l'ellipse devront être identiques, tandis que les valeurs de début et de fin de l'arc seront respectivement 0° et 360 °. Nous utilisons ensuite la fonction ImageFillToBorder pour remplir la région de l'image limitée par la couleur de contour du cercle (noir).

<?
$image = imagecreatefromjpeg("palmier.jpg");
$largeur = imageSX($image);
$hauteur = imageSY($image); $centre_w = $largeur / 2;
$centre_h = $hauteur / 2; $diametre = round(min($largeur,$hauteur) / 2); $noir = imagecolorallocate($image, 0, 0, 0); imagearc($image, $centre_w, $centre_h, $diametre, $diametre, 0, 360, $noir); imagefilltoborder($image, 0, 0, $noir, $noir); $dst_im = imagecreatetruecolor($diametre,$diametre); imagecopy($dst_im,$image,0,0,$centre_w - (int)($diametre / 2), $centre_h - (int)($diametre / 2),$diametre,$diametre); imagejpeg($dst_im,"masque.jpg"); ImageDestroy($image); ImageDestroy($dst_im); ?>

Le trésor n'était pas caché ici.

 

Transparence


Comme le format GIF, le PNG prend en charge la transparence. Dans cette section, nous ne traiterons de la transparence que pour les images en mode couleurs indexées (8 bits). Dans ce mode, chaque pixel peut contenir jusqu'à 256 couleurs (les 2 couleurs, noir et blanc, du mode bitmap élevées à la puissance 8). Une table des couleurs, située dans l'en-tête du fichier, comprend les références RVB des 256 couleurs qui composent l'image. A chaque pixel de l'image est attribué un de ces numéros.

Reprenons la chaine de texte que nous avons dessinée plus haut (avec une police true type). Nous allons changer la couleur de fond de l'image sans toucher au texte. Ensuite, nous rendrons la couleur du texte transparente afin de créer un masque avec la photo du palmier. En mode couleurs indexées, il faut procéder en 2 étapes :

  • retrouver la référence de la couleur que nous souhaitons modifier ou rendre transparente
  • attribuer de nouvelles valeurs RVB à cette couleur

La fonction ImageColorAt permet de retrouver l'index de la couleur d'un pixel. ImageColorSet attribue une couleur à l'index identifié par ImageColorAt, tandis qu'ImageColorTransparent rend transparente la couleur indexée.

Note : le texte a été dessiné sans anti-aliasing pour permettre un détourage plus net. Pour désactiver l'anti-aliasing, donnez un identifiant de couleur négatif à la fonction ImageTtfText.

  • Exemple 1 :
<?
$image = ImageCreateFromPng("texte_truetype.png");
/* ImageColorAt retourne l'index de la couleur du pixel situé aux coordonnées (x, y) 0, 0 */
$color = ImageColorAt($image,0,0);
/* ImageColorSet fixe une couleur à l'index identifié par ImageColorAt */
ImageColorSet ($image, $color, 0, 130, 255);
Imagepng($image,"fond_couleur.png");
ImageDestroy($image);
?>

  • Exemple 2 :
<?
$texte = ImageCreateFromPng("texte_truetype.png");
$image = ImageCreateFromPng("palmier.png");
$largeur_texte = imageSX($texte);
$hauteur_texte = imageSY($texte);
$largeur_image = imageSX($image);
$hauteur_image = imageSY($image);
$coord_x = floor(($largeur_image - $largeur_texte) / 2);
$coord_y = floor(($hauteur_image - $hauteur_texte) / 2);
$index = imagecolorexact($texte,255,210,100);
ImageColorTransparent($texte,$index);
imagecopy($image, $texte, $coord_x, $coord_y, 0, 0, $largeur_image, $hauteur_image);
imagecopy($texte, $image, 0, 0,$coord_x,$coord_y,$largeur_image, $hauteur_image);
imagepng($texte,"texte_transparent.png");
ImageDestroy($texte);
ImageDestroy($image);
?>

 

EffetsHaut

En allant plus loin avec les fonctions d'images à palette, vous pouvez créer des effets intéressants.
La fonction ImageCopyMerge fusionne deux images, ce qui donne un effet de transparence au fond blanc :
<?
...
/* ImageCopyMerge fonctionne exactement comme ImageCopy, mais ajoute un paramètre*/
imagecopymerge($image, $texte, $coord_x, $coord_y, 0, 0, $largeur_image, $hauteur_image, 60);
...
?>

L'exemple suivant simule une image en niveaux de gris en modifiant les valeurs RVB des index de la table des couleurs :

<?
$im = imageCreateFromPNG("palmier.png");
for($i=0;$i<imagecolorstotal ($im);$i++) 
{ 
           $color = ImageColorsForIndex($im,$i); 
           $rvb=.299 * ($color['red'])+ .587 * ($color['green'])+ .114 * ($color['blue']); 
           ImageColorSet($im, $i, $rvb, $rvb, $rvb);
}
imagePNG($im,"noir_et_blanc.png"); ImageDestroy($im); ?>

 

L'île au trésor à l'époque de Barbe-Rouge

Toujours avec la même méthode, il est possible de coloriser l'image :

<?
...
$rouge=.299 * ($color['red'])+ .587 * ($color['green'])+ .114 * ($color['blue']); 
$vert=.299 * ($color['red'])+ .587 * ($color['green'])+ .114 * ($color['blue']);
$bleu=.100 * ($color['red'])+ .100 * ($color['green'])+ .100 * ($color['blue']);
ImageColorSet($im, $i, $rouge, $vert, $bleu); ... ?>

L'île avait pourtant la couleur de l'or