ACCUEIL > TUTORIAUX SPIP

UN HABILLAGE IRRÉGULIER (FLOAT : RAGGED)

   

[SPIP 1.9 et GD2] Voici un nouveau filtre SPIP qui vous permettra de réaliser un effet que nous attendons depuis des années en HTML (et qui n’est pas prêt d’arriver sous la forme d’un style facile à utiliser), réalisable en bidouillant mais pour lequel il n’existait pas encore d’automatisme (à ma connaissance).

(Attention : cet article est désormais complété par un second article ; les explications qui suivent sont nécessaires à la compréhension de ce nouvel article.)

Le but est de réaliser un « habillage » irrégulier autour d’une image. Comme vous le savez, en HTML, lorsqu’on insère une image à l’intérieur d’un texte, en l’alignant à droite ou à gauche, le texte « habille » l’image selon les bords du rectangle :

Le filtre qui suit va nous permettre de réaliser l’habillage en fonction de la forme réelle de l’image :

Principe de l’habillage irrégulier

Le principe général de l’habillage irrégulier est celui de Curvelicious d’Eric Meyer. Ce que notre script va réaliser, c’est l’étape suivante, puisque l’effet pourra dès lors être réalisé automatiquement : le filtre va analyser l’image pour fabriquer le découpage.

- Première étape : ne pas utiliser l’image pour créer l’habillage

Nous insérons l’image dans la page, mais sans réaliser d’habillage. Le texte et l’image sont superposés.

(Eric Meyer propose, lui, de découper l’image en tranches (à éviter absolument !). FlumpCakes place l’image en background. )

Notre script va se contenter d’insérer l’image dans un <div> flottant de largeur nulle, l’image étant ensuite positionnée à l’intérieur, en position absolue. À tester, mais l’avantage est pouvoir afficher correctement les images PNG 24 avec couche alpha (que nous utiliserons ici) dans MSIE, comme nous l’avons déjà expliqué.

- Insérer des petits rectangle horizontaux successifs pour créer l’habillage

Nous allons maintenant réaliser l’habillage en insérant une série de <div> successifs dont le code sera le suivant :

  1. <div style='float: right; clear: right; width: 178px ; height: 5px; overflow: hidden;'></div>

L’astuce est dans la suite float: right; clear: right; (right devenant simplement left si on veut aligner l’image à gauche) : on insère un élément flottant, et chaque élément flottant chasse l’autre).

De cette façon, on « empile » des rectangles horizontaux, dont la hauteur totale sera égale à la hauteur de l’image. On créé un espace « flottant » irrégulier destiné à « recouvrir » les parties de l’image que l’on souhaite habiller. On « recouvre » avec des blocs invisibles, évidemment.

Si l’on recouvrait avec des blocs colorés, voici ce qu’on obtiendrait :

Le gnou n’est ajouté que pour comprendre la forme des <div> : ce sont ces <div> successifs (empilés) qui provoquent l’habillage irrégulier du texte, et non l’image (rectangulaire) du gnou.

(Note : j’ai ajouté overflow: hidden; dans mes <div> ; sans lui, j’obtenais des rectangles trop grands sous MSIE. Cédric, dans le forum ci-dessous, indique qu’il s’agit de la taille de la police qui « force » la hauteur des boîtes ; même si le <div> ne contient aucun texte.)

Automatiser la découpe

Ce principe est plutôt simple, mais sans automatisme, il n’est vraiment pas utilisable... C’est sans doute la raison pour laquelle le truc de Curvelicious ne se rencontre presque jamais sur le Web.

Le script suivant va nous permettre de créer automatiquement la série de rectangles empilés, en faisant analyser par SPIP la forme de l’image.

