Une classe et sept méthodes pour créer des miniatures
Les miniatures c'est chouette, mais les faire avec Photoshop c'est déjà moins chouette. Surtout quand on gère un album web dans lequel de nombreuses personnes peuvent laisser des images. Il est possible de les redimensionner par l'intermédiaire de la fonction imagecopyresampled() mais on ne peut pas dire que l'on soit étouffé sous une pléthore d'options de redimensionnement et de recadrage.
Je vous propose donc la méthode resize() de ma classe WdImage et quelques exemples de rendu pour vous mettre l'eau à la bouche.
Sept méthodes de redimensionnement
Les exemples suivant utilisent trois images aux formats bien différents : un carré (300 × 300px), un rectangle horizontal (400 × 200px) et un rectangle vertical (200 × 400px). Elles sont ensuite redimensionnées en 100 × 200px.
Voilà ce que cela donne en fonction des options de redimensionnement choisies :
WdImage::RESIZE_SCALE_MAX
Redimensionne l'image pour quelle tienne dans l'espace cible. Cela donne des miniatures ayant toutes la même largeur et la même hauteur, avec un fond visible.

WdImage::RESIZE_SCALE_MIN
Redimensionne l'image pour que tout l'espace cible soit rempli. L'image est cadrée horizontalement et verticalement. Cela donne des miniatures ayant toutes la même largeur et la même hauteur, avec un maximum de l'image source visible.

WdImage::RESIZE_FIXED_HEIGHT
L'image est redimensionnée par rapport à la hauteur cible. La largeur est calculée proportionnellement. Cela donne des miniatures qui ont toutes la même hauteur, mais pas forcément la même largeur.

WdImage::RESIZE_FIXED_HEIGHT_CROPPED
L'image est redimensionnée par rapport à la hauteur cible. Si la largeur de l'image est plus importante que celle de la cible, l'image est cadrée. Cela donne des miniatures qui ont toutes la même hauteur, mais pas forcément la même largeur.

WdImage::RESIZE_FIXED_WIDTH
L'image est redimensionnée par rapport à la largeur cible. La hauteur de l'image est calculée proportionnellement. Cela donne des miniatures qui ont toutes la même largeur, mais pas forcément la même hauteur.

WdImage::RESIZE_FIXED_WIDTH_CROPPED
L'image est redimensionnée par rapport à la largeur cible. Si la hauteur de l'image est plus importante que celle de la cible, l'image est cadrée. Cela donne des miniatures qui ont toutes la même largeur, mais pas forcément la même hauteur.

WdImage::RESIZE_SURFACE
L'image est redimensionnée pour que sa surface égale celle de la cible. Cela donne des miniatures de largeurs et de hauteurs différentes mais qui occupent toutes la même surface en terme de pixels.

