Accueil Dev WEB

Tuto: Distorsions en GD2 1h

On me demande parfois de quelle façon fait notre équipe pour produire les écrans visuels présents sur la homepage de Maespirit.fr ( http://www.maespirit.fr/ ), ou sur ses réalisations ( http://www.maespirit.fr/fr/projet-web/boc/ ). Et bien non, on ne fait pas ça à la main :D !

Manufacturing, développé par Maespirit Buy or Click, développé par Maespirit Dogfinance, développé par Maespirit

Je vais vous montrer comment réaliser une telle chose via la librairie GD2 (php).

GD est le nom d’une bibliothèque libre servant à manipuler des images dynamiquement, son nom vient de l’anglais gif draw (dessiner un GIF). Cette bibliothèque peut manipuler dynamiquement plusieurs types d’images, tels que les formats GIF, PNG, JPEG, WBMP, XBM et XPM.

Source: Wikipedia

Pré-requis

- IDE Webdev
- L’image à distordre et travailler (pour notre exemple, ce sera l’image suivante:)

- Le cadre qui va se poser PAR DESSUS l’image distordue, afin de conserver les effets de reflet sur l’écran :) Vous devez utiliser bien sur un cadre au format PNG pour en conserver la transparence. Pour notre exemple, ce sera l’image suivante:

- Un esprit assez mathématique, il va être important comment souvent en dev ;) (HS: je vais bientôt écrire un article qui va mettre au fond bon nombre de mathématicien, keep alive ;) )

Bon, on s’y met?

Les variables

Je vais ici expliquer les différentes variables dont nous allons avoir besoin au cours de cette réalisation. Elles sont bien sûr à placer en haut de script.

// Variables utilisees
$image="tb.jpg";
$cadre="cadre3.png";
$largeurZoneFinale=108;
$hauteurZoneFinale=112;
$x1FinalDansRec=16;
$y1FinalDansRec=22;
$y3FinalDansRec=109;
$x4FinalDansRec=94;
$xRec=15;
$yRec=11;
$qualite=80;
$fichierSortie="";

Bon, pour image et cadre, c’est assez instinctif. largeur et hauteurZoneFinale définissent les dimensions du rectangle final qui va accueillir l’image distordue. Ces mesures sont à récupérer dans un logiciel de traitement d’image, comme celles qui vont suivre.

Pour nous aider, voici le schéma du rectangle qui va contenir l’image distordue, et dont nous allons parler pour les mesures qui vont suivre:

x1 et y1FinalDansRec, sont les coordonnées du point en haut à gauche de l’image distordue finale.
On ne définit ni x2 ni y2 (point nord-est), car c’est le plus haut et le plus à droite. Il a donc la même position que le point nord-est du rectangle qui va contenir l’image.
On ne définit pas x3 qui correspond au point le plus à gauche de l’image. Il est donc définit par la position du point sud-ouest du rectangle conteneur. On définit par contre y3.
On définit x4 car il n’est pas confondu avec un le x d’unsommet du rectangle conteneur. Ce n’est pas vrai pour y4, on ne le définit donc pas.

Vous l’aurez sans doute deviné, on a donc besoin des coordonnées du point nord-ouest du rectangle conteneur, puisqu’il n’est confondu avec rien.

Qualité définit la qualité d’image du JPEG en sortie, et fichierSortie reste vide pour un simple affichage, prend le nom (voir le chemin physique) d’une image si l’on veut enregistrer le résultat.

Affichage de l’image en GD

Mettons maintenant en place un affichage basique de l’image chargée en GD, après nos variables:

header('Content-type: image/jpeg');

//Chargement de l'image
$contenu=imagecreatefromjpeg($image);

// On affiche l'image
imagejpeg($contenu,$fichierSortie,$qualite);

Surtout ne pas oublier de définir le header permettant d’indiquer au navigateur le type de fichier que l’on s’apprête à renvoyer. Ici du JPEG.

On va donc procéder par étape pour réaliser cette distorsion. 4 étapes seront nécessaires: la distorsion en Y, la distorsion en X, le redimensionnement au format du rectangle conteneur, l’application du cadre.