Je vous donne le script tout de suite :

  1. function image_float ($img, $align, $margin=10) {
  2.         $image = valeurs_image_trans($img, "float-$align", "php");
  3.         if (!$image) return("");
  4.         $w = $image["largeur"];
  5.         $h = $image["hauteur"];
  6.         $precision = round($h / 5);
  7.        
  8.         $im = $image["fichier"];
  9.         $dest = $image["fichier_dest"];
  10.         $creer = $image["creer"];
  11.         $ret .= "<div style='position: relative; float: $align; width: 0px; height: 0px;'><img src='$im' class='format_png' alt='' style='position: absolute; $align: 0px;' /></div>";
  12.         if ($creer) {
  13.                 include_spip('inc/logos'); // bicoz presence reduire_image
  14.                 $nouveau = valeurs_image_trans(reduire_image($im, 0, $precision),"");
  15.                 $im_n = $nouveau["fichier"];
  16.                 $x_i = $nouveau["largeur"];
  17.                 $y_i = $nouveau["hauteur"];
  18.                 $rapport = ($w / $x_i);
  19.                
  20.                 $im_n = $image["fonction_imagecreatefrom"]($im_n);
  21.                 // une premiere passe
  22.                 // pour recuperer les valeurs
  23.                 for ($j = 0; $j < $y_i; $j++) {
  24.                         $transp = true;
  25.                
  26.                         for ($i = 0; $i < $x_i && $transp; $i++) {
  27.                                 if ($align == "right") $rgb = ImageColorAt($im_n, $i+1, $j);
  28.                                 else $rgb = ImageColorAt($im_n, ($x_i - $i)-1, $j);
  29.                                 $a = ($rgb >> 24) & 0xFF;
  30.                                 if ($a > 125) $larg[$j] ++;
  31.                                 else $transp = false;
  32.                                 }                      
  33.                 }
  34.                
  35.                 $larg[-1] = $w;
  36.                 $larg[$y_i] = $w;
  37.                 // une deuxieme passe
  38.                 // pour appliquer les valeurs
  39.                 // en utilisant les valeurs precedente et suivante
  40.                 for ($j = 0; $j < $y_i; $j++) {
  41.                         $reste = ($precision - $j);
  42.                         $haut_rest = $h - $haut_tot;
  43.                         $hauteur = round(($haut_rest) / $reste);
  44.                         $haut_tot = $haut_tot + $hauteur;
  45.                         $resultat = min($larg[$j-1],$larg[$j],$larg[$j+1]);
  46.                        
  47.                         $forme .= "\n<div style='float: $align; clear: $align; width: ".($margin+round(($w - ($resultat)*$rapport)))."px ; height: ".round($hauteur)."px; overflow: hidden;'></div>";
  48.                 }
  49.                 // Ajouter un div de plus en dessous
  50.                 $forme .= "\n<div style='float: $align; clear: $align; width: ".($margin+round(($w - ($resultat)*$rapport)))."px ; height: ".round($hauteur)."px; overflow: hidden;'></div>";
  51.                 // Sauvegarder le fichier              
  52.                 $handle = fopen($dest, 'w');
  53.                 fwrite($handle, $forme);
  54.                 fclose($handle);
  55.                 $ret .= $forme;
  56.         }
  57.         else {
  58.                 $ret .= join(file($dest),"");
  59.         }
  60.         return $ret;
  61. }

C’est un filtre qui s’applique à n’importe quelle image : logo ou document joint dans un portfolio.