Un exemple d'utilisation
<?php
$thumbnail = WdImage::resize
(
$source,
$target_width,
$target_height,
WdImage::RESIZE_SCALE_MAX,
'fill_callback'
);
?>
Comme vous pouvez le voir dans l'exemple ci-dessus, il est possible de définir la fonction qui sera appelée pour remplir la zone cible avant de redimensionner l'image source. J'en profite pour utiliser la méthode drawGrid() que je vous avez présenté précédemment.
<?php
function fill_callback($image, $w, $h)
{
WdImage::drawGrid
(
$image, 0, 0, $w - 1, $h - 1
);
}
?>
Un exemple d'utilisation dans la vrai vie
À la demande confuse générale, voici un exemple tout simple qui ravira les petits comme les grands. Nous allons charger une image, la redimensionner en 128×128 et renvoyer la version redimensionnée au format PNG.
Pour cet exemple, j'utilise la méthode RESIZE_SURFACE. Elle ne respecte pas vraiment les dimensions de destination, mais plutôt sa surface. J'ai choisi cette méthode pour justifier l'utilisation de variables pour passer les dimensions cibles, puisque les dimensions finales seront écrites dans ces variables à la fin du redimensionnement.
J'espère que ce sera assez clair, et utile !
<?php
#
# voici l'adresse de l'image que nous voulons redimensionner.
# il est recommandé d'utiliser un chemin absolu. dans notre exemple
# l'image se trouve au même niveau que notre script, alors on fait les
# fégniants :-)
#
$file = 'image-resize-source.jpg';
#
# on crée une ressource _image_ depuis le fichier
#
$image = imagecreatefromjpeg($file);
#
# on redimensionne l'image en 128x128 en utilisant la méthode
# RESIZE_SURFACE.
#
# note: les dimensions *doivent* être passées sous forme de
# variables. Elles sont utilisées comme _référence_. La méthode
# RESIZE_SURFACE y écrira les dimensions finales de l'image.
# Ici ce sera 181x91 (16384 => 16471)
#
$w = 128;
$h = 128;
$result = WdImage::resize($image, $w, $h, WdImage::RESIZE_SURFACE);
#
# on renvoie le résultat sous forme d'image png en utilisant
# un callback sur le tampon de sortie
#
function output_handler($img)
{
header('Content-type: image/png');
header('Content-Length: ' . strlen($img));
return $img;
}
ob_start('output_handler');
imagepng($result, null);
ob_end_flush();
exit;
?>
Une méthode bien méritée
Voici donc la méthode resize() extraite de la classe WdImage :
<?php
class WdImage
{
const RESIZE_SCALE_MAX = 'scale-max';
const RESIZE_SCALE_MIN = 'scale-min';
const RESIZE_FIXED_HEIGHT = 'fixed-height';
const RESIZE_FIXED_HEIGHT_CROPPED = 'fixed-height-cropped';
const RESIZE_FIXED_WIDTH = 'fixed-width';
const RESIZE_FIXED_WIDTH_CROPPED = 'fixed-width-cropped';
const RESIZE_SURFACE = 'surface';
static public function resize($source, &$t_w, &$t_h, $method, $fill_callback=NULL)
{
#
# source dimensions
#
$s_x = 0;
$s_y = 0;
$s_w = imagesx($source);
$s_h = imagesy($source);
#
# destination dimensions
#
$d_x = 0;
$d_y = 0;
$d_w = $t_w;
$d_h = $t_h;
#
# select scale method
#
switch ($method)
{
case self::RESIZE_SCALE_MAX:
default:
{
#
# scale-max
#
# Resize the image so that it all fits in the target space.
# This will result in thumbnails equals in width and height, with a possible
# background visible.
#
$s_r = $s_w / $s_h;
$d_r = $d_w / $d_h;
$r = $s_r > $d_r ? $s_w / $d_w : $s_h / $d_h;
$d_w = round($s_w / $r);
$d_h = round($s_h / $r);
}
break;
case self::RESIZE_SCALE_MIN:
{
#
# scale - min
#
# Resize the image so that the whole target space is filled. The image is cropped
# to the target width and height.
# This will result in thumbnails equals in width and height, with the maximum of
# information visible.
#
$s_r = $s_w / $s_h;
$d_r = $d_w / $d_h;
if ($s_r > $d_r)
{
$r = $s_h / $d_h;
$s_x += round(($s_w - $t_w * $r) / 2);
}
else
{
$r = $s_w / $d_w;
$s_y += round(($s_h - $t_h * $r) / 2);
}
$d_w = round($s_w / $r);
$d_h = round($s_h / $r);
}
break;
case self::RESIZE_FIXED_HEIGHT:
{
#
# fixed-height
#
# The image is resized to match the target height.
# The image width is resized accordingly.
# This will result in thumbnails equals in height, but not in width.
#
$r = $s_h / $d_h;
$d_w = round($s_w / $r);
if ($s_w > $s_h)
{
$d_h = round($s_h / $r);
}
$t_w = $d_w;
}
break;
case self::RESIZE_FIXED_HEIGHT_CROPPED:
{
#
# fixed-height-cropped
#
# The image is resized to match the target height.
# If the image width is larger than the target width, the image is cropped.
# This will result in thumbnails equals in height, but not in width.
#
$r = $s_h / $d_h;
$d_w = round($s_w / $r);
if ($s_w > $s_h)
{
$d_h = round($s_h / $r);
}
else
{
$t_w = $d_w;
}
#
# crop image
#
if ($s_w > $t_w * $r)
{
$s_x += round(($s_w - $t_w * $r) / 2);
}
}
break;
case self::RESIZE_FIXED_WIDTH:
{
#
# fixed-width
#
# The image is resized to match the target width.
# The image height resized accordingly.
# This will result in thumbnails equals in width, but not in height.
#
$r = $s_w / $d_w;
$d_h = round($s_h / $r);
if ($s_w < $s_h)
{
$d_w = round($s_w / $r);
}
$t_h = $d_h;
}
break;
case self::RESIZE_FIXED_WIDTH_CROPPED:
{
#
# fixed-width-cropped
#
# The image is resized to match the target width.
# If the image height is taller than the target height, the image is cropped.
# This will result in thumbnails equals in width, but not in height.
#
$r = $s_w / $d_w;
$d_h = round($s_h / $r);
if ($s_w > $s_h)
{
$t_h = $d_h;
}
else
{
$d_w = round($s_w / $r);
}
#
# crop image
#
if ($s_h > $t_h * $r)
{
$s_y += round(($s_h - $t_h * $r) / 2);
}
}
break;
case self::RESIZE_SURFACE:
{
#
# surface
#
# The image is resized so to its surface matches the target surface.
# this will result in thumbnails with different width and height, but with
# the same amount of pixels.
#
$r = sqrt(($s_w * $s_h) / ($t_w * $t_h));
$d_w = round($s_w / $r);
$d_h = round($s_h / $r);
$t_w = $d_w;
$t_h = $d_h;
}
break;
}
#
# center destination image result
#
if ($t_h > $d_h)
{
$d_y = round(($t_h - $d_h) / 2);
}
if ($t_w > $d_w)
{
$d_x = round(($t_w - $d_w) / 2);
}
#
# create destination image
#
$destination = imagecreatetruecolor($t_w, $t_h);
#
# call user's function to fill the area
#
if ($fill_callback)
{
call_user_func($fill_callback, $destination, $t_w, $t_h);
}
#
# now we resize and paste our image
#
imagecopyresampled
(
$destination,
$source,
$d_x, $d_y, $s_x, $s_y,
$d_w, $d_h, $s_w, $s_h
);
return $destination;
}
}
?>
Salut à toi,
ton script est très bien foutu… mais j'aimerai bien savoir comment faire pour l'utiliser et surtout pour appeler la classe et les variables à envoyer….
Merci d'avance
moi c'est pareil, car ca n'est pas très clair à mes yeux
J'ai ajouté Un exemple d'utilisation dans la vrai vie. J'espère qu'il sera assez clair et surtout utile. N'hésitez pas à venir râler si vous n'y comprenez toujours rien ;-)
Bonjour,
Je suis entrain de développer un plugin pour la gestion des miniatures (mise en cache, recadrages, crop) pour le site de wikine. Je me suis permis de prendre votre script comme base pour les fonctions de retouche.
Cela vous embête-t-il ? Seriez vous intéressé par la publication libre du plugin et à une plus ample participation (en diversifiant encore les possibilité offerte ?).
Cordialement.
Salut Btacquet,
Aucun problème pour l'utilisation du script, il est là pour ça de toute façon :-)
Si ça t'intéresse j'ai aussi développé un module pour mon CMS qui permet de générer des miniatures à la volée (avec un cache). On utilise une simple URL dans laquelle on définit le chemin vers l'image, les dimensions souhaitées, le mode de redimensionnement et la couleur de remplissage.
Je pense que le code peut facilement être adapté, le plus important restant le principe.
Pour ce qui est de la publication libre de ton plugin, je suis pour !
Vive l'échange !
bonjour et tout d'abord, un grand ouah pour ton script!
je serais aussi intéressée de voir le module que tu as développer pour l'installer sur un cms!