Etape 1 – Première distorsion

Il s’agit d’effectuer une distorsion en Y. Le but est d’arriver à ce résultat:

Pour se faire, on va avoir besoin des dimensions de l’image en entrée:

header('Content-type: image/jpeg');

//Chargement de l'image
$contenu=imagecreatefromjpeg($image);
$detailsImage=getimagesize($image);
$largeurImg=$detailsImage[0];
$hauteurImg=$detailsImage[1];

// On affiche l'image
imagejpeg($contenu,$fichierSortie,$qualite);

Il faut ensuite calculer les coordonnées nécessaires à cette distorsion: y1 et y3, sachant que y2 et y4 ne seront pas amenés à changer dans la distorsion finale.

////////////////////DISTORSION 1/////////////////////

//Coordonnees
$y1=$hauteurImg*$y1FinalDansRec/$hauteurZoneFinale;
$y3=$hauteurImg*$y3FinalDansRec/$hauteurZoneFinale;
$h1=$y3-$y1;

// On affiche l'image
imagejpeg($contenu,$fichierSortie,$qualite);

On créé ensuite un conteneur sur fond transparent:

//Creation du container
$container=imagecreatetruecolor($largeurImg,$hauteurImg);
$noir=imagecolorallocate($container,0,0,0);
imagecolortransparent($container,$noir);

Il s’agit maintenant de lui appliquer un algorithme de distorsion, conçu par mes soins. Ce dernier traite l’image colonne de pixels par colonne de pixels, en leur appliquant la nouvelle hauteur désirée dans l’image distordue finale:

////////////////////DISTORSION 1/////////////////////

//Coordonnees
$y1=$hauteurImg*$y1FinalDansRec/$hauteurZoneFinale;
$y3=$hauteurImg*$y3FinalDansRec/$hauteurZoneFinale;
$h1=$y3-$y1;
$monte=$y1/$largeurImg;
$descend=($hauteurImg-$y3)/$largeurImg;

//Creation du container
$container=imagecreatetruecolor($largeurImg,$hauteurImg);
$noir=imagecolorallocate($container,0,0,0);
imagecolortransparent($container,$noir);

//Algo distorsion
$bufferVirgule=0;
$bufferVirgule2=0;
for($i=0;$i<$largeurImg;$i++){
	$dest_x=$i;
	$y1=$y1-$monte;
	//Premiere bufferisation
	$bufferVirgule+=($y1-floor($y1));
	if($bufferVirgule>=1) {
		$y1+=1;
		$bufferVirgule-=1;
	}
	$y1=floor($y1);
	/////////////////////////
	$dest_h=$h1+($i*$monte)+($i*$descend);
	//Deuxieme bufferisation
	$bufferVirgule2+=($dest_h-floor($dest_h));
	if($bufferVirgule2>=1) {
		$dest_h+=1;
		$bufferVirgule2-=1;
	}
	$dest_h=floor($dest_h);
	////////////////////////
	imagecopyresampled($container,$contenu,$dest_x,$y1,$dest_x,0,1,
$dest_h,1,$hauteurImg);
}

A ce stade, si on affiche l’image, dont la ressource est contenue dans $container, on obtient bien le résultat escompté à cette étape.

Etape 2 – Deuxième distorsion

Il s’agit maintenant de reprendre l’algorithme précédent pour recalculer, ligne de pixel par ligne de pixel, la nouvelle largeur et effectuer une distorsion horizontale. On pourra obtenir le résultat suivant:

////////////////////DISTORSION 2/////////////////////

//Coordonnees
$x1=$largeurImg*$x1FinalDansRec/$largeurZoneFinale;
$y1=$hauteurImg*$y1FinalDansRec/$hauteurZoneFinale;
$x3=0;
$x4=$x4FinalDansRec*$largeurImg/$largeurZoneFinale;
$gauche=$x1/($hauteurImg-$y1);
$droite=($largeurImg-$x4)/$hauteurImg;

$newLarg=2*$largeurImg-$x4;