Pour mon exemple, le code dans le squelette est très simple :

  1. <BOUCLE_doc(DOCUMENTS){titre=GNU}>
  2. [(#FICHIER|image_float{right,5})]
  3. </BOUCLE_doc>
  4. <p>Lorem ipsum dolor sit amet ...</p>

Évidemment, vous ne travaillez certainement pas uniquement avec un document joint intitulé « GNU »...

Les deux variables de image_float sont :
— l’alignement left ou right (on place l’image à gauche ou à droite) ;
— l’espacement en pixels du texte par rapport à l’image.

Il est très important de noter ici que l’habillage va se baser sur le transparence de l’image. Cela ne concerne donc que les images GIF ou PNG, dont le filtre va analyser les informations de transparence. (On peut cependant très facilement adapter ce filtre pour qu’il réalise le détourage en fonction d’une couleur de référence — blanc par exemple.)

Le code expliqué

Je n’entrerai pas dans les détails, je me contenterai d’exposer le fonctionnement général (le code est, par ailleurs, plutôt simple une fois qu’on en a compris le principe).

Le travail principal se trouve à l’intérieur du test :

  1. if ($creer) {
  2.      Ici le code intéressant...
  3. }

— Pour commencer, nous travaillons sur une image $nouveau qui est une version dont les dimensions sont réduites par 5 de l’image d’origine.

Pourquoi 5 ? Parce que nous voulons des rectangles de 5 pixels de haut. Cette valeur peut être modifiée en début de script, à la ligne qui indique :

  1. $precision = round($h / 5);

Cette réduction va nous économiser beaucoup de temps et de puissance (25 fois moins de calculs).

— Voici réellement le principe du script :

  1. for ($j = 0; $j < $y_i; $j++) {
  2.         $transp = true;
  3.         for ($i = 0; $i < $x_i && $transp; $i++) {
  4.                 if ($align == "right") $rgb = ImageColorAt($im_n, $i+1, $j);
  5.                 else $rgb = ImageColorAt($im_n, ($x_i - $i)-1, $j);
  6.                 $a = ($rgb >> 24) & 0xFF;
  7.                 if ($a > 125) $larg[$j] ++;
  8.                 else $transp = false;
  9.         }                      
  10. }

On parcourt l’image réduite ligne par ligne (chaque ligne deviendra un div). Pour chaque ligne, on commence par le premier pixel (à droite ou à gauche selon l’alignement qu’on a choisi), et on parcourt cette ligne jusqu’à tomber sur un pixel non transparent.

Ainsi, pour chaque ligne $j, on connaît le nombre de pixels transparents $larg[$j] avant de « tomber » sur les pixels non transparents de l’image.

— Une seconde boucle va appliquer ces valeurs en créant l’empilement de <div>.

  1. for ($j = 0; $j < $y_i; $j++) {
  2.         $resultat = min($larg[$j-1],$larg[$j],$larg[$j+1]);
  3.         $forme .= "\n<div style='float: $align; clear: $align; width: ".($margin+round(($w - ($resultat)*$rapport)))."px ; height: ".round($hauteur)."px; overflow: hidden;'></div>";
  4. }

À chaque ligne, on récupère dans $resultat la valeur $larg minimale de la ligne actuelle, de la ligne précédente et de la ligne suivante. (Pourquoi pas la moyenne ? Pour éviter que le texte ne « rentre » dans l’image.)

Ensuite, à chaque ligne, la largeur du <div> est calculée selon la largeur totale de l’image $w diminuée de $resultat, le nombre de pixels transparents (on multiplie le chiffre final, puisqu’on travaille sur une image réduite).

Si vous comparez avec le script complet, vous verrez que j’ai masqué dans l’extrait précédent les lignes suivantes :

  1. $reste = ($precision - $j);
  2. $haut_rest = $h - $haut_tot;
  3. $hauteur = round(($haut_rest) / $reste);
  4. $haut_tot = $haut_tot + $hauteur;

Ces lignes (que l’on peut simplifier et améliorer) permettent de calculer la hauteur de chaque <div>. En gros, chaque rectangle doit faire 5 pixels de haut mais, avec les arrondis, cela ne tombe que rarement juste.

En réalité, ces lignes sont plus utiles dans les cas où l’on modifie le calcul de la $precision en début de script. Ici, la précision est calculée de façon à ce que les rectangles aient toujours 5 pixels de haut, le nombre de ces rectangles pouvant varier en fonction de la hauteur de l’image. On peut aussi décréter que la précision sera une valeur fixe, par exemple 20, ce qui signifie alors qu’il y aura 20 rectangles superposés, quelle que soit la hauteur de l’image.

— Enfin, toute une partie du script, au début et à la fin, est liée à la création d’un fichier de cache, pour éviter de recalculer à chaque fois cette série de valeurs. Là où les filtres graphiques de SPIP stockent en général l’image filtrée, ici on stocke tout simplement le code HTML de la série de rectangles.


Et voici le résultat

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vivamus ut nunc eget ante ornare nonummy. Ut arcu. Duis tincidunt tincidunt quam. In elementum blandit odio. Nullam ultrices. Nulla sem augue, mollis id, vulputate eget, ullamcorper ultrices, purus. Aenean porttitor odio at mauris. Mauris quis enim vitae purus dictum ultricies. Proin pharetra lectus auctor lacus. Quisque at sem ac lectus ornare vehicula. Nunc pulvinar, leo ut tristique auctor, felis diam gravida neque, consectetuer cursus sem nisl ut enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec justo. Aliquam erat volutpat. Sed vel enim nec tellus suscipit imperdiet. Maecenas sagittis, dolor id tincidunt suscipit, orci tortor fermentum mi, id varius dolor nisi quis lectus. Quisque ante sem, molestie a, euismod sed, tempus sit amet, mauris. Integer vel ante eget urna sagittis consectetuer. Quisque ullamcorper convallis velit.

Ut porta. Quisque euismod. Cras adipiscing, tellus id vestibulum ultrices, leo erat auctor diam, vitae aliquam libero orci ac augue. Fusce vel dui. Sed eleifend est ac mauris. Nam in leo. In hac habitasse platea dictumst. Suspendisse hendrerit nisl at orci. Curabitur condimentum. Proin scelerisque. Nunc eros.


modération à priori

Ce forum est modéré à priori : votre contribution n'apparaîtra qu'après avoir été validée par un administrateur du site.

Un message, un commentaire ?
  • (Pour créer des paragraphes, laissez simplement des lignes vides.)

Qui êtes-vous ? (optionnel)