//Creation du container
$container2=imagecreatetruecolor($largeurImg,$hauteurImg);
$noir=imagecolorallocate($container2,0,0,0);
imagecolortransparent($container2,$noir);

//Algo distorsion
$bufferVirgule=0;
$bufferVirgule2=0;
for($i=0;$i<$hauteurImg;$i++){
	//echo $x4." ";
	$dest_y=$hauteurImg-$i;
	$width_distor2=$x4-$x3;
	imagecopyresampled($container2,$container,$x3,$dest_y,0,$dest_y,
$width_distor2,1,$largeurImg,1);
	$x3+=$gauche;
	//Premiere bufferisation
	$bufferVirgule+=($x3-floor($x3));
	if($bufferVirgule>=1) {
		$x3+=1;
		$bufferVirgule-=1;
	}
	$x3=floor($x3);
	/////////////////////////
	$x4+=$droite;
	//Deuxieme bufferisation
	$bufferVirgule2+=($x4-floor($x4));
	if($bufferVirgule2>=1) {
		$x4+=1;
		$bufferVirgule2-=1;
	}
	$x4=floor($x4);
	////////////////////////
}

////////////////////Fin distorsion/////////////////////

Il suffit alors d’afficher l’image contenue dans $container2 pour obtenir le résultat.

Etape 3 – Redimensionnement

Cette fois le but est de récupérer l’image distordue qui s’adaptera parfaitement au cadre. Il est donc question de redimensionner au format du cadre, et à la bonne position finale:

////////////////////Infos cadre/////////////////////
$cadre_contain=imagecreatefrompng($cadre);
$detailsCadre=getimagesize($cadre);
$largeurCadre=$detailsCadre[0];
$hauteurCadre=$detailsCadre[1];

//Redimensionnement
$container3=imagecreatetruecolor($largeurCadre,$hauteurCadre);
$noir=imagecolorallocate($container3,0,0,0);
imagecolortransparent($container3,$noir);
imagecopyresampled($container3,$container2,$xRec,$yRec,0,0,
$largeurZoneFinale,$hauteurZoneFinale,$largeurImg,$hauteurImg);

On pourra obtenir le résultat suivant en affichant la variable $container3:

Etape 4 – Application du cadre

Un simple usage de la fonction GD imagecopy pour copier le cadre PNG par dessus notre image distordue:

//Ajout du cadre
imagecopy($container3,$cadre_contain,0,0,0,0,$largeurCadre,$hauteurCadre);

Et voici le résultat à l’affichage de $container3 :) :

Pour terminer

Et voila, vous connaissez maintenant la façon de distordre une image en GD2. Cette opération peut être faite à la volée (à chaque affichage navigateur), mais attention car le traitement est assez lourd, le rendu ne sera donc probablement pas instantané chez les clients. De notre côté, on s’en sert uniquement comme génération, on utilise ensuite une image physique, c’est bien plus optimisé.

Si vous avez des commentaires pour améliorer ce code, c’est parti, je l’ai quand même rédigé il y a 3 ans, il serait intéressant de le remettre à jour :)

Source complète dispo ici

3 Commentaires

  1. TitoX
    12 janvier 2011 at 23 h 01 min

    merci, bon tuto ! ;)

  2. 20 avril 2011 at 11 h 42 min

    J’ai essayé, c’est top!
    Cependant que penses-tu de la distorsion disponible en CSS 3 ? (transform: skew), je penses que le javascript va quand même rendre l’image plus « nette » ?

  3. Renaud Feigenbaum
    21 avril 2011 at 10 h 00 min

    C’est très largement plus léger bien sûr, et même en terme de qualité, je viens de tester c’est bien plus propre. Je pense que les navigateurs intègrent une fonctionnalité de « lissage » avec les nouvelles propriétés css. Comme le travail est quoiqu’il en soit un travail au pixel, je pense qu’en bossant sur l’algo PHP de l’article, on peut produire quelque chose de bien plus propre, se rapprochant plus du css3.
    Bonne remarque :D

Ajouter un commentaire

Votre Email n'est jamais publiée ou partagée. Les champs requis sont marqués *.

*
*